RX tarafından ValiDate ISO 8601


16

Meydan okuma

En kısa normal ifadeyi bulun

  1. Proleptik Gregoryen takvimindeki (1582'de ilk kabulünden önceki tüm tarihler için de geçerlidir) olası her tarihi doğrular, yani eşleşir ve
  2. geçersiz tarihle eşleşmiyor.

Çıktı

Dolayısıyla çıktı doğru ya da yanlıştır.

Giriş

Giriş, 3 genişletilmiş ISO 8601 tarih biçiminden herhangi birinde - hiçbir zaman yok.

İlk ikisi ±YYYY-MM-DD(yıl, ay, gün) ve ±YYYY-DDD(yıl, gün). Her ikisinin artık gün için özel kasaya ihtiyacı var. Bu genişletilmiş RX'ler tarafından ayrı ayrı eşleştirilirler:

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

Üçüncü girdi biçimi ±YYYY-wWW-D(yıl, hafta, gün) şeklindedir. Karmaşık sıçrama haftası paterni nedeniyle karmaşık olanıdır.

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

Üçünün tümü için temel, ancak yetersiz bir geçerlilik kontrolü şöyle görünecektir:

[+-]?\d{4,}-((0\d|1[0-2])-([0-2]\d|3[01]) ↩
            |([0-2]\d\d|3[0-5]\d|36[0-6]) ↩
            |(W([0-4]\d|5[0-3])-[1-7]))

Koşullar

Proleptik Gregoryen takvimindeki bir sıçrama yılı artık günü içerir …-02-29ve bu nedenle 366 gün uzunluğundadır …-366. Bu, sıra numarası 4 ile bölünebilir, ancak 400 ile bölünemezse 100 ile değil herhangi bir yılda olur. Bu takvimde sıfır yılı var ve artık bir yıl.

Bir uzun yıl ISO hafta takvimde biri “diyebileceğimiz bir 53 hafta, içerdiği sıçrama hafta ”. Bu, 1 Ocak'ın Perşembe olduğu tüm yıllarda ve ek olarak Çarşamba olduğu tüm artık yıllarda olur. Genellikle 5 veya 6 yılda bir, görünüşte düzensiz bir düzende ortaya çıkıyor.

Bir yılda en az 4 basamak vardır. 10 basamaktan fazla yılların desteklenmesi gerekmez, çünkü bu evrenin yaşına yeterince yakındır (yaklaşık 14 milyar yıl). Öncü artı işareti isteğe bağlıdır, ancak gerçek standart 4 basamaktan fazla yıllar için gerekli olduğunu göstermektedir.

Kısmi veya kesilmiş tarihler, yani gün hassasiyetinden daha az olan tarihler kabul edilmemelidir.

Örneğin ayda tarih gösterimde, parçaları, do not başvurulan edilebilecek bir grup tarafından eşleşti gerekmektedir.

kurallar

Bu kod golfü. Yürütülen kod olmadan en kısa normal ifade kazanır. Güncelleme: Özyineleme ve dengeli gruplar gibi özellikleri kullanabilirsiniz, ancak karakter sayımı ile çarpılan 10 katına kadar para cezası verilir! Bu, şimdi Sabit kod golf: 7 ile bölünebilirlik için Regex kurallarından farklıdır . Daha önceki cevap bir kravat kazanır.

Test senaryoları

Geçerli testler

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
200000-02-29
2016-366
2000-366
0000-366
-2016-366
-2000-366
2015-081
2015-W33-1
2015-W53-7
 2015-08-10 

Sonuncusu isteğe bağlı olarak geçerlidir, yani giriş dizelerindeki ön ve arka boşluklar kesilebilir.

Geçersiz biçimler

-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

Geçersiz tarihler

2015        # a valid ISO format, but we require day-precision
2015-08     # 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
Normal ifade dili belirtilmediğinden bu soru iyi tanımlanmamış.
orlp

1
@orlp Belirtilmezse, seçim sınırlı değildir. Bilerek “regex” ya da “RX” yazdım, bu yüzden kişi özyinelemeye izin veren lehçeler vb.
Crissov

Normal ifade dilini sınırlamanızı şiddetle tavsiye ederim, çünkü bir yarışmacının sadece temelde daha güçlü bir dil tarafından önemsiz bir şekilde dövülmesi için bir çözüm üzerinde saatlerce çalışması çok ekşi olacaktır. Dili, normal ifadelerin (DFA gibi) gerçek CS tanımıyla sınırlayacak olsaydınız, sorun ilginç bir optimizasyon yanıtı haline gelir.
orlp

ISO-8601 tarihlerini düzenli ifadeler kullanarak doğrulamak aslında iş için yapmam gereken bir şey. Ama orlp ile aynı fikirdeyim, bence burada bir dil gerekli.
Alex

1
Regex Perl 6'daki Method'dan devralınır, bu yüzden kendisi bir yürütülebilir kod biçimidir.
Brad Gilbert b2gills

Yanıtlar:


4

PCRE (ayrıca Perl), 778 bayt

/^([+-]?\d*((([02468][048]|[13579][26]|\d\d(?!00))([02468][048]|[13579][26]))|\d{4}(?!-02-29|-366))-((?!02-3|(0[469]|11)-31|000)((0[1-9]|1[012])-(0[1-9]|[12]\d|30|31)|([012]\d\d|3([0-5]\d|6[0-6])))|(W(?!00)([0-4]\d|51|52)-[1-7]))|((\+?\d*([02468][048]|[13579][26])|-\d*([02468][159]|[13579][37]))(04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99)|(\+?\d*([02468][159]|[13579][37])|-\d*([02468][26]|[13579][048]))(05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95)|(\+?\d*([02468][26]|[13579][048])|-\d*([02468][37]|[13579][159]))(01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96)|\+?\d*(([02468][37]|[13579][159])(03|14|20|25|31|36|42|53|59|64|70|76|81|87|92|[049]8))|-\d*(([02468][048]|[13579][26])([059]2|08|13|19|24|30|36|41|47|58|64|69|75|80|86|97)))-W53-[1-7])$/

Herhangi bir bayrağa dayanmadığını göstermek için bayt sayısına ayırıcılar ekledim.

O mu değil gibi diğer dizeleri içinde geçerli tarihleri, maç 1234-56-89 2016-02-29 9876-54-32. Normal ifade, yıl için en fazla 10 basamak kontrol edilmeden daha kısadır.

Yorumlarla genişletildi:

/^  # Start of pattern (no leading space)
  (
    # YEAR
    # Optional sign and digits if more than 4 in year
    [+-]?\d*(
      # Years 00??, 04??, 08?? ... 92??, 96?? OR dd not followed by 00
      # followed by 00, 04, 08 ... 92, 96 OR
      (([02468][048]|[13579][26]|\d\d(?!00))([02468][048]|[13579][26])) |
      # any year not followed by 29 February or day 366
      \d{4}(?!-02-29|-366)
    # dash
    ) -
    # MONTH AND DAY, or DAY OF YEAR, or WEEK OF YEAR AND DAY if less than 53 weeks
    (
      # Not (30 or 31 February OR 31 April, June, September or December OR day 0)
      (?!02-3|(0[469]|11)-31|000)
      (
        # Month         dash         day         OR
        (0[1-9]|1[012]) - (0[1-9]|[12]\d|30|31) |
        # 001-299 OR 300-359 OR 360-366
        ([012]\d\d | 3([0-5]\d | 6[0-6]))
      # OR
      ) |
      (
        # W    01-52    dash    1-7
        W(?!00)([0-4]\d|51|52)-[1-7]
      )
    # OR
    ) |
    # WEEK OF YEAR AND DAY only if week is 53
    (
      # Optional plus and extra year digits
      \+?\d*(
        # Years +0303 - +9998
        ([02468][37]|[13579][159])(03|14|20|25|31|36|42|53|59|64|70|76|81|87|92|[049]8)
      ) |
      # Minus and extra year digits
      -\d*(
        # Years -0002 - -9697
        ([02468][048]|[13579][26])([059]2|08|13|19|24|30|36|41|47|58|64|69|75|80|86|97)
      ) |
      # Years +0004 - +9699, -0104 - -9799
      (\+?\d*([02468][048]|[13579][26])|-\d*([02468][159]|[13579][37]))
          (04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99) |
      # Years +0105 - +9795, -0205 - -9895
      (\+?\d*([02468][159]|[13579][37])|-\d*([02468][26]|[13579][048]))
          (05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95) |
      # Years +0201 - +9896, -0301 - -9996
      (\+?\d*([02468][26]|[13579][048])|-\d*([02468][37]|[13579][159]))
          (01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96)
    # dash W 53 dash 1-7
    )-W53-[1-7]
  # End of pattern (no trailing space)
  )$/x

Henüz her şeyi kontrol etmedim, ancak (?!…)çözümümle karşılaştırıldığında ifadelerle en fazla bayt kazandığınız görülüyor .
Crissov

1
@Crissov (?!…)İfadelerin her biri yalnızca birkaç bayt tasarruf eder. Pozitif / negatif yıl haftası / haftanın günü yıl modellerinden üçünü bir araya getirerek çok sayıda bayt azalttım. Sonuncusu birbirine uymuyor. Bu yüzden 5'e kadar 8 uzun alt desen aldım. Ayrıca, daha okunabilir seçenek için gittiğim ile |20|25|aynı uzunluktadır |2[05]|.
CJ Dennis

Bu ifade, test senaryosuyla -0000-08-10 eşleşir ␠2015-08-10␠ve önde gelen ve arkadaki boşluklarla eşleşmez , ancak her ikisi de keyfi kararlar veya isteğe bağlı özellikler olduğundan kaymasına izin vereceğim.
Crissov

Bu çözümün W50 içindeki tarihler için bir hata olduğunu düşünüyorum.
Crissov

W(?!00)([0-4]\d|51|52)-[1-7]eşdeğer bir şey olmalı W(?!00)([0-4]\d|5[0-2])-[1-7]. Bu, uzunluğa bir karakter ekler. 779
Crissov

9

PCRE: 603 940 947 949 956 bayt

^\s*[+-]?(\d{4,10}-((00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5])|(0[1-9]|1[0-2])-(0[1-9]|1\d|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31|W(0[1-9]|[1-4]\d|5[0-2])-[1-7]))|((\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))-(366|02-29))|(\+?\d{0,6}(([02468][048]|[13579][26])([26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9)|([02468][159]|[13579][37])(50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9)|([02468][26]|[13579][048])([48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29)|([02468][37]|[13579][159])([27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9))|-\d{0,6}(([02468][048]|[13579][26])(0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27])|([02468][159]|[13579][37])(0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39])|([02468][26]|[13579][048])(0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95)|([02468][37]|[13579][159])(0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16])))-W53-[1-7]\s*$

