Pokonaj czyste wyrażenia regularne podczas sprawdzania poprawności dat ISO 8601

12

W ValiDate ISO 8601 firmy RX wyzwaniem było użycie tylko standardowych wyrażeń regularnych do sprawdzania poprawności standardowych formatów i wartości dat (pierwsze z nich jest częstym zadaniem dla RX, drugie było niezwykłe). Zwycięska odpowiedź wykorzystała 778 bajtów. To wyzwanie polega na pokonaniu go w dowolnym wybranym języku, ale bez specjalnych funkcji randkowych lub zajęć .

Wyzwanie

Znajdź najkrótszy kod, który

  1. zatwierdza każdą możliwą datę w proleptycznym kalendarzu gregoriańskim (która dotyczy także wszystkich dat przed jej pierwszym przyjęciem w 1582 r.),
  2. nie pasuje do żadnej nieprawidłowej daty i
  3. nie używa żadnych predefiniowanych funkcji, metod, klas, modułów lub podobnych do obsługi dat (i godzin), tzn. opiera się na operacjach łańcuchowych i numerycznych.

Wynik

Dane wyjściowe są zgodne z prawdą lub falsey. Nie trzeba generować ani konwertować daty.

Wejście

Dane wejściowe to pojedynczy ciąg znaków w dowolnym z 3 rozszerzonych formatów daty ISO 8601 - bez żadnych opóźnień.

Pierwsze dwa to ±YYYY-MM-DD(rok, miesiąc, dzień) i ±YYYY-DDD(rok, dzień). Oba wymagają specjalnej obudowy na dzień przestępny. Są naiwnie dobierane osobno przez te rozszerzone RX:

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

Trzeci format wejściowy to ±YYYY-wWW-D(rok, tydzień, dzień). Jest to skomplikowane ze względu na skomplikowany wzór tygodnia przestępnego.

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

Warunki

Rok przestępny w Proleptycznym kalendarzu gregoriańskim zawiera dzień przestępny, …-02-29 a zatem ma 366 dni, stąd też …-366istnieje. Dzieje się tak w każdym roku, którego (ewentualnie ujemna) liczba porządkowa jest podzielna przez 4, ale nie przez 100, chyba że jest również podzielna przez 400. Rok zerowy istnieje w tym kalendarzu i jest to rok przestępny.

Długie lata w tygodniu kalendarza ISO zawiera tydzień 53. …-W53-…, które mogłyby jeden perspektywie „ skok tydzień ”. Dzieje się tak we wszystkich latach, w których 1 stycznia jest czwartkiem, a dodatkowo we wszystkich latach przestępnych, w których jest środa. 0001-01-01i 2001-01-01są poniedziałki. Okazuje się, że występuje zwykle co 5 lub 6 lat, na pozór nieregularny wzór.

Rok ma co najmniej 4 cyfry. Lata z więcej niż 10 cyframi nie muszą być obsługiwane, ponieważ jest to wystarczająco blisko epoki wszechświata (około 14 miliardów lat). Wiodący znak plus jest opcjonalny, chociaż rzeczywisty standard sugeruje, że powinien być wymagany przez lata z więcej niż 4 cyframi.

Częściowe lub skrócone daty, tj. Z dokładnością poniżej jednego dnia, nie mogą być akceptowane. -We wszystkich przypadkach wymagane są oddzielne łączniki . (Te warunki wstępne pozwalają +zawsze być opcjonalnym).

Zasady

To jest golf golfowy. Najkrótszy kod w bajtach wygrywa. Wcześniejsza odpowiedź wygrywa remis.

Przypadki testowe

Prawidłowe testy

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 

Ostatni jest opcjonalnie ważny, tzn. Można wstawić spacje początkowe i końcowe w ciągach wejściowych.

Niepoprawne formaty

-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

Nieprawidłowe daty

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
Crissov
źródło
2
nie na temat, ale może się przydaje - Przepełnienie stosu: stackoverflow.com/questions/28020805/... (jeśli nie powinienem tego pisać, powiedz mi)
Daniele D
Co jeśli programista jest YEC (Young-Earth Creationist)?
Leaky Nun
O -0000-08-10czym jest arbitralna decyzja? Nie dopuszczasz roku jako ujemnego 0?
edc65
@ edc65 Tak, +0000-08-10i 0000-08-10powinno się go używać. Zauważ jednak, że zaakceptowana odpowiedź w wariancie wyrażenia regularnego tego wyzwania nie spełnia tego konkretnego przypadku testowego, więc tak naprawdę nie jest to warunek niepowodzenia (tj. Powinien , nie musi ).
Crissov
@KennyLau Następnie programista się myli .
Arcturus,

Odpowiedzi:

2

JavaScript (ES6), 236

236 bajtów dopuszczających 0 lat ujemnych ( -0000). Zwraca true lub false

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))

Dodanie zaznaczenia dla ujemnego 0 przecina 2 bajty, ale dodaje 13. Zauważ, że w javascript -0istnieje wartość liczbowa , a jej specjalną wartością jest 0, ale 1/-0jest -Infinity. Ta wersja zwraca 0 lub 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)

Test

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>

edc65
źródło