Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3a17edfaf0 | |||
| 3320a9c19d | 
| @@ -1,5 +1,5 @@ | |||||||
| package goext | package goext | ||||||
|  |  | ||||||
| const GoextVersion = "0.0.507" | const GoextVersion = "0.0.509" | ||||||
|  |  | ||||||
| const GoextVersionTimestamp = "2024-08-25T15:41:17+0200" | const GoextVersionTimestamp = "2024-08-26T14:35:49+0200" | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								langext/io.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								langext/io.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | package langext | ||||||
|  |  | ||||||
|  | import "io" | ||||||
|  |  | ||||||
|  | type nopCloser struct { | ||||||
|  | 	io.Writer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n nopCloser) Close() error { | ||||||
|  | 	return nil // no op | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WriteNopCloser(w io.Writer) io.WriteCloser { | ||||||
|  | 	return nopCloser{w} | ||||||
|  | } | ||||||
| @@ -2,6 +2,9 @@ package timeext | |||||||
|  |  | ||||||
| import "time" | import "time" | ||||||
|  |  | ||||||
|  | // YearDifference calculates the difference between two timestamps in years. | ||||||
|  | // = t1 - t2 | ||||||
|  | // returns a float value | ||||||
| func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { | func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { | ||||||
|  |  | ||||||
| 	yDelta := float64(t1.Year() - t2.Year()) | 	yDelta := float64(t1.Year() - t2.Year()) | ||||||
| @@ -11,3 +14,31 @@ func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 { | |||||||
|  |  | ||||||
| 	return yDelta + (processT1 - processT2) | 	return yDelta + (processT1 - processT2) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // MonthDifference calculates the difference between two timestamps in months. | ||||||
|  | // = t1 - t2 | ||||||
|  | // returns a float value | ||||||
|  | func MonthDifference(t1 time.Time, t2 time.Time) float64 { | ||||||
|  |  | ||||||
|  | 	yDelta := float64(t1.Year() - t2.Year()) | ||||||
|  | 	mDelta := float64(t1.Month() - t2.Month()) | ||||||
|  |  | ||||||
|  | 	dDelta := float64(0) | ||||||
|  |  | ||||||
|  | 	t1MonthDays := DaysInMonth(t1) | ||||||
|  | 	t2MonthDays := DaysInMonth(t2) | ||||||
|  |  | ||||||
|  | 	if t2.Year() > t1.Year() || (t2.Year() == t1.Year() && t2.Month() > t1.Month()) { | ||||||
|  | 		dDelta -= 1 | ||||||
|  | 		dDelta += float64(t1MonthDays-t1.Day()) / float64(t1MonthDays) | ||||||
|  | 		dDelta += float64(t2.Day()) / float64(t2MonthDays) | ||||||
|  | 	} else if t2.Year() < t1.Year() || (t2.Year() == t1.Year() && t2.Month() < t1.Month()) { | ||||||
|  | 		dDelta -= 1 | ||||||
|  | 		dDelta += float64(t1.Day()) / float64(t1MonthDays) | ||||||
|  | 		dDelta += float64(t2MonthDays-t2.Day()) / float64(t2MonthDays) | ||||||
|  | 	} else { | ||||||
|  | 		dDelta += float64(t1.Day()-t2.Day()) / float64(t1MonthDays) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return yDelta*12 + mDelta + dDelta | ||||||
|  | } | ||||||
|   | |||||||
| @@ -81,3 +81,63 @@ func epsilonEquals(a, b float64) bool { | |||||||
| 	epsilon := 0.01 | 	epsilon := 0.01 | ||||||
| 	return math.Abs(a-b) < epsilon | 	return math.Abs(a-b) < epsilon | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceSameDate(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := 0.0 | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceSameMonth(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 1, 31, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := 0.967741935483871 // Approximation of 30/31 days | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceDifferentMonthsSameYear(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := 2.0 | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceDifferentYears(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := 2.0 | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceT1BeforeT2(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := 5.0 | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMonthDifferenceT1AfterT2(t *testing.T) { | ||||||
|  | 	t1 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) | ||||||
|  | 	expected := -5.0 | ||||||
|  | 	result := MonthDifference(t2, t1) | ||||||
|  | 	if !epsilonEquals(result, expected) { | ||||||
|  | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -184,3 +184,10 @@ func AddYears(t time.Time, yearCount float64, tz *time.Location) time.Time { | |||||||
|  |  | ||||||
| 	return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount)) | 	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() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -191,3 +191,39 @@ func TestCombineDateAndTime_CombineDifferentParts(t *testing.T) { | |||||||
| 		t.Errorf("Expected %v, got %v", expected, result) | 		t.Errorf("Expected %v, got %v", expected, result) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestDaysInMonth_31Days(t *testing.T) { | ||||||
|  | 	date := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) // January | ||||||
|  | 	expected := 31 | ||||||
|  | 	result := DaysInMonth(date) | ||||||
|  | 	if result != expected { | ||||||
|  | 		t.Errorf("Expected %d but got %d", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDaysInMonth_30Days(t *testing.T) { | ||||||
|  | 	date := time.Date(2022, 4, 1, 0, 0, 0, 0, time.UTC) // April | ||||||
|  | 	expected := 30 | ||||||
|  | 	result := DaysInMonth(date) | ||||||
|  | 	if result != expected { | ||||||
|  | 		t.Errorf("Expected %d but got %d", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDaysInMonth_FebruaryLeapYear(t *testing.T) { | ||||||
|  | 	date := time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) // February in a leap year | ||||||
|  | 	expected := 29 | ||||||
|  | 	result := DaysInMonth(date) | ||||||
|  | 	if result != expected { | ||||||
|  | 		t.Errorf("Expected %d but got %d", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDaysInMonth_FebruaryNonLeapYear(t *testing.T) { | ||||||
|  | 	date := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) // February in a non-leap year | ||||||
|  | 	expected := 28 | ||||||
|  | 	result := DaysInMonth(date) | ||||||
|  | 	if result != expected { | ||||||
|  | 		t.Errorf("Expected %d but got %d", expected, result) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user