Kalahkan Pure Regular Expressions pada Memvalidasi Tanggal ISO 8601

12

Dalam ValiDate ISO 8601 oleh RX , tantangannya adalah hanya menggunakan ekspresi reguler standar untuk memvalidasi format dan nilai tanggal standar (yang pertama adalah pekerjaan umum untuk RX, yang terakhir tidak biasa). Jawaban yang menang menggunakan 778 byte. Tantangan ini adalah untuk mengalahkan yang menggunakan bahasa apa pun yang Anda pilih, tetapi tanpa fungsi tanggal khusus atau kelas .

Tantangan

Cari kode terpendek itu

  1. memvalidasi setiap tanggal yang mungkin dalam kalender Gregorian Proleptik (yang juga berlaku untuk semua tanggal sebelum adopsi pertama pada 1582),
  2. tidak cocok dengan tanggal yang tidak valid dan
  3. tidak menggunakan fungsi, metode, kelas, modul, atau yang serupa lainnya untuk menangani tanggal (dan waktu), yaitu mengandalkan operasi string dan numerik.

Keluaran

Keluaran itu benar atau salah. Tidak perlu mengeluarkan atau mengonversi tanggal.

Memasukkan

Input adalah string tunggal dalam salah satu dari 3 format tanggal ISO 8601 diperluas - tidak ada waktu.

Dua yang pertama adalah ±YYYY-MM-DD(tahun, bulan, hari) dan ±YYYY-DDD(tahun, hari). Keduanya membutuhkan casing khusus untuk hari kabisat. Mereka secara naif dicocokkan secara terpisah oleh RX yang diperluas ini:

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

Format input ketiga adalah ±YYYY-wWW-D(tahun, minggu, hari). Ini yang rumit karena pola minggu kabisat yang kompleks.

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

Kondisi

Sebuah tahun kabisat dalam kalender Gregorian proleptic berisi hari lompatan …-02-29 dan dengan demikian itu adalah 366 hari panjang, maka …-366ada. Ini terjadi di setiap tahun yang nomor ordinal (mungkin negatif) dapat dibagi dengan 4, tetapi tidak oleh 100 kecuali itu juga dapat dibagi dengan 400. Tahun nol ada di kalender ini dan itu adalah tahun kabisat.

Setahun yang panjang dalam kalender minggu ISO berisi minggu ke-53 …-W53-…, yang bisa disebut " minggu kabisat ". Ini terjadi di semua tahun di mana 1 Januari adalah hari Kamis dan juga di semua tahun kabisat di mana itu hari Rabu. 0001-01-01dan 2001-01-01hari Senin. Ternyata terjadi setiap 5 atau 6 tahun biasanya, dalam pola yang tampaknya tidak teratur.

Setahun memiliki setidaknya 4 digit. Tahun dengan lebih dari 10 digit tidak harus didukung, karena itu cukup dekat dengan usia alam semesta (sekitar 14 miliar tahun). Tanda tambah utama adalah opsional, meskipun standar aktual menunjukkan bahwa tanda tersebut harus diisi selama bertahun-tahun dengan lebih dari 4 digit.

Tanggal parsial atau terpotong, yaitu dengan kurang dari ketepatan hari, tidak boleh diterima. -Diperlukan tanda hubung yang terpisah dalam semua kasus. (Prasyarat ini memungkinkan untuk memimpin +agar selalu opsional.)

Aturan

Ini adalah kode-golf. Kode terpendek dalam byte menang. Jawaban sebelumnya memenangkan dasi.

Uji kasus

Tes yang valid

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 

Yang terakhir adalah opsional opsional, yaitu ruang memimpin dan tertinggal di string input dapat dipangkas.

Format tidak valid

-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

Tanggal tidak valid

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
sumber
2
di luar topik, tapi mungkin berguna - Stack Overflow: stackoverflow.com/questions/28020805/… (jika saya tidak boleh memposting itu, katakan padaku)
Daniele D
Bagaimana jika programmernya adalah YEC (Young-Earth Creationist)?
Leaky Nun
Tentang -0000-08-10apa tepatnya keputusan sewenang-wenang? Tidak mengizinkan tahun sebagai negatif 0?
edc65
@ edc65 Ya, +0000-08-10dan 0000-08-10sebagai gantinya harus digunakan. Catatan, meskipun, bahwa jawaban yang diterima di varian ekspresi reguler dari tantangan ini gagal ujian khusus ini, sehingga tidak benar-benar suatu kondisi gagal (yaitu harus , bukan keharusan ).
Crissov
@ KennyLau Maka programmer salah .
Arcturus

Jawaban:

2

JavaScript (ES6), 236

236 byte memungkinkan negatif 0 tahun ( -0000). Mengembalikan nilai true atau 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))

Menambahkan tanda centang untuk negatif 0 memotong 2 byte tetapi menambahkan 13. Perhatikan bahwa dalam javascript nilai numerik -0ada, dan itu adalah casing khusus untuk menjadi sama dengan 0, tetapi 1/-0adalah -Infinity. Versi ini mengembalikan 0 atau 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)

Uji

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
sumber