Побийте чисті регулярні вирази на валідації дат ISO 8601


12

У ValiDate ISO 8601 від RX викликом було використання лише стандартних регулярних виразів для перевірки стандартних форматів дат і значень (перший - це звичайна робота для RX, другий - незвичний). У виграшній відповіді використано 778 байт. Це завдання полягає в тому, щоб перемогти цю мову, використовуючи будь-яку мову на ваш вибір, але без спеціальних функцій дати чи класів .

Виклик

Знайдіть найкоротший код, який

  1. підтверджує кожну можливу дату в пролептичному григоріанському календарі (що стосується також усіх дат до його першого прийняття в 1582 р.),
  2. не відповідає жодній недійсній даті та
  3. не використовує жодних заздалегідь визначених функцій, методів, класів, модулів або подібних даних для обробки дат (і часу), тобто покладається на рядкові та числові операції.

Вихідні дані

Вихідний результат - фальшивий або фальсийний. Не потрібно виводити чи конвертувати дату.

Вхідні дані

Введення - це один рядок у будь-якому з 3 розширених форматів дати ISO 8601 - не раз.

Перші два - ±YYYY-MM-DD(рік, місяць, день) та ±YYYY-DDD(рік, день). Обом потрібен спеціальний кожух для високосного дня. Вони наївно узгоджуються окремо цими розширеними RX:

(?<year>[+-]?\d{4,})-(?<month>\d\d)-(?<day>\d\d)
(?<year>[+-]?\d{4,})-(?<doy>\d{3})

Третім форматом введення є ±YYYY-wWW-D(рік, тиждень, день). Він складний через складну схему високосного тижня.

(?<year>[+-]?\d{4,})-W(?<week>\d\d)-(?<dow>\d)

Умови

Високосний рік в календарі преждевренного григоріанським містить високосний день …-02-29 і , таким чином , він довго 366 днів, отже , …-366існує. Це відбувається в будь-який рік, чий (можливо негативний) порядковий номер ділиться на 4, але не на 100, якщо він також не ділиться на 400. У цьому календарі існує нульовий рік, і це високосний рік.

Довгий рік в тижневому календарі ISO містить 53 - й тиждень …-W53-…, що один може термін на « стрибок тиждень ». Це відбувається в усі роки, коли 1 січня - четвер, а також у всі високосні роки, де середа. 0001-01-01і 2001-01-01є понеділками. Виявляється, це відбувається кожні 5 або 6 років, як правило, за нерегулярною схемою.

На рік принаймні 4 цифри. Роки з більш ніж 10 цифрами не повинні підтримуватися, оскільки це досить близько до віку Всесвіту (приблизно 14 мільярдів років). Провідний знак плюс необов’язковий, хоча фактичний стандарт передбачає, що його потрібно вимагати роками з більш ніж 4 цифрами.

