Skip to content

Commit e5b2344

Browse files
committed
support jiff
1 parent 6ab4a11 commit e5b2344

13 files changed

Lines changed: 816 additions & 259 deletions

File tree

.github/workflows/ci-version.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ jobs:
2424
-
2525
- --features chrono
2626
- --features time
27+
- --features jiff
2728
- --all-features
2829
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
2930
runs-on: ${{ matrix.os }}
@@ -49,6 +50,7 @@ jobs:
4950
-
5051
- --features chrono
5152
- --features time
53+
- --features jiff
5254
- --all-features
5355
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
5456
runs-on: ${{ matrix.os }}

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
-
4141
- --features chrono
4242
- --features time
43+
- --features jiff
4344
- --all-features
4445
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
4546
runs-on: ${{ matrix.os }}
@@ -65,6 +66,7 @@ jobs:
6566
-
6667
- --features chrono
6768
- --features time
69+
- --features jiff
6870
- --all-features
6971
name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }})
7072
runs-on: ${{ matrix.os }}

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"]
1515
[features]
1616
default = []
1717
chrono = ["dep:chrono"]
18+
jiff = ["dep:jiff"]
1819
time = ["dep:time"]
1920

2021
[dependencies]
2122
chrono = { version = "0.4", default-features = false, optional = true }
23+
jiff = { version = "0.2", default-features = false, optional = true }
2224
time = { version = "0.3", default-features = false, optional = true }
2325

2426
year-helper = "0.2"
2527

2628
[dev-dependencies]
2729
rand = "0.9"
2830
chrono = { version = "0.4", default-features = false, features = ["clock"] }
31+
jiff = "0.2"
2932
time = { version = "0.3", default-features = false, features = ["local-offset"] }
3033

3134
[[example]]
@@ -36,6 +39,10 @@ required-features = ["chrono"]
3639
name = "time"
3740
required-features = ["time"]
3841

42+
[[example]]
43+
name = "jiff"
44+
required-features = ["jiff"]
45+
3946
[package.metadata.docs.rs]
4047
all-features = true
41-
rustdoc-args = ["--cfg", "docsrs"]
48+
rustdoc-args = ["--cfg", "docsrs"]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Date-time crate support is enabled through Cargo features. Enable only the provi
1212
| Feature | Time-zone aware types | Naive/local date-time types |
1313
| --- | --- | --- |
1414
| `chrono` | `chrono::DateTime<Tz>` | `chrono::NaiveDateTime` |
15+
| `jiff` | `jiff::Zoned` | `jiff::civil::DateTime` |
1516
| `time` | `time::OffsetDateTime`, `time::UtcDateTime` | `time::PrimitiveDateTime` |
1617

1718
Time-zone aware types keep a timezone or UTC offset in the value. Naive/local types store only calendar and clock fields, so the caller decides how to interpret them.

examples/jiff.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use date_differencer::{add_date_time_diff, date_diff, date_time_diff};
2+
use jiff::{Zoned, civil::date, tz::TimeZone};
3+
4+
fn date_time(
5+
time_zone: &TimeZone,
6+
year: i16,
7+
month: i8,
8+
day: i8,
9+
hour: i8,
10+
minute: i8,
11+
second: i8,
12+
) -> Zoned {
13+
date(year, month, day).at(hour, minute, second, 0).to_zoned(time_zone.clone()).unwrap()
14+
}
15+
16+
fn main() {
17+
// Use the system time zone so this example feels like local date-time code.
18+
let time_zone = TimeZone::system();
19+
20+
let a = date_time(&time_zone, 2022, 4, 6, 0, 0, 0);
21+
let b = date_time(&time_zone, 2023, 6, 9, 1, 0, 0);
22+
23+
println!("{:?}", date_diff(a.clone(), b.clone()));
24+
/*
25+
{
26+
"years": 1,
27+
"months": 2,
28+
"days": 3
29+
}
30+
*/
31+
32+
println!("{:?}", date_time_diff(a.clone(), b.clone()));
33+
/*
34+
{
35+
"years": 1,
36+
"months": 2,
37+
"days": 3,
38+
"hours": 1,
39+
"minutes": 0,
40+
"seconds": 0,
41+
"nanoseconds": 0
42+
}
43+
*/
44+
45+
println!("{}", add_date_time_diff(a.clone(), &date_time_diff(a.clone(), b.clone())).unwrap()); // the same as b
46+
47+
let a = date_time(&time_zone, 2020, 2, 27, 0, 0, 0);
48+
let b = date_time(&time_zone, 2021, 3, 1, 0, 0, 0);
49+
50+
println!("{:?}", date_diff(a.clone(), b.clone()));
51+
/*
52+
{
53+
"years": 1,
54+
"months": 0,
55+
"days": 2
56+
}
57+
58+
Explanation:
59+
1. 2020-02-27 + 1 year -> 2021-02-27
60+
2. 2021-02-27 + 2 days -> 2021-03-01 (2021-02 has 28 days)
61+
*/
62+
63+
println!("{:?}", date_diff(b, a));
64+
/*
65+
{
66+
"years": -1,
67+
"months": 0,
68+
"days": -3
69+
}
70+
71+
Explanation:
72+
1. 2021-03-01 - 1 year -> 2020-03-01
73+
2. 2020-03-01 - 3 days -> 2020-02-27 (2020-02 has 29 days)
74+
*/
75+
}

