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