Comprehensive time validation

This commit is contained in:
Klondike Dragon 2023-12-18 20:52:16 -07:00
parent a45d593447
commit 89df0f8c49

View File

@ -1769,13 +1769,12 @@ iterRunes:
if p.seci == 0 { if p.seci == 0 {
// 22:18+0530 // 22:18+0530
p.minlen = i - p.mini p.minlen = i - p.mini
} else if p.seclen == 0 {
p.seclen = i - p.seci
} else if p.msi > 0 && p.mslen == 0 {
p.mslen = i - p.msi
} else { } else {
if p.seclen == 0 { return p, p.unknownErr(datestr)
p.seclen = i - p.seci
}
if p.msi > 0 && p.mslen == 0 {
p.mslen = i - p.msi
}
} }
p.offseti = i p.offseti = i
case '.', ',': case '.', ',':
@ -1806,6 +1805,8 @@ iterRunes:
// September 17, 2012 at 5:00pm UTC-05 // September 17, 2012 at 5:00pm UTC-05
i++ // skip ' ' i++ // skip ' '
p.houri = 0 // reset hour p.houri = 0 // reset hour
} else {
return p, p.unknownErr(datestr)
} }
} else { } else {
// Could be AM/PM // Could be AM/PM
@ -1813,13 +1814,17 @@ iterRunes:
isTwoLetterWord := ((i+2) == len(p.datestr) || p.nextIs(i+1, ' ')) isTwoLetterWord := ((i+2) == len(p.datestr) || p.nextIs(i+1, ' '))
switch { switch {
case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM: case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM:
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.set(i, "pm") p.set(i, "pm")
p.parsedAMPM = true p.parsedAMPM = true
// skip 'm' // skip 'm'
i++ i++
case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM: case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM:
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.set(i, "PM") p.set(i, "PM")
p.parsedAMPM = true p.parsedAMPM = true
// skip 'M' // skip 'M'
@ -1829,7 +1834,9 @@ iterRunes:
} }
} }
case ' ': case ' ':
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.stateTime = timeWs p.stateTime = timeWs
case ':': case ':':
if p.mini == 0 { if p.mini == 0 {
@ -1841,7 +1848,11 @@ iterRunes:
} else if p.seci > 0 { } else if p.seci > 0 {
// 18:31:59:257 ms uses colon, wtf // 18:31:59:257 ms uses colon, wtf
p.seclen = i - p.seci p.seclen = i - p.seci
p.set(p.seci, "05") if p.seclen == 2 {
p.set(p.seci, "05")
} else {
return p, p.unknownErr(datestr)
}
p.msi = i + 1 p.msi = i + 1
// gross, gross, gross. manipulating the datestr is horrible. // gross, gross, gross. manipulating the datestr is horrible.
@ -1861,6 +1872,8 @@ iterRunes:
// 15:04:05-07:00 // 15:04:05-07:00
if r == ':' { if r == ':' {
p.stateTime = timeOffsetColon p.stateTime = timeOffsetColon
} else if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
} }
case timeWs: case timeWs:
// timeWsAlpha // timeWsAlpha
@ -1905,6 +1918,10 @@ iterRunes:
// 00:12:00 2008 // 00:12:00 2008
p.stateTime = timeWsYear p.stateTime = timeWsYear
p.yeari = i p.yeari = i
} else if r == '(' {
// (start of time zone description, ignore)
} else {
return p, p.unknownErr(datestr)
} }
} }
case timeWsYear: case timeWsYear:
@ -1953,6 +1970,8 @@ iterRunes:
p.set(p.tzi, " MST") p.set(p.tzi, " MST")
} else if p.tzlen == 3 { } else if p.tzlen == 3 {
p.set(p.tzi, "MST") p.set(p.tzi, "MST")
} else if p.tzlen > 0 {
return p, p.unknownErr(datestr)
} }
p.stateTime = timeWsAlphaZoneOffset p.stateTime = timeWsAlphaZoneOffset
p.offseti = i p.offseti = i
@ -1965,6 +1984,8 @@ iterRunes:
p.set(p.tzi, " MST") p.set(p.tzi, " MST")
} else if p.tzlen == 3 { } else if p.tzlen == 3 {
p.set(p.tzi, "MST") p.set(p.tzi, "MST")
} else if p.tzlen > 0 {
return p, p.unknownErr(datestr)
} }
if r == ' ' { if r == ' ' {
p.stateTime = timeWsAlphaWs p.stateTime = timeWsAlphaWs
@ -1997,6 +2018,10 @@ iterRunes:
p.yeari = i + 1 p.yeari = i + 1
} }
p.stateTime = timeWsAlphaZoneOffsetWs p.stateTime = timeWsAlphaZoneOffsetWs
default:
if r != ':' && !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
}
} }
case timeWsAlphaZoneOffsetWs: case timeWsAlphaZoneOffsetWs:
// timeWsAlphaZoneOffsetWs // timeWsAlphaZoneOffsetWs
@ -2018,7 +2043,11 @@ iterRunes:
if !p.setYear() { if !p.setYear() {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} else if p.yearlen > 4 {
return p, p.unknownErr(datestr)
} }
} else {
return p, p.unknownErr(datestr)
} }
case timeWsAMPMMaybe: case timeWsAMPMMaybe:
// timeWsAMPMMaybe // timeWsAMPMMaybe
@ -2045,6 +2074,8 @@ iterRunes:
p.set(p.houri, "03") p.set(p.houri, "03")
} else if p.hourlen == 1 { } else if p.hourlen == 1 {
p.set(p.houri, "3") p.set(p.houri, "3")
} else {
return p, p.unknownErr(datestr)
} }
} else { } else {
p.stateTime = timeWsAlpha p.stateTime = timeWsAlpha
@ -2078,6 +2109,10 @@ iterRunes:
p.set(p.offseti, "-0700") p.set(p.offseti, "-0700")
p.yeari = i + 1 p.yeari = i + 1
p.stateTime = timeWsOffsetWs p.stateTime = timeWsOffsetWs
default:
if !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
}
} }
case timeWsOffsetWs: case timeWsOffsetWs:
// 17:57:51 -0700 2009 // 17:57:51 -0700 2009
@ -2091,6 +2126,8 @@ iterRunes:
if p.datestr[i-1] == 'm' { if p.datestr[i-1] == 'm' {
p.extra = i - 2 p.extra = i - 2
p.trimExtra(false) p.trimExtra(false)
} else {
return p, p.unknownErr(datestr)
} }
case '+', '-', '(': case '+', '-', '(':
// This really doesn't seem valid, but for some reason when round-tripping a go date // This really doesn't seem valid, but for some reason when round-tripping a go date
@ -2100,6 +2137,8 @@ iterRunes:
p.extra = i - 1 p.extra = i - 1
p.stateTime = timeWsOffset p.stateTime = timeWsOffset
p.trimExtra(false) p.trimExtra(false)
case ' ':
// continue
default: default:
switch { switch {
case unicode.IsDigit(r): case unicode.IsDigit(r):
@ -2108,12 +2147,16 @@ iterRunes:
if !p.setYear() { if !p.setYear() {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
} else if p.yearlen > 4 {
return p, p.unknownErr(datestr)
} }
case unicode.IsLetter(r): case unicode.IsLetter(r):
// 15:04:05 -0700 MST // 15:04:05 -0700 MST
if p.tzi == 0 { if p.tzi == 0 {
p.tzi = i p.tzi = i
} }
default:
return p, p.unknownErr(datestr)
} }
} }
@ -2136,6 +2179,8 @@ iterRunes:
} }
p.tzi = i p.tzi = i
break iterTimeRunes break iterTimeRunes
} else if r != ' ' && !unicode.IsDigit(r) {
return p, p.unknownErr(datestr)
} }
case timePeriod: case timePeriod:
// 15:04:05.999999999 // 15:04:05.999999999
@ -2155,7 +2200,9 @@ iterRunes:
switch r { switch r {
case ' ': case ' ':
p.mslen = i - p.msi p.mslen = i - p.msi
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.stateTime = timeWs p.stateTime = timeWs
case '+', '-': case '+', '-':
p.mslen = i - p.msi p.mslen = i - p.msi
@ -2177,7 +2224,9 @@ iterRunes:
switch { switch {
case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM: case isLower && p.nextIs(i, 'm') && isTwoLetterWord && !p.parsedAMPM:
p.mslen = i - p.msi p.mslen = i - p.msi
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.set(i, "pm") p.set(i, "pm")
p.parsedAMPM = true p.parsedAMPM = true
// skip 'm' // skip 'm'
@ -2185,7 +2234,9 @@ iterRunes:
p.stateTime = timePeriodAMPM p.stateTime = timePeriodAMPM
case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM: case !isLower && p.nextIs(i, 'M') && isTwoLetterWord && !p.parsedAMPM:
p.mslen = i - p.msi p.mslen = i - p.msi
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
p.set(i, "PM") p.set(i, "PM")
p.parsedAMPM = true p.parsedAMPM = true
// skip 'M' // skip 'M'
@ -2269,7 +2320,7 @@ iterRunes:
} }
case timeWsAlphaRParen: case timeWsAlphaRParen:
// continue // nothing extra to do
case timeWsAlphaWs: case timeWsAlphaWs:
p.yearlen = i - p.yeari p.yearlen = i - p.yeari
@ -2358,7 +2409,9 @@ iterRunes:
} }
} }
} }
p.coalesceTime(i) if !p.coalesceTime(i) {
return p, p.unknownErr(datestr)
}
} }
switch p.stateDate { switch p.stateDate {
@ -2401,7 +2454,7 @@ iterRunes:
} else if len(p.datestr) == len("2014") { } else if len(p.datestr) == len("2014") {
p.setEntireFormat([]byte("2006")) p.setEntireFormat([]byte("2006"))
return p, nil return p, nil
} else if len(p.datestr) < 4 { } else {
return p, p.unknownErr(datestr) return p, p.unknownErr(datestr)
} }
if !t.IsZero() { if !t.IsZero() {
@ -2412,6 +2465,8 @@ iterRunes:
t = t.In(loc) t = t.In(loc)
p.t = &t p.t = &t
return p, nil return p, nil
} else {
return p, p.unknownErr(datestr)
} }
case dateDigitSt: case dateDigitSt:
// 171113 14:14:20 // 171113 14:14:20
@ -2435,6 +2490,8 @@ iterRunes:
p.set(p.offseti, "-0700") p.set(p.offseti, "-0700")
case 6: case 6:
p.set(p.offseti, "-07:00") p.set(p.offseti, "-07:00")
default:
return p, p.unknownErr(datestr)
} }
return p, nil return p, nil
@ -2530,6 +2587,8 @@ iterRunes:
p.set(p.offseti, "-0700") p.set(p.offseti, "-0700")
case 6: case 6:
p.set(p.offseti, "-07:00") p.set(p.offseti, "-07:00")
default:
return p, p.unknownErr(datestr)
} }
return p, nil return p, nil
@ -2906,7 +2965,7 @@ func (p *parser) ts() string {
func (p *parser) ds() string { func (p *parser) ds() string {
return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen) return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen)
} }
func (p *parser) coalesceTime(end int) { func (p *parser) coalesceTime(end int) bool {
// 03:04:05 // 03:04:05
// 15:04:05 // 15:04:05
// 3:04:05 // 3:04:05
@ -2917,6 +2976,8 @@ func (p *parser) coalesceTime(end int) {
p.set(p.houri, "15") p.set(p.houri, "15")
} else if p.hourlen == 1 { } else if p.hourlen == 1 {
p.set(p.houri, "3") p.set(p.houri, "3")
} else {
return false
} }
} }
if p.mini > 0 { if p.mini > 0 {
@ -2925,8 +2986,10 @@ func (p *parser) coalesceTime(end int) {
} }
if p.minlen == 2 { if p.minlen == 2 {
p.set(p.mini, "04") p.set(p.mini, "04")
} else { } else if p.minlen == 1 {
p.set(p.mini, "4") p.set(p.mini, "4")
} else {
return false
} }
} }
if p.seci > 0 { if p.seci > 0 {
@ -2935,8 +2998,10 @@ func (p *parser) coalesceTime(end int) {
} }
if p.seclen == 2 { if p.seclen == 2 {
p.set(p.seci, "05") p.set(p.seci, "05")
} else { } else if p.seclen == 1 {
p.set(p.seci, "5") p.set(p.seci, "5")
} else {
return false
} }
} }
@ -2949,6 +3014,7 @@ func (p *parser) coalesceTime(end int) {
p.formatSetLen = endPos p.formatSetLen = endPos
} }
} }
return true
} }
func (p *parser) setFullMonth(month string) { func (p *parser) setFullMonth(month string) {
oldLen := len(p.format) oldLen := len(p.format)