Skip to content

Commit

Permalink
Merge pull request #42 from winebarrel/support_dow_L
Browse files Browse the repository at this point in the history
Support "L" without wday
  • Loading branch information
winebarrel authored Sep 29, 2023
2 parents 8717ea3 + 4c4adb6 commit 4996a38
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 13 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ func main() {
}
```

## About the behavior of "L" in day-of-week

If you specify L for day-of-week, the last day of the week of each month is usually matched.

```
# cron(0 0 ? * 6L *)
Fri, 27 Oct 2023 00:00:00
Fri, 24 Nov 2023 00:00:00
Fri, 29 Dec 2023 00:00:00
Fri, 26 Jan 2024 00:00:00
Fri, 23 Feb 2024 00:00:00
```

However, if you do not specify the day of the week before "L", the behavior will be the same as when you specify "SAT".

```
# cron(0 0 ? * L *) = cron(0 0 ? * SAT *)
Sun, 01 Oct 2023 00:00:00
Sun, 08 Oct 2023 00:00:00
Sun, 15 Oct 2023 00:00:00
Sun, 22 Oct 2023 00:00:00
Sun, 29 Oct 2023 00:00:00
```

# cronplan CLI

CLI to show next triggers.
Expand Down
8 changes: 7 additions & 1 deletion match.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,13 @@ func (v *NthDayOfWeek) Match(t time.Time) bool {
}

func (v *LastDayOfWeek) Match(t time.Time) bool {
return util.LastWdayOfMonth(t, v.Weekday()) == t.Day()
if v.Wday == nil {
// NOTE: If the day of the week is not specified,
// it will be the same as when SAT is specified.
return t.Weekday() == time.Sunday
} else {
return util.LastWdayOfMonth(t, v.Weekday()) == t.Day()
}
}

func (e *DayOfWeekExp) Match(t time.Time) bool {
Expand Down
54 changes: 54 additions & 0 deletions match_intg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,60 @@ func TestIntegrationMatch(t *testing.T) {
{time.Date(2021, 11, 11, 11, 1, 0, 0, time.UTC), false},
},
},
{
exp: "0 0 ? * 6L *",
tests: []struct {
tm time.Time
expected bool
}{
{time.Date(2023, 10, 27, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 24, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 12, 29, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 20, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 17, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 12, 22, 0, 0, 0, 0, time.UTC), false},
},
},
{
exp: "0 0 ? * 7L *",
tests: []struct {
tm time.Time
expected bool
}{
{time.Date(2023, 10, 28, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 25, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 12, 30, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 21, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 18, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 12, 21, 0, 0, 0, 0, time.UTC), false},
},
},
{
exp: "0 0 ? * L *",
tests: []struct {
tm time.Time
expected bool
}{
{time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 8, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 15, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 29, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 5, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 12, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 19, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 11, 26, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 12, 3, 0, 0, 0, 0, time.UTC), true},
{time.Date(2023, 10, 2, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 10, 9, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 10, 16, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 10, 30, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 4, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 11, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 18, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 11, 25, 0, 0, 0, 0, time.UTC), false},
{time.Date(2023, 12, 2, 0, 0, 0, 0, time.UTC), false},
},
},
}

for _, t := range tt {
Expand Down
66 changes: 66 additions & 0 deletions next_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,72 @@ func TestNextN_10(t *testing.T) {
)
}

func TestNextN_10_LastFri(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("0 0 ? * 6L *")
assert.NoError(err)
schedule := cron.NextN(time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC), 10)
assert.Equal(
[]time.Time{
time.Date(2023, time.October, 27, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 24, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.December, 29, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.January, 26, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.February, 23, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.March, 29, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.April, 26, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.May, 31, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.June, 28, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.July, 26, 0, 0, 0, 0, time.UTC),
},
schedule,
)
}

func TestNextN_10_LastSat(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("0 0 ? * SATL *")
assert.NoError(err)
schedule := cron.NextN(time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC), 10)
assert.Equal(
[]time.Time{
time.Date(2023, time.October, 28, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 25, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.December, 30, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.January, 27, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.February, 24, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.March, 30, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.April, 27, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.May, 25, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.June, 29, 0, 0, 0, 0, time.UTC),
time.Date(2024, time.July, 27, 0, 0, 0, 0, time.UTC),
},
schedule,
)
}

func TestNextN_10_LastWdayWithoutWday(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("0 0 ? * L *")
assert.NoError(err)
schedule := cron.NextN(time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC), 10)
assert.Equal(
[]time.Time{
time.Date(2023, time.October, 1, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.October, 8, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.October, 15, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.October, 22, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.October, 29, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 05, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 12, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 19, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.November, 26, 0, 0, 0, 0, time.UTC),
time.Date(2023, time.December, 03, 0, 0, 0, 0, time.UTC),
},
schedule,
)
}

func TestNextN_TZ(t *testing.T) {
jst, err := time.LoadLocation("Asia/Tokyo")

Expand Down
12 changes: 7 additions & 5 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,17 +492,19 @@ func (v *NthDayOfWeek) String() string {
}

type LastDayOfWeek struct {
// NOTE: It seems that "L" without a number can be entered,
// but the schedule is strange so it is not allowed.
Wday Weekday `(@Number | @Weekday) "L"`
Wday *Weekday `(@Number | @Weekday)? "L"`
}

func (v *LastDayOfWeek) Weekday() time.Weekday {
return time.Weekday(v.Wday)
return time.Weekday(*v.Wday)
}

func (v *LastDayOfWeek) String() string {
return fmt.Sprintf("%sL", v.Wday.String())
if v.Wday == nil {
return "L"
} else {
return fmt.Sprintf("%sL", v.Wday.String())
}
}

type DayOfWeekExp struct {
Expand Down
18 changes: 13 additions & 5 deletions parse_day_of_week_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ func TestDayOfWeekLast(t *testing.T) {
}
}

func TestDayOfWeekLastWithoutWday(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("* * ? * L *")
assert.NoError(err)
assert.Equal(&cronplan.LastDayOfWeek{}, cron.DayOfWeek.Exps[0].Last)
}

func TestDayOfWeekName(t *testing.T) {
assert := assert.New(t)
tt := map[string]time.Weekday{
Expand Down Expand Up @@ -161,7 +168,7 @@ func TestDayOfWeekNameIncrRange(t *testing.T) {

func TestDayOfWeekComplex(t *testing.T) {
assert := assert.New(t)
cron, err := cronplan.Parse("* * ? * *,1,1-7,1/5,*/5,5L,MONL,SUN,SUN-SAT *")
cron, err := cronplan.Parse("* * ? * *,1,1-7,1/5,*/5,L,5L,MONL,SUN,SUN-SAT *")
assert.NoError(err)
assert.True(cron.DayOfWeek.Exps[0].Wildcard)
assert.Equal(wday(time.Sunday), cron.DayOfWeek.Exps[1].Wday)
Expand All @@ -177,11 +184,12 @@ func TestDayOfWeekComplex(t *testing.T) {
Wildcard: true,
Bottom: intptr(5),
}, cron.DayOfWeek.Exps[4])
assert.Equal(lastw(time.Thursday), cron.DayOfWeek.Exps[5].Last)
assert.Equal(lastw(time.Monday), cron.DayOfWeek.Exps[6].Last)
assert.Equal(wday(time.Sunday), cron.DayOfWeek.Exps[7].Wday)
assert.Equal(&cronplan.LastDayOfWeek{}, cron.DayOfWeek.Exps[5].Last)
assert.Equal(lastw(time.Thursday), cron.DayOfWeek.Exps[6].Last)
assert.Equal(lastw(time.Monday), cron.DayOfWeek.Exps[7].Last)
assert.Equal(wday(time.Sunday), cron.DayOfWeek.Exps[8].Wday)
assert.Equal(&cronplan.WeekdayRange{
Start: wday(time.Sunday),
End: wday(time.Saturday),
}, cron.DayOfWeek.Exps[8].Range)
}, cron.DayOfWeek.Exps[9].Range)
}
2 changes: 1 addition & 1 deletion parse_intg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func nwday(i int) *cronplan.NearestWeekday {
}

func lastw(i time.Weekday) *cronplan.LastDayOfWeek {
v := cronplan.LastDayOfWeek{*wday(i)}
v := cronplan.LastDayOfWeek{wday(i)}
return &v
}

Expand Down
8 changes: 7 additions & 1 deletion string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,17 @@ func TestLastOfWeekToString(t *testing.T) {
assert := assert.New(t)

for w, s := range tt {
x := cronplan.LastDayOfWeek{*wday(w)}
x := cronplan.LastDayOfWeek{wday(w)}
assert.Equal(s+"L", x.String())
}
}

func TestLastOfWeekToStringWithoutWday(t *testing.T) {
assert := assert.New(t)
x := cronplan.LastDayOfWeek{}
assert.Equal("L", x.String())
}

func TestNearestWeekdayToString(t *testing.T) {
assert := assert.New(t)
x := &cronplan.DayOfMonthExp{
Expand Down

0 comments on commit 4996a38

Please sign in to comment.