package timeext import ( "fmt" "math" "time" ) var TimezoneBerlin *time.Location func init() { var err error TimezoneBerlin, err = time.LoadLocation("Europe/Berlin") if err != nil { panic(fmt.Sprintf("Could not load Timezone: %v", err)) } } // TimeToDatePart returns a timestamp at the start of the day which contains t (= 00:00:00) func TimeToDatePart(t time.Time, tz *time.Location) time.Time { t = t.In(tz) return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) } // TimeToDayStart returns a timestamp at the start of the day which contains t (= 00:00:00) func TimeToDayStart(t time.Time, tz *time.Location) time.Time { t = t.In(tz) return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) } // TimeToDayEnd returns a timestamp at the end of the day which contains t (= 23:59:59) func TimeToDayEnd(t time.Time, tz *time.Location) time.Time { return TimeToDayStart(t, tz).AddDate(0, 0, 1).Add(-1) } // TimeToWeekStart returns a timestamp at the start of the week which contains t (= Monday 00:00:00) func TimeToWeekStart(t time.Time, tz *time.Location) time.Time { t = TimeToDatePart(t, tz) delta := time.Duration(((int64(t.Weekday()) + 6) % 7) * 24 * int64(time.Hour)) t = t.Add(-1 * delta) return t } // TimeToMonthStart returns a timestamp at the start of the month which contains t (= yyyy-MM-00 00:00:00) func TimeToMonthStart(t time.Time, tz *time.Location) time.Time { t = t.In(tz) return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) } // TimeToMonthEnd returns a timestamp at the end of the month which contains t (= yyyy-MM-31 23:59:59.999999999) func TimeToMonthEnd(t time.Time, tz *time.Location) time.Time { return TimeToMonthStart(t, tz).AddDate(0, 1, 0).Add(-1) } // TimeToYearStart returns a timestamp at the start of the year which contains t (= yyyy-01-01 00:00:00) func TimeToYearStart(t time.Time, tz *time.Location) time.Time { t = t.In(tz) return time.Date(t.Year(), 1, 1, 0, 0, 0, 0, t.Location()) } // TimeToYearEnd returns a timestamp at the end of the month which contains t (= yyyy-12-31 23:59:59.999999999) func TimeToYearEnd(t time.Time, tz *time.Location) time.Time { return TimeToYearStart(t, tz).AddDate(1, 0, 0).Add(-1) } func TimeToNextYearStart(t time.Time, tz *time.Location) time.Time { return TimeToYearStart(t, tz).AddDate(1, 0, 0) } // IsSameDayIncludingDateBoundaries returns true if t1 and t2 are part of the same day (TZ/Berlin), the boundaries of the day are // inclusive, this means 2021-09-15T00:00:00 is still part of the day 2021-09-14 func IsSameDayIncludingDateBoundaries(t1 time.Time, t2 time.Time, tz *time.Location) bool { dp1 := TimeToDatePart(t1, tz) dp2 := TimeToDatePart(t2, tz) if dp1.Equal(dp2) { return true } if dp1.AddDate(0, 0, 1).Equal(dp2) && dp2.Equal(t2) { return true } return false } // IsDatePartEqual returns true if a and b have the same date part (`yyyy`, `MM` and `dd`) func IsDatePartEqual(a time.Time, b time.Time, tz *time.Location) bool { yy1, mm1, dd1 := a.In(tz).Date() yy2, mm2, dd2 := b.In(tz).Date() return yy1 == yy2 && mm1 == mm2 && dd1 == dd2 } // WithTimePart returns a timestamp with the date-part (`yyyy`, `MM`, `dd`) from base // and the time (`HH`, `mm`, `ss`) from the parameter func WithTimePart(base time.Time, hour, minute, second int) time.Time { datepart := TimeToDatePart(base, base.Location()) delta := time.Duration(int64(hour)*int64(time.Hour) + int64(minute)*int64(time.Minute) + int64(second)*int64(time.Second)) return datepart.Add(delta) } // CombineDateAndTime returns a timestamp with the date-part (`yyyy`, `MM`, `dd`) from the d parameter // and the time (`HH`, `mm`, `ss`) from the t parameter func CombineDateAndTime(d time.Time, t time.Time) time.Time { datepart := TimeToDatePart(d, d.Location()) delta := time.Duration(int64(t.Hour())*int64(time.Hour) + int64(t.Minute())*int64(time.Minute) + int64(t.Second())*int64(time.Second) + int64(t.Nanosecond())*int64(time.Nanosecond)) return datepart.Add(delta) } // IsSunday returns true if t is a sunday (in TZ/Berlin) func IsSunday(t time.Time, tz *time.Location) bool { if t.In(tz).Weekday() == time.Sunday { return true } return false } func DurationFromTime(hours int, minutes int, seconds int) time.Duration { return time.Duration(int64(hours)*int64(time.Hour) + int64(minutes)*int64(time.Minute) + int64(seconds)*int64(time.Second)) } func Min(a time.Time, b time.Time) time.Time { if a.UnixNano() < b.UnixNano() { return a } else { return b } } func Max(a time.Time, b time.Time) time.Time { if a.UnixNano() > b.UnixNano() { return a } else { return b } } func UnixFloatSeconds(v float64) time.Time { sec, dec := math.Modf(v) return time.Unix(int64(sec), int64(dec*(1e9))) } func FloorTime(t time.Time) time.Time { return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) } func SubtractYears(t time.Time, yearCount float64, tz *time.Location) time.Time { t = t.In(tz) if yearCount < 0 { return AddYears(t, -yearCount, tz) } intCount, floatCount := math.Modf(yearCount) t = t.AddDate(-int(intCount), 0, 0) t0 := TimeToYearStart(t, tz) t1 := TimeToYearEnd(t, tz) return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount * -1)) } func AddYears(t time.Time, yearCount float64, tz *time.Location) time.Time { t = t.In(tz) if yearCount < 0 { return SubtractYears(t, -yearCount, tz) } intCount, floatCount := math.Modf(yearCount) t = t.AddDate(int(intCount), 0, 0) t0 := TimeToYearStart(t, tz) t1 := TimeToYearEnd(t, tz) return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount)) } func DaysInMonth(t time.Time) int { // https://stackoverflow.com/a/73882035/1761622 y, m, _ := t.Date() return time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC).Day() }