Skip to content

Commit

Permalink
parsing now uses 64-bit arithmetic for its calculations, allowing the…
Browse files Browse the repository at this point in the history
… correct parsing of, say, large numbers of seconds
  • Loading branch information
rickb777 committed Apr 27, 2019
1 parent a337401 commit a4bb3f7
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 24 deletions.
8 changes: 4 additions & 4 deletions period/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func TestPeriodJSONMarshalling(t *testing.T) {
value Period
want string
}{
{New(-1111, -123, -3, -11, -59, -59), `"-P1111Y123M3DT11H59M59S"`},
{New(-1, -12, -31, -5, -4, -20), `"-P1Y12M31DT5H4M20S"`},
{New(-1111, -4, -3, -11, -59, -59), `"-P1111Y4M3DT11H59M59S"`},
{New(-1, -10, -31, -5, -4, -20), `"-P1Y10M31DT5H4M20S"`},
{New(0, 0, 0, 0, 0, 0), `"P0D"`},
{New(0, 0, 0, 0, 0, 1), `"PT1S"`},
{New(0, 0, 0, 0, 1, 0), `"PT1M"`},
Expand Down Expand Up @@ -84,8 +84,8 @@ func TestPeriodTextMarshalling(t *testing.T) {
value Period
want string
}{
{New(-1111, -123, -3, -11, -59, -59), "-P1111Y123M3DT11H59M59S"},
{New(-1, -12, -31, -5, -4, -20), "-P1Y12M31DT5H4M20S"},
{New(-1111, -4, -3, -11, -59, -59), "-P1111Y4M3DT11H59M59S"},
{New(-1, -9, -31, -5, -4, -20), "-P1Y9M31DT5H4M20S"},
{New(0, 0, 0, 0, 0, 0), "P0D"},
{New(0, 0, 0, 0, 0, 1), "PT1S"},
{New(0, 0, 0, 0, 1, 0), "PT1M"},
Expand Down
21 changes: 8 additions & 13 deletions period/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ func Parse(period string) (Period, error) {
return Period{}, nil
}

result := period64{}
pcopy := period
negate := false
if pcopy[0] == '-' {
negate = true
result.neg = true
pcopy = pcopy[1:]
} else if pcopy[0] == '+' {
pcopy = pcopy[1:]
Expand All @@ -50,8 +50,6 @@ func Parse(period string) (Period, error) {
}
pcopy = pcopy[1:]

result := Period{}

st := parseState{period, pcopy, false, nil}
t := strings.IndexByte(pcopy, 'T')
if t >= 0 {
Expand Down Expand Up @@ -101,10 +99,8 @@ func Parse(period string) (Period, error) {
if !st.ok {
return Period{}, fmt.Errorf("expected 'Y', 'M', 'W', 'D', 'H', 'M', or 'S' marker: %s", period)
}
if negate {
return result.Negate(), nil
}
return result, nil

return result.normalise64(true).toPeriod(), nil
}

type parseState struct {
Expand All @@ -113,9 +109,9 @@ type parseState struct {
err error
}

func parseField(st parseState, mark byte) (int16, parseState) {
func parseField(st parseState, mark byte) (int64, parseState) {
//fmt.Printf("%c %#v\n", mark, st)
r := int16(0)
r := int64(0)
m := strings.IndexByte(st.pcopy, mark)
if m > 0 {
r, st.err = parseDecimalFixedPoint(st.pcopy[:m], st.period)
Expand All @@ -129,7 +125,7 @@ func parseField(st parseState, mark byte) (int16, parseState) {
}

// Fixed-point three decimal places
func parseDecimalFixedPoint(s, original string) (int16, error) {
func parseDecimalFixedPoint(s, original string) (int64, error) {
//was := s
dec := strings.IndexByte(s, '.')
if dec < 0 {
Expand All @@ -147,6 +143,5 @@ func parseDecimalFixedPoint(s, original string) (int16, error) {
s = s + "0"
}

n, e := strconv.ParseInt(s, 10, 32)
return int16(n), e
return strconv.ParseInt(s, 10, 64)
}
3 changes: 2 additions & 1 deletion period/period.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,8 @@ func (p *period64) rippleUp(precise bool) *period64 {
p.hours = p.hours + (p.minutes/600)*10
p.minutes = p.minutes % 600

if !precise || p.hours > 32670-(32670/60)-(32670/3600) {
// 32670-(32670/60)-(32670/3600) = 32760 - 546 - 9.1 = 32204.9
if !precise || p.hours > 32204 {
p.days += (p.hours / 240) * 10
p.hours = p.hours % 240
}
Expand Down
28 changes: 22 additions & 6 deletions period/period_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ func TestParsePeriod(t *testing.T) {
{"P1Y2.5M", Period{10, 25, 0, 0, 0, 0}},
{"P1Y2.15M", Period{10, 21, 0, 0, 0, 0}},
{"P1Y2.125M", Period{10, 21, 0, 0, 0, 0}},
{"P3276.7Y", Period{32767, 0, 0, 0, 0, 0}},
{"-P3276.7Y", Period{-32767, 0, 0, 0, 0, 0}},
// largest possible number of seconds normalised only in hours, mins, sec
{"PT11592000S", Period{0, 0, 0, 32200, 0, 0}},
{"-PT11592000S", Period{0, 0, 0, -32200, 0, 0}},
{"PT11595599S", Period{0, 0, 0, 32200, 590, 590}},
// largest possible number of seconds normalised only in days, hours, mins, sec
{"PT283046400S", Period{0, 0, 32760, 0, 0, 0}},
{"-PT283046400S", Period{0, 0, -32760, 0, 0, 0}},
{"PT283132799S", Period{0, 0, 32760, 230, 590, 590}},
// largest possible number of months
{"P39312M", Period{32760, 0, 0, 0, 0, 0}},
{"-P39312M", Period{-32760, 0, 0, 0, 0, 0}},
}
for i, c := range cases {
d := MustParse(c.value)
Expand Down Expand Up @@ -218,7 +231,7 @@ func TestPeriodAddToTime(t *testing.T) {
const hr = 60 * min

// A conveniently round number (14 July 2017 @ 2:40am UTC)
var t0 = time.Unix(1500000000, 0)
var t0 = time.Unix(1500000000, 0).UTC()

cases := []struct {
value string
Expand Down Expand Up @@ -250,16 +263,17 @@ func TestPeriodAddToTime(t *testing.T) {
{"-P0.1D", t0.Add(-144 * min), false},
{"P0.1M", t0.Add(oneMonthApprox / 10), false},
{"P0.1Y", t0.Add(oneYearApprox / 10), false},
{"-P0.1Y0.1M0.1D", t0.Add(-(oneYearApprox / 10) - (oneMonthApprox / 10) - (144 * min)), false},
// after normalisation, this period is one month and 9.2 days
{"-P0.1Y0.1M0.1D", t0.Add(-oneMonthApprox - (13248 * min)), false},
}
for i, c := range cases {
p := MustParse(c.value)
t1, prec := p.AddTo(t0)
if t1 != c.result {
t.Errorf("%d: AddTo(t) == %s %v, want %s for %s", i, t1, prec, c.result, c.value)
if !t1.Equal(c.result) {
t.Errorf("%d: %s.AddTo(t) == %s %v, want %s", i, c.value, t1, prec, c.result)
}
if prec != c.precise {
t.Errorf("%d: AddTo(t) == %s %v, want %v for %s", i, t1, prec, c.precise, c.value)
t.Errorf("%d: %s.AddTo(t) == %s %v, want %v", i, c.value, t1, prec, c.precise)
}
}
}
Expand All @@ -280,7 +294,9 @@ func TestPeriodToDuration(t *testing.T) {
{"PT3276M", 3276 * time.Minute, true},
{"PT1H", 3600 * time.Second, true},
{"PT0.1H", 360 * time.Second, true},
{"PT3276H", 3276 * time.Hour, true},
{"PT3220H", 3220 * time.Hour, true},
{"PT3221H", 3221 * time.Hour, false}, // threshold of normalisation wrapping
// days, months and years conversions are never precise
{"P1D", 24 * time.Hour, false},
{"P0.1D", 144 * time.Minute, false},
{"P3276D", 3276 * 24 * time.Hour, false},
Expand Down

0 comments on commit a4bb3f7

Please sign in to comment.