Schlagen Sie reine reguläre Ausdrücke bei der Validierung von ISO 8601-Daten

12

In Validate ISO 8601 von RX , die Herausforderung nur Standard reguläre Ausdrücke zu verwenden war zu validieren Standard Datumsformate und Werte (ersteres ist eine gemeinsame Aufgabe für RX, war dieser ungewöhnlich). Die Gewinnerantwort verwendete 778 Bytes. Diese Herausforderung besteht darin, diese mit einer beliebigen Sprache Ihrer Wahl zu meistern, jedoch ohne spezielle Datumsfunktionen oder Klassen .

Herausforderung

Finden Sie den kürzesten Code dafür

  1. validiert jedes mögliche Datum im proleptischen Gregorianischen Kalender (der auch für alle Daten vor seiner ersten Annahme im Jahr 1582 gilt),
  2. stimmt mit keinem ungültigen Datum überein und
  3. verwendet keine vordefinierten Funktionen, Methoden, Klassen, Module oder ähnliches für die Behandlung von Datum (und Uhrzeit), dh stützt sich auf Zeichenfolgen und numerische Operationen.

Ausgabe

Die Ausgabe ist wahr oder falsch. Es ist nicht erforderlich, das Datum auszugeben oder zu konvertieren.

Eingang

Die Eingabe ist eine einzelne Zeichenfolge in einem der drei erweiterten ISO 8601- Datumsformate - keine Zeit.

Die ersten beiden sind ±YYYY-MM-DD(Jahr, Monat, Tag) und ±YYYY-DDD(Jahr, Tag). Beide benötigen ein spezielles Gehäuse für den Schalttag. Sie werden von diesen erweiterten Empfängern naiv separat abgeglichen:

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

Das dritte Eingabeformat ist ±YYYY-wWW-D(Jahr, Woche, Tag). Es ist das komplizierte wegen des komplexen Schaltwochenmusters.

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

Bedingungen

Ein Schaltjahr im proleptischen Gregorianischen Kalender enthält den Schalttag …-02-29 und ist somit 366 Tage lang und …-366existiert daher . Dies geschieht in jedem Jahr, dessen (möglicherweise negative) Ordnungszahl durch 4 teilbar ist, jedoch nicht durch 100, es sei denn, sie ist auch durch 400 teilbar. Das Jahr Null ist in diesem Kalender vorhanden und es ist ein Schaltjahr.

Ein langes Jahr im ISO-Wochenkalender enthält eine 53. Woche …-W53-…, die man als „ Schaltwoche “ bezeichnen könnte. Dies geschieht in allen Jahren, in denen der 1. Januar ein Donnerstag ist, und zusätzlich in allen Schaltjahren, in denen es ein Mittwoch ist. 0001-01-01und 2001-01-01sind montags. Es stellt sich heraus, dass es normalerweise alle 5 oder 6 Jahre in einem scheinbar unregelmäßigen Muster auftritt.

Ein Jahr hat mindestens 4 Ziffern. Jahre mit mehr als 10 Ziffern müssen nicht unterstützt werden, da dies nahe genug am Alter des Universums liegt (ca. 14 Milliarden Jahre). Das führende Pluszeichen ist optional, obwohl der tatsächliche Standard vorschlägt, dass es für Jahre mit mehr als 4 Ziffern erforderlich sein sollte.

Teilweise oder abgeschnittene Daten, dh mit weniger als Tagesgenauigkeit, dürfen nicht akzeptiert werden. -In jedem Fall sind separate Bindestriche erforderlich. (Diese Voraussetzungen ermöglichen es, dass das Führen +immer optional ist.)

Regeln

Das ist Code-Golf. Der kürzeste Code in Bytes gewinnt. Frühere Antwort gewinnt ein Unentschieden.

Testfälle

Gültige Tests

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 

Der letzte ist optional gültig, dh führende und nachfolgende Leerzeichen in Eingabezeichenfolgen können gekürzt werden.

Ungültige Formate

-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

Ungültige Daten

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
quelle
2
Off Topic, aber vielleicht nützlich - Stack Overflow: stackoverflow.com/questions/28020805/… (wenn ich das nicht posten sollte, sag es mir)
Daniele D
Was ist, wenn der Programmierer ein YEC (Young-Earth Creationist) ist?
Undichte Nonne
Über -0000-08-10was exaclty die willkürliche Entscheidung? Das Jahr nicht als negative 0 zulassen?
edc65
@ edc65 Ja, +0000-08-10und 0000-08-10sollte stattdessen verwendet werden. Beachten Sie jedoch, dass die akzeptierte Antwort in der regulären Ausdrucksvariante dieser Herausforderung diesen bestimmten Testfall nicht besteht, sodass es sich nicht wirklich um eine fehlerhafte Bedingung handelt (dh ein Soll , kein Muss ).
Crissov
@KennyLau Dann ist der Programmierer falsch .
Arcturus

Antworten:

2

JavaScript (ES6), 236

236 Bytes erlauben negative 0 Jahre ( -0000). Gibt true oder false zurück

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

Wenn Sie die Prüfung auf negative 0 hinzufügen, werden 2 Bytes abgeschnitten, aber 13 hinzugefügt. Beachten Sie, dass in Javascript der numerische Wert -0vorhanden ist und er in einem speziellen Fall gleich 0 ist, dies jedoch 1/-0ist -Infinity. Diese Version gibt 0 oder 1 zurück

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)

Prüfung

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
quelle