src/add_diff/jiff_support.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use core::fmt;
2+
3+
use jiff::{Zoned, civil::DateTime};
4+
5+
use super::{AddDateTimeDiff, AddedDateTimeParts, DateTimeDiff, add_date_time_parts};
6+
7+
#[inline]
8+
fn jiff_error(args: fmt::Arguments<'_>) -> jiff::Error {
9+
jiff::Error::from_args(args)
10+
}
11+
12+
#[inline]
13+
fn added_date_time_parts(
14+
from: &impl super::DateTimeParts,
15+
date_time_diff: &impl DateTimeDiff,
16+
) -> Result<AddedDateTimeParts, jiff::Error> {
17+
add_date_time_parts(from, date_time_diff).ok_or_else(|| {
18+
jiff_error(format_args!("date-time addition overflowed before constructing the Jiff value"))
19+
})
20+
}
21+
22+
#[inline]
23+
fn jiff_year(year: i32) -> Result<i16, jiff::Error> {
24+
i16::try_from(year)
25+
.map_err(|_| jiff_error(format_args!("date-time year is outside Jiff's supported range")))
26+
}
27+
28+
impl AddDateTimeDiff for DateTime {
29+
type Output = Result<DateTime, jiff::Error>;
30+
31+
#[inline]
32+
fn add_date_time_diff(self, date_time_diff: &impl DateTimeDiff) -> Self::Output {
33+
let parts = added_date_time_parts(&self, date_time_diff)?;
34+
35+
DateTime::new(
36+
jiff_year(parts.year)?,
37+
parts.month as i8,
38+
parts.day as i8,
39+
parts.hour as i8,
40+
parts.minute as i8,
41+
parts.second as i8,
42+
parts.nanosecond as i32,
43+
)
44+
}
45+
}
46+
47+
impl AddDateTimeDiff for Zoned {
48+
type Output = Result<Zoned, jiff::Error>;
49+
50+
#[inline]
51+
fn add_date_time_diff(self, date_time_diff: &impl DateTimeDiff) -> Self::Output {
52+
let parts = added_date_time_parts(&self, date_time_diff)?;
53+
54+
self.with()
55+
.year(jiff_year(parts.year)?)
56+
.month(parts.month as i8)
57+
.day(parts.day as i8)
58+
.hour(parts.hour as i8)
59+
.minute(parts.minute as i8)
60+
.second(parts.second as i8)
61+
.subsec_nanosecond(parts.nanosecond as i32)
62+
.build()
63+
}
64+
}

src/add_diff/mod.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#[cfg(feature = "chrono")]
22
mod chrono_support;
3+
#[cfg(feature = "jiff")]
4+
mod jiff_support;
35
#[cfg(feature = "time")]
46
mod time_support;
57

6-
#[cfg(any(feature = "chrono", feature = "time"))]
8+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
79
use super::constants::*;
810
use super::{DateTimeDiff, DateTimeParts};
911

