summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/rickb777/date/period/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/rickb777/date/period/parse.go')
-rw-r--r--vendor/github.com/rickb777/date/period/parse.go152
1 files changed, 152 insertions, 0 deletions
diff --git a/vendor/github.com/rickb777/date/period/parse.go b/vendor/github.com/rickb777/date/period/parse.go
new file mode 100644
index 00000000..30b3a693
--- /dev/null
+++ b/vendor/github.com/rickb777/date/period/parse.go
@@ -0,0 +1,152 @@
+// Copyright 2015 Rick Beton. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package period
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// MustParse is as per Parse except that it panics if the string cannot be parsed.
+// This is intended for setup code; don't use it for user inputs.
+func MustParse(value string) Period {
+ d, err := Parse(value)
+ if err != nil {
+ panic(err)
+ }
+ return d
+}
+
+// Parse parses strings that specify periods using ISO-8601 rules.
+//
+// In addition, a plus or minus sign can precede the period, e.g. "-P10D"
+//
+// The value is normalised, e.g. multiple of 12 months become years so "P24M"
+// is the same as "P2Y". However, this is done without loss of precision, so
+// for example whole numbers of days do not contribute to the months tally
+// because the number of days per month is variable.
+//
+// The zero value can be represented in several ways: all of the following
+// are equivalent: "P0Y", "P0M", "P0W", "P0D", "PT0H", PT0M", PT0S", and "P0".
+// The canonical zero is "P0D".
+func Parse(period string) (Period, error) {
+ if period == "" {
+ return Period{}, fmt.Errorf("cannot parse a blank string as a period")
+ }
+
+ if period == "P0" {
+ return Period{}, nil
+ }
+
+ result := period64{}
+ pcopy := period
+ if pcopy[0] == '-' {
+ result.neg = true
+ pcopy = pcopy[1:]
+ } else if pcopy[0] == '+' {
+ pcopy = pcopy[1:]
+ }
+
+ if pcopy[0] != 'P' {
+ return Period{}, fmt.Errorf("expected 'P' period mark at the start: %s", period)
+ }
+ pcopy = pcopy[1:]
+
+ st := parseState{period, pcopy, false, nil}
+ t := strings.IndexByte(pcopy, 'T')
+ if t >= 0 {
+ st.pcopy = pcopy[t+1:]
+
+ result.hours, st = parseField(st, 'H')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'H' marker: %s", period)
+ }
+
+ result.minutes, st = parseField(st, 'M')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'M' marker: %s", period)
+ }
+
+ result.seconds, st = parseField(st, 'S')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'S' marker: %s", period)
+ }
+
+ st.pcopy = pcopy[:t]
+ }
+
+ result.years, st = parseField(st, 'Y')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'Y' marker: %s", period)
+ }
+
+ result.months, st = parseField(st, 'M')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'M' marker: %s", period)
+ }
+
+ weeks, st := parseField(st, 'W')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'W' marker: %s", period)
+ }
+
+ days, st := parseField(st, 'D')
+ if st.err != nil {
+ return Period{}, fmt.Errorf("expected a number before the 'D' marker: %s", period)
+ }
+
+ result.days = weeks*7 + days
+ //fmt.Printf("%#v\n", st)
+
+ if !st.ok {
+ return Period{}, fmt.Errorf("expected 'Y', 'M', 'W', 'D', 'H', 'M', or 'S' marker: %s", period)
+ }
+
+ return result.normalise64(true).toPeriod(), nil
+}
+
+type parseState struct {
+ period, pcopy string
+ ok bool
+ err error
+}
+
+func parseField(st parseState, mark byte) (int64, parseState) {
+ //fmt.Printf("%c %#v\n", mark, st)
+ r := int64(0)
+ m := strings.IndexByte(st.pcopy, mark)
+ if m > 0 {
+ r, st.err = parseDecimalFixedPoint(st.pcopy[:m], st.period)
+ if st.err != nil {
+ return 0, st
+ }
+ st.pcopy = st.pcopy[m+1:]
+ st.ok = true
+ }
+ return r, st
+}
+
+// Fixed-point three decimal places
+func parseDecimalFixedPoint(s, original string) (int64, error) {
+ //was := s
+ dec := strings.IndexByte(s, '.')
+ if dec < 0 {
+ dec = strings.IndexByte(s, ',')
+ }
+
+ if dec >= 0 {
+ dp := len(s) - dec
+ if dp > 1 {
+ s = s[:dec] + s[dec+1:dec+2]
+ } else {
+ s = s[:dec] + s[dec+1:] + "0"
+ }
+ } else {
+ s = s + "0"
+ }
+
+ return strconv.ParseInt(s, 10, 64)
+}