Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion rules/en/exact_month_date.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ func ExactMonthDate(s rules.Strategy) rules.Rule {
),

Applier: func(m *rules.Match, c *rules.Context, o *rules.Options, ref time.Time) (bool, error) {
_ = overwrite
if (c.Month != nil || c.Day != nil) && !overwrite {
return false, nil
}

ord1 := strings.ToLower(strings.TrimSpace(m.Captures[0]))
num1 := strings.ToLower(strings.TrimSpace(m.Captures[1]))
Expand Down Expand Up @@ -102,7 +104,36 @@ func ExactMonthDate(s rules.Strategy) rules.Rule {
c.Day = &num
}

if c.Year == nil {
year := ref.Year()
day := ref.Day()
if c.Day != nil {
day = *c.Day
}

if last := daysIn(time.Month(monInt), year); day > last {
day = last
}

dateInRefYear := time.Date(year, time.Month(monInt), day, 0, 0, 0, 0, ref.Location())
refDate := time.Date(ref.Year(), ref.Month(), ref.Day(), 0, 0, 0, 0, ref.Location())
if dateInRefYear.Before(refDate) {
year++
c.Year = &year
if c.Day != nil {
if last := daysIn(time.Month(monInt), year); *c.Day > last {
c.Day = &last
}
}
}
}

return true, nil
},
}
}

func daysIn(m time.Month, year int) int {
return time.Date(year, m+1, 0, 0, 0, 0, 0, time.UTC).Day()
}

60 changes: 55 additions & 5 deletions rules/en/exact_month_date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestExactMonthDate(t *testing.T) {
{"october 21st", 0, "october 21st", 6936 * time.Hour},
{"twentieth of december", 0, "twentieth of december", 8376 * time.Hour},
{"march 10th", 0, "march 10th", 1536 * time.Hour},
{"jan. 4", 0, "jan. 4", -48 * time.Hour},
{"jan. 4", 0, "jan. 4", 8736 * time.Hour},
{"february", 0, "february", 744 * time.Hour},
{"october", 0, "october", 6576 * time.Hour},
{"jul.", 0, "jul.", 4368 * time.Hour},
Expand All @@ -51,11 +51,11 @@ func TestExactMonthDateFromLongMonth(t *testing.T) {
Text string
Expected time.Time
}{
{"september 5th", time.Date(2016, time.September, 5, 0, 0, 0, 0, time.UTC)},
{"february 11", time.Date(2016, time.February, 11, 0, 0, 0, 0, time.UTC)},
{"1st of june", time.Date(2016, time.June, 1, 0, 0, 0, 0, time.UTC)},
{"september 5th", time.Date(2017, time.September, 5, 0, 0, 0, 0, time.UTC)},
{"february 11", time.Date(2017, time.February, 11, 0, 0, 0, 0, time.UTC)},
{"1st of june", time.Date(2017, time.June, 1, 0, 0, 0, 0, time.UTC)},
// No day in the text: the reference day clamps to the month's last.
{"september", time.Date(2016, time.September, 30, 0, 0, 0, 0, time.UTC)},
{"september", time.Date(2017, time.September, 30, 0, 0, 0, 0, time.UTC)},
// Months that do have a 31st keep the reference day untouched.
{"december", time.Date(2016, time.December, 31, 0, 0, 0, 0, time.UTC)},
}
Expand All @@ -67,3 +67,53 @@ func TestExactMonthDateFromLongMonth(t *testing.T) {
require.Equal(t, f.Expected, res.Time, "[en.ExactMonthDate] time #%d", i)
}
}

func TestExactMonthDateFutureBias(t *testing.T) {
w := when.New(nil)
w.Add(en.ExactMonthDate(rules.Override))

// End of year case from issue #29:
ref := time.Date(2021, time.December, 28, 12, 0, 0, 0, time.UTC)

tests := []struct {
text string
expected time.Time
}{
{"january 4th", time.Date(2022, time.January, 4, 12, 0, 0, 0, time.UTC)},
{"january 4", time.Date(2022, time.January, 4, 12, 0, 0, 0, time.UTC)},
{"jan. 4", time.Date(2022, time.January, 4, 12, 0, 0, 0, time.UTC)},
{"4th of january", time.Date(2022, time.January, 4, 12, 0, 0, 0, time.UTC)},
{"january", time.Date(2022, time.January, 28, 12, 0, 0, 0, time.UTC)},
{"december 28th", time.Date(2021, time.December, 28, 12, 0, 0, 0, time.UTC)},
{"december 29th", time.Date(2021, time.December, 29, 12, 0, 0, 0, time.UTC)},
{"december 27th", time.Date(2022, time.December, 27, 12, 0, 0, 0, time.UTC)},
}

for _, tt := range tests {
res, err := w.Parse(tt.text, ref)
require.NoError(t, err)
require.NotNil(t, res)
require.Equal(t, tt.expected, res.Time)
}

// Leap year edge case: 2020 is a leap year, next year 2021 is not
leapRef := time.Date(2020, time.March, 1, 10, 0, 0, 0, time.UTC)
res, err := w.Parse("february 29th", leapRef)
require.NoError(t, err)
require.NotNil(t, res)
require.Equal(t, time.Date(2021, time.February, 28, 10, 0, 0, 0, time.UTC), res.Time)

// Strategy check: skip should not override already set month
wSkip := when.New(nil)
wSkip.Add(en.ExactMonthDate(rules.Skip))
ctx := &rules.Context{
Month: func(i int) *int { return &i }(11),
}
match := en.ExactMonthDate(rules.Skip).Find("january 4th")
require.NotNil(t, match)
applied, err := match.Apply(ctx, nil, ref)
require.NoError(t, err)
require.False(t, applied)
require.Equal(t, 11, *ctx.Month)
}

Loading