diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c96b7f653..a9bc9d5d8 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -21,9 +21,10 @@ package apier import ( "errors" "fmt" + "time" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" - "time" ) type AttrAcntAction struct { @@ -51,7 +52,7 @@ func (self *ApierV1) GetAccountActionPlan(attrs AttrAcntAction, reply *[]*Accoun for _, ats := range allATs { for _, at := range ats { if utils.IsSliceMember(at.UserBalanceIds, utils.BalanceKey(attrs.Tenant, attrs.Account, attrs.Direction)) { - accountATs = append(accountATs, &AccountActionTiming{Id: at.Id, ActionPlanId: at.Tag, ActionsId: at.ActionsId, NextExecTime: at.GetNextStartTime()}) + accountATs = append(accountATs, &AccountActionTiming{Id: at.Id, ActionPlanId: at.Tag, ActionsId: at.ActionsId, NextExecTime: at.GetNextStartTime(time.Now())}) } } } diff --git a/engine/action_timing.go b/engine/action_timing.go index 63aacc5a3..02302201b 100644 --- a/engine/action_timing.go +++ b/engine/action_timing.go @@ -47,7 +47,7 @@ type ActionTiming struct { type ActionPlan []*ActionTiming -func (at *ActionTiming) GetNextStartTime() (t time.Time) { +func (at *ActionTiming) GetNextStartTime(now time.Time) (t time.Time) { if !at.stCache.IsZero() { return at.stCache } @@ -55,7 +55,6 @@ func (at *ActionTiming) GetNextStartTime() (t time.Time) { if i == nil { return } - now := time.Now() y, m, d := now.Date() z, _ := now.Zone() if i.Timing.StartTime != "" && i.Timing.StartTime != ASAP { @@ -88,9 +87,8 @@ func (at *ActionTiming) GetNextStartTime() (t time.Time) { // monthdays if i.Timing.MonthDays != nil && len(i.Timing.MonthDays) > 0 { i.Timing.MonthDays.Sort() - now := time.Now() year := now.Year() - month := now.Month() + month := now x := sort.SearchInts(i.Timing.MonthDays, now.Day()) d = i.Timing.MonthDays[0] if x < len(i.Timing.MonthDays) { @@ -103,33 +101,34 @@ func (at *ActionTiming) GetNextStartTime() (t time.Time) { if x+1 < len(i.Timing.MonthDays) { // today was found in the list, jump to the next grater day d = i.Timing.MonthDays[x+1] } else { // jump to next month - month = now.AddDate(0, 1, 0).Month() + //not using now to make sure the next month has the the 1 date + //(if today is 31) next month may not have it + tmp := time.Date(year, month.Month(), 1, 0, 0, 0, 0, time.Local) + month = tmp.AddDate(0, 1, 0) } } else { // today was not found in the list, x is the first greater day d = i.Timing.MonthDays[x] } } else { if len(i.Timing.Months) == 0 { - t = time.Date(now.Year(), month, d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) - year = t.Year() - month = t.Month() + t = time.Date(month.Year(), month.Month(), d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) + month = t } } h, m, s := t.Clock() - t = time.Date(year, month, d, h, m, s, 0, time.Local) + t = time.Date(month.Year(), month.Month(), d, h, m, s, 0, time.Local) } MONTHS: if i.Timing.Months != nil && len(i.Timing.Months) > 0 { i.Timing.Months.Sort() - now := time.Now() year := now.Year() x := sort.Search(len(i.Timing.Months), func(x int) bool { return i.Timing.Months[x] >= now.Month() }) m = i.Timing.Months[0] if x < len(i.Timing.Months) { if i.Timing.Months[x] == now.Month() { if t.Equal(now) || t.After(now) { - h, m, s := t.Clock() - t = time.Date(now.Year(), now.Month(), t.Day(), h, m, s, 0, time.Local) + //h, m, s := t.Clock() + //t = time.Date(now.Year(), now.Month(), t.Day(), h, m, s, 0, time.Local) goto YEARS } if x+1 < len(i.Timing.Months) { // this month was found in the list so jump to next available month @@ -139,7 +138,10 @@ MONTHS: t = time.Date(t.Year(), t.Month(), i.Timing.MonthDays[0], t.Hour(), t.Minute(), t.Second(), 0, t.Location()) } } else { // jump to next year - year = now.AddDate(1, 0, 0).Year() + //not using now to make sure the next year has the the 1 date + //(if today is 31) next month may not have it + tmp := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local) + year = tmp.AddDate(1, 0, 0).Year() } } else { // this month was not found in the list, x is the first greater month m = i.Timing.Months[x] @@ -160,8 +162,7 @@ MONTHS: YEARS: if i.Timing.Years != nil && len(i.Timing.Years) > 0 { i.Timing.Years.Sort() - now := time.Now() - x := sort.Search(len(i.Timing.Years), func(x int) bool { return i.Timing.Years[x] >= now.Year() }) + x := sort.Search(len(i.Timing.Years), func(x int) bool { return i.Timing.Years[x] >= t.Year() }) y = i.Timing.Years[0] if x < len(i.Timing.Years) { if i.Timing.Years[x] == now.Year() { @@ -287,10 +288,10 @@ func (atpl ActionTimingPriotityList) Swap(i, j int) { } func (atpl ActionTimingPriotityList) Less(i, j int) bool { - if atpl[i].GetNextStartTime().Equal(atpl[j].GetNextStartTime()) { + if atpl[i].GetNextStartTime(time.Now()).Equal(atpl[j].GetNextStartTime(time.Now())) { return atpl[i].Weight < atpl[j].Weight } - return atpl[i].GetNextStartTime().Before(atpl[j].GetNextStartTime()) + return atpl[i].GetNextStartTime(time.Now()).Before(atpl[j].GetNextStartTime(time.Now())) } func (atpl ActionTimingPriotityList) Sort() { @@ -298,7 +299,7 @@ func (atpl ActionTimingPriotityList) Sort() { } func (at *ActionTiming) String_DISABLED() string { - return at.Tag + " " + at.GetNextStartTime().String() + ",w: " + strconv.FormatFloat(at.Weight, 'f', -1, 64) + return at.Tag + " " + at.GetNextStartTime(time.Now()).String() + ",w: " + strconv.FormatFloat(at.Weight, 'f', -1, 64) } // Helper to remove ActionTiming members based on specific filters, empty data means no always match diff --git a/engine/actions_test.go b/engine/actions_test.go index 5f9c039b7..4c234121b 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -27,9 +27,17 @@ import ( "time" ) +var ( + //referenceDate = time.Date(2014, 1, 1, 0, 0, 0, 0, time.Local) + //referenceDate = time.Date(2013, 7, 10, 10, 30, 0, 0, time.Local) + //referenceDate = time.Date(2013, 12, 31, 23, 59, 59, 0, time.Local) + referenceDate = time.Now() + now = referenceDate +) + func TestActionTimingNothing(t *testing.T) { at := &ActionTiming{} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -38,8 +46,8 @@ func TestActionTimingNothing(t *testing.T) { func TestActionTimingOnlyHour(t *testing.T) { at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{StartTime: "10:01:00"}}} - st := at.GetNextStartTime() - now := time.Now() + st := at.GetNextStartTime(referenceDate) + y, m, d := now.Date() expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { @@ -49,8 +57,8 @@ func TestActionTimingOnlyHour(t *testing.T) { func TestActionTimingOnlyWeekdays(t *testing.T) { at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{WeekDays: []time.Weekday{time.Monday}}}} - st := at.GetNextStartTime() - now := time.Now() + st := at.GetNextStartTime(referenceDate) + y, m, d := now.Date() h, min, s := now.Clock() e := time.Date(y, m, d, h, min, s, 0, time.Local) @@ -68,8 +76,8 @@ func TestActionTimingOnlyWeekdays(t *testing.T) { func TestActionTimingHourWeekdays(t *testing.T) { at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}} - st := at.GetNextStartTime() - now := time.Now() + st := at.GetNextStartTime(referenceDate) + y, m, d := now.Date() e := time.Date(y, m, d, 10, 1, 0, 0, time.Local) day := e.Day() @@ -85,11 +93,11 @@ func TestActionTimingHourWeekdays(t *testing.T) { } func TestActionTimingOnlyMonthdays(t *testing.T) { - now := time.Now() + y, m, d := now.Date() tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{MonthDays: utils.MonthDays{1, 25, 2, tomorrow.Day()}}}} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := tomorrow if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -97,29 +105,28 @@ func TestActionTimingOnlyMonthdays(t *testing.T) { } func TestActionTimingHourMonthdays(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) - day := now.Day() if now.After(testTime) { - day = tomorrow.Day() + y, m, d = tomorrow.Date() } at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{MonthDays: utils.MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}} - st := at.GetNextStartTime() - expected := time.Date(y, m, day, 10, 1, 0, 0, time.Local) + st := at.GetNextStartTime(referenceDate) + expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) } } func TestActionTimingOnlyMonths(t *testing.T) { - now := time.Now() + y, m, _ := now.Date() nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) t.Log("NextMonth: ", nextMonth) at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Months: utils.Months{time.February, time.May, nextMonth.Month()}}}} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -127,34 +134,37 @@ func TestActionTimingOnlyMonths(t *testing.T) { } func TestActionTimingHourMonths(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) - nextMonth := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) - month := now.Month() + nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) if now.After(testTime) { - month = nextMonth.Month() + m = nextMonth.Month() + y = nextMonth.Year() + } - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Months: utils.Months{now.Month(), nextMonth.Month()}, StartTime: "10:01:00"}}} - st := at.GetNextStartTime() - expected := time.Date(y, month, d, 10, 1, 0, 0, time.Local) + at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{ + Months: utils.Months{now.Month(), nextMonth.Month()}, + StartTime: "10:01:00"}}} + st := at.GetNextStartTime(referenceDate) + expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) } } func TestActionTimingHourMonthdaysMonths(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) - nextMonth := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) + nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) - day := now.Day() + if now.After(testTime) { - day = tomorrow.Day() + y, m, d = tomorrow.Date() } - nextDay := time.Date(y, m, day, 10, 1, 0, 0, time.Local) - month := now.Month() + nextDay := time.Date(y, m, d, 10, 1, 0, 0, time.Local) + month := nextDay.Month() if nextDay.Before(now) { if now.After(testTime) { month = nextMonth.Month() @@ -167,15 +177,15 @@ func TestActionTimingHourMonthdaysMonths(t *testing.T) { StartTime: "10:01:00", }, }} - st := at.GetNextStartTime() - expected := time.Date(y, month, day, 10, 1, 0, 0, time.Local) + st := at.GetNextStartTime(referenceDate) + expected := time.Date(y, month, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) } } func TestActionTimingFirstOfTheMonth(t *testing.T) { - now := time.Now() + y, m, _ := now.Date() nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) at := &ActionTiming{Timing: &RateInterval{ @@ -183,7 +193,7 @@ func TestActionTimingFirstOfTheMonth(t *testing.T) { MonthDays: utils.MonthDays{1}, }, }} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := nextMonth if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -191,11 +201,11 @@ func TestActionTimingFirstOfTheMonth(t *testing.T) { } func TestActionTimingOnlyYears(t *testing.T) { - now := time.Now() + y, m, d := now.Date() nextYear := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{now.Year(), nextYear.Year()}}}} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := time.Date(nextYear.Year(), 1, 1, 0, 0, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -203,7 +213,7 @@ func TestActionTimingOnlyYears(t *testing.T) { } func TestActionTimingHourYears(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) nextYear := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) @@ -212,7 +222,7 @@ func TestActionTimingHourYears(t *testing.T) { year = nextYear.Year() } at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{now.Year(), nextYear.Year()}, StartTime: "10:01:00"}}} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := time.Date(year, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -220,17 +230,16 @@ func TestActionTimingHourYears(t *testing.T) { } func TestActionTimingHourMonthdaysYear(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) - nextYear := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) + nextYear := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) - day := now.Day() if now.After(testTime) { - day = tomorrow.Day() + y, m, d = tomorrow.Date() } - nextDay := time.Date(y, m, day, 10, 1, 0, 0, time.Local) - year := now.Year() + nextDay := time.Date(y, m, d, 10, 1, 0, 0, time.Local) + year := nextDay.Year() if nextDay.Before(now) { if now.After(testTime) { year = nextYear.Year() @@ -243,19 +252,19 @@ func TestActionTimingHourMonthdaysYear(t *testing.T) { StartTime: "10:01:00", }, }} - st := at.GetNextStartTime() - expected := time.Date(year, m, day, 10, 1, 0, 0, time.Local) + st := at.GetNextStartTime(referenceDate) + expected := time.Date(year, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) } } func TestActionTimingHourMonthdaysMonthYear(t *testing.T) { - now := time.Now() + y, m, d := now.Date() testTime := time.Date(y, m, d, 10, 1, 0, 0, time.Local) - nextYear := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) - nextMonth := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) + nextYear := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) + nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) day := now.Day() if now.After(testTime) { @@ -283,7 +292,7 @@ func TestActionTimingHourMonthdaysMonthYear(t *testing.T) { StartTime: "10:01:00", }, }} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := time.Date(year, month, day, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -291,7 +300,6 @@ func TestActionTimingHourMonthdaysMonthYear(t *testing.T) { } func TestActionTimingFirstOfTheYear(t *testing.T) { - now := time.Now() y, _, _ := now.Date() nextYear := time.Date(y, 1, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) at := &ActionTiming{Timing: &RateInterval{ @@ -302,7 +310,7 @@ func TestActionTimingFirstOfTheYear(t *testing.T) { StartTime: "00:00:00", }, }} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := nextYear if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -310,7 +318,6 @@ func TestActionTimingFirstOfTheYear(t *testing.T) { } func TestActionTimingFirstMonthOfTheYear(t *testing.T) { - now := time.Now() y, _, _ := now.Date() nextYear := time.Date(y, 1, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) at := &ActionTiming{Timing: &RateInterval{ @@ -318,7 +325,7 @@ func TestActionTimingFirstMonthOfTheYear(t *testing.T) { Months: utils.Months{time.January}, }, }} - st := at.GetNextStartTime() + st := at.GetNextStartTime(referenceDate) expected := nextYear if !st.Equal(expected) { t.Errorf("Expected %v was %v", expected, st) @@ -345,7 +352,7 @@ func TestActionTimingIsOneTimeRun(t *testing.T) { func TestActionTimingOneTimeRun(t *testing.T) { at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{StartTime: ASAP}}} at.CheckForASAP() - nextRun := at.GetNextStartTime() + nextRun := at.GetNextStartTime(referenceDate) if nextRun.IsZero() { t.Error("next time failed for asap") } diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 14a1a50eb..dacc596e7 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -46,7 +46,7 @@ func (s *Scheduler) Loop() { s.Lock() a0 := s.queue[0] now := time.Now() - if a0.GetNextStartTime().Equal(now) || a0.GetNextStartTime().Before(now) { + if a0.GetNextStartTime(now).Equal(now) || a0.GetNextStartTime(now).Before(now) { engine.Logger.Debug(fmt.Sprintf("%v - %v", a0.Tag, a0.Timing)) go a0.Execute() s.queue = append(s.queue, a0) @@ -55,7 +55,7 @@ func (s *Scheduler) Loop() { s.Unlock() } else { s.Unlock() - d := a0.GetNextStartTime().Sub(now) + d := a0.GetNextStartTime(now).Sub(now) // engine.Logger.Info(fmt.Sprintf("Timer set to wait for %v", d)) s.timer = time.NewTimer(d) select {