package planner import ( "time" "github.com/evcc-io/evcc/api" ) // Start returns the earliest slot's start time func Start(plan api.Rates) time.Time { var start time.Time for _, slot := range plan { if start.IsZero() || slot.Start.Before(start) { start = slot.Start } } return start } func End(plan api.Rates) time.Time { var end time.Time for _, slot := range plan { if end.IsZero() || slot.End.After(end) { end = slot.End } } return end } // Duration returns the sum of all slot's durations func Duration(plan api.Rates) time.Duration { var duration time.Duration for _, slot := range plan { slotDuration := slot.End.Sub(slot.Start) duration += slotDuration } return duration } // AverageCost returns the time-weighted average cost func AverageCost(plan api.Rates) float64 { var cost float64 var duration time.Duration for _, slot := range plan { slotDuration := slot.End.Sub(slot.Start) duration += slotDuration cost += float64(slotDuration) * slot.Value } return cost / float64(duration) } // SlotAt returns the slot for the given time or an empty slot func SlotAt(time time.Time, plan api.Rates) api.Rate { for _, slot := range plan { if !slot.Start.After(time) && slot.End.After(time) { return slot } } return api.Rate{} } // SlotHasSuccessor returns if the slot has an immediate successor. // Does not require the plan to be sorted by start time. func SlotHasSuccessor(r api.Rate, plan api.Rates) bool { for _, slot := range plan { if r.End.Equal(slot.Start) { return true } } return false } // IsFirst returns if the slot is the first slot in the plan. // Does not require the plan to be sorted by start time. func IsFirst(r api.Rate, plan api.Rates) bool { for _, slot := range plan { if r.Start.After(slot.Start) { return false } } return true }