1// +build go1.7 2 3package date 4 5// Copyright 2017 Microsoft Corporation 6// 7// Licensed under the Apache License, Version 2.0 (the "License"); 8// you may not use this file except in compliance with the License. 9// You may obtain a copy of the License at 10// 11// http://www.apache.org/licenses/LICENSE-2.0 12// 13// Unless required by applicable law or agreed to in writing, software 14// distributed under the License is distributed on an "AS IS" BASIS, 15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16// See the License for the specific language governing permissions and 17// limitations under the License. 18 19import ( 20 "bytes" 21 "encoding/binary" 22 "encoding/json" 23 "fmt" 24 "math" 25 "testing" 26 "time" 27) 28 29func ExampleUnixTime_MarshalJSON() { 30 epoch := UnixTime(UnixEpoch()) 31 text, _ := json.Marshal(epoch) 32 fmt.Print(string(text)) 33 // Output: 0 34} 35 36func ExampleUnixTime_UnmarshalJSON() { 37 var myTime UnixTime 38 json.Unmarshal([]byte("1.3e2"), &myTime) 39 fmt.Printf("%v", time.Time(myTime)) 40 // Output: 1970-01-01 00:02:10 +0000 UTC 41} 42 43func TestUnixTime_MarshalJSON(t *testing.T) { 44 testCases := []time.Time{ 45 UnixEpoch().Add(-1 * time.Second), // One second befote the Unix Epoch 46 time.Date(2017, time.April, 14, 20, 27, 47, 0, time.UTC), // The time this test was written 47 UnixEpoch(), 48 time.Date(1800, 01, 01, 0, 0, 0, 0, time.UTC), 49 time.Date(2200, 12, 29, 00, 01, 37, 82, time.UTC), 50 } 51 52 for _, tc := range testCases { 53 t.Run(tc.String(), func(subT *testing.T) { 54 var actual, expected float64 55 var marshaled []byte 56 57 target := UnixTime(tc) 58 expected = float64(target.Duration().Nanoseconds()) / 1e9 59 60 if temp, err := json.Marshal(target); err == nil { 61 marshaled = temp 62 } else { 63 subT.Error(err) 64 return 65 } 66 67 dec := json.NewDecoder(bytes.NewReader(marshaled)) 68 if err := dec.Decode(&actual); err != nil { 69 subT.Error(err) 70 return 71 } 72 73 diff := math.Abs(actual - expected) 74 subT.Logf("\ngot :\t%g\nwant:\t%g\ndiff:\t%g", actual, expected, diff) 75 if diff > 1e-9 { //Must be within 1 nanosecond of one another 76 subT.Fail() 77 } 78 }) 79 } 80} 81 82func TestUnixTime_UnmarshalJSON(t *testing.T) { 83 testCases := []struct { 84 text string 85 expected time.Time 86 }{ 87 {"1", UnixEpoch().Add(time.Second)}, 88 {"0", UnixEpoch()}, 89 {"1492203742", time.Date(2017, time.April, 14, 21, 02, 22, 0, time.UTC)}, // The time this test was written 90 {"-1", time.Date(1969, time.December, 31, 23, 59, 59, 0, time.UTC)}, 91 {"1.5", UnixEpoch().Add(1500 * time.Millisecond)}, 92 {"0e1", UnixEpoch()}, // See http://json.org for 'number' format definition. 93 {"1.3e+2", UnixEpoch().Add(130 * time.Second)}, 94 {"1.6E-10", UnixEpoch()}, // This is so small, it should get truncated into the UnixEpoch 95 {"2E-6", UnixEpoch().Add(2 * time.Microsecond)}, 96 {"1.289345e9", UnixEpoch().Add(1289345000 * time.Second)}, 97 {"1e-9", UnixEpoch().Add(time.Nanosecond)}, 98 } 99 100 for _, tc := range testCases { 101 t.Run(tc.text, func(subT *testing.T) { 102 var rehydrated UnixTime 103 if err := json.Unmarshal([]byte(tc.text), &rehydrated); err != nil { 104 subT.Error(err) 105 return 106 } 107 108 if time.Time(rehydrated) != tc.expected { 109 subT.Logf("\ngot: \t%v\nwant:\t%v\ndiff:\t%v", time.Time(rehydrated), tc.expected, time.Time(rehydrated).Sub(tc.expected)) 110 subT.Fail() 111 } 112 }) 113 } 114} 115 116func TestUnixTime_JSONRoundTrip(t *testing.T) { 117 testCases := []time.Time{ 118 UnixEpoch(), 119 time.Date(2005, time.November, 5, 0, 0, 0, 0, time.UTC), // The day V for Vendetta (film) was released. 120 UnixEpoch().Add(-6 * time.Second), 121 UnixEpoch().Add(800 * time.Hour), 122 UnixEpoch().Add(time.Nanosecond), 123 time.Date(2015, time.September, 05, 4, 30, 12, 9992, time.UTC), 124 } 125 126 for _, tc := range testCases { 127 t.Run(tc.String(), func(subT *testing.T) { 128 subject := UnixTime(tc) 129 var marshaled []byte 130 if temp, err := json.Marshal(subject); err == nil { 131 marshaled = temp 132 } else { 133 subT.Error(err) 134 return 135 } 136 137 var unmarshaled UnixTime 138 if err := json.Unmarshal(marshaled, &unmarshaled); err != nil { 139 subT.Error(err) 140 } 141 142 actual := time.Time(unmarshaled) 143 diff := actual.Sub(tc) 144 subT.Logf("\ngot :\t%s\nwant:\t%s\ndiff:\t%s", actual.String(), tc.String(), diff.String()) 145 146 if diff > time.Duration(100) { // We lose some precision be working in floats. We shouldn't lose more than 100 nanoseconds. 147 subT.Fail() 148 } 149 }) 150 } 151} 152 153func TestUnixTime_MarshalBinary(t *testing.T) { 154 testCases := []struct { 155 expected int64 156 subject time.Time 157 }{ 158 {0, UnixEpoch()}, 159 {-15 * int64(time.Second), UnixEpoch().Add(-15 * time.Second)}, 160 {54, UnixEpoch().Add(54 * time.Nanosecond)}, 161 } 162 163 for _, tc := range testCases { 164 t.Run("", func(subT *testing.T) { 165 var marshaled []byte 166 167 if temp, err := UnixTime(tc.subject).MarshalBinary(); err == nil { 168 marshaled = temp 169 } else { 170 subT.Error(err) 171 return 172 } 173 174 var unmarshaled int64 175 if err := binary.Read(bytes.NewReader(marshaled), binary.LittleEndian, &unmarshaled); err != nil { 176 subT.Error(err) 177 return 178 } 179 180 if unmarshaled != tc.expected { 181 subT.Logf("\ngot: \t%d\nwant:\t%d", unmarshaled, tc.expected) 182 subT.Fail() 183 } 184 }) 185 } 186} 187 188func TestUnixTime_BinaryRoundTrip(t *testing.T) { 189 testCases := []time.Time{ 190 UnixEpoch(), 191 UnixEpoch().Add(800 * time.Minute), 192 UnixEpoch().Add(7 * time.Hour), 193 UnixEpoch().Add(-1 * time.Nanosecond), 194 } 195 196 for _, tc := range testCases { 197 t.Run(tc.String(), func(subT *testing.T) { 198 original := UnixTime(tc) 199 var marshaled []byte 200 201 if temp, err := original.MarshalBinary(); err == nil { 202 marshaled = temp 203 } else { 204 subT.Error(err) 205 return 206 } 207 208 var traveled UnixTime 209 if err := traveled.UnmarshalBinary(marshaled); err != nil { 210 subT.Error(err) 211 return 212 } 213 214 if traveled != original { 215 subT.Logf("\ngot: \t%s\nwant:\t%s", time.Time(original).String(), time.Time(traveled).String()) 216 subT.Fail() 217 } 218 }) 219 } 220} 221 222func TestUnixTime_MarshalText(t *testing.T) { 223 testCases := []time.Time{ 224 UnixEpoch(), 225 UnixEpoch().Add(45 * time.Second), 226 UnixEpoch().Add(time.Nanosecond), 227 UnixEpoch().Add(-100000 * time.Second), 228 } 229 230 for _, tc := range testCases { 231 expected, _ := tc.MarshalText() 232 t.Run("", func(subT *testing.T) { 233 var marshaled []byte 234 235 if temp, err := UnixTime(tc).MarshalText(); err == nil { 236 marshaled = temp 237 } else { 238 subT.Error(err) 239 return 240 } 241 242 if string(marshaled) != string(expected) { 243 subT.Logf("\ngot: \t%s\nwant:\t%s", string(marshaled), string(expected)) 244 subT.Fail() 245 } 246 }) 247 } 248} 249 250func TestUnixTime_TextRoundTrip(t *testing.T) { 251 testCases := []time.Time{ 252 UnixEpoch(), 253 UnixEpoch().Add(-1 * time.Nanosecond), 254 UnixEpoch().Add(1 * time.Nanosecond), 255 time.Date(2017, time.April, 17, 21, 00, 00, 00, time.UTC), 256 } 257 258 for _, tc := range testCases { 259 t.Run(tc.String(), func(subT *testing.T) { 260 unixTC := UnixTime(tc) 261 262 var marshaled []byte 263 264 if temp, err := unixTC.MarshalText(); err == nil { 265 marshaled = temp 266 } else { 267 subT.Error(err) 268 return 269 } 270 271 var unmarshaled UnixTime 272 if err := unmarshaled.UnmarshalText(marshaled); err != nil { 273 subT.Error(err) 274 return 275 } 276 277 if unmarshaled != unixTC { 278 t.Logf("\ngot: \t%s\nwant:\t%s", time.Time(unmarshaled).String(), tc.String()) 279 t.Fail() 280 } 281 }) 282 } 283} 284