1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package v1
18
19import (
20	"encoding/json"
21	"time"
22
23	"github.com/google/gofuzz"
24)
25
26// Time is a wrapper around time.Time which supports correct
27// marshaling to YAML and JSON.  Wrappers are provided for many
28// of the factory methods that the time package offers.
29//
30// +protobuf.options.marshal=false
31// +protobuf.as=Timestamp
32// +protobuf.options.(gogoproto.goproto_stringer)=false
33type Time struct {
34	time.Time `protobuf:"-"`
35}
36
37// DeepCopyInto creates a deep-copy of the Time value.  The underlying time.Time
38// type is effectively immutable in the time API, so it is safe to
39// copy-by-assign, despite the presence of (unexported) Pointer fields.
40func (t *Time) DeepCopyInto(out *Time) {
41	*out = *t
42}
43
44// String returns the representation of the time.
45func (t Time) String() string {
46	return t.Time.String()
47}
48
49// NewTime returns a wrapped instance of the provided time
50func NewTime(time time.Time) Time {
51	return Time{time}
52}
53
54// Date returns the Time corresponding to the supplied parameters
55// by wrapping time.Date.
56func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time {
57	return Time{time.Date(year, month, day, hour, min, sec, nsec, loc)}
58}
59
60// Now returns the current local time.
61func Now() Time {
62	return Time{time.Now()}
63}
64
65// IsZero returns true if the value is nil or time is zero.
66func (t *Time) IsZero() bool {
67	if t == nil {
68		return true
69	}
70	return t.Time.IsZero()
71}
72
73// Before reports whether the time instant t is before u.
74func (t *Time) Before(u *Time) bool {
75	return t.Time.Before(u.Time)
76}
77
78// Equal reports whether the time instant t is equal to u.
79func (t *Time) Equal(u *Time) bool {
80	if t == nil && u == nil {
81		return true
82	}
83	if t != nil && u != nil {
84		return t.Time.Equal(u.Time)
85	}
86	return false
87}
88
89// Unix returns the local time corresponding to the given Unix time
90// by wrapping time.Unix.
91func Unix(sec int64, nsec int64) Time {
92	return Time{time.Unix(sec, nsec)}
93}
94
95// Rfc3339Copy returns a copy of the Time at second-level precision.
96func (t Time) Rfc3339Copy() Time {
97	copied, _ := time.Parse(time.RFC3339, t.Format(time.RFC3339))
98	return Time{copied}
99}
100
101// UnmarshalJSON implements the json.Unmarshaller interface.
102func (t *Time) UnmarshalJSON(b []byte) error {
103	if len(b) == 4 && string(b) == "null" {
104		t.Time = time.Time{}
105		return nil
106	}
107
108	var str string
109	err := json.Unmarshal(b, &str)
110	if err != nil {
111		return err
112	}
113
114	pt, err := time.Parse(time.RFC3339, str)
115	if err != nil {
116		return err
117	}
118
119	t.Time = pt.Local()
120	return nil
121}
122
123// UnmarshalQueryParameter converts from a URL query parameter value to an object
124func (t *Time) UnmarshalQueryParameter(str string) error {
125	if len(str) == 0 {
126		t.Time = time.Time{}
127		return nil
128	}
129	// Tolerate requests from older clients that used JSON serialization to build query params
130	if len(str) == 4 && str == "null" {
131		t.Time = time.Time{}
132		return nil
133	}
134
135	pt, err := time.Parse(time.RFC3339, str)
136	if err != nil {
137		return err
138	}
139
140	t.Time = pt.Local()
141	return nil
142}
143
144// MarshalJSON implements the json.Marshaler interface.
145func (t Time) MarshalJSON() ([]byte, error) {
146	if t.IsZero() {
147		// Encode unset/nil objects as JSON's "null".
148		return []byte("null"), nil
149	}
150
151	return json.Marshal(t.UTC().Format(time.RFC3339))
152}
153
154// OpenAPISchemaType is used by the kube-openapi generator when constructing
155// the OpenAPI spec of this type.
156//
157// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
158func (_ Time) OpenAPISchemaType() []string { return []string{"string"} }
159
160// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
161// the OpenAPI spec of this type.
162func (_ Time) OpenAPISchemaFormat() string { return "date-time" }
163
164// MarshalQueryParameter converts to a URL query parameter value
165func (t Time) MarshalQueryParameter() (string, error) {
166	if t.IsZero() {
167		// Encode unset/nil objects as an empty string
168		return "", nil
169	}
170
171	return t.UTC().Format(time.RFC3339), nil
172}
173
174// Fuzz satisfies fuzz.Interface.
175func (t *Time) Fuzz(c fuzz.Continue) {
176	if t == nil {
177		return
178	}
179	// Allow for about 1000 years of randomness.  Leave off nanoseconds
180	// because JSON doesn't represent them so they can't round-trip
181	// properly.
182	t.Time = time.Unix(c.Rand.Int63n(1000*365*24*60*60), 0)
183}
184
185var _ fuzz.Interface = &Time{}
186