diff --git a/decode.go b/decode.go index 78ce1d2..6d900ed 100644 --- a/decode.go +++ b/decode.go @@ -43,6 +43,10 @@ func parseLocalDate(b []byte) (LocalDate, error) { date.Day = parseDecimalDigits(b[8:10]) + if !isValidDate(date.Year, date.Month, date.Day) { + return LocalDate{}, newDecodeError(b, "impossible date") + } + return date, nil } @@ -330,3 +334,38 @@ func checkAndRemoveUnderscores(b []byte) ([]byte, error) { return cleaned, nil } + +// isValidDate checks if a provided date is a date that exists. +func isValidDate(year int, month int, day int) bool { + return day <= daysIn(month, year) +} + +// daysBefore[m] counts the number of days in a non-leap year +// before month m begins. There is an entry for m=12, counting +// the number of days before January of next year (365). +var daysBefore = [...]int32{ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, +} + +func daysIn(m int, year int) int { + if m == 2 && isLeap(year) { + return 29 + } + return int(daysBefore[m] - daysBefore[m-1]) +} + +func isLeap(year int) bool { + return year%4 == 0 && (year%100 != 0 || year%400 == 0) +} diff --git a/toml_testgen_test.go b/toml_testgen_test.go index f552cb4..d6ebdee 100644 --- a/toml_testgen_test.go +++ b/toml_testgen_test.go @@ -176,7 +176,6 @@ func TestTOMLTest_Invalid_Control_StringUs(t *testing.T) { } func TestTOMLTest_Invalid_Datetime_ImpossibleDate(t *testing.T) { - t.Skip("FIXME") input := "d = 2006-01-50T00:00:00Z\n" testgenInvalid(t, input) } diff --git a/unmarshaler_test.go b/unmarshaler_test.go index a33b555..491f554 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -353,6 +353,22 @@ func TestUnmarshal(t *testing.T) { } }, }, + { + desc: "local leap-day date into interface", + input: `a = 2020-02-29`, + gen: func() test { + type doc struct { + A interface{} + } + + return test{ + target: &doc{}, + expected: &doc{ + A: toml.LocalDate{2020, 2, 29}, + }, + } + }, + }, { desc: "local-time with nano second", input: `a = 12:08:05.666666666`, @@ -2091,6 +2107,16 @@ world'`, desc: "multiline basic string with unfinished escape sequence after the first escape code", data: "a = \"\"\"\\t\\", }, + { + desc: `impossible date-day`, + data: `A = 2021-03-40T23:59:00`, + msg: `impossible date`, + }, + { + desc: `leap day in non-leap year`, + data: `A = 2021-02-29T23:59:00`, + msg: `impossible date`, + }, } for _, e := range examples {