1package date
2
3import (
4	"regexp"
5	"time"
6)
7
8// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases.
9const (
10	azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"`
11	azureUtcFormat     = "2006-01-02T15:04:05.999999999"
12	rfc3339JSON        = `"` + time.RFC3339Nano + `"`
13	rfc3339            = time.RFC3339Nano
14	tzOffsetRegex      = `(Z|z|\+|-)(\d+:\d+)*"*$`
15)
16
17// Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e.,
18// 2006-01-02T15:04:05Z).
19type Time struct {
20	time.Time
21}
22
23// MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
24// 2006-01-02T15:04:05Z).
25func (t Time) MarshalBinary() ([]byte, error) {
26	return t.Time.MarshalText()
27}
28
29// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
30// (i.e., 2006-01-02T15:04:05Z).
31func (t *Time) UnmarshalBinary(data []byte) error {
32	return t.UnmarshalText(data)
33}
34
35// MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e.,
36// 2006-01-02T15:04:05Z).
37func (t Time) MarshalJSON() (json []byte, err error) {
38	return t.Time.MarshalJSON()
39}
40
41// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time
42// (i.e., 2006-01-02T15:04:05Z).
43func (t *Time) UnmarshalJSON(data []byte) (err error) {
44	timeFormat := azureUtcFormatJSON
45	match, err := regexp.Match(tzOffsetRegex, data)
46	if err != nil {
47		return err
48	} else if match {
49		timeFormat = rfc3339JSON
50	}
51	t.Time, err = ParseTime(timeFormat, string(data))
52	return err
53}
54
55// MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
56// 2006-01-02T15:04:05Z).
57func (t Time) MarshalText() (text []byte, err error) {
58	return t.Time.MarshalText()
59}
60
61// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
62// (i.e., 2006-01-02T15:04:05Z).
63func (t *Time) UnmarshalText(data []byte) (err error) {
64	timeFormat := azureUtcFormat
65	match, err := regexp.Match(tzOffsetRegex, data)
66	if err != nil {
67		return err
68	} else if match {
69		timeFormat = rfc3339
70	}
71	t.Time, err = ParseTime(timeFormat, string(data))
72	return err
73}
74
75// String returns the Time formatted as an RFC3339 date-time string (i.e.,
76// 2006-01-02T15:04:05Z).
77func (t Time) String() string {
78	// Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does.
79	b, err := t.MarshalText()
80	if err != nil {
81		return ""
82	}
83	return string(b)
84}
85
86// ToTime returns a Time as a time.Time
87func (t Time) ToTime() time.Time {
88	return t.Time
89}
90