Часткові або усічені дати, тобто з точністю до дня, не повинні прийматися. Розділові дефіси -потрібні у всіх випадках. (Ці передумови дають можливість вести +завжди не обов'язково.)

Правила

Це код-гольф. Виграє найкоротший код у байтах. Раніша відповідь виграла внічию.

Тестові справи

Дійсні тести

2015-08-10
2015-10-08
12015-08-10
-2015-08-10
+2015-08-10
0015-08-10
1582-10-10
2015-02-28
2016-02-29
2000-02-29
0000-02-29
-2000-02-29
-2016-02-29
+2016-02-29
200000-02-29
-200000-02-29
+200000-02-29
2016-366
2000-366
0000-366
-2000-366
-2016-366
+2016-366
2015-081
2015-W33-1
2015-W53-7
+2015-W53-7
+2015-W33-1
-2015-W33-1
 2015-08-10 

Останній необов'язково дійсний, тобто провідні та кінцеві проміжки у вхідних рядках можуть бути оброблені.

Недійсні формати

-0000-08-10     # that's an arbitrary decision
15-08-10        # year is at least 4 digits long
2015-8-10       # month (and day) is exactly two digits long, i.e. leading zero is required
015-08-10       # year is at least 4 digits long
20150810        # though a valid ISO format, we require separators; could also be interpreted as a 8-digit year
2015 08 10      # separator must be hyphen-minus
2015.08.10      # separator must be hyphen-minus
2015–08–10      # separator must be hyphen-minus
2015-0810
201508-10       # could be October in the year 201508
2015 - 08 - 10  # no internal spaces allowed
2015-w33-1      # letter ‘W’ must be uppercase
2015W33-1       # it would be unambiguous to omit the separator in front of a letter, but not in the standard
2015W331        # though a valid ISO format we require separators
2015-W331
2015-W33        # a valid ISO date, but we require day-precision
2015W33         # though a valid ISO format we require separators and day-precision
2015-08         # a valid ISO format, but we require day-precision
201508          # a valid but ambiguous ISO format
2015            # a valid ISO format, but we require day-precision

Недійсні дати

2015-00-10  # month range is 1–12
2015-13-10  # month range is 1–12
2015-08-00  # day range is 1–28 through 31
2015-08-32  # max. day range is 1–31
2015-04-31  # day range for April is 1–30
2015-02-30  # day range for February is 1–28 or 29
2015-02-29  # day range for common February is 1–28
2100-02-29  # most century years are non-leap
-2100-02-29 # most century years are non-leap
2015-000    # day range is 1–365 or 366
2015-366    # day range is 1–365 in common years
2016-367    # day range is 1–366 in leap years
2100-366    # most century years are non-leap
-2100-366   # most century years are non-leap
2015-W00-1  # week range is 1–52 or 53
2015-W54-1  # week range is 1–53 in long years
2016-W53-1  # week range is 1–52 in short years
2015-W33-0  # day range is 1–7
2015-W33-8  # day range is 1–7

2
поза темою, але, можливо, корисно - Переповнення стека: stackoverflow.com/questions/28020805/… (якщо я не повинен цього публікувати, скажіть мені)
Daniele D

Що робити, якщо програмістом є YEC (Young-Earth Creationist)?
Лина монашка

Про -0000-08-10що таке довільність у довільному рішенні? Не допускати рік як мінус 0?
edc65

@ edc65 Так, +0000-08-10і його 0000-08-10слід використовувати замість цього. Зауважте, що прийнята відповідь у варіанті регулярного вираження цього виклику не відповідає саме цій тестовій справі, тому це насправді не відповідає умові (тобто не , а не обов'язково ).
Крісов

@KennyLau Тоді програміст помиляється .
Арктур

Відповіді:


2

JavaScript (ES6), 236

236 байт, що дозволяє негативно 0 рік ( -0000). Повертає істинне або хибне

s=>!!([,y,w,d]=s.match(/^([+-]?\d{4,})(-W?\d\d)?(-\d{1,3})$/)||[],n=y%100==0&y%400!=0|y%4!=0,l=((l=y-1)+8-~(l/4)+~(l/100)-~(l/400))%7,l=l==5|l==4&!n,+d&&(-w?d>`0${2+n}0101001010`[~w]-32:w?(w=w.slice(2),w>0&w<(53+l)&d>-8):d[3]&&d>n-367))

Додаючи чек на від'ємний 0 вирізати 2 байти, але додаємо 13. Зауважте, що у javascript числове значення -0існує, і воно має спеціальний зразок, який дорівнює 0, але 1/-0є -Infinity. Ця версія повертає 0 або 1

s=>([,y,w,d]=s.match(/^([+-]?\d{4,})(-W?\d\d)?(-\d{1,3})$/)||[],n=y%100==0&y%400!=0|y%4!=0,l=((l=y-1)+8-~(l/4)+~(l/100)-~(l/400))%7,l=l==5|l==4&!n,+d&&(-w?d>`0${2+n}0101001010`[~w]-32:w?(w=w.slice(2),w>0&w<(53+l)&d>-8):d[3]&&d>n-367))&!(!+y&1/y<0)

Тест

Check=
  s=>!! // to obtain a true/false 
  (
    // parse year in y, middle part in w, day in d
    // day will be negative with 1 or 3 numeric digits and could be 0
    // week will be '-W' + 2 digits
    // month will be negative with2 digits and could be 0
    // if the date is in format yyyy-ddd, then w is empty
    [,y,w,d] = s.match(/^([+-]?\d{4,})(-W?\d\d)?(-\d{1,3})$/) || [],
    n = y%100==0 & y%400!=0 | y%4!=0, // n: not leap year
    l = ((l=y-1) + 8 -~(l/4) +~(l/100) -~(l/400)) % 7, 
    l = l==5| l==4 & !n, // l: long year (see http://mathforum.org/library/drmath/view/55837.html)
    +d && ( // if d is not empty and not 0
     -w // if w is numeric and not 0, then it's the month (negative)
     ? d > `0${2+n}0101001010`[~w] - 32 // check month length (for leap year too)
      : w // if w is not empty, then it's the week ('-Wnn')
        ? ( w = w.slice(2), w > 0 & w < (53+l) & d >- 8) // check long year too
        : d[3] && d > n-367 // else d is the prog day, has to be 3 digits and < 367 o 366
    )
  )

console.log=x=>O.textContent += x +'\n'

OK=['1900-01-01','2015-08-10','2015-10-08','12015-08-10','-2015-08-10','+2015-08-10'
,'0015-08-10','1582-10-10','2015-02-28','2016-02-29','2000-02-29'
,'0000-02-29','-2000-02-29','-2016-02-29','+2016-02-29','200000-02-29'
,'-200000-02-29','+200000-02-29','2016-366','2000-366','0000-366'
,'-2000-366','-2016-366','+2016-366','2015-081','2015-W33-1'
,'2015-W53-7','+2015-W53-7','+2015-W33-1','-2015-W33-1','2015-08-10']

KO=['-0000-08-10','15-08-10','2015-8-10','015-08-10','20150810','2015 08 10'
,'2015.08.10','2015–08–10','2015-0810','201508-10','2015 - 08 - 10','2015-w33-1'
,'2015W33-1','2015W331','2015-W331','2015-W33','2015W33','2015-08','201508'
,'2015','2015-00-10','2015-13-10','2015-08-00','2015-08-32','2015-04-31'
,'2015-02-30','2015-02-29','2100-02-29','-2100-02-29','2015-000'
,'2015-366','2016-367','2100-366','-2100-366','2015-W00-1'
,'2015-W54-1','2016-W53-1','2015-W33-0','2015-W33-8']

console.log('Valid')
OK.forEach(x=>console.log(Check(x)+' '+x))
console.log('Not valid')
KO.forEach(x=>console.log(Check(x)+' '+x))
<pre id=O></pre>

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.