...

Source file src/github.com/go-openapi/strfmt/time.go

Documentation: github.com/go-openapi/strfmt

     1  // Copyright 2015 go-swagger maintainers
     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 strfmt
    16  
    17  import (
    18  	"database/sql/driver"
    19  	"encoding/binary"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"regexp"
    24  	"strings"
    25  	"time"
    26  
    27  	"go.mongodb.org/mongo-driver/bson"
    28  
    29  	"go.mongodb.org/mongo-driver/bson/bsontype"
    30  )
    31  
    32  var (
    33  	// UnixZero sets the zero unix timestamp we want to compare against.
    34  	// Unix 0 for an EST timezone is not equivalent to a UTC timezone.
    35  	UnixZero = time.Unix(0, 0).UTC()
    36  )
    37  
    38  func init() {
    39  	dt := DateTime{}
    40  	Default.Add("datetime", &dt, IsDateTime)
    41  }
    42  
    43  // IsDateTime returns true when the string is a valid date-time
    44  func IsDateTime(str string) bool {
    45  	if len(str) < 4 {
    46  		return false
    47  	}
    48  	s := strings.Split(strings.ToLower(str), "t")
    49  	if len(s) < 2 || !IsDate(s[0]) {
    50  		return false
    51  	}
    52  
    53  	matches := rxDateTime.FindAllStringSubmatch(s[1], -1)
    54  	if len(matches) == 0 || len(matches[0]) == 0 {
    55  		return false
    56  	}
    57  	m := matches[0]
    58  	res := m[1] <= "23" && m[2] <= "59" && m[3] <= "59"
    59  	return res
    60  }
    61  
    62  const (
    63  	// RFC3339Millis represents a ISO8601 format to millis instead of to nanos
    64  	RFC3339Millis = "2006-01-02T15:04:05.000Z07:00"
    65  	// RFC3339MillisNoColon represents a ISO8601 format to millis instead of to nanos
    66  	RFC3339MillisNoColon = "2006-01-02T15:04:05.000Z0700"
    67  	// RFC3339Micro represents a ISO8601 format to micro instead of to nano
    68  	RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
    69  	// RFC3339MicroNoColon represents a ISO8601 format to micro instead of to nano
    70  	RFC3339MicroNoColon = "2006-01-02T15:04:05.000000Z0700"
    71  	// ISO8601LocalTime represents a ISO8601 format to ISO8601 in local time (no timezone)
    72  	ISO8601LocalTime = "2006-01-02T15:04:05"
    73  	// ISO8601TimeWithReducedPrecision represents a ISO8601 format with reduced precision (dropped secs)
    74  	ISO8601TimeWithReducedPrecision = "2006-01-02T15:04Z"
    75  	// ISO8601TimeWithReducedPrecisionLocaltime represents a ISO8601 format with reduced precision and no timezone (dropped seconds + no timezone)
    76  	ISO8601TimeWithReducedPrecisionLocaltime = "2006-01-02T15:04"
    77  	// ISO8601TimeUniversalSortableDateTimePattern represents a ISO8601 universal sortable date time pattern.
    78  	ISO8601TimeUniversalSortableDateTimePattern = "2006-01-02 15:04:05"
    79  	// short form of ISO8601TimeUniversalSortableDateTimePattern
    80  	ISO8601TimeUniversalSortableDateTimePatternShortForm = "2006-01-02"
    81  	// DateTimePattern pattern to match for the date-time format from http://tools.ietf.org/html/rfc3339#section-5.6
    82  	DateTimePattern = `^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$`
    83  )
    84  
    85  var (
    86  	rxDateTime = regexp.MustCompile(DateTimePattern)
    87  
    88  	// DateTimeFormats is the collection of formats used by ParseDateTime()
    89  	DateTimeFormats = []string{RFC3339Micro, RFC3339MicroNoColon, RFC3339Millis, RFC3339MillisNoColon, time.RFC3339, time.RFC3339Nano, ISO8601LocalTime, ISO8601TimeWithReducedPrecision, ISO8601TimeWithReducedPrecisionLocaltime, ISO8601TimeUniversalSortableDateTimePattern, ISO8601TimeUniversalSortableDateTimePatternShortForm}
    90  
    91  	// MarshalFormat sets the time resolution format used for marshaling time (set to milliseconds)
    92  	MarshalFormat = RFC3339Millis
    93  
    94  	// NormalizeTimeForMarshal provides a normalization function on time befeore marshalling (e.g. time.UTC).
    95  	// By default, the time value is not changed.
    96  	NormalizeTimeForMarshal = func(t time.Time) time.Time { return t }
    97  
    98  	// DefaultTimeLocation provides a location for a time when the time zone is not encoded in the string (ex: ISO8601 Local variants).
    99  	DefaultTimeLocation = time.UTC
   100  )
   101  
   102  // ParseDateTime parses a string that represents an ISO8601 time or a unix epoch
   103  func ParseDateTime(data string) (DateTime, error) {
   104  	if data == "" {
   105  		return NewDateTime(), nil
   106  	}
   107  	var lastError error
   108  	for _, layout := range DateTimeFormats {
   109  		dd, err := time.ParseInLocation(layout, data, DefaultTimeLocation)
   110  		if err != nil {
   111  			lastError = err
   112  			continue
   113  		}
   114  		return DateTime(dd), nil
   115  	}
   116  	return DateTime{}, lastError
   117  }
   118  
   119  // DateTime is a time but it serializes to ISO8601 format with millis
   120  // It knows how to read 3 different variations of a RFC3339 date time.
   121  // Most APIs we encounter want either millisecond or second precision times.
   122  // This just tries to make it worry-free.
   123  //
   124  // swagger:strfmt date-time
   125  type DateTime time.Time
   126  
   127  // NewDateTime is a representation of zero value for DateTime type
   128  func NewDateTime() DateTime {
   129  	return DateTime(time.Unix(0, 0).UTC())
   130  }
   131  
   132  // String converts this time to a string
   133  func (t DateTime) String() string {
   134  	return NormalizeTimeForMarshal(time.Time(t)).Format(MarshalFormat)
   135  }
   136  
   137  // IsZero returns whether the date time is a zero value
   138  func (t *DateTime) IsZero() bool {
   139  	if t == nil {
   140  		return true
   141  	}
   142  	return time.Time(*t).IsZero()
   143  }
   144  
   145  // IsUnixZerom returns whether the date time is equivalent to time.Unix(0, 0).UTC().
   146  func (t *DateTime) IsUnixZero() bool {
   147  	if t == nil {
   148  		return true
   149  	}
   150  	return time.Time(*t).Equal(UnixZero)
   151  }
   152  
   153  // MarshalText implements the text marshaller interface
   154  func (t DateTime) MarshalText() ([]byte, error) {
   155  	return []byte(t.String()), nil
   156  }
   157  
   158  // UnmarshalText implements the text unmarshaller interface
   159  func (t *DateTime) UnmarshalText(text []byte) error {
   160  	tt, err := ParseDateTime(string(text))
   161  	if err != nil {
   162  		return err
   163  	}
   164  	*t = tt
   165  	return nil
   166  }
   167  
   168  // Scan scans a DateTime value from database driver type.
   169  func (t *DateTime) Scan(raw interface{}) error {
   170  	// TODO: case int64: and case float64: ?
   171  	switch v := raw.(type) {
   172  	case []byte:
   173  		return t.UnmarshalText(v)
   174  	case string:
   175  		return t.UnmarshalText([]byte(v))
   176  	case time.Time:
   177  		*t = DateTime(v)
   178  	case nil:
   179  		*t = DateTime{}
   180  	default:
   181  		return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v", v)
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // Value converts DateTime to a primitive value ready to written to a database.
   188  func (t DateTime) Value() (driver.Value, error) {
   189  	return driver.Value(t.String()), nil
   190  }
   191  
   192  // MarshalJSON returns the DateTime as JSON
   193  func (t DateTime) MarshalJSON() ([]byte, error) {
   194  	return json.Marshal(NormalizeTimeForMarshal(time.Time(t)).Format(MarshalFormat))
   195  }
   196  
   197  // UnmarshalJSON sets the DateTime from JSON
   198  func (t *DateTime) UnmarshalJSON(data []byte) error {
   199  	if string(data) == jsonNull {
   200  		return nil
   201  	}
   202  
   203  	var tstr string
   204  	if err := json.Unmarshal(data, &tstr); err != nil {
   205  		return err
   206  	}
   207  	tt, err := ParseDateTime(tstr)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	*t = tt
   212  	return nil
   213  }
   214  
   215  // MarshalBSON renders the DateTime as a BSON document
   216  func (t DateTime) MarshalBSON() ([]byte, error) {
   217  	return bson.Marshal(bson.M{"data": t})
   218  }
   219  
   220  // UnmarshalBSON reads the DateTime from a BSON document
   221  func (t *DateTime) UnmarshalBSON(data []byte) error {
   222  	var obj struct {
   223  		Data DateTime
   224  	}
   225  
   226  	if err := bson.Unmarshal(data, &obj); err != nil {
   227  		return err
   228  	}
   229  
   230  	*t = obj.Data
   231  
   232  	return nil
   233  }
   234  
   235  // MarshalBSONValue is an interface implemented by types that can marshal themselves
   236  // into a BSON document represented as bytes. The bytes returned must be a valid
   237  // BSON document if the error is nil.
   238  // Marshals a DateTime as a bsontype.DateTime, an int64 representing
   239  // milliseconds since epoch.
   240  func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) {
   241  	// UnixNano cannot be used directly, the result of calling UnixNano on the zero
   242  	// Time is undefined. Thats why we use time.Nanosecond() instead.
   243  
   244  	tNorm := NormalizeTimeForMarshal(time.Time(t))
   245  	i64 := tNorm.Unix()*1000 + int64(tNorm.Nanosecond())/1e6
   246  
   247  	buf := make([]byte, 8)
   248  	binary.LittleEndian.PutUint64(buf, uint64(i64))
   249  
   250  	return bson.TypeDateTime, buf, nil
   251  }
   252  
   253  // UnmarshalBSONValue is an interface implemented by types that can unmarshal a
   254  // BSON value representation of themselves. The BSON bytes and type can be
   255  // assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it
   256  // wishes to retain the data after returning.
   257  func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error {
   258  	if tpe == bson.TypeNull {
   259  		*t = DateTime{}
   260  		return nil
   261  	}
   262  
   263  	if len(data) != 8 {
   264  		return errors.New("bson date field length not exactly 8 bytes")
   265  	}
   266  
   267  	i64 := int64(binary.LittleEndian.Uint64(data))
   268  	// TODO: Use bsonprim.DateTime.Time() method
   269  	*t = DateTime(time.Unix(i64/1000, i64%1000*1000000))
   270  
   271  	return nil
   272  }
   273  
   274  // DeepCopyInto copies the receiver and writes its value into out.
   275  func (t *DateTime) DeepCopyInto(out *DateTime) {
   276  	*out = *t
   277  }
   278  
   279  // DeepCopy copies the receiver into a new DateTime.
   280  func (t *DateTime) DeepCopy() *DateTime {
   281  	if t == nil {
   282  		return nil
   283  	}
   284  	out := new(DateTime)
   285  	t.DeepCopyInto(out)
   286  	return out
   287  }
   288  
   289  // GobEncode implements the gob.GobEncoder interface.
   290  func (t DateTime) GobEncode() ([]byte, error) {
   291  	return t.MarshalBinary()
   292  }
   293  
   294  // GobDecode implements the gob.GobDecoder interface.
   295  func (t *DateTime) GobDecode(data []byte) error {
   296  	return t.UnmarshalBinary(data)
   297  }
   298  
   299  // MarshalBinary implements the encoding.BinaryMarshaler interface.
   300  func (t DateTime) MarshalBinary() ([]byte, error) {
   301  	return NormalizeTimeForMarshal(time.Time(t)).MarshalBinary()
   302  }
   303  
   304  // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
   305  func (t *DateTime) UnmarshalBinary(data []byte) error {
   306  	var original time.Time
   307  
   308  	err := original.UnmarshalBinary(data)
   309  	if err != nil {
   310  		return err
   311  	}
   312  
   313  	*t = DateTime(original)
   314  
   315  	return nil
   316  }
   317  
   318  // Equal checks if two DateTime instances are equal using time.Time's Equal method
   319  func (t DateTime) Equal(t2 DateTime) bool {
   320  	return time.Time(t).Equal(time.Time(t2))
   321  }
   322  

View as plain text