this repo has no description
at master 253 lines 8.9 kB view raw
1// Copyright 2019 CUE Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Package time defines time-related types. 16// 17// In CUE time values are represented as a string of the format 18// time.RFC3339Nano. 19package time 20 21import ( 22 "fmt" 23 "time" 24) 25 26// These are predefined layouts for use in Time.Format and time.Parse. 27// The reference time used in the layouts is the specific time: 28// 29// Mon Jan 2 15:04:05 MST 2006 30// 31// which is Unix time 1136239445. Since MST is GMT-0700, 32// the reference time can be thought of as 33// 34// 01/02 03:04:05PM '06 -0700 35// 36// To define your own format, write down what the reference time would look 37// like formatted your way; see the values of constants like ANSIC, 38// StampMicro or Kitchen for examples. The model is to demonstrate what the 39// reference time looks like so that the Format and Parse methods can apply 40// the same transformation to a general time value. 41// 42// Some valid layouts are invalid time values for time.Parse, due to formats 43// such as _ for space padding and Z for zone information. 44// 45// Within the format string, an underscore _ represents a space that may be 46// replaced by a digit if the following number (a day) has two digits; for 47// compatibility with fixed-width Unix time formats. 48// 49// A decimal point followed by one or more zeros represents a fractional 50// second, printed to the given number of decimal places. A decimal point 51// followed by one or more nines represents a fractional second, printed to 52// the given number of decimal places, with trailing zeros removed. 53// When parsing (only), the input may contain a fractional second 54// field immediately after the seconds field, even if the layout does not 55// signify its presence. In that case a decimal point followed by a maximal 56// series of digits is parsed as a fractional second. 57// 58// Numeric time zone offsets format as follows: 59// 60// -0700 ±hhmm 61// -07:00 ±hh:mm 62// -07 ±hh 63// 64// Replacing the sign in the format with a Z triggers 65// the ISO 8601 behavior of printing Z instead of an 66// offset for the UTC zone. Thus: 67// 68// Z0700 Z or ±hhmm 69// Z07:00 Z or ±hh:mm 70// Z07 Z or ±hh 71// 72// The recognized day of week formats are "Mon" and "Monday". 73// The recognized month formats are "Jan" and "January". 74// 75// Text in the format string that is not recognized as part of the reference 76// time is echoed verbatim during Format and expected to appear verbatim 77// in the input to Parse. 78// 79// The executable example for Time.Format demonstrates the working 80// of the layout string in detail and is a good reference. 81// 82// Note that the RFC822, RFC850, and RFC1123 formats should be applied 83// only to local times. Applying them to UTC times will use "UTC" as the 84// time zone abbreviation, while strictly speaking those RFCs require the 85// use of "GMT" in that case. 86// In general RFC1123Z should be used instead of RFC1123 for servers 87// that insist on that format, and RFC3339 should be preferred for new protocols. 88// RFC3339, RFC822, RFC822Z, RFC1123, and RFC1123Z are useful for formatting; 89// when used with time.Parse they do not accept all the time formats 90// permitted by the RFCs. 91// The RFC3339Nano format removes trailing zeros from the seconds field 92// and thus may not sort correctly once formatted. 93const ( 94 ANSIC = "Mon Jan _2 15:04:05 2006" 95 UnixDate = "Mon Jan _2 15:04:05 MST 2006" 96 RubyDate = "Mon Jan 02 15:04:05 -0700 2006" 97 RFC822 = "02 Jan 06 15:04 MST" 98 RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone 99 RFC850 = "Monday, 02-Jan-06 15:04:05 MST" 100 RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" 101 RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone 102 RFC3339 = "2006-01-02T15:04:05Z07:00" 103 RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" 104 RFC3339Date = "2006-01-02" 105 Kitchen = "3:04PM" 106 Kitchen24 = "15:04" 107) 108 109const ( 110 January = 1 111 February = 2 112 March = 3 113 April = 4 114 May = 5 115 June = 6 116 July = 7 117 August = 8 118 September = 9 119 October = 10 120 November = 11 121 December = 12 122) 123 124const ( 125 Sunday = 0 126 Monday = 1 127 Tuesday = 2 128 Wednesday = 3 129 Thursday = 4 130 Friday = 5 131 Saturday = 6 132) 133 134// Time validates a RFC3339 date-time. 135// 136// Caveat: this implementation uses the Go implementation, which does not 137// accept leap seconds. 138func Time(s string) (bool, error) { 139 return timeFormat(s, time.RFC3339Nano) 140} 141 142func timeFormat(value, layout string) (bool, error) { 143 _, err := time.ParseInLocation(layout, value, time.UTC) 144 if err != nil { 145 // Use our own error, the time package's error as the Go error is too 146 // confusing within this context. 147 return false, fmt.Errorf("invalid time %q", value) 148 } 149 return true, nil 150} 151 152// Format defines a type string that must adhere to a certain layout. 153// 154// See Parse for a description on layout strings. 155func Format(value, layout string) (bool, error) { 156 return timeFormat(value, layout) 157} 158 159// FormatString returns a textual representation of the time value. 160// The formatted value is formatted according to the layout defined by the 161// argument. See Parse for more information on the layout string. 162func FormatString(layout, value string) (string, error) { 163 t, err := time.Parse(time.RFC3339Nano, value) 164 if err != nil { 165 return "", err 166 } 167 return t.Format(layout), nil 168} 169 170// Parse parses a formatted string and returns the time value it represents. 171// The layout defines the format by showing how the reference time, 172// defined to be 173// 174// Mon Jan 2 15:04:05 -0700 MST 2006 175// 176// would be interpreted if it were the value; it serves as an example of 177// the input format. The same interpretation will then be made to the 178// input string. 179// 180// Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard 181// and convenient representations of the reference time. For more information 182// about the formats and the definition of the reference time, see the 183// documentation for ANSIC and the other constants defined by this package. 184// Also, the executable example for Time.Format demonstrates the working 185// of the layout string in detail and is a good reference. 186// 187// Elements omitted from the value are assumed to be zero or, when 188// zero is impossible, one, so parsing "3:04pm" returns the time 189// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is 190// 0, this time is before the zero Time). 191// Years must be in the range 0000..9999. The day of the week is checked 192// for syntax but it is otherwise ignored. 193// 194// In the absence of a time zone indicator, Parse returns a time in UTC. 195// 196// When parsing a time with a zone offset like -0700, if the offset corresponds 197// to a time zone used by the current location (Local), then Parse uses that 198// location and zone in the returned time. Otherwise it records the time as 199// being in a fabricated location with time fixed at the given zone offset. 200// 201// Parse currently does not support zone abbreviations like MST. All are 202// interpreted as UTC. 203func Parse(layout, value string) (string, error) { 204 // TODO: should we support locations? The result will be non-hermetic. 205 // See comments on github.com/cue-lang/cue/issues/1522. 206 t, err := time.ParseInLocation(layout, value, time.UTC) 207 if err != nil { 208 return "", err 209 } 210 return t.UTC().Format(time.RFC3339Nano), nil 211} 212 213// Unix returns the Time, in UTC, corresponding to the given Unix time, 214// sec seconds and nsec nanoseconds since January 1, 1970 UTC. 215// It is valid to pass nsec outside the range [0, 999999999]. 216// Not all sec values have a corresponding time value. One such 217// value is 1<<63-1 (the largest int64 value). 218func Unix(sec int64, nsec int64) string { 219 t := time.Unix(sec, nsec) 220 return t.UTC().Format(time.RFC3339Nano) 221} 222 223// Parts holds individual parts of a parsed time stamp. 224type Parts struct { 225 Year int `json:"year"` 226 Month int `json:"month"` 227 Day int `json:"day"` 228 Hour int `json:"hour"` 229 Minute int `json:"minute"` 230 231 // Second is equal to div(Nanosecond, 1_000_000_000) 232 Second int `json:"second"` 233 Nanosecond int `json:"nanosecond"` 234} 235 236// Split parses a time string into its individual parts. 237func Split(t string) (*Parts, error) { 238 st, err := time.Parse(time.RFC3339Nano, t) 239 if err != nil { 240 return nil, err 241 } 242 year, month, day := st.Date() 243 return &Parts{ 244 Year: year, 245 Month: int(month), 246 Day: day, 247 Hour: st.Hour(), 248 Minute: st.Minute(), 249 250 Second: st.Second(), 251 Nanosecond: st.Nanosecond(), 252 }, nil 253}