Not: Bazı parantez çiftleri atılabilir.

4'e bölünebilirlik

4'ün katları basit bir desende tekrarlanır:

  • 00, 04, 08, 12, 16,
    20, 24, 28, 32, 36,
    40, 44, 48, 52, 56,
    60, 64, 68, 72, 76,
    80, 84, 88, 92, 96, ...

Bu veya tersi, iki basamaklı tüm sayılar için baştaki sıfıra sahip aynı şekilde basit bir normal ifade ile eşleştirilebilir:

(?<divisible-by-four>[13579][26]|[02468][048])
(?<not-divisible-by-four>[13579][048]|[02468][26]|\d[13579])

Tek ve çift rakamlar ( \ove gibi \e) için karakter sınıfları varsa, bazı baytlar kurtarabilirdi , ancak farkında olduğum kadar değil.

yıl

Bu ifade, Jülyen takvimi için yeterli olacaktır, ancak Gregoryen sıçrama yılı tespiti 00, 4'e kadar yüzyıl bölünebilirliği ile özel durum takibi gerektirir:

(?<leap-year>[+-]?(\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))
(?<year>[+-]?\d{4,10})

Bunun için yasadışı -0000-…( -00000-…vb. İle birlikte ) veya 4'ten fazla basamaklı pozitif yıl sayıları için artı işaretini zorunlu kılmak için bazı değişiklikler yapılması gerekir . İkincisi oldukça basit olurdu, ancak gerekli değildir:

