Skip to content

Commit

Permalink
Merge pull request #45 from winebarrel/support_LW
Browse files Browse the repository at this point in the history
Support lw
  • Loading branch information
winebarrel authored Oct 1, 2023
2 parents aed887e + 15cce3e commit 594df0e
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 7 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

N/A

## [1.10.0] - 2023-10-01

### Added

* Support "LW" in day-of-month. (e.g "0 0 LW * ? *")

## [1.9.2] - 2023-09-30

### Fixed
Expand Down
10 changes: 10 additions & 0 deletions internal/util/date.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ func LastWdayOfMonth(t time.Time, w time.Weekday) int {
}
}

func LastWeekdayOfMonth(t time.Time) int {
lom := t.AddDate(0, 1, -t.Day())

for i := lom; ; i = i.AddDate(0, 0, -1) {
if i.Weekday() != time.Saturday && i.Weekday() != time.Sunday {
return i.Day()
}
}
}

func NearestWeekday(t2 time.Time, day int) int {
base := time.Date(t2.Year(), t2.Month(), day, 0, 0, 0, 0, t2.Location())
lom := LastOfMonth(time.Date(t2.Year(), t2.Month(), 1, 0, 0, 0, 0, t2.Location()))
Expand Down
38 changes: 38 additions & 0 deletions internal/util/date_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,41 @@ func TestNthDayOfWeek(t *testing.T) {
assert.Equal(t.expected, util.NthDayOfWeek(t.tm, t.w, t.nth), fmt.Sprintf("%s %v", t.tm, t))
}
}

func TestLastWeekdayOfMonth(t *testing.T) {
assert := assert.New(t)

tt := []struct {
tm time.Time
expected int
}{
{time.Date(2023, 1, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 2, 1, 9, 0, 0, 0, time.UTC), 28},
{time.Date(2023, 3, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 4, 1, 9, 0, 0, 0, time.UTC), 28},
{time.Date(2023, 5, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 6, 1, 9, 0, 0, 0, time.UTC), 30},
{time.Date(2023, 7, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 8, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 9, 1, 9, 0, 0, 0, time.UTC), 29},
{time.Date(2023, 10, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2023, 11, 1, 9, 0, 0, 0, time.UTC), 30},
{time.Date(2023, 12, 1, 9, 0, 0, 0, time.UTC), 29},
{time.Date(2024, 1, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2024, 2, 1, 9, 0, 0, 0, time.UTC), 29},
{time.Date(2024, 3, 1, 9, 0, 0, 0, time.UTC), 29},
{time.Date(2024, 4, 1, 9, 0, 0, 0, time.UTC), 30},
{time.Date(2024, 5, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2024, 6, 1, 9, 0, 0, 0, time.UTC), 28},
{time.Date(2024, 7, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2024, 8, 1, 9, 0, 0, 0, time.UTC), 30},
{time.Date(2024, 9, 1, 9, 0, 0, 0, time.UTC), 30},
{time.Date(2024, 10, 1, 9, 0, 0, 0, time.UTC), 31},
{time.Date(2024, 11, 1, 9, 0, 0, 0, time.UTC), 29},
{time.Date(2024, 12, 1, 9, 0, 0, 0, time.UTC), 31},
}

for _, t := range tt {
assert.Equal(t.expected, util.LastWeekdayOfMonth(t.tm), t.tm)
}
}
6 changes: 6 additions & 0 deletions match.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,15 @@ func (v *LastDayOfMonth) Match(t time.Time) bool {
return util.LastOfMonth(t)-v.Int() == t.Day()
}

func (v *LastWeekdayOfMonth) Match(t time.Time) bool {
return util.LastWeekdayOfMonth(t) == t.Day()
}

func (e *DayOfMonthExp) Match(t time.Time) bool {
if e.NearestWeekday != nil {
return e.NearestWeekday.Match(t)
} else if e.LastWeekday != nil {
return e.LastWeekday.Match(t)
} else if e.Last != nil {
return e.Last.Match(t)
} else if e.Bottom != nil {
Expand Down
56 changes: 56 additions & 0 deletions match_day_of_month_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,62 @@ func TestMatchDayObMonth(t *testing.T) {
{time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), true},
},
},
{
exp: "* * LW * ? *",
tests: []struct {
tm time.Time
expected bool
}{
{time.Date(2023, 1, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 2, 28, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 3, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 4, 28, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 5, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 6, 30, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 7, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 8, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 9, 29, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 30, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 12, 29, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 1, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 2, 29, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 3, 29, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 4, 30, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 5, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 6, 28, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 7, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 8, 30, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 9, 30, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 10, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 11, 29, 9, 0, 0, 0, time.UTC), true},
{time.Date(2024, 12, 31, 9, 0, 0, 0, time.UTC), true},
{time.Date(2023, 1, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 2, 27, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 3, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 4, 27, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 5, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 6, 29, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 7, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 8, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 9, 28, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 10, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 29, 9, 0, 0, 0, time.UTC), false},
{time.Date(2023, 12, 28, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 1, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 2, 28, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 3, 31, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 4, 29, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 5, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 6, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 7, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 8, 31, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 9, 20, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 10, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 11, 30, 9, 0, 0, 0, time.UTC), false},
{time.Date(2024, 12, 30, 9, 0, 0, 0, time.UTC), false},
},
},
}

