dateparse/parseany.go

1467 lines
34 KiB
Go
Raw Normal View History

2017-10-04 18:50:25 +08:00
// Package dateparse parses date-strings without knowing the format
// in advance, using a fast lex based approach to eliminate shotgun
// attempts. It leans towards US style dates when there is a conflict.
2014-04-21 10:56:17 +08:00
package dateparse
import (
"fmt"
2014-04-26 07:59:10 +08:00
"strconv"
2014-04-21 10:56:17 +08:00
"time"
"unicode"
2017-11-19 03:07:33 +08:00
"unicode/utf8"
2014-04-21 10:56:17 +08:00
)
// func init() {
// gou.SetupLogging("debug")
// gou.SetColorOutput()
// }
2018-02-26 08:35:33 +08:00
type dateState uint8
type timeState uint8
2014-05-21 13:08:25 +08:00
2014-04-21 10:56:17 +08:00
const (
2018-02-26 08:35:33 +08:00
dateStart dateState = iota
dateDigit
dateDigitDash
2018-02-26 14:46:45 +08:00
dateDigitDashDash
dateDigitDashDashWs
dateDigitDashDashT
dateDigitDashDashAlpha
2018-02-26 08:35:33 +08:00
dateDigitDot
dateDigitDotDot
dateDigitSlash
dateDigitChineseYear
dateDigitChineseYearWs
dateDigitWs
2018-02-28 12:05:14 +08:00
dateDigitWsMoYear
2018-03-02 10:36:46 +08:00
dateDigitWsMolong
2018-02-26 08:35:33 +08:00
dateAlpha
2018-03-02 10:36:46 +08:00
dateAlphaWs
dateAlphaWsDigit
dateAlphaWsDigitComma
dateAlphaWsDigitCommaWs
dateAlphaWsDigitCommaWsYear
dateAlphaWsAlpha
dateAlphaWsAlphaYearmaybe
2018-02-26 08:35:33 +08:00
dateWeekdayComma
dateWeekdayAbbrevComma
2018-02-26 14:46:45 +08:00
)
const (
// Time state
timeIgnore timeState = iota // 0
2018-02-26 08:35:33 +08:00
timeStart
timeWs
2018-03-12 08:39:59 +08:00
timeWsAlpha
timeWsAlphaWs
timeWsAlphaZoneOffset // 5
2018-03-02 10:36:46 +08:00
timeWsAlphaZoneOffsetWs
timeWsAlphaZoneOffsetWsYear
timeWsAlphaZoneOffsetWsExtra
2018-02-26 08:35:33 +08:00
timeWsAMPMMaybe
timeWsAMPM // 10
2018-03-02 10:36:46 +08:00
timeWsOffset
timeWsOffsetWs // 12
2018-02-26 08:35:33 +08:00
timeWsOffsetColonAlpha
timeWsOffsetColon
timeWsYear // 15
2018-03-02 10:36:46 +08:00
timeOffset
2018-02-26 08:35:33 +08:00
timeOffsetColon
timeAlpha
timePeriod
timePeriodOffset // 20
2018-03-02 10:36:46 +08:00
timePeriodOffsetColon
2018-03-12 08:39:59 +08:00
timePeriodOffsetColonWs
2018-02-26 14:46:45 +08:00
timePeriodWs
2018-02-28 12:05:14 +08:00
timePeriodWsAlpha
timePeriodWsOffset // 25
2018-02-28 12:05:14 +08:00
timePeriodWsOffsetWs
2018-03-02 10:36:46 +08:00
timePeriodWsOffsetWsAlpha
2018-03-12 08:39:59 +08:00
timePeriodWsOffsetColon
timePeriodWsOffsetColonAlpha
2018-02-26 08:35:33 +08:00
timeZ
timeZDigit
2014-04-21 10:56:17 +08:00
)
var (
2018-03-25 08:49:27 +08:00
ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format")
)
2018-03-26 07:48:11 +08:00
// ParseAny parse an unknown date format, detect the layout.
// Normal parse. Equivalent Timezone rules as time.Parse().
// NOTE: please see readme on mmdd vs ddmm ambiguous dates.
2014-05-07 12:15:43 +08:00
func ParseAny(datestr string) (time.Time, error) {
2018-03-25 06:56:26 +08:00
p, err := parseTime(datestr, nil)
if err != nil {
return time.Time{}, err
}
return p.parse()
2017-07-27 07:42:12 +08:00
}
2017-08-13 00:48:28 +08:00
// ParseIn with Location, equivalent to time.ParseInLocation() timezone/offset
2017-07-27 13:37:37 +08:00
// rules. Using location arg, if timezone/offset info exists in the
// datestring, it uses the given location rules for any zone interpretation.
// That is, MST means one thing when using America/Denver and something else
// in other locations.
2017-07-27 07:42:12 +08:00
func ParseIn(datestr string, loc *time.Location) (time.Time, error) {
2018-03-25 06:56:26 +08:00
p, err := parseTime(datestr, loc)
if err != nil {
return time.Time{}, err
}
return p.parse()
2017-07-27 07:42:12 +08:00
}
// ParseLocal Given an unknown date format, detect the layout,
// using time.Local, parse.
2017-07-27 08:08:58 +08:00
//
2017-07-27 13:37:37 +08:00
// Set Location to time.Local. Same as ParseIn Location but lazily uses
// the global time.Local variable for Location argument.
//
// denverLoc, _ := time.LoadLocation("America/Denver")
// time.Local = denverLoc
//
// t, err := dateparse.ParseLocal("3/1/2014")
//
// Equivalent to:
//
// t, err := dateparse.ParseIn("3/1/2014", denverLoc)
//
2017-07-27 07:42:12 +08:00
func ParseLocal(datestr string) (time.Time, error) {
2018-03-25 06:56:26 +08:00
p, err := parseTime(datestr, time.Local)
if err != nil {
return time.Time{}, err
}
return p.parse()
2017-07-27 07:42:12 +08:00
}
2017-08-13 00:48:28 +08:00
// MustParse parse a date, and panic if it can't be parsed. Used for testing.
// Not recommended for most use-cases.
2017-07-27 13:39:44 +08:00
func MustParse(datestr string) time.Time {
2018-03-25 06:56:26 +08:00
p, err := parseTime(datestr, nil)
if err != nil {
panic(err.Error())
}
t, err := p.parse()
2017-07-27 13:39:44 +08:00
if err != nil {
panic(err.Error())
}
return t
}
2018-03-26 07:48:11 +08:00
// ParseFormat parse's an unknown date-time string and returns a layout
// string that can parse this (and exact same format) other date-time strings.
//
// layout, err := dateparse.ParseFormat("2013-02-01 00:00:00")
// // layout = "2006-01-02 15:04:05"
//
2018-03-25 06:56:26 +08:00
func ParseFormat(datestr string) (string, error) {
p, err := parseTime(datestr, nil)
if err != nil {
return "", err
}
_, err = p.parse()
if err != nil {
return "", err
2017-07-27 07:42:12 +08:00
}
2018-03-25 06:56:26 +08:00
return string(p.format), nil
2017-07-27 07:42:12 +08:00
}
2014-04-21 10:56:17 +08:00
2018-03-25 08:49:27 +08:00
// ParseStrict parse an unknown date format. IF the date is ambigous
2018-03-26 07:48:11 +08:00
// mm/dd vs dd/mm then return an error. These return errors: 3.3.2014 , 8/8/71 etc
2018-03-25 08:49:27 +08:00
func ParseStrict(datestr string) (time.Time, error) {
p, err := parseTime(datestr, nil)
if err != nil {
return time.Time{}, err
}
if p.ambiguousMD {
return time.Time{}, ErrAmbiguousMMDD
}
return p.parse()
}
2018-02-26 14:46:45 +08:00
type parser struct {
loc *time.Location
preferMonthFirst bool
2018-03-25 06:56:26 +08:00
ambiguousMD bool
2018-02-26 14:46:45 +08:00
stateDate dateState
stateTime timeState
format []byte
datestr string
2018-03-02 10:36:46 +08:00
skip int
extra int
2018-02-26 14:46:45 +08:00
part1Len int
yeari int
yearlen int
moi int
molen int
dayi int
daylen int
houri int
hourlen int
mini int
minlen int
seci int
seclen int
msi int
mslen int
offseti int
2018-03-02 10:36:46 +08:00
offsetlen int
tzi int
tzlen int
2018-03-25 06:56:26 +08:00
t *time.Time
2018-02-26 14:46:45 +08:00
}
2018-03-02 10:36:46 +08:00
func newParser(dateStr string, loc *time.Location) *parser {
2018-02-26 14:46:45 +08:00
p := parser{
stateDate: dateStart,
stateTime: timeIgnore,
datestr: dateStr,
loc: loc,
preferMonthFirst: true,
2018-02-26 14:46:45 +08:00
}
p.format = []byte(dateStr)
2018-03-02 10:36:46 +08:00
return &p
2018-02-26 14:46:45 +08:00
}
2018-03-02 10:36:46 +08:00
func (p *parser) set(start int, val string) {
2018-02-28 12:05:14 +08:00
if start < 0 {
return
}
2018-02-26 14:46:45 +08:00
if len(p.format) < start+len(val) {
return
}
for i, r := range val {
p.format[start+i] = byte(r)
}
}
2018-03-02 10:36:46 +08:00
func (p *parser) setMonth() {
2018-02-26 14:46:45 +08:00
if p.molen == 2 {
p.set(p.moi, "01")
2018-03-02 10:36:46 +08:00
} else if p.molen == 1 {
2018-02-26 14:46:45 +08:00
p.set(p.moi, "1")
}
}
2018-02-28 12:05:14 +08:00
2018-03-02 10:36:46 +08:00
func (p *parser) setDay() {
2018-02-26 14:46:45 +08:00
if p.daylen == 2 {
p.set(p.dayi, "02")
2018-03-02 10:36:46 +08:00
} else if p.daylen == 1 {
2018-02-26 14:46:45 +08:00
p.set(p.dayi, "2")
}
}
2018-03-02 10:36:46 +08:00
func (p *parser) setYear() {
if p.yearlen == 2 {
p.set(p.yeari, "06")
} else if p.yearlen == 4 {
p.set(p.yeari, "2006")
}
}
func (p *parser) coalesceDate(end int) {
2018-02-28 12:05:14 +08:00
if p.yeari > 0 {
if p.yearlen == 0 {
p.yearlen = end - p.yeari
}
2018-03-02 10:36:46 +08:00
p.setYear()
2018-02-28 12:05:14 +08:00
}
if p.moi > 0 && p.molen == 0 {
p.molen = end - p.moi
p.setMonth()
}
if p.dayi > 0 && p.daylen == 0 {
p.daylen = end - p.dayi
p.setDay()
}
}
func (p *parser) ts() string {
return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen)
}
func (p *parser) ds() string {
return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen)
}
2018-03-02 10:36:46 +08:00
func (p *parser) coalesceTime(end int) {
2018-02-26 14:46:45 +08:00
// 03:04:05
// 15:04:05
// 3:04:05
// 3:4:5
// 15:04:05.00
if p.houri > 0 {
if p.hourlen == 2 {
p.set(p.houri, "15")
2018-03-02 10:36:46 +08:00
} else if p.hourlen == 1 {
2018-02-26 14:46:45 +08:00
p.set(p.houri, "3")
}
}
if p.mini > 0 {
if p.minlen == 0 {
p.minlen = end - p.mini
}
if p.minlen == 2 {
p.set(p.mini, "04")
} else {
p.set(p.mini, "4")
}
}
if p.seci > 0 {
if p.seclen == 0 {
p.seclen = end - p.seci
}
if p.seclen == 2 {
p.set(p.seci, "05")
} else {
p.set(p.seci, "5")
}
}
if p.msi > 0 {
for i := 0; i < p.mslen; i++ {
p.format[p.msi+i] = '0'
}
}
}
2018-02-28 12:05:14 +08:00
2018-03-02 10:36:46 +08:00
func (p *parser) trimExtra() {
if p.extra > 0 && len(p.format) > p.extra {
p.format = p.format[0:p.extra]
p.datestr = p.datestr[0:p.extra]
}
}
func (p *parser) parse() (time.Time, error) {
2018-03-25 06:56:26 +08:00
if p.t != nil {
return *p.t, nil
}
2018-03-02 10:36:46 +08:00
if p.skip > 0 && len(p.format) > p.skip {
p.format = p.format[p.skip:]
p.datestr = p.datestr[p.skip:]
}
2018-03-12 08:39:59 +08:00
//gou.Debugf("parse %q AS %s", p.datestr, string(p.format))
2018-02-26 14:46:45 +08:00
if p.loc == nil {
return time.Parse(string(p.format), p.datestr)
}
return time.ParseInLocation(string(p.format), p.datestr, p.loc)
}
2018-03-25 06:56:26 +08:00
func parseTime(datestr string, loc *time.Location) (*parser, error) {
2018-02-26 08:35:33 +08:00
2018-02-26 14:46:45 +08:00
p := newParser(datestr, loc)
2018-02-26 08:35:33 +08:00
i := 0
2014-04-21 10:56:17 +08:00
// General strategy is to read rune by rune through the date looking for
// certain hints of what type of date we are dealing with.
// Hopefully we only need to read about 5 or 6 bytes before
// we figure it out and then attempt a parse
2014-05-07 12:15:43 +08:00
iterRunes:
2018-02-26 08:35:33 +08:00
for ; i < len(datestr); i++ {
2017-11-19 03:07:33 +08:00
//r := rune(datestr[i])
r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:])
if bytesConsumed > 1 {
i += (bytesConsumed - 1)
}
2014-05-07 12:15:43 +08:00
2018-04-19 08:36:22 +08:00
//gou.Debugf("i=%d r=%s state=%d %s", i, string(r), p.stateDate, datestr)
2018-02-26 14:46:45 +08:00
switch p.stateDate {
2018-02-26 08:35:33 +08:00
case dateStart:
2014-05-07 12:15:43 +08:00
if unicode.IsDigit(r) {
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigit
2017-02-06 02:04:03 +08:00
} else if unicode.IsLetter(r) {
2018-02-26 14:46:45 +08:00
p.stateDate = dateAlpha
2018-03-25 06:56:26 +08:00
} else {
return nil, fmt.Errorf("unrecognized first character '%s' in %v", string(r), datestr)
2014-04-21 10:56:17 +08:00
}
2018-02-26 14:46:45 +08:00
case dateDigit:
2014-05-07 12:15:43 +08:00
switch r {
2017-07-14 10:57:15 +08:00
case '-', '\u2212':
2018-02-26 14:46:45 +08:00
// 2006-01-02
// 2006-01-02T15:04:05Z07:00
// 13-Feb-03
// 2013-Feb-03
p.stateDate = dateDigitDash
p.yeari = 0
p.yearlen = i
p.moi = i + 1
if i == 4 {
p.set(0, "2006")
}
2014-05-07 12:15:43 +08:00
case '/':
// 03/31/2005
// 2014/02/24
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigitSlash
if i == 4 {
p.yearlen = i
p.moi = i + 1
p.setYear()
} else {
2018-03-25 06:56:26 +08:00
p.ambiguousMD = true
if p.preferMonthFirst {
if p.molen == 0 {
p.molen = i
p.setMonth()
p.dayi = i + 1
}
}
}
2018-01-12 05:21:04 +08:00
case '.':
2018-02-28 12:05:14 +08:00
// 3.31.2014
2018-05-04 09:06:52 +08:00
// 08.21.71
// 2014.05
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigitDot
2018-05-04 09:06:52 +08:00
if i == 4 {
p.yearlen = i
p.moi = i + 1
p.setYear()
} else {
p.ambiguousMD = true
p.moi = 0
p.molen = i
p.setMonth()
p.dayi = i + 1
}
2018-01-25 09:59:25 +08:00
case ' ':
2018-02-28 12:05:14 +08:00
// 18 January 2018
// 8 January 2018
2018-03-11 05:43:16 +08:00
// 8 jan 2018
2018-02-28 12:05:14 +08:00
// 02 Jan 2018 23:59
// 02 Jan 2018 23:59:34
// 12 Feb 2006, 19:17
// 12 Feb 2006, 19:17:22
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigitWs
2018-02-28 12:05:14 +08:00
p.dayi = 0
p.daylen = i
2018-02-26 14:46:45 +08:00
case '年':
// Chinese Year
p.stateDate = dateDigitChineseYear
2018-04-19 08:36:22 +08:00
case ',':
return nil, fmt.Errorf("Unrecognized format %q", datestr)
2018-02-26 14:46:45 +08:00
default:
//if unicode.IsDigit(r) {
continue
2014-04-25 08:51:56 +08:00
}
2018-02-26 14:46:45 +08:00
p.part1Len = i
2018-01-25 09:59:25 +08:00
2018-02-26 14:46:45 +08:00
case dateDigitDash:
2018-02-28 12:05:14 +08:00
// 2006-01
2014-05-07 12:15:43 +08:00
// 2006-01-02
2018-02-26 14:46:45 +08:00
// dateDigitDashDashT
2018-02-14 12:44:26 +08:00
// 2006-01-02T15:04:05Z07:00
// 2017-06-25T17:46:57.45706582-07:00
// 2006-01-02T15:04:05.999999999Z07:00
// 2006-01-02T15:04:05+0000
2018-02-26 14:46:45 +08:00
// dateDigitDashDashWs
2018-02-14 12:44:26 +08:00
// 2012-08-03 18:31:59.257000000
// 2014-04-26 17:24:37.3186369
// 2017-01-27 00:07:31.945167
// 2016-03-14 00:00:00.000
// 2014-05-11 08:20:13,787
// 2017-07-19 03:21:51+00:00
// 2013-04-01 22:43:22
// 2014-04-26 05:24:37 PM
2018-02-26 14:46:45 +08:00
// dateDigitDashDashAlpha
2018-02-14 12:44:26 +08:00
// 2013-Feb-03
2018-02-26 14:46:45 +08:00
// 13-Feb-03
switch r {
case '-':
p.molen = i - p.moi
p.dayi = i + 1
p.stateDate = dateDigitDashDash
p.setMonth()
2014-12-03 06:57:00 +08:00
default:
2018-02-26 14:46:45 +08:00
if unicode.IsDigit(r) {
//continue
} else if unicode.IsLetter(r) {
p.stateDate = dateDigitDashDashAlpha
2014-12-03 06:57:00 +08:00
}
2014-05-07 12:15:43 +08:00
}
2018-02-26 14:46:45 +08:00
case dateDigitDashDash:
// 2006-01-02
// dateDigitDashDashT
// 2006-01-02T15:04:05Z07:00
// 2017-06-25T17:46:57.45706582-07:00
// 2006-01-02T15:04:05.999999999Z07:00
// 2006-01-02T15:04:05+0000
// dateDigitDashDashWs
// 2012-08-03 18:31:59.257000000
// 2014-04-26 17:24:37.3186369
// 2017-01-27 00:07:31.945167
// 2016-03-14 00:00:00.000
// 2014-05-11 08:20:13,787
// 2017-07-19 03:21:51+00:00
// 2013-04-01 22:43:22
// 2014-04-26 05:24:37 PM
switch r {
case ' ':
p.daylen = i - p.dayi
p.stateDate = dateDigitDashDashWs
p.stateTime = timeStart
p.setDay()
break iterRunes
case 'T':
p.daylen = i - p.dayi
p.stateDate = dateDigitDashDashT
p.stateTime = timeStart
p.setDay()
break iterRunes
}
case dateDigitDashDashAlpha:
// 2013-Feb-03
// 13-Feb-03
switch r {
case '-':
2018-02-28 12:05:14 +08:00
p.molen = i - p.moi
2018-02-26 14:46:45 +08:00
p.set(p.moi, "Jan")
p.dayi = i + 1
}
case dateDigitSlash:
2014-07-11 06:25:23 +08:00
// 2014/07/10 06:55:38.156283
2014-05-07 12:15:43 +08:00
// 03/19/2012 10:11:59
2014-05-12 04:33:13 +08:00
// 04/2/2014 03:00:37
2014-04-21 10:56:17 +08:00
// 3/1/2012 10:11:59
2014-04-28 04:26:17 +08:00
// 4/8/2014 22:05
2014-04-21 10:56:17 +08:00
// 3/1/2014
// 10/13/2014
// 01/02/2006
2016-01-06 03:34:31 +08:00
// 1/2/06
2018-02-26 14:46:45 +08:00
2014-05-07 12:15:43 +08:00
switch r {
case ' ':
p.stateTime = timeStart
if p.yearlen == 0 {
p.yearlen = i - p.yeari
p.setYear()
} else if p.daylen == 0 {
p.daylen = i - p.dayi
p.setDay()
}
break iterRunes
2018-02-26 14:46:45 +08:00
case '/':
if p.yearlen > 0 {
// 2014/07/10 06:55:38.156283
if p.molen == 0 {
p.molen = i - p.moi
p.setMonth()
p.dayi = i + 1
}
} else if p.preferMonthFirst {
if p.daylen == 0 {
p.daylen = i - p.dayi
p.setDay()
p.yeari = i + 1
}
}
2014-05-07 12:15:43 +08:00
}
2018-01-25 09:59:25 +08:00
2018-02-26 08:35:33 +08:00
case dateDigitWs:
2018-01-25 09:59:25 +08:00
// 18 January 2018
// 8 January 2018
2018-03-11 05:43:16 +08:00
// 8 jan 2018
// 1 jan 18
2018-02-28 12:05:14 +08:00
// 02 Jan 2018 23:59
// 02 Jan 2018 23:59:34
// 12 Feb 2006, 19:17
// 12 Feb 2006, 19:17:22
switch r {
case ' ':
p.yeari = i + 1
2018-03-11 05:43:16 +08:00
//p.yearlen = 4
2018-02-28 12:05:14 +08:00
p.dayi = 0
p.daylen = p.part1Len
p.setDay()
p.stateTime = timeStart
if i <= len("12 Feb") {
p.moi = p.daylen + 1
p.molen = 3
p.set(p.moi, "Jan")
2018-03-02 10:36:46 +08:00
p.stateDate = dateDigitWsMoYear
} else {
2018-03-02 10:36:46 +08:00
p.stateDate = dateDigitWsMolong
}
}
2018-02-28 12:05:14 +08:00
case dateDigitWsMoYear:
2018-03-11 05:43:16 +08:00
// 8 jan 2018
2018-02-28 12:05:14 +08:00
// 02 Jan 2018 23:59
// 02 Jan 2018 23:59:34
// 12 Feb 2006, 19:17
// 12 Feb 2006, 19:17:22
switch r {
2018-02-28 12:05:14 +08:00
case ',':
2018-03-12 08:39:59 +08:00
p.yearlen = i - p.yeari
p.setYear()
2018-02-28 12:05:14 +08:00
i++
break iterRunes
case ' ':
p.yearlen = i - p.yeari
p.setYear()
break iterRunes
2018-01-25 09:59:25 +08:00
}
2018-03-02 10:36:46 +08:00
case dateDigitWsMolong:
// 18 January 2018
// 8 January 2018
2018-01-25 09:59:25 +08:00
2018-02-26 08:35:33 +08:00
case dateDigitChineseYear:
// dateDigitChineseYear
2017-11-19 03:07:33 +08:00
// 2014年04月08日
// weekday %Y年%m月%e日 %A %I:%M %p
// 2013年07月18日 星期四 10:27 上午
if r == ' ' {
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigitChineseYearWs
2017-11-19 03:07:33 +08:00
break
}
2018-02-26 08:35:33 +08:00
case dateDigitDot:
2018-01-12 05:21:04 +08:00
// 3.31.2014
2018-05-04 09:06:52 +08:00
// 08.21.71
// 2014.05
2018-01-12 05:21:04 +08:00
if r == '.' {
2018-02-28 12:05:14 +08:00
p.daylen = i - p.dayi
p.yeari = i + 1
2018-03-02 10:36:46 +08:00
p.setDay()
2018-02-26 14:46:45 +08:00
p.stateDate = dateDigitDotDot
2018-01-12 05:21:04 +08:00
}
2018-02-26 08:35:33 +08:00
case dateDigitDotDot:
2018-01-12 05:21:04 +08:00
// iterate all the way through
2018-02-26 08:35:33 +08:00
case dateAlpha:
// dateAlphaWS
2017-07-14 00:11:41 +08:00
// Mon Jan _2 15:04:05 2006
// Mon Jan _2 15:04:05 MST 2006
// Mon Jan 02 15:04:05 -0700 2006
// Mon Aug 10 15:44:11 UTC+0100 2015
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
2018-02-26 08:35:33 +08:00
// dateAlphaWSDigit
2017-07-14 00:11:41 +08:00
// May 8, 2009 5:57:51 PM
2018-03-11 05:43:16 +08:00
// oct 1, 1970
2017-07-14 00:11:41 +08:00
//
2018-02-26 08:35:33 +08:00
// dateWeekdayComma
// Monday, 02 Jan 2006 15:04:05 MST
// Monday, 02-Jan-06 15:04:05 MST
// Monday, 02 Jan 2006 15:04:05 -0700
// Monday, 02 Jan 2006 15:04:05 +0100
2018-02-26 08:35:33 +08:00
// dateWeekdayAbbrevComma
2017-07-14 00:11:41 +08:00
// Mon, 02 Jan 2006 15:04:05 MST
// Mon, 02 Jan 2006 15:04:05 -0700
// Thu, 13 Jul 2017 08:58:40 +0100
// Tue, 11 Jul 2017 16:28:13 +0200 (CEST)
2018-03-12 08:39:59 +08:00
// Mon, 02-Jan-06 15:04:05 MST
2014-05-07 12:15:43 +08:00
switch {
case r == ' ':
if i > 4 {
// September 17, 2012 at 5:00pm UTC-05
// This one doesn't follow standard parse methodologies. the "January"
// is difficult to use the format string replace method because of its variable-length (march, june)
// so we just use this format here. If we see more similar to this we will do something else.
2018-03-25 06:56:26 +08:00
p.format = []byte("January 02, 2006 at 3:04pm MST-07")
return p, nil
}
2018-03-02 10:36:46 +08:00
p.stateDate = dateAlphaWs
2014-05-21 13:53:52 +08:00
case r == ',':
// p.moi = 0
// p.molen = i
2017-07-14 00:11:41 +08:00
if i == 3 {
2018-02-26 14:46:45 +08:00
p.stateDate = dateWeekdayAbbrevComma
p.set(0, "Mon")
2017-07-14 00:11:41 +08:00
} else {
2018-02-26 14:46:45 +08:00
p.stateDate = dateWeekdayComma
2018-03-02 10:36:46 +08:00
//p.set(0, "Monday")
p.skip = i + 2
i++
2018-03-02 10:36:46 +08:00
// TODO: lets just make this "skip" as we don't need
// the mon, monday, they are all superfelous and not needed
// just lay down the skip, no need to fill and then skip
}
}
case dateWeekdayComma:
// Monday, 02 Jan 2006 15:04:05 MST
// Monday, 02 Jan 2006 15:04:05 -0700
// Monday, 02 Jan 2006 15:04:05 +0100
// Monday, 02-Jan-06 15:04:05 MST
2018-03-02 10:36:46 +08:00
if p.dayi == 0 {
p.dayi = i
}
switch r {
case ' ', '-':
if p.moi == 0 {
p.moi = i + 1
p.daylen = i - p.dayi
p.setDay()
} else if p.yeari == 0 {
p.yeari = i + 1
p.molen = i - p.moi
p.set(p.moi, "Jan")
} else {
p.stateTime = timeStart
break iterRunes
}
}
case dateWeekdayAbbrevComma:
2017-07-14 00:11:41 +08:00
// Mon, 02 Jan 2006 15:04:05 MST
// Mon, 02 Jan 2006 15:04:05 -0700
// Thu, 13 Jul 2017 08:58:40 +0100
// Thu, 4 Jan 2018 17:53:36 +0000
// Tue, 11 Jul 2017 16:28:13 +0200 (CEST)
2018-03-12 08:39:59 +08:00
// Mon, 02-Jan-06 15:04:05 MST
switch r {
case ' ', '-':
if p.dayi == 0 {
p.dayi = i + 1
} else if p.moi == 0 {
p.daylen = i - p.dayi
p.setDay()
p.moi = i + 1
} else if p.yeari == 0 {
p.molen = i - p.moi
p.set(p.moi, "Jan")
p.yeari = i + 1
} else {
p.yearlen = i - p.yeari
p.setYear()
p.stateTime = timeStart
break iterRunes
}
2017-07-14 22:02:22 +08:00
}
2018-03-02 10:36:46 +08:00
case dateAlphaWs:
// dateAlphaWsAlpha
// Mon Jan _2 15:04:05 2006
// Mon Jan _2 15:04:05 MST 2006
// Mon Jan 02 15:04:05 -0700 2006
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
// Mon Aug 10 15:44:11 UTC+0100 2015
// dateAlphaWsDigit
// May 8, 2009 5:57:51 PM
2018-03-11 05:43:16 +08:00
// oct 1, 1970
// oct 7, '70
2014-05-07 12:15:43 +08:00
switch {
case unicode.IsLetter(r):
2018-03-02 10:36:46 +08:00
p.set(0, "Mon")
p.stateDate = dateAlphaWsAlpha
//p.moi = i
p.set(i, "Jan")
2017-07-14 00:11:41 +08:00
case unicode.IsDigit(r):
2018-03-02 10:36:46 +08:00
p.set(0, "Jan")
p.stateDate = dateAlphaWsDigit
p.dayi = i
2014-05-21 13:53:52 +08:00
}
2018-03-02 10:36:46 +08:00
case dateAlphaWsDigit:
2018-03-11 05:43:16 +08:00
// May 8, 2009 5:57:51 PM
// oct 1, 1970
// oct 7, '70
//gou.Debugf("%d %s dateAlphaWsDigit %s %s", i, string(r), p.ds(), p.ts())
if r == ',' {
2018-03-02 10:36:46 +08:00
p.daylen = i - p.dayi
p.setDay()
p.stateDate = dateAlphaWsDigitComma
2018-02-26 08:35:33 +08:00
}
2018-03-02 10:36:46 +08:00
case dateAlphaWsDigitComma:
2018-03-11 05:43:16 +08:00
// x
// May 8, 2009 5:57:51 PM
// oct 1, 1970
// oct 7, '70
if r == ' ' {
2018-03-02 10:36:46 +08:00
p.stateDate = dateAlphaWsDigitCommaWs
p.yeari = i + 1
2018-02-26 08:35:33 +08:00
}
2018-03-02 10:36:46 +08:00
case dateAlphaWsDigitCommaWs:
2018-03-11 05:43:16 +08:00
// x
// May 8, 2009 5:57:51 PM
// oct 1, 1970
// oct 7, '70
switch r {
case '\'':
p.yeari = i + 1
case ' ':
2018-03-02 10:36:46 +08:00
p.stateDate = dateAlphaWsDigitCommaWsYear
p.yearlen = i - p.yeari
p.setYear()
p.stateTime = timeStart
2018-02-26 08:35:33 +08:00
break iterRunes
2018-02-08 23:03:45 +08:00
}
2018-03-02 10:36:46 +08:00
case dateAlphaWsAlpha:
2015-07-04 01:27:48 +08:00
// Mon Jan _2 15:04:05 2006
// Mon Jan 02 15:04:05 -0700 2006
2015-07-04 01:27:48 +08:00
// Mon Jan _2 15:04:05 MST 2006
// Mon Aug 10 15:44:11 UTC+0100 2015
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
2018-03-02 10:36:46 +08:00
if r == ' ' {
if p.dayi > 0 {
2018-03-12 05:08:21 +08:00
2018-03-02 10:36:46 +08:00
p.daylen = i - p.dayi
p.setDay()
p.yeari = i + 1
p.stateDate = dateAlphaWsAlphaYearmaybe
p.stateTime = timeStart
}
} else if unicode.IsDigit(r) {
if p.dayi == 0 {
p.dayi = i
}
}
2018-03-02 10:36:46 +08:00
case dateAlphaWsAlphaYearmaybe:
// x
// Mon Jan _2 15:04:05 2006
// Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)
2018-03-02 10:36:46 +08:00
if r == ':' {
i = i - 3
p.stateDate = dateAlphaWsAlpha
p.yeari = 0
break iterRunes
} else if r == ' ' {
// must be year format, not 15:04
p.yearlen = i - p.yeari
p.setYear()
break iterRunes
2014-05-21 13:53:52 +08:00
}
2018-03-02 10:36:46 +08:00
2014-05-07 12:15:43 +08:00
default:
break iterRunes
2014-04-21 10:56:17 +08:00
}
2014-05-07 12:15:43 +08:00
}
2018-02-28 12:05:14 +08:00
p.coalesceDate(i)
2018-02-26 14:46:45 +08:00
if p.stateTime == timeStart {
// increment first one, since the i++ occurs at end of loop
2018-03-11 05:43:16 +08:00
if i < len(p.datestr) {
i++
}
2018-03-12 08:39:59 +08:00
// ensure we skip any whitespace prefix
for ; i < len(datestr); i++ {
r := rune(datestr[i])
if r != ' ' {
break
}
}
2018-02-26 14:46:45 +08:00
iterTimeRunes:
for ; i < len(datestr); i++ {
2018-02-28 12:05:14 +08:00
r := rune(datestr[i])
2018-02-26 14:46:45 +08:00
2018-04-06 21:57:28 +08:00
//gou.Debugf("%d %s %d iterTimeRunes %s %s", i, string(r), p.stateTime, p.ds(), p.ts())
2018-03-12 05:08:21 +08:00
2018-02-26 14:46:45 +08:00
switch p.stateTime {
case timeStart:
// 22:43:22
// 22:43
2018-02-26 14:46:45 +08:00
// timeComma
// 08:20:13,787
// timeWs
// 05:24:37 PM
// 06:20:00 UTC
// 00:12:00 +0000 UTC
// 22:18:00 +0000 UTC m=+0.000000001
2018-02-26 14:46:45 +08:00
// 15:04:05 -0700
// 15:04:05 -07:00
2018-03-02 10:36:46 +08:00
// 15:04:05 2008
2018-02-26 14:46:45 +08:00
// timeOffset
// 03:21:51+00:00
2018-02-28 12:05:14 +08:00
// 19:55:00+0100
2018-02-26 14:46:45 +08:00
// timePeriod
// 17:24:37.3186369
// 00:07:31.945167
// 18:31:59.257000000
// 00:00:00.000
// timePeriodOffset
2018-02-28 12:05:14 +08:00
// 19:55:00.799+0100
// timePeriodOffsetColon
// 15:04:05.999-07:00
// timePeriodWs
// timePeriodWsOffset
// 00:07:31.945167 +0000
// 00:00:00.000 +0000
// timePeriodWsOffsetAlpha
2018-02-26 14:46:45 +08:00
// 00:07:31.945167 +0000 UTC
// 22:18:00.001 +0000 UTC m=+0.000000001
2018-02-26 14:46:45 +08:00
// 00:00:00.000 +0000 UTC
2018-02-28 12:05:14 +08:00
// timePeriodWsAlpha
// 06:20:00.000 UTC
2018-02-26 14:46:45 +08:00
if p.houri == 0 {
p.houri = i
}
switch r {
case ',':
2018-03-11 05:05:55 +08:00
// hm, lets just swap out comma for period. for some reason go
// won't parse it.
// 2014-05-11 08:20:13,787
ds := []byte(p.datestr)
ds[i] = '.'
return parseTime(string(ds), loc)
2018-02-26 14:46:45 +08:00
case '-', '+':
// 03:21:51+00:00
p.stateTime = timeOffset
2018-04-06 21:57:28 +08:00
if p.seci == 0 {
// 22:18+0530
p.minlen = i - p.mini
} else {
p.seclen = i - p.seci
}
2018-02-26 14:46:45 +08:00
p.offseti = i
case '.':
p.stateTime = timePeriod
p.seclen = i - p.seci
p.msi = i + 1
case 'Z':
p.stateTime = timeZ
if p.seci == 0 {
p.minlen = i - p.mini
} else {
p.seclen = i - p.seci
}
case ' ':
p.coalesceTime(i)
2018-02-26 14:46:45 +08:00
p.stateTime = timeWs
case ':':
if p.mini == 0 {
p.mini = i + 1
p.hourlen = i - p.houri
} else if p.seci == 0 {
p.seci = i + 1
p.minlen = i - p.mini
}
2018-02-26 08:35:33 +08:00
}
2018-02-26 14:46:45 +08:00
case timeOffset:
2018-02-28 12:05:14 +08:00
// 19:55:00+0100
2018-02-26 14:46:45 +08:00
// timeOffsetColon
2018-02-28 12:05:14 +08:00
// 15:04:05+07:00
// 15:04:05-07:00
2018-02-26 14:46:45 +08:00
if r == ':' {
p.stateTime = timeOffsetColon
2018-02-26 08:35:33 +08:00
}
2018-02-26 14:46:45 +08:00
case timeWs:
2018-03-02 10:36:46 +08:00
// timeWsAlpha
2018-02-26 14:46:45 +08:00
// 06:20:00 UTC
2018-03-02 10:36:46 +08:00
// 15:44:11 UTC+0100 2015
// 18:04:07 GMT+0100 (GMT Daylight Time)
2018-03-12 08:39:59 +08:00
// 17:57:51 MST 2009
2018-02-26 14:46:45 +08:00
// timeWsAMPMMaybe
// 05:24:37 PM
// timeWsOffset
// 15:04:05 -0700
2018-03-12 08:39:59 +08:00
// 00:12:00 +0000 UTC
2018-02-26 14:46:45 +08:00
// timeWsOffsetColon
// 15:04:05 -07:00
2018-03-12 05:08:21 +08:00
// 17:57:51 -0700 2009
2018-02-26 14:46:45 +08:00
// timeWsOffsetColonAlpha
// 00:12:00 +00:00 UTC
2018-03-02 10:36:46 +08:00
// timeWsYear
// 00:12:00 2008
2018-02-26 14:46:45 +08:00
// timeZ
// 15:04:05.99Z
2018-02-26 08:35:33 +08:00
switch r {
2018-02-26 14:46:45 +08:00
case 'A', 'P':
// Could be AM/PM or could be PST or similar
2018-03-02 10:36:46 +08:00
p.tzi = i
2018-02-26 14:46:45 +08:00
p.stateTime = timeWsAMPMMaybe
case '+', '-':
2018-02-28 12:05:14 +08:00
p.offseti = i
2018-02-26 14:46:45 +08:00
p.stateTime = timeWsOffset
default:
if unicode.IsLetter(r) {
// 06:20:00 UTC
2018-03-02 10:36:46 +08:00
// 15:44:11 UTC+0100 2015
2018-03-12 08:39:59 +08:00
// 17:57:51 MST 2009
2018-03-02 10:36:46 +08:00
p.tzi = i
2018-02-26 14:46:45 +08:00
p.stateTime = timeWsAlpha
2018-03-02 10:36:46 +08:00
//break iterTimeRunes
} else if unicode.IsDigit(r) {
// 00:12:00 2008
p.stateTime = timeWsYear
p.yeari = i
2018-02-26 14:46:45 +08:00
}
2018-02-26 08:35:33 +08:00
}
2018-03-02 10:36:46 +08:00
case timeWsAlpha:
// 06:20:00 UTC
2018-03-12 08:39:59 +08:00
// timeWsAlphaWs
// 17:57:51 MST 2009
2018-03-02 10:36:46 +08:00
// timeWsAlphaZoneOffset
// timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015
switch r {
case '+', '-':
p.tzlen = i - p.tzi
if p.tzlen == 4 {
p.set(p.tzi, " MST")
2018-03-02 10:36:46 +08:00
} else if p.tzlen == 3 {
p.set(p.tzi, "MST")
}
p.stateTime = timeWsAlphaZoneOffset
p.offseti = i
2018-03-12 08:39:59 +08:00
case ' ':
// 17:57:51 MST 2009
p.stateTime = timeWsAlphaWs
p.yeari = i + 1
2018-03-02 10:36:46 +08:00
}
2018-03-12 08:39:59 +08:00
case timeWsAlphaWs:
// 17:57:51 MST 2009
2018-03-02 10:36:46 +08:00
case timeWsAlphaZoneOffset:
// timeWsAlphaZoneOffset
// timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015
switch r {
case ' ':
p.set(p.offseti, "-0700")
p.yeari = i + 1
p.stateTime = timeWsAlphaZoneOffsetWs
}
case timeWsAlphaZoneOffsetWs:
// timeWsAlphaZoneOffsetWs
// timeWsAlphaZoneOffsetWsExtra
// 18:04:07 GMT+0100 (GMT Daylight Time)
// timeWsAlphaZoneOffsetWsYear
// 15:44:11 UTC+0100 2015
if unicode.IsDigit(r) {
p.stateTime = timeWsAlphaZoneOffsetWsYear
} else {
p.extra = i - 1
p.stateTime = timeWsAlphaZoneOffsetWsExtra
}
case timeWsAlphaZoneOffsetWsYear:
// 15:44:11 UTC+0100 2015
if unicode.IsDigit(r) {
p.yearlen = i - p.yeari + 1
if p.yearlen == 4 {
p.setYear()
}
}
2018-02-26 14:46:45 +08:00
case timeWsAMPMMaybe:
// timeWsAMPMMaybe
// timeWsAMPM
// 05:24:37 PM
// timeWsAlpha
// 00:12:00 PST
2018-03-02 10:36:46 +08:00
// 15:44:11 UTC+0100 2015
2018-02-26 14:46:45 +08:00
if r == 'M' {
//return parse("2006-01-02 03:04:05 PM", datestr, loc)
p.stateTime = timeWsAMPM
2018-02-28 12:05:14 +08:00
p.set(i-1, "PM")
if p.hourlen == 2 {
p.set(p.houri, "03")
} else if p.hourlen == 1 {
p.set(p.houri, "3")
}
2018-02-26 14:46:45 +08:00
} else {
p.stateTime = timeWsAlpha
}
case timeWsOffset:
// timeWsOffset
// 15:04:05 -0700
2018-03-12 05:08:21 +08:00
// timeWsOffsetWs
// 17:57:51 -0700 2009
2018-03-12 08:39:59 +08:00
// 00:12:00 +0000 UTC
2018-02-26 14:46:45 +08:00
// timeWsOffsetColon
// 15:04:05 -07:00
// timeWsOffsetColonAlpha
// 00:12:00 +00:00 UTC
2018-02-28 12:05:14 +08:00
switch r {
case ':':
2018-02-26 14:46:45 +08:00
p.stateTime = timeWsOffsetColon
2018-02-28 12:05:14 +08:00
case ' ':
p.set(p.offseti, "-0700")
2018-03-12 08:39:59 +08:00
p.yeari = i + 1
2018-03-12 05:08:21 +08:00
p.stateTime = timeWsOffsetWs
2018-02-26 08:35:33 +08:00
}
2018-03-12 05:08:21 +08:00
case timeWsOffsetWs:
2018-03-12 08:39:59 +08:00
// 17:57:51 -0700 2009
// 00:12:00 +0000 UTC
// 22:18:00.001 +0000 UTC m=+0.000000001
switch {
case unicode.IsDigit(r):
2018-03-12 08:39:59 +08:00
p.yearlen = i - p.yeari + 1
if p.yearlen == 4 {
p.setYear()
}
case unicode.IsLetter(r):
2018-03-12 08:39:59 +08:00
if p.tzi == 0 {
p.tzi = i
}
case r == '=':
// eff you golang
if datestr[i-1] == 'm' {
p.extra = i - 2
p.trimExtra()
break
}
2018-03-12 08:39:59 +08:00
}
2018-02-26 14:46:45 +08:00
case timeWsOffsetColon:
// timeWsOffsetColon
// 15:04:05 -07:00
// timeWsOffsetColonAlpha
// 2015-02-18 00:12:00 +00:00 UTC
if unicode.IsLetter(r) {
// 2015-02-18 00:12:00 +00:00 UTC
p.stateTime = timeWsOffsetColonAlpha
break iterTimeRunes
}
2018-02-26 08:35:33 +08:00
2018-02-26 14:46:45 +08:00
case timePeriod:
// 15:04:05.999999999+07:00
// 15:04:05.999999999-07:00
// 15:04:05.999999+07:00
// 15:04:05.999999-07:00
// 15:04:05.999+07:00
// 15:04:05.999-07:00
// timePeriod
2018-02-28 12:05:14 +08:00
// 17:24:37.3186369
// 00:07:31.945167
// 18:31:59.257000000
// 00:00:00.000
// timePeriodOffset
// 19:55:00.799+0100
// timePeriodOffsetColon
// 15:04:05.999-07:00
// timePeriodWs
// timePeriodWsOffset
// 00:07:31.945167 +0000
// 00:00:00.000 +0000
// timePeriodWsOffsetAlpha
// 00:07:31.945167 +0000 UTC
// 00:00:00.000 +0000 UTC
// 22:18:00.001 +0000 UTC m=+0.000000001
2018-02-28 12:05:14 +08:00
// timePeriodWsAlpha
// 06:20:00.000 UTC
2018-02-26 14:46:45 +08:00
switch r {
case ' ':
p.mslen = i - p.msi
p.stateTime = timePeriodWs
case '+', '-':
2018-02-28 12:05:14 +08:00
// This really shouldn't happen
2018-02-26 14:46:45 +08:00
p.mslen = i - p.msi
2018-02-28 12:05:14 +08:00
p.offseti = i
2018-02-26 14:46:45 +08:00
p.stateTime = timePeriodOffset
default:
if unicode.IsLetter(r) {
// 06:20:00.000 UTC
p.mslen = i - p.msi
2018-02-28 12:05:14 +08:00
p.stateTime = timePeriodWsAlpha
2018-02-26 14:46:45 +08:00
}
}
2018-02-28 12:05:14 +08:00
case timePeriodOffset:
// timePeriodOffset
// 19:55:00.799+0100
// timePeriodOffsetColon
// 15:04:05.999-07:00
2018-03-12 08:39:59 +08:00
// 13:31:51.999-07:00 MST
if r == ':' {
2018-02-28 12:05:14 +08:00
p.stateTime = timePeriodOffsetColon
}
case timePeriodOffsetColon:
// timePeriodOffset
// timePeriodOffsetColon
// 15:04:05.999-07:00
2018-03-12 08:39:59 +08:00
// 13:31:51.999 -07:00 MST
switch r {
case ' ':
p.set(p.offseti, "-07:00")
p.stateTime = timePeriodOffsetColonWs
p.tzi = i + 1
}
case timePeriodOffsetColonWs:
// continue
2018-02-26 14:46:45 +08:00
case timePeriodWs:
2018-02-28 12:05:14 +08:00
// timePeriodWs
// timePeriodWsOffset
// 00:07:31.945167 +0000
// 00:00:00.000 +0000
// timePeriodWsOffsetAlpha
// 00:07:31.945167 +0000 UTC
// 00:00:00.000 +0000 UTC
2018-03-12 08:39:59 +08:00
// timePeriodWsOffsetColon
// 13:31:51.999 -07:00 MST
2018-02-28 12:05:14 +08:00
// timePeriodWsAlpha
// 06:20:00.000 UTC
if p.offseti == 0 {
p.offseti = i
2018-02-26 14:46:45 +08:00
}
2018-02-28 12:05:14 +08:00
switch r {
case '+', '-':
p.mslen = i - p.msi - 1
p.stateTime = timePeriodWsOffset
default:
if unicode.IsLetter(r) {
// 00:07:31.945167 +0000 UTC
// 00:00:00.000 +0000 UTC
p.stateTime = timePeriodWsOffsetWsAlpha
break iterTimeRunes
}
}
2018-02-26 14:46:45 +08:00
case timePeriodWsOffset:
2018-02-28 12:05:14 +08:00
// timePeriodWs
// timePeriodWsOffset
// 00:07:31.945167 +0000
// 00:00:00.000 +0000
// timePeriodWsOffsetAlpha
// 00:07:31.945167 +0000 UTC
// 00:00:00.000 +0000 UTC
// 03:02:00.001 +0300 MSK m=+0.000000001
2018-03-12 08:39:59 +08:00
// timePeriodWsOffsetColon
// 13:31:51.999 -07:00 MST
2018-02-28 12:05:14 +08:00
// timePeriodWsAlpha
// 06:20:00.000 UTC
switch r {
2018-03-12 08:39:59 +08:00
case ':':
p.stateTime = timePeriodWsOffsetColon
2018-02-28 12:05:14 +08:00
case ' ':
p.set(p.offseti, "-0700")
default:
if unicode.IsLetter(r) {
// 00:07:31.945167 +0000 UTC
// 00:00:00.000 +0000 UTC
// 03:02:00.001 +0300 MSK m=+0.000000001
2018-02-28 12:05:14 +08:00
p.stateTime = timePeriodWsOffsetWsAlpha
}
2018-02-26 14:46:45 +08:00
}
case timePeriodWsOffsetWsAlpha:
// 03:02:00.001 +0300 MSK m=+0.000000001
// eff you golang
if r == '=' && datestr[i-1] == 'm' {
p.extra = i - 2
p.trimExtra()
break
}
2018-02-28 12:05:14 +08:00
2018-03-12 08:39:59 +08:00
case timePeriodWsOffsetColon:
// 13:31:51.999 -07:00 MST
switch r {
case ' ':
p.set(p.offseti, "-07:00")
default:
if unicode.IsLetter(r) {
// 13:31:51.999 -07:00 MST
p.tzi = i
p.stateTime = timePeriodWsOffsetColonAlpha
}
}
case timePeriodWsOffsetColonAlpha:
// continue
2018-02-26 14:46:45 +08:00
case timeZ:
// timeZ
// 15:04:05.99Z
// With a time-zone at end after Z
// 2006-01-02T15:04:05.999999999Z07:00
// 2006-01-02T15:04:05Z07:00
// RFC3339 = "2006-01-02T15:04:05Z07:00"
// RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
if unicode.IsDigit(r) {
p.stateTime = timeZDigit
}
2018-02-26 08:35:33 +08:00
}
2018-02-26 14:46:45 +08:00
}
2018-02-26 08:35:33 +08:00
2018-02-26 14:46:45 +08:00
switch p.stateTime {
2018-03-12 08:39:59 +08:00
case timeWsAlphaWs:
p.yearlen = i - p.yeari
p.setYear()
2018-03-02 10:36:46 +08:00
case timeWsYear:
p.yearlen = i - p.yeari
p.setYear()
case timeWsAlphaZoneOffsetWsExtra:
p.trimExtra()
2018-02-26 08:35:33 +08:00
case timePeriod:
2018-02-26 14:46:45 +08:00
p.mslen = i - p.msi
case timeOffset:
2018-02-28 12:05:14 +08:00
// 19:55:00+0100
2018-03-02 10:36:46 +08:00
p.set(p.offseti, "-0700")
case timeWsOffset:
2018-02-28 12:05:14 +08:00
p.set(p.offseti, "-0700")
2018-03-12 05:08:21 +08:00
case timeWsOffsetWs:
2018-03-12 08:39:59 +08:00
// 17:57:51 -0700 2009
// 00:12:00 +0000 UTC
case timeWsOffsetColon:
// 17:57:51 -07:00
p.set(p.offseti, "-07:00")
2018-02-26 14:46:45 +08:00
case timeOffsetColon:
// 15:04:05+07:00
p.set(p.offseti, "-07:00")
2018-02-28 12:05:14 +08:00
case timePeriodOffset:
// 19:55:00.799+0100
p.set(p.offseti, "-0700")
case timePeriodOffsetColon:
p.set(p.offseti, "-07:00")
2018-03-12 08:39:59 +08:00
case timePeriodWsOffsetColonAlpha:
p.tzlen = i - p.tzi
switch p.tzlen {
case 3:
p.set(p.tzi, "MST")
case 4:
p.set(p.tzi, "MST ")
}
2018-02-28 12:05:14 +08:00
case timePeriodWsOffset:
p.set(p.offseti, "-0700")
2018-02-26 08:35:33 +08:00
}
2018-02-28 12:05:14 +08:00
p.coalesceTime(i)
2018-02-26 08:35:33 +08:00
}
2018-02-26 14:46:45 +08:00
switch p.stateDate {
2018-02-26 08:35:33 +08:00
case dateDigit:
2014-05-07 12:15:43 +08:00
// unixy timestamps ish
2017-07-14 10:57:15 +08:00
// 1499979655583057426 nanoseconds
// 1499979795437000 micro-seconds
// 1499979795437 milliseconds
2015-08-19 06:50:04 +08:00
// 1384216367189
// 1332151919 seconds
// 20140601 yyyymmdd
2016-03-02 05:25:00 +08:00
// 2014 yyyy
t := time.Time{}
2017-07-14 10:57:15 +08:00
if len(datestr) > len("1499979795437000") {
2014-04-26 07:59:10 +08:00
if nanoSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
t = time.Unix(0, nanoSecs)
2014-04-26 07:59:10 +08:00
}
2017-07-14 10:57:15 +08:00
} else if len(datestr) > len("1499979795437") {
2014-05-07 12:15:43 +08:00
if microSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
t = time.Unix(0, microSecs*1000)
2014-05-07 12:15:43 +08:00
}
2017-07-14 10:57:15 +08:00
} else if len(datestr) > len("1332151919") {
2014-10-08 09:30:17 +08:00
if miliSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
t = time.Unix(0, miliSecs*1000*1000)
2014-10-08 09:30:17 +08:00
}
2015-08-19 06:50:04 +08:00
} else if len(datestr) == len("20140601") {
2018-03-25 06:56:26 +08:00
p.format = []byte("20060102")
return p, nil
2016-03-02 05:25:00 +08:00
} else if len(datestr) == len("2014") {
2018-03-25 06:56:26 +08:00
p.format = []byte("2006")
return p, nil
} else if len(datestr) < 4 {
return nil, fmt.Errorf("unrecognized format, to short %v", datestr)
2014-04-26 07:59:10 +08:00
}
if t.IsZero() {
if secs, err := strconv.ParseInt(datestr, 10, 64); err == nil {
2018-03-26 07:48:11 +08:00
if secs > 0 {
2017-10-08 00:34:59 +08:00
// Now, for unix-seconds we aren't going to guess a lot
// nothing before unix-epoch
t = time.Unix(secs, 0)
2018-03-25 06:56:26 +08:00
p.t = &t
2017-10-08 00:34:59 +08:00
}
}
}
if !t.IsZero() {
if loc == nil {
2018-03-25 06:56:26 +08:00
p.t = &t
return p, nil
}
2018-03-25 06:56:26 +08:00
t = t.In(loc)
p.t = &t
return p, nil
2017-08-13 00:48:28 +08:00
}
2018-02-28 12:05:14 +08:00
case dateDigitDash:
// 2006-01
2018-03-25 06:56:26 +08:00
return p, nil
2018-02-28 12:05:14 +08:00
2018-02-26 14:46:45 +08:00
case dateDigitDashDash:
2014-05-07 12:15:43 +08:00
// 2006-01-02
2018-02-08 23:03:45 +08:00
// 2006-1-02
// 2006-1-2
// 2006-01-2
2018-03-25 06:56:26 +08:00
return p, nil
2018-02-08 23:03:45 +08:00
2018-02-26 14:46:45 +08:00
case dateDigitDashDashAlpha:
2017-07-27 07:55:21 +08:00
// 2013-Feb-03
2018-02-08 23:03:45 +08:00
// 2013-Feb-3
2018-02-26 14:46:45 +08:00
p.daylen = i - p.dayi
p.setDay()
2018-03-25 06:56:26 +08:00
return p, nil
2018-02-26 14:46:45 +08:00
case dateDigitDashDashWs: // starts digit then dash 02- then whitespace 1 << 2 << 5 + 3
// 2013-04-01 22:43:22
// 2013-04-01 22:43
2018-03-25 06:56:26 +08:00
return p, nil
2018-02-26 14:46:45 +08:00
case dateDigitDashDashT:
2018-03-25 06:56:26 +08:00
return p, nil
2017-07-27 07:55:21 +08:00
2018-05-04 09:06:52 +08:00
case dateDigitDot:
// 2014.05
p.molen = i - p.moi
p.setMonth()
return p, nil
2018-02-26 08:35:33 +08:00
case dateDigitDotDot:
2018-02-28 12:05:14 +08:00
// 03.31.1981
2018-05-04 09:06:52 +08:00
// 3.31.2014
2018-02-28 12:05:14 +08:00
// 3.2.1981
// 3.2.81
2018-05-04 09:06:52 +08:00
// 08.21.71
2018-03-02 10:36:46 +08:00
p.setYear()
2018-02-28 12:05:14 +08:00
p.yearlen = i - p.yeari
2018-03-25 06:56:26 +08:00
return p, nil
2018-01-12 05:21:04 +08:00
2018-02-28 12:05:14 +08:00
case dateDigitWsMoYear:
2018-03-11 05:43:16 +08:00
// 2 Jan 2018
// 2 Jan 18
// 2 Jan 2018 23:59
// 02 Jan 2018 23:59
// 02 Jan 2018 23:59:45
2018-01-25 09:59:25 +08:00
// 12 Feb 2006, 19:17
// 12 Feb 2006, 19:17:22
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-11 05:05:55 +08:00
2018-03-02 10:36:46 +08:00
case dateDigitWsMolong:
// 18 January 2018
// 8 January 2018
if p.daylen == 2 {
2018-03-25 06:56:26 +08:00
p.format = []byte("02 January 2006")
return p, nil
2018-03-02 10:36:46 +08:00
}
2018-03-25 06:56:26 +08:00
p.format = []byte("2 January 2006")
return p, nil // parse("2 January 2006", datestr, loc)
2018-02-08 23:03:45 +08:00
2018-03-11 05:43:16 +08:00
case dateAlphaWsDigitCommaWs:
// oct 1, 1970
p.yearlen = i - p.yeari
p.setYear()
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-11 05:43:16 +08:00
2018-03-02 10:36:46 +08:00
case dateAlphaWsDigitCommaWsYear:
2018-02-26 08:35:33 +08:00
// May 8, 2009 5:57:51 PM
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-02 10:36:46 +08:00
case dateAlphaWsAlpha:
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-02 10:36:46 +08:00
case dateAlphaWsAlphaYearmaybe:
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-02 10:36:46 +08:00
2018-03-12 08:39:59 +08:00
case dateDigitSlash:
2014-05-07 12:15:43 +08:00
// 3/1/2014
// 10/13/2014
// 01/02/2006
2014-07-11 06:25:23 +08:00
// 2014/10/13
2018-03-25 06:56:26 +08:00
return p, nil
2014-05-07 12:15:43 +08:00
2018-02-26 08:35:33 +08:00
case dateDigitChineseYear:
// dateDigitChineseYear
2017-11-19 03:07:33 +08:00
// 2014年04月08日
2018-03-25 06:56:26 +08:00
p.format = []byte("2006年01月02日")
return p, nil
2018-03-02 10:36:46 +08:00
2018-02-26 08:35:33 +08:00
case dateDigitChineseYearWs:
2018-03-25 06:56:26 +08:00
p.format = []byte("2006年01月02日 15:04:05")
return p, nil
2018-03-02 10:36:46 +08:00
case dateWeekdayComma:
2017-07-14 00:11:41 +08:00
// Monday, 02 Jan 2006 15:04:05 -0700
// Monday, 02 Jan 2006 15:04:05 +0100
// Monday, 02-Jan-06 15:04:05 MST
2018-03-25 06:56:26 +08:00
return p, nil
2018-03-02 10:36:46 +08:00
2018-03-12 08:39:59 +08:00
case dateWeekdayAbbrevComma:
2017-07-14 00:11:41 +08:00
// Mon, 02-Jan-06 15:04:05 MST
// Mon, 02 Jan 2006 15:04:05 MST
2018-03-25 06:56:26 +08:00
return p, nil
2018-02-08 23:03:45 +08:00
2014-04-21 10:56:17 +08:00
}
2018-03-25 06:56:26 +08:00
return nil, fmt.Errorf("Could not find date format for %s", datestr)
2014-04-21 10:56:17 +08:00
}