Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,28 @@
use time::macros::date;
fn main() {
let _ = date!(+1_000_000-01-01);
let _ = date!(10_000-01-01);
let _ = date!(2021-W 60-1);
let _ = date!(2021-W60-1);
let _ = date!(2021-W 01-0);
let _ = date!(2021-W01-0);
let _ = date!(2021-W 01-8);
let _ = date!(2021-W01-8);
let _ = date!(2021-00-01);
let _ = date!(2021-13-01);
let _ = date!(2021-01-00);
let _ = date!(2021-01-32);
let _ = date!(2021-000);
let _ = date!(2021-366);
let _ = date!(0a);
let _ = date!(2021:);
let _ = date!(2021-W 0a);
let _ = date!(2021-W0a);
let _ = date!(2021-W 01:);
let _ = date!(2021-W01:);
let _ = date!(2021-W 01-0a);
let _ = date!(2021-W01-0a);
let _ = date!(2021-0a);
let _ = date!(2021-01-0a);
}
@@ -0,0 +1,143 @@
error: invalid component: year was 1000000
--> tests/integration/compile-fail/invalid_date.rs:4:19
|
4 | let _ = date!(+1_000_000-01-01);
| ^^^^^^^^^^
error: years with more than four digits must have an explicit sign
--> tests/integration/compile-fail/invalid_date.rs:5:19
|
5 | let _ = date!(10_000-01-01);
| ^^^^^^
error: invalid component: week was 60
--> tests/integration/compile-fail/invalid_date.rs:6:24
|
6 | let _ = date!(2021-W 60-1);
| ^^^^
error: invalid component: week was 60
--> tests/integration/compile-fail/invalid_date.rs:7:24
|
7 | let _ = date!(2021-W60-1);
| ^^^
error: invalid component: day was 0
--> tests/integration/compile-fail/invalid_date.rs:8:29
|
8 | let _ = date!(2021-W 01-0);
| ^
error: invalid component: day was 0
--> tests/integration/compile-fail/invalid_date.rs:9:28
|
9 | let _ = date!(2021-W01-0);
| ^
error: invalid component: day was 8
--> tests/integration/compile-fail/invalid_date.rs:10:29
|
10 | let _ = date!(2021-W 01-8);
| ^
error: invalid component: day was 8
--> tests/integration/compile-fail/invalid_date.rs:11:28
|
11 | let _ = date!(2021-W01-8);
| ^
error: invalid component: month was 0
--> tests/integration/compile-fail/invalid_date.rs:12:24
|
12 | let _ = date!(2021-00-01);
| ^^
error: invalid component: month was 13
--> tests/integration/compile-fail/invalid_date.rs:13:24
|
13 | let _ = date!(2021-13-01);
| ^^
error: invalid component: day was 0
--> tests/integration/compile-fail/invalid_date.rs:14:27
|
14 | let _ = date!(2021-01-00);
| ^^
error: invalid component: day was 32
--> tests/integration/compile-fail/invalid_date.rs:15:27
|
15 | let _ = date!(2021-01-32);
| ^^
error: invalid component: ordinal was 0
--> tests/integration/compile-fail/invalid_date.rs:16:24
|
16 | let _ = date!(2021-000);
| ^^^
error: invalid component: ordinal was 366
--> tests/integration/compile-fail/invalid_date.rs:17:24
|
17 | let _ = date!(2021-366);
| ^^^
error: invalid component: year was 0a
--> tests/integration/compile-fail/invalid_date.rs:18:19
|
18 | let _ = date!(0a);
| ^^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_date.rs:19:23
|
19 | let _ = date!(2021:);
| ^
error: invalid component: week was 0a
--> tests/integration/compile-fail/invalid_date.rs:20:26
|
20 | let _ = date!(2021-W 0a);
| ^^
error: invalid component: week was 0a
--> tests/integration/compile-fail/invalid_date.rs:21:24
|
21 | let _ = date!(2021-W0a);
| ^^^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_date.rs:22:28
|
22 | let _ = date!(2021-W 01:);
| ^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_date.rs:23:27
|
23 | let _ = date!(2021-W01:);
| ^
error: invalid component: day was 0a
--> tests/integration/compile-fail/invalid_date.rs:24:29
|
24 | let _ = date!(2021-W 01-0a);
| ^^
error: invalid component: day was 0a
--> tests/integration/compile-fail/invalid_date.rs:25:28
|
25 | let _ = date!(2021-W01-0a);
| ^^
error: invalid component: month or ordinal was 0a
--> tests/integration/compile-fail/invalid_date.rs:26:24
|
26 | let _ = date!(2021-0a);
| ^^
error: invalid component: day was 0a
--> tests/integration/compile-fail/invalid_date.rs:27:27
|
27 | let _ = date!(2021-01-0a);
| ^^
@@ -0,0 +1,8 @@
use time::macros::datetime;
fn main() {
let _ = datetime!(2021-000 0:00);
let _ = datetime!(2021-001 24:00);
let _ = datetime!(2021-001 0:00 0);
let _ = datetime!(2021-001 0:00 UTC x);
}
@@ -0,0 +1,23 @@
error: invalid component: ordinal was 0
--> tests/integration/compile-fail/invalid_datetime.rs:4:28
|
4 | let _ = datetime!(2021-000 0:00);
| ^^^
error: invalid component: hour was 24
--> tests/integration/compile-fail/invalid_datetime.rs:5:32
|
5 | let _ = datetime!(2021-001 24:00);
| ^^
error: unexpected token: 0
--> tests/integration/compile-fail/invalid_datetime.rs:6:37
|
6 | let _ = datetime!(2021-001 0:00 0);
| ^
error: unexpected token: x
--> tests/integration/compile-fail/invalid_datetime.rs:7:41
|
7 | let _ = datetime!(2021-001 0:00 UTC x);
| ^
@@ -0,0 +1,46 @@
use time::macros::format_description;
fn main() {
let _ = format_description!();
let _ = format_description!("[]");
let _ = format_description!("[foo]");
let _ = format_description!("[");
let _ = format_description!("[hour foo]");
let _ = format_description!("" x);
let _ = format_description!(x);
let _ = format_description!(0);
let _ = format_description!({});
let _ = format_description!("[ invalid ]");
let _ = format_description!("[");
let _ = format_description!("[ ");
let _ = format_description!("[]");
let _ = format_description!("[day sign:mandatory]");
let _ = format_description!("[day sign:]");
let _ = format_description!("[day :mandatory]");
let _ = format_description!("[day sign:mandatory");
let _ = format_description!("[day padding:invalid]");
let _ = format_description!(version);
let _ = format_description!(version "");
let _ = format_description!(version =);
let _ = format_description!(version = 0);
let _ = format_description!(version = 1);
let _ = format_description!(version = 4);
let _ = format_description!(version = two);
let _ = format_description!(version = 2, r"\a");
let _ = format_description!(version = 2, r"\");
let _ = format_description!(version = 2, "[year foo]");
let _ = format_description!(version = 2, "[optional[]]");
let _ = format_description!(version = 2, "[first[]]");
let _ = format_description!(version = 2, "[optional []");
let _ = format_description!(version = 2, "[first []");
let _ = format_description!(version = 2, "[optional [");
let _ = format_description!(version = 2, "[optional [[year");
let _ = format_description!(version = 2, "[optional ");
let _ = format_description!("[ignore]");
let _ = format_description!("[ignore count:0]");
}
@@ -0,0 +1,229 @@
error: expected string literal
--> tests/integration/compile-fail/invalid_format_description.rs:4:13
|
4 | let _ = format_description!();
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected component name
--> tests/integration/compile-fail/invalid_format_description.rs:5:33
|
5 | let _ = format_description!("[]");
| ^^^^
error: invalid component
--> tests/integration/compile-fail/invalid_format_description.rs:6:33
|
6 | let _ = format_description!("[foo]");
| ^^^^^^^
error: expected component name
--> tests/integration/compile-fail/invalid_format_description.rs:7:33
|
7 | let _ = format_description!("[");
| ^^^
error: modifier must be of the form `key:value`
--> tests/integration/compile-fail/invalid_format_description.rs:8:33
|
8 | let _ = format_description!("[hour foo]");
| ^^^^^^^^^^^^
error: unexpected token: x
--> tests/integration/compile-fail/invalid_format_description.rs:9:36
|
9 | let _ = format_description!("" x);
| ^
error: expected string literal
--> tests/integration/compile-fail/invalid_format_description.rs:10:33
|
10 | let _ = format_description!(x);
| ^
error: expected string literal
--> tests/integration/compile-fail/invalid_format_description.rs:11:33
|
11 | let _ = format_description!(0);
| ^
error: expected string literal
--> tests/integration/compile-fail/invalid_format_description.rs:12:33
|
12 | let _ = format_description!({});
| ^^
error: invalid component
--> tests/integration/compile-fail/invalid_format_description.rs:14:33
|
14 | let _ = format_description!("[ invalid ]");
| ^^^^^^^^^^^^^
error: expected component name
--> tests/integration/compile-fail/invalid_format_description.rs:15:33
|
15 | let _ = format_description!("[");
| ^^^
error: expected component name
--> tests/integration/compile-fail/invalid_format_description.rs:16:33
|
16 | let _ = format_description!("[ ");
| ^^^^
error: expected component name
--> tests/integration/compile-fail/invalid_format_description.rs:17:33
|
17 | let _ = format_description!("[]");
| ^^^^
error: invalid modifier key
--> tests/integration/compile-fail/invalid_format_description.rs:18:33
|
18 | let _ = format_description!("[day sign:mandatory]");
| ^^^^^^^^^^^^^^^^^^^^^^
error: expected modifier value
--> tests/integration/compile-fail/invalid_format_description.rs:19:33
|
19 | let _ = format_description!("[day sign:]");
| ^^^^^^^^^^^^^
error: expected modifier key
--> tests/integration/compile-fail/invalid_format_description.rs:20:33
|
20 | let _ = format_description!("[day :mandatory]");
| ^^^^^^^^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:21:33
|
21 | let _ = format_description!("[day sign:mandatory");
| ^^^^^^^^^^^^^^^^^^^^^
error: invalid modifier value
--> tests/integration/compile-fail/invalid_format_description.rs:22:33
|
22 | let _ = format_description!("[day padding:invalid]");
| ^^^^^^^^^^^^^^^^^^^^^^^
error: expected `=`
--> tests/integration/compile-fail/invalid_format_description.rs:24:13
|
24 | let _ = format_description!(version);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected `=`
--> tests/integration/compile-fail/invalid_format_description.rs:25:41
|
25 | let _ = format_description!(version "");
| ^^
error: expected 1, 2, or 3
--> tests/integration/compile-fail/invalid_format_description.rs:26:13
|
26 | let _ = format_description!(version =);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid format description version
--> tests/integration/compile-fail/invalid_format_description.rs:27:43
|
27 | let _ = format_description!(version = 0);
| ^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_format_description.rs:28:13
|
28 | let _ = format_description!(version = 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid format description version
--> tests/integration/compile-fail/invalid_format_description.rs:29:43
|
29 | let _ = format_description!(version = 4);
| ^
error: expected 1, 2, or 3
--> tests/integration/compile-fail/invalid_format_description.rs:30:43
|
30 | let _ = format_description!(version = two);
| ^^^
error: invalid escape sequence
--> tests/integration/compile-fail/invalid_format_description.rs:32:46
|
32 | let _ = format_description!(version = 2, r"\a");
| ^^^^^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_format_description.rs:33:46
|
33 | let _ = format_description!(version = 2, r"\");
| ^^^^
error: modifier must be of the form `key:value`
--> tests/integration/compile-fail/invalid_format_description.rs:35:46
|
35 | let _ = format_description!(version = 2, "[year foo]");
| ^^^^^^^^^^^^
error: missing leading whitespace before nested format description
--> tests/integration/compile-fail/invalid_format_description.rs:36:46
|
36 | let _ = format_description!(version = 2, "[optional[]]");
| ^^^^^^^^^^^^^^
error: missing leading whitespace before nested format description
--> tests/integration/compile-fail/invalid_format_description.rs:37:46
|
37 | let _ = format_description!(version = 2, "[first[]]");
| ^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:38:46
|
38 | let _ = format_description!(version = 2, "[optional []");
| ^^^^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:39:46
|
39 | let _ = format_description!(version = 2, "[first []");
| ^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:40:46
|
40 | let _ = format_description!(version = 2, "[optional [");
| ^^^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:41:46
|
41 | let _ = format_description!(version = 2, "[optional [[year");
| ^^^^^^^^^^^^^^^^^^
error: unclosed bracket
--> tests/integration/compile-fail/invalid_format_description.rs:42:46
|
42 | let _ = format_description!(version = 2, "[optional ");
| ^^^^^^^^^^^^
error: missing required modifier
--> tests/integration/compile-fail/invalid_format_description.rs:44:33
|
44 | let _ = format_description!("[ignore]");
| ^^^^^^^^^^
error: invalid modifier value
--> tests/integration/compile-fail/invalid_format_description.rs:45:33
|
45 | let _ = format_description!("[ignore count:0]");
| ^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,12 @@
use time::macros::offset;
fn main() {
let _ = offset!(+26);
let _ = offset!(+0:60);
let _ = offset!(+0:00:60);
let _ = offset!(0);
let _ = offset!();
let _ = offset!(+0a);
let _ = offset!(+0:0a);
let _ = offset!(+0:00:0a);
}
@@ -0,0 +1,49 @@
error: invalid component: hour was 26
--> tests/integration/compile-fail/invalid_offset.rs:4:22
|
4 | let _ = offset!(+26);
| ^^
error: invalid component: minute was 60
--> tests/integration/compile-fail/invalid_offset.rs:5:24
|
5 | let _ = offset!(+0:60);
| ^^
error: invalid component: second was 60
--> tests/integration/compile-fail/invalid_offset.rs:6:27
|
6 | let _ = offset!(+0:00:60);
| ^^
error: unexpected token: 0
--> tests/integration/compile-fail/invalid_offset.rs:7:21
|
7 | let _ = offset!(0);
| ^
error: missing component: sign
--> tests/integration/compile-fail/invalid_offset.rs:8:13
|
8 | let _ = offset!();
| ^^^^^^^^^
|
= note: this error originates in the macro `offset` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid component: hour was 0a
--> tests/integration/compile-fail/invalid_offset.rs:9:22
|
9 | let _ = offset!(+0a);
| ^^
error: invalid component: minute was 0a
--> tests/integration/compile-fail/invalid_offset.rs:10:24
|
10 | let _ = offset!(+0:0a);
| ^^
error: invalid component: second was 0a
--> tests/integration/compile-fail/invalid_offset.rs:11:27
|
11 | let _ = offset!(+0:00:0a);
| ^^
@@ -0,0 +1,16 @@
use time::serde;
serde::format_description!(); // unexpected end of input
serde::format_description!("bad string", OffsetDateTime, "[year] [month]"); // module name is not ident
serde::format_description!(my_format: OffsetDateTime, "[year] [month]"); // not a comma
serde::format_description!(my_format,); // missing formattable and string
serde::format_description!(my_format, "[year] [month]"); // missing formattable
serde::format_description!(OffsetDateTime, "[year] [month]"); // missing ident
serde::format_description!(my_format, OffsetDateTime); // missing string format
serde::format_description!(my_format, OffsetDateTime,); // missing string format
serde::format_description!(my_format, OffsetDateTime "[year] [month]"); // missing comma
serde::format_description!(my_format, OffsetDateTime : "[year] [month]"); // not a comma
serde::format_description!(my_format, OffsetDateTime, "[bad]"); // bad component name
serde::format_description!(my_format, OffsetDateTime, not_string); // not in scope
fn main() {}
@@ -0,0 +1,79 @@
error: unexpected end of input
--> tests/integration/compile-fail/invalid_serializer.rs:3:1
|
3 | serde::format_description!(); // unexpected end of input
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `serde::format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected token: "bad string"
--> tests/integration/compile-fail/invalid_serializer.rs:4:28
|
4 | serde::format_description!("bad string", OffsetDateTime, "[year] [month]"); // module name is not ident
| ^^^^^^^^^^^^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_serializer.rs:5:37
|
5 | serde::format_description!(my_format: OffsetDateTime, "[year] [month]"); // not a comma
| ^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_serializer.rs:6:1
|
6 | serde::format_description!(my_format,); // missing formattable and string
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `serde::format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected token: "[year] [month]"
--> tests/integration/compile-fail/invalid_serializer.rs:7:39
|
7 | serde::format_description!(my_format, "[year] [month]"); // missing formattable
| ^^^^^^^^^^^^^^^^
error: unexpected token: "[year] [month]"
--> tests/integration/compile-fail/invalid_serializer.rs:8:44
|
8 | serde::format_description!(OffsetDateTime, "[year] [month]"); // missing ident
| ^^^^^^^^^^^^^^^^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_serializer.rs:9:1
|
9 | serde::format_description!(my_format, OffsetDateTime); // missing string format
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `serde::format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected end of input
--> tests/integration/compile-fail/invalid_serializer.rs:10:1
|
10 | serde::format_description!(my_format, OffsetDateTime,); // missing string format
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `serde::format_description` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected token: "[year] [month]"
--> tests/integration/compile-fail/invalid_serializer.rs:11:54
|
11 | serde::format_description!(my_format, OffsetDateTime "[year] [month]"); // missing comma
| ^^^^^^^^^^^^^^^^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_serializer.rs:12:54
|
12 | serde::format_description!(my_format, OffsetDateTime : "[year] [month]"); // not a comma
| ^
error: invalid component
--> tests/integration/compile-fail/invalid_serializer.rs:13:55
|
13 | serde::format_description!(my_format, OffsetDateTime, "[bad]"); // bad component name
| ^^^^^^^
error[E0425]: cannot find value `not_string` in this scope
--> tests/integration/compile-fail/invalid_serializer.rs:14:55
|
14 | serde::format_description!(my_format, OffsetDateTime, not_string); // not in scope
| ^^^^^^^^^^ not found in this scope
@@ -0,0 +1,17 @@
use time::macros::time;
fn main() {
let _ = time!(24:00);
let _ = time!(0:60);
let _ = time!(0:00:60);
let _ = time!(x);
let _ = time!(0:00:00 x);
let _ = time!("");
let _ = time!(0:);
let _ = time!(0,);
let _ = time!(0:00:0a);
let _ = time!(0:00 pm);
let _ = time!(0);
let _ = time!(0 pm);
let _ = time!(1 am :);
}
@@ -0,0 +1,81 @@
error: invalid component: hour was 24
--> tests/integration/compile-fail/invalid_time.rs:4:19
|
4 | let _ = time!(24:00);
| ^^
error: invalid component: minute was 60
--> tests/integration/compile-fail/invalid_time.rs:5:21
|
5 | let _ = time!(0:60);
| ^^
error: invalid component: second was 60
--> tests/integration/compile-fail/invalid_time.rs:6:24
|
6 | let _ = time!(0:00:60);
| ^^
error: unexpected token: x
--> tests/integration/compile-fail/invalid_time.rs:7:19
|
7 | let _ = time!(x);
| ^
error: unexpected token: x
--> tests/integration/compile-fail/invalid_time.rs:8:27
|
8 | let _ = time!(0:00:00 x);
| ^
error: invalid component: hour was ""
--> tests/integration/compile-fail/invalid_time.rs:9:19
|
9 | let _ = time!("");
| ^^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_time.rs:10:13
|
10 | let _ = time!(0:);
| ^^^^^^^^^
|
= note: this error originates in the macro `time` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected token: ,
--> tests/integration/compile-fail/invalid_time.rs:11:20
|
11 | let _ = time!(0,);
| ^
error: invalid component: second was 0a
--> tests/integration/compile-fail/invalid_time.rs:12:24
|
12 | let _ = time!(0:00:0a);
| ^^
error: invalid component: hour was 0
--> tests/integration/compile-fail/invalid_time.rs:13:19
|
13 | let _ = time!(0:00 pm);
| ^^^^^^^
error: unexpected end of input
--> tests/integration/compile-fail/invalid_time.rs:14:13
|
14 | let _ = time!(0);
| ^^^^^^^^
|
= note: this error originates in the macro `time` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid component: hour was 0
--> tests/integration/compile-fail/invalid_time.rs:15:19
|
15 | let _ = time!(0 pm);
| ^^^^
error: unexpected token: :
--> tests/integration/compile-fail/invalid_time.rs:16:24
|
16 | let _ = time!(1 am :);
| ^
@@ -0,0 +1,9 @@
use time::macros::utc_datetime;
fn main() {
let _ = utc_datetime!(2021-000 0:00);
let _ = utc_datetime!(2021-001 24:00);
let _ = utc_datetime!(2021-001 0:00 0);
let _ = utc_datetime!(2021-001 0:00 UTC);
let _ = utc_datetime!(2021-001 0:00 UTC x);
}
@@ -0,0 +1,29 @@
error: invalid component: ordinal was 0
--> tests/integration/compile-fail/invalid_utc_datetime.rs:4:32
|
4 | let _ = utc_datetime!(2021-000 0:00);
| ^^^
error: invalid component: hour was 24
--> tests/integration/compile-fail/invalid_utc_datetime.rs:5:36
|
5 | let _ = utc_datetime!(2021-001 24:00);
| ^^
error: unexpected token: 0
--> tests/integration/compile-fail/invalid_utc_datetime.rs:6:41
|
6 | let _ = utc_datetime!(2021-001 0:00 0);
| ^
error: unexpected token: UTC
--> tests/integration/compile-fail/invalid_utc_datetime.rs:7:41
|
7 | let _ = utc_datetime!(2021-001 0:00 UTC);
| ^^^
error: unexpected token: UTC
--> tests/integration/compile-fail/invalid_utc_datetime.rs:8:41
|
8 | let _ = utc_datetime!(2021-001 0:00 UTC x);
| ^^^
@@ -0,0 +1,7 @@
use rstest::rstest;
use time::unit::*;
#[rstest]
fn issue_749() {
assert_eq!(Nanosecond::per(Second), 1_000_000_000u32);
}
File diff suppressed because it is too large Load Diff
+266
View File
@@ -0,0 +1,266 @@
use std::vec::Vec;
use rstest::rstest;
use time::Date;
use time::iter::DateIter;
use time::macros::date;
const AFTER_MIN: Date = Date::MIN.next_day().unwrap();
const BEFORE_MAX: Date = Date::MAX.previous_day().unwrap();
#[rstest]
#[case(
date!(2019-01-01),
date!(2019-01-03),
vec![date!(2019-01-01), date!(2019-01-02), date!(2019-01-03)]
)]
#[case(date!(2019-01-01), date!(2019-01-01), vec![date!(2019-01-01)])]
#[case(date!(2019-01-03), date!(2019-01-01), vec![])]
#[case(
date!(2019-12-30),
date!(2020-01-02),
vec![date!(2019-12-30), date!(2019-12-31), date!(2020-01-01), date!(2020-01-02)]
)]
#[case(Date::MIN, AFTER_MIN, vec![Date::MIN, AFTER_MIN])]
#[case(BEFORE_MAX, Date::MAX, vec![BEFORE_MAX, Date::MAX])]
fn forward(#[case] start: Date, #[case] end: Date, #[case] expected: Vec<Date>) {
let results: Vec<_> = start.iter_to(end).collect();
assert_eq!(results, expected);
}
#[rstest]
fn double_ended() {
let mut iter = date!(2019-01-01).iter_to(date!(2019-01-05));
assert_eq!(iter.next(), Some(date!(2019-01-01)));
assert_eq!(iter.next_back(), Some(date!(2019-01-05)));
assert_eq!(iter.next_back(), Some(date!(2019-01-04)));
assert_eq!(iter.next(), Some(date!(2019-01-02)));
assert_eq!(iter.next(), Some(date!(2019-01-03)));
assert_eq!(iter.next(), None);
}
#[rstest]
fn double_ended_to_self() {
let d = date!(2019-06-15);
let mut iter = d.iter_to(d);
assert_eq!(iter.next_back(), Some(d));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
}
#[rstest]
fn new() {
let mut iter = DateIter::new(date!(2020-01-01), date!(2020-01-03));
assert_eq!(iter.next(), Some(date!(2020-01-01)));
assert_eq!(iter.next(), Some(date!(2020-01-02)));
assert_eq!(iter.next(), Some(date!(2020-01-03)));
assert_eq!(iter.next(), None);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), (5, Some(5)))]
#[case(date!(2019-01-05), date!(2019-01-01), (0, Some(0)))]
#[case(date!(2019-01-01), date!(2019-01-01), (1, Some(1)))]
fn size_hint(#[case] start: Date, #[case] end: Date, #[case] expected: (usize, Option<usize>)) {
assert_eq!(start.iter_to(end).size_hint(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), 5)]
#[case(date!(2019-01-01), date!(2019-01-01), 1)]
#[case(date!(2019-01-05), date!(2019-01-01), 0)]
#[case(date!(-9999-01-01), date!(-9999-12-31), 365)]
fn count(#[case] start: Date, #[case] end: Date, #[case] expected: usize) {
assert_eq!(start.iter_to(end).count(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), Some(date!(2019-01-05)))]
#[case(date!(2019-01-01), date!(2019-01-01), Some(date!(2019-01-01)))]
#[case(date!(2019-01-05), date!(2019-01-01), None)]
fn last(#[case] start: Date, #[case] end: Date, #[case] expected: Option<Date>) {
assert_eq!(start.iter_to(end).last(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), Some(date!(2019-01-05)))]
#[case(date!(2019-01-01), date!(2019-01-01), Some(date!(2019-01-01)))]
#[case(date!(2019-01-05), date!(2019-01-01), None)]
fn max(#[case] start: Date, #[case] end: Date, #[case] expected: Option<Date>) {
assert_eq!(start.iter_to(end).max(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), Some(date!(2019-01-01)))]
#[case(date!(2019-01-01), date!(2019-01-01), Some(date!(2019-01-01)))]
#[case(date!(2019-01-05), date!(2019-01-01), None)]
fn min(#[case] start: Date, #[case] end: Date, #[case] expected: Option<Date>) {
assert_eq!(start.iter_to(end).min(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), 0, Some(date!(2019-01-01)))]
#[case(date!(2019-01-01), date!(2019-01-05), 2, Some(date!(2019-01-03)))]
#[case(date!(2019-01-01), date!(2019-01-05), 4, Some(date!(2019-01-05)))]
#[case(date!(2019-01-01), date!(2019-01-05), 5, None)]
#[case(date!(2019-01-05), date!(2019-01-01), 0, None)]
#[case(Date::MAX, Date::MAX, 0, Some(Date::MAX))]
#[case(Date::MAX, Date::MAX, 1, None)]
#[case(BEFORE_MAX, Date::MAX, 1, Some(Date::MAX))]
#[case(BEFORE_MAX, Date::MAX, 2, None)]
#[case(date!(2019-12-30), date!(2020-01-01), 2, Some(date!(2020-01-01)))]
#[case(date!(2019-01-01), date!(2019-12-31), 364, Some(date!(2019-12-31)))]
#[case(date!(2020-01-01), date!(2020-12-31), 365, Some(date!(2020-12-31)))]
fn nth(#[case] start: Date, #[case] end: Date, #[case] n: usize, #[case] expected: Option<Date>) {
let mut iter = start.iter_to(end);
assert_eq!(iter.nth(n), expected);
if (expected.is_none() || expected == Some(end)) && start <= end {
assert_eq!(iter.next(), None);
}
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), 0, Some(date!(2019-01-05)))]
#[case(date!(2019-01-01), date!(2019-01-05), 2, Some(date!(2019-01-03)))]
#[case(date!(2019-01-01), date!(2019-01-05), 4, Some(date!(2019-01-01)))]
#[case(date!(2019-01-01), date!(2019-01-05), 5, None)]
#[case(date!(2019-01-05), date!(2019-01-01), 0, None)]
#[case(Date::MIN, Date::MIN, 0, Some(Date::MIN))]
#[case(Date::MIN, Date::MIN, 1, None)]
#[case(Date::MIN, AFTER_MIN, 1, Some(Date::MIN))]
#[case(Date::MIN, AFTER_MIN, 2, None)]
#[case(date!(2019-12-30), date!(2020-01-01), 2, Some(date!(2019-12-30)))]
#[case(date!(2019-01-01), date!(2019-12-31), 364, Some(date!(2019-01-01)))]
#[case(date!(2020-01-01), date!(2020-12-31), 365, Some(date!(2020-01-01)))]
fn nth_back(
#[case] start: Date,
#[case] end: Date,
#[case] n: usize,
#[case] expected: Option<Date>,
) {
let mut iter = start.iter_to(end);
assert_eq!(iter.nth_back(n), expected);
if (expected.is_none() || expected == Some(start)) && start <= end {
assert_eq!(iter.next_back(), None);
}
}
#[rstest]
fn nth_mixed() {
let mut iter = date!(2019-01-01).iter_to(date!(2019-01-07));
assert_eq!(iter.nth(2), Some(date!(2019-01-03)));
assert_eq!(iter.nth_back(1), Some(date!(2019-01-06)));
assert_eq!(iter.next(), Some(date!(2019-01-04)));
assert_eq!(iter.next_back(), Some(date!(2019-01-05)));
assert_eq!(iter.next(), None);
}
#[rstest]
fn nth_back_after_next() {
let mut iter = date!(2019-01-01).iter_to(date!(2019-01-05));
assert_eq!(iter.next(), Some(date!(2019-01-01)));
assert_eq!(iter.nth_back(2), Some(date!(2019-01-03)));
assert_eq!(iter.next(), Some(date!(2019-01-02)));
assert_eq!(iter.next(), None);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05), 5)]
#[case(date!(2019-01-01), date!(2019-01-01), 1)]
#[case(date!(2019-01-05), date!(2019-01-01), 0)]
fn len(#[case] start: Date, #[case] end: Date, #[case] expected: usize) {
assert_eq!(start.iter_to(end).len(), expected);
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-05))]
#[case(date!(2019-01-01), date!(2019-01-01))]
#[case(date!(2019-01-05), date!(2019-01-01))]
fn is_sorted(#[case] start: Date, #[case] end: Date) {
assert!(start.iter_to(end).is_sorted());
}
#[rstest]
#[case(date!(2019-01-01), date!(2019-01-03))]
#[case(date!(2019-01-05), date!(2019-01-01))]
#[case(Date::MIN, Date::MIN)]
#[case(Date::MAX, Date::MAX)]
#[case(Date::MIN, AFTER_MIN)]
#[case(BEFORE_MAX, Date::MAX)]
fn fused(#[case] start: Date, #[case] end: Date) {
let mut iter = start.iter_to(end);
while iter.next().is_some() {}
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
}
#[rstest]
#[case(Date::MIN)]
#[case(Date::MAX)]
fn boundary_single(#[case] d: Date) {
let mut iter = d.iter_to(d);
assert_eq!(iter.next(), Some(d));
assert_eq!(iter.next(), None);
}
#[rstest]
fn boundary_two() {
let mut iter = Date::MIN.iter_to(AFTER_MIN);
assert_eq!(iter.next(), Some(Date::MIN));
assert_eq!(iter.next(), Some(AFTER_MIN));
assert_eq!(iter.next(), None);
}
#[rstest]
fn len_after_consumption() {
let mut iter = date!(2019-01-01).iter_to(date!(2019-01-05));
assert_eq!(iter.len(), 5);
iter.next();
assert_eq!(iter.len(), 4);
iter.next_back();
assert_eq!(iter.len(), 3);
iter.nth(1);
assert_eq!(iter.len(), 1);
iter.next();
assert_eq!(iter.len(), 0);
}
#[rstest]
#[case(date!(2019-12-01), date!(2020-01-31), 15, date!(2019-12-16), date!(2019-12-17))]
#[case(date!(2020-12-01), date!(2021-01-31), 15, date!(2020-12-16), date!(2020-12-17))]
#[case(date!(2019-12-30), date!(2020-01-05), 2, date!(2020-01-01), date!(2020-01-02))]
fn nth_cross_year(
#[case] start: Date,
#[case] end: Date,
#[case] n: usize,
#[case] expected: Date,
#[case] next_expected: Date,
) {
let mut iter = start.iter_to(end);
assert_eq!(iter.nth(n), Some(expected));
assert_eq!(iter.next(), Some(next_expected));
}
#[rstest]
#[case(date!(2019-12-30), date!(2020-01-10), 9, date!(2020-01-01), date!(2019-12-31))]
#[case(date!(2019-12-30), date!(2020-01-05), 5, date!(2019-12-31), date!(2019-12-30))]
fn nth_back_cross_year(
#[case] start: Date,
#[case] end: Date,
#[case] n: usize,
#[case] expected: Date,
#[case] next_expected: Date,
) {
let mut iter = start.iter_to(end);
assert_eq!(iter.nth_back(n), Some(expected));
assert_eq!(iter.next_back(), Some(next_expected));
}
#[rstest]
#[case(date!(2019-12-30), date!(2020-01-02), 4)]
fn cross_year_span(#[case] start: Date, #[case] end: Date, #[case] expected: usize) {
assert_eq!(start.iter_to(end).count(), expected);
assert_eq!(start.iter_to(end).size_hint(), (expected, Some(expected)));
assert_eq!(start.iter_to(end).len(), expected);
}
+249
View File
@@ -0,0 +1,249 @@
use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::fmt::Debug;
use std::hash::Hash;
use rstest::rstest;
#[expect(deprecated)]
use time::Instant;
use time::error::{self, ConversionRange, IndeterminateOffset, TryFromParsed};
use time::ext::NumericalDuration;
use time::format_description::{
self, BorrowedFormatItem, Component, OwnedFormatItem, modifier, well_known,
};
use time::macros::{date, datetime, offset, time, utc_datetime};
use time::parsing::Parsed;
use time::{Error, Month, SignedDuration, Time, Weekday};
fn component_range_error() -> error::ComponentRange {
Time::from_hms(24, 0, 0).expect_err("24 is not a valid hour")
}
fn invalid_format_description() -> error::InvalidFormatDescription {
format_description::parse_borrowed::<3>("[").expect_err("format description is invalid")
}
#[rstest]
#[expect(deprecated)]
#[case(Instant::now())]
#[case(date!(2021-001))]
#[case(time!(0:00))]
#[case(offset!(UTC))]
#[case(datetime!(2021-001 0:00))]
#[case(datetime!(2021-001 0:00 UTC))]
#[case(utc_datetime!(2021-001 0:00))]
#[case(Weekday::Monday)]
#[case(Month::January)]
#[case(SignedDuration::ZERO)]
#[case(IndeterminateOffset)]
#[case(ConversionRange)]
#[case(invalid_format_description())]
#[case(TryFromParsed::InsufficientInformation)]
#[case(error::Parse::ParseFromDescription(error::ParseFromDescription::InvalidComponent("foo")))]
#[case(error::DifferentVariant)]
#[case(error::InvalidVariant)]
#[case(error::ParseFromDescription::InvalidComponent("foo"))]
#[case(Component::OffsetSecond(modifier::OffsetSecond::default()))]
#[case(well_known::Rfc2822)]
#[case(well_known::Rfc3339)]
#[case(well_known::Iso8601::DEFAULT)]
#[case(well_known::iso8601::FormattedComponents::None)]
#[case(well_known::iso8601::DateKind::Calendar)]
#[case(well_known::iso8601::TimePrecision::Hour { decimal_digits: None })]
#[case(well_known::iso8601::OffsetPrecision::Hour)]
#[case(well_known::iso8601::FormattedComponents::None)]
#[case(component_range_error())]
#[case(BorrowedFormatItem::StringLiteral(""))]
#[case(modifier::Day::default())]
#[case(modifier::MonthNumerical::default())]
#[case(modifier::MonthShort::default())]
#[case(modifier::MonthLong::default())]
#[expect(deprecated)]
#[case(modifier::MonthRepr::default())]
#[expect(deprecated)]
#[case(modifier::Month::default())]
#[case(modifier::Ordinal::default())]
#[expect(deprecated)]
#[case(modifier::WeekdayRepr::default())]
#[case(modifier::WeekdayShort::default())]
#[case(modifier::WeekdayLong::default())]
#[case(modifier::WeekdaySunday::default())]
#[case(modifier::WeekdayMonday::default())]
#[expect(deprecated)]
#[case(modifier::Weekday::default())]
#[expect(deprecated)]
#[case(modifier::WeekNumberRepr::default())]
#[case(modifier::WeekNumberIso::default())]
#[case(modifier::WeekNumberSunday::default())]
#[case(modifier::WeekNumberMonday::default())]
#[expect(deprecated)]
#[case(modifier::WeekNumber::default())]
#[expect(deprecated)]
#[case(modifier::YearRepr::default())]
#[case(modifier::CalendarYearFullExtendedRange::default())]
#[case(modifier::CalendarYearFullStandardRange::default())]
#[case(modifier::IsoYearFullExtendedRange::default())]
#[case(modifier::IsoYearFullStandardRange::default())]
#[case(modifier::CalendarYearCenturyExtendedRange::default())]
#[case(modifier::CalendarYearCenturyStandardRange::default())]
#[case(modifier::IsoYearCenturyExtendedRange::default())]
#[case(modifier::IsoYearCenturyStandardRange::default())]
#[case(modifier::CalendarYearLastTwo::default())]
#[case(modifier::IsoYearLastTwo::default())]
#[expect(deprecated)]
#[case(modifier::Year::default())]
#[case(modifier::Hour12::default())]
#[case(modifier::Hour24::default())]
#[expect(deprecated)]
#[case(modifier::Hour::default())]
#[case(modifier::Minute::default())]
#[case(modifier::Period::default())]
#[case(modifier::Second::default())]
#[case(modifier::SubsecondDigits::default())]
#[case(modifier::Subsecond::default())]
#[case(modifier::OffsetHour::default())]
#[case(modifier::OffsetMinute::default())]
#[case(modifier::OffsetSecond::default())]
#[case(modifier::Padding::default())]
fn clone(#[case] value: impl Clone + PartialEq + Debug) {
assert_eq!(value.clone(), value);
}
#[rstest]
#[case(Parsed::new())]
fn clone_coverage(#[case] value: impl Clone) {
#[expect(clippy::redundant_clone, reason = "intended for test coverage")]
drop(value.clone());
}
#[rstest]
#[case(date!(2021-001))]
#[case(time!(0:00))]
#[case(offset!(UTC))]
#[case(datetime!(2021-001 0:00))]
#[case(datetime!(2021-001 0:00 UTC))]
#[case(utc_datetime!(2021-001 0:00))]
#[case(Weekday::Monday)]
#[case(Month::January)]
#[expect(deprecated)]
#[case(Instant::now())]
#[case(SignedDuration::ZERO)]
#[case(component_range_error())]
fn hash(#[case] value: impl Hash) {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
}
#[rstest]
#[case(offset!(UTC), offset!(+1), Ordering::Less)]
#[case(offset!(+1), offset!(UTC), Ordering::Greater)]
#[expect(deprecated)]
#[case(Instant::now() - 1.seconds(), Instant::now(), Ordering::Less)]
#[expect(deprecated)]
#[case(Instant::now() + 1.seconds(), Instant::now(), Ordering::Greater)]
fn partial_ord<T>(#[case] a: T, #[case] b: T, #[case] ordering: Ordering)
where
T: PartialOrd,
{
assert_eq!(a.partial_cmp(&b), Some(ordering));
}
#[rstest]
#[case(offset!(UTC), offset!(+1), Ordering::Less)]
#[case(offset!(+1), offset!(UTC), Ordering::Greater)]
#[case(offset!(UTC), offset!(UTC), Ordering::Equal)]
fn ord(#[case] a: time::UtcOffset, #[case] b: time::UtcOffset, #[case] ordering: Ordering) {
assert_eq!(a.cmp(&b), ordering);
}
#[rstest]
#[case(utc_datetime!(2021-001 0:00))]
#[case(SignedDuration::ZERO)]
#[case(IndeterminateOffset)]
#[case(ConversionRange)]
#[case(TryFromParsed::InsufficientInformation)]
#[case(Parsed::new())]
#[expect(deprecated)]
#[case(Instant::now())]
#[case(error::ParseFromDescription::InvalidComponent("foo"))]
#[case(error::Format::InvalidComponent("foo"))]
#[case(well_known::Rfc2822)]
#[case(well_known::Rfc3339)]
#[case(well_known::Iso8601::DEFAULT)]
#[case(well_known::iso8601::FormattedComponents::None)]
#[case(well_known::iso8601::DateKind::Calendar)]
#[case(well_known::iso8601::TimePrecision::Hour { decimal_digits: None })]
#[case(well_known::iso8601::OffsetPrecision::Hour)]
#[case(well_known::iso8601::Config::DEFAULT)]
#[case(component_range_error())]
#[case(Error::ConversionRange(ConversionRange))]
#[case(modifier::Day::default())]
#[expect(deprecated)]
#[case(modifier::MonthRepr::default())]
#[case(modifier::MonthNumerical::default())]
#[case(modifier::MonthShort::default())]
#[case(modifier::MonthLong::default())]
#[expect(deprecated)]
#[case(modifier::Month::default())]
#[case(modifier::Ordinal::default())]
#[expect(deprecated)]
#[case(modifier::WeekdayRepr::default())]
#[case(modifier::WeekdayShort::default())]
#[case(modifier::WeekdayLong::default())]
#[case(modifier::WeekdaySunday::default())]
#[case(modifier::WeekdayMonday::default())]
#[expect(deprecated)]
#[case(modifier::Weekday::default())]
#[expect(deprecated)]
#[case(modifier::WeekNumberRepr::default())]
#[case(modifier::WeekNumberIso::default())]
#[case(modifier::WeekNumberSunday::default())]
#[case(modifier::WeekNumberMonday::default())]
#[expect(deprecated)]
#[case(modifier::WeekNumber::default())]
#[expect(deprecated)]
#[case(modifier::YearRepr::default())]
#[case(modifier::CalendarYearFullExtendedRange::default())]
#[case(modifier::CalendarYearFullStandardRange::default())]
#[case(modifier::IsoYearFullExtendedRange::default())]
#[case(modifier::IsoYearFullStandardRange::default())]
#[case(modifier::CalendarYearCenturyExtendedRange::default())]
#[case(modifier::CalendarYearCenturyStandardRange::default())]
#[case(modifier::IsoYearCenturyExtendedRange::default())]
#[case(modifier::IsoYearCenturyStandardRange::default())]
#[case(modifier::CalendarYearLastTwo::default())]
#[case(modifier::IsoYearLastTwo::default())]
#[expect(deprecated)]
#[case(modifier::Year::default())]
#[case(modifier::Hour12::default())]
#[case(modifier::Hour24::default())]
#[expect(deprecated)]
#[case(modifier::Hour::default())]
#[case(modifier::Minute::default())]
#[case(modifier::Period::default())]
#[case(modifier::Second::default())]
#[case(modifier::SubsecondDigits::default())]
#[case(modifier::Subsecond::default())]
#[case(modifier::OffsetHour::default())]
#[case(modifier::OffsetMinute::default())]
#[case(modifier::OffsetSecond::default())]
#[case(modifier::Padding::default())]
#[expect(deprecated)]
#[case(BorrowedFormatItem::Literal(b"abcdef"))]
#[case(BorrowedFormatItem::StringLiteral("abcdef"))]
#[case(BorrowedFormatItem::Compound(
const { &[BorrowedFormatItem::Component(Component::Day(modifier::Day::default()))] }
))]
#[case(BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[])))]
#[case(BorrowedFormatItem::First(&[]))]
#[expect(deprecated)]
#[case(OwnedFormatItem::from(BorrowedFormatItem::Literal(b"abcdef")))]
#[case(OwnedFormatItem::from(BorrowedFormatItem::StringLiteral("abcdef")))]
#[case(OwnedFormatItem::from(BorrowedFormatItem::Compound(
&[BorrowedFormatItem::Component(Component::Day(modifier::Day::default()))]
)))]
#[case(OwnedFormatItem::from(BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[]))))]
#[case(OwnedFormatItem::from(BorrowedFormatItem::First(&[])))]
fn debug(#[case] value: impl Debug) {
let _unused = format!("{value:?}");
}
File diff suppressed because it is too large Load Diff
+184
View File
@@ -0,0 +1,184 @@
use std::fmt::Display;
use std::io;
use std::marker::PhantomData;
use rstest::rstest;
use time::error::{
ComponentRange, ConversionRange, DifferentVariant, Error, Format, IndeterminateOffset,
InvalidFormatDescription, InvalidVariant, Parse, ParseFromDescription, TryFromParsed,
};
use time::macros::format_description;
use time::parsing::Parsed;
use time::{Date, Time, format_description};
fn component_range() -> ComponentRange {
Date::from_ordinal_date(0, 367).expect_err("367 is not a valid day")
}
fn insufficient_type_information() -> Format {
Time::MIDNIGHT
.format(format_description!("[year]"))
.expect_err("missing date and UTC offset")
}
fn unexpected_trailing_characters() -> Parse {
Time::parse("a", format_description!("")).expect_err("should fail to parse")
}
fn invalid_format_description() -> InvalidFormatDescription {
format_description::parse_borrowed::<3>("[").expect_err("format description is invalid")
}
fn io_error() -> io::Error {
io::Error::last_os_error()
}
fn invalid_literal() -> ParseFromDescription {
Parsed::parse_literal(b"a", b"b").expect_err("should fail to parse")
}
#[rstest]
#[case(Parse::from(ParseFromDescription::InvalidComponent("a")))]
#[case(invalid_format_description())]
#[case(DifferentVariant)]
#[case(InvalidVariant)]
fn debug_reflexive<T>(#[case] value: T)
where
T: std::fmt::Debug,
{
assert_eq!(format!("{value:?}"), format!("{value:?}"))
}
#[rstest]
#[case(ConversionRange, Error::from(ConversionRange))]
#[case(component_range(), Error::from(component_range()))]
#[case(component_range(), TryFromParsed::from(component_range()))]
#[case(IndeterminateOffset, Error::from(IndeterminateOffset))]
#[case(
TryFromParsed::InsufficientInformation,
Error::from(TryFromParsed::InsufficientInformation)
)]
#[case(
insufficient_type_information(),
Error::from(insufficient_type_information())
)]
#[case(
Format::InvalidComponent("a"),
Error::from(Format::InvalidComponent("a"))
)]
#[case(
ParseFromDescription::InvalidComponent("a"),
Error::from(Parse::from(ParseFromDescription::InvalidComponent("a")))
)]
#[case(invalid_literal(), Parse::from(invalid_literal()))]
#[case(
component_range(),
Error::from(Parse::from(TryFromParsed::from(component_range())))
)]
#[case(
ParseFromDescription::InvalidComponent("a"),
Parse::from(ParseFromDescription::InvalidComponent("a"))
)]
#[case(component_range(), Parse::from(TryFromParsed::from(component_range())))]
#[case(
unexpected_trailing_characters(),
Error::from(unexpected_trailing_characters())
)]
#[case(
invalid_format_description(),
Error::from(invalid_format_description())
)]
#[case(io_error(), Format::from(io_error()))]
#[case(DifferentVariant, Error::from(DifferentVariant))]
#[case(InvalidVariant, Error::from(InvalidVariant))]
fn display_eq<T, U>(#[case] lhs: T, #[case] rhs: U)
where
T: Display,
U: Display,
{
assert_eq!(lhs.to_string(), rhs.to_string());
}
#[rstest]
#[case(Error::from(ConversionRange), PhantomData::<ConversionRange>)]
#[case(Error::from(component_range()), PhantomData::<ComponentRange>)]
#[case(TryFromParsed::from(component_range()), PhantomData::<ComponentRange>)]
#[case(Error::from(insufficient_type_information()), PhantomData::<Format>)]
#[case(Error::from(IndeterminateOffset), PhantomData::<IndeterminateOffset>)]
#[case(Parse::from(TryFromParsed::InsufficientInformation), PhantomData::<TryFromParsed>)]
#[case(Error::from(TryFromParsed::InsufficientInformation), PhantomData::<TryFromParsed>)]
#[case(Parse::from(ParseFromDescription::InvalidComponent("a")), PhantomData::<ParseFromDescription>)]
#[case(Error::from(ParseFromDescription::InvalidComponent("a")), PhantomData::<ParseFromDescription>)]
#[case(unexpected_trailing_characters(), PhantomData::<ParseFromDescription>)]
#[case(Error::from(unexpected_trailing_characters()), PhantomData::<ParseFromDescription>)]
#[case(Error::from(invalid_format_description()), PhantomData::<InvalidFormatDescription>)]
#[case(Format::from(io_error()), PhantomData::<io::Error>)]
#[case(Error::from(DifferentVariant), PhantomData::<DifferentVariant>)]
#[case(Error::from(InvalidVariant), PhantomData::<InvalidVariant>)]
fn source<E, S>(#[case] err: E, #[case] _source: PhantomData<S>)
where
E: std::error::Error + 'static,
S: std::error::Error + 'static,
{
assert!(
err.source()
.expect("error type should have source")
.is::<S>()
);
}
#[rstest]
#[case(TryFromParsed::InsufficientInformation)]
#[case(insufficient_type_information())]
#[case(Format::InvalidComponent("a"))]
fn source_none<T>(#[case] err: T)
where
T: std::error::Error,
{
assert!(err.source().is_none());
}
#[rstest]
fn component_name() {
assert_eq!(component_range().name(), "ordinal");
}
#[rstest]
#[case(ComponentRange::try_from(Error::from(component_range())))]
#[case(ConversionRange::try_from(Error::from(ConversionRange)))]
#[case(Format::try_from(Error::from(insufficient_type_information())))]
#[case(IndeterminateOffset::try_from(Error::from(IndeterminateOffset)))]
#[case(InvalidFormatDescription::try_from(Error::from(invalid_format_description())))]
#[case(ParseFromDescription::try_from(Error::from(invalid_literal())))]
#[case(ParseFromDescription::try_from(Parse::from(invalid_literal())))]
#[case(ParseFromDescription::try_from(unexpected_trailing_characters()))]
#[case(Parse::try_from(Error::from(unexpected_trailing_characters())))]
#[case(Parse::try_from(Error::from(invalid_literal())))]
#[case(Parse::try_from(Error::from(TryFromParsed::InsufficientInformation)))]
#[case(DifferentVariant::try_from(Error::from(DifferentVariant)))]
#[case(InvalidVariant::try_from(Error::from(InvalidVariant)))]
#[case(ComponentRange::try_from(TryFromParsed::ComponentRange(component_range())))]
#[case(TryFromParsed::try_from(Error::from(TryFromParsed::InsufficientInformation)))]
#[case(TryFromParsed::try_from(Parse::from(TryFromParsed::InsufficientInformation)))]
#[case(io::Error::try_from(Format::from(io_error())))]
fn conversion<T, E>(#[case] res: Result<T, E>) {
assert!(res.is_ok());
}
#[rstest]
#[case(ComponentRange::try_from(Error::from(IndeterminateOffset)))]
#[case(ConversionRange::try_from(Error::from(IndeterminateOffset)))]
#[case(Format::try_from(Error::from(IndeterminateOffset)))]
#[case(IndeterminateOffset::try_from(Error::from(ConversionRange)))]
#[case(InvalidFormatDescription::try_from(Error::from(IndeterminateOffset)))]
#[case(ParseFromDescription::try_from(Error::from(IndeterminateOffset)))]
#[case(Parse::try_from(Error::from(IndeterminateOffset)))]
#[case(DifferentVariant::try_from(Error::from(IndeterminateOffset)))]
#[case(InvalidVariant::try_from(Error::from(IndeterminateOffset)))]
#[case(ComponentRange::try_from(TryFromParsed::InsufficientInformation))]
#[case(TryFromParsed::try_from(Error::from(IndeterminateOffset)))]
#[case(TryFromParsed::try_from(unexpected_trailing_characters()))]
#[case(io::Error::try_from(insufficient_type_information()))]
fn conversion_err<T, E>(#[case] res: Result<T, E>) {
assert!(res.is_err());
}
+109
View File
@@ -0,0 +1,109 @@
mod numerical_duration {
use rstest::rstest;
use time::SignedDuration;
use time::ext::NumericalDuration;
#[rstest]
#[case(5.nanoseconds(), SignedDuration::nanoseconds(5))]
#[case(5.microseconds(), SignedDuration::microseconds(5))]
#[case(5.milliseconds(), SignedDuration::milliseconds(5))]
#[case(5.seconds(), SignedDuration::seconds(5))]
#[case(5.minutes(), SignedDuration::minutes(5))]
#[case(5.hours(), SignedDuration::hours(5))]
#[case(5.days(), SignedDuration::days(5))]
#[case(5.weeks(), SignedDuration::weeks(5))]
fn unsigned(#[case] ext_trait: SignedDuration, #[case] expected: SignedDuration) {
assert_eq!(ext_trait, expected);
}
#[rstest]
#[case((-5).nanoseconds(), SignedDuration::nanoseconds(-5))]
#[case((-5).microseconds(), SignedDuration::microseconds(-5))]
#[case((-5).milliseconds(), SignedDuration::milliseconds(-5))]
#[case((-5).seconds(), SignedDuration::seconds(-5))]
#[case((-5).minutes(), SignedDuration::minutes(-5))]
#[case((-5).hours(), SignedDuration::hours(-5))]
#[case((-5).days(), SignedDuration::days(-5))]
#[case((-5).weeks(), SignedDuration::weeks(-5))]
fn signed(#[case] ext_trait: SignedDuration, #[case] expected: SignedDuration) {
assert_eq!(ext_trait, expected);
}
#[rstest]
#[case::truncate_not_round(1.9.nanoseconds(), SignedDuration::nanoseconds(1))]
#[case(1.0.nanoseconds(), SignedDuration::nanoseconds(1))]
#[case(1.0.microseconds(), SignedDuration::microseconds(1))]
#[case(1.0.milliseconds(), SignedDuration::milliseconds(1))]
#[case(1.0.seconds(), SignedDuration::seconds(1))]
#[case(1.0.minutes(), SignedDuration::minutes(1))]
#[case(1.0.hours(), SignedDuration::hours(1))]
#[case(1.0.days(), SignedDuration::days(1))]
#[case(1.0.weeks(), SignedDuration::weeks(1))]
#[case(1.5.nanoseconds(), SignedDuration::nanoseconds(1))]
#[case(1.5.microseconds(), SignedDuration::nanoseconds(1_500))]
#[case(1.5.milliseconds(), SignedDuration::microseconds(1_500))]
#[case(1.5.seconds(), SignedDuration::milliseconds(1_500))]
#[case(1.5.minutes(), SignedDuration::seconds(90))]
#[case(1.5.hours(), SignedDuration::minutes(90))]
#[case(1.5.days(), SignedDuration::hours(36))]
#[case(1.5.weeks(), SignedDuration::hours(252))]
fn float(#[case] ext_trait: SignedDuration, #[case] expected: SignedDuration) {
assert_eq!(ext_trait, expected);
}
#[rstest]
#[case(2.seconds() + 500.milliseconds(), 2_500.milliseconds())]
#[case(2.seconds() - 500.milliseconds(), 1_500.milliseconds())]
fn arithmetic(#[case] arithmetic_: SignedDuration, #[case] expected: SignedDuration) {
assert_eq!(arithmetic_, expected);
}
}
mod numerical_std_duration {
use std::time::Duration as StdDuration;
use rstest::rstest;
use time::ext::NumericalStdDuration;
#[rstest]
#[case(5.std_nanoseconds(), StdDuration::from_nanos(5))]
#[case(5.std_microseconds(), StdDuration::from_micros(5))]
#[case(5.std_milliseconds(), StdDuration::from_millis(5))]
#[case(5.std_seconds(), StdDuration::from_secs(5))]
#[case(5.std_minutes(), StdDuration::from_secs(5 * 60))]
#[case(5.std_hours(), StdDuration::from_secs(5 * 3_600))]
#[case(5.std_days(), StdDuration::from_secs(5 * 86_400))]
#[case(5.std_weeks(), StdDuration::from_secs(5 * 604_800))]
fn unsigned(#[case] ext_trait: StdDuration, #[case] expected: StdDuration) {
assert_eq!(ext_trait, expected);
}
#[rstest]
#[case::truncate_not_round(1.9.std_nanoseconds(), StdDuration::from_nanos(1))]
#[case(1.0.std_nanoseconds(), StdDuration::from_nanos(1))]
#[case(1.0.std_microseconds(), StdDuration::from_micros(1))]
#[case(1.0.std_milliseconds(), StdDuration::from_millis(1))]
#[case(1.0.std_seconds(), StdDuration::from_secs(1))]
#[case(1.0.std_minutes(), StdDuration::from_secs(60))]
#[case(1.0.std_hours(), StdDuration::from_secs(3_600))]
#[case(1.0.std_days(), StdDuration::from_secs(86_400))]
#[case(1.0.std_weeks(), StdDuration::from_secs(604_800))]
#[case(1.5.std_nanoseconds(), StdDuration::from_nanos(1))]
#[case(1.5.std_microseconds(), StdDuration::from_nanos(1_500))]
#[case(1.5.std_milliseconds(), StdDuration::from_micros(1_500))]
#[case(1.5.std_seconds(), StdDuration::from_millis(1_500))]
#[case(1.5.std_minutes(), StdDuration::from_secs(90))]
#[case(1.5.std_hours(), StdDuration::from_secs(90 * 60))]
#[case(1.5.std_days(), StdDuration::from_secs(36 * 3_600))]
#[case(1.5.std_weeks(), StdDuration::from_secs(252 * 3_600))]
fn float(#[case] ext_trait: StdDuration, #[case] expected: StdDuration) {
assert_eq!(ext_trait, expected);
}
#[rstest]
#[case(2.std_seconds() + 500.std_milliseconds(), 2_500.std_milliseconds())]
#[case(2.std_seconds() - 500.std_milliseconds(), 1_500.std_milliseconds())]
fn arithmetic(#[case] arithmetic_: StdDuration, #[case] expected: StdDuration) {
assert_eq!(arithmetic_, expected);
}
}
@@ -0,0 +1,95 @@
use rstest::rstest;
use time::format_description::{BorrowedFormatItem, Component, OwnedFormatItem, modifier};
#[rstest]
fn borrowed_format_item_component_conversions() {
let component = Component::CalendarYearFullExtendedRange(
modifier::CalendarYearFullExtendedRange::default(),
);
let item = BorrowedFormatItem::from(component);
assert!(matches!(item, BorrowedFormatItem::Component(inner) if inner == component));
assert_eq!(Component::try_from(item), Ok(component));
assert!(Component::try_from(BorrowedFormatItem::StringLiteral("")).is_err());
assert!(<&[BorrowedFormatItem<'_>]>::try_from(BorrowedFormatItem::StringLiteral("")).is_err());
}
#[rstest]
fn borrowed_format_item_compound_conversions() {
let compound = [BorrowedFormatItem::StringLiteral("")].as_slice();
let item = BorrowedFormatItem::from(compound);
assert!(matches!(item, BorrowedFormatItem::Compound(inner) if inner == compound));
assert_eq!(<&[BorrowedFormatItem<'_>]>::try_from(item), Ok(compound));
}
#[rstest]
fn borrowed_format_item_equality() {
let component = Component::CalendarYearFullExtendedRange(
modifier::CalendarYearFullExtendedRange::default(),
);
let compound = [BorrowedFormatItem::StringLiteral("")].as_slice();
let component_item = BorrowedFormatItem::from(component);
let compound_item = BorrowedFormatItem::from(compound);
assert_eq!(component, component_item);
assert_eq!(component_item, component);
assert_eq!(compound, compound_item);
assert_eq!(compound_item, compound);
}
#[rstest]
fn owned_format_item_component_conversions() {
let component = Component::CalendarYearFullExtendedRange(
modifier::CalendarYearFullExtendedRange::default(),
);
let item = OwnedFormatItem::from(component);
assert!(matches!(item, OwnedFormatItem::Component(inner) if inner == component));
assert_eq!(Component::try_from(item), Ok(component));
assert!(
Component::try_from(OwnedFormatItem::StringLiteral(
"".to_owned().into_boxed_str()
))
.is_err()
);
assert!(
Vec::<OwnedFormatItem>::try_from(OwnedFormatItem::StringLiteral(
"".to_owned().into_boxed_str()
))
.is_err()
);
}
#[rstest]
fn owned_format_item_compound_conversions() {
let compound = vec![OwnedFormatItem::StringLiteral(
"".to_owned().into_boxed_str(),
)];
let item = OwnedFormatItem::from(compound.clone());
assert!(matches!(item.clone(), OwnedFormatItem::Compound(inner) if inner.to_vec() == compound));
assert_eq!(Vec::<OwnedFormatItem>::try_from(item), Ok(compound));
}
#[rstest]
fn owned_format_item_equality() {
let component = Component::CalendarYearFullExtendedRange(
modifier::CalendarYearFullExtendedRange::default(),
);
let compound = OwnedFormatItem::from([BorrowedFormatItem::StringLiteral("")].as_slice());
let component_item = OwnedFormatItem::from(component);
assert_eq!(component, component_item);
assert_eq!(component_item, component);
assert_eq!(
compound,
[OwnedFormatItem::StringLiteral(
"".to_owned().into_boxed_str()
)]
.as_slice()
);
assert_eq!(
[OwnedFormatItem::StringLiteral(
"".to_owned().into_boxed_str()
)]
.as_slice(),
compound
);
}
@@ -0,0 +1,807 @@
use std::io;
use std::num::NonZero;
use rstest::rstest;
use time::format_description::well_known::iso8601::{DateKind, OffsetPrecision, TimePrecision};
use time::format_description::well_known::{Iso8601, Rfc2822, Rfc3339, iso8601};
use time::format_description::{self, BorrowedFormatItem, OwnedFormatItem};
use time::formatting::Formattable;
use time::macros::{date, datetime, format_description as fd, offset, time, utc_datetime};
use time::{Date, OffsetDateTime, PlainDateTime, Time, UtcDateTime, UtcOffset};
#[rstest]
#[case(datetime!(2021-01-02 03:04:05 UTC), "Sat, 02 Jan 2021 03:04:05 +0000")]
#[case(datetime!(2021-01-02 03:04:05 +06:07), "Sat, 02 Jan 2021 03:04:05 +0607")]
#[case(datetime!(2021-01-02 03:04:05 -06:07), "Sat, 02 Jan 2021 03:04:05 -0607")]
fn rfc_2822_odt(#[case] dt: OffsetDateTime, #[case] expected: &str) {
assert_eq!(dt.format(&Rfc2822).ok().as_deref(), Some(expected));
}
#[rstest]
#[case(utc_datetime!(2021-01-02 03:04:05), "Sat, 02 Jan 2021 03:04:05 +0000")]
fn rfc_2822_udt(#[case] dt: UtcDateTime, #[case] expected: &str) {
assert_eq!(dt.format(&Rfc2822).ok().as_deref(), Some(expected));
}
#[rstest]
#[case(datetime!(1885-01-01 01:01:01 UTC), "year")]
#[case(datetime!(2000-01-01 00:00:00 +00:00:01), "offset_second")]
fn rfc_2822_invalid_odt(#[case] odt: OffsetDateTime, #[case] component: &str) {
assert!(matches!(
odt.format(&Rfc2822),
Err(time::error::Format::InvalidComponent(c)) if c == component
));
}
#[rstest]
#[case(utc_datetime!(1885-01-01 01:01:01), "year")]
fn rfc_2822_invalid_udt(#[case] udt: UtcDateTime, #[case] component: &str) {
assert!(matches!(
udt.format(&Rfc2822),
Err(time::error::Format::InvalidComponent(c)) if c == component
));
}
#[rstest]
#[case(datetime!(2021-01-02 03:04:05 UTC), "2021-01-02T03:04:05Z")]
#[case(datetime!(2021-01-02 03:04:05.1 UTC), "2021-01-02T03:04:05.1Z")]
#[case(datetime!(2021-01-02 03:04:05.12 UTC), "2021-01-02T03:04:05.12Z")]
#[case(datetime!(2021-01-02 03:04:05.123 UTC), "2021-01-02T03:04:05.123Z")]
#[case(datetime!(2021-01-02 03:04:05.123_4 UTC), "2021-01-02T03:04:05.1234Z")]
#[case(datetime!(2021-01-02 03:04:05.123_45 UTC), "2021-01-02T03:04:05.12345Z")]
#[case(datetime!(2021-01-02 03:04:05.123_456 UTC), "2021-01-02T03:04:05.123456Z")]
#[case(datetime!(2021-01-02 03:04:05.123_456_7 UTC), "2021-01-02T03:04:05.1234567Z")]
#[case(datetime!(2021-01-02 03:04:05.123_456_78 UTC), "2021-01-02T03:04:05.12345678Z")]
#[case(datetime!(2021-01-02 03:04:05.123_456_789 UTC), "2021-01-02T03:04:05.123456789Z")]
#[case(datetime!(2021-01-02 03:04:05.123_456_789 -01:02), "2021-01-02T03:04:05.123456789-01:02")]
#[case(datetime!(2021-01-02 03:04:05.123_456_789 +01:02), "2021-01-02T03:04:05.123456789+01:02")]
fn rfc_3339(#[case] dt: OffsetDateTime, #[case] expected: &str) {
assert_eq!(dt.format(&Rfc3339).ok(), Some(expected.to_string()));
}
#[rstest]
#[case(datetime!(-0001-01-01 0:00 UTC), "year")]
#[case(datetime!(0000-01-01 0:00 +00:00:01), "offset_second")]
fn rfc_3339_err(#[case] dt: OffsetDateTime, #[case] component: &str) {
assert!(matches!(
dt.format(&Rfc3339),
Err(time::error::Format::InvalidComponent(c)) if c == component
));
}
#[rstest]
#[case(time!(01:02:03.123_456_789), Iso8601::TIME, "01:02:03.123456789")]
fn iso_8601_time(#[case] value: Time, #[case] format: impl Formattable, #[case] expected: &str) {
assert!(matches!(value.format(&format), Ok(e) if e == expected));
}
#[rstest]
#[case(
datetime!(-123_456-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_year_is_six_digits(true)
.encode()
}>,
"-123456-01-02T03:04:05.000000000Z",
)]
#[case(
datetime!(-123_456-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_date_kind(DateKind::Ordinal)
.set_year_is_six_digits(true)
.encode()
}>,
"-123456-002T03:04:05.000000000Z",
)]
#[case(
datetime!(-123_456-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_date_kind(DateKind::Week)
.set_year_is_six_digits(true)
.encode()
}>,
"-123456-W01-4T03:04:05.000000000Z",
)]
#[case(
datetime!(2021-01-02 03:04:05+1:00),
Iso8601::DEFAULT,
"2021-01-02T03:04:05.000000000+01:00",
)]
#[case(
datetime!(2021-01-02 03:04:05+1:00),
Iso8601::<{
iso8601::Config::DEFAULT
.set_offset_precision(OffsetPrecision::Hour)
.encode()
}>,
"2021-01-02T03:04:05.000000000+01",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::DEFAULT,
"2021-01-02T03:04:05.000000000Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_use_separators(false).encode() }>,
"20210102T030405.000000000Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_year_is_six_digits(true).encode() }>,
"+002021-01-02T03:04:05.000000000Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Hour { decimal_digits: None })
.encode()
}>,
"2021-01-02T03Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Minute { decimal_digits: None })
.encode()
}>,
"2021-01-02T03:04Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Second { decimal_digits: None })
.encode()
}>,
"2021-01-02T03:04:05Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_date_kind(DateKind::Ordinal).encode() }>,
"2021-002T03:04:05.000000000Z",
)]
#[case(
datetime!(2021-01-02 03:04:05 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_date_kind(DateKind::Week).encode() }>,
"2020-W53-6T03:04:05.000000000Z",
)]
#[case::issue_773(
datetime!(2026-03-23 09:27:03.631670 UTC),
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Second { decimal_digits: NonZero::new(6) })
.encode()
}>,
"2026-03-23T09:27:03.631670Z",
)]
fn iso_8601_odt(
#[case] value: OffsetDateTime,
#[case] format: impl Formattable,
#[case] expected: &str,
) {
assert!(matches!(value.format(&format), Ok(e) if e == expected));
}
#[rstest]
#[case(datetime!(+10_000-01-01 0:00 UTC), Iso8601::DEFAULT, "year")]
#[case(
datetime!(+10_000-W01-1 0:00 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_date_kind(DateKind::Week).encode() }>,
"year",
)]
#[case(
datetime!(+10_000-001 0:00 UTC),
Iso8601::<{ iso8601::Config::DEFAULT.set_date_kind(DateKind::Ordinal).encode() }>,
"year",
)]
#[case(datetime!(2021-01-02 03:04:05 +0:00:01), Iso8601::DEFAULT, "offset_second")]
#[case(
datetime!(2021-01-02 03:04:05 +0:01),
Iso8601::<{
iso8601::Config::DEFAULT
.set_offset_precision(OffsetPrecision::Hour)
.encode()
}>,
"offset_minute",
)]
fn iso_8601_invalid(
#[case] value: OffsetDateTime,
#[case] format: impl Formattable,
#[case] component: &str,
) {
assert!(matches!(
value.format(&format),
Err(time::error::Format::InvalidComponent(c)) if c == component
));
}
#[rstest]
#[case(Iso8601::DEFAULT, "2021-01-02T03:04:05.999999999Z")]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Second { decimal_digits: NonZero::new(9) })
.encode()
}>,
"2021-01-02T03:04:05.999999999Z",
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Second { decimal_digits: NonZero::new(6) })
.encode()
}>,
"2021-01-02T03:04:05.999999Z",
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Second { decimal_digits: NonZero::new(3) })
.encode()
}>,
"2021-01-02T03:04:05.999Z",
)]
fn iso_8601_issue_678(#[case] format: impl Formattable, #[case] expected: &str) {
assert!(matches!(
datetime!(2021-01-02 03:04:05.999_999_999 UTC).format(&format),
Ok(e) if e == expected
));
}
#[rstest]
#[case(time!(13:02:03.456_789_012), fd!("[hour]"), "13")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:12]"), "01")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:12 padding:none]"), "1")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:12 padding:space]"), " 1")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:24]"), "13")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:24 padding:none]"), "13")]
#[case(time!(13:02:03.456_789_012), fd!("[hour repr:24 padding:space]"), "13")]
#[case(time!(13:02:03.456_789_012), fd!("[minute]"), "02")]
#[case(time!(13:02:03.456_789_012), fd!("[minute padding:none]"), "2")]
#[case(time!(13:02:03.456_789_012), fd!("[minute padding:space]"), " 2")]
#[case(time!(13:02:03.456_789_012), fd!("[minute padding:zero]"), "02")]
#[case(time!(13:02:03.456_789_012), fd!("[period]"), "PM")]
#[case(time!(13:02:03.456_789_012), fd!("[period case:upper]"), "PM")]
#[case(time!(13:02:03.456_789_012), fd!("[period case:lower]"), "pm")]
#[case(time!(13:02:03.456_789_012), fd!("[second]"), "03")]
#[case(time!(13:02:03.456_789_012), fd!("[second padding:none]"), "3")]
#[case(time!(13:02:03.456_789_012), fd!("[second padding:space]"), " 3")]
#[case(time!(13:02:03.456_789_012), fd!("[second padding:zero]"), "03")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond]"), "456789012")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:1]"), "4")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:2]"), "45")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:3]"), "456")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:4]"), "4567")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:5]"), "45678")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:6]"), "456789")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:7]"), "4567890")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:8]"), "45678901")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:9]"), "456789012")]
#[case(time!(13:02:03.456_789_012), fd!("[subsecond digits:1+]"), "456789012")]
#[case(time!(1:02:03), fd!("[hour repr:12][period]"), "01AM")]
#[case(Time::MIDNIGHT, fd!("[hour repr:12][period case:lower]"), "12am")]
#[case(Time::MIDNIGHT, fd!("[subsecond digits:1+]"), "0")]
#[case(time!(0:00:00.01), fd!("[subsecond digits:1+]"), "01")]
#[case(time!(0:00:00.001), fd!("[subsecond digits:1+]"), "001")]
#[case(time!(0:00:00.0001), fd!("[subsecond digits:1+]"), "0001")]
#[case(time!(0:00:00.00001), fd!("[subsecond digits:1+]"), "00001")]
#[case(time!(0:00:00.000001), fd!("[subsecond digits:1+]"), "000001")]
#[case(time!(0:00:00.0000001), fd!("[subsecond digits:1+]"), "0000001")]
#[case(time!(0:00:00.00000001), fd!("[subsecond digits:1+]"), "00000001")]
#[case(time!(0:00:00.000000001), fd!("[subsecond digits:1+]"), "000000001")]
fn format_time(
#[case] value: Time,
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] output: &str,
) -> time::Result<()> {
assert_eq!(value.format(format_description)?, output);
assert!(
value
.format_into(&mut io::sink(), format_description)
.is_ok()
);
assert_eq!(
value.format(&OwnedFormatItem::from(format_description))?,
output
);
assert!(
value
.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
Ok(())
}
#[rstest]
#[case(time!(0:00), "0:00:00.0")]
#[case(time!(23:59), "23:59:00.0")]
#[case(time!(23:59:59), "23:59:59.0")]
#[case(time!(0:00:01), "0:00:01.0")]
#[case(time!(0:00:00.1), "0:00:00.1")]
#[case(time!(0:00:00.01), "0:00:00.01")]
#[case(time!(0:00:00.001), "0:00:00.001")]
#[case(time!(0:00:00.000_1), "0:00:00.0001")]
#[case(time!(0:00:00.000_01), "0:00:00.00001")]
#[case(time!(0:00:00.000_001), "0:00:00.000001")]
#[case(time!(0:00:00.000_000_1), "0:00:00.0000001")]
#[case(time!(0:00:00.000_000_01), "0:00:00.00000001")]
#[case(time!(0:00:00.000_000_001), "0:00:00.000000001")]
fn display_time(#[case] t: Time, #[case] expected: &str) {
assert_eq!(t.to_string(), expected);
}
#[rstest]
fn display_time_padding() {
assert_eq!(format!("{:>12}", time!(0:00)), " 0:00:00.0");
assert_eq!(format!("{:x^14}", time!(0:00)), "xx0:00:00.0xxx");
}
#[rstest]
#[case(fd!("[day]"), "31")]
#[case(fd!("[month]"), "12")]
#[case(fd!("[month repr:short]"), "Dec")]
#[case(fd!("[month repr:long]"), "December")]
#[case(fd!("[ordinal]"), "365")]
#[case(fd!("[weekday]"), "Tuesday")]
#[case(fd!("[weekday repr:short]"), "Tue")]
#[case(fd!("[weekday repr:sunday]"), "3")]
#[case(fd!("[weekday repr:sunday one_indexed:false]"), "2")]
#[case(fd!("[weekday repr:monday]"), "2")]
#[case(fd!("[weekday repr:monday one_indexed:false]"), "1")]
#[case(fd!("[week_number]"), "01")]
#[case(fd!("[week_number padding:none]"), "1")]
#[case(fd!("[week_number padding:space]"), " 1")]
#[case(fd!("[week_number repr:sunday]"), "52")]
#[case(fd!("[week_number repr:monday]"), "52")]
#[case(fd!("[year]"), "2019")]
#[case(fd!("[year base:iso_week]"), "2020")]
#[case(fd!("[year sign:mandatory]"), "+2019")]
#[case(fd!("[year base:iso_week sign:mandatory]"), "+2020")]
#[case(fd!("[year repr:century]"), "20")]
#[case(fd!("[year repr:last_two]"), "19")]
#[case(fd!("[year base:iso_week repr:last_two]"), "20")]
#[case(fd!("[year range:standard]"), "2019")]
#[case(fd!("[year range:standard repr:century]"), "20")]
#[case(fd!("[year range:standard repr:last_two]"), "19")]
fn format_date(
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] output: &str,
) -> time::Result<()> {
assert_eq!(date!(2019-12-31).format(format_description)?, output);
assert!(
date!(2019-12-31)
.format_into(&mut io::sink(), format_description)
.is_ok()
);
assert_eq!(
date!(2019-12-31).format(&OwnedFormatItem::from(format_description))?,
output
);
assert!(
date!(2019-12-31)
.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
Ok(())
}
#[rstest]
#[case(date!(+10_000-01-01), fd!("[year range:standard]"), "year")]
#[case(date!(+10_000-01-01), fd!("[year repr:century range:standard]"), "year")]
fn format_date_err(
#[case] value: Date,
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] component: &str,
) {
assert!(matches!(
value.format(format_description),
Err(time::error::Format::ComponentRange(cr)) if cr.name() == component
));
}
#[rstest]
#[case(date!(2019-01-01), "2019-01-01")]
#[case(date!(2019-12-31), "2019-12-31")]
#[case(date!(-4713-11-24), "-4713-11-24")]
#[case(date!(-0001-01-01), "-0001-01-01")]
#[case(date!(+10_000-01-01), "+10000-01-01")]
#[case(date!(+100_000-01-01), "+100000-01-01")]
#[case(date!(-10_000-01-01), "-10000-01-01")]
#[case(date!(-100_000-01-01), "-100000-01-01")]
fn display_date(#[case] d: Date, #[case] expected: &str) {
assert_eq!(d.to_string(), expected);
}
#[rstest]
#[case(offset!(+01:02:03), fd!("[offset_hour sign:automatic]"), "01")]
#[case(offset!(+01:02:03), fd!("[offset_hour sign:mandatory]"), "+01")]
#[case(offset!(-01:02:03), fd!("[offset_hour sign:automatic]"), "-01")]
#[case(offset!(-01:02:03), fd!("[offset_hour sign:mandatory]"), "-01")]
#[case(offset!(+01:02:03), fd!("[offset_minute]"), "02")]
#[case(offset!(+01:02:03), fd!("[offset_second]"), "03")]
fn format_offset(
#[case] value: UtcOffset,
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] output: &str,
) -> time::Result<()> {
assert_eq!(value.format(format_description)?, output);
assert!(
value
.format_into(&mut io::sink(), format_description)
.is_ok()
);
assert_eq!(
value.format(&OwnedFormatItem::from(format_description))?,
output
);
assert!(
value
.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
Ok(())
}
#[rstest]
#[case(offset!(UTC), "+00:00:00")]
#[case(offset!(+0:00:01), "+00:00:01")]
#[case(offset!(-0:00:01), "-00:00:01")]
#[case(offset!(+1), "+01:00:00")]
#[case(offset!(-1), "-01:00:00")]
#[case(offset!(+23:59), "+23:59:00")]
#[case(offset!(-23:59), "-23:59:00")]
#[case(offset!(+23:59:59), "+23:59:59")]
#[case(offset!(-23:59:59), "-23:59:59")]
fn display_offset(#[case] offset: UtcOffset, #[case] expected: &str) {
assert_eq!(offset.to_string(), expected);
}
#[rstest]
fn display_offset_padding() {
assert_eq!(format!("{:>10}", offset!(UTC)), " +00:00:00");
assert_eq!(format!("{:x^14}", offset!(UTC)), "xx+00:00:00xxx");
}
#[rstest]
#[case(
datetime!(1970-01-01 0:00),
fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
"1970-01-01 00:00:00.0",
)]
fn format_pdt(
#[case] pdt: PlainDateTime,
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] expected: &str,
) -> time::Result<()> {
assert_eq!(pdt.format(format_description)?, expected);
assert!(pdt.format_into(&mut io::sink(), format_description).is_ok());
assert_eq!(
pdt.format(&OwnedFormatItem::from(format_description))?,
expected
);
assert!(
pdt.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
Ok(())
}
#[rstest]
#[case(datetime!(1970-01-01 0:00), "1970-01-01 0:00:00.0")]
#[case(datetime!(1970-01-01 0:00:01), "1970-01-01 0:00:01.0")]
fn display_pdt(#[case] pdt: PlainDateTime, #[case] expected: &str) {
assert_eq!(pdt.to_string(), expected);
}
#[rstest]
#[case(datetime!(1970-01-01 0:00 UTC), "1970-01-01 00:00:00.0 +00:00:00")]
fn format_odt(#[case] odt: OffsetDateTime, #[case] expected: &str) -> time::Result<()> {
let format_description = format_description::parse_borrowed::<2>(
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:1+] [offset_hour \
sign:mandatory]:[offset_minute]:[offset_second]",
)?;
assert_eq!(odt.format(&format_description)?, expected);
assert!(
odt.format_into(&mut io::sink(), &format_description)
.is_ok()
);
assert_eq!(
odt.format(&OwnedFormatItem::from(&format_description))?,
expected
);
assert!(
odt.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
let format_description_v3 = format_description::parse_borrowed::<3>(
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:1+] [offset_hour \
sign:mandatory]:[offset_minute]:[offset_second]",
)?;
assert_eq!(odt.format(&format_description_v3)?, expected);
assert!(
odt.format_into(&mut io::sink(), &format_description_v3)
.is_ok()
);
let format_description_v3 = format_description_v3.to_owned();
assert_eq!(odt.format(&format_description_v3)?, expected);
assert!(
odt.format_into(&mut io::sink(), &format_description_v3)
.is_ok()
);
Ok(())
}
#[rstest]
#[case(datetime!(1970-01-01 0:00 UTC), "1970-01-01 0:00:00.0 +00:00:00")]
fn display_odt(#[case] odt: OffsetDateTime, #[case] expected: &str) {
assert_eq!(odt.to_string(), expected);
}
#[rstest]
#[case(utc_datetime!(1970-01-01 0:00), "1970-01-01 00:00:00.0")]
fn format_udt(#[case] udt: UtcDateTime, #[case] expected: &str) -> time::Result<()> {
let format_description = fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]");
assert_eq!(udt.format(format_description)?, expected);
assert!(udt.format_into(&mut io::sink(), format_description).is_ok());
assert_eq!(
udt.format(&OwnedFormatItem::from(format_description))?,
expected
);
assert!(
udt.format_into(&mut io::sink(), &OwnedFormatItem::from(format_description))
.is_ok()
);
Ok(())
}
#[rstest]
#[case(utc_datetime!(1970-01-01 0:00), "1970-01-01 0:00:00.0 +00")]
fn display_udt(#[case] udt: UtcDateTime, #[case] expected: &str) {
assert_eq!(udt.to_string(), expected);
}
#[rstest]
fn insufficient_type_information() {
let assert_insufficient_type_information = |res| {
assert!(matches!(
res,
Err(time::error::Format::InsufficientTypeInformation { .. })
));
};
assert_insufficient_type_information(Time::MIDNIGHT.format(fd!("[year]")));
assert_insufficient_type_information(Time::MIDNIGHT.format(&BorrowedFormatItem::First(&[
BorrowedFormatItem::Compound(fd!("[year]")),
])));
}
#[rstest]
#[case(fd!("foo"))]
#[case(OwnedFormatItem::from(fd!("foo")))]
#[case(BorrowedFormatItem::Compound(fd!("foo")))]
#[case(
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(fd!("foo")))
)]
#[case(
OwnedFormatItem::from(
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(fd!("foo")))
)
)]
fn failed_write_time(#[case] format: impl Formattable) -> time::Result<()> {
let value = Time::MIDNIGHT;
let success_len = value.format(&format)?.len();
for len in 0..success_len {
let mut buf = &mut vec![0; len][..];
let res = value.format_into(&mut buf, &format);
assert!(matches!(
res,
Err(time::error::Format::StdIo(e)) if e.kind() == io::ErrorKind::WriteZero
));
}
Ok(())
}
#[rstest]
#[case(Rfc3339)]
#[case(Rfc2822)]
#[case(Iso8601::DEFAULT)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_year_is_six_digits(true)
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_date_kind(DateKind::Ordinal)
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_year_is_six_digits(true)
.set_date_kind(DateKind::Ordinal)
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_year_is_six_digits(true)
.set_date_kind(DateKind::Week)
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_date_kind(DateKind::Week)
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Minute { decimal_digits: None })
.encode()
}>,
)]
#[case(
Iso8601::<{
iso8601::Config::DEFAULT
.set_time_precision(TimePrecision::Hour { decimal_digits: None })
.encode()
}>,
)]
fn failed_write_offset(#[case] format: impl Formattable) -> time::Result<()> {
let value = OffsetDateTime::UNIX_EPOCH;
let success_len = value.format(&format)?.len();
for len in 0..success_len {
let mut buf = &mut vec![0; len][..];
let res = value.format_into(&mut buf, &format);
assert!(matches!(
res,
Err(time::error::Format::StdIo(e)) if e.kind() == io::ErrorKind::WriteZero
));
}
Ok(())
}
#[rstest]
#[case(date!(-1-001), fd!("[year]"))]
#[case(date!(2021-001), fd!("[year sign:mandatory]"))]
#[case(date!(+999_999-001), fd!("[year]"))]
#[case(date!(+99_999-001), fd!("[year]"))]
fn failed_write_date(#[case] value: Date, #[case] format: impl Formattable) -> time::Result<()> {
let success_len = value.format(&format)?.len();
for len in 0..success_len {
let mut buf = &mut vec![0; len][..];
let res = value.format_into(&mut buf, &format);
assert!(matches!(
res,
Err(time::error::Format::StdIo(e)) if e.kind() == io::ErrorKind::WriteZero
));
}
Ok(())
}
#[rstest]
#[case(fd!("[day]"))]
#[case(fd!("[month]"))]
#[case(fd!("[ordinal]"))]
#[case(fd!("[weekday]"))]
#[case(fd!("[week_number]"))]
#[case(fd!("[year]"))]
#[case(fd!("[hour]"))]
#[case(fd!("[minute]"))]
#[case(fd!("[period]"))]
#[case(fd!("[second]"))]
#[case(fd!("[subsecond]"))]
#[case(fd!("[offset_hour]"))]
#[case(fd!("[offset_minute]"))]
#[case(fd!("[offset_second]"))]
fn failed_write_component(#[case] format: &[BorrowedFormatItem<'_>]) -> time::Result<()> {
let value = OffsetDateTime::UNIX_EPOCH;
let success_len = value.format(format)?.len();
for len in 0..success_len {
let mut buf = &mut vec![0; len][..];
let res = value.format_into(&mut buf, format);
assert!(
matches!(res,Err(time::error::Format::StdIo(e))if e.kind() == io::ErrorKind::WriteZero)
);
}
Ok(())
}
#[rstest]
#[case(BorrowedFormatItem::First(&[]), "")]
#[case(BorrowedFormatItem::First(&[BorrowedFormatItem::Compound(fd!("[hour]"))]), "00")]
fn first_borrowed(#[case] format_description: BorrowedFormatItem<'_>, #[case] expected: &str) {
assert!(matches!(Time::MIDNIGHT.format(&format_description), Ok(e) if e == expected));
}
#[rstest]
#[case(OwnedFormatItem::First(Box::new([])), "")]
#[case(
OwnedFormatItem::from(BorrowedFormatItem::First(&[
BorrowedFormatItem::Compound(fd!("[hour]"))
])),
"00"
)]
fn first_owned(#[case] format_description: OwnedFormatItem, #[case] expected: &str) {
assert!(matches!(Time::MIDNIGHT.format(&format_description), Ok(e) if e == expected));
}
#[rstest]
#[case(fd!("[ignore count:2]"), "")]
fn ignore(#[case] format_description: &[BorrowedFormatItem<'_>], #[case] expected: &str) {
assert!(matches!(Time::MIDNIGHT.format(format_description), Ok(e) if e == expected));
}
#[rstest]
#[case(fd!("[end]"), "")]
fn end(#[case] format_description: &[BorrowedFormatItem<'_>], #[case] expected: &str) {
assert!(matches!(Time::MIDNIGHT.format(format_description), Ok(e) if e == expected));
}
#[rstest]
#[case(
datetime!(2009-02-13 23:31:30.123456789 UTC),
fd!("[unix_timestamp]"),
"1234567890",
)]
#[case(
datetime!(2009-02-13 23:31:30.123456789 UTC),
fd!("[unix_timestamp sign:mandatory]"),
"+1234567890",
)]
#[case(
datetime!(2009-02-13 23:31:30.123456789 UTC),
fd!("[unix_timestamp precision:millisecond]"),
"1234567890123",
)]
#[case(
datetime!(2009-02-13 23:31:30.123456789 UTC),
fd!("[unix_timestamp precision:microsecond]"),
"1234567890123456",
)]
#[case(
datetime!(2009-02-13 23:31:30.123456789 UTC),
fd!("[unix_timestamp precision:nanosecond]"),
"1234567890123456789",
)]
#[case(
datetime!(1969-12-31 23:59:59 UTC),
fd!("[unix_timestamp]"),
"-1",
)]
fn unix_timestamp(
#[case] dt: OffsetDateTime,
#[case] format_description: &[BorrowedFormatItem<'_>],
#[case] expected: &str,
) -> time::Result<()> {
assert_eq!(dt.format(format_description)?, expected);
Ok(())
}
+262
View File
@@ -0,0 +1,262 @@
#![expect(deprecated)]
use std::cmp::Ordering;
use std::thread;
use std::time::Instant as StdInstant;
use rstest::rstest;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::{Instant, SignedDuration};
#[rstest]
fn elapsed() {
let instant = Instant::now();
thread::sleep(1.std_milliseconds());
assert!(instant.elapsed() >= 1.milliseconds());
}
#[rstest]
#[case(0.seconds())]
#[case(5.seconds())]
#[case((-5).seconds())]
fn checked_add(#[case] duration: SignedDuration) {
let now = Instant::now();
assert_eq!(now.checked_add(duration), Some(now + duration));
}
#[rstest]
#[case(0.seconds())]
#[case(5.seconds())]
#[case((-5).seconds())]
fn checked_sub(#[case] duration: SignedDuration) {
let now = Instant::now();
assert_eq!(now.checked_sub(duration), Some(now - duration));
}
#[rstest]
fn into_inner() {
let now = Instant::now();
assert_eq!(now.into_inner(), now.0);
}
#[rstest]
fn std_from() {
let now_time = Instant::now();
let now_std = StdInstant::from(now_time);
assert_eq!(now_time, now_std);
}
#[rstest]
fn from_std() {
let now_std = StdInstant::now();
let now_time = Instant::from(now_std);
assert_eq!(now_time, now_std);
}
#[rstest]
#[case(0)]
#[case(1)]
fn sub(#[case] ms: u64) {
let start = Instant::now();
thread::sleep(ms.std_milliseconds());
assert!(Instant::now() - start >= ms.cast_signed().milliseconds());
}
#[rstest]
#[case(0)]
#[case(1)]
fn sub_std(#[case] ms: u64) {
let start = StdInstant::now();
thread::sleep(ms.std_milliseconds());
assert!(Instant::now() - start >= ms.cast_signed().milliseconds());
}
#[rstest]
#[case(0)]
#[case(1)]
fn std_sub(#[case] ms: u64) {
let start = Instant::now();
thread::sleep(ms.std_milliseconds());
assert!(StdInstant::now() - start >= ms.cast_signed().milliseconds());
}
#[rstest]
#[case(0)]
#[case(1)]
fn add_duration(#[case] ms: u64) {
let start = Instant::now();
thread::sleep(ms.std_milliseconds());
assert!(start + ms.cast_signed().milliseconds() <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(1)]
fn std_add_duration(#[case] ms: u64) {
let start = StdInstant::now();
thread::sleep(ms.std_milliseconds());
assert!(start + ms.cast_signed().milliseconds() <= StdInstant::now());
}
#[rstest]
#[case(0)]
#[case(1)]
fn add_std_duration(#[case] ms: u64) {
let start = Instant::now();
thread::sleep(ms.std_milliseconds());
assert!(start + ms.std_milliseconds() <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(1)]
fn add_assign_duration(#[case] ms: u64) {
let mut start = Instant::now();
thread::sleep(ms.std_milliseconds());
start += ms.cast_signed().milliseconds();
assert!(start <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(1)]
fn std_add_assign_duration(#[case] ms: u64) {
let mut start = StdInstant::now();
thread::sleep(ms.std_milliseconds());
start += ms.cast_signed().milliseconds();
assert!(start <= StdInstant::now());
}
#[rstest]
#[case(0)]
#[case(1)]
fn add_assign_std_duration(#[case] ms: u64) {
let mut start = Instant::now();
thread::sleep(ms.std_milliseconds());
start += ms.std_milliseconds();
assert!(start <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(100)]
fn sub_duration(#[case] ms: i64) {
let instant = Instant::now();
assert!(instant - ms.milliseconds() <= Instant::now());
assert_eq!(instant - SignedDuration::ZERO, instant);
}
#[rstest]
#[case(0)]
#[case(100)]
fn std_sub_duration(#[case] ms: i64) {
let instant = StdInstant::now();
assert!(instant - ms.milliseconds() <= StdInstant::now());
}
#[rstest]
#[case(0)]
#[case(100)]
fn sub_std_duration(#[case] ms: u64) {
let instant = Instant::now();
assert!(instant - ms.std_milliseconds() <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(100)]
fn sub_assign_duration(#[case] ms: i64) {
let mut instant = Instant::now();
instant -= ms.milliseconds();
assert!(instant <= Instant::now());
}
#[rstest]
#[case(0)]
#[case(100)]
fn std_sub_assign_duration(#[case] ms: i64) {
let mut instant = StdInstant::now();
instant -= ms.milliseconds();
assert!(instant <= StdInstant::now());
}
#[rstest]
#[case(0)]
#[case(100)]
fn sub_assign_std_duration(#[case] ms: u64) {
let mut instant = Instant::now();
instant -= ms.std_milliseconds();
assert!(instant <= Instant::now());
}
#[rstest]
fn eq_std() {
let now_time = Instant::now();
let now_std = StdInstant::from(now_time);
assert_eq!(now_time, now_std);
}
#[rstest]
fn std_eq() {
let now_time = Instant::now();
let now_std = StdInstant::from(now_time);
assert_eq!(now_std, now_time);
}
#[rstest]
#[case(1.seconds(), Ordering::Less)]
#[case((-1).seconds(), Ordering::Greater)]
fn ord(#[case] duration: SignedDuration, #[case] expected: Ordering) {
let now_time = Instant::now();
let now_std = now_time + duration;
assert_eq!(now_time.cmp(&now_std), expected);
}
#[rstest]
#[case(1.seconds(), Ordering::Less)]
#[case((-1).seconds(), Ordering::Greater)]
fn partial_ord_std(#[case] duration: SignedDuration, #[case] expected: Ordering) {
let now_time = Instant::now();
let now_std = StdInstant::from(now_time) + duration;
assert_eq!(now_time.partial_cmp(&now_std), Some(expected));
}
#[rstest]
#[case(1.seconds(), Ordering::Greater)]
#[case((-1).seconds(), Ordering::Less)]
fn std_partial_ord(#[case] duration: SignedDuration, #[case] expected: Ordering) {
let now_time = Instant::now();
let now_std = StdInstant::from(now_time) + duration;
assert_eq!(now_std.partial_cmp(&now_time), Some(expected));
}
#[rstest]
fn sub_regression() {
let now = Instant::now();
let future = now + SignedDuration::seconds(5);
let past = now - SignedDuration::seconds(5);
assert_eq!(future - now, SignedDuration::seconds(5));
assert_eq!(now - past, SignedDuration::seconds(5));
assert_eq!(future - past, SignedDuration::seconds(10));
assert_eq!(now - future, SignedDuration::seconds(-5));
assert_eq!(past - now, SignedDuration::seconds(-5));
assert_eq!(past - future, SignedDuration::seconds(-10));
}
#[rstest]
fn as_ref() {
let now = Instant::now();
assert_eq!(now.as_ref(), now.as_ref());
}
#[rstest]
fn borrow() {
use std::borrow::Borrow;
let now = Instant::now();
assert_eq!(
<Instant as Borrow<StdInstant>>::borrow(&now),
<Instant as Borrow<StdInstant>>::borrow(&now)
);
}
+373
View File
@@ -0,0 +1,373 @@
use core::num::NonZero;
use rstest::rstest;
use time::format_description::modifier::{
CalendarYearFullExtendedRange, Day, End, Ignore, MonthNumerical, OffsetMinute, OffsetSecond,
Ordinal, Padding, Period, TrailingInput, UnixTimestampNanosecond, WeekNumberIso, WeekdayLong,
};
use time::format_description::{BorrowedFormatItem, Component};
use time::macros::{date, format_description, time};
use time::{Date, Time};
#[rstest]
fn nontrivial_string() {
assert!(format_description!(r"").is_empty());
assert!(format_description!(r###""###).is_empty());
assert!(format_description!(b"").is_empty());
assert!(format_description!(br"").is_empty());
assert!(format_description!(br###""###).is_empty());
#[rustfmt::skip]
assert_eq!(
format_description!("foo\
bar\n\r\t\\\"\'\0\x20\x4E\x4e\u{20}\u{4E}\u{4_e}"),
&[BorrowedFormatItem::StringLiteral("foobar\n\r\t\\\"'\0 NN NN")]
);
#[rustfmt::skip]
assert_eq!(
format_description!(b"foo\
bar\n\r\t\\\"\'\0\x20\x4E\x4e"),
&[BorrowedFormatItem::StringLiteral("foobar\n\r\t\\\"'\0 NN")]
);
}
#[rstest]
fn format_description_version() {
assert_eq!(
format_description!(version = 1, "[["),
&[BorrowedFormatItem::StringLiteral("[")]
);
assert_eq!(
format_description!(version = 1, r"\\"),
&[BorrowedFormatItem::StringLiteral(r"\\")]
);
assert_eq!(
format_description!(version = 2, r"\\"),
&[BorrowedFormatItem::StringLiteral(r"\")]
);
}
#[rstest]
fn nested_v1() {
assert_eq!(
format_description!(version = 1, "[optional [[[]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral("[")
)]
);
assert_eq!(
format_description!(version = 1, "[optional [ [[ ]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral(" [ ")
)]
);
assert_eq!(
format_description!(version = 1, "[first [a][[[]]"),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::StringLiteral("a"),
BorrowedFormatItem::StringLiteral("[")
])]
);
}
#[rstest]
fn optional() {
assert_eq!(
format_description!(version = 2, "[optional [:[year]]]"),
&[BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(
&[
BorrowedFormatItem::StringLiteral(":"),
BorrowedFormatItem::Component(Component::CalendarYearFullExtendedRange(
Default::default()
))
]
))]
);
assert_eq!(
format_description!(version = 2, "[optional [[year]]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::Component(Component::CalendarYearFullExtendedRange(
Default::default()
))
)]
);
assert_eq!(
format_description!(version = 2, r"[optional [\[]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral("[")
)]
);
assert_eq!(
format_description!(version = 2, r"[optional [ \[ ]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral(" [ ")
)]
);
}
#[rstest]
fn first() {
assert_eq!(
format_description!(version = 2, "[first [a]]"),
&[BorrowedFormatItem::StringLiteral("a")]
);
assert_eq!(
format_description!(version = 2, "[first [a] [b]]"),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::StringLiteral("a"),
BorrowedFormatItem::StringLiteral("b"),
])]
);
assert_eq!(
format_description!(version = 2, "[first [a][b]]"),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::StringLiteral("a"),
BorrowedFormatItem::StringLiteral("b"),
])]
);
assert_eq!(
format_description!(version = 2, r"[first [a][\[]]"),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::StringLiteral("a"),
BorrowedFormatItem::StringLiteral("["),
])]
);
assert_eq!(
format_description!(version = 2, r"[first [a][\[\[]]"),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::StringLiteral("a"),
BorrowedFormatItem::StringLiteral("[["),
])]
);
assert_eq!(
format_description!(
version = 2,
"[first [[period case:upper]] [[period case:lower]] ]"
),
&[BorrowedFormatItem::First(&[
BorrowedFormatItem::Component(Component::Period(
Period::default()
.with_is_uppercase(true)
.with_case_sensitive(true)
)),
BorrowedFormatItem::Component(Component::Period(
Period::default()
.with_is_uppercase(false)
.with_case_sensitive(true)
)),
])]
);
}
#[rstest]
fn backslash_escape() {
assert_eq!(
format_description!(version = 2, r"[optional [\]]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral("]")
)]
);
assert_eq!(
format_description!(version = 2, r"[optional [\[]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral("[")
)]
);
assert_eq!(
format_description!(version = 2, r"[optional [\\]]"),
&[BorrowedFormatItem::Optional(
&BorrowedFormatItem::StringLiteral(r"\")
)]
);
assert_eq!(
format_description!(version = 2, r"\\"),
&[BorrowedFormatItem::StringLiteral(r"\")]
);
assert_eq!(
format_description!(version = 2, r"\["),
&[BorrowedFormatItem::StringLiteral(r"[")]
);
assert_eq!(
format_description!(version = 2, r"\]"),
&[BorrowedFormatItem::StringLiteral(r"]")]
);
assert_eq!(
format_description!(version = 2, r"foo\\"),
&[BorrowedFormatItem::StringLiteral(r"foo\"),]
);
assert_eq!(
format_description!(version = 2, r"\\"),
&[BorrowedFormatItem::StringLiteral(r"\")]
);
assert_eq!(
format_description!(version = 2, r"\["),
&[BorrowedFormatItem::StringLiteral(r"[")]
);
assert_eq!(
format_description!(version = 2, r"\]"),
&[BorrowedFormatItem::StringLiteral(r"]")]
);
assert_eq!(
format_description!(version = 2, r"foo\\"),
&[BorrowedFormatItem::StringLiteral(r"foo\")]
);
}
#[rstest]
fn format_description_coverage() {
assert_eq!(
format_description!("[day padding:space][day padding:zero][day padding:none]"),
&[
BorrowedFormatItem::Component(Component::Day(
Day::default().with_padding(Padding::Space)
)),
BorrowedFormatItem::Component(Component::Day(
Day::default().with_padding(Padding::Zero)
)),
BorrowedFormatItem::Component(Component::Day(
Day::default().with_padding(Padding::None)
))
]
);
assert_eq!(
format_description!(
"[offset_minute padding:space][offset_minute padding:zero][offset_minute padding:none]"
),
&[
BorrowedFormatItem::Component(Component::OffsetMinute(
OffsetMinute::default().with_padding(Padding::Space)
)),
BorrowedFormatItem::Component(Component::OffsetMinute(
OffsetMinute::default().with_padding(Padding::Zero)
)),
BorrowedFormatItem::Component(Component::OffsetMinute(
OffsetMinute::default().with_padding(Padding::None)
))
]
);
assert_eq!(
format_description!(
"[offset_second padding:space][offset_second padding:zero][offset_second padding:none]"
),
&[
BorrowedFormatItem::Component(Component::OffsetSecond(
OffsetSecond::default().with_padding(Padding::Space)
)),
BorrowedFormatItem::Component(Component::OffsetSecond(
OffsetSecond::default().with_padding(Padding::Zero)
)),
BorrowedFormatItem::Component(Component::OffsetSecond(
OffsetSecond::default().with_padding(Padding::None)
)),
]
);
assert_eq!(
format_description!("[ordinal padding:space][ordinal padding:zero][ordinal padding:none]"),
&[
BorrowedFormatItem::Component(Component::Ordinal(
Ordinal::default().with_padding(Padding::Space)
)),
BorrowedFormatItem::Component(Component::Ordinal(
Ordinal::default().with_padding(Padding::Zero)
)),
BorrowedFormatItem::Component(Component::Ordinal(
Ordinal::default().with_padding(Padding::None)
)),
]
);
assert_eq!(
format_description!("[month repr:numerical]"),
&[BorrowedFormatItem::Component(Component::MonthNumerical(
MonthNumerical::default().with_padding(Padding::Zero)
))]
);
assert_eq!(
format_description!("[week_number repr:iso ]"),
&[BorrowedFormatItem::Component(Component::WeekNumberIso(
WeekNumberIso::default().with_padding(Padding::Zero)
))]
);
assert_eq!(
format_description!("[weekday repr:long]"),
&[BorrowedFormatItem::Component(Component::WeekdayLong(
WeekdayLong::default()
))]
);
assert_eq!(
format_description!("[year repr:full base:calendar]"),
&[BorrowedFormatItem::Component(
Component::CalendarYearFullExtendedRange(
CalendarYearFullExtendedRange::default()
.with_padding(Padding::Zero)
.with_sign_is_mandatory(false)
)
)]
);
assert_eq!(
format_description!("[[ "),
&[BorrowedFormatItem::StringLiteral("[ ")]
);
assert_eq!(
format_description!("[ignore count:2]"),
&[BorrowedFormatItem::Component(Component::Ignore(
Ignore::count(const { NonZero::new(2).unwrap() })
))]
);
assert_eq!(
format_description!("[unix_timestamp precision:nanosecond sign:mandatory]"),
&[BorrowedFormatItem::Component(
Component::UnixTimestampNanosecond(
UnixTimestampNanosecond::default().with_sign_is_mandatory(true)
)
)]
);
assert_eq!(
format_description!("[end]"),
&[BorrowedFormatItem::Component(
Component::End(End::default())
)]
);
assert_eq!(
format_description!("[end trailing_input:prohibit]"),
&[BorrowedFormatItem::Component(Component::End(
End::default().with_trailing_input(TrailingInput::Prohibit)
))]
);
assert_eq!(
format_description!("[end trailing_input:discard]"),
&[BorrowedFormatItem::Component(Component::End(
End::default().with_trailing_input(TrailingInput::Discard)
))]
);
}
#[rstest]
fn date_coverage() {
assert_eq!(Ok(date!(2000-001)), Date::from_ordinal_date(2000, 1));
assert_eq!(Ok(date!(2019-W01-1)), Date::from_ordinal_date(2018, 365));
assert_eq!(Ok(date!(2021-W52-6)), Date::from_ordinal_date(2022, 1));
assert_eq!(Ok(date!(2021-W34-5)), Date::from_ordinal_date(2021, 239));
}
#[rstest]
fn time_coverage() {
assert_eq!(time!(12 AM), Time::MIDNIGHT);
assert_eq!(Ok(time!(12 PM)), Time::from_hms(12, 0, 0));
}
mod demo {
#[expect(dead_code)]
type Result<T> = core::result::Result<T, ()>;
#[expect(dead_code)]
type Option = core::option::Option<()>;
#[expect(dead_code)]
fn serialize() {}
#[expect(dead_code)]
fn deserialize() {}
time::serde::format_description!(
seconds,
OffsetDateTime,
"[year]-[month]-[day]T[hour]:[minute]:[second]Z"
);
}
+131
View File
@@ -0,0 +1,131 @@
#![allow(missing_docs, reason = "may be removed in the future")]
#![allow(
clippy::missing_const_for_fn,
clippy::std_instead_of_core,
clippy::std_instead_of_alloc,
clippy::alloc_instead_of_core,
reason = "irrelevant for tests"
)]
#[cfg(not(all(
feature = "default",
feature = "alloc",
feature = "formatting",
feature = "large-dates",
feature = "local-offset",
feature = "macros",
feature = "parsing",
feature = "quickcheck",
feature = "serde-human-readable",
feature = "serde-well-known",
feature = "std",
feature = "rand",
feature = "serde",
)))]
#[rstest]
fn run_with_all_features() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Debug)]
struct Error(std::process::ExitStatus);
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::error::Error for Error {}
let status = std::process::Command::new("cargo")
.args(["test", "--all-features"])
.status()?;
return if status.success() {
Ok(())
} else {
Err(Box::new(Error(status)))
};
// Intentionally unreachable. This is to show the user a warning when they don't provide
// `--all-features`.
"Tests must be run with `--all-features`. Because the flag was not provided, `cargo test \
--all-features` is run.";
}
macro_rules! require_all_features {
($($x:item)*) => {$(
#[cfg(all(
feature = "default",
feature = "alloc",
feature = "formatting",
feature = "large-dates",
feature = "local-offset",
feature = "macros",
feature = "parsing",
feature = "quickcheck",
feature = "serde-human-readable",
feature = "serde-well-known",
feature = "std",
feature = "rand",
feature = "serde",
))]
$x
)*};
}
require_all_features! {
/// `assert_eq!` or `assert_ne!` depending on the value of `$is_eq`.
///
/// This provides better diagnostics than `assert_eq!($left == $right, $is_eq)`.
macro_rules! assert_eq_ne {
($left:expr, $right:expr, $is_eq:expr $(, $($rest:tt)*)?) => {{
if $is_eq {
assert_eq!($left, $right $(, $($rest)*)?);
} else {
assert_ne!($left, $right $(, $($rest)*)?);
}
}}
}
#[cfg(__ui_tests)]
use rstest::rstest;
mod convert;
mod date;
mod date_iter;
mod month_iter;
mod weekday_iter;
mod derives;
mod duration;
mod error;
mod ext;
mod format_description;
mod formatting;
mod instant;
mod macros;
mod meta;
mod month;
mod offset_date_time;
mod parse_format_description;
mod parsed;
mod parsing;
mod plain_date_time;
#[path = "quickcheck.rs"]
mod quickcheck_mod;
mod rand;
mod serde;
mod serde_helpers;
mod time;
mod timestamp;
mod utc_date_time;
mod utc_offset;
mod util;
mod weekday;
#[cfg(__ui_tests)]
#[rstest]
fn compile_fail() {
let tests = trybuild::TestCases::new();
// Path is relative from `time/Cargo.toml`.
tests.compile_fail("./tests/integration/compile-fail/*.rs");
}
}
File diff suppressed because it is too large Load Diff
+178
View File
@@ -0,0 +1,178 @@
use rstest::rstest;
use time::Month::{self, *};
#[rstest]
#[case(January, December)]
#[case(February, January)]
#[case(March, February)]
#[case(April, March)]
#[case(May, April)]
#[case(June, May)]
#[case(July, June)]
#[case(August, July)]
#[case(September, August)]
#[case(October, September)]
#[case(November, October)]
#[case(December, November)]
fn previous(#[case] month: Month, #[case] expected: Month) {
assert_eq!(month.previous(), expected);
}
#[rstest]
#[case(January, February)]
#[case(February, March)]
#[case(March, April)]
#[case(April, May)]
#[case(May, June)]
#[case(June, July)]
#[case(July, August)]
#[case(August, September)]
#[case(September, October)]
#[case(October, November)]
#[case(November, December)]
#[case(December, January)]
fn next(#[case] month: Month, #[case] expected: Month) {
assert_eq!(month.next(), expected);
}
#[rstest]
#[case(January, 0, January)]
#[case(January, 1, February)]
#[case(January, 2, March)]
#[case(January, 3, April)]
#[case(January, 4, May)]
#[case(January, 5, June)]
#[case(January, 6, July)]
#[case(January, 7, August)]
#[case(January, 8, September)]
#[case(January, 9, October)]
#[case(January, 10, November)]
#[case(January, 11, December)]
#[case(December, 0, December)]
#[case(December, 1, January)]
#[case(December, 2, February)]
#[case(December, 3, March)]
#[case(December, 4, April)]
#[case(December, 5, May)]
#[case(December, 6, June)]
#[case(December, 7, July)]
#[case(December, 8, August)]
#[case(December, 9, September)]
#[case(December, 10, October)]
#[case(December, 11, November)]
#[case(January, 12, January)]
#[case(January, u8::MAX, April)]
#[case(December, 12, December)]
#[case(December, u8::MAX, March)]
fn nth_next(#[case] month: Month, #[case] n: u8, #[case] expected: Month) {
assert_eq!(month.nth_next(n), expected);
}
#[rstest]
#[case(January, 0, January)]
#[case(January, 1, December)]
#[case(January, 2, November)]
#[case(January, 3, October)]
#[case(January, 4, September)]
#[case(January, 5, August)]
#[case(January, 6, July)]
#[case(January, 7, June)]
#[case(January, 8, May)]
#[case(January, 9, April)]
#[case(January, 10, March)]
#[case(January, 11, February)]
#[case(December, 0, December)]
#[case(December, 1, November)]
#[case(December, 2, October)]
#[case(December, 3, September)]
#[case(December, 4, August)]
#[case(December, 5, July)]
#[case(December, 6, June)]
#[case(December, 7, May)]
#[case(December, 8, April)]
#[case(December, 9, March)]
#[case(December, 10, February)]
#[case(December, 11, January)]
#[case(January, 12, January)]
#[case(January, u8::MAX, October)]
#[case(December, 12, December)]
#[case(December, u8::MAX, September)]
fn nth_prev(#[case] month: Month, #[case] n: u8, #[case] expected: Month) {
assert_eq!(month.nth_prev(n), expected);
}
#[rstest]
#[case(January, "January")]
#[case(February, "February")]
#[case(March, "March")]
#[case(April, "April")]
#[case(May, "May")]
#[case(June, "June")]
#[case(July, "July")]
#[case(August, "August")]
#[case(September, "September")]
#[case(October, "October")]
#[case(November, "November")]
#[case(December, "December")]
fn display(#[case] month: Month, #[case] expected: &str) {
assert_eq!(month.to_string(), expected);
}
#[rstest]
#[case("January", Ok(January))]
#[case("February", Ok(February))]
#[case("March", Ok(March))]
#[case("April", Ok(April))]
#[case("May", Ok(May))]
#[case("June", Ok(June))]
#[case("July", Ok(July))]
#[case("August", Ok(August))]
#[case("September", Ok(September))]
#[case("October", Ok(October))]
#[case("November", Ok(November))]
#[case("December", Ok(December))]
#[case("foo", Err(time::error::InvalidVariant))]
fn from_str(#[case] s: &str, #[case] expected: Result<Month, time::error::InvalidVariant>) {
assert_eq!(s.parse::<Month>(), expected);
}
#[rstest]
#[case(January, 1)]
#[case(February, 2)]
#[case(March, 3)]
#[case(April, 4)]
#[case(May, 5)]
#[case(June, 6)]
#[case(July, 7)]
#[case(August, 8)]
#[case(September, 9)]
#[case(October, 10)]
#[case(November, 11)]
#[case(December, 12)]
fn to_u8(#[case] month: Month, #[case] expected: u8) {
assert_eq!(u8::from(month), expected);
}
#[rstest]
#[case(1, January)]
#[case(2, February)]
#[case(3, March)]
#[case(4, April)]
#[case(5, May)]
#[case(6, June)]
#[case(7, July)]
#[case(8, August)]
#[case(9, September)]
#[case(10, October)]
#[case(11, November)]
#[case(12, December)]
fn try_from_u8_success(#[case] input: u8, #[case] expected: Month) {
assert_eq!(Month::try_from(input), Ok(expected));
}
#[rstest]
#[case(0)]
#[case(13)]
fn try_from_u8_error(#[case] input: u8) {
assert!(matches!(Month::try_from(input), Err(err) if err.name() == "month"));
}
@@ -0,0 +1,180 @@
use rstest::rstest;
use rstest_reuse::{apply, template};
use time::Month::{self, *};
use time::iter::MonthIter;
#[template]
#[rstest]
fn all_months(
#[values(
January, February, March, April, May, June, July, August, September, October, November,
December
)]
start: Month,
) {
}
#[apply(all_months)]
fn new(start: Month) {
let mut iter = MonthIter::new(start);
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.next()));
}
#[apply(all_months)]
fn iter_from(start: Month) {
let mut iter = Month::iter_from(start);
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.next()));
}
#[apply(all_months)]
fn size_hint(start: Month) {
assert_eq!(MonthIter::new(start).size_hint(), (usize::MAX, None));
}
#[apply(all_months)]
fn max(start: Month) {
assert_eq!(MonthIter::new(start).max(), Some(December));
}
#[apply(all_months)]
fn min(start: Month) {
assert_eq!(MonthIter::new(start).min(), Some(January));
}
#[apply(all_months)]
fn is_sorted(start: Month) {
assert!(!MonthIter::new(start).is_sorted());
}
#[apply(all_months)]
fn nth(start: Month, #[values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)] n: u8) {
let expected = start.nth_next(n);
assert_eq!(MonthIter::new(start).nth(n as usize), Some(expected));
}
#[apply(all_months)]
fn any_checks_all_months(
start: Month,
#[values(
January, February, March, April, May, June, July, August, September, October, November,
December
)]
target: Month,
) {
assert!(MonthIter::new(start).any(|m| m == target));
}
#[apply(all_months)]
fn any_terminates(start: Month) {
assert!(!MonthIter::new(start).any(|_| false));
}
#[apply(all_months)]
fn all_checks_all_months(
start: Month,
#[values(
January, February, March, April, May, June, July, August, September, October, November,
December
)]
excludes: Month,
) {
assert!(!MonthIter::new(start).all(|m| m != excludes));
}
#[apply(all_months)]
fn all_terminates(start: Month) {
assert!(MonthIter::new(start).all(|_| true));
}
#[apply(all_months)]
fn rev_next(start: Month) {
let mut iter = MonthIter::new(start).rev();
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.previous()));
}
#[apply(all_months)]
fn rev_nth(start: Month, #[values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)] n: u8) {
let expected = start.nth_prev(n);
assert_eq!(MonthIter::new(start).rev().nth(n as usize), Some(expected));
}
#[apply(all_months)]
fn rev_size_hint(start: Month) {
assert_eq!(MonthIter::new(start).rev().size_hint(), (usize::MAX, None));
}
#[apply(all_months)]
fn rev_max(start: Month) {
assert_eq!(MonthIter::new(start).rev().max(), Some(December));
}
#[apply(all_months)]
fn rev_min(start: Month) {
assert_eq!(MonthIter::new(start).rev().min(), Some(January));
}
#[apply(all_months)]
fn rev_is_sorted(start: Month) {
assert!(!MonthIter::new(start).rev().is_sorted());
}
#[apply(all_months)]
#[should_panic(expected = "infinite")]
fn count_panics(start: Month) {
MonthIter::new(start).count();
}
#[apply(all_months)]
#[should_panic(expected = "infinite")]
fn last_panics(start: Month) {
MonthIter::new(start).last();
}
#[apply(all_months)]
#[should_panic(expected = "infinite")]
fn rev_count_panics(start: Month) {
MonthIter::new(start).rev().count();
}
#[apply(all_months)]
#[should_panic(expected = "infinite")]
fn rev_last_panics(start: Month) {
MonthIter::new(start).rev().last();
}
#[apply(all_months)]
fn rev_any_checks_all_months(
start: Month,
#[values(
January, February, March, April, May, June, July, August, September, October, November,
December
)]
target: Month,
) {
assert!(MonthIter::new(start).rev().any(|m| m == target));
}
#[apply(all_months)]
fn rev_any_terminates(start: Month) {
assert!(!MonthIter::new(start).rev().any(|_| false));
}
#[apply(all_months)]
fn rev_all_checks_all_months(
start: Month,
#[values(
January, February, March, April, May, June, July, August, September, October, November,
December
)]
excludes: Month,
) {
assert!(!MonthIter::new(start).rev().all(|m| m != excludes));
}
#[apply(all_months)]
fn rev_all_terminates(start: Month) {
assert!(MonthIter::new(start).rev().all(|_| true));
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+152
View File
@@ -0,0 +1,152 @@
use std::fmt::Debug;
use std::num::NonZero;
use rstest::rstest;
use time::format_description::modifier::{Hour12, WeekNumberIso};
use time::format_description::{BorrowedFormatItem, Component};
use time::parsing::Parsed;
use time::{Month, Time, Weekday, error};
#[rstest]
#[case(Parsed::set_year, Parsed::year, 5)]
#[case(Parsed::set_year_last_two, Parsed::year_last_two, 5)]
#[case(Parsed::set_iso_year, Parsed::iso_year, 5)]
#[case(Parsed::set_iso_year_last_two, Parsed::iso_year_last_two, 5)]
#[case(Parsed::set_month, Parsed::month, Month::May)]
#[case(Parsed::set_sunday_week_number, Parsed::sunday_week_number, 5)]
#[case(Parsed::set_monday_week_number, Parsed::monday_week_number, 5)]
#[case(Parsed::set_iso_week_number, Parsed::iso_week_number, const { NonZero::new(5).unwrap() })]
#[case(Parsed::set_weekday, Parsed::weekday, Weekday::Monday)]
#[case(Parsed::set_ordinal, Parsed::ordinal, const { NonZero::new(5).unwrap() })]
#[case(Parsed::set_day, Parsed::day, const { NonZero::new(5).unwrap() })]
#[case(Parsed::set_hour_24, Parsed::hour_24, 5)]
#[case(Parsed::set_hour_12, Parsed::hour_12, const { NonZero::new(5).unwrap() })]
#[case(Parsed::set_hour_12_is_pm, Parsed::hour_12_is_pm, true)]
#[case(Parsed::set_minute, Parsed::minute, 5)]
#[case(Parsed::set_second, Parsed::second, 5)]
#[case(Parsed::set_subsecond, Parsed::subsecond, 5)]
#[case(Parsed::set_offset_hour, Parsed::offset_hour, 5)]
#[expect(deprecated)]
#[case(Parsed::set_offset_minute, Parsed::offset_minute, 5)]
#[case(Parsed::set_offset_minute_signed, Parsed::offset_minute_signed, -5)]
#[expect(deprecated)]
#[case(Parsed::set_offset_second, Parsed::offset_second, 5)]
#[case(Parsed::set_offset_second_signed, Parsed::offset_second_signed, -5)]
fn getters_setters<T>(
#[case] setter: fn(&mut Parsed, T) -> Option<()>,
#[case] getter: fn(&Parsed) -> Option<T>,
#[case] value: T,
) where
T: PartialEq + Copy + Debug,
{
let mut parsed = Parsed::new();
assert!(setter(&mut parsed, value).is_some());
assert_eq!(getter(&parsed), Some(value));
}
#[rstest]
#[expect(deprecated)]
#[case(Parsed::set_offset_minute, Parsed::offset_minute, 200)]
#[expect(deprecated)]
#[case(Parsed::set_offset_second, Parsed::offset_second, 200)]
fn getters_setters_fail<T>(
#[case] setter: fn(&mut Parsed, T) -> Option<()>,
#[case] getter: fn(&Parsed) -> Option<T>,
#[case] value: T,
) {
let mut parsed = Parsed::new();
assert!(setter(&mut parsed, value).is_none());
assert!(getter(&parsed).is_none());
}
#[rstest]
#[case(Parsed::with_year, Parsed::year, 5)]
#[case(Parsed::with_year_last_two, Parsed::year_last_two, 5)]
#[case(Parsed::with_iso_year, Parsed::iso_year, 5)]
#[case(Parsed::with_iso_year_last_two, Parsed::iso_year_last_two, 5)]
#[case(Parsed::with_month, Parsed::month, Month::May)]
#[case(Parsed::with_sunday_week_number, Parsed::sunday_week_number, 5)]
#[case(Parsed::with_monday_week_number, Parsed::monday_week_number, 5)]
#[case(Parsed::with_iso_week_number, Parsed::iso_week_number, const { NonZero::new(5).unwrap() })]
#[case(Parsed::with_weekday, Parsed::weekday, Weekday::Monday)]
#[case(Parsed::with_ordinal, Parsed::ordinal, const { NonZero::new(5).unwrap() })]
#[case(Parsed::with_day, Parsed::day, const { NonZero::new(5).unwrap() })]
#[case(Parsed::with_hour_24, Parsed::hour_24, 5)]
#[case(Parsed::with_hour_12, Parsed::hour_12, const { NonZero::new(5).unwrap() })]
#[case(Parsed::with_hour_12_is_pm, Parsed::hour_12_is_pm, true)]
#[case(Parsed::with_minute, Parsed::minute, 5)]
#[case(Parsed::with_second, Parsed::second, 5)]
#[case(Parsed::with_subsecond, Parsed::subsecond, 5)]
#[case(Parsed::with_offset_hour, Parsed::offset_hour, 5)]
#[expect(deprecated)]
#[case(Parsed::with_offset_minute, Parsed::offset_minute, 5)]
#[case(Parsed::with_offset_minute_signed, Parsed::offset_minute_signed, -5)]
#[expect(deprecated)]
#[case(Parsed::with_offset_second, Parsed::offset_second, 5)]
#[case(Parsed::with_offset_second_signed, Parsed::offset_second_signed, -5)]
fn builder_methods<T>(
#[case] builder: fn(Parsed, T) -> Option<Parsed>,
#[case] getter: fn(&Parsed) -> Option<T>,
#[case] value: T,
) where
T: PartialEq + Copy + Debug,
{
let parsed = builder(Parsed::new(), value).expect("valid value");
assert_eq!(getter(&parsed), Some(value));
}
#[rstest]
#[expect(deprecated)]
#[case(Parsed::with_offset_minute, Parsed::offset_minute, 200)]
#[expect(deprecated)]
#[case(Parsed::with_offset_second, Parsed::offset_second, 200)]
fn builder_methods_fail<T>(
#[case] builder: fn(Parsed, T) -> Option<Parsed>,
#[case] getter: fn(&Parsed) -> Option<T>,
#[case] value: T,
) {
assert!(builder(Parsed::new(), value).is_none());
assert!(getter(&Parsed::new()).is_none());
}
#[rstest]
#[case("a", BorrowedFormatItem::StringLiteral("a"))]
#[case("b", BorrowedFormatItem::StringLiteral("a"))]
fn single_item_parse(#[case] input: &str, #[case] format_item: BorrowedFormatItem) {
assert!(Time::parse(input, &format_item).is_err());
}
#[rstest]
#[case("day", Component::Day(<_>::default()), b"")]
#[case("month", Component::MonthNumerical(<_>::default()), b"")]
#[case("ordinal", Component::Ordinal(<_>::default()), b"")]
#[case("weekday", Component::WeekdayLong(<_>::default()), b"")]
#[case("week number", Component::WeekNumberIso(<_>::default()), b"")]
#[case("year", Component::CalendarYearFullExtendedRange(<_>::default()), b"")]
#[case("minute", Component::Minute(<_>::default()), b"")]
#[case("period", Component::Period(<_>::default()), b"")]
#[case("second", Component::Second(<_>::default()), b"")]
#[case("subsecond", Component::Subsecond(<_>::default()), b"")]
#[case("offset hour", Component::OffsetHour(<_>::default()), b"")]
#[case("offset minute", Component::OffsetMinute(<_>::default()), b"")]
#[case("offset second", Component::OffsetSecond(<_>::default()), b"")]
#[case("unix_timestamp", Component::UnixTimestampSecond(<_>::default()), b"")]
#[case(
"week number",
Component::WeekNumberIso(WeekNumberIso::default()),
b"00"
)]
#[case("hour", Component::Hour12(Hour12::default()), b"00")]
fn component_err(
#[case] component_name: &'static str,
#[case] component: Component,
#[case] input: &[u8],
) {
let mut parsed = Parsed::new();
assert_eq!(
parsed.parse_component(input, component),
Err(error::ParseFromDescription::InvalidComponent(
component_name
))
);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,630 @@
use std::cmp::Ordering;
use rstest::rstest;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::{date, datetime, offset, time};
use time::{Date, Month, PlainDateTime, SignedDuration, Time, UtcOffset, Weekday};
#[rstest]
#[case(PlainDateTime::new(date!(2019-01-01), time!(0:00)), datetime!(2019-01-01 0:00))]
fn new(#[case] input: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(input, expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), date!(2019-01-01))]
fn date(#[case] datetime: PlainDateTime, #[case] expected: Date) {
assert_eq!(datetime.date(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), time!(0:00))]
fn time_(#[case] datetime: PlainDateTime, #[case] expected: Time) {
assert_eq!(datetime.time(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 2019)]
#[case(datetime!(2019-12-31 0:00), 2019)]
#[case(datetime!(2020-01-01 0:00), 2020)]
fn year(#[case] datetime: PlainDateTime, #[case] expected: i32) {
assert_eq!(datetime.year(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), Month::January)]
#[case(datetime!(2019-12-31 0:00), Month::December)]
fn month(#[case] datetime: PlainDateTime, #[case] expected: Month) {
assert_eq!(datetime.month(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 1)]
#[case(datetime!(2019-12-31 0:00), 31)]
fn day(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.day(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 1)]
#[case(datetime!(2019-12-31 0:00), 365)]
fn ordinal(#[case] datetime: PlainDateTime, #[case] expected: u16) {
assert_eq!(datetime.ordinal(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 1)]
#[case(datetime!(2019-10-04 0:00), 40)]
#[case(datetime!(2020-01-01 0:00), 1)]
#[case(datetime!(2020-12-31 0:00), 53)]
#[case(datetime!(2021-01-01 0:00), 53)]
fn iso_week(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.iso_week(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2020-01-01 0:00), 0)]
#[case(datetime!(2020-12-31 0:00), 52)]
#[case(datetime!(2021-01-01 0:00), 0)]
fn sunday_based_week(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.sunday_based_week(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2020-01-01 0:00), 0)]
#[case(datetime!(2020-12-31 0:00), 52)]
#[case(datetime!(2021-01-01 0:00), 0)]
fn monday_based_week(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.monday_based_week(), expected);
}
#[rstest]
#[case(datetime!(2019-01-02 0:00), (2019, Month::January, 2))]
fn to_calendar_date(#[case] datetime: PlainDateTime, #[case] expected: (i32, Month, u8)) {
assert_eq!(datetime.to_calendar_date(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), (2019, 1))]
fn to_ordinal_date(#[case] datetime: PlainDateTime, #[case] expected: (i32, u16)) {
assert_eq!(datetime.to_ordinal_date(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), (2019, 1, Weekday::Tuesday))]
#[case(datetime!(2019-10-04 0:00), (2019, 40, Weekday::Friday))]
#[case(datetime!(2020-01-01 0:00), (2020, 1, Weekday::Wednesday))]
#[case(datetime!(2020-12-31 0:00), (2020, 53, Weekday::Thursday))]
#[case(datetime!(2021-01-01 0:00), (2020, 53, Weekday::Friday))]
fn to_iso_week_date(#[case] datetime: PlainDateTime, #[case] expected: (i32, u8, Weekday)) {
assert_eq!(datetime.to_iso_week_date(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), Weekday::Tuesday)]
#[case(datetime!(2019-02-01 0:00), Weekday::Friday)]
#[case(datetime!(2019-03-01 0:00), Weekday::Friday)]
#[case(datetime!(2019-04-01 0:00), Weekday::Monday)]
#[case(datetime!(2019-05-01 0:00), Weekday::Wednesday)]
#[case(datetime!(2019-06-01 0:00), Weekday::Saturday)]
#[case(datetime!(2019-07-01 0:00), Weekday::Monday)]
#[case(datetime!(2019-08-01 0:00), Weekday::Thursday)]
#[case(datetime!(2019-09-01 0:00), Weekday::Sunday)]
#[case(datetime!(2019-10-01 0:00), Weekday::Tuesday)]
#[case(datetime!(2019-11-01 0:00), Weekday::Friday)]
#[case(datetime!(2019-12-01 0:00), Weekday::Sunday)]
fn weekday(#[case] datetime: PlainDateTime, #[case] expected: Weekday) {
assert_eq!(datetime.weekday(), expected);
}
#[rstest]
#[case(datetime!(-999_999-01-01 0:00), -363_521_074)]
#[case(datetime!(-4713-11-24 0:00), 0)]
#[case(datetime!(2000-01-01 0:00), 2_451_545)]
#[case(datetime!(2019-01-01 0:00), 2_458_485)]
#[case(datetime!(2019-12-31 0:00), 2_458_849)]
fn to_julian_day(#[case] datetime: PlainDateTime, #[case] expected: i32) {
assert_eq!(datetime.to_julian_day(), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 1:02:03), (1, 2, 3))]
fn as_hms(#[case] datetime: PlainDateTime, #[case] expected: (u8, u8, u8)) {
assert_eq!(datetime.as_hms(), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 1:02:03.004), (1, 2, 3, 4))]
fn as_hms_milli(#[case] datetime: PlainDateTime, #[case] expected: (u8, u8, u8, u16)) {
assert_eq!(datetime.as_hms_milli(), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 1:02:03.004_005), (1, 2, 3, 4_005))]
fn as_hms_micro(#[case] datetime: PlainDateTime, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(datetime.as_hms_micro(), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 1:02:03.004_005_006), (1, 2, 3, 4_005_006))]
fn as_hms_nano(#[case] datetime: PlainDateTime, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(datetime.as_hms_nano(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59), 23)]
fn hour(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.hour(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59), 59)]
fn minute(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.minute(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59), 59)]
fn second(#[case] datetime: PlainDateTime, #[case] expected: u8) {
assert_eq!(datetime.second(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59.999), 999)]
fn millisecond(#[case] datetime: PlainDateTime, #[case] expected: u16) {
assert_eq!(datetime.millisecond(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59.999_999), 999_999)]
fn microsecond(#[case] datetime: PlainDateTime, #[case] expected: u32) {
assert_eq!(datetime.microsecond(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 0)]
#[case(datetime!(2019-01-01 23:59:59.999_999_999), 999_999_999)]
fn nanosecond(#[case] datetime: PlainDateTime, #[case] expected: u32) {
assert_eq!(datetime.nanosecond(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), offset!(UTC), 1_546_300_800)]
#[case(datetime!(2019-01-01 0:00), offset!(-1), 1_546_304_400)]
fn assume_offset(
#[case] datetime: PlainDateTime,
#[case] offset: UtcOffset,
#[case] expected: i64,
) {
assert_eq!(datetime.assume_offset(offset).unix_timestamp(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 1_546_300_800)]
fn assume_utc(#[case] datetime: PlainDateTime, #[case] expected: i64) {
assert_eq!(datetime.assume_utc().unix_timestamp(), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 12:00), time!(5:00), datetime!(2020-01-01 5:00))]
fn replace_time(
#[case] datetime: PlainDateTime,
#[case] replacement: Time,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime.replace_time(replacement), expected);
}
#[rstest]
#[case(datetime!(2020-01-01 12:00), date!(2020-01-30), datetime!(2020-01-30 12:00))]
fn replace_date(
#[case] datetime: PlainDateTime,
#[case] replacement: Date,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime.replace_date(replacement), expected);
}
#[rstest]
#[case(datetime!(2022-02-18 12:00), 2019, datetime!(2019-02-18 12:00))]
#[case(datetime!(2022-02-18 12:00), -1_000_000_000, None)]
#[case(datetime!(2022-02-18 12:00), 1_000_000_000, None)]
fn replace_year(
#[case] datetime: PlainDateTime,
#[case] year: i32,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_year(year).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 12:00), Month::January, datetime!(2022-01-18 12:00))]
#[case(datetime!(2022-01-30 12:00), Month::February, None)]
fn replace_month(
#[case] datetime: PlainDateTime,
#[case] month: Month,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_month(month).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 12:00), 1, datetime!(2022-02-01 12:00))]
#[case(datetime!(2022-02-18 12:00), 0, None)]
#[case(datetime!(2022-02-18 12:00), 30, None)]
fn replace_day(
#[case] datetime: PlainDateTime,
#[case] day: u8,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_day(day).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 12:00), 1, datetime!(2022-001 12:00))]
#[case(datetime!(2024-02-29 12:00), 366, datetime!(2024-366 12:00))]
#[case(datetime!(2022-049 12:00), 0, None)]
#[case(datetime!(2022-049 12:00), 366, None)]
#[case(datetime!(2022-049 12:00), 367, None)]
fn replace_ordinal(
#[case] datetime: PlainDateTime,
#[case] ordinal: u16,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_ordinal(ordinal).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 7, datetime!(2022-02-18 07:02:03.004_005_006))]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 24, None)]
fn replace_hour(
#[case] datetime: PlainDateTime,
#[case] value: u8,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_hour(value).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 7, datetime!(2022-02-18 01:07:03.004_005_006))]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 60, None)]
fn replace_minute(
#[case] datetime: PlainDateTime,
#[case] value: u8,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_minute(value).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 7, datetime!(2022-02-18 01:02:07.004_005_006))]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 60, None)]
fn replace_second(
#[case] datetime: PlainDateTime,
#[case] value: u8,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_second(value).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 7, datetime!(2022-02-18 01:02:03.007))]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 1_000, None)]
fn replace_millisecond(
#[case] datetime: PlainDateTime,
#[case] value: u16,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_millisecond(value).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 7_008, datetime!(2022-02-18 01:02:03.007_008))]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 1_000_000, None)]
fn replace_microsecond(
#[case] datetime: PlainDateTime,
#[case] value: u32,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_microsecond(value).ok(), expected.into());
}
#[rstest]
#[case(
datetime!(2022-02-18 01:02:03.004_005_006),
7_008_009,
datetime!(2022-02-18 01:02:03.007_008_009),
)]
#[case(datetime!(2022-02-18 01:02:03.004_005_006), 1_000_000_000, None)]
fn replace_nanosecond(
#[case] datetime: PlainDateTime,
#[case] value: u32,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.replace_nanosecond(value).ok(), expected.into());
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 0:00))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_day(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_day(), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 17:00))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_hour(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_hour(), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 17:47))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_minute(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_minute(), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 17:47:53))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_second(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_second(), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 17:47:53.123))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_millisecond(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_millisecond(), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47:53.123_456_789), datetime!(2021-11-12 17:47:53.123_456))]
#[case(datetime!(2021-11-12 0:00), datetime!(2021-11-12 0:00))]
fn truncate_to_microsecond(#[case] datetime: PlainDateTime, #[case] expected: PlainDateTime) {
assert_eq!(datetime.truncate_to_microsecond(), expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 5.days(), datetime!(2019-01-06 0:00))]
#[case(datetime!(2019-12-31 0:00), 1.days(), datetime!(2020-01-01 0:00))]
#[case(datetime!(2019-12-31 23:59:59), 2.seconds(), datetime!(2020-01-01 0:00:01))]
#[case(datetime!(2020-01-01 0:00:01), (-2).seconds(), datetime!(2019-12-31 23:59:59))]
#[case(datetime!(1999-12-31 23:00), 1.hours(), datetime!(2000-01-01 0:00))]
fn add_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime + duration, expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 5.std_days(), datetime!(2019-01-06 0:00))]
#[case(datetime!(2019-12-31 0:00), 1.std_days(), datetime!(2020-01-01 0:00))]
#[case(datetime!(2019-12-31 23:59:59), 2.std_seconds(), datetime!(2020-01-01 0:00:01))]
fn add_std_duration(
#[case] datetime: PlainDateTime,
#[case] duration: std::time::Duration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime + duration, expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 5.days(), datetime!(2019-01-06 0:00))]
#[case(datetime!(2019-12-31 0:00), 1.days(), datetime!(2020-01-01 0:00))]
#[case(datetime!(2019-12-31 23:59:59), 2.seconds(), datetime!(2020-01-01 0:00:01))]
#[case(datetime!(2020-01-01 0:00:01), (-2).seconds(), datetime!(2019-12-31 23:59:59))]
fn add_assign_duration(
#[case] mut datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
datetime += duration;
assert_eq!(datetime, expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), 5.std_days(), datetime!(2019-01-06 0:00))]
#[case(datetime!(2019-12-31 0:00), 1.std_days(), datetime!(2020-01-01 0:00))]
#[case(datetime!(2019-12-31 23:59:59), 2.std_seconds(), datetime!(2020-01-01 0:00:01))]
fn add_assign_std_duration(
#[case] mut datetime: PlainDateTime,
#[case] duration: std::time::Duration,
#[case] expected: PlainDateTime,
) {
datetime += duration;
assert_eq!(datetime, expected);
}
#[rstest]
#[case(datetime!(2019-01-06 0:00), 5.days(), datetime!(2019-01-01 0:00))]
#[case(datetime!(2020-01-01 0:00), 1.days(), datetime!(2019-12-31 0:00))]
#[case(datetime!(2020-01-01 0:00:01), 2.seconds(), datetime!(2019-12-31 23:59:59))]
#[case(datetime!(2019-12-31 23:59:59), (-2).seconds(), datetime!(2020-01-01 0:00:01))]
#[case(datetime!(1999-12-31 23:00), (-1).hours(), datetime!(2000-01-01 0:00))]
fn sub_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime - duration, expected);
}
#[rstest]
#[case(datetime!(2019-01-06 0:00), 5.std_days(), datetime!(2019-01-01 0:00))]
#[case(datetime!(2020-01-01 0:00), 1.std_days(), datetime!(2019-12-31 0:00))]
#[case(datetime!(2020-01-01 0:00:01), 2.std_seconds(), datetime!(2019-12-31 23:59:59))]
fn sub_std_duration(
#[case] datetime: PlainDateTime,
#[case] duration: std::time::Duration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime - duration, expected);
}
#[rstest]
#[case(datetime!(2019-01-06 0:00), 5.days(), datetime!(2019-01-01 0:00))]
#[case(datetime!(2020-01-01 0:00), 1.days(), datetime!(2019-12-31 0:00))]
#[case(datetime!(2020-01-01 0:00:01), 2.seconds(), datetime!(2019-12-31 23:59:59))]
#[case(datetime!(2019-12-31 23:59:59), (-2).seconds(), datetime!(2020-01-01 0:00:01))]
fn sub_assign_duration(
#[case] mut datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
datetime -= duration;
assert_eq!(datetime, expected);
}
#[rstest]
#[case(datetime!(2019-01-06 0:00), 5.std_days(), datetime!(2019-01-01 0:00))]
#[case(datetime!(2020-01-01 0:00), 1.std_days(), datetime!(2019-12-31 0:00))]
#[case(datetime!(2020-01-01 0:00:01), 2.std_seconds(), datetime!(2019-12-31 23:59:59))]
fn sub_assign_std_duration(
#[case] mut datetime: PlainDateTime,
#[case] duration: std::time::Duration,
#[case] expected: PlainDateTime,
) {
datetime -= duration;
assert_eq!(datetime, expected);
}
#[rstest]
#[case(datetime!(2019-01-02 0:00), datetime!(2019-01-01 0:00), 1.days())]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-02 0:00), (-1).days())]
#[case(datetime!(2020-01-01 0:00), datetime!(2019-12-31 0:00), 1.days())]
#[case(datetime!(2019-12-31 0:00), datetime!(2020-01-01 0:00), (-1).days())]
fn sub_datetime(
#[case] lhs: PlainDateTime,
#[case] rhs: PlainDateTime,
#[case] expected: SignedDuration,
) {
assert_eq!(lhs - rhs, expected);
}
#[rstest]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-01 0:00), Ordering::Equal)]
#[case(datetime!(2019-01-01 0:00), datetime!(2020-01-01 0:00), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-02-01 0:00), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-02 0:00), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-01 1:00), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-01 0:01), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-01 0:00:01), Ordering::Less)]
#[case(datetime!(2019-01-01 0:00), datetime!(2019-01-01 0:00:00.000_000_001), Ordering::Less)]
#[case(datetime!(2020-01-01 0:00), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-02-01 0:00), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-01-02 0:00), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-01-01 1:00), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-01-01 0:01), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-01-01 0:00:01), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(2019-01-01 0:00:00.000_000_001), datetime!(2019-01-01 0:00), Ordering::Greater)]
#[case(datetime!(-0001-01-01 0:00), datetime!(0001-01-01 0:00), Ordering::Less)]
fn ord(
#[case] lhs: PlainDateTime,
#[case] rhs: PlainDateTime,
#[case] expected: impl Into<Option<Ordering>>,
) {
assert_eq!(lhs.partial_cmp(&rhs), expected.into());
}
#[rstest]
#[case(
datetime!(2021-10-25 14:01:53.45),
5.nanoseconds(),
datetime!(2021-10-25 14:01:53.450_000_005),
)]
#[case(datetime!(2021-10-25 14:01:53.45), 4.seconds(), datetime!(2021-10-25 14:01:57.45))]
#[case(datetime!(2021-10-25 14:01:53.45), 2.days(), datetime!(2021-10-27 14:01:53.45))]
#[case(datetime!(2021-10-25 14:01:53.45), 1.weeks(), datetime!(2021-11-01 14:01:53.45))]
#[case(
datetime!(2021-10-25 14:01:53.45),
(-5).nanoseconds(),
datetime!(2021-10-25 14:01:53.449_999_995),
)]
#[case(datetime!(2021-10-25 14:01:53.45), (-4).seconds(), datetime!(2021-10-25 14:01:49.45))]
#[case(datetime!(2021-10-25 14:01:53.45), (-2).days(), datetime!(2021-10-23 14:01:53.45))]
#[case(datetime!(2021-10-25 14:01:53.45), (-1).weeks(), datetime!(2021-10-18 14:01:53.45))]
#[case::underflow(PlainDateTime::MIN, (-1).nanoseconds(), None)]
#[case::underflow(PlainDateTime::MIN, SignedDuration::MIN, None)]
#[case::underflow(PlainDateTime::MIN, (-530).weeks(), None)]
#[case::overflow(PlainDateTime::MAX, 1.nanoseconds(), None)]
#[case::overflow(PlainDateTime::MAX, SignedDuration::MAX, None)]
#[case::overflow(PlainDateTime::MAX, 530.weeks(), None)]
fn checked_add_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.checked_add(duration), expected.into());
}
#[rstest]
#[case(
datetime!(2021-10-25 14:01:53.45),
(-5).nanoseconds(),
datetime!(2021-10-25 14:01:53.450_000_005),
)]
#[case(datetime!(2021-10-25 14:01:53.45), (-4).seconds(), datetime!(2021-10-25 14:01:57.45))]
#[case(datetime!(2021-10-25 14:01:53.45), (-2).days(), datetime!(2021-10-27 14:01:53.45))]
#[case(datetime!(2021-10-25 14:01:53.45), (-1).weeks(), datetime!(2021-11-01 14:01:53.45))]
#[case(
datetime!(2021-10-25 14:01:53.45),
5.nanoseconds(),
datetime!(2021-10-25 14:01:53.449_999_995),
)]
#[case(datetime!(2021-10-25 14:01:53.45), 4.seconds(), datetime!(2021-10-25 14:01:49.45))]
#[case(datetime!(2021-10-25 14:01:53.45), 2.days(), datetime!(2021-10-23 14:01:53.45))]
#[case(datetime!(2021-10-25 14:01:53.45), 1.weeks(), datetime!(2021-10-18 14:01:53.45))]
#[case::underflow(PlainDateTime::MIN, 1.nanoseconds(), None)]
#[case::underflow(PlainDateTime::MIN, SignedDuration::MIN, None)]
#[case::underflow(PlainDateTime::MIN, 530.weeks(), None)]
#[case::overflow(PlainDateTime::MAX, (-1).nanoseconds(), None)]
#[case::overflow(PlainDateTime::MAX, SignedDuration::MAX, None)]
#[case::overflow(PlainDateTime::MAX, (-530).weeks(), None)]
fn checked_sub_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: impl Into<Option<PlainDateTime>>,
) {
assert_eq!(datetime.checked_sub(duration), expected.into());
}
#[rstest]
#[case(datetime!(2021-11-12 17:47), 2.days(), datetime!(2021-11-14 17:47))]
#[case(datetime!(2021-11-12 17:47), (-2).days(), datetime!(2021-11-10 17:47))]
#[case::underflow(PlainDateTime::MIN, (-10).days(), PlainDateTime::MIN)]
#[case::overflow(PlainDateTime::MAX, 10.days(), PlainDateTime::MAX)]
#[case::zero_duration_min(PlainDateTime::MIN, SignedDuration::ZERO, PlainDateTime::MIN)]
#[case::zero_duration_max(PlainDateTime::MAX, SignedDuration::ZERO, PlainDateTime::MAX)]
fn saturating_add_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime.saturating_add(duration), expected);
}
#[rstest]
#[case(datetime!(2021-11-12 17:47), 2.days(), datetime!(2021-11-10 17:47))]
#[case(datetime!(2021-11-12 17:47), (-2).days(), datetime!(2021-11-14 17:47))]
#[case::underflow(PlainDateTime::MIN, 10.days(), PlainDateTime::MIN)]
#[case::overflow(PlainDateTime::MAX, (-10).days(), PlainDateTime::MAX)]
#[case::zero_duration_min(PlainDateTime::MIN, SignedDuration::ZERO, PlainDateTime::MIN)]
#[case::zero_duration_max(PlainDateTime::MAX, SignedDuration::ZERO, PlainDateTime::MAX)]
fn saturating_sub_duration(
#[case] datetime: PlainDateTime,
#[case] duration: SignedDuration,
#[case] expected: PlainDateTime,
) {
assert_eq!(datetime.saturating_sub(duration), expected);
}
@@ -0,0 +1,413 @@
use num_conv::prelude::*;
use quickcheck::{Arbitrary, TestResult};
use quickcheck_macros::quickcheck;
use time::Weekday::*;
use time::macros::{format_description, time};
use time::{Date, Month, OffsetDateTime, PlainDateTime, SignedDuration, Time, UtcOffset, Weekday};
macro_rules! test_shrink {
($type:ty,
$fn_name:ident,
$($method:ident()).+
$(, min=$min_value:literal)?
) => {
#[quickcheck]
fn $fn_name(v: $type) -> TestResult {
let method_value = v.$($method()).+;
if method_value == test_shrink!(@min_or_zero $($min_value)?) {
TestResult::discard()
} else {
TestResult::from_bool(v.shrink().any(|shrunk|
if method_value > 0 {
shrunk.$($method()).+ < v.$($method()).+
} else {
shrunk.$($method()).+ > v.$($method()).+
}
))
}
}
};
(@min_or_zero) => { 0 };
(@min_or_zero $min:literal) => { $min };
}
macro_rules! no_panic {
($($x:tt)*) => {
std::panic::catch_unwind(|| {
$($x)*
})
.is_ok()
}
}
#[quickcheck]
fn date_yo_roundtrip(d: Date) -> bool {
Date::from_ordinal_date(d.year(), d.ordinal()) == Ok(d)
}
#[quickcheck]
fn date_ymd_roundtrip(d: Date) -> bool {
Date::from_calendar_date(d.year(), d.month(), d.day()) == Ok(d)
}
#[quickcheck]
fn date_ywd_roundtrip(d: Date) -> bool {
let (year, week, weekday) = d.to_iso_week_date();
Date::from_iso_week_date(year, week, weekday) == Ok(d)
}
#[quickcheck]
fn date_format_century_last_two_equivalent(d: Date) -> bool {
let split_format = format_description!("[year repr:century][year repr:last_two]-[month]-[day]");
let split = d.format(&split_format).expect("formatting failed");
let combined_format = format_description!("[year]-[month]-[day]");
let combined = d.format(&combined_format).expect("formatting failed");
split == combined
}
#[quickcheck]
fn date_parse_century_last_two_equivalent_extended(d: Date) -> TestResult {
// With the extended range, there is an ambiguity when parsing a year with fewer than six
// digits, as the first four are consumed by the century, leaving at most one for the last
// two digits.
if !matches!(d.year().unsigned_abs().to_string().len(), 6) {
return TestResult::discard();
}
let split_format = format_description!("[year repr:century][year repr:last_two]-[month]-[day]");
let combined_format = format_description!("[year]-[month]-[day]");
let combined = d.format(&combined_format).expect("formatting failed");
TestResult::from_bool(Date::parse(&combined, &split_format).expect("parsing failed") == d)
}
#[quickcheck]
fn date_parse_century_last_two_equivalent_standard(d: Date) -> TestResult {
// With the standard range, the year must be at most four digits.
if !matches!(d.year(), -9999..=9999) {
return TestResult::discard();
}
let split_format = format_description!(
"[year repr:century range:standard][year repr:last_two range:standard]-[month]-[day]"
);
let combined_format = format_description!("[year range:standard]-[month]-[day]");
let combined = d.format(&combined_format).expect("formatting failed");
TestResult::from_bool(Date::parse(&combined, &split_format).expect("parsing failed") == d)
}
#[quickcheck]
fn julian_day_roundtrip(d: Date) -> bool {
Date::from_julian_day(d.to_julian_day()) == Ok(d)
}
#[quickcheck]
fn duration_roundtrip(d: SignedDuration) -> bool {
SignedDuration::new(d.whole_seconds(), d.subsec_nanoseconds()) == d
}
#[quickcheck]
fn time_roundtrip(t: Time) -> bool {
Time::from_hms_nano(t.hour(), t.minute(), t.second(), t.nanosecond()) == Ok(t)
}
#[quickcheck]
fn plain_date_time_roundtrip(a: PlainDateTime) -> bool {
PlainDateTime::new(a.date(), a.time()) == a
}
#[quickcheck]
fn utc_offset_roundtrip(o: UtcOffset) -> bool {
let (hours, minutes, seconds) = o.as_hms();
UtcOffset::from_hms(hours, minutes, seconds) == Ok(o)
}
#[quickcheck]
fn offset_date_time_roundtrip(a: OffsetDateTime) -> TestResult {
// Values near the edge of what is allowed may panic if the conversion brings the underlying
// value outside the valid range of values.
if a.date() == Date::MIN || a.date() == Date::MAX {
return TestResult::discard();
}
TestResult::from_bool(PlainDateTime::new(a.date(), a.time()).assume_offset(a.offset()) == a)
}
#[quickcheck]
fn unix_timestamp_roundtrip(odt: OffsetDateTime) -> TestResult {
match odt.date() {
Date::MIN | Date::MAX => TestResult::discard(),
_ => TestResult::from_bool({
// nanoseconds are not stored in the basic Unix timestamp
let odt = odt - SignedDuration::nanoseconds(odt.nanosecond().into());
OffsetDateTime::from_unix_timestamp(odt.unix_timestamp()) == Ok(odt)
}),
}
}
#[quickcheck]
fn unix_timestamp_nanos_roundtrip(odt: OffsetDateTime) -> TestResult {
match odt.date() {
Date::MIN | Date::MAX => TestResult::discard(),
_ => TestResult::from_bool(
OffsetDateTime::from_unix_timestamp_nanos(odt.unix_timestamp_nanos()) == Ok(odt),
),
}
}
#[quickcheck]
fn number_from_monday_roundtrip(w: Weekday) -> bool {
Monday.nth_next(w.number_from_monday() + 7 - 1) == w
}
#[quickcheck]
fn number_from_sunday_roundtrip(w: Weekday) -> bool {
Sunday.nth_next(w.number_from_sunday() + 7 - 1) == w
}
#[quickcheck]
fn number_days_from_monday_roundtrip(w: Weekday) -> bool {
Monday.nth_next(w.number_days_from_monday()) == w
}
#[quickcheck]
fn number_days_from_sunday_roundtrip(w: Weekday) -> bool {
Sunday.nth_next(w.number_days_from_sunday()) == w
}
#[quickcheck]
fn weekday_supports_arbitrary(w: Weekday) -> bool {
(1..=7).contains(&w.number_from_monday())
}
#[quickcheck]
fn weekday_can_shrink(w: Weekday) -> bool {
match w {
Monday => w.shrink().next().is_none(),
_ => w.shrink().next() == Some(w.previous()),
}
}
#[quickcheck]
fn month_supports_arbitrary(m: Month) -> bool {
(1..=12).contains(&u8::from(m))
}
#[quickcheck]
fn month_can_shrink(m: Month) -> bool {
match m {
Month::January => m.shrink().next().is_none(),
_ => m.shrink().next() == Some(m.previous()),
}
}
#[quickcheck]
fn date_replace_year(date: Date, year: i32) -> bool {
date.replace_year(year) == Date::from_calendar_date(year, date.month(), date.day())
}
#[quickcheck]
fn date_replace_month(date: Date, month: Month) -> bool {
date.replace_month(month) == Date::from_calendar_date(date.year(), month, date.day())
}
#[quickcheck]
fn date_replace_day(date: Date, day: u8) -> bool {
date.replace_day(day) == Date::from_calendar_date(date.year(), date.month(), day)
}
#[quickcheck]
fn time_replace_hour(time: Time, hour: u8) -> bool {
time.replace_hour(hour)
== Time::from_hms_nano(hour, time.minute(), time.second(), time.nanosecond())
}
#[quickcheck]
fn pdt_replace_year(pdt: PlainDateTime, year: i32) -> bool {
pdt.replace_year(year)
== Date::from_calendar_date(year, pdt.month(), pdt.day())
.map(|date| date.with_time(pdt.time()))
}
#[quickcheck]
fn pdt_replace_month(pdt: PlainDateTime, month: Month) -> bool {
pdt.replace_month(month)
== Date::from_calendar_date(pdt.year(), month, pdt.day())
.map(|date| date.with_time(pdt.time()))
}
#[quickcheck]
fn pdt_replace_day(pdt: PlainDateTime, day: u8) -> bool {
pdt.replace_day(day)
== Date::from_calendar_date(pdt.year(), pdt.month(), day)
.map(|date| date.with_time(pdt.time()))
}
// Regression test for #481
#[quickcheck]
fn time_sub_time_no_panic(time_a: Time, time_b: Time) -> bool {
no_panic! {
let _ = time_a - time_b;
let _ = time_b - time_a;
}
}
#[quickcheck]
fn time_duration_until_since_range(time_a: Time, time_b: Time) -> bool {
let a_until_b = time_a.duration_until(time_b);
let b_until_a = time_b.duration_until(time_a);
let a_since_b = time_a.duration_since(time_b);
let b_since_a = time_b.duration_since(time_a);
(SignedDuration::ZERO..SignedDuration::DAY).contains(&a_until_b)
&& (SignedDuration::ZERO..SignedDuration::DAY).contains(&b_until_a)
&& (SignedDuration::ZERO..SignedDuration::DAY).contains(&a_since_b)
&& (SignedDuration::ZERO..SignedDuration::DAY).contains(&b_since_a)
}
#[quickcheck]
fn time_duration_until_since_arithmetic(time_a: Time, time_b: Time) -> bool {
let a_until_b = time_a.duration_until(time_b);
let b_until_a = time_b.duration_until(time_a);
let a_since_b = time_a.duration_since(time_b);
let b_since_a = time_b.duration_since(time_a);
time_a + a_until_b == time_b
&& time_b + b_until_a == time_a
&& time_b + a_since_b == time_a
&& time_a + b_since_a == time_b
}
#[quickcheck]
fn from_julian_day_no_panic(julian_day: i32) -> TestResult {
if !(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()).contains(&julian_day) {
return TestResult::discard();
}
TestResult::from_bool(
std::panic::catch_unwind(|| {
let _ = Date::from_julian_day(julian_day);
})
.is_ok(),
)
}
#[quickcheck]
fn odt_eq_no_panic(left: OffsetDateTime, right: OffsetDateTime) -> bool {
no_panic! {
let _ = left == right;
}
}
#[quickcheck]
fn odt_ord_no_panic(left: OffsetDateTime, right: OffsetDateTime) -> bool {
no_panic! {
let _ = left < right;
let _ = left > right;
}
}
#[quickcheck]
fn odt_sub_no_panic(left: OffsetDateTime, right: OffsetDateTime) -> bool {
no_panic! {
let _ = left - right;
}
}
#[quickcheck]
fn odt_to_offset_no_panic(odt: OffsetDateTime, offset: UtcOffset) -> TestResult {
let offset_difference = offset.whole_seconds() - odt.offset().whole_seconds();
if Date::MIN
.midnight()
.assume_utc()
.checked_add(SignedDuration::seconds(offset_difference.widen()))
.is_none()
|| Date::MAX
.with_time(time!(23:59:59.999_999_999))
.assume_utc()
.checked_add(SignedDuration::seconds(offset_difference.widen()))
.is_none()
{
return TestResult::discard();
}
TestResult::from_bool(no_panic! {
let _ = odt.to_offset(offset);
})
}
#[quickcheck]
fn odt_replace_offset_no_panic(odt: OffsetDateTime, offset: UtcOffset) -> TestResult {
if Date::MIN
.midnight()
.assume_offset(odt.offset())
.checked_add(SignedDuration::seconds(offset.whole_seconds().widen()))
.is_none()
|| Date::MAX
.with_time(time!(23:59:59.999_999_999))
.assume_offset(odt.offset())
.checked_add(SignedDuration::seconds(offset.whole_seconds().widen()))
.is_none()
{
return TestResult::discard();
}
TestResult::from_bool(no_panic! {
let _ = odt.replace_offset(offset);
})
}
test_shrink!(Date, date_can_shrink_year, year());
test_shrink!(Date, date_can_shrink_ordinal, ordinal(), min = 1);
test_shrink!(SignedDuration, duration_can_shrink_seconds, whole_seconds());
test_shrink!(SignedDuration, duration_can_shrink_ns, subsec_nanoseconds());
test_shrink!(Time, time_can_shrink_hour, hour());
test_shrink!(Time, time_can_shrink_minute, minute());
test_shrink!(Time, time_can_shrink_second, second());
test_shrink!(Time, time_can_shrink_nanosecond, nanosecond());
test_shrink!(PlainDateTime, plain_date_time_can_shrink_year, year());
test_shrink!(
PlainDateTime,
plain_date_time_can_shrink_ordinal,
ordinal(),
min = 1
);
test_shrink!(PlainDateTime, plain_date_time_can_shrink_hour, hour());
test_shrink!(PlainDateTime, plain_date_time_can_shrink_minute, minute());
test_shrink!(PlainDateTime, plain_date_time_can_shrink_second, second());
test_shrink!(
PlainDateTime,
plain_date_time_can_shrink_nanosecond,
nanosecond()
);
test_shrink!(UtcOffset, utc_offset_can_shrink, whole_seconds());
test_shrink!(
OffsetDateTime,
offset_date_time_can_shrink_offset,
offset().whole_seconds()
);
test_shrink!(OffsetDateTime, offset_date_time_can_shrink_year, year());
test_shrink!(
OffsetDateTime,
offset_date_time_can_shrink_ordinal,
ordinal(),
min = 1
);
test_shrink!(OffsetDateTime, offset_date_time_can_shrink_hour, hour());
test_shrink!(OffsetDateTime, offset_date_time_can_shrink_minute, minute());
test_shrink!(OffsetDateTime, offset_date_time_can_shrink_second, second());
test_shrink!(
OffsetDateTime,
offset_date_time_can_shrink_nanosecond,
nanosecond()
);
+124
View File
@@ -0,0 +1,124 @@
use std::marker::PhantomData;
use rstest::rstest;
use time::{Date, Month, OffsetDateTime, PlainDateTime, SignedDuration, Time, UtcOffset, Weekday};
#[rstest]
#[case(7, PhantomData::<Weekday>)]
#[case(12, PhantomData::<Month>)]
#[case(1, PhantomData::<Time>)]
#[case(1, PhantomData::<Date>)]
#[case(1, PhantomData::<UtcOffset>)]
#[case(1, PhantomData::<PlainDateTime>)]
#[case(1, PhantomData::<OffsetDateTime>)]
#[case(1, PhantomData::<SignedDuration>)]
fn support08<T>(#[case] iterations: usize, #[case] _type: PhantomData<T>)
where
rand08::distributions::Standard: rand08::distributions::Distribution<T>,
{
use rand08::Rng as _;
// Work around rust-random/rand#1020.
let mut rng = rand08::rngs::mock::StepRng::new(0, 2_505_397_590);
for _ in 0..iterations {
drop(rng.r#gen::<T>());
}
}
#[rstest]
#[case(7, PhantomData::<Weekday>)]
#[case(12, PhantomData::<Month>)]
#[case(1, PhantomData::<Time>)]
#[case(1, PhantomData::<Date>)]
#[case(1, PhantomData::<UtcOffset>)]
#[case(1, PhantomData::<PlainDateTime>)]
#[case(1, PhantomData::<OffsetDateTime>)]
#[case(1, PhantomData::<SignedDuration>)]
fn support09<T>(#[case] iterations: usize, #[case] _type: PhantomData<T>)
where
rand09::distr::StandardUniform: rand09::distr::Distribution<T>,
{
use rand09::Rng as _;
// Work around rust-random/rand#1020.
let mut rng = StepRng::new(0, 2_505_397_590);
for _ in 0..iterations {
drop(rng.random::<T>());
}
}
#[rstest]
#[case(7, PhantomData::<Weekday>)]
#[case(12, PhantomData::<Month>)]
#[case(1, PhantomData::<Time>)]
#[case(1, PhantomData::<Date>)]
#[case(1, PhantomData::<UtcOffset>)]
#[case(1, PhantomData::<PlainDateTime>)]
#[case(1, PhantomData::<OffsetDateTime>)]
#[case(1, PhantomData::<SignedDuration>)]
fn support010<T>(#[case] iterations: usize, #[case] _type: PhantomData<T>)
where
rand010::distr::StandardUniform: rand010::distr::Distribution<T>,
{
use rand010::RngExt as _;
// Work around rust-random/rand#1020.
let mut rng = StepRng::new(0, 2_505_397_590);
for _ in 0..iterations {
drop(rng.random::<T>());
}
}
// copy of `StepRng` from rand 0.8 to avoid deprecation warnings
#[derive(Debug, Clone)]
struct StepRng {
v: u64,
a: u64,
}
impl StepRng {
fn new(initial: u64, increment: u64) -> Self {
Self {
v: initial,
a: increment,
}
}
}
impl rand09::RngCore for StepRng {
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
fn next_u64(&mut self) -> u64 {
let res = self.v;
self.v = self.v.wrapping_add(self.a);
res
}
fn fill_bytes(&mut self, dst: &mut [u8]) {
rand09::rand_core::impls::fill_bytes_via_next(self, dst)
}
}
impl rand010::TryRng for StepRng {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
self.try_next_u64().map(|v| v as u32)
}
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let res = self.v;
self.v = self.v.wrapping_add(self.a);
Ok(res)
}
#[expect(clippy::unimplemented)]
fn try_fill_bytes(&mut self, _dst: &mut [u8]) -> Result<(), Self::Error> {
unimplemented!("not used in testing, so not implemented");
}
}
@@ -0,0 +1,47 @@
use rstest::rstest;
use serde::{Deserialize, Serialize, Serializer};
use serde_test2::{Token, assert_ser_tokens_error};
use time::macros::{datetime, format_description};
use time::{OffsetDateTime, error};
/// Trigger `time::error::Format::StdIo` errors.
///
/// `StdIo` won't be reached during normal serde operation: it's instantiated
/// only during calls to `format_into()`, but most `Serializable`
/// implementations will only call `format()` because serde `Serializer`
/// interface doesn't expose the underlying `io::Write`.
///
/// Therefore, we need a contrived serializer to trigger coverage.
fn serialize<S: Serializer>(datetime: &OffsetDateTime, _serializer: S) -> Result<S::Ok, S::Error> {
Err(datetime
.format_into(
&mut [0u8; 0].as_mut_slice(),
format_description!("nonempty format description"),
)
.map_err(error::Format::into_invalid_serde_value::<S>)
.expect_err("Writing to a zero-length buffer should always error."))
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestBadIo {
#[serde(serialize_with = "serialize")]
dt: OffsetDateTime,
}
#[rstest]
fn custom_serialize_io_error() {
let value = TestBadIo {
dt: datetime!(2000-01-01 00:00 -4:00),
};
assert_ser_tokens_error::<TestBadIo>(
&value,
&[
Token::Struct {
name: "TestBadIo",
len: 1,
},
Token::Str("dt"),
],
"failed to write whole buffer",
);
}
@@ -0,0 +1,130 @@
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{
Compact, Configure, Token, assert_de_tokens, assert_de_tokens_error, assert_ser_tokens_error,
assert_tokens,
};
use time::OffsetDateTime;
use time::macros::datetime;
use time::serde::iso8601;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(with = "iso8601")]
dt: OffsetDateTime,
#[serde(with = "iso8601::option")]
option_dt: Option<OffsetDateTime>,
}
#[rstest]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: Some(datetime!(2000-01-01 00:00:00 UTC)),
}.compact(),
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("+002000-01-01T00:00:00.000000000Z"),
Token::Str("option_dt"),
Token::Some,
Token::BorrowedStr("+002000-01-01T00:00:00.000000000Z"),
Token::StructEnd,
]
)]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: None,
}.compact(),
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("+002000-01-01T00:00:00.000000000Z"),
Token::Str("option_dt"),
Token::None,
Token::StructEnd,
]
)]
fn serialize(#[case] test: Compact<Test>, #[case] tokens: &[Token]) {
assert_tokens(&test, tokens);
}
#[rstest]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 +00:00:01),
option_dt: None,
},
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
],
"The offset_second component cannot be formatted into the requested format.",
)]
fn serialize_error(#[case] value: Test, #[case] tokens: &[Token], #[case] error: &str) {
assert_ser_tokens_error::<Test>(&value, tokens, error);
}
#[rstest]
#[case(
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("bad"),
Token::StructEnd,
],
"the 'year' component could not be parsed"
)]
fn deserialize_error(#[case] tokens: &[Token], #[case] error: &str) {
assert_de_tokens_error::<Test>(tokens, error);
}
#[rstest]
fn issue_674_leap_second_support() {
assert_de_tokens::<Test>(
&Test {
dt: datetime!(2016-12-31 23:59:59.999999999 UTC),
option_dt: Some(datetime!(2016-12-31 23:59:59.999999999 UTC)),
},
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("2016-12-31T23:59:60.000Z"),
Token::Str("option_dt"),
Token::Some,
Token::BorrowedStr("2016-12-31T23:59:60.000Z"),
Token::StructEnd,
],
);
}
#[rstest]
fn issue_724_truncation() {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct Demo(#[serde(with = "time::serde::iso8601")] OffsetDateTime);
let value = datetime!(2025-01-10 23:01:16.000081999 UTC);
let info = Demo(value);
let serialized = serde_json::to_string(&info).expect("serialization failed");
assert_eq!(serialized, r#""+002025-01-10T23:01:16.000081999Z""#);
let deserialized: Demo = serde_json::from_str(&serialized).expect("deserialization failed");
assert_eq!(info, deserialized);
}
@@ -0,0 +1,111 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::Configure;
use time::Month::*;
use time::Weekday::*;
use time::macros::{date, datetime, time};
use time::{Date, Month, OffsetDateTime, PlainDateTime, SignedDuration, Time, Weekday};
enum Format {
Compact,
Readable,
}
use Format::*;
#[rstest]
#[case(Monday.compact(), "1")]
#[case(Monday.readable(), r#""Monday""#)]
#[case(March.compact(), "3")]
#[case(March.readable(), r#""March""#)]
#[case(time!(12:40:20).compact(), "[12,40,20,0]")]
#[case(time!(12:40:20).readable(), r#""12:40:20.0""#)]
#[case(datetime!(2022-05-20 12:40:20).compact(), "[2022,140,12,40,20,0]")]
#[case(datetime!(2022-05-20 12:40:20).readable(), r#""2022-05-20 12:40:20.0""#)]
#[case(datetime!(2022-05-20 12:40:20 UTC).compact(), "[2022,140,12,40,20,0,0,0,0]")]
#[case(datetime!(2022-05-20 12:40:20 UTC).readable(), r#""2022-05-20 12:40:20.0 +00:00:00""#)]
#[case(SignedDuration::new(50, 0).compact(), "[50,0]")]
#[case(SignedDuration::new(50, 0).readable(), r#""50.000000000""#)]
#[case(date!(2022-04-05).compact(), "[2022,95]")]
#[case(date!(2022-04-05).readable(), r#""2022-04-05""#)]
fn serialize<T>(#[case] value: T, #[case] expected: &str)
where
T: Serialize,
{
let mut buf: Vec<u8> = Vec::new();
let mut ser = serde_json::Serializer::new(&mut buf);
value.serialize(&mut ser).expect("serialization failed");
assert_eq!(String::from_utf8(buf).as_deref(), Ok(expected));
}
#[rstest]
#[case(PhantomData::<Weekday>, Compact, "1", Monday)]
#[case(PhantomData::<Weekday>, Readable, "1", Monday)]
#[case(PhantomData::<Weekday>, Readable, r#""Monday""#, Monday)]
#[case(PhantomData::<Month>, Compact, "3", March)]
#[case(PhantomData::<Month>, Readable, "3", March)]
#[case(PhantomData::<Month>, Readable, r#""March""#, March)]
#[case(PhantomData::<Time>, Compact, "[12,40,20,0]", time!(12:40:20))]
#[case(PhantomData::<Time>, Readable, "[12,40,20,0]", time!(12:40:20))]
#[case(PhantomData::<Time>, Readable, r#""12:40:20.0""#, time!(12:40:20))]
#[case(
PhantomData::<PlainDateTime>,
Compact,
"[2022,140,12,40,20,0]",
datetime!(2022-05-20 12:40:20),
)]
#[case(
PhantomData::<PlainDateTime>,
Readable,
"[2022,140,12,40,20,0]",
datetime!(2022-05-20 12:40:20),
)]
#[case(
PhantomData::<PlainDateTime>,
Readable,
r#""2022-05-20 12:40:20.0""#,
datetime!(2022-05-20 12:40:20),
)]
#[case(
PhantomData::<OffsetDateTime>,
Compact,
"[2022,140,12,40,20,0,0,0,0]",
datetime!(2022-05-20 12:40:20 UTC),
)]
#[case(
PhantomData::<OffsetDateTime>,
Readable,
"[2022,140,12,40,20,0,0,0,0]",
datetime!(2022-05-20 12:40:20 UTC),
)]
#[case(
PhantomData::<OffsetDateTime>,
Readable,
r#""2022-05-20 12:40:20.0 +00:00:00""#,
datetime!(2022-05-20 12:40:20 UTC),
)]
#[case(PhantomData::<SignedDuration>, Compact, "[50,0]", SignedDuration::new(50, 0))]
#[case(PhantomData::<SignedDuration>, Readable, "[50,0]", SignedDuration::new(50, 0))]
#[case(PhantomData::<SignedDuration>, Readable, r#""50.000000000""#, SignedDuration::new(50, 0))]
#[case(PhantomData::<Date>, Compact, "[2022,95]", date!(2022-04-05))]
#[case(PhantomData::<Date>, Readable, "[2022,95]", date!(2022-04-05))]
#[case(PhantomData::<Date>, Readable, r#""2022-04-05""#, date!(2022-04-05))]
fn deserialize<T>(
#[case] _type: PhantomData<T>,
#[case] format: Format,
#[case] value: &str,
#[case] expected: T,
) where
T: Debug + PartialEq + for<'de> Deserialize<'de>,
{
let mut de = serde_json::Deserializer::from_str(value);
let value = match format {
Compact => T::deserialize((&mut de).compact()),
Readable => T::deserialize((&mut de).readable()),
}
.expect("deserialization failed");
assert_eq!(value, expected);
}
@@ -0,0 +1,310 @@
use rstest::rstest;
use serde_test2::Compact;
#[rustfmt::skip] // Tries to remove the leading `::`, which breaks compilation.
use ::serde::{Deserialize, Serialize};
use serde_test2::{
Configure, Token, assert_de_tokens, assert_de_tokens_error, assert_ser_tokens_error,
assert_tokens,
};
use time::format_description::BorrowedFormatItem;
use time::format_description::well_known::{Iso8601, iso8601};
use time::macros::{date, datetime, offset, time};
use time::{Date, OffsetDateTime, PlainDateTime, Time, UtcOffset, serde};
// Not used in the tests, but ensures that the macro compiles.
#[expect(dead_code)]
const ISO_FORMAT: Iso8601<{ iso8601::Config::DEFAULT.encode() }> =
Iso8601::<{ iso8601::Config::DEFAULT.encode() }>;
time::serde::format_description!(my_format, OffsetDateTime, ISO_FORMAT);
time::serde::format_description!(
my_format2,
OffsetDateTime,
Iso8601::<{ iso8601::Config::DEFAULT.encode() }>
);
mod nested {
time::serde::format_description!(
pub(super) offset_dt_format,
OffsetDateTime,
"custom format: [year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
sign:mandatory]:[offset_minute]"
);
time::serde::format_description!(
pub plain_dt_format,
PlainDateTime,
"custom format: [year]-[month]-[day] [hour]:[minute]:[second]"
);
time::serde::format_description!(
pub(in crate::serde::macros) time_format,
Time,
"custom format: [minute]:[second]"
);
}
serde::format_description!(date_format, Date, "custom format: [year]-[month]-[day]");
serde::format_description!(
offset_format,
UtcOffset,
"custom format: [offset_hour sign:mandatory]:[offset_minute]"
);
const TIME_FORMAT_ALT: &[BorrowedFormatItem<'_>] =
time::macros::format_description!("[hour]:[minute]");
serde::format_description!(time_format_alt, Time, TIME_FORMAT_ALT);
serde::format_description!(
time_format_alt2,
Time,
time::macros::format_description!("[hour]:[minute]")
);
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestCustomFormat {
#[serde(with = "nested::offset_dt_format")]
offset_dt: OffsetDateTime,
#[serde(with = "nested::plain_dt_format::option")]
plain_dt: Option<PlainDateTime>,
#[serde(with = "date_format")]
date: Date,
#[serde(with = "nested::time_format::option")]
time: Option<Time>,
#[serde(with = "offset_format")]
offset: UtcOffset,
#[serde(with = "time_format_alt")]
time_alt: Time,
}
#[rstest]
#[case(
TestCustomFormat {
offset_dt: datetime!(2000-01-01 00:00 -4:00),
plain_dt: Some(datetime!(2000-01-01 00:00)),
date: date!(2000-01-01),
time: None,
offset: offset!(-4),
time_alt: time!(12:34),
}.compact(),
&[
Token::Struct {
name: "TestCustomFormat",
len: 6,
},
Token::Str("offset_dt"),
Token::BorrowedStr("custom format: 2000-01-01 00:00:00 -04:00"),
Token::Str("plain_dt"),
Token::Some,
Token::BorrowedStr("custom format: 2000-01-01 00:00:00"),
Token::Str("date"),
Token::BorrowedStr("custom format: 2000-01-01"),
Token::Str("time"),
Token::None,
Token::Str("offset"),
Token::BorrowedStr("custom format: -04:00"),
Token::Str("time_alt"),
Token::BorrowedStr("12:34"),
Token::StructEnd,
]
)]
fn custom_serialize(#[case] value: Compact<TestCustomFormat>, #[case] tokens: &[Token]) {
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
&[
Token::Struct {
name: "TestCustomFormat",
len: 6,
},
Token::Str("offset_dt"),
Token::BorrowedStr("custom format: 2000-01-01 0:00:00 -04:00"),
],
"the 'hour' component could not be parsed",
)]
#[case(
&[
Token::Struct {
name: "TestCustomFormat",
len: 6,
},
Token::Str("offset_dt"),
Token::BorrowedStr("custom format: 2000-01-01 00:00:00 -04:00"),
Token::Str("plain_dt"),
Token::Some,
Token::BorrowedStr("custom format: 2000-01-01 0:00:00 -04:00"),
],
"the 'hour' component could not be parsed"
)]
#[case(
&[
Token::Struct {
name: "TestCustomFormat",
len: 6,
},
Token::Str("offset_dt"),
Token::Bool(false),
],
"invalid type: boolean `false`, expected a(n) `OffsetDateTime` in the format \"custom format: \
[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory]:[offset_minute]\""
)]
#[case(
&[
Token::Struct {
name: "TestCustomFormat",
len: 6,
},
Token::Str("offset_dt"),
Token::BorrowedStr("custom format: 2000-01-01 00:00:00 -04:00"),
Token::Str("plain_dt"),
Token::Bool(false),
],
"invalid type: boolean `false`, expected an `Option<PlainDateTime>` in the format \"custom \
format: [year]-[month]-[day] [hour]:[minute]:[second]\""
)]
fn custom_deserialize_error(#[case] tokens: &[Token], #[case] error: &str) {
assert_de_tokens_error::<TestCustomFormat>(tokens, error);
}
// This format string has offset_hour and offset_minute, but is for formatting PlainDateTime.
serde::format_description!(
plain_date_time_format_bad,
PlainDateTime,
"[offset_hour]:[offset_minute]"
);
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestCustomFormatPlainDateTimeBad {
#[serde(with = "plain_date_time_format_bad")]
dt: PlainDateTime,
}
#[rstest]
#[case(
TestCustomFormatPlainDateTimeBad {
dt: datetime!(2000-01-01 00:00),
},
&[
Token::Struct {
name: "TestCustomFormatPlainDateTimeBad",
len: 1,
},
Token::Str("dt"),
],
"The type being formatted does not contain sufficient information to format a component.",
)]
fn custom_serialize_bad_type_error(
#[case] value: TestCustomFormatPlainDateTimeBad,
#[case] tokens: &[Token],
#[case] error: &str,
) {
assert_ser_tokens_error::<TestCustomFormatPlainDateTimeBad>(&value, tokens, error);
}
// Test the behavior of versioning.
serde::format_description!(version = 1, version_test_1, Time, "[[ [hour]:[minute]");
serde::format_description!(version = 1, version_test_2, Time, r"\\ [hour]:[minute]");
serde::format_description!(version = 2, version_test_3, Time, r"\\ [hour]:[minute]");
serde::format_description!(version, Time, "[hour]:[minute]");
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestVersioning {
#[serde(with = "version_test_1")]
time_1: Time,
#[serde(with = "version_test_2")]
time_2: Time,
#[serde(with = "version_test_3")]
time_3: Time,
#[serde(with = "version")]
time_4: Time,
}
#[rstest]
fn versioning() {
let value = TestVersioning {
time_1: Time::MIDNIGHT,
time_2: Time::MIDNIGHT,
time_3: Time::MIDNIGHT,
time_4: Time::MIDNIGHT,
};
assert_tokens(
&value,
&[
Token::Struct {
name: "TestVersioning",
len: 4,
},
Token::Str("time_1"),
Token::Str("[ 00:00"),
Token::Str("time_2"),
Token::Str(r"\\ 00:00"),
Token::Str("time_3"),
Token::Str(r"\ 00:00"),
Token::Str("time_4"),
Token::Str("00:00"),
Token::StructEnd,
],
);
}
serde::format_description!(
version = 1,
nested_v1,
Time,
"[hour]:[minute][optional [:[second]]]"
);
serde::format_description!(
version = 2,
nested_v2,
Time,
"[hour]:[minute][optional [:[second]]]"
);
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestNested {
#[serde(with = "nested_v1")]
time_1: Time,
#[serde(with = "nested_v2")]
time_2: Time,
}
#[rstest]
#[case(
TestNested {
time_1: time!(12:34:56),
time_2: time!(12:34:56),
},
&[
Token::Struct {
name: "TestNested",
len: 2,
},
Token::Str("time_1"),
Token::Str("12:34:56"),
Token::Str("time_2"),
Token::Str("12:34:56"),
Token::StructEnd,
]
)]
fn nested_roundtrip(#[case] value: TestNested, #[case] tokens: &[Token]) {
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
TestNested {
time_1: time!(12:34),
time_2: time!(12:34),
},
&[
Token::Struct {
name: "TestNested",
len: 2,
},
Token::Str("time_1"),
Token::Str("12:34"),
Token::Str("time_2"),
Token::Str("12:34"),
Token::StructEnd,
]
)]
fn nested_deserialize(#[case] value: TestNested, #[case] tokens: &[Token]) {
assert_de_tokens(&value, tokens);
}
+965
View File
@@ -0,0 +1,965 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{
Compact, Configure, Readable, Token, assert_de_tokens, assert_de_tokens_error, assert_tokens,
};
use time::Month::*;
use time::Weekday::*;
use time::macros::{date, datetime, offset, time};
use time::{Date, Month, OffsetDateTime, PlainDateTime, SignedDuration, Time, UtcOffset, Weekday};
mod error_conditions;
mod iso8601;
mod json;
mod macros;
mod rfc2822;
mod rfc3339;
mod timestamps;
#[rstest]
#[case(
Time::MIDNIGHT.compact(),
&[
Token::Tuple { len: 4 },
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::TupleEnd,
],
)]
#[case(
time!(23:58:59.123_456_789).compact(),
&[
Token::Tuple { len: 4 },
Token::U8(23),
Token::U8(58),
Token::U8(59),
Token::U32(123_456_789),
Token::TupleEnd,
],
)]
#[case(Time::MIDNIGHT.readable(), &[Token::BorrowedStr("00:00:00.0")])]
#[case(time!(23:58:59.123_456_789).readable(), &[Token::BorrowedStr("23:58:59.123456789")])]
#[case(
date!(-9999-001).compact(),
&[
Token::Tuple { len: 2 },
Token::I32(-9999),
Token::U16(1),
Token::TupleEnd,
],
)]
#[case(
date!(+9999-365).compact(),
&[
Token::Tuple { len: 2 },
Token::I32(9999),
Token::U16(365),
Token::TupleEnd,
],
)]
#[case(date!(-9999-001).readable(), &[Token::BorrowedStr("-9999-01-01")])]
#[case(date!(+9999-365).readable(), &[Token::BorrowedStr("9999-12-31")])]
#[case(
datetime!(-9999-001 0:00).compact(),
&[
Token::Tuple { len: 6 },
Token::I32(-9999),
Token::U16(1),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::TupleEnd,
],
)]
#[case(
datetime!(+9999-365 23:58:59.123_456_789).compact(),
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::U8(58),
Token::U8(59),
Token::U32(123_456_789),
Token::TupleEnd,
],
)]
#[case(datetime!(-9999-001 0:00).readable(), &[Token::BorrowedStr("-9999-01-01 00:00:00.0")])]
#[case(
datetime!(+9999-365 23:58:59.123_456_789).readable(),
&[Token::BorrowedStr("9999-12-31 23:58:59.123456789")],
)]
#[case(
datetime!(-9999-001 0:00 UTC).to_offset(offset!(+23:58:59)).compact(),
&[
Token::Tuple { len: 9 },
Token::I32(-9999),
Token::U16(1),
Token::U8(23),
Token::U8(58),
Token::U8(59),
Token::U32(0),
Token::I8(23),
Token::I8(58),
Token::I8(59),
Token::TupleEnd,
],
)]
#[case(
datetime!(+9999-365 23:58:59.123_456_789 UTC).to_offset(offset!(-23:58:59)).compact(),
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::I8(-23),
Token::I8(-58),
Token::I8(-59),
Token::TupleEnd,
],
)]
#[case(
datetime!(-9999-001 0:00 UTC).to_offset(offset!(+23:58:59)).readable(),
&[Token::BorrowedStr("-9999-01-01 23:58:59.0 +23:58:59")],
)]
#[case(
datetime!(+9999-365 23:58:59.123_456_789 UTC).to_offset(offset!(-23:58:59)).readable(),
&[Token::BorrowedStr("9999-12-31 00:00:00.123456789 -23:58:59")],
)]
#[case(
offset!(-23:58:59).compact(),
&[
Token::Tuple { len: 3 },
Token::I8(-23),
Token::I8(-58),
Token::I8(-59),
Token::TupleEnd,
],
)]
#[case(
offset!(+23:58:59).compact(),
&[
Token::Tuple { len: 3 },
Token::I8(23),
Token::I8(58),
Token::I8(59),
Token::TupleEnd,
],
)]
#[case(offset!(-23:58:59).readable(), &[Token::BorrowedStr("-23:58:59")])]
#[case(offset!(+23:58:59).readable(), &[Token::BorrowedStr("+23:58:59")])]
#[case(
SignedDuration::MIN.compact(),
&[
Token::Tuple { len: 2 },
Token::I64(i64::MIN),
Token::I32(-999_999_999),
Token::TupleEnd,
],
)]
#[case(
SignedDuration::MAX.compact(),
&[
Token::Tuple { len: 2 },
Token::I64(i64::MAX),
Token::I32(999_999_999),
Token::TupleEnd,
],
)]
#[case(SignedDuration::MIN.readable(), &[Token::BorrowedStr("-9223372036854775808.999999999")])]
#[case(SignedDuration::MAX.readable(), &[Token::BorrowedStr("9223372036854775807.999999999")])]
#[case(SignedDuration::ZERO.readable(), &[Token::BorrowedStr("0.000000000")])]
#[case(SignedDuration::nanoseconds(123).readable(), &[Token::BorrowedStr("0.000000123")])]
#[case(SignedDuration::nanoseconds(-123).readable(), &[Token::BorrowedStr("-0.000000123")])]
#[case(Monday.compact(), &[Token::U8(1)])]
#[case(Tuesday.compact(), &[Token::U8(2)])]
#[case(Wednesday.compact(), &[Token::U8(3)])]
#[case(Thursday.compact(), &[Token::U8(4)])]
#[case(Friday.compact(), &[Token::U8(5)])]
#[case(Saturday.compact(), &[Token::U8(6)])]
#[case(Sunday.compact(), &[Token::U8(7)])]
#[case(Monday.readable(), &[Token::BorrowedStr("Monday")])]
#[case(Tuesday.readable(), &[Token::BorrowedStr("Tuesday")])]
#[case(Wednesday.readable(), &[Token::BorrowedStr("Wednesday")])]
#[case(Thursday.readable(), &[Token::BorrowedStr("Thursday")])]
#[case(Friday.readable(), &[Token::BorrowedStr("Friday")])]
#[case(Saturday.readable(), &[Token::BorrowedStr("Saturday")])]
#[case(Sunday.readable(), &[Token::BorrowedStr("Sunday")])]
#[case(January.compact(), &[Token::U8(1)])]
#[case(February.compact(), &[Token::U8(2)])]
#[case(March.compact(), &[Token::U8(3)])]
#[case(April.compact(), &[Token::U8(4)])]
#[case(May.compact(), &[Token::U8(5)])]
#[case(June.compact(), &[Token::U8(6)])]
#[case(July.compact(), &[Token::U8(7)])]
#[case(August.compact(), &[Token::U8(8)])]
#[case(September.compact(), &[Token::U8(9)])]
#[case(October.compact(), &[Token::U8(10)])]
#[case(November.compact(), &[Token::U8(11)])]
#[case(December.compact(), &[Token::U8(12)])]
#[case(January.readable(), &[Token::BorrowedStr("January")])]
#[case(February.readable(), &[Token::BorrowedStr("February")])]
#[case(March.readable(), &[Token::BorrowedStr("March")])]
#[case(April.readable(), &[Token::BorrowedStr("April")])]
#[case(May.readable(), &[Token::BorrowedStr("May")])]
#[case(June.readable(), &[Token::BorrowedStr("June")])]
#[case(July.readable(), &[Token::BorrowedStr("July")])]
#[case(August.readable(), &[Token::BorrowedStr("August")])]
#[case(September.readable(), &[Token::BorrowedStr("September")])]
#[case(October.readable(), &[Token::BorrowedStr("October")])]
#[case(November.readable(), &[Token::BorrowedStr("November")])]
#[case(December.readable(), &[Token::BorrowedStr("December")])]
fn success<T>(#[case] value: T, #[case] tokens: &[Token])
where
T: Debug + PartialEq + Serialize + for<'de> Deserialize<'de>,
{
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
PhantomData::<Compact<Time>>,
&[
Token::Tuple { len: 4 },
Token::U8(24),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::TupleEnd,
],
"invalid hour, expected an in-range value",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::BorrowedStr("24:00:00.0")],
"the 'hour' component could not be parsed",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::BorrowedStr("23-00:00.0")],
"a character literal was not valid",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::BorrowedStr("0:00:00.0")],
"the 'hour' component could not be parsed",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::BorrowedStr("00:00:00.0x")],
"unexpected trailing characters; the end of input was expected",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Time`",
)]
#[case(
PhantomData::<Compact<Time>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Time`",
)]
#[case(
PhantomData::<Readable<Date>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Date`",
)]
#[case(
PhantomData::<Compact<Date>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Date`",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `PlainDateTime`",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `PlainDateTime`",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(2021),
Token::U16(366),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::TupleEnd,
],
"invalid ordinal, expected an in-range value",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(2021),
Token::U16(1),
Token::U8(24),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::TupleEnd,
],
"invalid hour, expected an in-range value",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected an `OffsetDateTime`",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected an `OffsetDateTime`",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(2021),
Token::U16(366),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::I8(0),
Token::I8(0),
Token::I8(0),
Token::TupleEnd,
],
"invalid ordinal, expected an in-range value",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(2021),
Token::U16(1),
Token::U8(24),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::I8(0),
Token::I8(0),
Token::I8(0),
Token::TupleEnd,
],
"invalid hour, expected an in-range value",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(2021),
Token::U16(1),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(0),
Token::I8(26),
Token::I8(0),
Token::I8(0),
Token::TupleEnd,
],
"invalid offset hour, expected an in-range value",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(2021),
Token::U16(365),
Token::U8(23),
Token::U8(59),
Token::U8(60),
Token::U32(0),
Token::I8(0),
Token::I8(0),
Token::I8(0),
Token::TupleEnd,
],
"invalid second, expected an in-range value",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[Token::BorrowedStr("2021-12-31 23:59:60.0 +00:00:00")],
"second was not in range",
)]
#[case(
PhantomData::<Readable<UtcOffset>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `UtcOffset`",
)]
#[case(
PhantomData::<Compact<UtcOffset>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `UtcOffset`",
)]
#[case(
PhantomData::<Compact<UtcOffset>>,
&[
Token::Tuple { len: 3 },
Token::I8(26),
Token::I8(0),
Token::I8(0),
Token::TupleEnd,
],
"invalid offset hour, expected an in-range value",
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[Token::BorrowedStr("x")],
r#"invalid value: string "x", expected a decimal point"#,
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[Token::BorrowedStr("x.0")],
r#"invalid value: string "x", expected seconds"#,
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[Token::BorrowedStr("0.x")],
r#"invalid value: string "x", expected nanoseconds"#,
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `SignedDuration`",
)]
#[case(
PhantomData::<Compact<SignedDuration>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `SignedDuration`",
)]
#[case(
PhantomData::<Compact<Weekday>>,
&[Token::U8(0)],
"invalid value: integer `0`, expected a value in the range 1..=7",
)]
#[case(
PhantomData::<Readable<Weekday>>,
&[Token::BorrowedStr("NotADay")],
r#"invalid value: string "NotADay", expected a `Weekday`"#,
)]
#[case(
PhantomData::<Readable<Weekday>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Weekday`",
)]
#[case(
PhantomData::<Compact<Weekday>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Weekday`",
)]
#[case(
PhantomData::<Compact<Month>>,
&[Token::U8(0)],
"invalid value: integer `0`, expected a value in the range 1..=12",
)]
#[case(
PhantomData::<Readable<Month>>,
&[Token::BorrowedStr("NotAMonth")],
r#"invalid value: string "NotAMonth", expected a `Month`"#,
)]
#[case(
PhantomData::<Readable<Month>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Month`",
)]
#[case(
PhantomData::<Compact<Month>>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected a `Month`",
)]
fn deserialize_error<T>(
#[case] _type: PhantomData<T>,
#[case] tokens: &[Token],
#[case] expected: &str,
) where
T: for<'de> Deserialize<'de>,
{
assert_de_tokens_error::<T>(tokens, expected);
}
#[rstest]
#[case(
PhantomData::<Compact<Time>>,
&[Token::Tuple { len: 4 }, Token::TupleEnd],
"expected hour",
)]
#[case(
PhantomData::<Compact<Time>>,
&[Token::Tuple { len: 4 }, Token::U8(0), Token::TupleEnd],
"expected minute",
)]
#[case(
PhantomData::<Compact<Time>>,
&[
Token::Tuple { len: 4 },
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Compact<Time>>,
&[
Token::Tuple { len: 4 },
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::Tuple { len: 4 }, Token::TupleEnd],
"expected hour",
)]
#[case(
PhantomData::<Readable<Time>>,
&[Token::Tuple { len: 4 }, Token::U8(0), Token::TupleEnd],
"expected minute",
)]
#[case(
PhantomData::<Readable<Time>>,
&[
Token::Tuple { len: 4 },
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Readable<Time>>,
&[
Token::Tuple { len: 4 },
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Compact<Date>>,
&[Token::Tuple { len: 2 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Compact<Date>>,
&[Token::Tuple { len: 2 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Readable<Date>>,
&[Token::Tuple { len: 2 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Readable<Date>>,
&[Token::Tuple { len: 2 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[Token::Tuple { len: 6 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[Token::Tuple { len: 6 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::TupleEnd,
],
"expected hour",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::TupleEnd,
],
"expected minute",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::U8(58),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Compact<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::U8(58),
Token::U8(59),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[Token::Tuple { len: 6 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[Token::Tuple { len: 6 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::TupleEnd,
],
"expected hour",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::TupleEnd,
],
"expected minute",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::U8(58),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Readable<PlainDateTime>>,
&[
Token::Tuple { len: 6 },
Token::I32(9999),
Token::U16(365),
Token::U8(23),
Token::U8(58),
Token::U8(59),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[Token::Tuple { len: 9 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[Token::Tuple { len: 9 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::TupleEnd,
],
"expected hour",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::TupleEnd,
],
"expected minute",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::TupleEnd,
],
"expected offset hours",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::I8(-23),
Token::TupleEnd,
],
"expected offset minutes",
)]
#[case(
PhantomData::<Compact<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::I8(-23),
Token::I8(-58),
Token::TupleEnd,
],
"expected offset seconds",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[Token::Tuple { len: 9 }, Token::TupleEnd],
"expected year",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[Token::Tuple { len: 9 }, Token::I32(9999), Token::TupleEnd],
"expected day of year",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::TupleEnd,
],
"expected hour",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::TupleEnd,
],
"expected minute",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected second",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::TupleEnd,
],
"expected nanosecond",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::TupleEnd,
],
"expected offset hours",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::I8(-23),
Token::TupleEnd,
],
"expected offset minutes",
)]
#[case(
PhantomData::<Readable<OffsetDateTime>>,
&[
Token::Tuple { len: 9 },
Token::I32(9999),
Token::U16(365),
Token::U8(0),
Token::U8(0),
Token::U8(0),
Token::U32(123_456_789),
Token::I8(-23),
Token::I8(-58),
Token::TupleEnd,
],
"expected offset seconds",
)]
#[case(
PhantomData::<Compact<UtcOffset>>,
&[Token::Tuple { len: 0 }, Token::TupleEnd],
"expected offset hours",
)]
#[case(
PhantomData::<Readable<UtcOffset>>,
&[Token::Tuple { len: 0 }, Token::TupleEnd],
"expected offset hours",
)]
#[case(
PhantomData::<Compact<SignedDuration>>,
&[Token::Tuple { len: 2 }, Token::TupleEnd],
"expected seconds",
)]
#[case(
PhantomData::<Compact<SignedDuration>>,
&[
Token::Tuple { len: 2 },
Token::I64(i64::MAX),
Token::TupleEnd,
],
"expected nanoseconds",
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[Token::Tuple { len: 2 }, Token::TupleEnd],
"expected seconds",
)]
#[case(
PhantomData::<Readable<SignedDuration>>,
&[
Token::Tuple { len: 2 },
Token::I64(i64::MAX),
Token::TupleEnd,
],
"expected nanoseconds",
)]
fn partial_deserialize<T>(
#[case] _type: PhantomData<T>,
#[case] tokens: &[Token],
#[case] error_message: &str,
) where
T: for<'de> Deserialize<'de>,
{
assert_de_tokens_error::<T>(tokens, error_message);
}
#[rstest]
#[case(
offset!(+23).compact(),
&[Token::Tuple { len: 1 }, Token::I8(23), Token::TupleEnd],
)]
#[case(
offset!(+23).readable(),
&[Token::Tuple { len: 1 }, Token::I8(23), Token::TupleEnd],
)]
#[case(
offset!(+23:58).compact(),
&[Token::Tuple { len: 2 }, Token::I8(23), Token::I8(58), Token::TupleEnd],
)]
#[case(
offset!(+23:58).readable(),
&[Token::Tuple { len: 2 }, Token::I8(23), Token::I8(58), Token::TupleEnd],
)]
fn deserialize_success<T>(#[case] value: T, #[case] tokens: &[Token])
where
T: Debug + PartialEq + for<'de> Deserialize<'de>,
{
assert_de_tokens::<T>(&value, tokens);
}
@@ -0,0 +1,49 @@
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{Compact, Configure, Token, assert_tokens};
use time::OffsetDateTime;
use time::serde::rfc2822;
use time_macros::datetime;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(with = "rfc2822")]
dt: OffsetDateTime,
#[serde(with = "rfc2822::option")]
option_dt: Option<OffsetDateTime>,
}
#[rstest]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: Some(datetime!(2000-01-01 00:00:00 UTC)),
}.compact(),
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("Sat, 01 Jan 2000 00:00:00 +0000"),
Token::Str("option_dt"),
Token::Some,
Token::BorrowedStr("Sat, 01 Jan 2000 00:00:00 +0000"),
Token::StructEnd,
]
)]
fn serialize_deserialize(#[case] value: Compact<Test>, #[case] tokens: &[Token]) {
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
r#"{"dt": "Sat, 01 Jan 2000 00:00:00 +0000", "option_dt": null}"#,
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: None,
}
)]
fn parse_json(#[case] json: &str, #[case] expected: Test) {
assert_eq!(serde_json::from_str::<Test>(json).ok(), Some(expected));
}
@@ -0,0 +1,134 @@
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{
Compact, Configure, Token, assert_de_tokens_error, assert_ser_tokens_error, assert_tokens,
};
use time::OffsetDateTime;
use time::macros::datetime;
use time::serde::rfc3339;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct Test {
#[serde(with = "rfc3339")]
dt: OffsetDateTime,
#[serde(with = "rfc3339::option")]
option_dt: Option<OffsetDateTime>,
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct TestWithDefault {
#[serde(with = "rfc3339")]
date: OffsetDateTime,
#[serde(with = "rfc3339::option", default)]
maybe_date: Option<OffsetDateTime>,
}
#[rstest]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: Some(datetime!(2000-01-01 00:00:00 UTC)),
}.compact(),
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("2000-01-01T00:00:00Z"),
Token::Str("option_dt"),
Token::Some,
Token::BorrowedStr("2000-01-01T00:00:00Z"),
Token::StructEnd,
]
)]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: None,
}.compact(),
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("2000-01-01T00:00:00Z"),
Token::Str("option_dt"),
Token::None,
Token::StructEnd,
]
)]
fn serialize_deserialize(#[case] value: Compact<Test>, #[case] tokens: &[Token]) {
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
Token::BorrowedStr("bad"),
Token::StructEnd,
],
"the 'year' component could not be parsed"
)]
fn deserialize_error(#[case] tokens: &[Token], #[case] expected: &str) {
assert_de_tokens_error::<Test>(tokens, expected);
}
#[rstest]
#[case(
Test {
dt: datetime!(2000-01-01 00:00:00 +00:00:01),
option_dt: None,
},
&[
Token::Struct {
name: "Test",
len: 2,
},
Token::Str("dt"),
],
"The offset_second component cannot be formatted into the requested format."
)]
fn serialize_error(#[case] value: Test, #[case] tokens: &[Token], #[case] expected: &str) {
assert_ser_tokens_error::<Test>(&value, tokens, expected);
}
#[rstest]
#[case(
r#"{"dt": "2000-01-01T00:00:00Z", "option_dt": null}"#,
Test {
dt: datetime!(2000-01-01 00:00:00 UTC),
option_dt: None,
}
)]
fn parse_json(#[case] json: &str, #[case] expected: Test) {
assert_eq!(serde_json::from_str::<Test>(json).ok(), Some(expected));
}
#[rstest]
#[case(
r#"{"date": "2022-05-01T10:20:42.123Z"}"#,
TestWithDefault {
date: datetime!(2022-05-01 10:20:42.123 UTC),
maybe_date: None,
}
)]
#[case(
r#"{"date": "2022-05-01T10:20:42.123Z", "maybe_date": "2022-05-01T10:20:42.123Z"}"#,
TestWithDefault {
date: datetime!(2022-05-01 10:20:42.123 UTC),
maybe_date: Some(datetime!(2022-05-01 10:20:42.123 UTC)),
}
)]
fn issue_479(#[case] json: &str, #[case] expected: TestWithDefault) {
assert_eq!(
serde_json::from_str::<TestWithDefault>(json).ok(),
Some(expected)
);
}
@@ -0,0 +1,117 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{Token, assert_de_tokens_error, assert_tokens};
use time::OffsetDateTime;
use time::macros::datetime;
use time::serde::timestamp;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestSeconds {
#[serde(with = "timestamp")]
dt: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestMilliseconds {
#[serde(with = "timestamp::milliseconds")]
dt: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestMicroseconds {
#[serde(with = "timestamp::microseconds")]
dt: OffsetDateTime,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestNanoseconds {
#[serde(with = "timestamp::nanoseconds")]
dt: OffsetDateTime,
}
#[rstest]
#[case(
TestSeconds { dt: datetime!(2000-01-01 00:00:00 UTC) },
&[
Token::Struct {
name: "TestSeconds",
len: 1,
},
Token::Str("dt"),
Token::I64(946684800),
Token::StructEnd,
],
)]
// TODO add cases for milliseconds, microseconds, and nanoseconds when serde_test supports i128
fn round_trip<T>(#[case] value: T, #[case] tokens: &[Token])
where
T: Debug + PartialEq + Serialize + for<'de> Deserialize<'de>,
{
assert_tokens(&value, tokens);
}
#[rstest]
#[case(
PhantomData::<TestSeconds>,
&[
Token::Struct {
name: "TestSeconds",
len: 1,
},
Token::Str("dt"),
Token::Str("bad"),
Token::StructEnd,
],
r#"invalid type: string "bad", expected i64"#,
)]
#[case(
PhantomData::<TestMilliseconds>,
&[
Token::Struct {
name: "TestMilliseconds",
len: 1,
},
Token::Str("dt"),
Token::Str("bad"),
Token::StructEnd,
],
r#"invalid type: string "bad", expected i128"#,
)]
#[case(
PhantomData::<TestMicroseconds>,
&[
Token::Struct {
name: "TestMicroseconds",
len: 1,
},
Token::Str("dt"),
Token::Str("bad"),
Token::StructEnd,
],
r#"invalid type: string "bad", expected i128"#,
)]
#[case(
PhantomData::<TestNanoseconds>,
&[
Token::Struct {
name: "TestNanoseconds",
len: 1,
},
Token::Str("dt"),
Token::Str("bad"),
Token::StructEnd,
],
r#"invalid type: string "bad", expected i128"#,
)]
fn deserialize_error<T>(
#[case] _type: PhantomData<T>,
#[case] tokens: &[Token],
#[case] expected: &str,
) where
T: for<'de> Deserialize<'de>,
{
assert_de_tokens_error::<T>(tokens, expected);
}
@@ -0,0 +1,247 @@
use std::fmt::Debug;
use std::marker::PhantomData;
use rstest::rstest;
use serde::{Deserialize, Serialize};
use serde_test2::{Token, assert_de_tokens_error, assert_ser_tokens_error, assert_tokens};
use time::OffsetDateTime;
use time::macros::datetime;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Rfc2822(
#[serde(with = "time::serde::rfc2822")] OffsetDateTime,
#[serde(with = "time::serde::rfc2822::option")] Option<OffsetDateTime>,
#[serde(with = "time::serde::rfc2822::option")] Option<OffsetDateTime>,
);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Rfc3339(
#[serde(with = "time::serde::rfc3339")] OffsetDateTime,
#[serde(with = "time::serde::rfc3339::option")] Option<OffsetDateTime>,
#[serde(with = "time::serde::rfc3339::option")] Option<OffsetDateTime>,
);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
struct Timestamp(#[serde(with = "time::serde::timestamp")] OffsetDateTime);
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
struct OptTimestamp(#[serde(with = "time::serde::timestamp::option")] Option<OffsetDateTime>);
#[rstest]
#[case(
Rfc2822(
datetime!(2021-01-02 03:04:05 UTC),
Some(datetime!(2021-01-02 03:04:05 UTC)),
None
), &[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
Token::Str("Sat, 02 Jan 2021 03:04:05 +0000"),
Token::Some,
Token::Str("Sat, 02 Jan 2021 03:04:05 +0000"),
Token::None,
Token::TupleStructEnd,
],)
]
#[case(
Rfc3339(
datetime!(2021-01-02 03:04:05 UTC),
Some(datetime!(2021-01-02 03:04:05 UTC)),
None
), &[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
Token::Str("2021-01-02T03:04:05Z"),
Token::Some,
Token::Str("2021-01-02T03:04:05Z"),
Token::None,
Token::TupleStructEnd,
],
)]
#[case(Timestamp(datetime!(2021-01-02 03:04:05 UTC)), &[Token::I64(1_609_556_645)])]
#[case(
OptTimestamp(Some(datetime!(2021-01-02 03:04:05 UTC))),
&[Token::Some, Token::I64(1_609_556_645)],
)]
#[case(OptTimestamp(None), &[Token::None])]
fn success<T>(#[case] input: T, #[case] expected: &[Token])
where
T: Debug + PartialEq + Serialize + for<'de> Deserialize<'de>,
{
assert_tokens(&input, expected);
}
#[rstest]
#[case(
Rfc2822(
datetime!(2021-01-02 03:04:05 +0:00:01),
Some(datetime!(2021-01-02 03:04:05 UTC)),
None
),
&[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
],
"The offset_second component cannot be formatted into the requested format.",
)]
#[case(
Rfc2822(
datetime!(2021-01-02 03:04:05 UTC),
Some(datetime!(2021-01-02 03:04:05 +0:00:01)),
None
),
&[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
Token::Str("Sat, 02 Jan 2021 03:04:05 +0000"),
],
"The offset_second component cannot be formatted into the requested format.",
)]
#[case(
Rfc3339(
datetime!(2021-01-02 03:04:05 +0:00:01),
Some(datetime!(2021-01-02 03:04:05 UTC)),
None
),
&[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
],
"The offset_second component cannot be formatted into the requested format.",
)]
#[case(
Rfc3339(
datetime!(2021-01-02 03:04:05 UTC),
Some(datetime!(2021-01-02 03:04:05 +0:00:01)),
None
),
&[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
Token::Str("2021-01-02T03:04:05Z"),
],
"The offset_second component cannot be formatted into the requested format.",
)]
fn serialize_error<T>(#[case] value: T, #[case] tokens: &[Token], #[case] error: &str)
where
T: Serialize,
{
assert_ser_tokens_error(&value, tokens, error);
}
#[rstest]
#[case(
PhantomData::<Rfc2822>,
&[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC2822-formatted `OffsetDateTime`",
)]
#[case(
PhantomData::<Rfc2822>,
&[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
Token::Str("Sat, 02 Jan 2021 03:04:05 +0000"),
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC2822-formatted `Option<OffsetDateTime>`",
)]
#[case(
PhantomData::<Rfc2822>,
&[
Token::TupleStruct {
name: "Rfc2822",
len: 3,
},
Token::Str("Sat, 02 Jan 2021 03:04:05 +0000"),
Token::Some,
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC2822-formatted `OffsetDateTime`",
)]
#[case(
PhantomData::<Rfc3339>,
&[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC3339-formatted `OffsetDateTime`",
)]
#[case(
PhantomData::<Rfc3339>,
&[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
Token::Str("2021-01-02T03:04:05Z"),
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC3339-formatted `Option<OffsetDateTime>`",
)]
#[case(
PhantomData::<Rfc3339>,
&[
Token::TupleStruct {
name: "Rfc3339",
len: 3,
},
Token::Str("2021-01-02T03:04:05Z"),
Token::Some,
Token::Bool(false),
],
"invalid type: boolean `false`, expected an RFC3339-formatted `OffsetDateTime`",
)]
#[case(
PhantomData::<Timestamp>,
&[Token::Bool(false)],
"invalid type: boolean `false`, expected i64",
)]
#[case(
PhantomData::<OptTimestamp>,
&[Token::Some, Token::Bool(false)],
"invalid type: boolean `false`, expected i64",
)]
#[case(
PhantomData::<Timestamp>,
&[Token::I64(100_000_000_000_000)],
"invalid timestamp, expected an in-range value",
)]
#[case(
PhantomData::<OptTimestamp>,
&[Token::Some, Token::I64(-100_000_000_000_000)],
"invalid timestamp, expected an in-range value",
)]
fn deserialize_error<T>(
#[case] _type: PhantomData<T>,
#[case] tokens: &[Token],
#[case] error: &str,
) where
T: for<'de> Deserialize<'de>,
{
assert_de_tokens_error::<T>(tokens, error);
}
+432
View File
@@ -0,0 +1,432 @@
use std::cmp::Ordering;
use rstest::rstest;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::time;
use time::{Result, SignedDuration, Time};
#[rstest]
#[case(1, 2, 3, time!(1:02:03))]
#[case(24, 0, 0, None)]
#[case(0, 60, 0, None)]
#[case(0, 0, 60, None)]
fn from_hms(
#[case] hour: u8,
#[case] minute: u8,
#[case] second: u8,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(Time::from_hms(hour, minute, second).ok(), expected.into());
}
#[rstest]
#[case(1, 2, 3, 4, time!(1:02:03.004))]
#[case(24, 0, 0, 0, None)]
#[case(0, 60, 0, 0, None)]
#[case(0, 0, 60, 0, None)]
#[case(0, 0, 0, 1_000, None)]
fn from_hms_milli(
#[case] hour: u8,
#[case] minute: u8,
#[case] second: u8,
#[case] milli: u16,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(
Time::from_hms_milli(hour, minute, second, milli).ok(),
expected.into()
);
}
#[rstest]
#[case(1, 2, 3, 4, time!(1:02:03.000_004))]
#[case(24, 0, 0, 0, None)]
#[case(0, 60, 0, 0, None)]
#[case(0, 0, 60, 0, None)]
#[case(0, 0, 0, 1_000_000, None)]
fn from_hms_micro(
#[case] hour: u8,
#[case] minute: u8,
#[case] second: u8,
#[case] micro: u32,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(
Time::from_hms_micro(hour, minute, second, micro).ok(),
expected.into()
);
}
#[rstest]
#[case(1, 2, 3, 4, time!(1:02:03.000_000_004))]
#[case(24, 0, 0, 0, None)]
#[case(0, 60, 0, 0, None)]
#[case(0, 0, 60, 0, None)]
#[case(0, 0, 0, 1_000_000_000, None)]
fn from_hms_nano(
#[case] hour: u8,
#[case] minute: u8,
#[case] second: u8,
#[case] nano: u32,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(
Time::from_hms_nano(hour, minute, second, nano).ok(),
expected.into()
);
}
#[rstest]
#[case(time!(1:02:03), (1, 2, 3))]
fn as_hms(#[case] datetime: Time, #[case] expected: (u8, u8, u8)) {
assert_eq!(datetime.as_hms(), expected);
}
#[rstest]
#[case(time!(1:02:03.004), (1, 2, 3, 4))]
fn as_hms_milli(#[case] datetime: Time, #[case] expected: (u8, u8, u8, u16)) {
assert_eq!(datetime.as_hms_milli(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005), (1, 2, 3, 4_005))]
fn as_hms_micro(#[case] datetime: Time, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(datetime.as_hms_micro(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), (1, 2, 3, 4_005_006))]
fn as_hms_nano(#[case] datetime: Time, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(datetime.as_hms_nano(), expected);
}
#[rstest]
#[case(0)]
#[case(12)]
#[case(23)]
fn hour(#[case] hour_: u8) -> Result<()> {
assert_eq!(Time::from_hms(hour_, 0, 0)?.hour(), hour_);
assert_eq!(Time::from_hms(hour_, 59, 59)?.hour(), hour_);
Ok(())
}
#[rstest]
#[case(0)]
#[case(30)]
#[case(59)]
fn minute(#[case] minute_: u8) -> Result<()> {
assert_eq!(Time::from_hms(0, minute_, 0)?.minute(), minute_);
assert_eq!(Time::from_hms(23, minute_, 59)?.minute(), minute_);
Ok(())
}
#[rstest]
#[case(0)]
#[case(30)]
#[case(59)]
fn second(#[case] second_: u8) -> Result<()> {
assert_eq!(Time::from_hms(0, 0, second_)?.second(), second_);
assert_eq!(Time::from_hms(23, 59, second_)?.second(), second_);
Ok(())
}
#[rstest]
#[case(0)]
#[case(500)]
#[case(999)]
fn millisecond(#[case] milli: u16) -> Result<()> {
assert_eq!(Time::from_hms_milli(0, 0, 0, milli)?.millisecond(), milli);
assert_eq!(
Time::from_hms_milli(23, 59, 59, milli)?.millisecond(),
milli
);
Ok(())
}
#[rstest]
#[case(0)]
#[case(4_005)]
#[case(999_000)]
fn microsecond(#[case] micro: u32) -> Result<()> {
assert_eq!(Time::from_hms_micro(0, 0, 0, micro)?.microsecond(), micro);
assert_eq!(
Time::from_hms_micro(23, 59, 59, micro)?.microsecond(),
micro
);
Ok(())
}
#[rstest]
#[case(0)]
#[case(4_005_006)]
#[case(999_000_000)]
fn nanosecond(#[case] nano: u32) -> Result<()> {
assert_eq!(Time::from_hms_nano(0, 0, 0, nano)?.nanosecond(), nano);
assert_eq!(Time::from_hms_nano(23, 59, 59, nano)?.nanosecond(), nano);
Ok(())
}
#[rstest]
#[case(time!(18:00), Time::MIDNIGHT, 6.hours())]
#[case(time!(23:00), time!(1:00), 2.hours())]
#[case(time!(12:30), time!(14:00), 90.minutes())]
fn duration_until(#[case] start: Time, #[case] end: Time, #[case] expected: SignedDuration) {
assert_eq!(start.duration_until(end), expected);
}
#[rstest]
#[case(Time::MIDNIGHT, time!(18:00), 6.hours())]
#[case(time!(1:00), time!(23:00), 2.hours())]
#[case(time!(14:00), time!(12:30), 90.minutes())]
fn duration_since(#[case] end: Time, #[case] start: Time, #[case] expected: SignedDuration) {
assert_eq!(end.duration_since(start), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7, time!(7:02:03.004_005_006))]
#[case(time!(1:02:03.004_005_006), 24, None)]
fn replace_hour(
#[case] time_val: Time,
#[case] value: u8,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time_val.replace_hour(value).ok(), expected.into());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7, time!(1:07:03.004_005_006))]
#[case(time!(1:02:03.004_005_006), 60, None)]
fn replace_minute(
#[case] time_val: Time,
#[case] value: u8,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time_val.replace_minute(value).ok(), expected.into());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7, time!(1:02:07.004_005_006))]
#[case(time!(1:02:03.004_005_006), 60, None)]
fn replace_second(
#[case] time_val: Time,
#[case] value: u8,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time_val.replace_second(value).ok(), expected.into());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7, time!(1:02:03.007))]
#[case(time!(1:02:03.004_005_006), 1_000, None)]
fn replace_millisecond(
#[case] time_val: Time,
#[case] value: u16,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time_val.replace_millisecond(value).ok(), expected.into());
}
#[rstest]
#[case(9999)]
#[case(4294)]
#[case(4295)]
fn replace_millisecond_regression(#[case] milli: u16) {
assert!(Time::MIDNIGHT.replace_millisecond(milli).is_err());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7_008, time!(1:02:03.007_008))]
#[case(time!(1:02:03.004_005_006), 1_000_000, None)]
fn replace_microsecond(
#[case] time: Time,
#[case] value: u32,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time.replace_microsecond(value).ok(), expected.into());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), 7_008_009, time!(1:02:03.007_008_009))]
#[case(time!(1:02:03.004_005_006), 1_000_000_000, None)]
fn replace_nanosecond(
#[case] time: Time,
#[case] value: u32,
#[case] expected: impl Into<Option<Time>>,
) {
assert_eq!(time.replace_nanosecond(value).ok(), expected.into());
}
#[rstest]
#[case(time!(1:02:03.004_005_006), time!(1:00))]
#[case(Time::MIDNIGHT, Time::MIDNIGHT)]
fn truncate_to_hour(#[case] time: Time, #[case] expected: Time) {
assert_eq!(time.truncate_to_hour(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), time!(1:02))]
#[case(Time::MIDNIGHT, Time::MIDNIGHT)]
fn truncate_to_minute(#[case] time: Time, #[case] expected: Time) {
assert_eq!(time.truncate_to_minute(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), time!(1:02:03))]
#[case(Time::MIDNIGHT, Time::MIDNIGHT)]
fn truncate_to_second(#[case] time: Time, #[case] expected: Time) {
assert_eq!(time.truncate_to_second(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), time!(1:02:03.004))]
#[case(Time::MIDNIGHT, Time::MIDNIGHT)]
fn truncate_to_millisecond(#[case] itimeput: Time, #[case] expected: Time) {
assert_eq!(itimeput.truncate_to_millisecond(), expected);
}
#[rstest]
#[case(time!(1:02:03.004_005_006), time!(1:02:03.004_005))]
#[case(Time::MIDNIGHT, Time::MIDNIGHT)]
fn truncate_to_microsecond(#[case] time: Time, #[case] expected: Time) {
assert_eq!(time.truncate_to_microsecond(), expected);
}
#[rstest]
#[case(time!(0:00), 1.seconds(), time!(0:00:01))]
#[case(time!(0:00), 1.minutes(), time!(0:01))]
#[case(time!(0:00), 1.hours(), time!(1:00))]
#[case(time!(0:00), 1.days(), time!(0:00))]
fn add_duration(#[case] input: Time, #[case] duration: SignedDuration, #[case] expected: Time) {
assert_eq!(input + duration, expected);
}
#[rstest]
#[case(time!(0:00), 1.seconds(), time!(0:00:01))]
#[case(time!(0:00), 1.minutes(), time!(0:01:00))]
#[case(time!(0:00), 1.hours(), time!(1:00:00))]
#[case(time!(0:00), 1.days(), time!(0:00))]
fn add_assign_duration(
#[case] mut time: Time,
#[case] duration: SignedDuration,
#[case] expected: Time,
) {
time += duration;
assert_eq!(time, expected);
}
#[rstest]
#[case(time!(12:00), 1.hours(), time!(11:00))]
#[case(time!(0:00), 1.seconds(), time!(23:59:59))]
#[case(time!(0:00), 1.minutes(), time!(23:59))]
#[case(time!(0:00), 1.hours(), time!(23:00))]
#[case(time!(0:00), 1.days(), time!(0:00))]
fn sub_duration(#[case] time: Time, #[case] duration: SignedDuration, #[case] expected: Time) {
assert_eq!(time - duration, expected);
}
#[rstest]
#[case(time!(0:00), 1.seconds(), time!(23:59:59))]
#[case(time!(0:00), 1.minutes(), time!(23:59))]
#[case(time!(0:00), 1.hours(), time!(23:00))]
#[case(time!(0:00), 1.days(), time!(0:00))]
fn sub_assign_duration(
#[case] mut time: Time,
#[case] duration: SignedDuration,
#[case] expected: Time,
) {
time -= duration;
assert_eq!(time, expected);
}
#[rstest]
#[case(time!(0:00), 1.std_milliseconds(), time!(0:00:00.001))]
#[case(time!(0:00), 1.std_seconds(), time!(0:00:01))]
#[case(time!(0:00), 1.std_minutes(), time!(0:01))]
#[case(time!(0:00), 1.std_hours(), time!(1:00))]
#[case(time!(0:00), 1.std_days(), time!(0:00))]
fn add_std_duration(
#[case] time: Time,
#[case] duration: std::time::Duration,
#[case] expected: Time,
) {
assert_eq!(time + duration, expected);
}
#[rstest]
#[case(time!(0:00), 1.std_seconds(), time!(0:00:01))]
#[case(time!(0:00), 1.std_minutes(), time!(0:01))]
#[case(time!(0:00), 1.std_hours(), time!(1:00))]
#[case(time!(0:00), 1.std_days(), time!(0:00))]
fn add_assign_std_duration(
#[case] mut time: Time,
#[case] duration: std::time::Duration,
#[case] expected: Time,
) {
time += duration;
assert_eq!(time, expected);
}
#[rstest]
#[case(time!(12:00), 1.std_hours(), time!(11:00))]
#[case(time!(0:00), 1.std_milliseconds(), time!(23:59:59.999))]
#[case(time!(0:00), 1.std_seconds(), time!(23:59:59))]
#[case(time!(0:00), 1.std_minutes(), time!(23:59))]
#[case(time!(0:00), 1.std_hours(), time!(23:00))]
#[case(time!(0:00), 1.std_days(), time!(0:00))]
fn sub_std_duration(
#[case] time: Time,
#[case] duration: std::time::Duration,
#[case] expected: Time,
) {
assert_eq!(time - duration, expected);
}
#[rstest]
#[case(time!(0:00), 1.std_seconds(), time!(23:59:59))]
#[case(time!(0:00), 1.std_minutes(), time!(23:59))]
#[case(time!(0:00), 1.std_hours(), time!(23:00))]
#[case(time!(0:00), 1.std_days(), time!(0:00))]
fn sub_assign_std_duration(
#[case] mut time: Time,
#[case] duration: std::time::Duration,
#[case] expected: Time,
) {
time -= duration;
assert_eq!(time, expected);
}
#[rstest]
#[case(time!(0:00), time!(0:00), 0.seconds())]
#[case(time!(1:00), time!(0:00), 1.hours())]
#[case(time!(1:00), time!(0:00:01), 59.minutes() + 59.seconds())]
fn sub_time(#[case] a: Time, #[case] b: Time, #[case] expected: SignedDuration) {
assert_eq!(a - b, expected);
}
#[rstest]
#[case(time!(0:00), time!(0:00:00.000_000_001), Ordering::Less)]
#[case(time!(0:00), time!(0:00:01), Ordering::Less)]
#[case(time!(12:00), time!(11:00), Ordering::Greater)]
#[case(time!(0:00), time!(0:00), Ordering::Equal)]
fn ordering(#[case] a: Time, #[case] b: Time, #[case] expected: Ordering) {
assert_eq!(a.cmp(&b), expected);
}
#[rstest]
#[case(time!(00:00:00.4), time!(00:00:00.1))]
#[case(time!(01:00:00), time!(00:01:00.0))]
#[case(time!(01:00:00), time!(00:00:01.0))]
#[case(time!(01:00:00), time!(00:00:00.1))]
#[case(time!(00:01:00), time!(00:00:01.0))]
#[case(time!(00:01:00), time!(00:00:00.1))]
#[case(time!(00:00:01), time!(00:00:00.1))]
fn ordering_lexico_endianness(#[case] higher: Time, #[case] lower: Time) {
assert!(higher > lower);
}
#[rstest]
#[case(time!(0:00), time!(01:00:00.1), (-3600.1).seconds())]
#[case(time!(0:00), time!(23:59:59.999_999_999), (-86_399.999_999_999).seconds())]
fn issue_481(#[case] a: Time, #[case] b: Time, #[case] expected: SignedDuration) {
assert_eq!(a - b, expected);
}
+983
View File
@@ -0,0 +1,983 @@
use std::cmp::Ordering;
use std::time::{Duration as StdDuration, SystemTime};
use rstest::rstest;
use time::Weekday::*;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::{date, format_description, offset, time, timestamp, utc_datetime};
use time::{
Date, Month, OffsetDateTime, SignedDuration, Time, Timestamp, UtcDateTime, UtcOffset, Weekday,
};
#[rstest]
fn unix_epoch() {
assert_eq!(Timestamp::UNIX_EPOCH.as_seconds(), 0);
assert_eq!(Timestamp::UNIX_EPOCH.as_nanoseconds(), 0);
}
#[rstest]
#[case(0, 0, timestamp!(0))]
#[case(1, 0, timestamp!(1))]
#[case(-1, 0, timestamp!(-1))]
#[case(1_546_398_245, 0, timestamp!(1_546_398_245))]
#[case(1_546_398_245, 1, timestamp!(1_546_398_245.000_000_001))]
#[case(i64::MAX, 0, None)]
#[case(i64::MIN, 0, None)]
#[case(0, 1_000_000_000, None)]
fn new(
#[case] seconds: i64,
#[case] nanoseconds: u32,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(Timestamp::new(seconds, nanoseconds).ok(), expected.into());
}
#[rstest]
#[case(0, timestamp!(0))]
#[case(1, timestamp!(1))]
#[case(-1, timestamp!(-1))]
#[case(i64::MAX, None)]
#[case(i64::MIN, None)]
fn from_seconds(#[case] seconds: i64, #[case] expected: impl Into<Option<Timestamp>>) {
assert_eq!(Timestamp::from_seconds(seconds).ok(), expected.into());
}
#[rstest]
#[case(0, timestamp!(0))]
#[case(1_000, timestamp!(1))]
#[case(-1_000, timestamp!(-1))]
#[case(1_546_398_245_006, timestamp!(1_546_398_245.006))]
#[case(1_500, timestamp!(1.500))]
#[case(-1_500, timestamp!(-1.500))]
#[case(-500, timestamp!(-0.500))]
#[case(500, timestamp!(0.500))]
#[case(i64::MAX, None)]
#[case(i64::MIN, None)]
fn from_milliseconds(#[case] milliseconds: i64, #[case] expected: impl Into<Option<Timestamp>>) {
assert_eq!(
Timestamp::from_milliseconds(milliseconds).ok(),
expected.into()
);
}
#[rstest]
#[case(0, timestamp!(0))]
#[case(1_000_000, timestamp!(1))]
#[case(-1_000_000, timestamp!(-1))]
#[case(1_546_398_245_006_007, timestamp!(1_546_398_245.006_007))]
#[case(1_500_000, timestamp!(1.500_000))]
#[case(-1_500_000, timestamp!(-1.500_000))]
#[case(-500_000, timestamp!(-0.500_000))]
#[case(i128::MAX, None)]
#[case(i128::MIN, None)]
fn from_microseconds(#[case] microseconds: i128, #[case] expected: impl Into<Option<Timestamp>>) {
assert_eq!(
Timestamp::from_microseconds(microseconds).ok(),
expected.into()
);
}
#[rstest]
#[case(0, timestamp!(0))]
#[case(1_000_000_000, timestamp!(1))]
#[case(-1_000_000_000, timestamp!(-1))]
#[case(1_546_398_245_006_007_008, timestamp!(1_546_398_245.006_007_008))]
#[case(1_500_000_000, timestamp!(1.500_000_000))]
#[case(-1_500_000_000, timestamp!(-1.500_000_000))]
#[case(-500_000_000, timestamp!(-0.500_000_000))]
#[case(i128::MAX, None)]
#[case(i128::MIN, None)]
fn from_nanoseconds(#[case] nanoseconds: i128, #[case] expected: impl Into<Option<Timestamp>>) {
assert_eq!(
Timestamp::from_nanoseconds(nanoseconds).ok(),
expected.into()
);
}
#[rstest]
#[case(timestamp!(0), offset!(UTC), 0)]
#[case(timestamp!(1_546_398_245), offset!(+1), 4)]
fn to_offset(#[case] ts: Timestamp, #[case] offset: UtcOffset, #[case] expected_hour: u8) {
assert_eq!(ts.to_offset(offset).hour(), expected_hour);
}
#[rstest]
#[case(timestamp!(1_546_398_245), offset!(+1), true)]
#[case(Timestamp::MAX, offset!(+1), false)]
#[case(Timestamp::MIN, offset!(-1), false)]
fn checked_to_offset(#[case] ts: Timestamp, #[case] offset: UtcOffset, #[case] expected: bool) {
assert_eq!(ts.checked_to_offset(offset).is_some(), expected);
}
#[rstest]
#[case(timestamp!(0), UtcDateTime::UNIX_EPOCH)]
#[case(timestamp!(1_546_398_245), utc_datetime!(2019-01-02 3:04:05))]
fn to_utc(#[case] ts: Timestamp, #[case] expected: UtcDateTime) {
assert_eq!(ts.to_utc(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(-1), -1)]
#[case(timestamp!(1_546_398_245), 1_546_398_245)]
#[case(timestamp!(1_546_398_245.006_007_008), 1_546_398_245)]
fn as_seconds(#[case] ts: Timestamp, #[case] expected: i64) {
assert_eq!(ts.as_seconds(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1), 1_000)]
#[case(timestamp!(-1), -1_000)]
#[case(timestamp!(1_546_398_245.006), 1_546_398_245_006)]
#[case(timestamp!(1_546_398_245.006_007_008), 1_546_398_245_006)]
fn as_milliseconds(#[case] ts: Timestamp, #[case] expected: i64) {
assert_eq!(ts.as_milliseconds(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1), 1_000_000)]
#[case(timestamp!(-1), -1_000_000)]
#[case(timestamp!(1_546_398_245.006_007), 1_546_398_245_006_007)]
#[case(timestamp!(1_546_398_245.006_007_008), 1_546_398_245_006_007)]
fn as_microseconds(#[case] ts: Timestamp, #[case] expected: i128) {
assert_eq!(ts.as_microseconds(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1), 1_000_000_000)]
#[case(timestamp!(-1), -1_000_000_000)]
#[case(timestamp!(1_546_398_245.006_007_008), 1_546_398_245_006_007_008)]
fn as_nanoseconds(#[case] ts: Timestamp, #[case] expected: i128) {
assert_eq!(ts.as_nanoseconds(), expected);
}
#[rstest]
#[case(timestamp!(0), date!(1970-01-01))]
#[case(timestamp!(1_546_398_245), date!(2019-01-02))]
#[case(timestamp!(-1), date!(1969-12-31))]
fn date(#[case] ts: Timestamp, #[case] expected: Date) {
assert_eq!(ts.date(), expected);
}
#[rstest]
#[case(timestamp!(0), time!(0:00))]
#[case(timestamp!(1_546_398_245), time!(3:04:05))]
fn time_(#[case] ts: Timestamp, #[case] expected: Time) {
assert_eq!(ts.time(), expected);
}
#[rstest]
#[case(timestamp!(0), 1970)]
#[case(timestamp!(1_546_398_245), 2019)]
#[case(timestamp!(-1), 1969)]
fn year(#[case] ts: Timestamp, #[case] expected: i32) {
assert_eq!(ts.year(), expected);
}
#[rstest]
#[case(timestamp!(0), Month::January)]
#[case(timestamp!(1_546_398_245), Month::January)]
fn month(#[case] ts: Timestamp, #[case] expected: Month) {
assert_eq!(ts.month(), expected);
}
#[rstest]
#[case(timestamp!(0), 1)]
#[case(timestamp!(1_546_398_245), 2)]
fn day(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.day(), expected);
}
#[rstest]
#[case(timestamp!(0), 1)]
#[case(timestamp!(1_546_398_245), 2)]
fn ordinal(#[case] ts: Timestamp, #[case] expected: u16) {
assert_eq!(ts.ordinal(), expected);
}
#[rstest]
#[case(timestamp!(0), 1)]
#[case(timestamp!(1_546_398_245), 1)]
fn iso_week(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.iso_week(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245), 0)]
fn sunday_based_week(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.sunday_based_week(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245), 0)]
fn monday_based_week(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.monday_based_week(), expected);
}
#[rstest]
#[case(timestamp!(0), (1970, Month::January, 1))]
#[case(timestamp!(1_546_398_245), (2019, Month::January, 2))]
fn to_calendar_date(#[case] ts: Timestamp, #[case] expected: (i32, Month, u8)) {
assert_eq!(ts.to_calendar_date(), expected);
}
#[rstest]
#[case(timestamp!(0), (1970, 1))]
#[case(timestamp!(1_546_398_245), (2019, 2))]
fn to_ordinal_date(#[case] ts: Timestamp, #[case] expected: (i32, u16)) {
assert_eq!(ts.to_ordinal_date(), expected);
}
#[rstest]
#[case(timestamp!(0), (1970, 1, Thursday))]
#[case(timestamp!(1_546_398_245), (2019, 1, Wednesday))]
fn to_iso_week_date(#[case] ts: Timestamp, #[case] expected: (i32, u8, Weekday)) {
assert_eq!(ts.to_iso_week_date(), expected);
}
#[rstest]
#[case(timestamp!(0), Thursday)]
#[case(timestamp!(1_546_398_245), Wednesday)]
fn weekday(#[case] ts: Timestamp, #[case] expected: Weekday) {
assert_eq!(ts.weekday(), expected);
}
#[rstest]
#[case(timestamp!(0), Thursday)]
#[case(timestamp!(86_400), Friday)]
#[case(timestamp!(172_800), Saturday)]
#[case(timestamp!(259_200), Sunday)]
#[case(timestamp!(345_600), Monday)]
#[case(timestamp!(432_000), Tuesday)]
#[case(timestamp!(518_400), Wednesday)]
#[case(timestamp!(22_118_400), Monday)] // 256 days past epoch; > i8::MAX
#[case(timestamp!(-22_118_400), Sunday)] // 256 days before epoch; < i8::MIN
fn weekday_exhaustive(#[case] ts: Timestamp, #[case] expected: Weekday) {
assert_eq!(ts.weekday(), expected);
}
#[rstest]
#[case(timestamp!(0), (0, 0, 0))]
#[case(timestamp!(1_546_398_245), (3, 4, 5))]
fn as_hms(#[case] ts: Timestamp, #[case] expected: (u8, u8, u8)) {
assert_eq!(ts.as_hms(), expected);
}
#[rstest]
#[case(timestamp!(0), (0, 0, 0, 0))]
#[case(timestamp!(1_546_398_245.006), (3, 4, 5, 6))]
fn as_hms_milli(#[case] ts: Timestamp, #[case] expected: (u8, u8, u8, u16)) {
assert_eq!(ts.as_hms_milli(), expected);
}
#[rstest]
#[case(timestamp!(0), (0, 0, 0, 0))]
#[case(timestamp!(1_546_398_245.006_007), (3, 4, 5, 6_007))]
fn as_hms_micro(#[case] ts: Timestamp, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(ts.as_hms_micro(), expected);
}
#[rstest]
#[case(timestamp!(0), (0, 0, 0, 0))]
#[case(timestamp!(1_546_398_245.006_007_008), (3, 4, 5, 6_007_008))]
fn as_hms_nano(#[case] ts: Timestamp, #[case] expected: (u8, u8, u8, u32)) {
assert_eq!(ts.as_hms_nano(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245), 3)]
#[case(timestamp!(86_399), 23)]
fn hour(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.hour(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245), 4)]
#[case(timestamp!(86_340), 59)]
#[case(timestamp!(-1), 59)]
#[case(timestamp!(-61), 58)]
fn minute(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.minute(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245), 5)]
#[case(timestamp!(86_399), 59)]
fn second(#[case] ts: Timestamp, #[case] expected: u8) {
assert_eq!(ts.second(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245.006), 6)]
#[case(timestamp!(1_546_398_245.999), 999)]
fn millisecond(#[case] ts: Timestamp, #[case] expected: u16) {
assert_eq!(ts.millisecond(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245.006_007), 6_007)]
#[case(timestamp!(1_546_398_245.999_999), 999_999)]
fn microsecond(#[case] ts: Timestamp, #[case] expected: u32) {
assert_eq!(ts.microsecond(), expected);
}
#[rstest]
#[case(timestamp!(0), 0)]
#[case(timestamp!(1_546_398_245.006_007_008), 6_007_008)]
#[case(timestamp!(1_546_398_245.999_999_999), 999_999_999)]
fn nanosecond(#[case] ts: Timestamp, #[case] expected: u32) {
assert_eq!(ts.nanosecond(), expected);
}
#[rstest]
#[case(timestamp!(1_546_398_245), time!(12:34:56.789), timestamp!(1_546_432_496.789))]
#[case(timestamp!(1_546_398_245), time!(23:59:59), timestamp!(1_546_473_599))]
#[case(timestamp!(-1), time!(0:00), timestamp!(-86_400))]
#[case(timestamp!(-86_401), time!(0:00), timestamp!(-172_800))]
fn replace_time(#[case] ts: Timestamp, #[case] replacement: Time, #[case] expected: Timestamp) {
assert_eq!(ts.replace_time(replacement), expected);
}
#[rstest]
#[case(timestamp!(1_546_398_245), date!(2020-01-02), timestamp!(1_577_934_245))]
fn replace_date(#[case] ts: Timestamp, #[case] replacement: Date, #[case] expected: Timestamp) {
assert_eq!(ts.replace_date(replacement), expected);
}
#[rstest]
#[case(timestamp!(1_546_398_245), 2020, timestamp!(1_577_934_245))]
#[case(timestamp!(1_546_398_245), -1_000_000, None)]
#[case(timestamp!(1_546_398_245), 1_000_000, None)]
fn replace_year(
#[case] ts: Timestamp,
#[case] year: i32,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_year(year).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), Month::February, timestamp!(1_549_076_645))]
#[case(timestamp!(1_548_817_445), Month::February, None)]
fn replace_month(
#[case] ts: Timestamp,
#[case] month: Month,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_month(month).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 1, timestamp!(1_546_311_845))]
#[case(timestamp!(1_546_398_245), 0, None)]
#[case(timestamp!(1_546_398_245), 32, None)]
fn replace_day(
#[case] ts: Timestamp,
#[case] day: u8,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_day(day).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 1, timestamp!(1_546_311_845))]
#[case(timestamp!(1_546_398_245), 0, None)]
#[case(timestamp!(1_546_398_245), 366, None)]
fn replace_ordinal(
#[case] ts: Timestamp,
#[case] ordinal: u16,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_ordinal(ordinal).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 0, timestamp!(1_546_387_445))]
#[case(timestamp!(1_546_398_245), 24, None)]
#[case(timestamp!(-1), 0, timestamp!(-82_801))]
#[case(timestamp!(-86_401), 0, timestamp!(-169_201))]
fn replace_hour(
#[case] ts: Timestamp,
#[case] hour: u8,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_hour(hour).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 0, timestamp!(1_546_398_005))]
#[case(timestamp!(1_546_398_245), 60, None)]
#[case(timestamp!(-1), 0, timestamp!(-3_541))]
#[case(timestamp!(-86_401), 0, timestamp!(-89_941))]
fn replace_minute(
#[case] ts: Timestamp,
#[case] minute: u8,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_minute(minute).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 0, timestamp!(1_546_398_240))]
#[case(timestamp!(1_546_398_245), 60, None)]
#[case(timestamp!(-1), 0, timestamp!(-60))]
#[case(timestamp!(-86_401), 0, timestamp!(-86_460))]
fn replace_second(
#[case] ts: Timestamp,
#[case] second: u8,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_second(second).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245.006), 7, timestamp!(1_546_398_245.007))]
#[case(timestamp!(1_546_398_245.006), 1_000, None)]
#[case(timestamp!(-1), 400, timestamp!(-0.6))]
#[case(timestamp!(-1.5), 400, timestamp!(-1.4))]
#[case(timestamp!(-1.5), 0, timestamp!(-1))]
#[case(timestamp!(-1), 0, timestamp!(-1))]
#[case(Timestamp::MIN, 0, Timestamp::MIN)]
#[case(
Timestamp::MIN,
1,
Timestamp::new(Timestamp::MIN.as_seconds(), 1_000_000).ok()
)]
fn replace_millisecond(
#[case] ts: Timestamp,
#[case] millisecond: u16,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_millisecond(millisecond).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245.006_007), 123_456, timestamp!(1_546_398_245.123_456))]
#[case(timestamp!(1_546_398_245.006_007), 1_000_000, None)]
#[case(
timestamp!(-1_546_398_245.006_007),
123_456,
timestamp!(-1_546_398_245.123_456)
)]
#[case(timestamp!(-1_546_398_245.006_007), 0, timestamp!(-1_546_398_245))]
#[case(Timestamp::MIN, 0, Timestamp::MIN)]
#[case(
Timestamp::MIN,
1,
Timestamp::new(Timestamp::MIN.as_seconds(), 1_000).ok()
)]
fn replace_microsecond(
#[case] ts: Timestamp,
#[case] microsecond: u32,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_microsecond(microsecond).ok(), expected.into());
}
#[rstest]
#[case(
timestamp!(1_546_398_245.006_007_008),
123_456_789,
timestamp!(1_546_398_245.123_456_789),
)]
#[case(
timestamp!(-1_546_398_245.006_007_008),
123_456_789,
timestamp!(-1_546_398_245.123_456_789),
)]
#[case(timestamp!(1_546_398_245.006_007_008), 1_000_000_000, None)]
#[case(Timestamp::MIN, 0, Timestamp::MIN)]
#[case(
Timestamp::MIN,
1,
Timestamp::new(Timestamp::MIN.as_seconds(), 1).ok()
)]
fn replace_nanosecond(
#[case] ts: Timestamp,
#[case] nanosecond: u32,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.replace_nanosecond(nanosecond).ok(), expected.into());
}
#[rstest]
#[case(timestamp!(0), 5.days(), timestamp!(432_000))]
#[case(timestamp!(0), 1.days(), timestamp!(86_400))]
#[case(timestamp!(1_546_398_245), 1.days(), timestamp!(1_546_484_645))]
#[case(timestamp!(1_546_398_245), (-1).days(), timestamp!(1_546_311_845))]
fn add_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts + duration, expected);
}
#[rstest]
#[case(timestamp!(0), 5.std_days(), timestamp!(432_000))]
#[case(timestamp!(0), 1.std_days(), timestamp!(86_400))]
#[case(timestamp!(1_546_398_245), 1.std_days(), timestamp!(1_546_484_645))]
fn add_std_duration(
#[case] ts: Timestamp,
#[case] duration: StdDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts + duration, expected);
}
#[rstest]
#[case(Timestamp::MAX, 1.nanoseconds())]
#[should_panic(expected = "resulting value is out of range")]
fn add_duration_panics(#[case] ts: Timestamp, #[case] duration: SignedDuration) {
let _ = ts + duration;
}
#[rstest]
#[case(Timestamp::MAX, 1.std_nanoseconds())]
#[case(Timestamp::MIN, StdDuration::new(u64::MAX, 0))]
#[case(Timestamp::UNIX_EPOCH, StdDuration::new(u64::MAX, 0))]
#[should_panic(expected = "resulting value is out of range")]
fn add_std_panics(#[case] ts: Timestamp, #[case] duration: StdDuration) {
let _ = ts + duration;
}
#[rstest]
#[case(timestamp!(432_000), 5.days(), timestamp!(0))]
#[case(timestamp!(86_400), 1.days(), timestamp!(0))]
#[case(timestamp!(1_546_398_245), 1.days(), timestamp!(1_546_311_845))]
#[case(timestamp!(1_546_398_245), (-1).days(), timestamp!(1_546_484_645))]
fn sub_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts - duration, expected);
}
#[rstest]
#[case(timestamp!(432_000), 5.std_days(), timestamp!(0))]
#[case(timestamp!(86_400), 1.std_days(), timestamp!(0))]
#[case(timestamp!(1_546_484_645), 1.std_days(), timestamp!(1_546_398_245))]
fn sub_std_duration(
#[case] ts: Timestamp,
#[case] duration: StdDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts - duration, expected);
}
#[rstest]
#[case(Timestamp::MIN, 1.nanoseconds())]
#[should_panic(expected = "resulting value is out of range")]
fn sub_duration_panics(#[case] ts: Timestamp, #[case] duration: SignedDuration) {
let _ = ts - duration;
}
#[rstest]
#[case(Timestamp::MIN, 1.std_nanoseconds())]
#[case(Timestamp::MAX, StdDuration::new(u64::MAX, 0))]
#[case(Timestamp::UNIX_EPOCH, StdDuration::new(u64::MAX, 0))]
#[should_panic(expected = "resulting value is out of range")]
fn sub_std_panics(#[case] ts: Timestamp, #[case] duration: StdDuration) {
let _ = ts - duration;
}
#[rstest]
#[case(timestamp!(0), 5.days(), timestamp!(432_000))]
#[case(timestamp!(0), 1.days(), timestamp!(86_400))]
#[case(timestamp!(1_546_398_245), 1.days(), timestamp!(1_546_484_645))]
#[case(timestamp!(1_546_398_245), (-1).days(), timestamp!(1_546_311_845))]
fn add_assign_duration(
#[case] mut ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
ts += duration;
assert_eq!(ts, expected);
}
#[rstest]
#[case(timestamp!(0), 5.std_days(), timestamp!(432_000))]
#[case(timestamp!(0), 1.std_days(), timestamp!(86_400))]
#[case(timestamp!(1_546_398_245), 1.std_days(), timestamp!(1_546_484_645))]
fn add_assign_std_duration(
#[case] mut ts: Timestamp,
#[case] duration: StdDuration,
#[case] expected: Timestamp,
) {
ts += duration;
assert_eq!(ts, expected);
}
#[rstest]
#[case(timestamp!(432_000), 5.days(), timestamp!(0))]
#[case(timestamp!(86_400), 1.days(), timestamp!(0))]
#[case(timestamp!(1_546_398_245), 1.days(), timestamp!(1_546_311_845))]
#[case(timestamp!(1_546_398_245), (-1).days(), timestamp!(1_546_484_645))]
fn sub_assign_duration(
#[case] mut ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
ts -= duration;
assert_eq!(ts, expected);
}
#[rstest]
#[case(timestamp!(432_000), 5.std_days(), timestamp!(0))]
#[case(timestamp!(86_400), 1.std_days(), timestamp!(0))]
#[case(timestamp!(1_546_484_645), 1.std_days(), timestamp!(1_546_398_245))]
fn sub_assign_std_duration(
#[case] mut ts: Timestamp,
#[case] duration: StdDuration,
#[case] expected: Timestamp,
) {
ts -= duration;
assert_eq!(ts, expected);
}
#[rstest]
#[case(timestamp!(0), timestamp!(0), SignedDuration::ZERO)]
#[case(timestamp!(0), timestamp!(0.000_000_001), (-1).nanoseconds())]
#[case(timestamp!(0.000_000_001), timestamp!(0), 1.nanoseconds())]
#[case(timestamp!(-1), timestamp!(-2), 1.seconds())]
#[case(timestamp!(1_546_484_645), timestamp!(1_546_398_245), 1.days())]
#[case(timestamp!(1_546_398_245), timestamp!(1_546_484_645), (-1).days())]
fn sub_timestamp(#[case] lhs: Timestamp, #[case] rhs: Timestamp, #[case] expected: SignedDuration) {
assert_eq!(lhs - rhs, expected);
}
#[rstest]
#[case(timestamp!(1_546_398_245), 5.nanoseconds(), timestamp!(1_546_398_245.000_000_005))]
#[case(timestamp!(1_546_398_245), 4.seconds(), timestamp!(1_546_398_249))]
#[case(timestamp!(1_546_398_245), 2.days(), timestamp!(1_546_571_045))]
#[case(timestamp!(1_546_398_245), 1.weeks(), timestamp!(1_547_003_045))]
#[case(timestamp!(1_546_398_245), (-5).nanoseconds(), timestamp!(1_546_398_244.999_999_995))]
#[case(timestamp!(1_546_398_245), (-4).seconds(), timestamp!(1_546_398_241))]
#[case(timestamp!(1_546_398_245), (-2).days(), timestamp!(1_546_225_445))]
#[case(timestamp!(1_546_398_245), (-1).weeks(), timestamp!(1_545_793_445))]
#[case(Timestamp::MIN, (-1).nanoseconds(), None)]
#[case(Timestamp::MAX, 1.nanoseconds(), None)]
#[case(Timestamp::MIN, SignedDuration::MIN, None)]
#[case(Timestamp::MAX, SignedDuration::MAX, None)]
#[case(timestamp!(0.999_999_999), i64::MAX.seconds() + 1.nanoseconds(), None)]
#[case(timestamp!(0), SignedDuration::MIN, None)]
fn checked_add_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.checked_add(duration), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245.000_000_005), 5.nanoseconds(), timestamp!(1_546_398_245))]
#[case(timestamp!(1_546_398_249), 4.seconds(), timestamp!(1_546_398_245))]
#[case(timestamp!(1_546_571_045), 2.days(), timestamp!(1_546_398_245))]
#[case(timestamp!(1_547_003_045), 1.weeks(), timestamp!(1_546_398_245))]
#[case(timestamp!(1_546_398_245), (-1).days(), timestamp!(1_546_484_645))]
#[case(timestamp!(1_546_398_245), (-1).weeks(), timestamp!(1_547_003_045))]
#[case(Timestamp::MIN, 1.nanoseconds(), None)]
#[case(Timestamp::MAX, (-1).nanoseconds(), None)]
#[case(Timestamp::MIN, SignedDuration::MIN, None)]
#[case(Timestamp::MIN, SignedDuration::MAX, None)]
#[case(timestamp!(-1), i64::MAX.seconds() + 1.nanoseconds(), None)]
#[case(timestamp!(0.999_999_999), (-i64::MAX).seconds() - 1.nanoseconds(), None)]
fn checked_sub_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: impl Into<Option<Timestamp>>,
) {
assert_eq!(ts.checked_sub(duration), expected.into());
}
#[rstest]
#[case(timestamp!(1_546_398_245), 2.days(), timestamp!(1_546_571_045))]
#[case(timestamp!(1_546_398_245), (-2).days(), timestamp!(1_546_225_445))]
#[case(Timestamp::MIN, (-10).days(), Timestamp::MIN)]
#[case(Timestamp::MAX, 10.days(), Timestamp::MAX)]
#[case(Timestamp::MIN, (-1).nanoseconds(), Timestamp::MIN)]
#[case(Timestamp::MAX, 1.nanoseconds(), Timestamp::MAX)]
fn saturating_add_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts.saturating_add(duration), expected);
}
#[rstest]
#[case(timestamp!(1_546_571_045), 2.days(), timestamp!(1_546_398_245))]
#[case(timestamp!(1_546_571_045), (-2).days(), timestamp!(1_546_743_845))]
#[case(Timestamp::MIN, 10.days(), Timestamp::MIN)]
#[case(Timestamp::MAX, (-10).days(), Timestamp::MAX)]
#[case(Timestamp::MIN, 1.nanoseconds(), Timestamp::MIN)]
#[case(Timestamp::MAX, (-1).nanoseconds(), Timestamp::MAX)]
fn saturating_sub_duration(
#[case] ts: Timestamp,
#[case] duration: SignedDuration,
#[case] expected: Timestamp,
) {
assert_eq!(ts.saturating_sub(duration), expected);
}
#[rstest]
#[case(Timestamp::MAX, 1.nanoseconds())]
#[should_panic]
fn add_panics(#[case] ts: Timestamp, #[case] duration: SignedDuration) {
let _ = ts + duration;
}
#[rstest]
#[case(Timestamp::MIN, 1.nanoseconds())]
#[should_panic]
fn sub_panics(#[case] ts: Timestamp, #[case] duration: SignedDuration) {
let _ = ts - duration;
}
#[rstest]
#[case(timestamp!(0), timestamp!(0), Ordering::Equal)]
#[case(timestamp!(0), timestamp!(1), Ordering::Less)]
#[case(timestamp!(0), timestamp!(-1), Ordering::Greater)]
#[case(timestamp!(1_546_398_245), timestamp!(1_546_398_245), Ordering::Equal)]
#[case(timestamp!(1_546_398_245), timestamp!(1_546_398_246), Ordering::Less)]
#[case(timestamp!(1_546_398_246), timestamp!(1_546_398_245), Ordering::Greater)]
#[case(timestamp!(-1), timestamp!(0), Ordering::Less)]
#[case(timestamp!(1), timestamp!(0), Ordering::Greater)]
#[case(timestamp!(0), timestamp!(0.000_000_001), Ordering::Less)]
#[case(timestamp!(0.000_000_001), timestamp!(0), Ordering::Greater)]
fn ord(#[case] lhs: Timestamp, #[case] rhs: Timestamp, #[case] expected: Ordering) {
assert_eq!(lhs.cmp(&rhs), expected);
}
#[rstest]
#[case(timestamp!(0), timestamp!(0), true)]
#[case(timestamp!(0), timestamp!(1), false)]
#[case(timestamp!(0), timestamp!(-1), false)]
#[case(timestamp!(1_546_398_245.006_007_008), timestamp!(1_546_398_245.006_007_008), true)]
#[case(timestamp!(1_546_398_245.006_007_008), timestamp!(1_546_398_245.006_007_009), false)]
fn eq(#[case] lhs: Timestamp, #[case] rhs: Timestamp, #[case] expected: bool) {
assert_eq!(lhs == rhs, expected);
}
#[rstest]
#[case(timestamp!(0), timestamp!(0))]
#[case(timestamp!(1_546_398_245), timestamp!(1_546_398_245))]
fn hash(#[case] a: Timestamp, #[case] b: Timestamp) {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher_a = DefaultHasher::new();
let mut hasher_b = DefaultHasher::new();
a.hash(&mut hasher_a);
b.hash(&mut hasher_b);
assert_eq!(hasher_a.finish(), hasher_b.finish());
}
#[rstest]
#[case(timestamp!(0), "0")]
#[case(timestamp!(1), "1")]
#[case(timestamp!(-1), "-1")]
#[case(timestamp!(1_234_567_890.001), "1234567890.001")]
#[case(timestamp!(1_234_567_890.123_456_789), "1234567890.123456789")]
#[case(timestamp!(1_234_567_890.123_456), "1234567890.123456")]
#[case(timestamp!(1_234_567_890.123), "1234567890.123")]
#[case(timestamp!(-0.5), "-0.5")]
#[case(timestamp!(-1) + 400_000_000.nanoseconds(), "-0.6")]
#[case(timestamp!(-123_456_789) + 123_456_789.nanoseconds(), "-123456788.876543211")]
fn display(#[case] ts: Timestamp, #[case] expected: &str) {
assert_eq!(ts.to_string(), expected);
}
#[rstest]
#[case(timestamp!(0), "0")]
#[case(timestamp!(1_546_398_245), "1546398245")]
fn debug(#[case] ts: Timestamp, #[case] expected: &str) {
assert_eq!(format!("{ts:?}"), expected);
}
#[rstest]
fn now() {
assert!(Timestamp::now().year() >= 2019);
}
#[rstest]
#[case(timestamp!(0), 2_440_588)]
#[case(timestamp!(1_546_398_245), 2_458_486)]
fn to_julian_day(#[case] ts: Timestamp, #[case] expected: i32) {
assert_eq!(ts.to_julian_day(), expected);
}
#[rstest]
fn unix_timestamp_format() {
let format = format_description!("[unix_timestamp]");
assert_eq!(
Timestamp::parse("1546398245", &format),
Ok(timestamp!(1_546_398_245)),
);
assert!(matches!(
timestamp!(1_546_398_245).format(&format).as_deref(),
Ok("1546398245")
));
}
#[rstest]
#[case(utc_datetime!(1970-01-01 0:00), timestamp!(0))]
#[case(utc_datetime!(2019-01-02 3:04:05), timestamp!(1_546_398_245))]
fn from_utc_datetime_into_timestamp(#[case] source: UtcDateTime, #[case] expected: Timestamp) {
assert_eq!(Timestamp::from(source), expected);
}
#[rstest]
#[case(timestamp!(0), utc_datetime!(1970-01-01 0:00))]
#[case(timestamp!(1_546_398_245), utc_datetime!(2019-01-02 3:04:05))]
fn from_timestamp_into_utc_datetime(#[case] source: Timestamp, #[case] expected: UtcDateTime) {
assert_eq!(UtcDateTime::from(source), expected);
}
#[rstest]
fn utc_datetime_sub_timestamp() {
assert_eq!(
utc_datetime!(2019-01-02 3:04:05) - timestamp!(1_546_398_245),
SignedDuration::ZERO,
);
}
#[rstest]
fn timestamp_utc_datetime_partial_eq() {
assert_eq!(timestamp!(0), utc_datetime!(1970-01-01 0:00));
assert_eq!(utc_datetime!(1970-01-01 0:00), timestamp!(0));
assert_ne!(timestamp!(1), utc_datetime!(1970-01-01 0:00));
}
#[rstest]
fn timestamp_utc_datetime_partial_ord() {
assert!(timestamp!(0) < utc_datetime!(1970-01-02 0:00));
assert!(utc_datetime!(1970-01-02 0:00) > timestamp!(0));
}
#[rstest]
#[case(timestamp!(0))]
#[case(timestamp!(1_546_398_245))]
fn from_offset_date_time(#[case] expected: Timestamp) {
assert!(
OffsetDateTime::from_unix_timestamp(expected.as_seconds())
.is_ok_and(|odt| Timestamp::from(odt) == expected)
);
}
#[rstest]
#[case(timestamp!(0))]
#[case(timestamp!(1_546_398_245))]
fn into_offset_date_time(#[case] source: Timestamp) {
let odt = OffsetDateTime::from(source);
assert_eq!(odt.unix_timestamp(), source.as_seconds());
assert_eq!(odt.nanosecond(), source.nanosecond());
}
#[rstest]
#[case(timestamp!(1) - utc_datetime!(1970-01-01 0:00), 1.seconds())]
#[case(timestamp!(0) - utc_datetime!(1970-01-01 0:00), SignedDuration::ZERO)]
fn sub_utc_date_time_implicit_utc(
#[case] result: SignedDuration,
#[case] expected: SignedDuration,
) {
assert_eq!(result, expected);
}
#[rstest]
fn sub_offset_date_time() {
assert_eq!(
OffsetDateTime::from_unix_timestamp(1_546_398_245)
.map(|odt| odt - timestamp!(1_546_311_845)),
Ok(86_400.seconds()),
);
}
#[rstest]
fn eq_offset_date_time() {
assert!(OffsetDateTime::from_unix_timestamp(0).is_ok_and(|odt| timestamp!(0) == odt));
assert!(OffsetDateTime::from_unix_timestamp(0).is_ok_and(|odt| odt == timestamp!(0)));
assert!(OffsetDateTime::from_unix_timestamp(0).is_ok_and(|odt| timestamp!(1) != odt));
}
#[rstest]
fn ord_offset_date_time() {
assert!(
OffsetDateTime::from_unix_timestamp(0).is_ok_and(|earlier| timestamp!(86_400) > earlier)
);
assert!(OffsetDateTime::from_unix_timestamp(86_400).is_ok_and(|later| timestamp!(0) < later));
}
#[rstest]
fn from_system_time_into_timestamp() {
assert_eq!(Timestamp::from(SystemTime::UNIX_EPOCH), timestamp!(0));
assert_eq!(
Ok(Timestamp::from(
SystemTime::UNIX_EPOCH - 5.seconds() - 1.milliseconds()
)),
Timestamp::from_milliseconds(-5_001),
);
assert_eq!(
Ok(Timestamp::from(
SystemTime::UNIX_EPOCH + 5.seconds() + 1.milliseconds()
)),
Timestamp::from_milliseconds(5_001),
);
}
#[rstest]
fn from_timestamp_into_system_time() {
let st = SystemTime::from(timestamp!(0));
assert_eq!(st, SystemTime::UNIX_EPOCH);
}
#[rstest]
fn timestamp_sub_system_time() {
let duration = timestamp!(86_400) - SystemTime::UNIX_EPOCH;
assert_eq!(duration, 86_400.seconds());
let duration_before = timestamp!(0) - (SystemTime::UNIX_EPOCH - StdDuration::new(5, 0));
assert_eq!(duration_before, 5.seconds());
}
#[rstest]
fn system_time_sub_timestamp() {
let duration = SystemTime::UNIX_EPOCH - timestamp!(0);
assert_eq!(duration, SignedDuration::ZERO);
let duration_before = (SystemTime::UNIX_EPOCH - StdDuration::new(5, 0)) - timestamp!(0);
assert_eq!(duration_before, (-5).seconds());
}
#[rstest]
fn timestamp_system_time_partial_eq() {
assert_eq!(timestamp!(0), SystemTime::UNIX_EPOCH);
assert_eq!(SystemTime::UNIX_EPOCH, timestamp!(0));
assert_ne!(timestamp!(1), SystemTime::UNIX_EPOCH);
}
#[rstest]
fn timestamp_system_time_partial_ord() {
assert!(timestamp!(0) < SystemTime::UNIX_EPOCH + 1.seconds());
assert!(SystemTime::UNIX_EPOCH + 1.seconds() > timestamp!(0));
}
#[rstest]
fn format_into_timestamp() {
let mut output = Vec::new();
let format = format_description!("[unix_timestamp]");
match timestamp!(1_546_398_245).format_into(&mut output, &format) {
Ok(count) => assert_eq!(count, 10),
Err(_) => unreachable!("format should succeed"),
}
assert_eq!(output, b"1546398245");
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,192 @@
use std::cmp::Ordering;
use rstest::rstest;
use time::macros::offset;
use time::{OffsetDateTime, UtcOffset};
#[rstest]
fn utc_is_zero() {
assert_eq!(offset!(UTC), offset!(+0));
}
#[rstest]
#[case(0, 0, 0, offset!(UTC))]
#[case(0, 0, 1, offset!(+0:00:01))]
#[case(0, 0, -1, offset!(-0:00:01))]
#[case(1, 0, 0, offset!(+1))]
#[case(-1, 0, 0, offset!(-1))]
#[case(23, 59, 0, offset!(+23:59))]
#[case(-23, -59, 0, offset!(-23:59))]
#[case(23, 59, 59, offset!(+23:59:59))]
#[case(-23, -59, -59, offset!(-23:59:59))]
#[case(1, 2, 3, offset!(+1:02:03))]
#[case(1, -2, -3, offset!(+1:02:03))]
#[case(0, 2, -3, offset!(+0:02:03))]
fn from_hms(
#[case] hours: i8,
#[case] minutes: i8,
#[case] seconds: i8,
#[case] expected: UtcOffset,
) {
assert_eq!(UtcOffset::from_hms(hours, minutes, seconds), Ok(expected));
}
#[rstest]
#[case(0, offset!(UTC))]
#[case(1, offset!(+0:00:01))]
#[case(-1, offset!(-0:00:01))]
#[case(3_600, offset!(+1))]
#[case(-3_600, offset!(-1))]
#[case(86_340, offset!(+23:59))]
#[case(-86_340, offset!(-23:59))]
#[case(86_399, offset!(+23:59:59))]
#[case(-86_399, offset!(-23:59:59))]
fn from_whole_seconds(#[case] seconds: i32, #[case] expected: UtcOffset) {
assert_eq!(UtcOffset::from_whole_seconds(seconds), Ok(expected));
}
#[rstest]
#[case(offset!(UTC), (0, 0, 0))]
#[case(offset!(+0:00:01), (0, 0, 1))]
#[case(offset!(-0:00:01), (0, 0, -1))]
#[case(offset!(+1), (1, 0, 0))]
#[case(offset!(-1), (-1, 0, 0))]
#[case(offset!(+23:59), (23, 59, 0))]
#[case(offset!(-23:59), (-23, -59, 0))]
#[case(offset!(+23:59:59), (23, 59, 59))]
#[case(offset!(-23:59:59), (-23, -59, -59))]
fn as_hms(#[case] offset: UtcOffset, #[case] expected: (i8, i8, i8)) {
assert_eq!(offset.as_hms(), expected);
}
#[rstest]
#[case(offset!(+1:02:03), 1)]
#[case(offset!(-1:02:03), -1)]
fn whole_hours(#[case] offset: UtcOffset, #[case] expected: i8) {
assert_eq!(offset.whole_hours(), expected);
}
#[rstest]
#[case(offset!(+1:02:03), 62)]
#[case(offset!(-1:02:03), -62)]
fn whole_minutes(#[case] offset: UtcOffset, #[case] expected: i16) {
assert_eq!(offset.whole_minutes(), expected);
}
#[rstest]
#[case(offset!(+1:02:03), 2)]
#[case(offset!(-1:02:03), -2)]
fn minutes_past_hour(#[case] offset: UtcOffset, #[case] expected: i8) {
assert_eq!(offset.minutes_past_hour(), expected);
}
#[rstest]
#[case(offset!(UTC), 0)]
#[case(offset!(+0:00:01), 1)]
#[case(offset!(-0:00:01), -1)]
#[case(offset!(+1), 3_600)]
#[case(offset!(-1), -3_600)]
#[case(offset!(+23:59), 86_340)]
#[case(offset!(-23:59), -86_340)]
#[case(offset!(+23:59:59), 86_399)]
#[case(offset!(-23:59:59), -86_399)]
fn whole_seconds(#[case] offset: UtcOffset, #[case] expected: i32) {
assert_eq!(offset.whole_seconds(), expected);
}
#[rstest]
#[case(offset!(+1:02:03), 3)]
#[case(offset!(-1:02:03), -3)]
fn seconds_past_minute(#[case] offset: UtcOffset, #[case] expected: i8) {
assert_eq!(offset.seconds_past_minute(), expected);
}
#[rstest]
#[case(offset!(UTC), true)]
#[case(offset!(+0:00:01), false)]
#[case(offset!(-0:00:01), false)]
#[case(offset!(+1), false)]
#[case(offset!(-1), false)]
#[case(offset!(+23:59), false)]
#[case(offset!(-23:59), false)]
#[case(offset!(+23:59:59), false)]
#[case(offset!(-23:59:59), false)]
fn is_utc(#[case] offset: UtcOffset, #[case] expected: bool) {
assert_eq!(offset.is_utc(), expected);
}
#[rstest]
#[case(offset!(UTC), false)]
#[case(offset!(+0:00:01), true)]
#[case(offset!(-0:00:01), false)]
#[case(offset!(+1), true)]
#[case(offset!(-1), false)]
#[case(offset!(+23:59), true)]
#[case(offset!(-23:59), false)]
#[case(offset!(+23:59:59), true)]
#[case(offset!(-23:59:59), false)]
fn is_positive(#[case] offset: UtcOffset, #[case] expected: bool) {
assert_eq!(offset.is_positive(), expected);
}
#[rstest]
#[case(offset!(UTC), false)]
#[case(offset!(+0:00:01), false)]
#[case(offset!(-0:00:01), true)]
#[case(offset!(+1), false)]
#[case(offset!(-1), true)]
#[case(offset!(+23:59), false)]
#[case(offset!(-23:59), true)]
#[case(offset!(+23:59:59), false)]
#[case(offset!(-23:59:59), true)]
fn is_negative(#[case] offset: UtcOffset, #[case] expected: bool) {
assert_eq!(offset.is_negative(), expected);
}
#[rstest]
#[case(offset!(UTC), offset!(UTC), Ordering::Equal)]
#[case(offset!(+1), offset!(+1), Ordering::Equal)]
#[case(offset!(-1), offset!(-1), Ordering::Equal)]
#[case(offset!(+1), offset!(UTC), Ordering::Greater)]
#[case(offset!(UTC), offset!(-1), Ordering::Greater)]
#[case(offset!(-1), offset!(+1), Ordering::Less)]
#[case(offset!(+23:59), offset!(+23:58), Ordering::Greater)]
#[case(offset!(-23:59), offset!(-23:58), Ordering::Less)]
#[case(offset!(+23:59:59), offset!(+23:59:58), Ordering::Greater)]
#[case(offset!(-23:59:59), offset!(-23:59:58), Ordering::Less)]
fn ordering(#[case] a: UtcOffset, #[case] b: UtcOffset, #[case] expected: Ordering) {
assert_eq!(a.cmp(&b), expected);
}
#[rstest]
#[case(offset!(UTC), offset!(UTC))]
#[case(offset!(+0:00:01), offset!(-0:00:01))]
#[case(offset!(-0:00:01), offset!(+0:00:01))]
#[case(offset!(+1), offset!(-1))]
#[case(offset!(-1), offset!(+1))]
#[case(offset!(+23:59), offset!(-23:59))]
#[case(offset!(-23:59), offset!(+23:59))]
#[case(offset!(+23:59:59), offset!(-23:59:59))]
#[case(offset!(-23:59:59), offset!(+23:59:59))]
fn neg(#[case] offset: UtcOffset, #[case] expected: UtcOffset) {
assert_eq!(-offset, expected);
}
#[rstest]
fn local_offset_at() {
assert!(UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH).is_ok());
}
#[rstest]
fn current_local_offset() {
assert!(UtcOffset::current_local_offset().is_ok());
}
#[rstest]
fn local_offset_success_when_multithreaded() {
std::thread::spawn(|| {
assert!(UtcOffset::current_local_offset().is_ok());
})
.join()
.expect("failed to join thread");
}
+870
View File
@@ -0,0 +1,870 @@
use rstest::rstest;
use time::Month::*;
use time::{Month, util};
#[rstest]
#[case(2019, January, 31)]
#[case(2019, February, 28)]
#[case(2019, March, 31)]
#[case(2019, April, 30)]
#[case(2019, May, 31)]
#[case(2019, June, 30)]
#[case(2019, July, 31)]
#[case(2019, August, 31)]
#[case(2019, September, 30)]
#[case(2019, October, 31)]
#[case(2019, November, 30)]
#[case(2019, December, 31)]
#[case(2020, January, 31)]
#[case(2020, February, 29)]
#[case(2020, March, 31)]
#[case(2020, April, 30)]
#[case(2020, May, 31)]
#[case(2020, June, 30)]
#[case(2020, July, 31)]
#[case(2020, August, 31)]
#[case(2020, September, 30)]
#[case(2020, October, 31)]
#[case(2020, November, 30)]
#[case(2020, December, 31)]
fn days_in_year_month(#[case] year: i32, #[case] month: Month, #[case] expected: u8) {
#[expect(deprecated)]
{
assert_eq!(util::days_in_year_month(year, month), expected);
}
}
#[rstest]
#[case(0, true)]
#[case(1, false)]
#[case(2, false)]
#[case(3, false)]
#[case(4, true)]
#[case(5, false)]
#[case(6, false)]
#[case(7, false)]
#[case(8, true)]
#[case(9, false)]
#[case(10, false)]
#[case(11, false)]
#[case(12, true)]
#[case(13, false)]
#[case(14, false)]
#[case(15, false)]
#[case(16, true)]
#[case(17, false)]
#[case(18, false)]
#[case(19, false)]
#[case(20, true)]
#[case(21, false)]
#[case(22, false)]
#[case(23, false)]
#[case(24, true)]
#[case(25, false)]
#[case(26, false)]
#[case(27, false)]
#[case(28, true)]
#[case(29, false)]
#[case(30, false)]
#[case(31, false)]
#[case(32, true)]
#[case(33, false)]
#[case(34, false)]
#[case(35, false)]
#[case(36, true)]
#[case(37, false)]
#[case(38, false)]
#[case(39, false)]
#[case(40, true)]
#[case(41, false)]
#[case(42, false)]
#[case(43, false)]
#[case(44, true)]
#[case(45, false)]
#[case(46, false)]
#[case(47, false)]
#[case(48, true)]
#[case(49, false)]
#[case(50, false)]
#[case(51, false)]
#[case(52, true)]
#[case(53, false)]
#[case(54, false)]
#[case(55, false)]
#[case(56, true)]
#[case(57, false)]
#[case(58, false)]
#[case(59, false)]
#[case(60, true)]
#[case(61, false)]
#[case(62, false)]
#[case(63, false)]
#[case(64, true)]
#[case(65, false)]
#[case(66, false)]
#[case(67, false)]
#[case(68, true)]
#[case(69, false)]
#[case(70, false)]
#[case(71, false)]
#[case(72, true)]
#[case(73, false)]
#[case(74, false)]
#[case(75, false)]
#[case(76, true)]
#[case(77, false)]
#[case(78, false)]
#[case(79, false)]
#[case(80, true)]
#[case(81, false)]
#[case(82, false)]
#[case(83, false)]
#[case(84, true)]
#[case(85, false)]
#[case(86, false)]
#[case(87, false)]
#[case(88, true)]
#[case(89, false)]
#[case(90, false)]
#[case(91, false)]
#[case(92, true)]
#[case(93, false)]
#[case(94, false)]
#[case(95, false)]
#[case(96, true)]
#[case(97, false)]
#[case(98, false)]
#[case(99, false)]
#[case(100, false)]
#[case(101, false)]
#[case(102, false)]
#[case(103, false)]
#[case(104, true)]
#[case(105, false)]
#[case(106, false)]
#[case(107, false)]
#[case(108, true)]
#[case(109, false)]
#[case(110, false)]
#[case(111, false)]
#[case(112, true)]
#[case(113, false)]
#[case(114, false)]
#[case(115, false)]
#[case(116, true)]
#[case(117, false)]
#[case(118, false)]
#[case(119, false)]
#[case(120, true)]
#[case(121, false)]
#[case(122, false)]
#[case(123, false)]
#[case(124, true)]
#[case(125, false)]
#[case(126, false)]
#[case(127, false)]
#[case(128, true)]
#[case(129, false)]
#[case(130, false)]
#[case(131, false)]
#[case(132, true)]
#[case(133, false)]
#[case(134, false)]
#[case(135, false)]
#[case(136, true)]
#[case(137, false)]
#[case(138, false)]
#[case(139, false)]
#[case(140, true)]
#[case(141, false)]
#[case(142, false)]
#[case(143, false)]
#[case(144, true)]
#[case(145, false)]
#[case(146, false)]
#[case(147, false)]
#[case(148, true)]
#[case(149, false)]
#[case(150, false)]
#[case(151, false)]
#[case(152, true)]
#[case(153, false)]
#[case(154, false)]
#[case(155, false)]
#[case(156, true)]
#[case(157, false)]
#[case(158, false)]
#[case(159, false)]
#[case(160, true)]
#[case(161, false)]
#[case(162, false)]
#[case(163, false)]
#[case(164, true)]
#[case(165, false)]
#[case(166, false)]
#[case(167, false)]
#[case(168, true)]
#[case(169, false)]
#[case(170, false)]
#[case(171, false)]
#[case(172, true)]
#[case(173, false)]
#[case(174, false)]
#[case(175, false)]
#[case(176, true)]
#[case(177, false)]
#[case(178, false)]
#[case(179, false)]
#[case(180, true)]
#[case(181, false)]
#[case(182, false)]
#[case(183, false)]
#[case(184, true)]
#[case(185, false)]
#[case(186, false)]
#[case(187, false)]
#[case(188, true)]
#[case(189, false)]
#[case(190, false)]
#[case(191, false)]
#[case(192, true)]
#[case(193, false)]
#[case(194, false)]
#[case(195, false)]
#[case(196, true)]
#[case(197, false)]
#[case(198, false)]
#[case(199, false)]
#[case(200, false)]
#[case(201, false)]
#[case(202, false)]
#[case(203, false)]
#[case(204, true)]
#[case(205, false)]
#[case(206, false)]
#[case(207, false)]
#[case(208, true)]
#[case(209, false)]
#[case(210, false)]
#[case(211, false)]
#[case(212, true)]
#[case(213, false)]
#[case(214, false)]
#[case(215, false)]
#[case(216, true)]
#[case(217, false)]
#[case(218, false)]
#[case(219, false)]
#[case(220, true)]
#[case(221, false)]
#[case(222, false)]
#[case(223, false)]
#[case(224, true)]
#[case(225, false)]
#[case(226, false)]
#[case(227, false)]
#[case(228, true)]
#[case(229, false)]
#[case(230, false)]
#[case(231, false)]
#[case(232, true)]
#[case(233, false)]
#[case(234, false)]
#[case(235, false)]
#[case(236, true)]
#[case(237, false)]
#[case(238, false)]
#[case(239, false)]
#[case(240, true)]
#[case(241, false)]
#[case(242, false)]
#[case(243, false)]
#[case(244, true)]
#[case(245, false)]
#[case(246, false)]
#[case(247, false)]
#[case(248, true)]
#[case(249, false)]
#[case(250, false)]
#[case(251, false)]
#[case(252, true)]
#[case(253, false)]
#[case(254, false)]
#[case(255, false)]
#[case(256, true)]
#[case(257, false)]
#[case(258, false)]
#[case(259, false)]
#[case(260, true)]
#[case(261, false)]
#[case(262, false)]
#[case(263, false)]
#[case(264, true)]
#[case(265, false)]
#[case(266, false)]
#[case(267, false)]
#[case(268, true)]
#[case(269, false)]
#[case(270, false)]
#[case(271, false)]
#[case(272, true)]
#[case(273, false)]
#[case(274, false)]
#[case(275, false)]
#[case(276, true)]
#[case(277, false)]
#[case(278, false)]
#[case(279, false)]
#[case(280, true)]
#[case(281, false)]
#[case(282, false)]
#[case(283, false)]
#[case(284, true)]
#[case(285, false)]
#[case(286, false)]
#[case(287, false)]
#[case(288, true)]
#[case(289, false)]
#[case(290, false)]
#[case(291, false)]
#[case(292, true)]
#[case(293, false)]
#[case(294, false)]
#[case(295, false)]
#[case(296, true)]
#[case(297, false)]
#[case(298, false)]
#[case(299, false)]
#[case(300, false)]
#[case(301, false)]
#[case(302, false)]
#[case(303, false)]
#[case(304, true)]
#[case(305, false)]
#[case(306, false)]
#[case(307, false)]
#[case(308, true)]
#[case(309, false)]
#[case(310, false)]
#[case(311, false)]
#[case(312, true)]
#[case(313, false)]
#[case(314, false)]
#[case(315, false)]
#[case(316, true)]
#[case(317, false)]
#[case(318, false)]
#[case(319, false)]
#[case(320, true)]
#[case(321, false)]
#[case(322, false)]
#[case(323, false)]
#[case(324, true)]
#[case(325, false)]
#[case(326, false)]
#[case(327, false)]
#[case(328, true)]
#[case(329, false)]
#[case(330, false)]
#[case(331, false)]
#[case(332, true)]
#[case(333, false)]
#[case(334, false)]
#[case(335, false)]
#[case(336, true)]
#[case(337, false)]
#[case(338, false)]
#[case(339, false)]
#[case(340, true)]
#[case(341, false)]
#[case(342, false)]
#[case(343, false)]
#[case(344, true)]
#[case(345, false)]
#[case(346, false)]
#[case(347, false)]
#[case(348, true)]
#[case(349, false)]
#[case(350, false)]
#[case(351, false)]
#[case(352, true)]
#[case(353, false)]
#[case(354, false)]
#[case(355, false)]
#[case(356, true)]
#[case(357, false)]
#[case(358, false)]
#[case(359, false)]
#[case(360, true)]
#[case(361, false)]
#[case(362, false)]
#[case(363, false)]
#[case(364, true)]
#[case(365, false)]
#[case(366, false)]
#[case(367, false)]
#[case(368, true)]
#[case(369, false)]
#[case(370, false)]
#[case(371, false)]
#[case(372, true)]
#[case(373, false)]
#[case(374, false)]
#[case(375, false)]
#[case(376, true)]
#[case(377, false)]
#[case(378, false)]
#[case(379, false)]
#[case(380, true)]
#[case(381, false)]
#[case(382, false)]
#[case(383, false)]
#[case(384, true)]
#[case(385, false)]
#[case(386, false)]
#[case(387, false)]
#[case(388, true)]
#[case(389, false)]
#[case(390, false)]
#[case(391, false)]
#[case(392, true)]
#[case(393, false)]
#[case(394, false)]
#[case(395, false)]
#[case(396, true)]
#[case(397, false)]
#[case(398, false)]
#[case(399, false)]
fn is_leap_year(#[case] year: i32, #[case] expected: bool) {
assert_eq!(util::is_leap_year(year), expected, "year {year} failed");
}
#[rstest]
#[case(1900, 365)]
#[case(2000, 366)]
#[case(2004, 366)]
#[case(2005, 365)]
#[case(2100, 365)]
fn days_in_year(#[case] year: i32, #[case] expected: u16) {
assert_eq!(util::days_in_year(year), expected);
}
#[rstest]
#[case(0, 52)]
#[case(1, 52)]
#[case(2, 52)]
#[case(3, 52)]
#[case(4, 53)]
#[case(5, 52)]
#[case(6, 52)]
#[case(7, 52)]
#[case(8, 52)]
#[case(9, 53)]
#[case(10, 52)]
#[case(11, 52)]
#[case(12, 52)]
#[case(13, 52)]
#[case(14, 52)]
#[case(15, 53)]
#[case(16, 52)]
#[case(17, 52)]
#[case(18, 52)]
#[case(19, 52)]
#[case(20, 53)]
#[case(21, 52)]
#[case(22, 52)]
#[case(23, 52)]
#[case(24, 52)]
#[case(25, 52)]
#[case(26, 53)]
#[case(27, 52)]
#[case(28, 52)]
#[case(29, 52)]
#[case(30, 52)]
#[case(31, 52)]
#[case(32, 53)]
#[case(33, 52)]
#[case(34, 52)]
#[case(35, 52)]
#[case(36, 52)]
#[case(37, 53)]
#[case(38, 52)]
#[case(39, 52)]
#[case(40, 52)]
#[case(41, 52)]
#[case(42, 52)]
#[case(43, 53)]
#[case(44, 52)]
#[case(45, 52)]
#[case(46, 52)]
#[case(47, 52)]
#[case(48, 53)]
#[case(49, 52)]
#[case(50, 52)]
#[case(51, 52)]
#[case(52, 52)]
#[case(53, 52)]
#[case(54, 53)]
#[case(55, 52)]
#[case(56, 52)]
#[case(57, 52)]
#[case(58, 52)]
#[case(59, 52)]
#[case(60, 53)]
#[case(61, 52)]
#[case(62, 52)]
#[case(63, 52)]
#[case(64, 52)]
#[case(65, 53)]
#[case(66, 52)]
#[case(67, 52)]
#[case(68, 52)]
#[case(69, 52)]
#[case(70, 52)]
#[case(71, 53)]
#[case(72, 52)]
#[case(73, 52)]
#[case(74, 52)]
#[case(75, 52)]
#[case(76, 53)]
#[case(77, 52)]
#[case(78, 52)]
#[case(79, 52)]
#[case(80, 52)]
#[case(81, 52)]
#[case(82, 53)]
#[case(83, 52)]
#[case(84, 52)]
#[case(85, 52)]
#[case(86, 52)]
#[case(87, 52)]
#[case(88, 53)]
#[case(89, 52)]
#[case(90, 52)]
#[case(91, 52)]
#[case(92, 52)]
#[case(93, 53)]
#[case(94, 52)]
#[case(95, 52)]
#[case(96, 52)]
#[case(97, 52)]
#[case(98, 52)]
#[case(99, 53)]
#[case(100, 52)]
#[case(101, 52)]
#[case(102, 52)]
#[case(103, 52)]
#[case(104, 52)]
#[case(105, 53)]
#[case(106, 52)]
#[case(107, 52)]
#[case(108, 52)]
#[case(109, 52)]
#[case(110, 52)]
#[case(111, 53)]
#[case(112, 52)]
#[case(113, 52)]
#[case(114, 52)]
#[case(115, 52)]
#[case(116, 53)]
#[case(117, 52)]
#[case(118, 52)]
#[case(119, 52)]
#[case(120, 52)]
#[case(121, 52)]
#[case(122, 53)]
#[case(123, 52)]
#[case(124, 52)]
#[case(125, 52)]
#[case(126, 52)]
#[case(127, 52)]
#[case(128, 53)]
#[case(129, 52)]
#[case(130, 52)]
#[case(131, 52)]
#[case(132, 52)]
#[case(133, 53)]
#[case(134, 52)]
#[case(135, 52)]
#[case(136, 52)]
#[case(137, 52)]
#[case(138, 52)]
#[case(139, 53)]
#[case(140, 52)]
#[case(141, 52)]
#[case(142, 52)]
#[case(143, 52)]
#[case(144, 53)]
#[case(145, 52)]
#[case(146, 52)]
#[case(147, 52)]
#[case(148, 52)]
#[case(149, 52)]
#[case(150, 53)]
#[case(151, 52)]
#[case(152, 52)]
#[case(153, 52)]
#[case(154, 52)]
#[case(155, 52)]
#[case(156, 53)]
#[case(157, 52)]
#[case(158, 52)]
#[case(159, 52)]
#[case(160, 52)]
#[case(161, 53)]
#[case(162, 52)]
#[case(163, 52)]
#[case(164, 52)]
#[case(165, 52)]
#[case(166, 52)]
#[case(167, 53)]
#[case(168, 52)]
#[case(169, 52)]
#[case(170, 52)]
#[case(171, 52)]
#[case(172, 53)]
#[case(173, 52)]
#[case(174, 52)]
#[case(175, 52)]
#[case(176, 52)]
#[case(177, 52)]
#[case(178, 53)]
#[case(179, 52)]
#[case(180, 52)]
#[case(181, 52)]
#[case(182, 52)]
#[case(183, 52)]
#[case(184, 53)]
#[case(185, 52)]
#[case(186, 52)]
#[case(187, 52)]
#[case(188, 52)]
#[case(189, 53)]
#[case(190, 52)]
#[case(191, 52)]
#[case(192, 52)]
#[case(193, 52)]
#[case(194, 52)]
#[case(195, 53)]
#[case(196, 52)]
#[case(197, 52)]
#[case(198, 52)]
#[case(199, 52)]
#[case(200, 52)]
#[case(201, 53)]
#[case(202, 52)]
#[case(203, 52)]
#[case(204, 52)]
#[case(205, 52)]
#[case(206, 52)]
#[case(207, 53)]
#[case(208, 52)]
#[case(209, 52)]
#[case(210, 52)]
#[case(211, 52)]
#[case(212, 53)]
#[case(213, 52)]
#[case(214, 52)]
#[case(215, 52)]
#[case(216, 52)]
#[case(217, 52)]
#[case(218, 53)]
#[case(219, 52)]
#[case(220, 52)]
#[case(221, 52)]
#[case(222, 52)]
#[case(223, 52)]
#[case(224, 53)]
#[case(225, 52)]
#[case(226, 52)]
#[case(227, 52)]
#[case(228, 52)]
#[case(229, 53)]
#[case(230, 52)]
#[case(231, 52)]
#[case(232, 52)]
#[case(233, 52)]
#[case(234, 52)]
#[case(235, 53)]
#[case(236, 52)]
#[case(237, 52)]
#[case(238, 52)]
#[case(239, 52)]
#[case(240, 53)]
#[case(241, 52)]
#[case(242, 52)]
#[case(243, 52)]
#[case(244, 52)]
#[case(245, 52)]
#[case(246, 53)]
#[case(247, 52)]
#[case(248, 52)]
#[case(249, 52)]
#[case(250, 52)]
#[case(251, 52)]
#[case(252, 53)]
#[case(253, 52)]
#[case(254, 52)]
#[case(255, 52)]
#[case(256, 52)]
#[case(257, 53)]
#[case(258, 52)]
#[case(259, 52)]
#[case(260, 52)]
#[case(261, 52)]
#[case(262, 52)]
#[case(263, 53)]
#[case(264, 52)]
#[case(265, 52)]
#[case(266, 52)]
#[case(267, 52)]
#[case(268, 53)]
#[case(269, 52)]
#[case(270, 52)]
#[case(271, 52)]
#[case(272, 52)]
#[case(273, 52)]
#[case(274, 53)]
#[case(275, 52)]
#[case(276, 52)]
#[case(277, 52)]
#[case(278, 52)]
#[case(279, 52)]
#[case(280, 53)]
#[case(281, 52)]
#[case(282, 52)]
#[case(283, 52)]
#[case(284, 52)]
#[case(285, 53)]
#[case(286, 52)]
#[case(287, 52)]
#[case(288, 52)]
#[case(289, 52)]
#[case(290, 52)]
#[case(291, 53)]
#[case(292, 52)]
#[case(293, 52)]
#[case(294, 52)]
#[case(295, 52)]
#[case(296, 53)]
#[case(297, 52)]
#[case(298, 52)]
#[case(299, 52)]
#[case(300, 52)]
#[case(301, 52)]
#[case(302, 52)]
#[case(303, 53)]
#[case(304, 52)]
#[case(305, 52)]
#[case(306, 52)]
#[case(307, 52)]
#[case(308, 53)]
#[case(309, 52)]
#[case(310, 52)]
#[case(311, 52)]
#[case(312, 52)]
#[case(313, 52)]
#[case(314, 53)]
#[case(315, 52)]
#[case(316, 52)]
#[case(317, 52)]
#[case(318, 52)]
#[case(319, 52)]
#[case(320, 53)]
#[case(321, 52)]
#[case(322, 52)]
#[case(323, 52)]
#[case(324, 52)]
#[case(325, 53)]
#[case(326, 52)]
#[case(327, 52)]
#[case(328, 52)]
#[case(329, 52)]
#[case(330, 52)]
#[case(331, 53)]
#[case(332, 52)]
#[case(333, 52)]
#[case(334, 52)]
#[case(335, 52)]
#[case(336, 53)]
#[case(337, 52)]
#[case(338, 52)]
#[case(339, 52)]
#[case(340, 52)]
#[case(341, 52)]
#[case(342, 53)]
#[case(343, 52)]
#[case(344, 52)]
#[case(345, 52)]
#[case(346, 52)]
#[case(347, 52)]
#[case(348, 53)]
#[case(349, 52)]
#[case(350, 52)]
#[case(351, 52)]
#[case(352, 52)]
#[case(353, 53)]
#[case(354, 52)]
#[case(355, 52)]
#[case(356, 52)]
#[case(357, 52)]
#[case(358, 52)]
#[case(359, 53)]
#[case(360, 52)]
#[case(361, 52)]
#[case(362, 52)]
#[case(363, 52)]
#[case(364, 53)]
#[case(365, 52)]
#[case(366, 52)]
#[case(367, 52)]
#[case(368, 52)]
#[case(369, 52)]
#[case(370, 53)]
#[case(371, 52)]
#[case(372, 52)]
#[case(373, 52)]
#[case(374, 52)]
#[case(375, 52)]
#[case(376, 53)]
#[case(377, 52)]
#[case(378, 52)]
#[case(379, 52)]
#[case(380, 52)]
#[case(381, 53)]
#[case(382, 52)]
#[case(383, 52)]
#[case(384, 52)]
#[case(385, 52)]
#[case(386, 52)]
#[case(387, 53)]
#[case(388, 52)]
#[case(389, 52)]
#[case(390, 52)]
#[case(391, 52)]
#[case(392, 53)]
#[case(393, 52)]
#[case(394, 52)]
#[case(395, 52)]
#[case(396, 52)]
#[case(397, 52)]
#[case(398, 53)]
#[case(399, 52)]
fn weeks_in_year(#[case] year: i32, #[case] expected: u8) {
assert_eq!(util::weeks_in_year(year), expected, "year {year} failed");
}
#[rstest]
#[expect(deprecated)]
fn local_offset_soundness() {
use time::util::local_offset::*;
// These functions no longer do anything so they always return `Sound`.
assert_eq!(get_soundness(), Soundness::Sound);
// Safety: This no longer has any safety requirements.
unsafe { set_soundness(Soundness::Unsound) };
assert_eq!(get_soundness(), Soundness::Sound);
// Safety: See above.
unsafe { set_soundness(Soundness::Sound) };
assert_eq!(get_soundness(), Soundness::Sound);
}
+145
View File
@@ -0,0 +1,145 @@
use rstest::rstest;
use time::Weekday::{self, *};
#[rstest]
#[case(Sunday, Saturday)]
#[case(Monday, Sunday)]
#[case(Tuesday, Monday)]
#[case(Wednesday, Tuesday)]
#[case(Thursday, Wednesday)]
#[case(Friday, Thursday)]
#[case(Saturday, Friday)]
fn previous(#[case] current: Weekday, #[case] expected: Weekday) {
assert_eq!(current.previous(), expected);
}
#[rstest]
#[case(Sunday, Monday)]
#[case(Monday, Tuesday)]
#[case(Tuesday, Wednesday)]
#[case(Wednesday, Thursday)]
#[case(Thursday, Friday)]
#[case(Friday, Saturday)]
#[case(Saturday, Sunday)]
fn next(#[case] current: Weekday, #[case] expected: Weekday) {
assert_eq!(current.next(), expected);
}
#[rstest]
#[case(Sunday, 0, Sunday)]
#[case(Sunday, 1, Monday)]
#[case(Sunday, 2, Tuesday)]
#[case(Sunday, 3, Wednesday)]
#[case(Sunday, 4, Thursday)]
#[case(Sunday, 5, Friday)]
#[case(Sunday, 6, Saturday)]
#[case(Monday, 0, Monday)]
#[case(Monday, 1, Tuesday)]
#[case(Monday, 2, Wednesday)]
#[case(Monday, 3, Thursday)]
#[case(Monday, 4, Friday)]
#[case(Monday, 5, Saturday)]
#[case(Monday, 6, Sunday)]
#[case(Sunday, 7, Sunday)]
#[case(Sunday, u8::MAX, Wednesday)]
#[case(Monday, 7, Monday)]
#[case(Monday, u8::MAX, Thursday)]
fn nth_next(#[case] current: Weekday, #[case] n: u8, #[case] expected: Weekday) {
assert_eq!(current.nth_next(n), expected);
}
#[rstest]
#[case(Sunday, 0, Sunday)]
#[case(Sunday, 1, Saturday)]
#[case(Sunday, 2, Friday)]
#[case(Sunday, 3, Thursday)]
#[case(Sunday, 4, Wednesday)]
#[case(Sunday, 5, Tuesday)]
#[case(Sunday, 6, Monday)]
#[case(Monday, 0, Monday)]
#[case(Monday, 1, Sunday)]
#[case(Monday, 2, Saturday)]
#[case(Monday, 3, Friday)]
#[case(Monday, 4, Thursday)]
#[case(Monday, 5, Wednesday)]
#[case(Monday, 6, Tuesday)]
#[case(Sunday, 7, Sunday)]
#[case(Sunday, u8::MAX, Thursday)]
#[case(Monday, 7, Monday)]
#[case(Monday, u8::MAX, Friday)]
fn nth_prev(#[case] current: Weekday, #[case] n: u8, #[case] expected: Weekday) {
assert_eq!(current.nth_prev(n), expected);
}
#[rstest]
#[case(Monday, 1)]
#[case(Tuesday, 2)]
#[case(Wednesday, 3)]
#[case(Thursday, 4)]
#[case(Friday, 5)]
#[case(Saturday, 6)]
#[case(Sunday, 7)]
fn number_from_monday(#[case] weekday: Weekday, #[case] expected: u8) {
assert_eq!(weekday.number_from_monday(), expected);
}
#[rstest]
#[case(Sunday, 1)]
#[case(Monday, 2)]
#[case(Tuesday, 3)]
#[case(Wednesday, 4)]
#[case(Thursday, 5)]
#[case(Friday, 6)]
#[case(Saturday, 7)]
fn number_from_sunday(#[case] weekday: Weekday, #[case] expected: u8) {
assert_eq!(weekday.number_from_sunday(), expected);
}
#[rstest]
#[case(Monday, 0)]
#[case(Tuesday, 1)]
#[case(Wednesday, 2)]
#[case(Thursday, 3)]
#[case(Friday, 4)]
#[case(Saturday, 5)]
#[case(Sunday, 6)]
fn number_days_from_monday(#[case] weekday: Weekday, #[case] expected: u8) {
assert_eq!(weekday.number_days_from_monday(), expected);
}
#[rstest]
#[case(Sunday, 0)]
#[case(Monday, 1)]
#[case(Tuesday, 2)]
#[case(Wednesday, 3)]
#[case(Thursday, 4)]
#[case(Friday, 5)]
#[case(Saturday, 6)]
fn number_days_from_sunday(#[case] weekday: Weekday, #[case] expected: u8) {
assert_eq!(weekday.number_days_from_sunday(), expected);
}
#[rstest]
#[case(Monday, "Monday")]
#[case(Tuesday, "Tuesday")]
#[case(Wednesday, "Wednesday")]
#[case(Thursday, "Thursday")]
#[case(Friday, "Friday")]
#[case(Saturday, "Saturday")]
#[case(Sunday, "Sunday")]
fn display(#[case] weekday: Weekday, #[case] expected: &str) {
assert_eq!(weekday.to_string(), expected);
}
#[rstest]
#[case("Monday", Ok(Monday))]
#[case("Tuesday", Ok(Tuesday))]
#[case("Wednesday", Ok(Wednesday))]
#[case("Thursday", Ok(Thursday))]
#[case("Friday", Ok(Friday))]
#[case("Saturday", Ok(Saturday))]
#[case("Sunday", Ok(Sunday))]
#[case("foo", Err(time::error::InvalidVariant))]
fn from_str(#[case] input: &str, #[case] expected: Result<Weekday, time::error::InvalidVariant>) {
assert_eq!(input.parse::<Weekday>(), expected);
}
@@ -0,0 +1,136 @@
use rstest::rstest;
use rstest_reuse::{apply, template};
use time::Weekday::{self, *};
use time::iter::WeekdayIter;
#[template]
#[rstest]
fn all_weekdays(
#[values(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)] start: Weekday,
) {
}
#[apply(all_weekdays)]
fn new(start: Weekday) {
let mut iter = WeekdayIter::new(start);
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.next()));
}
#[apply(all_weekdays)]
fn iter_from(start: Weekday) {
let mut iter = Weekday::iter_from(start);
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.next()));
}
#[apply(all_weekdays)]
fn size_hint(start: Weekday) {
assert_eq!(WeekdayIter::new(start).size_hint(), (usize::MAX, None));
}
#[apply(all_weekdays)]
fn nth(start: Weekday, #[values(1, 2, 3, 4, 5, 6, 7)] n: u8) {
let expected = start.nth_next(n);
assert_eq!(WeekdayIter::new(start).nth(n as usize), Some(expected));
}
#[apply(all_weekdays)]
fn any_checks_all_weekdays(
start: Weekday,
#[values(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)] target: Weekday,
) {
assert!(WeekdayIter::new(start).any(|d| d == target));
}
#[apply(all_weekdays)]
fn any_terminates(start: Weekday) {
assert!(!WeekdayIter::new(start).any(|_| false));
}
#[apply(all_weekdays)]
fn all_checks_all_weekdays(
start: Weekday,
#[values(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)] excludes: Weekday,
) {
assert!(!WeekdayIter::new(start).all(|d| d != excludes));
}
#[apply(all_weekdays)]
fn all_terminates(start: Weekday) {
assert!(WeekdayIter::new(start).all(|_| true));
}
#[apply(all_weekdays)]
#[should_panic(expected = "infinite")]
fn count_panics(start: Weekday) {
WeekdayIter::new(start).count();
}
#[apply(all_weekdays)]
#[should_panic(expected = "infinite")]
fn last_panics(start: Weekday) {
WeekdayIter::new(start).last();
}
#[apply(all_weekdays)]
fn rev_next(start: Weekday) {
let mut iter = WeekdayIter::new(start).rev();
assert_eq!(iter.next(), Some(start));
assert_eq!(iter.next(), Some(start.previous()));
}
#[apply(all_weekdays)]
fn rev_nth(start: Weekday, #[values(1, 2, 3, 4, 5, 6, 7)] n: u8) {
let expected = start.nth_prev(n);
assert_eq!(
WeekdayIter::new(start).rev().nth(n as usize),
Some(expected)
);
}
#[apply(all_weekdays)]
fn rev_size_hint(start: Weekday) {
assert_eq!(
WeekdayIter::new(start).rev().size_hint(),
(usize::MAX, None)
);
}
#[apply(all_weekdays)]
#[should_panic(expected = "infinite")]
fn rev_count_panics(start: Weekday) {
WeekdayIter::new(start).rev().count();
}
#[apply(all_weekdays)]
#[should_panic(expected = "infinite")]
fn rev_last_panics(start: Weekday) {
WeekdayIter::new(start).rev().last();
}
#[apply(all_weekdays)]
fn rev_any_checks_all_weekdays(
start: Weekday,
#[values(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)] target: Weekday,
) {
assert!(WeekdayIter::new(start).rev().any(|d| d == target));
}
#[apply(all_weekdays)]
fn rev_any_terminates(start: Weekday) {
assert!(!WeekdayIter::new(start).rev().any(|_| false));
}
#[apply(all_weekdays)]
fn rev_all_checks_all_weekdays(
start: Weekday,
#[values(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)] excludes: Weekday,
) {
assert!(!WeekdayIter::new(start).rev().all(|d| d != excludes));
}
#[apply(all_weekdays)]
fn rev_all_terminates(start: Weekday) {
assert!(WeekdayIter::new(start).rev().all(|_| true));
}