// Parses an ISO8601 (with designators) string. // See the following links for examples: // - http://www.postgresql.org/docs/9.1/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT-EXAMPLES // - https://en.wikipedia.org/wiki/ISO_8601#Time_intervals // - https://en.wikipedia.org/wiki/ISO_8601#Durations func iso8601ToDuration(s string) (duration.Duration, error) { var d duration.Duration if len(s) == 0 || s[0] != 'P' { return d, fmt.Errorf("interval: invalid iso8601 duration %s", s) } // Advance to offset 1, since we don't care about the leading P. l := intervalLexer{str: s, offset: 1, err: nil} unitMap := isoDateUnitMap for l.offset < len(s) { // Check if we're in the time part yet. if s[l.offset] == 'T' { unitMap = isoTimeUnitMap l.offset++ } v := l.consumeInt() u := l.consumeUnit('T') if l.err != nil { return d, l.err } if unit, ok := unitMap[u]; ok { d = d.Add(unit.Mul(v)) } else { return d, fmt.Errorf("interval: unknown unit %s in iso8601 duration %s", u, s) } } return d, nil }
// EncodeDurationDescending is the descending version of EncodeDurationAscending. func EncodeDurationDescending(b []byte, d duration.Duration) ([]byte, error) { sortNanos, months, days, err := d.Encode() if err != nil { // TODO(dan): Handle this using d.EncodeBigInt() and the // durationBigNeg/durationBigPos markers. return b, err } b = append(b, durationMarker) b = EncodeVarintDescending(b, sortNanos) b = EncodeVarintDescending(b, months) b = EncodeVarintDescending(b, days) return b, nil }
// Parses a duration in the "traditional" Postgres format. func postgresToDuration(s string) (duration.Duration, error) { var d duration.Duration l := intervalLexer{str: s, offset: 0, err: nil} for l.offset != len(l.str) { v := l.consumeInt() l.consumeSpaces() u := l.consumeUnit(' ') l.consumeSpaces() if unit, ok := postgresUnitMap[u]; ok { d = d.Add(unit.Mul(v)) } else { return d, fmt.Errorf("interval: unknown unit %s in postgres duration %s", u, s) } } return d, nil }
// Parse dash separated date string to interval. // We parse sql stardard string to interval by two steps. // Parsing the date part and parsing the time part. // See the following links for exampels: // - http://www.postgresql.org/docs/9.1/static/datatype-datetime.html#DATATYPE-INTERVAL-INPUT-EXAMPLES func dateToDuration(s string) (duration.Duration, error) { var d duration.Duration if len(s) == 0 { return d, fmt.Errorf(errInvalidSQLDuration, s) } parts := strings.Split(s, "-") var v int var err error switch len(parts) { case 1: v, err = strconv.Atoi(parts[0]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Days: 1}.Mul(int64(v))) case 2: v, err = strconv.Atoi(parts[0]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Months: 12}.Mul(int64(v))) v, err = strconv.Atoi(parts[1]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Months: 1}.Mul(int64(v))) case 3: v, err = strconv.Atoi(parts[0]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Months: 12}.Mul(int64(v))) v, err = strconv.Atoi(parts[1]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Months: 1}.Mul(int64(v))) v, err = strconv.Atoi(parts[2]) if err != nil { return d, fmt.Errorf(errInvalidSQLDuration, s) } d = d.Add(duration.Duration{Days: 1}.Mul(int64(v))) default: return d, fmt.Errorf(errInvalidSQLDuration, s) } return d, nil }