Skip to content

Commit b6690e4

Browse files
author
Rick
committed
period.AddTo revised to reduce the impact of subtle behaviours of time.AddDate
1 parent ad3aa70 commit b6690e4

File tree

2 files changed

+55
-39
lines changed

2 files changed

+55
-39
lines changed

period/arithmetic.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,13 @@ func (period Period) AddTo(t time.Time) (time.Time, bool) {
3939
wholeDays := (period.days % 10) == 0
4040

4141
if wholeYears && wholeMonths && wholeDays {
42-
// in this case, time.AddDate provides an exact solution
42+
// in this case, time.AddDate(...).Add(...) provides an exact solution
4343
stE3 := totalSecondsE3(period)
44+
if period.years == 0 && period.months == 0 && period.days == 0 {
45+
// AddDate (below) normalises its result, so we don't call it unless needed
46+
return t.Add(stE3 * time.Millisecond), true
47+
}
48+
4449
t1 := t.AddDate(int(period.years/10), int(period.months/10), int(period.days/10))
4550
return t1.Add(stE3 * time.Millisecond), true
4651
}

period/arithmetic_test.go

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -89,45 +89,56 @@ func TestPeriodAddToTime(t *testing.T) {
8989
const min = 60 * sec
9090
const hr = 60 * min
9191

92-
// A conveniently round number (14 July 2017 @ 2:40am UTC)
93-
var t0 = time.Unix(1500000000, 0).UTC()
94-
95-
cases := []struct {
96-
value string
97-
result time.Time
98-
precise bool
99-
}{
100-
// precise cases
101-
{"P0D", t0, true},
102-
{"PT1S", t0.Add(sec), true},
103-
{"PT0.1S", t0.Add(100 * ms), true},
104-
{"-PT0.1S", t0.Add(-100 * ms), true},
105-
{"PT3276S", t0.Add(3276 * sec), true},
106-
{"PT1M", t0.Add(60 * sec), true},
107-
{"PT0.1M", t0.Add(6 * sec), true},
108-
{"PT3276M", t0.Add(3276 * min), true},
109-
{"PT1H", t0.Add(hr), true},
110-
{"PT0.1H", t0.Add(6 * min), true},
111-
{"PT3276H", t0.Add(3276 * hr), true},
112-
{"P1D", t0.AddDate(0, 0, 1), true},
113-
{"P3276D", t0.AddDate(0, 0, 3276), true},
114-
{"P1M", t0.AddDate(0, 1, 0), true},
115-
{"P3276M", t0.AddDate(0, 3276, 0), true},
116-
{"P1Y", t0.AddDate(1, 0, 0), true},
117-
{"-P1Y", t0.AddDate(-1, 0, 0), true},
118-
{"P3276Y", t0.AddDate(3276, 0, 0), true}, // near the upper limit of range
119-
{"-P3276Y", t0.AddDate(-3276, 0, 0), true}, // near the lower limit of range
120-
// approximate cases
121-
{"P0.1D", t0.Add(144 * min), false},
122-
{"-P0.1D", t0.Add(-144 * min), false},
123-
{"P0.1M", t0.Add(oneMonthApprox / 10), false},
124-
{"P0.1Y", t0.Add(oneYearApprox / 10), false},
92+
est, err := time.LoadLocation("America/New_York")
93+
g.Expect(err).NotTo(HaveOccurred())
94+
95+
times := []time.Time{
96+
// A conveniently round number but with non-zero nanoseconds (14 July 2017 @ 2:40am UTC)
97+
time.Unix(1500000000, 1).UTC(),
98+
// This specific time fails for EST due behaviour of Time.AddDate
99+
time.Date(2020, 11, 1, 1, 0, 0, 0, est),
125100
}
126-
for i, c := range cases {
127-
p := MustParse(c.value)
128-
t1, prec := p.AddTo(t0)
129-
g.Expect(t1).To(Equal(c.result), info(i, c.value))
130-
g.Expect(prec).To(Equal(c.precise), info(i, c.value))
101+
102+
for _, t0 := range times {
103+
cases := []struct {
104+
value string
105+
result time.Time
106+
precise bool
107+
}{
108+
// precise cases
109+
{"P0D", t0, true},
110+
{"PT1S", t0.Add(sec), true},
111+
{"PT0.1S", t0.Add(100 * ms), true},
112+
{"-PT0.1S", t0.Add(-100 * ms), true},
113+
{"PT3276S", t0.Add(3276 * sec), true},
114+
{"PT1M", t0.Add(60 * sec), true},
115+
{"PT0.1M", t0.Add(6 * sec), true},
116+
{"PT3276M", t0.Add(3276 * min), true},
117+
{"PT1H", t0.Add(hr), true},
118+
{"PT0.1H", t0.Add(6 * min), true},
119+
{"PT3276H", t0.Add(3276 * hr), true},
120+
{"P1D", t0.AddDate(0, 0, 1), true},
121+
{"P3276D", t0.AddDate(0, 0, 3276), true},
122+
{"P1M", t0.AddDate(0, 1, 0), true},
123+
{"P3276M", t0.AddDate(0, 3276, 0), true},
124+
{"P1Y", t0.AddDate(1, 0, 0), true},
125+
{"-P1Y", t0.AddDate(-1, 0, 0), true},
126+
{"P3276Y", t0.AddDate(3276, 0, 0), true}, // near the upper limit of range
127+
{"-P3276Y", t0.AddDate(-3276, 0, 0), true}, // near the lower limit of range
128+
// approximate cases
129+
{"P0.1D", t0.Add(144 * min), false},
130+
{"-P0.1D", t0.Add(-144 * min), false},
131+
{"P0.1M", t0.Add(oneMonthApprox / 10), false},
132+
{"P0.1Y", t0.Add(oneYearApprox / 10), false},
133+
}
134+
for i, c := range cases {
135+
p, err := ParseWithNormalise(c.value, false)
136+
g.Expect(err).NotTo(HaveOccurred())
137+
138+
t1, prec := p.AddTo(t0)
139+
g.Expect(t1).To(Equal(c.result), info(i, c.value, t0))
140+
g.Expect(prec).To(Equal(c.precise), info(i, c.value, t0))
141+
}
131142
}
132143
}
133144

0 commit comments

Comments
 (0)