fix(parse): reject the lenient grammar edges and name out-of-range date-times
Assisted-by: GLM 5.3
This commit is contained in:
+32
-15
@@ -79,7 +79,9 @@ func clockString(t time.Time) string {
|
||||
|
||||
// offsetString renders an offset date-time, the fourth TOML kind, in the same
|
||||
// shape: no zero seconds, no trailing zeros in the fraction, and the offset
|
||||
// written as "Z" when it is zero.
|
||||
// written as "Z" when it is zero. A zone offset that is not a whole number of
|
||||
// minutes loses its seconds to this rendering, which is why Marshal refuses
|
||||
// such a value rather than writing it.
|
||||
func offsetString(t time.Time) string {
|
||||
buf := t.AppendFormat(make([]byte, 0, 32), "2006-01-02T")
|
||||
buf = appendClock(buf, t)
|
||||
@@ -235,17 +237,21 @@ func normaliseDateTimeToken(tok string, kind dateTimeKind) string {
|
||||
|
||||
// parseDateTime classifies and parses a bare token as a TOML date-time value.
|
||||
// It returns the decoded value (OffsetDateTime, LocalDateTime, LocalDate or
|
||||
// LocalTime) and whether the token was a date-time at all.
|
||||
func parseDateTime(tok string) (any, bool) {
|
||||
// LocalTime), whether the token was a date-time at all, and an error for a
|
||||
// token whose shape is a date-time a component of which lies outside its
|
||||
// range: an hour of 24, a day the month does not hold. Such a token is a
|
||||
// broken date-time, not some other value, so the error names it instead of
|
||||
// leaving it to the number decoder's complaint.
|
||||
func parseDateTime(tok string) (any, bool, error) {
|
||||
if tok == "" || tok[0] < '0' || tok[0] > '9' {
|
||||
return nil, false
|
||||
return nil, false, nil
|
||||
}
|
||||
if !strings.ContainsAny(tok, "-:") {
|
||||
return nil, false
|
||||
return nil, false, nil
|
||||
}
|
||||
kind, seconds := scanDateTimeShape(tok)
|
||||
if kind == dateTimeNone {
|
||||
return nil, false
|
||||
return nil, false, nil
|
||||
}
|
||||
norm := normaliseDateTimeToken(tok, kind)
|
||||
switch kind {
|
||||
@@ -256,7 +262,7 @@ func parseDateTime(tok string) (any, bool) {
|
||||
}
|
||||
t, err := time.Parse(layout, norm)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
return nil, false, fmt.Errorf("invalid date-time %q", tok)
|
||||
}
|
||||
// A zero offset carries its own anonymous location from time.Parse,
|
||||
// while the written form is "Z" either way; normalising to UTC keeps
|
||||
@@ -264,7 +270,7 @@ func parseDateTime(tok string) (any, bool) {
|
||||
if _, off := t.Zone(); off == 0 {
|
||||
t = t.In(time.UTC)
|
||||
}
|
||||
return OffsetDateTime{t}, true
|
||||
return OffsetDateTime{t}, true, nil
|
||||
case dateTimeLocal:
|
||||
layout := localClockLayout
|
||||
if seconds {
|
||||
@@ -272,15 +278,15 @@ func parseDateTime(tok string) (any, bool) {
|
||||
}
|
||||
t, err := time.Parse(layout, norm)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
return nil, false, fmt.Errorf("invalid date-time %q", tok)
|
||||
}
|
||||
return LocalDateTime{t}, true
|
||||
return LocalDateTime{t}, true, nil
|
||||
case dateTimeDate:
|
||||
t, err := time.Parse(localDateOnlyLayout, norm)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
return nil, false, fmt.Errorf("invalid date-time %q", tok)
|
||||
}
|
||||
return LocalDate{t}, true
|
||||
return LocalDate{t}, true, nil
|
||||
case dateTimeClock:
|
||||
layout := localTimeClockLayout
|
||||
if seconds {
|
||||
@@ -288,11 +294,22 @@ func parseDateTime(tok string) (any, bool) {
|
||||
}
|
||||
t, err := time.Parse(layout, norm)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
return nil, false, fmt.Errorf("invalid date-time %q", tok)
|
||||
}
|
||||
return LocalTime{t}, true
|
||||
return LocalTime{t}, true, nil
|
||||
}
|
||||
return nil, false
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// wholeMinuteOffset reports an error when the zone offset carries seconds, a
|
||||
// shape no TOML offset can hold: writing only the minutes would silently
|
||||
// shift the instant on the way back, so the encoder refuses the value rather
|
||||
// than corrupting it.
|
||||
func wholeMinuteOffset(t time.Time) error {
|
||||
if _, off := t.Zone(); off%60 != 0 {
|
||||
return fmt.Errorf("interpres: date-time offset of %d seconds is not a whole number of minutes, which TOML cannot write", off)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isDateToken reports whether s is exactly a YYYY-MM-DD date, used to detect a
|
||||
|
||||
Reference in New Issue
Block a user