10-
#[cfg(any(feature = "chrono", feature = "time"))]
12+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
1113
#[derive(Debug, Clone, Copy)]
1214
struct AddedDateTimeParts {
1315
year: i32,
@@ -19,7 +21,7 @@ struct AddedDateTimeParts {
1921
nanosecond: u32,
2022
}
2123

22-
#[cfg(any(feature = "chrono", feature = "time"))]
24+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
2325
#[inline]
2426
fn month_add(year: &mut i32, month: &mut i32, n: i32) -> Option<()> {
2527
*month = month.checked_add(n)?;
@@ -40,7 +42,7 @@ fn month_add(year: &mut i32, month: &mut i32, n: i32) -> Option<()> {
4042
Some(())
4143
}
4244

43-
#[cfg(any(feature = "chrono", feature = "time"))]
45+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
4446
#[inline]
4547
fn date_add(year: &mut i32, month: &mut i32, date: &mut i32, n: i32) -> Option<()> {
4648
*date = date.checked_add(n)?;
@@ -81,7 +83,7 @@ fn date_add(year: &mut i32, month: &mut i32, date: &mut i32, n: i32) -> Option<(
8183
Some(())
8284
}
8385

84-
#[cfg(any(feature = "chrono", feature = "time"))]
86+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
8587
#[inline]
8688
fn hour_add(year: &mut i32, month: &mut i32, date: &mut i32, hour: &mut i32, n: i32) -> Option<()> {
8789
*hour = hour.checked_add(n)?;
@@ -102,7 +104,7 @@ fn hour_add(year: &mut i32, month: &mut i32, date: &mut i32, hour: &mut i32, n:
102104
Some(())
103105
}
104106

105-
#[cfg(any(feature = "chrono", feature = "time"))]
107+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
106108
#[inline]
107109
fn minute_add(
108110
year: &mut i32,
@@ -130,7 +132,7 @@ fn minute_add(
130132
Some(())
131133
}
132134

133-
#[cfg(any(feature = "chrono", feature = "time"))]
135+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
134136
#[inline]
135137
fn second_add(
136138
year: &mut i32,
@@ -159,7 +161,7 @@ fn second_add(
159161
Some(())
160162
}
161163

162-
#[cfg(any(feature = "chrono", feature = "time"))]
164+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
163165
#[allow(clippy::too_many_arguments)]
164166
#[inline]
165167
fn nanosecond_add(
@@ -187,7 +189,7 @@ fn nanosecond_add(
187189
Some(())
188190
}
189191

190-
#[cfg(any(feature = "chrono", feature = "time"))]
192+
#[cfg(any(feature = "chrono", feature = "jiff", feature = "time"))]
191193
fn add_date_time_parts(
192194
from: &impl DateTimeParts,
193195
date_time_diff: &impl DateTimeDiff,

src/diff/jiff_support.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use jiff::{Zoned, civil::DateTime};
2+
3+
use super::DateTimeParts;
4+
5+
impl DateTimeParts for DateTime {
6+
#[inline]
7+
fn year(&self) -> i32 {
8+
i32::from(DateTime::year(*self))
9+
}
10+
11+
#[inline]
12+
fn month(&self) -> u8 {
13+
DateTime::month(*self) as u8
14+
}
15+
16+
#[inline]
17+
fn day(&self) -> u8 {
18+
DateTime::day(*self) as u8
19+
}
20+
21+
#[inline]
22+
fn hour(&self) -> u8 {
23+
DateTime::hour(*self) as u8
24+
}
25+
26+
#[inline]
27+
fn minute(&self) -> u8 {
28+
DateTime::minute(*self) as u8
29+
}
30+
31+
#[inline]
32+
fn second(&self) -> u8 {
33+
DateTime::second(*self) as u8
34+
}
35+
36+
#[inline]
37+
fn nanosecond(&self) -> u32 {
38+
DateTime::subsec_nanosecond(*self) as u32
39+
}
40+
}
41+
42+
impl DateTimeParts for Zoned {
43+
#[inline]
44+
fn year(&self) -> i32 {
45+
i32::from(self.year())
46+
}
47+
48+
#[inline]
49+
fn month(&self) -> u8 {
50+
self.month() as u8
51+
}
52+
53+
#[inline]
54+
fn day(&self) -> u8 {
55+
self.day() as u8
56+
}
57+
58+
#[inline]
59+
fn hour(&self) -> u8 {
60+
self.hour() as u8
61+
}
62+
63+
#[inline]
64+
fn minute(&self) -> u8 {
65+
self.minute() as u8
66+
}
67+
68+
#[inline]
69+
fn second(&self) -> u8 {
70+
self.second() as u8
71+
}
72+
73+
#[inline]
74+
fn nanosecond(&self) -> u32 {
75+
self.subsec_nanosecond() as u32
76+
}
77+
}

src/diff/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#[cfg(feature = "chrono")]
22
mod chrono_support;
3+
#[cfg(feature = "jiff")]
4+
mod jiff_support;
35
#[cfg(feature = "time")]
46
mod time_support;
57

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Date-time crate support is enabled through Cargo features. Enable only the provi
1010
| Feature | Time-zone aware types | Naive/local date-time types |
1111
| --- | --- | --- |
1212
| `chrono` | `chrono::DateTime<Tz>` | `chrono::NaiveDateTime` |
13+
| `jiff` | `jiff::Zoned` | `jiff::civil::DateTime` |
1314
| `time` | `time::OffsetDateTime`, `time::UtcDateTime` | `time::PrimitiveDateTime` |
1415
1516
Time-zone aware types keep a timezone or UTC offset in the value. Naive/local types store only calendar and clock fields, so the caller decides how to interpret them.

0 commit comments

Comments
 (0)