1 use super::NaiveTime;
2 use crate::{FixedOffset, TimeDelta, Timelike};
3
4 #[test]
test_time_from_hms_milli()5 fn test_time_from_hms_milli() {
6 assert_eq!(
7 NaiveTime::from_hms_milli_opt(3, 5, 7, 0),
8 Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap())
9 );
10 assert_eq!(
11 NaiveTime::from_hms_milli_opt(3, 5, 7, 777),
12 Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_000_000).unwrap())
13 );
14 assert_eq!(
15 NaiveTime::from_hms_milli_opt(3, 5, 59, 1_999),
16 Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_000_000).unwrap())
17 );
18 assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 2_000), None);
19 assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 5_000), None); // overflow check
20 assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, u32::MAX), None);
21 }
22
23 #[test]
test_time_from_hms_micro()24 fn test_time_from_hms_micro() {
25 assert_eq!(
26 NaiveTime::from_hms_micro_opt(3, 5, 7, 0),
27 Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap())
28 );
29 assert_eq!(
30 NaiveTime::from_hms_micro_opt(3, 5, 7, 333),
31 Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 333_000).unwrap())
32 );
33 assert_eq!(
34 NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777),
35 Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_777_000).unwrap())
36 );
37 assert_eq!(
38 NaiveTime::from_hms_micro_opt(3, 5, 59, 1_999_999),
39 Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_999_000).unwrap())
40 );
41 assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 2_000_000), None);
42 assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 5_000_000), None); // overflow check
43 assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, u32::MAX), None);
44 }
45
46 #[test]
test_time_hms()47 fn test_time_hms() {
48 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().hour(), 3);
49 assert_eq!(
50 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(0),
51 Some(NaiveTime::from_hms_opt(0, 5, 7).unwrap())
52 );
53 assert_eq!(
54 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(23),
55 Some(NaiveTime::from_hms_opt(23, 5, 7).unwrap())
56 );
57 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(24), None);
58 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(u32::MAX), None);
59
60 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().minute(), 5);
61 assert_eq!(
62 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(0),
63 Some(NaiveTime::from_hms_opt(3, 0, 7).unwrap())
64 );
65 assert_eq!(
66 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(59),
67 Some(NaiveTime::from_hms_opt(3, 59, 7).unwrap())
68 );
69 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(60), None);
70 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(u32::MAX), None);
71
72 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().second(), 7);
73 assert_eq!(
74 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(0),
75 Some(NaiveTime::from_hms_opt(3, 5, 0).unwrap())
76 );
77 assert_eq!(
78 NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(59),
79 Some(NaiveTime::from_hms_opt(3, 5, 59).unwrap())
80 );
81 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(60), None);
82 assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(u32::MAX), None);
83 }
84
85 #[test]
test_time_add()86 fn test_time_add() {
87 macro_rules! check {
88 ($lhs:expr, $rhs:expr, $sum:expr) => {{
89 assert_eq!($lhs + $rhs, $sum);
90 //assert_eq!($rhs + $lhs, $sum);
91 }};
92 }
93
94 let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
95
96 check!(hmsm(3, 5, 59, 900), TimeDelta::zero(), hmsm(3, 5, 59, 900));
97 check!(hmsm(3, 5, 59, 900), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 6, 0, 0));
98 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-1800).unwrap(), hmsm(3, 5, 58, 500));
99 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-800).unwrap(), hmsm(3, 5, 59, 500));
100 check!(
101 hmsm(3, 5, 59, 1_300),
102 TimeDelta::try_milliseconds(-100).unwrap(),
103 hmsm(3, 5, 59, 1_200)
104 );
105 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 5, 59, 1_400));
106 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(800).unwrap(), hmsm(3, 6, 0, 100));
107 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(1800).unwrap(), hmsm(3, 6, 1, 100));
108 check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(86399).unwrap(), hmsm(3, 5, 58, 900)); // overwrap
109 check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(-86399).unwrap(), hmsm(3, 6, 0, 900));
110 check!(hmsm(3, 5, 59, 900), TimeDelta::try_days(12345).unwrap(), hmsm(3, 5, 59, 900));
111 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(1).unwrap(), hmsm(3, 5, 59, 300));
112 check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(-1).unwrap(), hmsm(3, 6, 0, 300));
113
114 // regression tests for #37
115 check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-990).unwrap(), hmsm(23, 59, 59, 10));
116 check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-9990).unwrap(), hmsm(23, 59, 50, 10));
117 }
118
119 #[test]
test_time_overflowing_add()120 fn test_time_overflowing_add() {
121 let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
122
123 assert_eq!(
124 hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(11).unwrap()),
125 (hmsm(14, 4, 5, 678), 0)
126 );
127 assert_eq!(
128 hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(23).unwrap()),
129 (hmsm(2, 4, 5, 678), 86_400)
130 );
131 assert_eq!(
132 hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(-7).unwrap()),
133 (hmsm(20, 4, 5, 678), -86_400)
134 );
135
136 // overflowing_add_signed with leap seconds may be counter-intuitive
137 assert_eq!(
138 hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(1).unwrap()),
139 (hmsm(3, 4, 59, 678), 86_400)
140 );
141 assert_eq!(
142 hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(-1).unwrap()),
143 (hmsm(3, 5, 0, 678), -86_400)
144 );
145 }
146
147 #[test]
test_time_addassignment()148 fn test_time_addassignment() {
149 let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
150 let mut time = hms(12, 12, 12);
151 time += TimeDelta::try_hours(10).unwrap();
152 assert_eq!(time, hms(22, 12, 12));
153 time += TimeDelta::try_hours(10).unwrap();
154 assert_eq!(time, hms(8, 12, 12));
155 }
156
157 #[test]
test_time_subassignment()158 fn test_time_subassignment() {
159 let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
160 let mut time = hms(12, 12, 12);
161 time -= TimeDelta::try_hours(10).unwrap();
162 assert_eq!(time, hms(2, 12, 12));
163 time -= TimeDelta::try_hours(10).unwrap();
164 assert_eq!(time, hms(16, 12, 12));
165 }
166
167 #[test]
test_time_sub()168 fn test_time_sub() {
169 macro_rules! check {
170 ($lhs:expr, $rhs:expr, $diff:expr) => {{
171 // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration`
172 assert_eq!($lhs.signed_duration_since($rhs), $diff);
173 assert_eq!($rhs.signed_duration_since($lhs), -$diff);
174 }};
175 }
176
177 let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap();
178
179 check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), TimeDelta::zero());
180 check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), TimeDelta::try_milliseconds(300).unwrap());
181 check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), TimeDelta::try_seconds(3600 + 60 + 1).unwrap());
182 check!(
183 hmsm(3, 5, 7, 200),
184 hmsm(2, 4, 6, 300),
185 TimeDelta::try_seconds(3600 + 60).unwrap() + TimeDelta::try_milliseconds(900).unwrap()
186 );
187
188 // treats the leap second as if it coincides with the prior non-leap second,
189 // as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence.
190 check!(hmsm(3, 6, 0, 200), hmsm(3, 5, 59, 1_800), TimeDelta::try_milliseconds(400).unwrap());
191 //check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), TimeDelta::try_milliseconds(1400).unwrap());
192 //check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), TimeDelta::try_milliseconds(1400).unwrap());
193
194 // additional equality: `time1 + duration = time2` is equivalent to
195 // `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second.
196 assert_eq!(hmsm(3, 5, 6, 800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200));
197 //assert_eq!(hmsm(3, 5, 6, 1_800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200));
198 }
199
200 #[test]
test_core_duration_ops()201 fn test_core_duration_ops() {
202 use core::time::Duration;
203
204 let mut t = NaiveTime::from_hms_opt(11, 34, 23).unwrap();
205 let same = t + Duration::ZERO;
206 assert_eq!(t, same);
207
208 t += Duration::new(3600, 0);
209 assert_eq!(t, NaiveTime::from_hms_opt(12, 34, 23).unwrap());
210
211 t -= Duration::new(7200, 0);
212 assert_eq!(t, NaiveTime::from_hms_opt(10, 34, 23).unwrap());
213 }
214
215 #[test]
test_time_fmt()216 fn test_time_fmt() {
217 assert_eq!(
218 format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 999).unwrap()),
219 "23:59:59.999"
220 );
221 assert_eq!(
222 format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap()),
223 "23:59:60"
224 );
225 assert_eq!(
226 format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_001).unwrap()),
227 "23:59:60.001"
228 );
229 assert_eq!(
230 format!("{}", NaiveTime::from_hms_micro_opt(0, 0, 0, 43210).unwrap()),
231 "00:00:00.043210"
232 );
233 assert_eq!(
234 format!("{}", NaiveTime::from_hms_nano_opt(0, 0, 0, 6543210).unwrap()),
235 "00:00:00.006543210"
236 );
237
238 // the format specifier should have no effect on `NaiveTime`
239 assert_eq!(
240 format!("{:30}", NaiveTime::from_hms_milli_opt(3, 5, 7, 9).unwrap()),
241 "03:05:07.009"
242 );
243 }
244
245 #[test]
test_time_from_str()246 fn test_time_from_str() {
247 // valid cases
248 let valid = [
249 "0:0:0",
250 "0:0:0.0000000",
251 "0:0:0.0000003",
252 " 4 : 3 : 2.1 ",
253 " 09:08:07 ",
254 " 09:08 ",
255 " 9:8:07 ",
256 "01:02:03",
257 "4:3:2.1",
258 "9:8:7",
259 "09:8:7",
260 "9:08:7",
261 "9:8:07",
262 "09:08:7",
263 "09:8:07",
264 "09:08:7",
265 "9:08:07",
266 "09:08:07",
267 "9:8:07.123",
268 "9:08:7.123",
269 "09:8:7.123",
270 "09:08:7.123",
271 "9:08:07.123",
272 "09:8:07.123",
273 "09:08:07.123",
274 "09:08:07.123",
275 "09:08:07.1234",
276 "09:08:07.12345",
277 "09:08:07.123456",
278 "09:08:07.1234567",
279 "09:08:07.12345678",
280 "09:08:07.123456789",
281 "09:08:07.1234567891",
282 "09:08:07.12345678912",
283 "23:59:60.373929310237",
284 ];
285 for &s in &valid {
286 eprintln!("test_time_parse_from_str valid {:?}", s);
287 let d = match s.parse::<NaiveTime>() {
288 Ok(d) => d,
289 Err(e) => panic!("parsing `{}` has failed: {}", s, e),
290 };
291 let s_ = format!("{:?}", d);
292 // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same
293 let d_ = match s_.parse::<NaiveTime>() {
294 Ok(d) => d,
295 Err(e) => {
296 panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e)
297 }
298 };
299 assert!(
300 d == d_,
301 "`{}` is parsed into `{:?}`, but reparsed result \
302 `{:?}` does not match",
303 s,
304 d,
305 d_
306 );
307 }
308
309 // some invalid cases
310 // since `ParseErrorKind` is private, all we can do is to check if there was an error
311 let invalid = [
312 "", // empty
313 "x", // invalid
314 "15", // missing data
315 "15:8:", // trailing colon
316 "15:8:x", // invalid data
317 "15:8:9x", // invalid data
318 "23:59:61", // invalid second (out of bounds)
319 "23:54:35 GMT", // invalid (timezone non-sensical for NaiveTime)
320 "23:54:35 +0000", // invalid (timezone non-sensical for NaiveTime)
321 "1441497364.649", // valid datetime, not a NaiveTime
322 "+1441497364.649", // valid datetime, not a NaiveTime
323 "+1441497364", // valid datetime, not a NaiveTime
324 "001:02:03", // invalid hour
325 "01:002:03", // invalid minute
326 "01:02:003", // invalid second
327 "12:34:56.x", // invalid fraction
328 "12:34:56. 0", // invalid fraction format
329 "09:08:00000000007", // invalid second / invalid fraction format
330 ];
331 for &s in &invalid {
332 eprintln!("test_time_parse_from_str invalid {:?}", s);
333 assert!(s.parse::<NaiveTime>().is_err());
334 }
335 }
336
337 #[test]
test_time_parse_from_str()338 fn test_time_parse_from_str() {
339 let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap();
340 assert_eq!(
341 NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
342 Ok(hms(12, 34, 56))
343 ); // ignore date and offset
344 assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0)));
345 assert_eq!(NaiveTime::parse_from_str("12:59 \n\t PM", "%H:%M \n\t %P"), Ok(hms(12, 59, 0)));
346 assert_eq!(NaiveTime::parse_from_str("\t\t12:59\tPM\t", "\t\t%H:%M\t%P\t"), Ok(hms(12, 59, 0)));
347 assert_eq!(
348 NaiveTime::parse_from_str("\t\t1259\t\tPM\t", "\t\t%H%M\t\t%P\t"),
349 Ok(hms(12, 59, 0))
350 );
351 assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M\t%P").is_ok());
352 assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_ok());
353 assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_ok());
354 assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err());
355 }
356
357 #[test]
test_overflowing_offset()358 fn test_overflowing_offset() {
359 let hmsm = |h, m, s, n| NaiveTime::from_hms_milli_opt(h, m, s, n).unwrap();
360
361 let positive_offset = FixedOffset::east_opt(4 * 60 * 60).unwrap();
362 // regular time
363 let t = hmsm(5, 6, 7, 890);
364 assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(9, 6, 7, 890), 0));
365 assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(1, 6, 7, 890), 0));
366 // leap second is preserved, and wrap to next day
367 let t = hmsm(23, 59, 59, 1_000);
368 assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(3, 59, 59, 1_000), 1));
369 assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(19, 59, 59, 1_000), 0));
370 // wrap to previous day
371 let t = hmsm(1, 2, 3, 456);
372 assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(21, 2, 3, 456), -1));
373 // an odd offset
374 let negative_offset = FixedOffset::west_opt(((2 * 60) + 3) * 60 + 4).unwrap();
375 let t = hmsm(5, 6, 7, 890);
376 assert_eq!(t.overflowing_add_offset(negative_offset), (hmsm(3, 3, 3, 890), 0));
377 assert_eq!(t.overflowing_sub_offset(negative_offset), (hmsm(7, 9, 11, 890), 0));
378
379 assert_eq!(t.overflowing_add_offset(positive_offset).0, t + positive_offset);
380 assert_eq!(t.overflowing_sub_offset(positive_offset).0, t - positive_offset);
381 }
382
383 #[test]
384 #[cfg(feature = "rkyv-validation")]
test_rkyv_validation()385 fn test_rkyv_validation() {
386 let t_min = NaiveTime::MIN;
387 let bytes = rkyv::to_bytes::<_, 8>(&t_min).unwrap();
388 assert_eq!(rkyv::from_bytes::<NaiveTime>(&bytes).unwrap(), t_min);
389
390 let t_max = NaiveTime::MAX;
391 let bytes = rkyv::to_bytes::<_, 8>(&t_max).unwrap();
392 assert_eq!(rkyv::from_bytes::<NaiveTime>(&bytes).unwrap(), t_max);
393 }
394