...

Source file src/github.com/pelletier/go-toml/localtime.go

Documentation: github.com/pelletier/go-toml

     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  }
   288  

View as plain text