Skip to content

Instantly share code, notes, and snippets.

@springcomp
Last active November 9, 2021 21:55
Show Gist options
  • Save springcomp/ba0207641947e55b1090af8e13e0402f to your computer and use it in GitHub Desktop.
Save springcomp/ba0207641947e55b1090af8e13e0402f to your computer and use it in GitHub Desktop.
A regex to parse gregorian date/times with leap year support
## this script iterates on all dates
## from 01-01-1600 and 200 000 days later
## and checks every single one of those dates
## against the pattern shown here
$pattern = "(?<ts>(?:(?<by400>(?:(?:(?:[0-1]\d|2\d)02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d[02468]00)|(?<by100>(?:(?:(?:[0-1]\d|2[0-8])02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d\d00)|(?<by4>(?:(?:(?:[0-1]\d|2\d)02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d\d(?:(?:[02468][48])|(?:[2468][048])|(?:[13579][26])))|(?<by1>(?:(?:(?:[0-1]\d|2[0-8])02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d{4})))"
$date = [DateTime]::Parse("1601-01-01T00:00.000")
$format = "ddMMyyyyHH:mm:ss"
$MODULO = 10000
1..200000 |% {
$dt = $date.ToString($format)
$success = $dt -match $pattern
if (-not $success) {
Write-Host "$($dt): KO" -ForegroundColor Red
throw "KO"
}
else {
if ($dt -ne $matches[0]){
Write-Host "$($dt): KO" -ForegroundColor Red
throw "KO"
}
else {
if (($_ % $MODULO) -eq 0) { Write-Host "$($_): $($dt): OK" -ForegroundColor Green }
}
}
$date = $date.AddDays(1.0)
}
1..86400 |% {
$dt = $date.ToString($format)
$success = $dt -match $pattern
if (-not $success) {
Write-Host "$($dt): KO" -ForegroundColor Red
throw "KO"
}
else {
if (($_ % $MODULO) -eq 0) { Write-Host "$($_): $($dt): OK" -ForegroundColor Green }
}
$date = $date.AddSeconds(1.0)
}
https://stackoverflow.com/questions/48352672/what-is-a-regular-expression-for-matching-numbers-divisible-by-4
years mod 400: (?<by400>\d[02468]00)
years mod 100: (?<by100>\d\d00)
years mod 4: (?<by4>\d\d(?:(?:[02468][048])|(?:[13579][26])))
years mod 4 (excluding mod 100): (?<by4>\d\d(?:(?:[02468][48])|(?:[2468][048])|(?:[13579][26])))
years (all others): (?<by1>\d{4})
year: (?<by400>\d[02468]00)|(?<by100>\d\d00)|(?<by4>\d\d(?:(?:[02468][48])|(?:[2468][048])|(?:[13579][26])))|(?<by1>\d{4})
test:
2000
2003
2004
2096
2100
2101
2010
2104
2200
2300
2400
month feb: (?<feb>(?:[0-1]\d|2[0-8])02)
month feb (leap years): (?<feb>(?:[0-1]\d|2\d)02)
month 30 days: (?<30d>(?:[0-2]\d|30)0[469]|11)
month 31 days: (?<31d>(?:[0-2]\d|3[0-1])(0[13578]|1[02]))
month: (?<feb>(?:[0-1]\d|2\d)02)|(?<30d>(?:[0-2]\d|30)0[469]|11)|(?<31d>(?:[0-2]\d|3[0-1])(0[13578]|1[02]))
test:
3001
3101
3201
2802
2902
3102
3202
3003
3103
3203
3004
3104
3204
3005
3105
3205
3006
3106
3206
3007
3107
3207
3008
3108
3208
3009
3109
3209
3010
3110
3210
3011
3111
3211
3012
3112
3212
hour: (?:[01]\d|2[0-3])
minute: (?:[0-5]\d)
second: (?:[0-5]\d)
The whole regex:
(?:(?<by400>(?:(?:(?:[0-1]\d|2\d)02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d[02468]00)|(?<by100>(?:(?:(?:[0-1]\d|2[0-8])02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d\d00)|(?<by4>(?:(?:(?:[0-1]\d|2\d)02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d\d(?:(?:[02468][48])|(?:[2468][048])|(?:[13579][26])))|(?<by1>(?:(?:(?:[0-1]\d|2[0-8])02)|(?:(?:[0-2]\d|30)0[469]|11)|(?:(?:[0-2]\d|3[0-1])(0[13578]|1[02])))\d{4}))(?:[01]\d|2[0-3])(?::[0-5]\d){2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment