diff --git a/schtasks/task.go b/schtasks/task.go index aa1d97b9..00f2b06e 100644 --- a/schtasks/task.go +++ b/schtasks/task.go @@ -30,9 +30,10 @@ type Task struct { Principals Principals `xml:"Principals"` Settings Settings `xml:"Settings"` Actions Actions `xml:"Actions"` + fromNow time.Time `xml:"-"` } -func NewTask() Task { +func NewTask(options ...TaskOption) Task { var userID string if currentUser, err := user.Current(); err == nil { userID = currentUser.Uid @@ -69,6 +70,11 @@ func NewTask() Task { Actions: Actions{ Context: author, }, + fromNow: time.Now(), + } + + for _, option := range options { + option.apply(&task) } return task } @@ -105,9 +111,14 @@ func (t *Task) AddSchedules(schedules []*calendar.Event) { } } +func (t *Task) setFromNow(fromNow time.Time) { + t.fromNow = fromNow + t.RegistrationInfo.Date = fromNow.Format(dateFormat) +} + func (t *Task) addTimeTrigger(triggerOnce time.Time) { timeTrigger := TimeTrigger{ - StartBoundary: triggerOnce.Format(dateFormat), + StartBoundary: &triggerOnce, } if t.Triggers.TimeTrigger == nil { t.Triggers.TimeTrigger = []TimeTrigger{timeTrigger} @@ -125,7 +136,7 @@ func (t *Task) addCalendarTrigger(trigger CalendarTrigger) { } func (t *Task) addDailyTrigger(schedule *calendar.Event) { - start := schedule.Next(time.Now()) + start := schedule.Next(t.fromNow) // get all recurrences in the same day recurrences := schedule.GetAllInBetween(start, start.Add(24*time.Hour)) if len(recurrences) == 0 { @@ -135,7 +146,7 @@ func (t *Task) addDailyTrigger(schedule *calendar.Event) { // Is it only once a day? if len(recurrences) == 1 { t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrences[0].Format(dateFormat), + StartBoundary: &recurrences[0], ScheduleByDay: &ScheduleByDay{ DaysInterval: 1, }, @@ -149,7 +160,7 @@ func (t *Task) addDailyTrigger(schedule *calendar.Event) { // case with regular repetition interval := period.NewOf(compactDifferences[0]) t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: start.Format(dateFormat), + StartBoundary: &start, ScheduleByDay: &ScheduleByDay{ DaysInterval: 1, }, @@ -168,7 +179,7 @@ func (t *Task) addDailyTrigger(schedule *calendar.Event) { // install them all for _, recurrence := range recurrences { t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrence.Format(dateFormat), + StartBoundary: &recurrence, ScheduleByDay: &ScheduleByDay{ DaysInterval: 1, }, @@ -177,7 +188,7 @@ func (t *Task) addDailyTrigger(schedule *calendar.Event) { } func (t *Task) addWeeklyTrigger(schedule *calendar.Event) { - start := schedule.Next(time.Now()) + start := schedule.Next(t.fromNow) // get all recurrences in the same day recurrences := schedule.GetAllInBetween(start, start.Add(24*time.Hour)) if len(recurrences) == 0 { @@ -187,7 +198,7 @@ func (t *Task) addWeeklyTrigger(schedule *calendar.Event) { // Is it only once per 24h? if len(recurrences) == 1 { t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrences[0].Format(dateFormat), + StartBoundary: &recurrences[0], ScheduleByWeek: &ScheduleByWeek{ WeeksInterval: 1, DaysOfWeek: convertWeekdays(schedule.WeekDay.GetRangeValues()), @@ -202,7 +213,7 @@ func (t *Task) addWeeklyTrigger(schedule *calendar.Event) { // case with regular repetition interval := period.NewOf(compactDifferences[0]) t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: start.Format(dateFormat), + StartBoundary: &start, ScheduleByWeek: &ScheduleByWeek{ WeeksInterval: 1, DaysOfWeek: convertWeekdays(schedule.WeekDay.GetRangeValues()), @@ -222,7 +233,7 @@ func (t *Task) addWeeklyTrigger(schedule *calendar.Event) { // install them all for _, recurrence := range recurrences { t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrence.Format(dateFormat), + StartBoundary: &recurrence, ScheduleByWeek: &ScheduleByWeek{ WeeksInterval: 1, DaysOfWeek: convertWeekdays(schedule.WeekDay.GetRangeValues()), @@ -232,7 +243,7 @@ func (t *Task) addWeeklyTrigger(schedule *calendar.Event) { } func (t *Task) addMonthlyTrigger(schedule *calendar.Event) { - start := schedule.Next(time.Now()) + start := schedule.Next(t.fromNow) // get all recurrences in the same day recurrences := schedule.GetAllInBetween(start, start.Add(24*time.Hour)) if len(recurrences) == 0 { @@ -252,7 +263,7 @@ func (t *Task) addMonthlyTrigger(schedule *calendar.Event) { } if schedule.WeekDay.HasValue() { t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrence.Format(dateFormat), + StartBoundary: &recurrence, ScheduleByMonthDayOfWeek: &ScheduleByMonthDayOfWeek{ DaysOfWeek: convertWeekdays(schedule.WeekDay.GetRangeValues()), Weeks: AllWeeks, @@ -262,7 +273,7 @@ func (t *Task) addMonthlyTrigger(schedule *calendar.Event) { continue } t.addCalendarTrigger(CalendarTrigger{ - StartBoundary: recurrence.Format(dateFormat), + StartBoundary: &recurrence, ScheduleByMonth: &ScheduleByMonth{ DaysOfMonth: convertDaysOfMonth(schedule.Day.GetRangeValues()), Months: convertMonths(schedule.Month.GetRangeValues()), diff --git a/schtasks/task_options.go b/schtasks/task_options.go new file mode 100644 index 00000000..b9c1905f --- /dev/null +++ b/schtasks/task_options.go @@ -0,0 +1,21 @@ +//go:build windows + +package schtasks + +import "time" + +type TaskOption interface { + apply(t *Task) +} + +type WithFromNowOption struct { + now time.Time +} + +func WithFromNow(now time.Time) WithFromNowOption { + return WithFromNowOption{now: now} +} + +func (w WithFromNowOption) apply(t *Task) { + t.setFromNow(w.now) +} diff --git a/schtasks/taskscheduler.go b/schtasks/taskscheduler.go index 7587dbc9..bddedf49 100644 --- a/schtasks/taskscheduler.go +++ b/schtasks/taskscheduler.go @@ -28,6 +28,7 @@ import ( "slices" "strings" "text/tabwriter" + "time" "github.com/creativeprojects/clog" "github.com/creativeprojects/resticprofile/calendar" @@ -53,8 +54,7 @@ func Create(config *Config, schedules []*calendar.Event, permission Permission) return fmt.Errorf("cannot delete existing task to replace it: %w", err) } } - - task := createTaskDefinition(config, schedules) + task := createTaskDefinition(config, schedules, time.Time{}) task.RegistrationInfo.URI = taskPath switch config.RunLevel { @@ -181,8 +181,12 @@ func getTaskPath(profileName, commandName string) string { return fmt.Sprintf("%s%s %s", tasksPathPrefix, profileName, commandName) } -func createTaskDefinition(config *Config, schedules []*calendar.Event) Task { - task := NewTask() +func createTaskDefinition(config *Config, schedules []*calendar.Event, from time.Time) Task { + options := make([]TaskOption, 0, 1) + if !from.IsZero() { + options = append(options, WithFromNow(from)) + } + task := NewTask(options...) task.RegistrationInfo.Description = config.JobDescription task.AddExecAction(ExecAction{ Command: config.Command, diff --git a/schtasks/taskscheduler_test.go b/schtasks/taskscheduler_test.go index 6c4d0359..d751fa85 100644 --- a/schtasks/taskscheduler_test.go +++ b/schtasks/taskscheduler_test.go @@ -125,98 +125,131 @@ func TestTaskSchedulerIntegration(t *testing.T) { fixtures := []struct { description string schedules []string + fromNow time.Time }{ { "only once", []string{"2020-01-02 03:04"}, + time.Time{}, }, // daily { "once every day", []string{"*-*-* 03:04"}, + time.Time{}, }, { "every hour", []string{"*-*-* *:04"}, + time.Time{}, }, { "every minute", []string{"*-*-* *:*"}, + time.Time{}, }, { - "every minute at 12", + "every minute at 12 (before 12)", []string{"*-*-* 12:*"}, + time.Date(2025, 7, 27, 11, 20, 0, 0, time.UTC), + }, + // this creates 60 triggers + // { + // "every minute at 12", + // []string{"*-*-* 12:*"}, + // time.Date(2025, 7, 27, 12, 20, 0, 0, time.UTC), + // }, + { + "every minute at 12 (after 12)", + []string{"*-*-* 12:*"}, + time.Date(2025, 7, 27, 13, 20, 0, 0, time.UTC), }, // daily - more than one { "three times a day", []string{"*-*-* 03..05:04"}, + time.Time{}, }, { "twice every hour", []string{"*-*-* *:04..05"}, + time.Time{}, }, // weekly { "once weekly", []string{"mon *-*-* 03:04"}, + time.Time{}, }, { "every hour on mondays", []string{strings.ToLower(fixedDay)[:3] + " *-*-* *:04"}, + time.Time{}, }, { "every minute on mondays", []string{strings.ToLower(fixedDay)[:3] + " *-*-* *:*"}, + time.Time{}, }, { "every minute at 12 on mondays", []string{"mon *-*-* 12:*"}, + time.Time{}, }, // more than once weekly { "twice weekly", []string{"mon *-*-* 03..04:04"}, + time.Time{}, }, { "twice mondays and tuesdays", []string{"mon,tue *-*-* 03:04..06"}, + time.Time{}, }, { "twice on fridays", []string{"fri *-*-* *:04..05"}, + time.Time{}, }, // monthly { "once monthly", []string{"*-01-* 03:04"}, + time.Time{}, }, { "every hour in january", []string{"*-01-* *:04"}, + time.Time{}, }, // monthly with weekdays { "mondays in January", []string{"mon *-01-* 03:04"}, + time.Time{}, }, { "every hour on Mondays in january", []string{"mon *-01-* *:04"}, + time.Time{}, }, // some days every month { "one day per month", []string{"*-*-0" + dayOfTheMonth + " 03:04"}, + time.Time{}, }, { "every hour on the 1st of each month", []string{"*-*-0" + dayOfTheMonth + " *:04"}, + time.Time{}, }, // more than once per month { "twice in one day per month", []string{"*-*-0" + dayOfTheMonth + " 03..04:04"}, + time.Time{}, }, } @@ -247,13 +280,15 @@ func TestTaskSchedulerIntegration(t *testing.T) { defer file.Close() taskPath := getTaskPath(config.ProfileName, config.CommandName) - sourceTask := createTaskDefinition(config, schedules) + sourceTask := createTaskDefinition(config, schedules, fixture.fromNow) sourceTask.RegistrationInfo.URI = taskPath err = createTaskFile(sourceTask, file) require.NoError(t, err) file.Close() + t.Logf("task contains %d time triggers and %d calendar triggers", len(sourceTask.Triggers.TimeTrigger), len(sourceTask.Triggers.CalendarTrigger)) + result, err := createTask(taskPath, file.Name(), "", "") t.Log(result) require.NoError(t, err) @@ -271,6 +306,9 @@ func TestTaskSchedulerIntegration(t *testing.T) { err = decoder.Decode(&readTask) require.NoError(t, err) + sourceTask.fromNow = time.Time{} // ignore fromNow in the source task + taskInUTC(&sourceTask) + taskInUTC(readTask) assert.Equal(t, sourceTask, *readTask) result, err = deleteTask(taskPath) @@ -287,3 +325,21 @@ func TestRunLevelOption(t *testing.T) { // see related: https://github.com/creativeprojects/resticprofile/issues/545 // TODO: implement test when possible } + +func taskInUTC(task *Task) { + // Windows Task Scheduler is using the current timezone when loading dates into the XML definition. + // This is a workaround to ensure that the tests run consistently. + for i := range task.Triggers.TimeTrigger { + if task.Triggers.TimeTrigger[i].StartBoundary != nil { + *task.Triggers.TimeTrigger[i].StartBoundary = task.Triggers.TimeTrigger[i].StartBoundary.UTC() + } + } + for i := range task.Triggers.CalendarTrigger { + if task.Triggers.CalendarTrigger[i].StartBoundary != nil { + *task.Triggers.CalendarTrigger[i].StartBoundary = task.Triggers.CalendarTrigger[i].StartBoundary.UTC() + } + if task.Triggers.CalendarTrigger[i].EndBoundary != nil { + *task.Triggers.CalendarTrigger[i].EndBoundary = task.Triggers.CalendarTrigger[i].EndBoundary.UTC() + } + } +} diff --git a/schtasks/trigger.go b/schtasks/trigger.go index e855f314..d28a4d04 100644 --- a/schtasks/trigger.go +++ b/schtasks/trigger.go @@ -3,6 +3,8 @@ package schtasks import ( + "time" + "github.com/rickb777/period" ) @@ -13,14 +15,14 @@ type Triggers struct { type TimeTrigger struct { Enabled *bool `xml:"Enabled"` // indicates whether the trigger is enabled - StartBoundary string `xml:"StartBoundary"` + StartBoundary *time.Time `xml:"StartBoundary"` ExecutionTimeLimit *period.Period `xml:"ExecutionTimeLimit"` RandomDelay *period.Period `xml:"RandomDelay,omitempty"` // a delay time that is randomly added to the start time of the trigger } type CalendarTrigger struct { - StartBoundary string `xml:"StartBoundary,omitempty"` // the date and time when the trigger is activated - EndBoundary string `xml:"EndBoundary,omitempty"` // the date and time when the trigger is deactivated + StartBoundary *time.Time `xml:"StartBoundary,omitempty"` // the date and time when the trigger is activated + EndBoundary *time.Time `xml:"EndBoundary,omitempty"` // the date and time when the trigger is deactivated Repetition *RepetitionPattern `xml:"Repetition"` ExecutionTimeLimit *period.Period `xml:"ExecutionTimeLimit"` // the maximum amount of time that the task launched by this trigger is allowed to run Enabled *bool `xml:"Enabled"` // indicates whether the trigger is enabled diff --git a/schtasks/trigger_test.go b/schtasks/trigger_test.go index 76acb60f..7ff269c9 100644 --- a/schtasks/trigger_test.go +++ b/schtasks/trigger_test.go @@ -32,145 +32,183 @@ func TestTriggerCreationFromXML(t *testing.T) { fixedDay = "Tuesday" } + timezone := `(Z|[+-]\d{2}:\d{2})` + fixtures := []struct { description string schedules []string expected string expectedMatchCount int + from time.Time }{ { "only once", []string{"2020-01-02 03:04"}, - `\s*2020-01-02T03:04:00\+00:00\s*`, + `\s*2020-01-02T03:04:00` + timezone + `\s*`, 1, + time.Time{}, }, // daily { "once every day", []string{"*-*-* 03:04"}, - `\s*\d{4}-\d{2}-\d{2}T03:04:00\+00:00\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T03:04:00` + timezone + `\s*\s*1\s*\s*`, 1, + time.Time{}, }, { "every hour", []string{"*-*-* *:04"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:04:00\+00:00\s*\s*PT1H\s*PT23H\s*\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:04:00` + timezone + `\s*\s*PT1H\s*PT23H\s*\s*\s*1\s*\s*`, 1, + time.Time{}, }, { "every minute", []string{"*-*-* *:*"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00\+00:00\s*\s*PT1M\s*P1D\s*\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00` + timezone + `\s*\s*PT1M\s*P1D\s*\s*\s*1\s*\s*`, 1, + time.Time{}, }, + // { + // "every minute at 12 (before 12)", + // []string{"*-*-* 12:*"}, + // `\s*\d{4}-\d{2}-\d{2}T11:\d{2}:00` + timezone + `\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*`, + // 1, + // time.Date(2025, 7, 27, 11, 20, 0, 0, time.UTC), + // }, + // { + // "every minute at 12", + // []string{"*-*-* 12:*"}, + // `\s*\d{4}-\d{2}-\d{2}T12:\d{2}:00` + timezone + `\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*`, + // 1, + // time.Date(2025, 7, 27, 12, 20, 0, 0, time.UTC), + // }, { - "every minute at 12", + "every minute at 12 (after 12)", []string{"*-*-* 12:*"}, - `\s*\d{4}-\d{2}-\d{2}T12:\d{2}:00\+00:00\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T13:\d{2}:00` + timezone + `\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*`, 1, + time.Date(2025, 7, 27, 13, 20, 0, 0, time.UTC), }, // daily - more than one { "three times a day", []string{"*-*-* 03..05:04"}, - `\s*\d{4}-\d{2}-\d{2}T03:04:00\+00:00\s*\s*PT1H\s*PT2H\s*\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T03:04:00` + timezone + `\s*\s*PT1H\s*PT2H\s*\s*\s*1\s*\s*`, 1, + time.Time{}, }, { "twice every hour", []string{"*-*-* *:04..05"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00\+00:00\s*\s*1\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00` + timezone + `\s*\s*1\s*\s*`, 48, + time.Time{}, }, // weekly { "once weekly", []string{"mon *-*-* 03:04"}, - `\s*\d{4}-\d{2}-\d{2}T03:04:00\+00:00\s*\s*1\s*\s*\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T03:04:00` + timezone + `\s*\s*1\s*\s*\s*\s*\s*`, 1, + time.Time{}, }, { "every hour on mondays", []string{strings.ToLower(fixedDay)[:3] + " *-*-* *:04"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:04:00\+00:00\s*\s*PT1H\s*PT23H\s*\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:04:00` + timezone + `\s*\s*PT1H\s*PT23H\s*\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, 1, + time.Time{}, }, { "every minute on mondays", []string{strings.ToLower(fixedDay)[:3] + " *-*-* *:*"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00\+00:00\s*\s*PT1M\s*P1D\s*\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00` + timezone + `\s*\s*PT1M\s*P1D\s*\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, 1, + time.Time{}, }, { "every minute at 12 on mondays", []string{"mon *-*-* 12:*"}, - `\s*\d{4}-\d{2}-\d{2}T12:\d{2}:00\+00:00\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T12:\d{2}:00` + timezone + `\s*\s*PT1M\s*PT59M\s*\s*\s*1\s*\s*\s*\s*\s*`, 1, + time.Time{}, }, // more than once weekly { "twice weekly", []string{"mon *-*-* 03..04:04"}, - `\s*\d{4}-\d{2}-\d{2}T03:04:00\+00:00\s*\s*PT1H\s*PT1H\s*\s*\s*1\s*\s*\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T03:04:00` + timezone + `\s*\s*PT1H\s*PT1H\s*\s*\s*1\s*\s*\s*\s*\s*`, 1, + time.Time{}, }, { "twice mondays and tuesdays", []string{"mon,tue *-*-* 03:04..06"}, - `\s*\d{4}-\d{2}-\d{2}T03:04:00\+00:00\s*\s*PT1M\s*PT2M\s*\s*\s*1\s*\s*\s*\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T03:04:00` + timezone + `\s*\s*PT1M\s*PT2M\s*\s*\s*1\s*\s*\s*\s*\s*\s*`, 1, + time.Time{}, }, { "twice on fixed day", []string{strings.ToLower(fixedDay)[:3] + " *-*-* *:04..05"}, - `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00\+00:00\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, + `\s*\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00` + timezone + `\s*\s*1\s*\s*<` + fixedDay + `>\s*\s*\s*`, 48, + time.Time{}, }, // monthly { "once monthly", []string{"*-01-* 03:04"}, - `\s*\d{4}-01-\d{2}T03:04:00\+00:00\s*\s*\s*\s*\s*\s*` + generateEveryDayString() + `\s*\s*`, + `\s*\d{4}-01-\d{2}T03:04:00` + timezone + `\s*\s*\s*\s*\s*\s*` + generateEveryDayString() + `\s*\s*`, 1, + time.Time{}, }, { "every hour in january", []string{"*-01-* *:04"}, - `\s*\d{4}-01-\d{2}T\d{2}:04:00\+00:00\s*\s*\s*\s*\s*\s*` + generateEveryDayString() + `\s*\s*`, + `\s*\d{4}-01-\d{2}T\d{2}:04:00` + timezone + `\s*\s*\s*\s*\s*\s*` + generateEveryDayString() + `\s*\s*`, 24, + time.Time{}, }, // monthly with weekdays { "mondays in January", []string{"mon *-01-* 03:04"}, - `\s*\d{4}-01-\d{2}T03:04:00\+00:00\s*\s*\s*\s*\s*\s*1\s*2\s*3\s*4\s*Last\s*\s*\s*\s*\s*\s*`, + `\s*\d{4}-01-\d{2}T03:04:00` + timezone + `\s*\s*\s*\s*\s*\s*1\s*2\s*3\s*4\s*Last\s*\s*\s*\s*\s*\s*`, 1, + time.Time{}, }, { "every hour on Mondays in january", []string{"mon *-01-* *:04"}, - `\s*\d{4}-01-\d{2}T\d{2}:04:00\+00:00\s*\s*\s*\s*\s*\s*1\s*2\s*3\s*4\s*Last\s*\s*\s*\s*\s*\s*`, + `\s*\d{4}-01-\d{2}T\d{2}:04:00` + timezone + `\s*\s*\s*\s*\s*\s*1\s*2\s*3\s*4\s*Last\s*\s*\s*\s*\s*\s*`, 24, + time.Time{}, }, // // some days every month { "one day per month", []string{"*-*-0" + dayOfTheMonth + " 03:04"}, - `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T03:04:00\+00:00\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, + `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T03:04:00` + timezone + `\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, 1, + time.Time{}, }, { "every hour on the 1st of each month", []string{"*-*-0" + dayOfTheMonth + " *:04"}, - `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T\d{2}:04:00\+00:00\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, + `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T\d{2}:04:00` + timezone + `\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, 24, // 1 per hour + time.Time{}, }, // // more than once per month { "twice in one day per month", []string{"*-*-0" + dayOfTheMonth + " 03..04:04"}, - `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T\d{2}:04:00\+00:00\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, + `\s*\d{4}-\d{2}-0` + dayOfTheMonth + `T\d{2}:04:00` + timezone + `\s*\s*\s*` + generateEveryMonthString() + `\s*\s*` + dayOfTheMonth + `\s*\s*\s*`, 2, + time.Time{}, }, } @@ -196,7 +234,8 @@ func TestTriggerCreationFromXML(t *testing.T) { schedules[index] = event } buffer := &bytes.Buffer{} - task := createTaskDefinition(scheduleConfig, schedules) + task := createTaskDefinition(scheduleConfig, schedules, fixture.from) + taskInLocal(&task) err = createTaskFile(task, buffer) require.NoError(t, err) @@ -226,3 +265,19 @@ func generateEveryMonthString() string { } return everyMonth } + +func taskInLocal(task *Task) { + for i := range task.Triggers.TimeTrigger { + if task.Triggers.TimeTrigger[i].StartBoundary != nil { + *task.Triggers.TimeTrigger[i].StartBoundary = task.Triggers.TimeTrigger[i].StartBoundary.Local() + } + } + for i := range task.Triggers.CalendarTrigger { + if task.Triggers.CalendarTrigger[i].StartBoundary != nil { + *task.Triggers.CalendarTrigger[i].StartBoundary = task.Triggers.CalendarTrigger[i].StartBoundary.Local() + } + if task.Triggers.CalendarTrigger[i].EndBoundary != nil { + *task.Triggers.CalendarTrigger[i].EndBoundary = task.Triggers.CalendarTrigger[i].EndBoundary.Local() + } + } +}