for _, t := range tt {
Expand Down
14 changes: 14 additions & 0 deletions next_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ func TestNext(t *testing.T) {
from: time.Date(2022, 10, 10, 9, 0, 0, 0, time.UTC),
expected: time.Date(2022, 10, 10, 12, 34, 0, 0, time.UTC),
},
{
exp: "35 13 LW * ? *",
from: time.Date(2022, 10, 10, 9, 0, 0, 0, time.UTC),
expected: time.Date(2022, 10, 31, 13, 35, 0, 0, time.UTC),
},
}

for _, t := range tt {
Expand Down Expand Up @@ -362,6 +367,15 @@ func TestNextN_3(t *testing.T) {
from: time.Date(2022, 10, 10, 0, 0, 0, 0, time.UTC),
expected: []time.Time{},
},
{
exp: "34 8 LW * ? *",
from: time.Date(2022, 10, 10, 0, 0, 0, 0, time.UTC),
expected: []time.Time{
time.Date(2022, 10, 31, 8, 34, 0, 0, time.UTC),
time.Date(2022, 11, 30, 8, 34, 0, 0, time.UTC),
time.Date(2022, 12, 30, 8, 34, 0, 0, time.UTC),
},
},
}

for _, t := range tt {
Expand Down
26 changes: 20 additions & 6 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,25 @@ func (v *LastDayOfMonth) String() string {
}
}

type LastWeekdayOfMonth struct{}

func (v *LastWeekdayOfMonth) Capture(values []string) error {
*v = LastWeekdayOfMonth{}
return nil
}

func (v *LastWeekdayOfMonth) String() string {
return "LW"
}

type DayOfMonthExp struct {
NearestWeekday *NearestWeekday `( @Number "W" )`
Wildcard bool `| ( ( @"*"`
Range *DayOfMonthRange ` | @@`
Number *DayOfMonth ` | @Number )`
Bottom *int ` ( "/" @Number )? )`
Last *LastDayOfMonth `| ( @"L" ( "-" @Number )? )`
NearestWeekday *NearestWeekday `( @Number "W" )`
Wildcard bool `| ( ( @"*"`
Range *DayOfMonthRange ` | @@`
Number *DayOfMonth ` | @Number )`
Bottom *int ` ( "/" @Number )? )`
LastWeekday *LastWeekdayOfMonth `| ( @"L" "W" )`
Last *LastDayOfMonth `| ( @"L" ( "-" @Number )? )`
}

func (e *DayOfMonthExp) String() string {
Expand All @@ -311,6 +323,8 @@ func (e *DayOfMonthExp) String() string {
s = e.Range.String()
} else if e.Number != nil {
s = e.Number.String()
} else if e.LastWeekday != nil {
s = e.LastWeekday.String()
} else if e.Last != nil {
s = e.Last.String()
} else if e.NearestWeekday != nil {
Expand Down
10 changes: 9 additions & 1 deletion parse_day_of_month_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,16 @@ func TestDayOfMonthWeekday(t *testing.T) {
assert.Equal(nwday(3), cron.DayOfMonth.Exps[0].NearestWeekday)
}

func TestDayOfMonthLastWeekday(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("* * LW * ? *")
assert.NoError(err)
assert.Equal(&cronplan.LastWeekdayOfMonth{}, cron.DayOfMonth.Exps[0].LastWeekday)
}

func TestDayOfMonthComplex(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("* * *,1,1-30,1/5,*/5,L,3W * ? *")
cron, err := cronplan.Parse("* * *,1,1-30,1/5,*/5,L,3W,LW * ? *")
assert.NoError(err)
assert.True(cron.DayOfMonth.Exps[0].Wildcard)
assert.Equal(day(1), cron.DayOfMonth.Exps[1].Number)
Expand All @@ -126,4 +133,5 @@ func TestDayOfMonthComplex(t *testing.T) {
}, cron.DayOfMonth.Exps[4])
assert.Equal(last(0), cron.DayOfMonth.Exps[5].Last)
assert.Equal(nwday(3), cron.DayOfMonth.Exps[6].NearestWeekday)
assert.Equal(&cronplan.LastWeekdayOfMonth{}, cron.DayOfMonth.Exps[7].LastWeekday)
}

0 comments on commit 594df0e

Please sign in to comment.