seth: Regular Expression für ein Datum

Beitrag lesen

gudn tach!

-?\d*(?:(?:(?:(?:[02468][048]|[13579][26])(?:[02468][048]|[13579][26])|(?:[02468][^048]|[13579][^26])(?:0[48]|[2468][048]|[13579][26]))[-./]?02[-./]?(?:0[1-9]|[12]\d)|(?:(?:[02468][^048]|[13579][^26])00|\d\d(?:[02468][^048]|[13579][^26]))[-./]?02[-./]?(?:0[1-9]|1\d|2[0-8]))|\d{4}[-./]?(?:(?:0[13578]|1[02])[-./]?(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)[-./]?(?:0[1-9]|[12]\d|30)))

geil!
aaaber es sind noch immer fehler drin, weil durch die negierten zeichenklassen z.b. '2a04-02-01' zugelassen wird.

-?\d*                                        # für die Ewigkeit

lol! wenn also 9999-12-31 kurz nach 23:59 alles den bach runter geht, weil niemand der grossen betriebssystem-hersteller an das y10k-problem dachte, laeuft dein regexp noch munter weiter und streckt der anderen software die zunge heraus. ;-)

es geht uebrigens noch kuerzer, wenn man die nicht-schaltjahre als teilmenge der menge der beliebigen jahre ansieht:

/-?\d*(?:(?:(?:[02468][048]|[13579][26])(?:[02468][048]|[13579][26])|(?:[02468][1-35-79]|[13579][013-57-9])(?:0[48]|[2468][048]|[13579][26]))([-./]?)02\1 29|\d{4}([-./]?)(?:(?:0[13578]|1[02])\2(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)\2(?:0[1-9]|[12]\d|30)|02\2(?:0[1-9]|1\d|2[0-8])))/x

bzw. kommentiert:

  
if($date=~/-?\d*  
  (?:  
    (?:                                     # 29. feb. im schaltjahr  
      (?:[02468][048]|[13579][26])           # jahrhundert durch 4 teilbar  
      (?:[02468][048]|[13579][26])           # jahr durch 4 teilbar  
      |  
      (?:[02468][1-35-79]|[13579][013-57-9]) # jahrhundert nicht durch 4 teilbar  
      (?:0[48]|[2468][048]|[13579][26])      # jahr durch 4 teilbar ausser volle jahrhunderte  
    )  
    ([-.\/]?)                                # separator \1  
    02\1 29                                  # 29. feb.; wiederholung des separators  
    |  
    \d{4}                                   # beliebiges jahr  
    ([-.\/]?)                                # separator \2  
    (?:  
      (?:0[13578]|1[02])                     # langer monat  
      \2                                     # wiederholung \2  
      (?:0[1-9]|[12]\d|3[01])                # 01 bis 31  
      |  
      (?:0[469]|11)                          # kurzer monat  
      \2                                     # wiederholung \2  
      (?:0[1-9]|[12]\d|30)                   # 01 bis 30  
      |  
      02                                     # februar  
      \2                                     # wiederholung \2  
      (?:0[1-9]|1\d|2[0-8])                  # 01 bis 28  
    )  
  )/x){  
  print 'valid'."\n";  
}else{  
  print 'not valid!';  
}

hierbei habe ich uebrigens durch backreferencing noch eingebaut, dass man zwei mal dasselbe trennzeichen (was noch immer iso-konform auch der leere string sein darf) verwenden muss.

bei bedarf kann man noch ein ^ an den anfang und ein $ an das ende stellen, falls man nicht nur ein datum sucht, sondern wissen will, ob der komplette string eine datumsangabe sein koennte.

jetzt hakt der test allerdings noch immer, weil der gregorianische kalender erst 1582 eingefuehrt (und deshalb z.b. der oktober 1582 radikal verkuerzt) wurde (siehe wikipedia).

dadurch wuerde der check dann wieder etwas laenger werden. ich poste aber mal nur den teil, der fuer die jahre 1583 bis 9999 zustaendig waere. fuer die zusaetzlichen jahre ab 10000 koennte man beide ausdruecke (also den obigen und den folgenden) mit einem dicken OR kombinieren.

  
if($date=~/# laesst nur jahre 1583 bis 9999 zu  
  (?:                                          # 29. feb. im schaltjahr  
    (?:[2468][048]|16|[3579][26])               # jahrhundert durch 4 teilbar  
    (?:[02468][048]|[13579][26])                # jahr durch 4 teilbar  
    |  
    (?:[2468][1-35-79]|1[789]|[3579][013-57-9]) # jahrhundert nicht durch 4 teilbar  
    (?:0[48]|[2468][048]|[13579][26])           # jahr durch 4 teilbar ausser volle jahrhunderte  
    |  
    15(?:88|9[26])                              # beachtung der einfuehrung des gregor. kalenders  
  )  
  ([-.\/]?)                                     # separator \1  
  02\1 29                                       # 29. feb.; wiederholung des separators \1  
  |  
  (?:[2-9]\d\d\d|1[6-9]\d\d|159\d|158[3-9])    # beliebiges jahr, ab 1583  
  ([-.\/]?)                                     # separator \2  
  (?:  
    (?:0[13578]|1[02])                          # langer monat  
    \2                                          # wiederholung \2  
    (?:0[1-9]|[12]\d|3[01])                     # 01 bis 31  
    |  
    (?:0[469]|11)                               # kurzer monat  
    \2                                          # wiederholung \2  
    (?:0[1-9]|[12]\d|30)                        # 01 bis 30  
    |  
    02                                          # februar  
    \2                                          # wiederholung \2  
    (?:0[1-9]|1\d|2[0-8])                       # 01 bis 28  
  )/x){  
  print 'valid'."\n";  
}else{  
  print 'not valid!';  
}

getestet habe ich den kram jetzt allerdings nicht, das darf jemand anders machen. *duck*

prost
seth