(?<leap-year>([+-]?(\d\d([13579][26]|[2468][048]|0[48])|(([13579][26]|[02468][048])00)))|([+-](\d{3,8}([13579][26]|[2468][048]|0[48])|(\d{1,6}([13579][26]|[02468][048])00))))
(?<year>([+-]?\d{4})|([+-]\d{5,10}))

Yılın günü

Üç basamaklı ordinal tarihler oldukça basittir, sadece yılları kısıtlamak -366(ve izin vermemek -000) zorundayız .

(?<ordinal-day>-(00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5]))
(?<ordinal-leap-day>-366)

Yılın ayı günü

31 gün olan yedi ay 01Ocak, 03Mart, 05Mayıs, 07Temmuz, 08Ağustos, 10Ekim ve 12Aralık aylarıdır . Sadece dört ayın tam 30 günü var, 04Nisan, 06Haziran, 09Eylül ve 11Kasım. Son olarak, 02Şubat ayının ortak yıllarda 28 gün ve artık yıllarda 29 gün vardır. Biz ilk her zaman geçerli gün için geçerli bir ifade oluşturabilirsiniz 01içinden 28ve daha sonra özel durumları ekleyin.

(?<month-day>-(0[1-9]|1[0-2])-(0[1-9]|1\d|2[0-8]))
(?<short-month-day>-(0[13-9]|1[0-2])-(29|30))
(?<long-month-day>-(0[13578]|1[02])-31)
(?<month-leap-day>-02-29)

Ne ay ne de gün 00önceki bir sürümün kapsamına girmemelidir.

Yılın haftası günü

Tüm yıllara 52 hafta dahildir

(?<week-day>-W(0[1-9]|[1-4]\d|5[0-2])-[1-7])

