Added option retryAmbiguousDateWithSwap

Original retry logic by KamalGalrani
Adapted into a deferred function at the source enabled by an option, by me
This commit is contained in:
troyspencer 2019-08-07 22:47:05 -05:00
parent ae62f1889d
commit 1bdd6d3fc9

View File

@ -209,9 +209,25 @@ func ParseStrict(datestr string, opts ...ParserOption) (time.Time, error) {
return p.parse() return p.parse()
} }
func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (*parser, error) { func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (p *parser, err error) {
p = newParser(datestr, loc, opts...)
if p.retryAmbiguousDateWithSwap {
// month out of range signifies that a day/month swap is the correct solution to an ambiguous date
// this is because it means that a day is being interpreted as a month and overflowing the valid value for that
// by retrying in this case, we can fix a common situation with no assumptions
defer func() {
if err != nil && strings.Contains(err.Error(), "month out of range") {
// create the option to reverse the preference
preferMonthFirst := PreferMonthFirst(!p.preferMonthFirst)
// turn off the retry to avoid endless recursion
retryAmbiguousDateWithSwap := RetryAmbiguousDateWithSwap(false)
modifiedOpts := append(opts, preferMonthFirst, retryAmbiguousDateWithSwap)
p, err = parseTime(datestr, time.Local, modifiedOpts...)
}
}()
}
p := newParser(datestr, loc, opts...)
i := 0 i := 0
// General strategy is to read rune by rune through the date looking for // General strategy is to read rune by rune through the date looking for
@ -1679,36 +1695,37 @@ iterRunes:
} }
type parser struct { type parser struct {
loc *time.Location loc *time.Location
preferMonthFirst bool preferMonthFirst bool
ambiguousMD bool retryAmbiguousDateWithSwap bool
stateDate dateState ambiguousMD bool
stateTime timeState stateDate dateState
format []byte stateTime timeState
datestr string format []byte
fullMonth string datestr string
skip int fullMonth string
extra int skip int
part1Len int extra int
yeari int part1Len int
yearlen int yeari int
moi int yearlen int
molen int moi int
dayi int molen int
daylen int dayi int
houri int daylen int
hourlen int houri int
mini int hourlen int
minlen int mini int
seci int minlen int
seclen int seci int
msi int seclen int
mslen int msi int
offseti int mslen int
offsetlen int offseti int
tzi int offsetlen int
tzlen int tzi int
t *time.Time tzlen int
t *time.Time
} }
type ParserOption func(*parser) error type ParserOption func(*parser) error
@ -1721,13 +1738,22 @@ func PreferMonthFirst(preferMonthFirst bool) ParserOption {
} }
} }
// RetryAmbiguousDateWithSwap is an option that allows retryAmbiguousDateWithSwap to be changed from its default
func RetryAmbiguousDateWithSwap(retryAmbiguousDateWithSwap bool) ParserOption {
return func(p *parser) error {
p.retryAmbiguousDateWithSwap = retryAmbiguousDateWithSwap
return nil
}
}
func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser { func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser {
p := &parser{ p := &parser{
stateDate: dateStart, stateDate: dateStart,
stateTime: timeIgnore, stateTime: timeIgnore,
datestr: dateStr, datestr: dateStr,
loc: loc, loc: loc,
preferMonthFirst: true, preferMonthFirst: true,
retryAmbiguousDateWithSwap: false,
} }
p.format = []byte(dateStr) p.format = []byte(dateStr)