...

Source file src/cuelang.org/go/pkg/time/time.go

Documentation: cuelang.org/go/pkg/time

     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.
    19  package time
    20  
    21  import (
    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.
    93  const (
    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  
   109  const (
   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  
   124  const (
   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.
   138  func Time(s string) (bool, error) {
   139  	return timeFormat(s, time.RFC3339Nano)
   140  }
   141  
   142  func 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.
   155  func 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.
   162  func 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.
   203  func 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).
   218  func 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.
   224  type 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.
   237  func 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  }
   254  

View as plain text