Регулярное выражение | Високосные годы и многое другое
Недавно я искал регулярное выражение для проверки даты на стороне клиента, и я не смог найти тот, который может удовлетворять следующим критериям:
- Имеет диапазон от 1800 - теперь
- Выполняет правильную проверку даты с летальными годами
- Форма MM/DD/YYYY
- Неверная проверка даты
(Эти ограничения были вне моей сферы действия и являются требованием для клиента, несмотря на мои попытки убедить их, что это не лучший маршрут)
Текущий код:
$('input').keyup(function()
{
var regex = /^(?:(0[1-9]|1[012])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})$/;
$(this).toggleClass('invalid',!regex.test($(this).val()));
});
Update:
Я должен отметить, что это прежде всего, чтобы увидеть, возможно ли регулярное выражение, подобное этому (поскольку использование Regex не является моим выбором в этом вопросе). Я знаю другие (и лучшие) варианты для проверки даты, однако, как уже упоминалось ранее, это означает, что это возможно с помощью регулярного выражения.
Ответы
Ответ 1
Как упоминается в другом месте, регулярные выражения почти не соответствуют тому, что вы хотите. Но, сказав это, если вы действительно хотите регулярное выражение, вот как оно построено:
31-дневный месяц
(0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2}
30-дневные месяцы
(0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2}
Февраль 1-28 всегда действительный
(02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2}
29 февраля также действителен в високосный год
(02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)
что означает, что это было бы, если бы вы все вместе взяли:
((0[13578]|1[02])[\/.](0[1-9]|[12][0-9]|3[01])[\/.](18|19|20)[0-9]{2})|((0[469]|11)[\/.](0[1-9]|[12][0-9]|30)[\/.](18|19|20)[0-9]{2})|((02)[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))
Эта версия немного короче, но немного сложнее понять.
((0[13578]|1[02])[\/.]31[\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\/.](29|30)[\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\/.](0[1-9]|1[0-9]|2[0-8])[\/.](18|19|20)[0-9]{2})|((02)[\/.]29[\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))
Эти сценарии длинные и неподъемные. Должно быть ясно, что это не очень хорошая идея, но это возможно.
Предостережения:
- диапазон 1800-2099 (больше можно добавить без особых трудностей, но требует изменений в 4-6 разрозненных местах)
- требуется 2 цифры месяцев и дней (строгость может быть удалена из выражения в ~ 8 местах)
-
[\/.]
в качестве разделителей (8 мест)
- Не тестировался (мы могли проверить его на все комбинации цифр и сравнить с функцией даты javascript? [доказательство того, что мы изобретаем колесо])
Ответ 2
Я бы посоветовал отказаться от попытки использовать для этого регулярные выражения. Вам намного лучше разобрать дату на ее составные части (месяц, день, год), а затем использовать численные сравнения, чтобы убедиться, что они находятся в правильном диапазоне.
Еще лучше, посмотрите, будет ли функция Javascript Date.parse делать то, что вы хотите.
Синхронизация дат с регулярными выражениями возможна, но расстраивает. Трудно понять, выражение для мастеров, не относящихся к регулярному выражению, трудно понять (что означает, что трудно доказать, что вещь верна), и она медленна по сравнению с другими вариантами.
Ответ 3
Вот как я это сделаю:
function validate( input ) {
var date = new Date( input );
input = input.split( '/' );
return date.getMonth() + 1 === +input[0] &&
date.getDate() === +input[1] &&
date.getFullYear() === +input[2];
}
Использование:
validate( '2/1/1983' ) // true
validate( '2/29/1983' ) // false
validate( '2/29/1984' ) // true (1984 is a leap year)
Live demo: http://jsfiddle.net/9QNRx/
Ответ 4
Очевидно, что регулярные выражения не идеальный способ сделать это. Кроме того, гораздо безопаснее работать с форматом YYYY-MM-DD
(ISO 8601), а не MM/DD/YYYY
.
Таким образом, здесь идет кратчайшее полностью действующее регулярное выражение для дат с 01/01/1800 по 12/31/2099:
^(((0[1-9]|1[012])\/(?!00|29)([012]\d)|(0[13-9]|1[012])\/(29|30)|(0[13578]|1[02])\/31)\/(18|19|20)\d{2}|02\/29\/((18|19|20)(0[48]|[2468][048]|[13579][26])|2000))$
Длина: 162 символа.
Структура:
^ # start
(
( # non-leap months & days
(0[1-9]|1[012])/(?!00|29)([012]\\d) # all months, days 01-28, uses negative lookahead
|
(0[13-9]|1[012])/(29|30) # all months except feb, days 29,30
|
(0[13578]|1[02])/31 # all 31 day months, day 31 only
)
/
(18|19|20)\\d{2} # all years
|
02/29 # leap day
/
(
(18|19|20)(0[48]|[2468][048]|[13579][26]) # leap years not divisible by 100
|
2000 # leap years divisible by 100
)
)
$ # end
Здесь скрипка, которая проверяет все варианты использования с 00/00/1800 на 99/99/2099.
Кроме того, для большего удовольствия, вот еще одна скрипка, которая генерирует самое страшное возможное регулярное выражение, которое все еще работает, 1205306 символов. Это выглядит примерно так:
^(01/01/1800|01/02/1800|01/03/1800|...|12/29/2099|12/30/2099|12/31/2099)$
Ответ 5
это регулярное выражение для формата YYYY-MM-DD
((18|19|20)[0-9]{2}[\-.](0[13578]|1[02])[\-.](0[1-9]|[12][0-9]|3[01]))|(18|19|20)[0-9]{2}[\-.](0[469]|11)[\-.](0[1-9]|[12][0-9]|30)|(18|19|20)[0-9]{2}[\-.](02)[\-.](0[1-9]|1[0-9]|2[0-8])|(((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)[\-.](02)[\-.]29
Ответ 6
Используя момент (не регулярное выражение), я сделал следующее:
Предполагая, что у вас есть дата ISO как строковое значение:
var isoDate = '2016-11-10';
var parsedIsoDate = moment(isoDate, ['YYYY-MM-DD'], true).format('YYYY-MM-DD');
if (parsedIsoDate !== isoDate) {
// Invalid date.
}
Ответ 7
^(((?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:(?:0?[13578]|1[02])(-)31)|(?:(?:0?[1,3-9]|1[0-2])(-)(?:29|30))))|(((?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))(-)(?:0?2(-)29))|((?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(-)(?:(?:0?[1-9])|(?:1[0-2]))(-)(?:0[1-9]|1\d|2[0-8]))))$
Попробуйте приведенное выше выражение Reg. Я попробовал несколько комбинаций и нашел, что работает.
Пожалуйста, проверьте, подходит ли это для вас.
Принимаемый формат: ГГГГ-ММ-ДД
Год принят от 1600
Ответ 8
Здравствуйте, найдите RegEx для вашего требования
- Имеет диапазон от 1800
- Сейчас выполняет правильную проверку даты с високосными годами
- Формат ДД/ММ/ГГГГ
- Неверная дата проверки
^ (? :(?: 31 (/) (?: 0 [13578] | 1 [02]))\1 | (:(?: 29 |? 30) (/) (?: 0 [13-9] | 1 [0-2])\2)) (:(?: 18 |? 19 | 20)\д {2}) $ |? ^ (?: 29 (/) 02\3 (:( :(? :(?: 18 | 19 | 20)) (?: 0 [48] | [2468] [048] | [13579] [26])))) $ |? ^ (?: 0 [1-9] | 1\д | 2 [0-8]) (/) (:(?: 0 [1-9]) | (:??? 1 [0-2]))\4 (:(?: 18 | 19 | 20)\д {2}) $
![enter image description here]()
Изображение и отладка RegEx на https://www.debuggex.com/
Тестирование:
- DD/MM/YYYY
-
01/12/190 Не соответствует -
29.02.1903 Не соответствует -
37/02/1903 не соответствует -
03.09.1703 Не соответствует -
03.09.2103 Не соответствует -
31.09.2103 Не соответствует - 29.02.1904 - Матч
- 01.12.1988 - Матч
Ответ 9
Это RegEx, который я использую для проверки даты на стороне клиента. Он имеет диапазон от 1000 до 2999, проверяет високосные годы и, возможно, временную часть. Разве это не великолепно:)
var r = /^(0[1-9]|1\d|2[0-8]|29(?=-\d\d-(?!1[01345789]00|2[1235679]00)\d\d(?:[02468][048]|[13579][26]))|30(?!-02)|31(?=-0[13578]|-1[02]))-(0[1-9]|1[0-2])-([12]\d{3})(\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d))?$/gm;
r.test('20-02-2013 10:01:07'); // true
r.test('29-02-1700'); // false
r.test('29-02-1604 14:01:45'); // true
r.test('29-02-1900 20:10:50'); // false
r.test('31-12-2000'); // true
r.test('31-11-2008 05:05:05'); // false
r.test('29-02-2004 05:01:23'); // true
r.test('24-06-2014 24:10:05'); // false
Ответ 10
Я пытался проверить YYYY-MM-DD, где YYYY может быть двухзначным, а MM и DD могут быть одинаковыми. Это то, что я придумал. Он рассматривает все века как високосные годы.
((\d\d)?\d\d-((0?(1|3|5|7|8)|10|12)-(31|30|[21]\d|0?[1-9])|(0?(4|6|9)|11)-(31|30|[21]\d|0?[1-9])|0?2-((2[0-8]|1\d)|0?[1-9]))|(\d\d)?((0|2|4|6|8)(0|4|8)|(1|3|5|7|9)(2|6))-0?2-29)
Ответ 11
Добавление моего ответа только для спорта - иначе я полностью согласен с @Jim.
Это будет соответствовать високосным годам, в том числе тем, у которых цифры меньше или больше 4.
^\d*((((^|0|[2468])[048])|[13579][26])00$)|((0[48]|(^0*|[2468])[048]|[13579][26]))$
Мини-тестовый пример в Ruby (^
заменен на \A
и $
на \Z
, потому что Ruby):
r = /\A\d*((((\A|0|[2468])[048])|[13579][26])00\Z)|((0[48]|(\A0*|[2468])[048]|[13579][26]))\Z/
100000.times do |year|
leap = year % 4 == 0 && ((year % 100 != 0) || (year % 400 == 0))
leap_regex = !year.to_s[r].nil?
if leap != leap_regex
print 'Assertion broken:', year, leap, leap_regex, "\n"
end
end
Ответ 12
Ниже Regex не работает на 29.10.1956
((0 [13578] | 1 [02]) 31/[0-9] {2} [/.].) | ((01 | 0 [3-9] | 1 [1-2])/./. [0-9] {2}) | ((0 [1-9] | 1 [0-2])/./[0-9] {2}) |. ((02) [/.] 29/.)
erike?
Ответ 13
((0 [13578] | 1 [02]) 31/[0-9] {2} [/.].) | ((01 | 0 [3-9] | 1 [1-2])/./.[0-9]{2})|((0[1-9]|1[0-2])/./.[0-9]{2})|((02)[/.] 29/.)
Ответ на короткую версию не работает в течение 10/29 и 10/30 в год, когда длинная версия работает ниже - это простая программа java script, которую я написал для тестирования
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class RegxDateTest {
public static void main(String[] args) {
// String to be scanned to find the pattern.
String line = "This order was placed for QT3000! OK?";
String pattern ="((0[13578]|1[02])[\\/.]31[\\/.](18|19|20)[0-9]{2})|((01|0[3-9]|1[1-2])[\\/.](29|30)[\\/.](18|19|20)[0-9]{2})|((0[1-9]|1[0-2])[\\/.](0[1-9]|1[0-9]|2[0-8])[\\/.](18|19|20)[0-9]{2})|((02)[\\/.]29[\\/.](((18|19|20)(04|08|[2468][048]|[13579][26]))|2000))";
// Create a Pattern object
Pattern r = Pattern.compile(pattern);
LocalDate startDate = new LocalDate("1950-01-01");
LocalDate endDate = new LocalDate("2020-01-01");
for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
{
if (date.toString("MM/dd/yyyy").matches(pattern)) {
// System.out.println("This date does match: " + date.toString("MM/dd/yyyy") );
}else{
System.out.println("This date does not match: " + date.toString("MM/dd/yyyy") );
}
}
String baddate1="02/29/2016";
if (baddate1.matches(pattern)) {
System.out.println("This date does match: " + baddate1 );
}else{
System.out.println("This date does not match: " + baddate1 );
}
System.out.println("alldone: " );
}
}