1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- // Copyright 2010 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package time
- import "errors"
- // These are predefined layouts for use in Time.Format and Time.Parse.
- // The reference time used in the layouts is the specific time:
- // Mon Jan 2 15:04:05 MST 2006
- // which is Unix time 1136239445. Since MST is GMT-0700,
- // the reference time can be thought of as
- // 01/02 03:04:05PM '06 -0700
- // To define your own format, write down what the reference time would look
- // like formatted your way; see the values of constants like ANSIC,
- // StampMicro or Kitchen for examples. The model is to demonstrate what the
- // reference time looks like so that the Format and Parse methods can apply
- // the same transformation to a general time value.
- //
- // Within the format string, an underscore _ represents a space that may be
- // replaced by a digit if the following number (a day) has two digits; for
- // compatibility with fixed-width Unix time formats.
- //
- // A decimal point followed by one or more zeros represents a fractional
- // second, printed to the given number of decimal places. A decimal point
- // followed by one or more nines represents a fractional second, printed to
- // the given number of decimal places, with trailing zeros removed.
- // When parsing (only), the input may contain a fractional second
- // field immediately after the seconds field, even if the layout does not
- // signify its presence. In that case a decimal point followed by a maximal
- // series of digits is parsed as a fractional second.
- //
- // Numeric time zone offsets format as follows:
- // -0700 ±hhmm
- // -07:00 ±hh:mm
- // Replacing the sign in the format with a Z triggers
- // the ISO 8601 behavior of printing Z instead of an
- // offset for the UTC zone. Thus:
- // Z0700 Z or ±hhmm
- // Z07:00 Z or ±hh:mm
- const (
- 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" // RFC822 with numeric zone
- 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" // RFC1123 with numeric zone
- RFC3339 = "2006-01-02T15:04:05Z07:00"
- RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
- Kitchen = "3:04PM"
- // Handy time stamps.
- 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"
- )
- const (
- _ = iota
- stdLongMonth = iota + stdNeedDate // "January"
- stdMonth // "Jan"
- stdNumMonth // "1"
- stdZeroMonth // "01"
- stdLongWeekDay // "Monday"
- stdWeekDay // "Mon"
- stdDay // "2"
- stdUnderDay // "_2"
- stdZeroDay // "02"
- stdHour = iota + stdNeedClock // "15"
- stdHour12 // "3"
- stdZeroHour12 // "03"
- stdMinute // "4"
- stdZeroMinute // "04"
- stdSecond // "5"
- stdZeroSecond // "05"
- stdLongYear = iota + stdNeedDate // "2006"
- stdYear // "06"
- stdPM = iota + stdNeedClock // "PM"
- stdpm // "pm"
- stdTZ = iota // "MST"
- stdISO8601TZ // "Z0700" // prints Z for UTC
- stdISO8601SecondsTZ // "Z070000"
- stdISO8601ColonTZ // "Z07:00" // prints Z for UTC
- stdISO8601ColonSecondsTZ // "Z07:00:00"
- stdNumTZ // "-0700" // always numeric
- stdNumSecondsTz // "-070000"
- stdNumShortTZ // "-07" // always numeric
- stdNumColonTZ // "-07:00" // always numeric
- stdNumColonSecondsTZ // "-07:00:00"
- stdFracSecond0 // ".0", ".00", ... , trailing zeros included
- stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
- stdNeedDate = 1 << 8 // need month, day, year
- stdNeedClock = 2 << 8 // need hour, minute, second
- stdArgShift = 16 // extra argument in high bits, above low stdArgShift
- stdMask = 1<<stdArgShift - 1 // mask out argument
- )
- // std0x records the std values for "01", "02", ..., "06".
- var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
- // startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
- // Its purpose is to prevent matching strings like "Month" when looking for "Mon".
- func startsWithLowerCase(str string) bool {
- if len(str) == 0 {
- return false
- }
- c := str[0]
- return 'a' <= c && c <= 'z'
- }
- // nextStdChunk finds the first occurrence of a std string in
- // layout and returns the text before, the std string, and the text after.
- 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': // January, Jan
- 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': // Monday, Mon, MST
- 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': // 01, 02, 03, 04, 05, 06
- 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:]
- }
- case '1': // 15, 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': // 2006, 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 '_': // _2
- if len(layout) >= i+2 && layout[i+1] == '2' {
- return layout[0:i], stdUnderDay, layout[i+2:]
- }
- 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': // PM
- if len(layout) >= i+2 && layout[i+1] == 'M' {
- return layout[0:i], stdPM, layout[i+2:]
- }
- case 'p': // pm
- if len(layout) >= i+2 && layout[i+1] == 'm' {
- return layout[0:i], stdpm, layout[i+2:]
- }
- case '-': // -070000, -07:00:00, -0700, -07:00, -07
- 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': // Z070000, Z07:00:00, Z0700, Z07:00,
- 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:]
- }
- case '.': // .000 or .999 - repeated digits for fractional seconds.
- 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++
- }
- // String of digits must end here - only fractional second is all digits.
- if !isDigit(layout, j) {
- std := stdFracSecond0
- if layout[i+1] == '9' {
- std = stdFracSecond9
- }
- std |= (j - (i + 1)) << stdArgShift
- 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",
- }
- // match returns true if s1 and s2 match ignoring case.
- // It is assumed s1 and s2 are the same length.
- func match(s1, s2 string) bool {
- for i := 0; i < len(s1); i++ {
- c1 := s1[i]
- c2 := s2[i]
- if c1 != c2 {
- // Switch to lower-case; 'a'-'A' is known to be a single bit.
- 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[0:len(v)], v) {
- return i, val[len(v):], nil
- }
- }
- return -1, val, errBad
- }
- // appendUint appends the decimal form of x to b and returns the result.
- // If x is a single-digit number and pad != 0, appendUint inserts the pad byte
- // before the digit.
- // Duplicates functionality in strconv, but avoids dependency.
- func appendUint(b []byte, x uint, pad byte) []byte {
- if x < 10 {
- if pad != 0 {
- b = append(b, pad)
- }
- return append(b, byte('0'+x))
- }
- if x < 100 {
- b = append(b, byte('0'+x/10))
- b = append(b, byte('0'+x%10))
- return b
- }
- var buf [32]byte
- n := len(buf)
- if x == 0 {
- return append(b, '0')
- }
- for x >= 10 {
- n--
- buf[n] = byte(x%10 + '0')
- x /= 10
- }
- n--
- buf[n] = byte(x + '0')
- return append(b, buf[n:]...)
- }
- // Never printed, just needs to be non-nil for return by atoi.
- var atoiError = errors.New("time: invalid number")
- // Duplicates functionality in strconv, but avoids dependency.
- func atoi(s string) (x int, err error) {
- neg := false
- if s != "" && (s[0] == '-' || s[0] == '+') {
- neg = s[0] == '-'
- s = s[1:]
- }
- q, rem, err := leadingInt(s)
- x = int(q)
- if err != nil || rem != "" {
- return 0, atoiError
- }
- if neg {
- x = -x
- }
- return x, nil
- }
- // formatNano appends a fractional second, as nanoseconds, to b
- // and returns the result.
- func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
- u := nanosec
- var buf [9]byte
- for start := len(buf); start > 0; {
- start--
- buf[start] = byte(u%10 + '0')
- u /= 10
- }
- if n > 9 {
- n = 9
- }
- if trim {
- for n > 0 && buf[n-1] == '0' {
- n--
- }
- if n == 0 {
- return b
- }
- }
- b = append(b, '.')
- return append(b, buf[:n]...)
- }
- // String returns the time formatted using the format string
- // "2006-01-02 15:04:05.999999999 -0700 MST"
- func (t Time) String() string {
- return t.Format("2006-01-02 15:04:05.999999999 -0700 MST")
- }
- // Format returns a textual representation of the time value formatted
- // according to layout, which defines the format by showing how the reference
- // time, defined to be
- // Mon Jan 2 15:04:05 -0700 MST 2006
- // would be displayed if it were the value; it serves as an example of the
- // desired output. The same display rules will then be applied to the time
- // value.
- // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
- // and convenient representations of the reference time. For more information
- // about the formats and the definition of the reference time, see the
- // documentation for ANSIC and the other constants defined by this package.
- func (t Time) Format(layout string) string {
- var (
- name, offset, abs = t.locabs()
- year int = -1
- month Month
- day int
- hour int = -1
- min int
- sec int
- b []byte
- buf [64]byte
- )
- max := len(layout) + 10
- if max <= len(buf) {
- b = buf[:0]
- } else {
- b = make([]byte, 0, max)
- }
- // Each iteration generates one std value.
- for layout != "" {
- prefix, std, suffix := nextStdChunk(layout)
- if prefix != "" {
- b = append(b, prefix...)
- }
- if std == 0 {
- break
- }
- layout = suffix
- // Compute year, month, day if needed.
- if year < 0 && std&stdNeedDate != 0 {
- year, month, day, _ = absDate(abs, true)
- }
- // Compute hour, minute, second if needed.
- if hour < 0 && std&stdNeedClock != 0 {
- hour, min, sec = absClock(abs)
- }
- switch std & stdMask {
- case stdYear:
- y := year
- if y < 0 {
- y = -y
- }
- b = appendUint(b, uint(y%100), '0')
- case stdLongYear:
- // Pad year to at least 4 digits.
- y := year
- switch {
- case year <= -1000:
- b = append(b, '-')
- y = -y
- case year <= -100:
- b = append(b, "-0"...)
- y = -y
- case year <= -10:
- b = append(b, "-00"...)
- y = -y
- case year < 0:
- b = append(b, "-000"...)
- y = -y
- case year < 10:
- b = append(b, "000"...)
- case year < 100:
- b = append(b, "00"...)
- case year < 1000:
- b = append(b, '0')
- }
- b = appendUint(b, uint(y), 0)
- case stdMonth:
- b = append(b, month.String()[:3]...)
- case stdLongMonth:
- m := month.String()
- b = append(b, m...)
- case stdNumMonth:
- b = appendUint(b, uint(month), 0)
- case stdZeroMonth:
- b = appendUint(b, uint(month), '0')
- case stdWeekDay:
- b = append(b, absWeekday(abs).String()[:3]...)
- case stdLongWeekDay:
- s := absWeekday(abs).String()
- b = append(b, s...)
- case stdDay:
- b = appendUint(b, uint(day), 0)
- case stdUnderDay:
- b = appendUint(b, uint(day), ' ')
- case stdZeroDay:
- b = appendUint(b, uint(day), '0')
- case stdHour:
- b = appendUint(b, uint(hour), '0')
- case stdHour12:
- // Noon is 12PM, midnight is 12AM.
- hr := hour % 12
- if hr == 0 {
- hr = 12
- }
- b = appendUint(b, uint(hr), 0)
- case stdZeroHour12:
- // Noon is 12PM, midnight is 12AM.
- hr := hour % 12
- if hr == 0 {
- hr = 12
- }
- b = appendUint(b, uint(hr), '0')
- case stdMinute:
- b = appendUint(b, uint(min), 0)
- case stdZeroMinute:
- b = appendUint(b, uint(min), '0')
- case stdSecond:
- b = appendUint(b, uint(sec), 0)
- case stdZeroSecond:
- b = appendUint(b, uint(sec), '0')
- 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, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
- // Ugly special case. We cheat and take the "Z" variants
- // to mean "the time zone as formatted for ISO 8601".
- if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) {
- b = append(b, 'Z')
- break
- }
- zone := offset / 60 // convert to minutes
- absoffset := offset
- if zone < 0 {
- b = append(b, '-')
- zone = -zone
- absoffset = -absoffset
- } else {
- b = append(b, '+')
- }
- b = appendUint(b, uint(zone/60), '0')
- if std == stdISO8601ColonTZ || std == stdNumColonTZ || std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ {
- b = append(b, ':')
- }
- b = appendUint(b, uint(zone%60), '0')
- // append seconds if appropriate
- if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
- if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ {
- b = append(b, ':')
- }
- b = appendUint(b, uint(absoffset%60), '0')
- }
- case stdTZ:
- if name != "" {
- b = append(b, name...)
- break
- }
- // No time zone known for this time, but we must print one.
- // Use the -0700 format.
- zone := offset / 60 // convert to minutes
- if zone < 0 {
- b = append(b, '-')
- zone = -zone
- } else {
- b = append(b, '+')
- }
- b = appendUint(b, uint(zone/60), '0')
- b = appendUint(b, uint(zone%60), '0')
- case stdFracSecond0, stdFracSecond9:
- b = formatNano(b, uint(t.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
- }
- }
- return string(b)
- }
- var errBad = errors.New("bad value for field") // placeholder not passed to user
- // ParseError describes a problem parsing a time string.
- type ParseError struct {
- Layout string
- Value string
- LayoutElem string
- ValueElem string
- Message string
- }
- func quote(s string) string {
- return "\"" + s + "\""
- }
- // Error returns the string representation of a ParseError.
- 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
- }
- // isDigit returns true if s[i] is a decimal digit, false if not or
- // if s[i] is out of range.
- func isDigit(s string, i int) bool {
- if len(s) <= i {
- return false
- }
- c := s[i]
- return '0' <= c && c <= '9'
- }
- // getnum parses s[0:1] or s[0:2] (fixed forces the latter)
- // as a decimal integer and returns the integer and the
- // remainder of the string.
- 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 cutspace(s string) string {
- for len(s) > 0 && s[0] == ' ' {
- s = s[1:]
- }
- return s
- }
- // skip removes the given prefix from value,
- // treating runs of space characters as equivalent.
- 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
- }
- // Parse parses a formatted string and returns the time value it represents.
- // The layout defines the format by showing how the reference time,
- // defined to be
- // Mon Jan 2 15:04:05 -0700 MST 2006
- // would be interpreted if it were the value; it serves as an example of
- // the input format. The same interpretation will then be made to the
- // input string.
- // Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
- // and convenient representations of the reference time. For more information
- // about the formats and the definition of the reference time, see the
- // documentation for ANSIC and the other constants defined by this package.
- //
- // Elements omitted from the value are assumed to be zero or, when
- // zero is impossible, one, so parsing "3:04pm" returns the time
- // corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
- // 0, this time is before the zero Time).
- // Years must be in the range 0000..9999. The day of the week is checked
- // for syntax but it is otherwise ignored.
- //
- // In the absence of a time zone indicator, Parse returns a time in UTC.
- //
- // When parsing a time with a zone offset like -0700, if the offset corresponds
- // to a time zone used by the current location (Local), then Parse uses that
- // location and zone in the returned time. Otherwise it records the time as
- // being in a fabricated location with time fixed at the given zone offset.
- //
- // When parsing a time with a zone abbreviation like MST, if the zone abbreviation
- // has a defined offset in the current location, then that offset is used.
- // The zone abbreviation "UTC" is recognized as UTC regardless of location.
- // If the zone abbreviation is unknown, Parse records the time as being
- // in a fabricated location with the given zone abbreviation and a zero offset.
- // This choice means that such a time can be parsed and reformatted with the
- // same layout losslessly, but the exact instant used in the representation will
- // differ by the actual zone offset. To avoid such problems, prefer time layouts
- // that use a numeric zone offset, or use ParseInLocation.
- func Parse(layout, value string) (Time, error) {
- return parse(layout, value, UTC, Local)
- }
- // ParseInLocation is like Parse but differs in two important ways.
- // First, in the absence of time zone information, Parse interprets a time as UTC;
- // ParseInLocation interprets the time as in the given location.
- // Second, when given a zone offset or abbreviation, Parse tries to match it
- // against the Local location; ParseInLocation uses the given location.
- func ParseInLocation(layout, value string, loc *Location) (Time, error) {
- return parse(layout, value, loc, loc)
- }
- func parse(layout, value string, defaultLocation, local *Location) (Time, error) {
- alayout, avalue := layout, value
- rangeErrString := "" // set if a value is out of range
- amSet := false // do we need to subtract 12 from the hour for midnight?
- pmSet := false // do we need to add 12 to the hour?
- // Time being constructed.
- var (
- year int
- month int = 1 // January
- day int = 1
- hour int
- min int
- sec int
- nsec int
- z *Location
- zoneOffset int = -1
- zoneName string
- )
- // Each iteration processes one std value.
- 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{}, &ParseError{alayout, avalue, prefix, value, ""}
- }
- if std == 0 {
- if len(value) != 0 {
- return Time{}, &ParseError{alayout, avalue, "", value, ": extra text: " + value}
- }
- break
- }
- layout = suffix
- var p string
- switch std & stdMask {
- case stdYear:
- if len(value) < 2 {
- err = errBad
- break
- }
- p, value = value[0:2], value[2:]
- year, err = atoi(p)
- if year >= 69 { // Unix time starts Dec 31 1969 in some time zones
- 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)
- case stdLongMonth:
- month, value, err = lookup(longMonthNames, value)
- case stdNumMonth, stdZeroMonth:
- month, value, err = getnum(value, std == stdZeroMonth)
- if month <= 0 || 12 < month {
- rangeErrString = "month"
- }
- case stdWeekDay:
- // Ignore weekday except for error checking.
- _, 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)
- if day < 0 || 31 < day {
- rangeErrString = "day"
- }
- 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 sec < 0 || 60 <= sec {
- rangeErrString = "second"
- }
- // Special case: do we have a fractional second but no
- // fractional second in the format?
- if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) {
- _, std, _ = nextStdChunk(layout)
- std &= stdMask
- if std == stdFracSecond0 || std == stdFracSecond9 {
- // Fractional second in the layout; proceed normally
- break
- }
- // No fractional second in the layout but we have one in the input.
- 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, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ:
- if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' {
- value = value[1:]
- z = UTC
- break
- }
- 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 {
- 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 = atoi(hour)
- if err == nil {
- mm, err = atoi(min)
- }
- if err == nil {
- ss, err = atoi(seconds)
- }
- zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds
- switch sign[0] {
- case '+':
- case '-':
- zoneOffset = -zoneOffset
- default:
- err = errBad
- }
- case stdTZ:
- // Does it look like a time zone?
- 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:
- // stdFracSecond0 requires the exact number of digits as specified in
- // the layout.
- ndigit := 1 + (std >> stdArgShift)
- if len(value) < ndigit {
- err = errBad
- break
- }
- nsec, rangeErrString, err = parseNanoseconds(value, ndigit)
- value = value[ndigit:]
- case stdFracSecond9:
- if len(value) < 2 || value[0] != '.' || value[1] < '0' || '9' < value[1] {
- // Fractional second omitted.
- break
- }
- // Take any number of digits, even more than asked for,
- // because it is what the stdSecond case would do.
- i := 0
- for i < 9 && 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{}, &ParseError{alayout, avalue, stdstr, value, ": " + rangeErrString + " out of range"}
- }
- if err != nil {
- return Time{}, &ParseError{alayout, avalue, stdstr, value, ""}
- }
- }
- if pmSet && hour < 12 {
- hour += 12
- } else if amSet && hour == 12 {
- hour = 0
- }
- 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.sec -= int64(zoneOffset)
- // Look for local zone with the given offset.
- // If that zone was in effect at the given time, use it.
- name, offset, _, _, _ := local.lookup(t.sec + internalToUnix)
- if offset == zoneOffset && (zoneName == "" || name == zoneName) {
- t.loc = local
- return t, nil
- }
- // Otherwise create fake zone to record offset.
- t.loc = FixedZone(zoneName, zoneOffset)
- return t, nil
- }
- if zoneName != "" {
- t := Date(year, Month(month), day, hour, min, sec, nsec, UTC)
- // Look for local zone with the given offset.
- // If that zone was in effect at the given time, use it.
- offset, _, ok := local.lookupName(zoneName, t.sec+internalToUnix)
- if ok {
- t.sec -= int64(offset)
- t.loc = local
- return t, nil
- }
- // Otherwise, create fake zone with unknown offset.
- if len(zoneName) > 3 && zoneName[:3] == "GMT" {
- offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT.
- offset *= 3600
- }
- t.loc = FixedZone(zoneName, offset)
- return t, nil
- }
- // Otherwise, fall back to default.
- return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
- }
- // parseTimeZone parses a time zone string and returns its length. Time zones
- // are human-generated and unpredictable. We can't do precise error checking.
- // On the other hand, for a correct parse there must be a time zone at the
- // beginning of the string, so it's almost always true that there's one
- // there. We look at the beginning of the string for a run of upper-case letters.
- // If there are more than 5, it's an error.
- // If there are 4 or 5 and the last is a T, it's a time zone.
- // If there are 3, it's a time zone.
- // Otherwise, other than special cases, it's not a time zone.
- // GMT is special because it can have an hour offset.
- func parseTimeZone(value string) (length int, ok bool) {
- if len(value) < 3 {
- return 0, false
- }
- // Special case 1: ChST and MeST are the only zones with a lower-case letter.
- if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
- return 4, true
- }
- // Special case 2: GMT may have an hour offset; treat it specially.
- if value[:3] == "GMT" {
- length = parseGMT(value)
- return length, true
- }
- // How many upper-case letters are there? Need at least three, at most five.
- 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: // Must end in T to match.
- if value[4] == 'T' {
- return 5, true
- }
- case 4: // Must end in T to match.
- if value[3] == 'T' {
- return 4, true
- }
- case 3:
- return 3, true
- }
- return 0, false
- }
- // parseGMT parses a GMT time zone. The input string is known to start "GMT".
- // The function checks whether that is followed by a sign and a number in the
- // range -14 through 12 excluding zero.
- func parseGMT(value string) int {
- value = value[3:]
- if len(value) == 0 {
- return 3
- }
- sign := value[0]
- if sign != '-' && sign != '+' {
- return 3
- }
- x, rem, err := leadingInt(value[1:])
- if err != nil {
- return 3
- }
- if sign == '-' {
- x = -x
- }
- if x == 0 || x < -14 || 12 < x {
- return 3
- }
- return 3 + len(value) - len(rem)
- }
- func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) {
- if value[0] != '.' {
- err = errBad
- return
- }
- if ns, err = atoi(value[1:nbytes]); err != nil {
- return
- }
- if ns < 0 || 1e9 <= ns {
- rangeErrString = "fractional second"
- return
- }
- // We need nanoseconds, which means scaling by the number
- // of missing digits in the format, maximum length 10. If it's
- // longer than 10, we won't scale.
- scaleDigits := 10 - nbytes
- for i := 0; i < scaleDigits; i++ {
- ns *= 10
- }
- return
- }
- var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
- // leadingInt consumes the leading [0-9]* from s.
- func leadingInt(s string) (x int64, rem string, err error) {
- i := 0
- for ; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- break
- }
- if x >= (1<<63-10)/10 {
- // overflow
- return 0, "", errLeadingInt
- }
- x = x*10 + int64(c) - '0'
- }
- return x, s[i:], nil
- }
- var unitMap = map[string]float64{
- "ns": float64(Nanosecond),
- "us": float64(Microsecond),
- "µs": float64(Microsecond), // U+00B5 = micro symbol
- "μs": float64(Microsecond), // U+03BC = Greek letter mu
- "ms": float64(Millisecond),
- "s": float64(Second),
- "m": float64(Minute),
- "h": float64(Hour),
- }
- // ParseDuration parses a duration string.
- // A duration string is a possibly signed sequence of
- // decimal numbers, each with optional fraction and a unit suffix,
- // such as "300ms", "-1.5h" or "2h45m".
- // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
- func ParseDuration(s string) (Duration, error) {
- // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
- orig := s
- f := float64(0)
- neg := false
- // Consume [-+]?
- if s != "" {
- c := s[0]
- if c == '-' || c == '+' {
- neg = c == '-'
- s = s[1:]
- }
- }
- // Special case: if all that is left is "0", this is zero.
- if s == "0" {
- return 0, nil
- }
- if s == "" {
- return 0, errors.New("time: invalid duration " + orig)
- }
- for s != "" {
- g := float64(0) // this element of the sequence
- var x int64
- var err error
- // The next character must be [0-9.]
- if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) {
- return 0, errors.New("time: invalid duration " + orig)
- }
- // Consume [0-9]*
- pl := len(s)
- x, s, err = leadingInt(s)
- if err != nil {
- return 0, errors.New("time: invalid duration " + orig)
- }
- g = float64(x)
- pre := pl != len(s) // whether we consumed anything before a period
- // Consume (\.[0-9]*)?
- post := false
- if s != "" && s[0] == '.' {
- s = s[1:]
- pl := len(s)
- x, s, err = leadingInt(s)
- if err != nil {
- return 0, errors.New("time: invalid duration " + orig)
- }
- scale := 1.0
- for n := pl - len(s); n > 0; n-- {
- scale *= 10
- }
- g += float64(x) / scale
- post = pl != len(s)
- }
- if !pre && !post {
- // no digits (e.g. ".s" or "-.s")
- return 0, errors.New("time: invalid duration " + orig)
- }
- // Consume unit.
- i := 0
- for ; i < len(s); i++ {
- c := s[i]
- if c == '.' || ('0' <= c && c <= '9') {
- break
- }
- }
- if i == 0 {
- return 0, errors.New("time: missing unit in duration " + orig)
- }
- u := s[:i]
- s = s[i:]
- unit, ok := unitMap[u]
- if !ok {
- return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
- }
- f += g * unit
- }
- if neg {
- f = -f
- }
- if f < float64(-1<<63) || f > float64(1<<63-1) {
- return 0, errors.New("time: overflow parsing duration")
- }
- return Duration(f), nil
- }
|