| | |
| | |
| | |
| |
|
| | package time |
| |
|
| | import ( |
| | "errors" |
| | "internal/stringslite" |
| | _ "unsafe" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | const ( |
| | Layout = "01/02 03:04:05PM '06 -0700" |
| | ANSIC = "Mon Jan _2 15:04:05 2006" |
| | UnixDate = "Mon Jan _2 15:04:05 MST 2006" |
| | RubyDate = "Mon Jan 02 15:04:05 -0700 2006" |
| | RFC822 = "02 Jan 06 15:04 MST" |
| | RFC822Z = "02 Jan 06 15:04 -0700" |
| | RFC850 = "Monday, 02-Jan-06 15:04:05 MST" |
| | RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" |
| | RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" |
| | RFC3339 = "2006-01-02T15:04:05Z07:00" |
| | RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" |
| | Kitchen = "3:04PM" |
| | |
| | Stamp = "Jan _2 15:04:05" |
| | StampMilli = "Jan _2 15:04:05.000" |
| | StampMicro = "Jan _2 15:04:05.000000" |
| | StampNano = "Jan _2 15:04:05.000000000" |
| | DateTime = "2006-01-02 15:04:05" |
| | DateOnly = "2006-01-02" |
| | TimeOnly = "15:04:05" |
| | ) |
| |
|
| | const ( |
| | _ = iota |
| | stdLongMonth = iota + stdNeedDate |
| | stdMonth |
| | stdNumMonth |
| | stdZeroMonth |
| | stdLongWeekDay |
| | stdWeekDay |
| | stdDay |
| | stdUnderDay |
| | stdZeroDay |
| | stdUnderYearDay = iota + stdNeedYday |
| | stdZeroYearDay |
| | stdHour = iota + stdNeedClock |
| | stdHour12 |
| | stdZeroHour12 |
| | stdMinute |
| | stdZeroMinute |
| | stdSecond |
| | stdZeroSecond |
| | stdLongYear = iota + stdNeedDate |
| | stdYear |
| | stdPM = iota + stdNeedClock |
| | stdpm |
| | stdTZ = iota |
| | stdISO8601TZ |
| | stdISO8601SecondsTZ |
| | stdISO8601ShortTZ |
| | stdISO8601ColonTZ |
| | stdISO8601ColonSecondsTZ |
| | stdNumTZ |
| | stdNumSecondsTz |
| | stdNumShortTZ |
| | stdNumColonTZ |
| | stdNumColonSecondsTZ |
| | stdFracSecond0 |
| | stdFracSecond9 |
| |
|
| | stdNeedDate = 1 << 8 |
| | stdNeedYday = 1 << 9 |
| | stdNeedClock = 1 << 10 |
| | stdArgShift = 16 |
| | stdSeparatorShift = 28 |
| | stdMask = 1<<stdArgShift - 1 |
| | ) |
| |
|
| | |
| | var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear} |
| |
|
| | |
| | |
| | func startsWithLowerCase(str string) bool { |
| | if len(str) == 0 { |
| | return false |
| | } |
| | c := str[0] |
| | return 'a' <= c && c <= 'z' |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func nextStdChunk(layout string) (prefix string, std int, suffix string) { |
| | for i := 0; i < len(layout); i++ { |
| | switch c := int(layout[i]); c { |
| | case 'J': |
| | if len(layout) >= i+3 && layout[i:i+3] == "Jan" { |
| | if len(layout) >= i+7 && layout[i:i+7] == "January" { |
| | return layout[0:i], stdLongMonth, layout[i+7:] |
| | } |
| | if !startsWithLowerCase(layout[i+3:]) { |
| | return layout[0:i], stdMonth, layout[i+3:] |
| | } |
| | } |
| |
|
| | case 'M': |
| | if len(layout) >= i+3 { |
| | if layout[i:i+3] == "Mon" { |
| | if len(layout) >= i+6 && layout[i:i+6] == "Monday" { |
| | return layout[0:i], stdLongWeekDay, layout[i+6:] |
| | } |
| | if !startsWithLowerCase(layout[i+3:]) { |
| | return layout[0:i], stdWeekDay, layout[i+3:] |
| | } |
| | } |
| | if layout[i:i+3] == "MST" { |
| | return layout[0:i], stdTZ, layout[i+3:] |
| | } |
| | } |
| |
|
| | case '0': |
| | if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { |
| | return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] |
| | } |
| | if len(layout) >= i+3 && layout[i+1] == '0' && layout[i+2] == '2' { |
| | return layout[0:i], stdZeroYearDay, layout[i+3:] |
| | } |
| |
|
| | case '1': |
| | if len(layout) >= i+2 && layout[i+1] == '5' { |
| | return layout[0:i], stdHour, layout[i+2:] |
| | } |
| | return layout[0:i], stdNumMonth, layout[i+1:] |
| |
|
| | case '2': |
| | if len(layout) >= i+4 && layout[i:i+4] == "2006" { |
| | return layout[0:i], stdLongYear, layout[i+4:] |
| | } |
| | return layout[0:i], stdDay, layout[i+1:] |
| |
|
| | case '_': |
| | if len(layout) >= i+2 && layout[i+1] == '2' { |
| | |
| | if len(layout) >= i+5 && layout[i+1:i+5] == "2006" { |
| | return layout[0 : i+1], stdLongYear, layout[i+5:] |
| | } |
| | return layout[0:i], stdUnderDay, layout[i+2:] |
| | } |
| | if len(layout) >= i+3 && layout[i+1] == '_' && layout[i+2] == '2' { |
| | return layout[0:i], stdUnderYearDay, layout[i+3:] |
| | } |
| |
|
| | case '3': |
| | return layout[0:i], stdHour12, layout[i+1:] |
| |
|
| | case '4': |
| | return layout[0:i], stdMinute, layout[i+1:] |
| |
|
| | case '5': |
| | return layout[0:i], stdSecond, layout[i+1:] |
| |
|
| | case 'P': |
| | if len(layout) >= i+2 && layout[i+1] == 'M' { |
| | return layout[0:i], stdPM, layout[i+2:] |
| | } |
| |
|
| | case 'p': |
| | if len(layout) >= i+2 && layout[i+1] == 'm' { |
| | return layout[0:i], stdpm, layout[i+2:] |
| | } |
| |
|
| | case '-': |
| | if len(layout) >= i+7 && layout[i:i+7] == "-070000" { |
| | return layout[0:i], stdNumSecondsTz, layout[i+7:] |
| | } |
| | if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { |
| | return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] |
| | } |
| | if len(layout) >= i+5 && layout[i:i+5] == "-0700" { |
| | return layout[0:i], stdNumTZ, layout[i+5:] |
| | } |
| | if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { |
| | return layout[0:i], stdNumColonTZ, layout[i+6:] |
| | } |
| | if len(layout) >= i+3 && layout[i:i+3] == "-07" { |
| | return layout[0:i], stdNumShortTZ, layout[i+3:] |
| | } |
| |
|
| | case 'Z': |
| | if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { |
| | return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] |
| | } |
| | if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { |
| | return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] |
| | } |
| | if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { |
| | return layout[0:i], stdISO8601TZ, layout[i+5:] |
| | } |
| | if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { |
| | return layout[0:i], stdISO8601ColonTZ, layout[i+6:] |
| | } |
| | if len(layout) >= i+3 && layout[i:i+3] == "Z07" { |
| | return layout[0:i], stdISO8601ShortTZ, layout[i+3:] |
| | } |
| |
|
| | case '.', ',': |
| | if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { |
| | ch := layout[i+1] |
| | j := i + 1 |
| | for j < len(layout) && layout[j] == ch { |
| | j++ |
| | } |
| | |
| | if !isDigit(layout, j) { |
| | code := stdFracSecond0 |
| | if layout[i+1] == '9' { |
| | code = stdFracSecond9 |
| | } |
| | std := stdFracSecond(code, j-(i+1), c) |
| | return layout[0:i], std, layout[j:] |
| | } |
| | } |
| | } |
| | } |
| | return layout, 0, "" |
| | } |
| |
|
| | var longDayNames = []string{ |
| | "Sunday", |
| | "Monday", |
| | "Tuesday", |
| | "Wednesday", |
| | "Thursday", |
| | "Friday", |
| | "Saturday", |
| | } |
| |
|
| | var shortDayNames = []string{ |
| | "Sun", |
| | "Mon", |
| | "Tue", |
| | "Wed", |
| | "Thu", |
| | "Fri", |
| | "Sat", |
| | } |
| |
|
| | var shortMonthNames = []string{ |
| | "Jan", |
| | "Feb", |
| | "Mar", |
| | "Apr", |
| | "May", |
| | "Jun", |
| | "Jul", |
| | "Aug", |
| | "Sep", |
| | "Oct", |
| | "Nov", |
| | "Dec", |
| | } |
| |
|
| | var longMonthNames = []string{ |
| | "January", |
| | "February", |
| | "March", |
| | "April", |
| | "May", |
| | "June", |
| | "July", |
| | "August", |
| | "September", |
| | "October", |
| | "November", |
| | "December", |
| | } |
| |
|
| | |
| | |
| | func match(s1, s2 string) bool { |
| | for i := 0; i < len(s1); i++ { |
| | c1 := s1[i] |
| | c2 := s2[i] |
| | if c1 != c2 { |
| | |
| | c1 |= 'a' - 'A' |
| | c2 |= 'a' - 'A' |
| | if c1 != c2 || c1 < 'a' || c1 > 'z' { |
| | return false |
| | } |
| | } |
| | } |
| | return true |
| | } |
| |
|
| | func lookup(tab []string, val string) (int, string, error) { |
| | for i, v := range tab { |
| | if len(val) >= len(v) && match(val[:len(v)], v) { |
| | return i, val[len(v):], nil |
| | } |
| | } |
| | return -1, val, errBad |
| | } |
| |
|
| | |
| | |
| | |
| | func appendInt(b []byte, x int, width int) []byte { |
| | u := uint(x) |
| | if x < 0 { |
| | b = append(b, '-') |
| | u = uint(-x) |
| | } |
| |
|
| | |
| | utod := func(u uint) byte { return '0' + byte(u) } |
| | switch { |
| | case width == 2 && u < 1e2: |
| | return append(b, utod(u/1e1), utod(u%1e1)) |
| | case width == 4 && u < 1e4: |
| | return append(b, utod(u/1e3), utod(u/1e2%1e1), utod(u/1e1%1e1), utod(u%1e1)) |
| | } |
| |
|
| | |
| | var n int |
| | if u == 0 { |
| | n = 1 |
| | } |
| | for u2 := u; u2 > 0; u2 /= 10 { |
| | n++ |
| | } |
| |
|
| | |
| | for pad := width - n; pad > 0; pad-- { |
| | b = append(b, '0') |
| | } |
| |
|
| | |
| | if len(b)+n <= cap(b) { |
| | b = b[:len(b)+n] |
| | } else { |
| | b = append(b, make([]byte, n)...) |
| | } |
| |
|
| | |
| | i := len(b) - 1 |
| | for u >= 10 && i > 0 { |
| | q := u / 10 |
| | b[i] = utod(u - q*10) |
| | u = q |
| | i-- |
| | } |
| | b[i] = utod(u) |
| | return b |
| | } |
| |
|
| | |
| | var errAtoi = errors.New("time: invalid number") |
| |
|
| | |
| | func atoi[bytes []byte | string](s bytes) (x int, err error) { |
| | neg := false |
| | if len(s) > 0 && (s[0] == '-' || s[0] == '+') { |
| | neg = s[0] == '-' |
| | s = s[1:] |
| | } |
| | q, rem, err := leadingInt(s) |
| | x = int(q) |
| | if err != nil || len(rem) > 0 { |
| | return 0, errAtoi |
| | } |
| | if neg { |
| | x = -x |
| | } |
| | return x, nil |
| | } |
| |
|
| | |
| | |
| | |
| | func stdFracSecond(code, n, c int) int { |
| | |
| | if c == '.' { |
| | return code | ((n & 0xfff) << stdArgShift) |
| | } |
| | return code | ((n & 0xfff) << stdArgShift) | 1<<stdSeparatorShift |
| | } |
| |
|
| | func digitsLen(std int) int { |
| | return (std >> stdArgShift) & 0xfff |
| | } |
| |
|
| | func separator(std int) byte { |
| | if (std >> stdSeparatorShift) == 0 { |
| | return '.' |
| | } |
| | return ',' |
| | } |
| |
|
| | |
| | |
| | func appendNano(b []byte, nanosec int, std int) []byte { |
| | trim := std&stdMask == stdFracSecond9 |
| | n := digitsLen(std) |
| | if trim && (n == 0 || nanosec == 0) { |
| | return b |
| | } |
| | dot := separator(std) |
| | b = append(b, dot) |
| | b = appendInt(b, nanosec, 9) |
| | if n < 9 { |
| | b = b[:len(b)-9+n] |
| | } |
| | if trim { |
| | for len(b) > 0 && b[len(b)-1] == '0' { |
| | b = b[:len(b)-1] |
| | } |
| | if len(b) > 0 && b[len(b)-1] == dot { |
| | b = b[:len(b)-1] |
| | } |
| | } |
| | return b |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func (t Time) String() string { |
| | s := t.Format("2006-01-02 15:04:05.999999999 -0700 MST") |
| |
|
| | |
| | if t.wall&hasMonotonic != 0 { |
| | m2 := uint64(t.ext) |
| | sign := byte('+') |
| | if t.ext < 0 { |
| | sign = '-' |
| | m2 = -m2 |
| | } |
| | m1, m2 := m2/1e9, m2%1e9 |
| | m0, m1 := m1/1e9, m1%1e9 |
| | buf := make([]byte, 0, 24) |
| | buf = append(buf, " m="...) |
| | buf = append(buf, sign) |
| | wid := 0 |
| | if m0 != 0 { |
| | buf = appendInt(buf, int(m0), 0) |
| | wid = 9 |
| | } |
| | buf = appendInt(buf, int(m1), wid) |
| | buf = append(buf, '.') |
| | buf = appendInt(buf, int(m2), 9) |
| | s += string(buf) |
| | } |
| | return s |
| | } |
| |
|
| | |
| | |
| | func (t Time) GoString() string { |
| | abs := t.absSec() |
| | year, month, day := abs.days().date() |
| | hour, minute, second := abs.clock() |
| |
|
| | buf := make([]byte, 0, len("time.Date(9999, time.September, 31, 23, 59, 59, 999999999, time.Local)")) |
| | buf = append(buf, "time.Date("...) |
| | buf = appendInt(buf, year, 0) |
| | if January <= month && month <= December { |
| | buf = append(buf, ", time."...) |
| | buf = append(buf, longMonthNames[month-1]...) |
| | } else { |
| | |
| | |
| | buf = appendInt(buf, int(month), 0) |
| | } |
| | buf = append(buf, ", "...) |
| | buf = appendInt(buf, day, 0) |
| | buf = append(buf, ", "...) |
| | buf = appendInt(buf, hour, 0) |
| | buf = append(buf, ", "...) |
| | buf = appendInt(buf, minute, 0) |
| | buf = append(buf, ", "...) |
| | buf = appendInt(buf, second, 0) |
| | buf = append(buf, ", "...) |
| | buf = appendInt(buf, t.Nanosecond(), 0) |
| | buf = append(buf, ", "...) |
| | switch loc := t.Location(); loc { |
| | case UTC, nil: |
| | buf = append(buf, "time.UTC"...) |
| | case Local: |
| | buf = append(buf, "time.Local"...) |
| | default: |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | buf = append(buf, `time.Location(`...) |
| | buf = append(buf, quote(loc.name)...) |
| | buf = append(buf, ')') |
| | } |
| | buf = append(buf, ')') |
| | return string(buf) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | func (t Time) Format(layout string) string { |
| | const bufSize = 64 |
| | var b []byte |
| | max := len(layout) + 10 |
| | if max < bufSize { |
| | var buf [bufSize]byte |
| | b = buf[:0] |
| | } else { |
| | b = make([]byte, 0, max) |
| | } |
| | b = t.AppendFormat(b, layout) |
| | return string(b) |
| | } |
| |
|
| | |
| | |
| | func (t Time) AppendFormat(b []byte, layout string) []byte { |
| | |
| | switch layout { |
| | case RFC3339: |
| | return t.appendFormatRFC3339(b, false) |
| | case RFC3339Nano: |
| | return t.appendFormatRFC3339(b, true) |
| | default: |
| | return t.appendFormat(b, layout) |
| | } |
| | } |
| |
|
| | func (t Time) appendFormat(b []byte, layout string) []byte { |
| | name, offset, abs := t.locabs() |
| | days := abs.days() |
| |
|
| | var ( |
| | year int = -1 |
| | month Month |
| | day int |
| | yday int = -1 |
| | hour int = -1 |
| | min int |
| | sec int |
| | ) |
| |
|
| | |
| | for layout != "" { |
| | prefix, std, suffix := nextStdChunk(layout) |
| | if prefix != "" { |
| | b = append(b, prefix...) |
| | } |
| | if std == 0 { |
| | break |
| | } |
| | layout = suffix |
| |
|
| | |
| | if year < 0 && std&stdNeedDate != 0 { |
| | year, month, day = days.date() |
| | } |
| | if yday < 0 && std&stdNeedYday != 0 { |
| | _, yday = days.yearYday() |
| | } |
| |
|
| | |
| | if hour < 0 && std&stdNeedClock != 0 { |
| | hour, min, sec = abs.clock() |
| | } |
| |
|
| | switch std & stdMask { |
| | case stdYear: |
| | y := year |
| | if y < 0 { |
| | y = -y |
| | } |
| | b = appendInt(b, y%100, 2) |
| | case stdLongYear: |
| | b = appendInt(b, year, 4) |
| | case stdMonth: |
| | b = append(b, month.String()[:3]...) |
| | case stdLongMonth: |
| | m := month.String() |
| | b = append(b, m...) |
| | case stdNumMonth: |
| | b = appendInt(b, int(month), 0) |
| | case stdZeroMonth: |
| | b = appendInt(b, int(month), 2) |
| | case stdWeekDay: |
| | b = append(b, days.weekday().String()[:3]...) |
| | case stdLongWeekDay: |
| | s := days.weekday().String() |
| | b = append(b, s...) |
| | case stdDay: |
| | b = appendInt(b, day, 0) |
| | case stdUnderDay: |
| | if day < 10 { |
| | b = append(b, ' ') |
| | } |
| | b = appendInt(b, day, 0) |
| | case stdZeroDay: |
| | b = appendInt(b, day, 2) |
| | case stdUnderYearDay: |
| | if yday < 100 { |
| | b = append(b, ' ') |
| | if yday < 10 { |
| | b = append(b, ' ') |
| | } |
| | } |
| | b = appendInt(b, yday, 0) |
| | case stdZeroYearDay: |
| | b = appendInt(b, yday, 3) |
| | case stdHour: |
| | b = appendInt(b, hour, 2) |
| | case stdHour12: |
| | |
| | hr := hour % 12 |
| | if hr == 0 { |
| | hr = 12 |
| | } |
| | b = appendInt(b, hr, 0) |
| | case stdZeroHour12: |
| | |
| | hr := hour % 12 |
| | if hr == 0 { |
| | hr = 12 |
| | } |
| | b = appendInt(b, hr, 2) |
| | case stdMinute: |
| | b = appendInt(b, min, 0) |
| | case stdZeroMinute: |
| | b = appendInt(b, min, 2) |
| | case stdSecond: |
| | b = appendInt(b, sec, 0) |
| | case stdZeroSecond: |
| | b = appendInt(b, sec, 2) |
| | case stdPM: |
| | if hour >= 12 { |
| | b = append(b, "PM"...) |
| | } else { |
| | b = append(b, "AM"...) |
| | } |
| | case stdpm: |
| | if hour >= 12 { |
| | b = append(b, "pm"...) |
| | } else { |
| | b = append(b, "am"...) |
| | } |
| | case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ShortTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumShortTZ, stdNumColonSecondsTZ: |
| | |
| | |
| | if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ShortTZ || std == stdISO8601ColonSecondsTZ) { |
| | b = append(b, 'Z') |
| | break |
| | } |
| | zone := offset / 60 |
| | absoffset := offset |
| | if zone < 0 { |
| | b = append(b, '-') |
| | zone = -zone |
| | absoffset = -absoffset |
| | } else { |
| | b = append(b, '+') |
| | } |
| | b = appendInt(b, zone/60, 2) |
| | if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ { |
| | b = append(b, ':') |
| | } |
| | if std != stdNumShortTZ && std != stdISO8601ShortTZ { |
| | b = appendInt(b, zone%60, 2) |
| | } |
| |
|
| | |
| | if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { |
| | if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { |
| | b = append(b, ':') |
| | } |
| | b = appendInt(b, absoffset%60, 2) |
| | } |
| |
|
| | case stdTZ: |
| | if name != "" { |
| | b = append(b, name...) |
| | break |
| | } |
| | |
| | |
| | zone := offset / 60 |
| | if zone < 0 { |
| | b = append(b, '-') |
| | zone = -zone |
| | } else { |
| | b = append(b, '+') |
| | } |
| | b = appendInt(b, zone/60, 2) |
| | b = appendInt(b, zone%60, 2) |
| | case stdFracSecond0, stdFracSecond9: |
| | b = appendNano(b, t.Nanosecond(), std) |
| | } |
| | } |
| | return b |
| | } |
| |
|
| | var errBad = errors.New("bad value for field") |
| |
|
| | |
| | type ParseError struct { |
| | Layout string |
| | Value string |
| | LayoutElem string |
| | ValueElem string |
| | Message string |
| | } |
| |
|
| | |
| | |
| | func newParseError(layout, value, layoutElem, valueElem, message string) *ParseError { |
| | valueCopy := stringslite.Clone(value) |
| | valueElemCopy := stringslite.Clone(valueElem) |
| | return &ParseError{layout, valueCopy, layoutElem, valueElemCopy, message} |
| | } |
| |
|
| | |
| | |
| | const ( |
| | lowerhex = "0123456789abcdef" |
| | runeSelf = 0x80 |
| | runeError = '\uFFFD' |
| | ) |
| |
|
| | func quote(s string) string { |
| | buf := make([]byte, 1, len(s)+2) |
| | buf[0] = '"' |
| | for i, c := range s { |
| | if c >= runeSelf || c < ' ' { |
| | |
| | |
| | |
| | |
| | |
| | |
| | var width int |
| | if c == runeError { |
| | width = 1 |
| | if i+2 < len(s) && s[i:i+3] == string(runeError) { |
| | width = 3 |
| | } |
| | } else { |
| | width = len(string(c)) |
| | } |
| | for j := 0; j < width; j++ { |
| | buf = append(buf, `\x`...) |
| | buf = append(buf, lowerhex[s[i+j]>>4]) |
| | buf = append(buf, lowerhex[s[i+j]&0xF]) |
| | } |
| | } else { |
| | if c == '"' || c == '\\' { |
| | buf = append(buf, '\\') |
| | } |
| | buf = append(buf, byte(c)) |
| | } |
| | } |
| | buf = append(buf, '"') |
| | return string(buf) |
| | } |
| |
|
| | |
| | func (e *ParseError) Error() string { |
| | if e.Message == "" { |
| | return "parsing time " + |
| | quote(e.Value) + " as " + |
| | quote(e.Layout) + ": cannot parse " + |
| | quote(e.ValueElem) + " as " + |
| | quote(e.LayoutElem) |
| | } |
| | return "parsing time " + |
| | quote(e.Value) + e.Message |
| | } |
| |
|
| | |
| | func isDigit[bytes []byte | string](s bytes, i int) bool { |
| | if len(s) <= i { |
| | return false |
| | } |
| | c := s[i] |
| | return '0' <= c && c <= '9' |
| | } |
| |
|
| | |
| | |
| | |
| | func getnum(s string, fixed bool) (int, string, error) { |
| | if !isDigit(s, 0) { |
| | return 0, s, errBad |
| | } |
| | if !isDigit(s, 1) { |
| | if fixed { |
| | return 0, s, errBad |
| | } |
| | return int(s[0] - '0'), s[1:], nil |
| | } |
| | return int(s[0]-'0')*10 + int(s[1]-'0'), s[2:], nil |
| | } |
| |
|
| | |
| | |
| | |
| | func getnum3(s string, fixed bool) (int, string, error) { |
| | var n, i int |
| | for i = 0; i < 3 && isDigit(s, i); i++ { |
| | n = n*10 + int(s[i]-'0') |
| | } |
| | if i == 0 || fixed && i != 3 { |
| | return 0, s, errBad |
| | } |
| | return n, s[i:], nil |
| | } |
| |
|
| | func cutspace(s string) string { |
| | for len(s) > 0 && s[0] == ' ' { |
| | s = s[1:] |
| | } |
| | return s |
| | } |
| |
|
| | |
| | |
| | func skip(value, prefix string) (string, error) { |
| | for len(prefix) > 0 { |
| | if prefix[0] == ' ' { |
| | if len(value) > 0 && value[0] != ' ' { |
| | return value, errBad |
| | } |
| | prefix = cutspace(prefix) |
| | value = cutspace(value) |
| | continue |
| | } |
| | if len(value) == 0 || value[0] != prefix[0] { |
| | return value, errBad |
| | } |
| | prefix = prefix[1:] |
| | value = value[1:] |
| | } |
| | return value, nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func Parse(layout, value string) (Time, error) { |
| | |
| | if layout == RFC3339 || layout == RFC3339Nano { |
| | if t, ok := parseRFC3339(value, Local); ok { |
| | return t, nil |
| | } |
| | } |
| | return parse(layout, value, UTC, Local) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func ParseInLocation(layout, value string, loc *Location) (Time, error) { |
| | |
| | if layout == RFC3339 || layout == RFC3339Nano { |
| | if t, ok := parseRFC3339(value, loc); ok { |
| | return t, nil |
| | } |
| | } |
| | return parse(layout, value, loc, loc) |
| | } |
| |
|
| | func parse(layout, value string, defaultLocation, local *Location) (Time, error) { |
| | alayout, avalue := layout, value |
| | rangeErrString := "" |
| | amSet := false |
| | pmSet := false |
| |
|
| | |
| | var ( |
| | year int |
| | month int = -1 |
| | day int = -1 |
| | yday int = -1 |
| | hour int |
| | min int |
| | sec int |
| | nsec int |
| | z *Location |
| | zoneOffset int = -1 |
| | zoneName string |
| | ) |
| |
|
| | |
| | for { |
| | var err error |
| | prefix, std, suffix := nextStdChunk(layout) |
| | stdstr := layout[len(prefix) : len(layout)-len(suffix)] |
| | value, err = skip(value, prefix) |
| | if err != nil { |
| | return Time{}, newParseError(alayout, avalue, prefix, value, "") |
| | } |
| | if std == 0 { |
| | if len(value) != 0 { |
| | return Time{}, newParseError(alayout, avalue, "", value, ": extra text: "+quote(value)) |
| | } |
| | break |
| | } |
| | layout = suffix |
| | var p string |
| | hold := value |
| | switch std & stdMask { |
| | case stdYear: |
| | if len(value) < 2 { |
| | err = errBad |
| | break |
| | } |
| | p, value = value[0:2], value[2:] |
| | year, err = atoi(p) |
| | if err != nil { |
| | break |
| | } |
| | if year >= 69 { |
| | year += 1900 |
| | } else { |
| | year += 2000 |
| | } |
| | case stdLongYear: |
| | if len(value) < 4 || !isDigit(value, 0) { |
| | err = errBad |
| | break |
| | } |
| | p, value = value[0:4], value[4:] |
| | year, err = atoi(p) |
| | case stdMonth: |
| | month, value, err = lookup(shortMonthNames, value) |
| | month++ |
| | case stdLongMonth: |
| | month, value, err = lookup(longMonthNames, value) |
| | month++ |
| | case stdNumMonth, stdZeroMonth: |
| | month, value, err = getnum(value, std == stdZeroMonth) |
| | if err == nil && (month <= 0 || 12 < month) { |
| | rangeErrString = "month" |
| | } |
| | case stdWeekDay: |
| | |
| | _, value, err = lookup(shortDayNames, value) |
| | case stdLongWeekDay: |
| | _, value, err = lookup(longDayNames, value) |
| | case stdDay, stdUnderDay, stdZeroDay: |
| | if std == stdUnderDay && len(value) > 0 && value[0] == ' ' { |
| | value = value[1:] |
| | } |
| | day, value, err = getnum(value, std == stdZeroDay) |
| | |
| | |
| | case stdUnderYearDay, stdZeroYearDay: |
| | for i := 0; i < 2; i++ { |
| | if std == stdUnderYearDay && len(value) > 0 && value[0] == ' ' { |
| | value = value[1:] |
| | } |
| | } |
| | yday, value, err = getnum3(value, std == stdZeroYearDay) |
| | |
| | |
| | case stdHour: |
| | hour, value, err = getnum(value, false) |
| | if hour < 0 || 24 <= hour { |
| | rangeErrString = "hour" |
| | } |
| | case stdHour12, stdZeroHour12: |
| | hour, value, err = getnum(value, std == stdZeroHour12) |
| | if hour < 0 || 12 < hour { |
| | rangeErrString = "hour" |
| | } |
| | case stdMinute, stdZeroMinute: |
| | min, value, err = getnum(value, std == stdZeroMinute) |
| | if min < 0 || 60 <= min { |
| | rangeErrString = "minute" |
| | } |
| | case stdSecond, stdZeroSecond: |
| | sec, value, err = getnum(value, std == stdZeroSecond) |
| | if err != nil { |
| | break |
| | } |
| | if sec < 0 || 60 <= sec { |
| | rangeErrString = "second" |
| | break |
| | } |
| | |
| | |
| | if len(value) >= 2 && commaOrPeriod(value[0]) && isDigit(value, 1) { |
| | _, std, _ = nextStdChunk(layout) |
| | std &= stdMask |
| | if std == stdFracSecond0 || std == stdFracSecond9 { |
| | |
| | break |
| | } |
| | |
| | n := 2 |
| | for ; n < len(value) && isDigit(value, n); n++ { |
| | } |
| | nsec, rangeErrString, err = parseNanoseconds(value, n) |
| | value = value[n:] |
| | } |
| | case stdPM: |
| | if len(value) < 2 { |
| | err = errBad |
| | break |
| | } |
| | p, value = value[0:2], value[2:] |
| | switch p { |
| | case "PM": |
| | pmSet = true |
| | case "AM": |
| | amSet = true |
| | default: |
| | err = errBad |
| | } |
| | case stdpm: |
| | if len(value) < 2 { |
| | err = errBad |
| | break |
| | } |
| | p, value = value[0:2], value[2:] |
| | switch p { |
| | case "pm": |
| | pmSet = true |
| | case "am": |
| | amSet = true |
| | default: |
| | err = errBad |
| | } |
| | case stdISO8601TZ, stdISO8601ShortTZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ: |
| | if len(value) >= 1 && value[0] == 'Z' { |
| | value = value[1:] |
| | z = UTC |
| | break |
| | } |
| | fallthrough |
| | case stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: |
| | var sign, hour, min, seconds string |
| | if std == stdISO8601ColonTZ || std == stdNumColonTZ { |
| | if len(value) < 6 { |
| | err = errBad |
| | break |
| | } |
| | if value[3] != ':' { |
| | err = errBad |
| | break |
| | } |
| | sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:] |
| | } else if std == stdNumShortTZ || std == stdISO8601ShortTZ { |
| | if len(value) < 3 { |
| | err = errBad |
| | break |
| | } |
| | sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:] |
| | } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ { |
| | if len(value) < 9 { |
| | err = errBad |
| | break |
| | } |
| | if value[3] != ':' || value[6] != ':' { |
| | err = errBad |
| | break |
| | } |
| | sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:] |
| | } else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz { |
| | if len(value) < 7 { |
| | err = errBad |
| | break |
| | } |
| | sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:] |
| | } else { |
| | if len(value) < 5 { |
| | err = errBad |
| | break |
| | } |
| | sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:] |
| | } |
| | var hr, mm, ss int |
| | hr, _, err = getnum(hour, true) |
| | if err == nil { |
| | mm, _, err = getnum(min, true) |
| | if err == nil { |
| | ss, _, err = getnum(seconds, true) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | if hr > 24 { |
| | rangeErrString = "time zone offset hour" |
| | } |
| | if mm > 60 { |
| | rangeErrString = "time zone offset minute" |
| | } |
| | if ss > 60 { |
| | rangeErrString = "time zone offset second" |
| | } |
| |
|
| | zoneOffset = (hr*60+mm)*60 + ss |
| | switch sign[0] { |
| | case '+': |
| | case '-': |
| | zoneOffset = -zoneOffset |
| | default: |
| | err = errBad |
| | } |
| | case stdTZ: |
| | |
| | if len(value) >= 3 && value[0:3] == "UTC" { |
| | z = UTC |
| | value = value[3:] |
| | break |
| | } |
| | n, ok := parseTimeZone(value) |
| | if !ok { |
| | err = errBad |
| | break |
| | } |
| | zoneName, value = value[:n], value[n:] |
| |
|
| | case stdFracSecond0: |
| | |
| | |
| | ndigit := 1 + digitsLen(std) |
| | if len(value) < ndigit { |
| | err = errBad |
| | break |
| | } |
| | nsec, rangeErrString, err = parseNanoseconds(value, ndigit) |
| | value = value[ndigit:] |
| |
|
| | case stdFracSecond9: |
| | if len(value) < 2 || !commaOrPeriod(value[0]) || value[1] < '0' || '9' < value[1] { |
| | |
| | break |
| | } |
| | |
| | |
| | i := 0 |
| | for i+1 < len(value) && '0' <= value[i+1] && value[i+1] <= '9' { |
| | i++ |
| | } |
| | nsec, rangeErrString, err = parseNanoseconds(value, 1+i) |
| | value = value[1+i:] |
| | } |
| | if rangeErrString != "" { |
| | return Time{}, newParseError(alayout, avalue, stdstr, value, ": "+rangeErrString+" out of range") |
| | } |
| | if err != nil { |
| | return Time{}, newParseError(alayout, avalue, stdstr, hold, "") |
| | } |
| | } |
| | if pmSet && hour < 12 { |
| | hour += 12 |
| | } else if amSet && hour == 12 { |
| | hour = 0 |
| | } |
| |
|
| | |
| | if yday >= 0 { |
| | var d int |
| | var m int |
| | if isLeap(year) { |
| | if yday == 31+29 { |
| | m = int(February) |
| | d = 29 |
| | } else if yday > 31+29 { |
| | yday-- |
| | } |
| | } |
| | if yday < 1 || yday > 365 { |
| | return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year out of range") |
| | } |
| | if m == 0 { |
| | m = (yday-1)/31 + 1 |
| | if daysBefore(Month(m+1)) < yday { |
| | m++ |
| | } |
| | d = yday - daysBefore(Month(m)) |
| | } |
| | |
| | |
| | if month >= 0 && month != m { |
| | return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match month") |
| | } |
| | month = m |
| | if day >= 0 && day != d { |
| | return Time{}, newParseError(alayout, avalue, "", value, ": day-of-year does not match day") |
| | } |
| | day = d |
| | } else { |
| | if month < 0 { |
| | month = int(January) |
| | } |
| | if day < 0 { |
| | day = 1 |
| | } |
| | } |
| |
|
| | |
| | if day < 1 || day > daysIn(Month(month), year) { |
| | return Time{}, newParseError(alayout, avalue, "", value, ": day out of range") |
| | } |
| |
|
| | if z != nil { |
| | return Date(year, Month(month), day, hour, min, sec, nsec, z), nil |
| | } |
| |
|
| | if zoneOffset != -1 { |
| | t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) |
| | t.addSec(-int64(zoneOffset)) |
| |
|
| | |
| | |
| | name, offset, _, _, _ := local.lookup(t.unixSec()) |
| | if offset == zoneOffset && (zoneName == "" || name == zoneName) { |
| | t.setLoc(local) |
| | return t, nil |
| | } |
| |
|
| | |
| | zoneNameCopy := stringslite.Clone(zoneName) |
| | t.setLoc(FixedZone(zoneNameCopy, zoneOffset)) |
| | return t, nil |
| | } |
| |
|
| | if zoneName != "" { |
| | t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) |
| | |
| | |
| | offset, ok := local.lookupName(zoneName, t.unixSec()) |
| | if ok { |
| | t.addSec(-int64(offset)) |
| | t.setLoc(local) |
| | return t, nil |
| | } |
| |
|
| | |
| | if len(zoneName) > 3 && zoneName[:3] == "GMT" { |
| | offset, _ = atoi(zoneName[3:]) |
| | offset *= 3600 |
| | } |
| | zoneNameCopy := stringslite.Clone(zoneName) |
| | t.setLoc(FixedZone(zoneNameCopy, offset)) |
| | return t, nil |
| | } |
| |
|
| | |
| | return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | func parseTimeZone(value string) (length int, ok bool) { |
| | if len(value) < 3 { |
| | return 0, false |
| | } |
| | |
| | if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") { |
| | return 4, true |
| | } |
| | |
| | if value[:3] == "GMT" { |
| | length = parseGMT(value) |
| | return length, true |
| | } |
| | |
| | if value[0] == '+' || value[0] == '-' { |
| | length = parseSignedOffset(value) |
| | ok := length > 0 |
| | return length, ok |
| | } |
| | |
| | var nUpper int |
| | for nUpper = 0; nUpper < 6; nUpper++ { |
| | if nUpper >= len(value) { |
| | break |
| | } |
| | if c := value[nUpper]; c < 'A' || 'Z' < c { |
| | break |
| | } |
| | } |
| | switch nUpper { |
| | case 0, 1, 2, 6: |
| | return 0, false |
| | case 5: |
| | if value[4] == 'T' { |
| | return 5, true |
| | } |
| | case 4: |
| | |
| | if value[3] == 'T' || value[:4] == "WITA" { |
| | return 4, true |
| | } |
| | case 3: |
| | return 3, true |
| | } |
| | return 0, false |
| | } |
| |
|
| | |
| | |
| | |
| | func parseGMT(value string) int { |
| | value = value[3:] |
| | if len(value) == 0 { |
| | return 3 |
| | } |
| |
|
| | return 3 + parseSignedOffset(value) |
| | } |
| |
|
| | |
| | |
| | |
| | func parseSignedOffset(value string) int { |
| | sign := value[0] |
| | if sign != '-' && sign != '+' { |
| | return 0 |
| | } |
| | x, rem, err := leadingInt(value[1:]) |
| |
|
| | |
| | if err != nil || value[1:] == rem { |
| | return 0 |
| | } |
| | if x > 23 { |
| | return 0 |
| | } |
| | return len(value) - len(rem) |
| | } |
| |
|
| | func commaOrPeriod(b byte) bool { |
| | return b == '.' || b == ',' |
| | } |
| |
|
| | func parseNanoseconds[bytes []byte | string](value bytes, nbytes int) (ns int, rangeErrString string, err error) { |
| | if !commaOrPeriod(value[0]) { |
| | err = errBad |
| | return |
| | } |
| | if nbytes > 10 { |
| | value = value[:10] |
| | nbytes = 10 |
| | } |
| | if ns, err = atoi(value[1:nbytes]); err != nil { |
| | return |
| | } |
| | if ns < 0 { |
| | rangeErrString = "fractional second" |
| | return |
| | } |
| | |
| | |
| | scaleDigits := 10 - nbytes |
| | for i := 0; i < scaleDigits; i++ { |
| | ns *= 10 |
| | } |
| | return |
| | } |
| |
|
| | var errLeadingInt = errors.New("time: bad [0-9]*") |
| |
|
| | |
| | func leadingInt[bytes []byte | string](s bytes) (x uint64, rem bytes, err error) { |
| | i := 0 |
| | for ; i < len(s); i++ { |
| | c := s[i] |
| | if c < '0' || c > '9' { |
| | break |
| | } |
| | if x > 1<<63/10 { |
| | |
| | return 0, rem, errLeadingInt |
| | } |
| | x = x*10 + uint64(c) - '0' |
| | if x > 1<<63 { |
| | |
| | return 0, rem, errLeadingInt |
| | } |
| | } |
| | return x, s[i:], nil |
| | } |
| |
|
| | |
| | |
| | |
| | func leadingFraction(s string) (x uint64, scale float64, rem string) { |
| | i := 0 |
| | scale = 1 |
| | overflow := false |
| | for ; i < len(s); i++ { |
| | c := s[i] |
| | if c < '0' || c > '9' { |
| | break |
| | } |
| | if overflow { |
| | continue |
| | } |
| | if x > (1<<63-1)/10 { |
| | |
| | overflow = true |
| | continue |
| | } |
| | y := x*10 + uint64(c) - '0' |
| | if y > 1<<63 { |
| | overflow = true |
| | continue |
| | } |
| | x = y |
| | scale *= 10 |
| | } |
| | return x, scale, s[i:] |
| | } |
| |
|
| | |
| | type parseDurationError struct { |
| | message string |
| | value string |
| | } |
| |
|
| | func (e *parseDurationError) Error() string { |
| | return "time: " + e.message + " " + quote(e.value) |
| | } |
| |
|
| | var unitMap = map[string]uint64{ |
| | "ns": uint64(Nanosecond), |
| | "us": uint64(Microsecond), |
| | "µs": uint64(Microsecond), |
| | "μs": uint64(Microsecond), |
| | "ms": uint64(Millisecond), |
| | "s": uint64(Second), |
| | "m": uint64(Minute), |
| | "h": uint64(Hour), |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | func ParseDuration(s string) (Duration, error) { |
| | |
| | orig := s |
| | var d uint64 |
| | neg := false |
| |
|
| | |
| | if s != "" { |
| | c := s[0] |
| | if c == '-' || c == '+' { |
| | neg = c == '-' |
| | s = s[1:] |
| | } |
| | } |
| | |
| | if s == "0" { |
| | return 0, nil |
| | } |
| | if s == "" { |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | for s != "" { |
| | var ( |
| | v, f uint64 |
| | scale float64 = 1 |
| | ) |
| |
|
| | var err error |
| |
|
| | |
| | if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | |
| | pl := len(s) |
| | v, s, err = leadingInt(s) |
| | if err != nil { |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | pre := pl != len(s) |
| |
|
| | |
| | post := false |
| | if s != "" && s[0] == '.' { |
| | s = s[1:] |
| | pl := len(s) |
| | f, scale, s = leadingFraction(s) |
| | post = pl != len(s) |
| | } |
| | if !pre && !post { |
| | |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| |
|
| | |
| | i := 0 |
| | for ; i < len(s); i++ { |
| | c := s[i] |
| | if c == '.' || '0' <= c && c <= '9' { |
| | break |
| | } |
| | } |
| | if i == 0 { |
| | return 0, &parseDurationError{"missing unit in duration", orig} |
| | } |
| | u := s[:i] |
| | s = s[i:] |
| | unit, ok := unitMap[u] |
| | if !ok { |
| | return 0, &parseDurationError{"unknown unit " + quote(u) + " in duration", orig} |
| | } |
| | if v > 1<<63/unit { |
| | |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | v *= unit |
| | if f > 0 { |
| | |
| | |
| | v += uint64(float64(f) * (float64(unit) / scale)) |
| | if v > 1<<63 { |
| | |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | } |
| | d += v |
| | if d > 1<<63 { |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | } |
| | if neg { |
| | return -Duration(d), nil |
| | } |
| | if d > 1<<63-1 { |
| | return 0, &parseDurationError{"invalid duration", orig} |
| | } |
| | return Duration(d), nil |
| | } |
| |
|