Skip to content

Commit

Permalink
Merge pull request #65 from ijt/issue/64/dec-2031
Browse files Browse the repository at this point in the history
Issue/64/dec 2031
  • Loading branch information
ijt authored Jan 17, 2023
2 parents 4f5e32e + ae3874a commit 3183858
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 102 deletions.
42 changes: 33 additions & 9 deletions v2/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,18 +312,36 @@ func parseDateWord(d *date, w string) (string, bool) {
return "m", true
}

// MMM-DD
if strings.Count(w, "-") == 1 {
idash := strings.IndexByte(w, '-')
mstr := w[:idash]
domstr := w[idash+1:]
m, ok := monthNameToMonth[mstr]
part1 := w[:idash]
part2 := w[idash+1:]
m, ok := monthNameToMonth[part1]
if ok {
dom, ok := parseDayOfMonthNoCheck(domstr)
if ok && okDayOfMonth(dom) {
d.month = m
d.dayOfMonth = dom
return "md", true
i, ok := parseDayOfMonthNoCheck(part2)
if ok {
if okDayOfMonth(i) {
// MMM-DD
d.month = m
d.dayOfMonth = i
return "md", true
} else if okYear(i) {
// MMM-YYYY
d.month = m
d.year = i
return "my", true
}
}
} else {
m, ok := monthNameToMonth[part2]
if ok {
// YYYY-MMM
y, err := strconv.Atoi(part1)
if err == nil && okYear(y) {
d.month = m
d.year = y
return "ym", true
}
}
}
}
Expand Down Expand Up @@ -355,6 +373,10 @@ func parseDateWord(d *date, w string) (string, bool) {
return "", false
}

func okYear(y int) bool {
return 1000 <= y && y <= 9999
}

func parseDayOfMonth(w string) (int, bool) {
dom, ok := parseDayOfMonthNoCheck(w)
if !ok || !okDayOfMonth(dom) {
Expand All @@ -363,6 +385,8 @@ func parseDayOfMonth(w string) (int, bool) {
return dom, ok
}

// parseDayOfMonthNoCheck returns w parsed as an int, and whether the parse
// was successful.
func parseDayOfMonthNoCheck(w string) (int, bool) {
i, ok := strToInt[w]
if ok {
Expand Down
206 changes: 113 additions & 93 deletions v2/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,99 +69,6 @@ func Test_parseImplicitRange_monthOnly(t *testing.T) {
}
}

func TestParseRange_fail(t *testing.T) {
var badCases = []struct {
input string
}{
{``},
{`�`},
{`a`},
{`from`},
{`from foo`},
{`from yesterday`},
{`from yesterday to`},
{`from yesterday to beeblebrox`},
{`red raspberry`},
{`next thing`},
{`not a date or a time`},
{`Message me in 2 minutes`},
{`Message me in 2 minutes from now`},
{`Remind me in 1 hour`},
{`Remind me in 1 hour from now`},
{`Remind me in 1 hour and 3 minutes from now`},
{`Remind me in an hour`},
{`Remind me in an hour from now`},
{`Remind me one day from now`},
{`Remind me in a day`},
{`Remind me in one day`},
{`Remind me in one day from now`},
{`Message me in a week`},
{`Message me in one week`},
{`Message me in one week from now`},
{`Message me in two weeks from now`},
{`Message me two weeks from now`},
{`Message me in two weeks`},
{`Remind me in 12 months from now at 6am`},
{`Remind me in a month`},
{`Remind me in 2 months`},
{`Remind me in a month from now`},
{`Remind me in 2 months from now`},
{`Remind me in one year from now`},
{`Remind me in a year`},
{`Remind me in a year from now`},
{`Restart the server in 2 days from now`},
{`Remind me on the 5th of next month`},
{`Remind me on the 5th of next month at 7am`},
{`Remind me at 7am on the 5th of next month`},
{`Remind me in one month from now`},
{`Remind me in one month from now at 7am`},
{`Remind me on the December 25th at 7am`},
{`Remind me at 7am on December 25th`},
{`Remind me on the 25th of December at 7am`},
{`Check logs in the past 5 minutes`},

// "1 minute" is a duration, not a time.
{`1 minute`},

// "one minute" is also a duration.
{`one minute`},

// "1 hour" is also a duration.
{`1 hour`},

// "1 day" is also a duration.
{`1 day`},

// "1 week" is also a duration.
{`1 week`},

// "1 month" is also a duration.
{`1 month`},

// "next 2 months" is a date range, not a time or a date.
{`next 2 months`},

// These are currently considered bad input, although they may
{`10`},
{`17`},

// Bare years don't have enough context to be confidently parsed as dates.
{`1999`},
{`2008`},

// Goofy input:
{`10:am`},
}
for _, c := range badCases {
t.Run(c.input, func(t *testing.T) {
_, _, err := ParseRange(c.input, time.Time{}, Future)
if err == nil {
t.Error("parsing succeeded, want failure")
}
})
}
}

func Test_parseImplicitRange(t *testing.T) {
type args struct {
s string
Expand Down Expand Up @@ -263,6 +170,26 @@ func Test_parseImplicitRange(t *testing.T) {
wantR: truncateMonth(time.Date(2022, 12, 1, 0, 0, 0, 0, time.UTC)),
wantParsed: "december",
},
{
name: "my date with dash",
args: args{
s: "dec-2031",
now: time.Time{},
dir: Future,
},
wantR: truncateMonth(time.Date(2031, 12, 1, 0, 0, 0, 0, time.UTC)),
wantParsed: "dec-2031",
},
{
name: "ym date with dash",
args: args{
s: "2031-dec",
now: time.Time{},
dir: Future,
},
wantR: truncateMonth(time.Date(2031, 12, 1, 0, 0, 0, 0, time.UTC)),
wantParsed: "2031-dec",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -281,6 +208,99 @@ func Test_parseImplicitRange(t *testing.T) {
}
}

func TestParseRange_fail(t *testing.T) {
var badCases = []struct {
input string
}{
{``},
{`�`},
{`a`},
{`from`},
{`from foo`},
{`from yesterday`},
{`from yesterday to`},
{`from yesterday to beeblebrox`},
{`red raspberry`},
{`next thing`},
{`not a date or a time`},
{`Message me in 2 minutes`},
{`Message me in 2 minutes from now`},
{`Remind me in 1 hour`},
{`Remind me in 1 hour from now`},
{`Remind me in 1 hour and 3 minutes from now`},
{`Remind me in an hour`},
{`Remind me in an hour from now`},
{`Remind me one day from now`},
{`Remind me in a day`},
{`Remind me in one day`},
{`Remind me in one day from now`},
{`Message me in a week`},
{`Message me in one week`},
{`Message me in one week from now`},
{`Message me in two weeks from now`},
{`Message me two weeks from now`},
{`Message me in two weeks`},
{`Remind me in 12 months from now at 6am`},
{`Remind me in a month`},
{`Remind me in 2 months`},
{`Remind me in a month from now`},
{`Remind me in 2 months from now`},
{`Remind me in one year from now`},
{`Remind me in a year`},
{`Remind me in a year from now`},
{`Restart the server in 2 days from now`},
{`Remind me on the 5th of next month`},
{`Remind me on the 5th of next month at 7am`},
{`Remind me at 7am on the 5th of next month`},
{`Remind me in one month from now`},
{`Remind me in one month from now at 7am`},
{`Remind me on the December 25th at 7am`},
{`Remind me at 7am on December 25th`},
{`Remind me on the 25th of December at 7am`},
{`Check logs in the past 5 minutes`},

// "1 minute" is a duration, not a time.
{`1 minute`},

// "one minute" is also a duration.
{`one minute`},

// "1 hour" is also a duration.
{`1 hour`},

// "1 day" is also a duration.
{`1 day`},

// "1 week" is also a duration.
{`1 week`},

// "1 month" is also a duration.
{`1 month`},

// "next 2 months" is a date range, not a time or a date.
{`next 2 months`},

// These are currently considered bad input, although they may
{`10`},
{`17`},

// Bare years don't have enough context to be confidently parsed as dates.
{`1999`},
{`2008`},

// Goofy input:
{`10:am`},
}
for _, c := range badCases {
t.Run(c.input, func(t *testing.T) {
_, _, err := ParseRange(c.input, time.Time{}, Future)
if err == nil {
t.Error("parsing succeeded, want failure")
}
})
}
}

func TestParseRange(t *testing.T) {
now := time.UnixMilli(rand.Int63())
type args struct {
Expand Down

0 comments on commit 3183858

Please sign in to comment.