vendor/github.com/pelletier/go-toml/localtime.go
changeset 251 1c52a0eeb952
child 256 6d9efbef00a9
equal deleted inserted replaced
250:c040f992052f 251:1c52a0eeb952
       
     1 // Implementation of TOML's local date/time.
       
     2 // Copied over from https://github.com/googleapis/google-cloud-go/blob/master/civil/civil.go
       
     3 // to avoid pulling all the Google dependencies.
       
     4 //
       
     5 // Copyright 2016 Google LLC
       
     6 //
       
     7 // Licensed under the Apache License, Version 2.0 (the "License");
       
     8 // you may not use this file except in compliance with the License.
       
     9 // You may obtain a copy of the License at
       
    10 //
       
    11 //      http://www.apache.org/licenses/LICENSE-2.0
       
    12 //
       
    13 // Unless required by applicable law or agreed to in writing, software
       
    14 // distributed under the License is distributed on an "AS IS" BASIS,
       
    15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    16 // See the License for the specific language governing permissions and
       
    17 // limitations under the License.
       
    18 
       
    19 // Package civil implements types for civil time, a time-zone-independent
       
    20 // representation of time that follows the rules of the proleptic
       
    21 // Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
       
    22 // minutes.
       
    23 //
       
    24 // Because they lack location information, these types do not represent unique
       
    25 // moments or intervals of time. Use time.Time for that purpose.
       
    26 package toml
       
    27 
       
    28 import (
       
    29 	"fmt"
       
    30 	"time"
       
    31 )
       
    32 
       
    33 // A LocalDate represents a date (year, month, day).
       
    34 //
       
    35 // This type does not include location information, and therefore does not
       
    36 // describe a unique 24-hour timespan.
       
    37 type LocalDate struct {
       
    38 	Year  int        // Year (e.g., 2014).
       
    39 	Month time.Month // Month of the year (January = 1, ...).
       
    40 	Day   int        // Day of the month, starting at 1.
       
    41 }
       
    42 
       
    43 // LocalDateOf returns the LocalDate in which a time occurs in that time's location.
       
    44 func LocalDateOf(t time.Time) LocalDate {
       
    45 	var d LocalDate
       
    46 	d.Year, d.Month, d.Day = t.Date()
       
    47 	return d
       
    48 }
       
    49 
       
    50 // ParseLocalDate parses a string in RFC3339 full-date format and returns the date value it represents.
       
    51 func ParseLocalDate(s string) (LocalDate, error) {
       
    52 	t, err := time.Parse("2006-01-02", s)
       
    53 	if err != nil {
       
    54 		return LocalDate{}, err
       
    55 	}
       
    56 	return LocalDateOf(t), nil
       
    57 }
       
    58 
       
    59 // String returns the date in RFC3339 full-date format.
       
    60 func (d LocalDate) String() string {
       
    61 	return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
       
    62 }
       
    63 
       
    64 // IsValid reports whether the date is valid.
       
    65 func (d LocalDate) IsValid() bool {
       
    66 	return LocalDateOf(d.In(time.UTC)) == d
       
    67 }
       
    68 
       
    69 // In returns the time corresponding to time 00:00:00 of the date in the location.
       
    70 //
       
    71 // In is always consistent with time.LocalDate, even when time.LocalDate returns a time
       
    72 // on a different day. For example, if loc is America/Indiana/Vincennes, then both
       
    73 //     time.LocalDate(1955, time.May, 1, 0, 0, 0, 0, loc)
       
    74 // and
       
    75 //     civil.LocalDate{Year: 1955, Month: time.May, Day: 1}.In(loc)
       
    76 // return 23:00:00 on April 30, 1955.
       
    77 //
       
    78 // In panics if loc is nil.
       
    79 func (d LocalDate) In(loc *time.Location) time.Time {
       
    80 	return time.Date(d.Year, d.Month, d.Day, 0, 0, 0, 0, loc)
       
    81 }
       
    82 
       
    83 // AddDays returns the date that is n days in the future.
       
    84 // n can also be negative to go into the past.
       
    85 func (d LocalDate) AddDays(n int) LocalDate {
       
    86 	return LocalDateOf(d.In(time.UTC).AddDate(0, 0, n))
       
    87 }
       
    88 
       
    89 // DaysSince returns the signed number of days between the date and s, not including the end day.
       
    90 // This is the inverse operation to AddDays.
       
    91 func (d LocalDate) DaysSince(s LocalDate) (days int) {
       
    92 	// We convert to Unix time so we do not have to worry about leap seconds:
       
    93 	// Unix time increases by exactly 86400 seconds per day.
       
    94 	deltaUnix := d.In(time.UTC).Unix() - s.In(time.UTC).Unix()
       
    95 	return int(deltaUnix / 86400)
       
    96 }
       
    97 
       
    98 // Before reports whether d1 occurs before d2.
       
    99 func (d1 LocalDate) Before(d2 LocalDate) bool {
       
   100 	if d1.Year != d2.Year {
       
   101 		return d1.Year < d2.Year
       
   102 	}
       
   103 	if d1.Month != d2.Month {
       
   104 		return d1.Month < d2.Month
       
   105 	}
       
   106 	return d1.Day < d2.Day
       
   107 }
       
   108 
       
   109 // After reports whether d1 occurs after d2.
       
   110 func (d1 LocalDate) After(d2 LocalDate) bool {
       
   111 	return d2.Before(d1)
       
   112 }
       
   113 
       
   114 // MarshalText implements the encoding.TextMarshaler interface.
       
   115 // The output is the result of d.String().
       
   116 func (d LocalDate) MarshalText() ([]byte, error) {
       
   117 	return []byte(d.String()), nil
       
   118 }
       
   119 
       
   120 // UnmarshalText implements the encoding.TextUnmarshaler interface.
       
   121 // The date is expected to be a string in a format accepted by ParseLocalDate.
       
   122 func (d *LocalDate) UnmarshalText(data []byte) error {
       
   123 	var err error
       
   124 	*d, err = ParseLocalDate(string(data))
       
   125 	return err
       
   126 }
       
   127 
       
   128 // A LocalTime represents a time with nanosecond precision.
       
   129 //
       
   130 // This type does not include location information, and therefore does not
       
   131 // describe a unique moment in time.
       
   132 //
       
   133 // This type exists to represent the TIME type in storage-based APIs like BigQuery.
       
   134 // Most operations on Times are unlikely to be meaningful. Prefer the LocalDateTime type.
       
   135 type LocalTime struct {
       
   136 	Hour       int // The hour of the day in 24-hour format; range [0-23]
       
   137 	Minute     int // The minute of the hour; range [0-59]
       
   138 	Second     int // The second of the minute; range [0-59]
       
   139 	Nanosecond int // The nanosecond of the second; range [0-999999999]
       
   140 }
       
   141 
       
   142 // LocalTimeOf returns the LocalTime representing the time of day in which a time occurs
       
   143 // in that time's location. It ignores the date.
       
   144 func LocalTimeOf(t time.Time) LocalTime {
       
   145 	var tm LocalTime
       
   146 	tm.Hour, tm.Minute, tm.Second = t.Clock()
       
   147 	tm.Nanosecond = t.Nanosecond()
       
   148 	return tm
       
   149 }
       
   150 
       
   151 // ParseLocalTime parses a string and returns the time value it represents.
       
   152 // ParseLocalTime accepts an extended form of the RFC3339 partial-time format. After
       
   153 // the HH:MM:SS part of the string, an optional fractional part may appear,
       
   154 // consisting of a decimal point followed by one to nine decimal digits.
       
   155 // (RFC3339 admits only one digit after the decimal point).
       
   156 func ParseLocalTime(s string) (LocalTime, error) {
       
   157 	t, err := time.Parse("15:04:05.999999999", s)
       
   158 	if err != nil {
       
   159 		return LocalTime{}, err
       
   160 	}
       
   161 	return LocalTimeOf(t), nil
       
   162 }
       
   163 
       
   164 // String returns the date in the format described in ParseLocalTime. If Nanoseconds
       
   165 // is zero, no fractional part will be generated. Otherwise, the result will
       
   166 // end with a fractional part consisting of a decimal point and nine digits.
       
   167 func (t LocalTime) String() string {
       
   168 	s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
       
   169 	if t.Nanosecond == 0 {
       
   170 		return s
       
   171 	}
       
   172 	return s + fmt.Sprintf(".%09d", t.Nanosecond)
       
   173 }
       
   174 
       
   175 // IsValid reports whether the time is valid.
       
   176 func (t LocalTime) IsValid() bool {
       
   177 	// Construct a non-zero time.
       
   178 	tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
       
   179 	return LocalTimeOf(tm) == t
       
   180 }
       
   181 
       
   182 // MarshalText implements the encoding.TextMarshaler interface.
       
   183 // The output is the result of t.String().
       
   184 func (t LocalTime) MarshalText() ([]byte, error) {
       
   185 	return []byte(t.String()), nil
       
   186 }
       
   187 
       
   188 // UnmarshalText implements the encoding.TextUnmarshaler interface.
       
   189 // The time is expected to be a string in a format accepted by ParseLocalTime.
       
   190 func (t *LocalTime) UnmarshalText(data []byte) error {
       
   191 	var err error
       
   192 	*t, err = ParseLocalTime(string(data))
       
   193 	return err
       
   194 }
       
   195 
       
   196 // A LocalDateTime represents a date and time.
       
   197 //
       
   198 // This type does not include location information, and therefore does not
       
   199 // describe a unique moment in time.
       
   200 type LocalDateTime struct {
       
   201 	Date LocalDate
       
   202 	Time LocalTime
       
   203 }
       
   204 
       
   205 // Note: We deliberately do not embed LocalDate into LocalDateTime, to avoid promoting AddDays and Sub.
       
   206 
       
   207 // LocalDateTimeOf returns the LocalDateTime in which a time occurs in that time's location.
       
   208 func LocalDateTimeOf(t time.Time) LocalDateTime {
       
   209 	return LocalDateTime{
       
   210 		Date: LocalDateOf(t),
       
   211 		Time: LocalTimeOf(t),
       
   212 	}
       
   213 }
       
   214 
       
   215 // ParseLocalDateTime parses a string and returns the LocalDateTime it represents.
       
   216 // ParseLocalDateTime accepts a variant of the RFC3339 date-time format that omits
       
   217 // the time offset but includes an optional fractional time, as described in
       
   218 // ParseLocalTime. Informally, the accepted format is
       
   219 //     YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
       
   220 // where the 'T' may be a lower-case 't'.
       
   221 func ParseLocalDateTime(s string) (LocalDateTime, error) {
       
   222 	t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
       
   223 	if err != nil {
       
   224 		t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
       
   225 		if err != nil {
       
   226 			return LocalDateTime{}, err
       
   227 		}
       
   228 	}
       
   229 	return LocalDateTimeOf(t), nil
       
   230 }
       
   231 
       
   232 // String returns the date in the format described in ParseLocalDate.
       
   233 func (dt LocalDateTime) String() string {
       
   234 	return dt.Date.String() + "T" + dt.Time.String()
       
   235 }
       
   236 
       
   237 // IsValid reports whether the datetime is valid.
       
   238 func (dt LocalDateTime) IsValid() bool {
       
   239 	return dt.Date.IsValid() && dt.Time.IsValid()
       
   240 }
       
   241 
       
   242 // In returns the time corresponding to the LocalDateTime in the given location.
       
   243 //
       
   244 // If the time is missing or ambigous at the location, In returns the same
       
   245 // result as time.LocalDate. For example, if loc is America/Indiana/Vincennes, then
       
   246 // both
       
   247 //     time.LocalDate(1955, time.May, 1, 0, 30, 0, 0, loc)
       
   248 // and
       
   249 //     civil.LocalDateTime{
       
   250 //         civil.LocalDate{Year: 1955, Month: time.May, Day: 1}},
       
   251 //         civil.LocalTime{Minute: 30}}.In(loc)
       
   252 // return 23:30:00 on April 30, 1955.
       
   253 //
       
   254 // In panics if loc is nil.
       
   255 func (dt LocalDateTime) In(loc *time.Location) time.Time {
       
   256 	return time.Date(dt.Date.Year, dt.Date.Month, dt.Date.Day, dt.Time.Hour, dt.Time.Minute, dt.Time.Second, dt.Time.Nanosecond, loc)
       
   257 }
       
   258 
       
   259 // Before reports whether dt1 occurs before dt2.
       
   260 func (dt1 LocalDateTime) Before(dt2 LocalDateTime) bool {
       
   261 	return dt1.In(time.UTC).Before(dt2.In(time.UTC))
       
   262 }
       
   263 
       
   264 // After reports whether dt1 occurs after dt2.
       
   265 func (dt1 LocalDateTime) After(dt2 LocalDateTime) bool {
       
   266 	return dt2.Before(dt1)
       
   267 }
       
   268 
       
   269 // MarshalText implements the encoding.TextMarshaler interface.
       
   270 // The output is the result of dt.String().
       
   271 func (dt LocalDateTime) MarshalText() ([]byte, error) {
       
   272 	return []byte(dt.String()), nil
       
   273 }
       
   274 
       
   275 // UnmarshalText implements the encoding.TextUnmarshaler interface.
       
   276 // The datetime is expected to be a string in a format accepted by ParseLocalDateTime
       
   277 func (dt *LocalDateTime) UnmarshalText(data []byte) error {
       
   278 	var err error
       
   279 	*dt, err = ParseLocalDateTime(string(data))
       
   280 	return err
       
   281 }