1package date
2
3// Copyright 2017 Microsoft Corporation
4//
5//  Licensed under the Apache License, Version 2.0 (the "License");
6//  you may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at
8//
9//      http://www.apache.org/licenses/LICENSE-2.0
10//
11//  Unless required by applicable law or agreed to in writing, software
12//  distributed under the License is distributed on an "AS IS" BASIS,
13//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14//  See the License for the specific language governing permissions and
15//  limitations under the License.
16
17import (
18	"regexp"
19	"time"
20)
21
22// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases.
23const (
24	azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"`
25	azureUtcFormat     = "2006-01-02T15:04:05.999999999"
26	rfc3339JSON        = `"` + time.RFC3339Nano + `"`
27	rfc3339            = time.RFC3339Nano
28	tzOffsetRegex      = `(Z|z|\+|-)(\d+:\d+)*"*$`
29)
30
31// Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e.,
32// 2006-01-02T15:04:05Z).
33type Time struct {
34	time.Time
35}
36
37// MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
38// 2006-01-02T15:04:05Z).
39func (t Time) MarshalBinary() ([]byte, error) {
40	return t.Time.MarshalText()
41}
42
43// UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
44// (i.e., 2006-01-02T15:04:05Z).
45func (t *Time) UnmarshalBinary(data []byte) error {
46	return t.UnmarshalText(data)
47}
48
49// MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e.,
50// 2006-01-02T15:04:05Z).
51func (t Time) MarshalJSON() (json []byte, err error) {
52	return t.Time.MarshalJSON()
53}
54
55// UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time
56// (i.e., 2006-01-02T15:04:05Z).
57func (t *Time) UnmarshalJSON(data []byte) (err error) {
58	timeFormat := azureUtcFormatJSON
59	match, err := regexp.Match(tzOffsetRegex, data)
60	if err != nil {
61		return err
62	} else if match {
63		timeFormat = rfc3339JSON
64	}
65	t.Time, err = ParseTime(timeFormat, string(data))
66	return err
67}
68
69// MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e.,
70// 2006-01-02T15:04:05Z).
71func (t Time) MarshalText() (text []byte, err error) {
72	return t.Time.MarshalText()
73}
74
75// UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time
76// (i.e., 2006-01-02T15:04:05Z).
77func (t *Time) UnmarshalText(data []byte) (err error) {
78	timeFormat := azureUtcFormat
79	match, err := regexp.Match(tzOffsetRegex, data)
80	if err != nil {
81		return err
82	} else if match {
83		timeFormat = rfc3339
84	}
85	t.Time, err = ParseTime(timeFormat, string(data))
86	return err
87}
88
89// String returns the Time formatted as an RFC3339 date-time string (i.e.,
90// 2006-01-02T15:04:05Z).
91func (t Time) String() string {
92	// Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does.
93	b, err := t.MarshalText()
94	if err != nil {
95		return ""
96	}
97	return string(b)
98}
99
100// ToTime returns a Time as a time.Time
101func (t Time) ToTime() time.Time {
102	return t.Time
103}
104