Skip to content

Commit

Permalink
Bug fixed in Period.AddTo calculation: a more accurate solution is po…
Browse files Browse the repository at this point in the history
…ssible
  • Loading branch information
Rick Beton committed Nov 27, 2018
1 parent 5959d0b commit 75ffca5
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 14 deletions.
16 changes: 11 additions & 5 deletions period/period.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,13 +403,19 @@ func (period Period) SecondsFloat() float32 {
// result is precise. However, when years, months or days contains fractions, the result
// is only an approximation (it assumes that all days are 24 hours and every year is 365.2425 days).
func (period Period) AddTo(t time.Time) (time.Time, bool) {
d, precise := period.Duration()
if !precise {
return t.Add(d), false
wholeYears := (period.years % 10) == 0
wholeMonths := (period.months % 10) == 0
wholeDays := (period.days % 10) == 0

if wholeYears && wholeMonths && wholeDays {
// in this case, time.AddDate provides an exact solution
stE3 := totalSecondsE3(period)
t1 := t.AddDate(int(period.years/10), int(period.months/10), int(period.days/10))
return t1.Add(stE3 * time.Millisecond), true
}

stE3 := totalSecondsE3(period)
return t.AddDate(period.Years(), period.Months(), period.Days()).Add(stE3 * time.Millisecond), true
d, precise := period.Duration()
return t.Add(d), precise
}

// DurationApprox converts a period to the equivalent duration in nanoseconds.
Expand Down
23 changes: 14 additions & 9 deletions period/period_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ func TestPeriodAddToTime(t *testing.T) {
result time.Time
precise bool
}{
// precise cases
{"P0D", t0, true},
{"PT1S", t0.Add(sec), true},
{"PT0.1S", t0.Add(100 * ms), true},
Expand All @@ -236,16 +237,20 @@ func TestPeriodAddToTime(t *testing.T) {
{"PT1H", t0.Add(hr), true},
{"PT0.1H", t0.Add(6 * min), true},
{"PT3276H", t0.Add(3276 * hr), true},
{"P1D", t0.Add(24 * hr), false},
{"P1D", t0.AddDate(0, 0, 1), true},
{"P3276D", t0.AddDate(0, 0, 3276), true},
{"P1M", t0.AddDate(0, 1, 0), true},
{"P3276M", t0.AddDate(0, 3276, 0), true},
{"P1Y", t0.AddDate(1, 0, 0), true},
{"-P1Y", t0.AddDate(-1, 0, 0), true},
{"P3276Y", t0.AddDate(3276, 0, 0), true}, // near the upper limit of range
{"-P3276Y", t0.AddDate(-3276, 0, 0), true}, // near the lower limit of range
// approximate cases
{"P0.1D", t0.Add(144 * min), false},
{"P3276D", t0.Add(3276 * 24 * hr), false},
{"P1M", t0.Add(oneMonthApprox), false},
{"-P0.1D", t0.Add(-144 * min), false},
{"P0.1M", t0.Add(oneMonthApprox / 10), false},
{"P3276M", t0.Add(3276 * oneMonthApprox), false},
{"P1Y", t0.Add(oneYearApprox), false},
{"-P1Y", t0.Add(-oneYearApprox), false},
{"P3276Y", t0.Add(3276 * oneYearApprox), false}, // near the upper limit of range
{"-P3276Y", t0.Add(-3276 * oneYearApprox), false}, // near the lower limit of range
{"P0.1Y", t0.Add(oneYearApprox / 10), false},
{"-P0.1Y0.1M0.1D", t0.Add(-(oneYearApprox / 10) - (oneMonthApprox / 10) - (144 * min)), false},
}
for i, c := range cases {
p := MustParse(c.value)
Expand All @@ -254,7 +259,7 @@ func TestPeriodAddToTime(t *testing.T) {
t.Errorf("%d: AddTo(t) == %s %v, want %s for %s", i, t1, prec, c.result, c.value)
}
if prec != c.precise {
t.Errorf("%d: Duration() == %s %v, want %v for %s", i, t1, prec, c.precise, c.value)
t.Errorf("%d: AddTo(t) == %s %v, want %v for %s", i, t1, prec, c.precise, c.value)
}
}
}
Expand Down

0 comments on commit 75ffca5

Please sign in to comment.