...

Source file src/cloud.google.com/go/civil/civil.go

Documentation: cloud.google.com/go/civil

     1  // Copyright 2016 Google LLC
     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 civil implements types for civil time, a time-zone-independent
    16  // representation of time that follows the rules of the proleptic
    17  // Gregorian calendar with exactly 24-hour days, 60-minute hours, and 60-second
    18  // minutes.
    19  //
    20  // Because they lack location information, these types do not represent unique
    21  // moments or intervals of time. Use time.Time for that purpose.
    22  package civil
    23  
    24  import (
    25  	"fmt"
    26  	"time"
    27  )
    28  
    29  // A Date represents a date (year, month, day).
    30  //
    31  // This type does not include location information, and therefore does not
    32  // describe a unique 24-hour timespan.
    33  type Date struct {
    34  	Year  int        // Year (e.g., 2014).
    35  	Month time.Month // Month of the year (January = 1, ...).
    36  	Day   int        // Day of the month, starting at 1.
    37  }
    38  
    39  // DateOf returns the Date in which a time occurs in that time's location.
    40  func DateOf(t time.Time) Date {
    41  	var d Date
    42  	d.Year, d.Month, d.Day = t.Date()
    43  	return d
    44  }
    45  
    46  // ParseDate parses a string in RFC3339 full-date format and returns the date value it represents.
    47  func ParseDate(s string) (Date, error) {
    48  	t, err := time.Parse("2006-01-02", s)
    49  	if err != nil {
    50  		return Date{}, err
    51  	}
    52  	return DateOf(t), nil
    53  }
    54  
    55  // String returns the date in RFC3339 full-date format.
    56  func (d Date) String() string {
    57  	return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
    58  }
    59  
    60  // IsValid reports whether the date is valid.
    61  func (d Date) IsValid() bool {
    62  	return DateOf(d.In(time.UTC)) == d
    63  }
    64  
    65  // In returns the time corresponding to time 00:00:00 of the date in the location.
    66  //
    67  // In is always consistent with time.Date, even when time.Date returns a time
    68  // on a different day. For example, if loc is America/Indiana/Vincennes, then both
    69  //
    70  //	time.Date(1955, time.May, 1, 0, 0, 0, 0, loc)
    71  //
    72  // and
    73  //
    74  //	civil.Date{Year: 1955, Month: time.May, Day: 1}.In(loc)
    75  //
    76  // return 23:00:00 on April 30, 1955.
    77  //
    78  // In panics if loc is nil.
    79  func (d Date) 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 Date) AddDays(n int) Date {
    86  	return DateOf(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 Date) DaysSince(s Date) (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 d occurs before d2.
    99  func (d Date) Before(d2 Date) bool {
   100  	if d.Year != d2.Year {
   101  		return d.Year < d2.Year
   102  	}
   103  	if d.Month != d2.Month {
   104  		return d.Month < d2.Month
   105  	}
   106  	return d.Day < d2.Day
   107  }
   108  
   109  // After reports whether d occurs after d2.
   110  func (d Date) After(d2 Date) bool {
   111  	return d2.Before(d)
   112  }
   113  
   114  // IsZero reports whether date fields are set to their default value.
   115  func (d Date) IsZero() bool {
   116  	return (d.Year == 0) && (int(d.Month) == 0) && (d.Day == 0)
   117  }
   118  
   119  // MarshalText implements the encoding.TextMarshaler interface.
   120  // The output is the result of d.String().
   121  func (d Date) MarshalText() ([]byte, error) {
   122  	return []byte(d.String()), nil
   123  }
   124  
   125  // UnmarshalText implements the encoding.TextUnmarshaler interface.
   126  // The date is expected to be a string in a format accepted by ParseDate.
   127  func (d *Date) UnmarshalText(data []byte) error {
   128  	var err error
   129  	*d, err = ParseDate(string(data))
   130  	return err
   131  }
   132  
   133  // A Time represents a time with nanosecond precision.
   134  //
   135  // This type does not include location information, and therefore does not
   136  // describe a unique moment in time.
   137  //
   138  // This type exists to represent the TIME type in storage-based APIs like BigQuery.
   139  // Most operations on Times are unlikely to be meaningful. Prefer the DateTime type.
   140  type Time struct {
   141  	Hour       int // The hour of the day in 24-hour format; range [0-23]
   142  	Minute     int // The minute of the hour; range [0-59]
   143  	Second     int // The second of the minute; range [0-59]
   144  	Nanosecond int // The nanosecond of the second; range [0-999999999]
   145  }
   146  
   147  // TimeOf returns the Time representing the time of day in which a time occurs
   148  // in that time's location. It ignores the date.
   149  func TimeOf(t time.Time) Time {
   150  	var tm Time
   151  	tm.Hour, tm.Minute, tm.Second = t.Clock()
   152  	tm.Nanosecond = t.Nanosecond()
   153  	return tm
   154  }
   155  
   156  // ParseTime parses a string and returns the time value it represents.
   157  // ParseTime accepts an extended form of the RFC3339 partial-time format. After
   158  // the HH:MM:SS part of the string, an optional fractional part may appear,
   159  // consisting of a decimal point followed by one to nine decimal digits.
   160  // (RFC3339 admits only one digit after the decimal point).
   161  func ParseTime(s string) (Time, error) {
   162  	t, err := time.Parse("15:04:05.999999999", s)
   163  	if err != nil {
   164  		return Time{}, err
   165  	}
   166  	return TimeOf(t), nil
   167  }
   168  
   169  // String returns the date in the format described in ParseTime. If Nanoseconds
   170  // is zero, no fractional part will be generated. Otherwise, the result will
   171  // end with a fractional part consisting of a decimal point and nine digits.
   172  func (t Time) String() string {
   173  	s := fmt.Sprintf("%02d:%02d:%02d", t.Hour, t.Minute, t.Second)
   174  	if t.Nanosecond == 0 {
   175  		return s
   176  	}
   177  	return s + fmt.Sprintf(".%09d", t.Nanosecond)
   178  }
   179  
   180  // IsValid reports whether the time is valid.
   181  func (t Time) IsValid() bool {
   182  	// Construct a non-zero time.
   183  	tm := time.Date(2, 2, 2, t.Hour, t.Minute, t.Second, t.Nanosecond, time.UTC)
   184  	return TimeOf(tm) == t
   185  }
   186  
   187  // IsZero reports whether time fields are set to their default value.
   188  func (t Time) IsZero() bool {
   189  	return (t.Hour == 0) && (t.Minute == 0) && (t.Second == 0) && (t.Nanosecond == 0)
   190  }
   191  
   192  // Before reports whether t occurs before t2.
   193  func (t Time) Before(t2 Time) bool {
   194  	if t.Hour != t2.Hour {
   195  		return t.Hour < t2.Hour
   196  	}
   197  	if t.Minute != t2.Minute {
   198  		return t.Minute < t2.Minute
   199  	}
   200  	if t.Second != t2.Second {
   201  		return t.Second < t2.Second
   202  	}
   203  
   204  	return t.Nanosecond < t2.Nanosecond
   205  }
   206  
   207  // After reports whether t occurs after t2.
   208  func (t Time) After(t2 Time) bool {
   209  	return t2.Before(t)
   210  }
   211  
   212  // MarshalText implements the encoding.TextMarshaler interface.
   213  // The output is the result of t.String().
   214  func (t Time) MarshalText() ([]byte, error) {
   215  	return []byte(t.String()), nil
   216  }
   217  
   218  // UnmarshalText implements the encoding.TextUnmarshaler interface.
   219  // The time is expected to be a string in a format accepted by ParseTime.
   220  func (t *Time) UnmarshalText(data []byte) error {
   221  	var err error
   222  	*t, err = ParseTime(string(data))
   223  	return err
   224  }
   225  
   226  // A DateTime represents a date and time.
   227  //
   228  // This type does not include location information, and therefore does not
   229  // describe a unique moment in time.
   230  type DateTime struct {
   231  	Date Date
   232  	Time Time
   233  }
   234  
   235  // Note: We deliberately do not embed Date into DateTime, to avoid promoting AddDays and Sub.
   236  
   237  // DateTimeOf returns the DateTime in which a time occurs in that time's location.
   238  func DateTimeOf(t time.Time) DateTime {
   239  	return DateTime{
   240  		Date: DateOf(t),
   241  		Time: TimeOf(t),
   242  	}
   243  }
   244  
   245  // ParseDateTime parses a string and returns the DateTime it represents.
   246  // ParseDateTime accepts a variant of the RFC3339 date-time format that omits
   247  // the time offset but includes an optional fractional time, as described in
   248  // ParseTime. Informally, the accepted format is
   249  //
   250  //	YYYY-MM-DDTHH:MM:SS[.FFFFFFFFF]
   251  //
   252  // where the 'T' may be a lower-case 't'.
   253  func ParseDateTime(s string) (DateTime, error) {
   254  	t, err := time.Parse("2006-01-02T15:04:05.999999999", s)
   255  	if err != nil {
   256  		t, err = time.Parse("2006-01-02t15:04:05.999999999", s)
   257  		if err != nil {
   258  			return DateTime{}, err
   259  		}
   260  	}
   261  	return DateTimeOf(t), nil
   262  }
   263  
   264  // String returns the date in the format described in ParseDate.
   265  func (dt DateTime) String() string {
   266  	return dt.Date.String() + "T" + dt.Time.String()
   267  }
   268  
   269  // IsValid reports whether the datetime is valid.
   270  func (dt DateTime) IsValid() bool {
   271  	return dt.Date.IsValid() && dt.Time.IsValid()
   272  }
   273  
   274  // In returns the time corresponding to the DateTime in the given location.
   275  //
   276  // If the time is missing or ambigous at the location, In returns the same
   277  // result as time.Date. For example, if loc is America/Indiana/Vincennes, then
   278  // both
   279  //
   280  //	time.Date(1955, time.May, 1, 0, 30, 0, 0, loc)
   281  //
   282  // and
   283  //
   284  //	civil.DateTime{
   285  //	    civil.Date{Year: 1955, Month: time.May, Day: 1}},
   286  //	    civil.Time{Minute: 30}}.In(loc)
   287  //
   288  // return 23:30:00 on April 30, 1955.
   289  //
   290  // In panics if loc is nil.
   291  func (dt DateTime) In(loc *time.Location) time.Time {
   292  	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)
   293  }
   294  
   295  // Before reports whether dt occurs before dt2.
   296  func (dt DateTime) Before(dt2 DateTime) bool {
   297  	return dt.In(time.UTC).Before(dt2.In(time.UTC))
   298  }
   299  
   300  // After reports whether dt occurs after dt2.
   301  func (dt DateTime) After(dt2 DateTime) bool {
   302  	return dt2.Before(dt)
   303  }
   304  
   305  // IsZero reports whether datetime fields are set to their default value.
   306  func (dt DateTime) IsZero() bool {
   307  	return dt.Date.IsZero() && dt.Time.IsZero()
   308  }
   309  
   310  // MarshalText implements the encoding.TextMarshaler interface.
   311  // The output is the result of dt.String().
   312  func (dt DateTime) MarshalText() ([]byte, error) {
   313  	return []byte(dt.String()), nil
   314  }
   315  
   316  // UnmarshalText implements the encoding.TextUnmarshaler interface.
   317  // The datetime is expected to be a string in a format accepted by ParseDateTime
   318  func (dt *DateTime) UnmarshalText(data []byte) error {
   319  	var err error
   320  	*dt, err = ParseDateTime(string(data))
   321  	return err
   322  }
   323  

View as plain text