-W53400 yıllık bir döngüde tekrarlanan uzun yıllar , örneğin mevcut döngü için 2000 ekleyin ve mevcut girişi üçüncü girişte bulun:

  • 004, 009, 015, 020, 026, 032, 037, 043, 048, 054, 060, 065, 071, 076, 082, 088, 093, 099,
  • 105, 111, 116, 122, 128, 133, 139, 144, 150, 156, 161, 167, 172, 178, 184, 189, 195,
  • 201, 207, 212, 218, 224, 229, 235, 240, 246, 252, 257, 263, 268, 274, 280, 285, 291, 296,
  • 303, 308, 314, 320, 325, 331, 336, 342, 348, 353, 359, 364, 370, 376, 381, 387, 392, 398.

Dört yüzyılın her birinin kendine özgü bir deseni vardır. Muhtemelen optimizasyon için fazla yer yoktur.

  1. 04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99
  2. 05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95
  3. 01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96
  4. 03|08|14|20|25|31|36|42|48|53|59|64|70|76|81|87|92|98

İki bayt kaydedebildiğimizi öğrenmek için her iki rakama göre gruplayabiliriz:

  • 1. basamağa göre gruplandırılmıştır.
    1. 0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39]
    2. 05|1[16]|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95
    3. 0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]
    4. 0[38]|14|2[05]|3[16]|4[28]|5[39]|64|7[06]|8[17]|9[28]
  • 2. basamağa göre gruplandırılmıştır.
    1. [26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9
    2. 50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9
    3. [48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29
    4. [27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9

Yüzyıl sayısı, bölünebilirlik ifadesinin bir varyasyonu ile kolayca yeniden eşleştirilir.

  • 1. yüzyıl: [02468][048]|[13579][26]
  • 2. yüzyıl: [02468][159]|[13579][37]
  • 3. yüzyıl: [02468][26]|[13579][048]
  • 4. yüzyıl: [02468][37]|[13579][159]

Şimdiye kadar, bu sadece sıfır yılı da içeren pozitif yıllar için işe yarıyor. Negatif yıllar için, yukarıdaki listeden değerleri 400'den çıkarmak ve gerisini tekrar yapmak zorundayız, çünkü desen simetrik değildir.

  1. 02|08|13|19|24|30|36|41|47|52|58|64|69|75|80|86|92|97
  2. 04|09|15|20|26|32|37|43|48|54|60|65|71|76|82|88|93|99
  3. 05|11|16|22|28|33|39|44|50|56|61|67|72|78|84|89|95
  4. 01|07|12|18|24|29|35|40|46|52|57|63|68|74|80|85|91|96

veya

  1. 0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27]
  2. 0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39]
  3. 0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95
  4. 0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]

Hepsini bir araya koy

Herhangi bir yıl

[+-]?\d{4,10}-((00[1-9]|0[1-9]\d|[12]\d\d|3[0-5]\d|36[0-5])|(0[1-9]|1[0-2])-(0[1-9]|1\d|2[0-8])|(0[13-9]|1[0-2])-(29|30)|(0[13578]|1[02])-31|W(0[1-9]|[1-4]\d|5[0-2])-[1-7])

Artık gün yıl ilaveleri

[+-]?(\d{2,8}([13579][26]|[2468][048]|0[48])|(\d{0,6}([13579][26]|[02468][048])00))-(366|02-29)

Artık hafta yılı ilaveleri

+?\d{0,6}(([02468][048]|[13579][26])([26]0|71|[38]2|[49]3|[05]4|15|[27]6|37|[48]8|[09]9)|([02468][159]|[13579][37])(50|[16]1|[27]2|33|[48]4|[09]5|[15]6|67|[27]8|[38]9)|([02468][26]|[13579][048])([48]0|[09]1|[15]2|63|[27]4|[38]5|[49]6|[05]7|[16]8|29)|([02468][37]|[13579][159])([27]0|[38]1|[49]2|[05]3|[16]4|25|[37]6|87|[049]8|[5]9))-W53-[1-7]
-\d{0,6}(([02468][048]|[13579][26])(0[28]|1[39]|24|3[06]|4[17]|5[28]|6[49]|75|8[06]|9[27])|([02468][159]|[13579][37])(0[49]|15|2[06]|3[27]|4[38]|54|6[05]|7[16]|8[28]|9[39])|([02468][26]|[13579][048])(0[51]|16|2[28]|3[39]|44|5[06]|6[17]|7[28]|8[49]|95)|([02468][37]|[13579][159])(0[17]|1[28]|2[49]|35|4[06]|5[27]|6[38]|74|8[05]|9[16]))-W53-[1-7]

Deseniniz başlangıçta ve bitişte sabitlenmediğinden, aksi takdirde geçersiz bir dize içindeki geçerli tarihlerle eşleşir.
CJ Dennis

@CJDennis Bu doğru, şimdi iki karakteri ekleyeceğim.
Crissov

Ayrıca isteğe bağlı önde gelen ve sondaki boşlukları da ekledim \s*.
Crissov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.