diff --git a/goextVersion.go b/goextVersion.go index 3dddc2d..9d52344 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.480" +const GoextVersion = "0.0.481" -const GoextVersionTimestamp = "2024-07-02T11:32:22+0200" +const GoextVersionTimestamp = "2024-07-04T16:24:49+0200" diff --git a/timeext/diff.go b/timeext/diff.go new file mode 100644 index 0000000..118dadc --- /dev/null +++ b/timeext/diff.go @@ -0,0 +1,13 @@ +package timeext + +import "time" + +func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { + + yDelta := float64(t1.Year() - t2.Year()) + + processT1 := float64(t1.Sub(TimeToYearStart(t1, tz))) / float64(TimeToYearEnd(t1, tz).Sub(TimeToYearStart(t1, tz))) + processT2 := float64(t2.Sub(TimeToYearStart(t2, tz))) / float64(TimeToYearEnd(t2, tz).Sub(TimeToYearStart(t2, tz))) + + return yDelta + (processT1 - processT2) +} diff --git a/timeext/diff_test.go b/timeext/diff_test.go new file mode 100644 index 0000000..79cbf4d --- /dev/null +++ b/timeext/diff_test.go @@ -0,0 +1,83 @@ +package timeext + +import ( + "math" + "testing" + "time" +) + +func TestYearDifferenceWithSameYearAndDay(t *testing.T) { + t1 := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + t2 := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + expected := 0.0 + result := YearDifference(t1, t2, time.UTC) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceWithOneYearApart(t *testing.T) { + t1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC) + t2 := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + expected := 1.0 + result := YearDifference(t1, t2, time.UTC) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceWithDifferentMonths(t *testing.T) { + t1 := time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC) + t2 := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + expected := 0.4166666666666667 // Approximation of 5/12 months + result := YearDifference(t1, t2, time.UTC) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceAcrossYears(t *testing.T) { + t1 := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC) + t2 := time.Date(2020, 6, 1, 0, 0, 0, 0, time.UTC) + expected := 0.5833333333333334 // Approximation of 7/12 months + result := YearDifference(t1, t2, time.UTC) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceWithTimezone(t *testing.T) { + tz, _ := time.LoadLocation("America/New_York") + t1 := time.Date(2021, 1, 1, 0, 0, 0, 0, tz) + t2 := time.Date(2020, 6, 1, 0, 0, 0, 0, tz) + expected := 0.5833333333333334 // Same as UTC but ensuring timezone is considered + result := YearDifference(t1, t2, tz) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceWithNegativeDifference(t *testing.T) { + t1 := time.Date(2020, 1, 1, 0, 0, 0, 0, TimezoneBerlin) + t2 := time.Date(2021, 1, 1, 0, 0, 0, 0, TimezoneBerlin) + expected := -1.0 + result := YearDifference(t1, t2, TimezoneBerlin) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestYearDifferenceWithNegativeDifference2(t *testing.T) { + t1 := time.Date(2020, 7, 1, 0, 0, 0, 0, TimezoneBerlin) + t2 := time.Date(2021, 7, 1, 0, 0, 0, 0, TimezoneBerlin) + expected := -1.0 + result := YearDifference(t1, t2, TimezoneBerlin) + if !epsilonEquals(result, expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func epsilonEquals(a, b float64) bool { + epsilon := 0.01 + return math.Abs(a-b) < epsilon +} diff --git a/timeext/time.go b/timeext/time.go index 517f02a..1b0b168 100644 --- a/timeext/time.go +++ b/timeext/time.go @@ -65,6 +65,10 @@ 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 { diff --git a/timeext/time_test.go b/timeext/time_test.go index df45868..2aed255 100644 --- a/timeext/time_test.go +++ b/timeext/time_test.go @@ -72,6 +72,13 @@ func TestIsSunday(t *testing.T) { } } +func TestIsSunday_OnSunday(t *testing.T) { + sunday := time.Date(2022, 5, 15, 0, 0, 0, 0, TimezoneBerlin) // A Sunday + if !IsSunday(sunday, TimezoneBerlin) { + t.Errorf("Expected true for Sunday") + } +} + func TestDurationFromTime(t *testing.T) { expected := time.Duration(13*time.Hour + 14*time.Minute + 15*time.Second) result := DurationFromTime(13, 14, 15) @@ -156,3 +163,31 @@ func TestAddYears(t *testing.T) { t.Errorf("Expected %v but got %v", expected, result) } } + +func TestIsDatePartEqual_SameDateDifferentTimes(t *testing.T) { + tz := time.UTC + t1 := time.Date(2022, 5, 18, 10, 30, 0, 0, tz) + t2 := time.Date(2022, 5, 18, 20, 45, 0, 0, tz) + if !IsDatePartEqual(t1, t2, tz) { + t.Errorf("Expected dates to be equal") + } +} + +func TestWithTimePart_ChangeTime(t *testing.T) { + base := time.Date(2022, 5, 18, 0, 0, 0, 0, time.UTC) + result := WithTimePart(base, 15, 30, 45) + expected := time.Date(2022, 5, 18, 15, 30, 45, 0, time.UTC) + if !result.Equal(expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +} + +func TestCombineDateAndTime_CombineDifferentParts(t *testing.T) { + date := time.Date(2022, 5, 18, 0, 0, 0, 0, time.UTC) + timePart := time.Date(2000, 1, 1, 15, 30, 45, 0, time.UTC) + result := CombineDateAndTime(date, timePart) + expected := time.Date(2022, 5, 18, 15, 30, 45, 0, time.UTC) + if !result.Equal(expected) { + t.Errorf("Expected %v, got %v", expected, result) + } +}