calendar_renew.go 1.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. package service
  2. import "time"
  3. // nextCalendarRenewal returns the next renewal strictly after from, at midnight
  4. // in loc; a missing day clamps to the month's last, so the 31st comes back (#6106).
  5. func nextCalendarRenewal(from time.Time, day int, loc *time.Location) time.Time {
  6. if loc == nil {
  7. loc = time.UTC
  8. }
  9. if day < 1 {
  10. day = 1
  11. }
  12. if day > 31 {
  13. day = 31
  14. }
  15. local := from.In(loc)
  16. candidate := calendarDay(local.Year(), local.Month(), day, loc)
  17. if !candidate.After(local) {
  18. year, month := local.Year(), local.Month()+1
  19. if month > time.December {
  20. year, month = year+1, time.January
  21. }
  22. candidate = calendarDay(year, month, day, loc)
  23. }
  24. return candidate
  25. }
  26. // Clamped rather than normalized: time.Date rolls 31 February into March, which
  27. // is the drift this mode exists to avoid.
  28. func calendarDay(year int, month time.Month, day int, loc *time.Location) time.Time {
  29. last := daysInMonth(year, month)
  30. if day > last {
  31. day = last
  32. }
  33. return time.Date(year, month, day, 0, 0, 0, 0, loc)
  34. }
  35. func daysInMonth(year int, month time.Month) int {
  36. return time.Date(year, month+1, 0, 0, 0, 0, 0, time.UTC).Day()
  37. }