unmarshal: validate date (#622)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user