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	"bytes"
19	"encoding/binary"
20	"encoding/json"
21	"time"
22)
23
24// unixEpoch is the moment in time that should be treated as timestamp 0.
25var unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
26
27// UnixTime marshals and unmarshals a time that is represented as the number
28// of seconds (ignoring skip-seconds) since the Unix Epoch.
29type UnixTime time.Time
30
31// Duration returns the time as a Duration since the UnixEpoch.
32func (t UnixTime) Duration() time.Duration {
33	return time.Time(t).Sub(unixEpoch)
34}
35
36// NewUnixTimeFromSeconds creates a UnixTime as a number of seconds from the UnixEpoch.
37func NewUnixTimeFromSeconds(seconds float64) UnixTime {
38	return NewUnixTimeFromDuration(time.Duration(seconds * float64(time.Second)))
39}
40
41// NewUnixTimeFromNanoseconds creates a UnixTime as a number of nanoseconds from the UnixEpoch.
42func NewUnixTimeFromNanoseconds(nanoseconds int64) UnixTime {
43	return NewUnixTimeFromDuration(time.Duration(nanoseconds))
44}
45
46// NewUnixTimeFromDuration creates a UnixTime as a duration of time since the UnixEpoch.
47func NewUnixTimeFromDuration(dur time.Duration) UnixTime {
48	return UnixTime(unixEpoch.Add(dur))
49}
50
51// UnixEpoch retreives the moment considered the Unix Epoch. I.e. The time represented by '0'
52func UnixEpoch() time.Time {
53	return unixEpoch
54}
55
56// MarshalJSON preserves the UnixTime as a JSON number conforming to Unix Timestamp requirements.
57// (i.e. the number of seconds since midnight January 1st, 1970 not considering leap seconds.)
58func (t UnixTime) MarshalJSON() ([]byte, error) {
59	buffer := &bytes.Buffer{}
60	enc := json.NewEncoder(buffer)
61	err := enc.Encode(float64(time.Time(t).UnixNano()) / 1e9)
62	if err != nil {
63		return nil, err
64	}
65	return buffer.Bytes(), nil
66}
67
68// UnmarshalJSON reconstitures a UnixTime saved as a JSON number of the number of seconds since
69// midnight January 1st, 1970.
70func (t *UnixTime) UnmarshalJSON(text []byte) error {
71	dec := json.NewDecoder(bytes.NewReader(text))
72
73	var secondsSinceEpoch float64
74	if err := dec.Decode(&secondsSinceEpoch); err != nil {
75		return err
76	}
77
78	*t = NewUnixTimeFromSeconds(secondsSinceEpoch)
79
80	return nil
81}
82
83// MarshalText stores the number of seconds since the Unix Epoch as a textual floating point number.
84func (t UnixTime) MarshalText() ([]byte, error) {
85	cast := time.Time(t)
86	return cast.MarshalText()
87}
88
89// UnmarshalText populates a UnixTime with a value stored textually as a floating point number of seconds since the Unix Epoch.
90func (t *UnixTime) UnmarshalText(raw []byte) error {
91	var unmarshaled time.Time
92
93	if err := unmarshaled.UnmarshalText(raw); err != nil {
94		return err
95	}
96
97	*t = UnixTime(unmarshaled)
98	return nil
99}
100
101// MarshalBinary converts a UnixTime into a binary.LittleEndian float64 of nanoseconds since the epoch.
102func (t UnixTime) MarshalBinary() ([]byte, error) {
103	buf := &bytes.Buffer{}
104
105	payload := int64(t.Duration())
106
107	if err := binary.Write(buf, binary.LittleEndian, &payload); err != nil {
108		return nil, err
109	}
110
111	return buf.Bytes(), nil
112}
113
114// UnmarshalBinary converts a from a binary.LittleEndian float64 of nanoseconds since the epoch into a UnixTime.
115func (t *UnixTime) UnmarshalBinary(raw []byte) error {
116	var nanosecondsSinceEpoch int64
117
118	if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &nanosecondsSinceEpoch); err != nil {
119		return err
120	}
121	*t = NewUnixTimeFromNanoseconds(nanosecondsSinceEpoch)
122	return nil
123}
124