github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/time/format_rfc3339.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package time 6 7 // RFC 3339 is the most commonly used format. 8 // 9 // It is implicitly used by the Time.(Marshal|Unmarshal)(Text|JSON) methods. 10 // Also, according to analysis on https://go.dev/issue/52746, 11 // RFC 3339 accounts for 57% of all explicitly specified time formats, 12 // with the second most popular format only being used 8% of the time. 13 // The overwhelming use of RFC 3339 compared to all other formats justifies 14 // the addition of logic to optimize formatting and parsing. 15 16 func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte { 17 _, offset, abs := t.locabs() 18 19 // Format date. 20 year, month, day, _ := absDate(abs, true) 21 b = appendInt(b, year, 4) 22 b = append(b, '-') 23 b = appendInt(b, int(month), 2) 24 b = append(b, '-') 25 b = appendInt(b, day, 2) 26 27 b = append(b, 'T') 28 29 // Format time. 30 hour, min, sec := absClock(abs) 31 b = appendInt(b, hour, 2) 32 b = append(b, ':') 33 b = appendInt(b, min, 2) 34 b = append(b, ':') 35 b = appendInt(b, sec, 2) 36 37 if nanos { 38 std := stdFracSecond(stdFracSecond9, 9, '.') 39 b = formatNano(b, uint(t.Nanosecond()), std) 40 } 41 42 if offset == 0 { 43 return append(b, 'Z') 44 } 45 46 // Format zone. 47 zone := offset / 60 // convert to minutes 48 if zone < 0 { 49 b = append(b, '-') 50 zone = -zone 51 } else { 52 b = append(b, '+') 53 } 54 b = appendInt(b, zone/60, 2) 55 b = append(b, ':') 56 b = appendInt(b, zone%60, 2) 57 return b 58 } 59 60 func parseRFC3339(s string, local *Location) (Time, bool) { 61 // parseUint parses s as an unsigned decimal integer and 62 // verifies that it is within some range. 63 // If it is invalid or out-of-range, 64 // it sets ok to false and returns the min value. 65 ok := true 66 parseUint := func(s string, min, max int) (x int) { 67 for _, c := range []byte(s) { 68 if c < '0' || '9' < c { 69 ok = false 70 return min 71 } 72 x = x*10 + int(c) - '0' 73 } 74 if x < min || max < x { 75 ok = false 76 return min 77 } 78 return x 79 } 80 81 // Parse the date and time. 82 if len(s) < len("2006-01-02T15:04:05") { 83 return Time{}, false 84 } 85 year := parseUint(s[0:4], 0, 9999) // e.g., 2006 86 month := parseUint(s[5:7], 1, 12) // e.g., 01 87 day := parseUint(s[8:10], 1, daysIn(Month(month), year)) // e.g., 02 88 hour := parseUint(s[11:13], 0, 23) // e.g., 15 89 min := parseUint(s[14:16], 0, 59) // e.g., 04 90 sec := parseUint(s[17:19], 0, 59) // e.g., 05 91 if !ok || !(s[4] == '-' && s[7] == '-' && s[10] == 'T' && s[13] == ':' && s[16] == ':') { 92 return Time{}, false 93 } 94 s = s[19:] 95 96 // Parse the fractional second. 97 var nsec int 98 if len(s) >= 2 && s[0] == '.' && isDigit(s, 1) { 99 n := 2 100 for ; n < len(s) && isDigit(s, n); n++ { 101 } 102 nsec, _, _ = parseNanoseconds(s, n) 103 s = s[n:] 104 } 105 106 // Parse the time zone. 107 t := Date(year, Month(month), day, hour, min, sec, nsec, UTC) 108 if s != "Z" { 109 if len(s) != len("-07:00") { 110 return Time{}, false 111 } 112 hr := parseUint(s[1:3], 0, 23) // e.g., 07 113 mm := parseUint(s[4:6], 0, 59) // e.g., 00 114 if !ok || !((s[0] == '-' || s[0] == '+') && s[3] == ':') { 115 return Time{}, false 116 } 117 zoneOffset := (hr*60 + mm) * 60 118 if s[0] == '-' { 119 zoneOffset *= -1 120 } 121 t.addSec(-int64(zoneOffset)) 122 123 // Use local zone with the given offset if possible. 124 if _, offset, _, _, _ := local.lookup(t.unixSec()); offset == zoneOffset { 125 t.setLoc(local) 126 } else { 127 t.setLoc(FixedZone("", zoneOffset)) 128 } 129 } 130 return t, true 131 }