Skip to content

Commit 240d1b7

Browse files
committed
Add suffix parsing
1 parent aba3770 commit 240d1b7

27 files changed

Lines changed: 456 additions & 70 deletions

File tree

crates/winnow-datetime-assert/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.0 - 2015-05-14
2+
* Added support for winnow-datetime 0.3.0 objects
3+
* Added assersions for Calendar and TimeZone
4+
15
## 0.2.0 - 2024-05-04
26
* Changed parser signatures to meet winnow 0.7 standards
37
* Removed dependency on galvanic-test which was accidentally left over from some initial design experiments.

crates/winnow-datetime-assert/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "winnow_datetime_assert"
3-
version = "0.2.0"
3+
version = "0.3.0"
44
description = "Testing/Benchmarking winnow-datetime parsers"
5-
keywords = [ "iso8601", "date-time", "parser", "winnow" ]
5+
keywords = [ "date-time", "parser", "winnow" ]
66
categories = [ "parser-implementations", "date-and-time" ]
77

88
repository = "https://github.com/soulstompp/winnow-datetime"
@@ -13,7 +13,7 @@ readme = "README.md"
1313
edition = "2021"
1414

1515
[dependencies]
16-
winnow_datetime = { path = "../winnow-datetime", version = "0.2", features = ["serde"] }
16+
winnow_datetime = { path = "../winnow-datetime", version = "0.3", features = ["serde"] }
1717
libtest-mimic = "0.8.1"
1818
winnow = "0.7"
1919
serde = { version = "1.0", features = ["derive"] }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
assertions:
2+
- format: "gregory"
3+
input: "gregory"
4+
expected:
5+
identifier: "gregory"
6+
critical: false
7+
- format: "islamic-umalqura"
8+
input: "islamic-umalqura"
9+
expected:
10+
identifier: "islamic-umalqura"
11+
critical: false

crates/winnow-datetime-assert/data/assertions/time_zone.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,10 @@ assertions:
5252
hours: -8
5353
minutes: 0
5454
critical: false
55+
- format: "America/Los_Angeles"
56+
input: "America/Los_Angeles"
57+
expected:
58+
!Named
59+
zone:
60+
identifier: "America/Los_Angeles"
61+
critical: false
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::{FormatAssertion, FormatAssertionBuilder, FormatCoverage, FormatCoverageBuilder};
2+
use serde::Deserialize;
3+
use winnow_datetime::types::Calendar;
4+
5+
#[derive(Debug, Deserialize)]
6+
pub struct CalendarAssertion {
7+
assertions: Vec<FormatAssertion<Calendar>>,
8+
}
9+
10+
impl FormatAssertionBuilder<Calendar> for CalendarAssertion {
11+
fn piece() -> &'static str {
12+
"calendar"
13+
}
14+
15+
fn base_assertions(&self) -> Vec<FormatAssertion<Calendar>> {
16+
self.assertions.clone()
17+
}
18+
19+
fn assertions(&self) -> Vec<FormatAssertion<Calendar>> {
20+
self.base_assertions()
21+
}
22+
}
23+
24+
#[derive(Clone, Debug, PartialEq, Deserialize)]
25+
pub struct CalendarCoverage {
26+
pub coverage: Vec<FormatCoverage<Calendar>>,
27+
}
28+
29+
impl FormatCoverageBuilder<Calendar> for CalendarCoverage {
30+
fn piece() -> &'static str {
31+
"calendar"
32+
}
33+
fn base_coverage(&self) -> Vec<FormatCoverage<Calendar>> {
34+
self.coverage.clone()
35+
}
36+
37+
fn coverage(&self) -> Vec<FormatCoverage<Calendar>> {
38+
vec![]
39+
}
40+
}

crates/winnow-datetime-assert/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ pub use offset::{OffsetAssertion, OffsetCoverage};
2222
pub mod time;
2323
pub use time::{TimeAssertion, TimeCoverage};
2424

25+
pub mod calendar;
2526
pub mod time_zone;
27+
2628
pub use time_zone::{TimeZoneAssertion, TimeZoneCoverage};
2729

30+
pub use calendar::{CalendarAssertion, CalendarCoverage};
31+
2832
#[derive(Clone, Debug, PartialEq, Deserialize)]
2933
pub struct FormatAssertion<T> {
3034
pub format: String,

crates/winnow-datetime-assert/src/time.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ impl FormatCoverageBuilder<Time> for TimeCoverage {
8888
);
8989
let exception = match (t.exception.clone(), offset.exception.clone()) {
9090
(Exception::Unspecified, Exception::Unspecified) => Exception::Unspecified,
91-
(Exception::Specific {value: mut t}, Exception::Specific { value: o }) => match o {
91+
(
92+
Exception::Specific { value: mut t },
93+
Exception::Specific { value: o },
94+
) => match o {
9295
Offset::Fixed {
9396
hours,
9497
minutes,
@@ -106,7 +109,9 @@ impl FormatCoverageBuilder<Time> for TimeCoverage {
106109
Exception::Specific { value: t }
107110
}
108111
},
109-
(Exception::Specific { value: t }, Exception::Unspecified) => Exception::Specific { value: t },
112+
(Exception::Specific { value: t }, Exception::Unspecified) => {
113+
Exception::Specific { value: t }
114+
}
110115
(Exception::Unspecified, Exception::Specific { value: o }) => {
111116
let mut default_t = assertions
112117
.get(&t.format)

crates/winnow-datetime/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.3.0 - 2015-05-30
2+
* Addition of TimeZone enum to support new information from RFC9557
3+
* Addition of Calendar struct to support new information from RFC9557
4+
* Changed Offset to not rely on Option, which caused problems with test suite YAML
5+
* Added critical value to Offset for RFC9557 support
6+
* Added support for setting named time zones for jiff exports
7+
18
## 0.2.3 - 2015-05-14
29
* Support for `Date::Ordinal` conversions to `jiff::civil::Date`
310

crates/winnow-datetime/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "winnow_datetime"
3-
version = "0.2.3"
3+
version = "0.3.0"
44
description = "Parsing dates using winnow"
55
keywords = [ "iso8601", "date-time", "parser", "winnow" ]
66
categories = [ "parser-implementations", "date-and-time" ]

crates/winnow-datetime/src/convert/jiff.rs

Lines changed: 149 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@ impl crate::Time {
1919
pub fn into_civil_time(self) -> Option<jiff::civil::Time> {
2020
jiff::civil::Time::try_from(self).ok()
2121
}
22+
23+
pub fn jiff_fixed_tz(o: Option<crate::Offset>) -> Result<jiff::tz::TimeZone, jiff::Error> {
24+
let o_seconds = if let Some(o) = o {
25+
match o {
26+
Offset::Fixed {
27+
hours,
28+
minutes,
29+
critical: _,
30+
} => hours * 3600 + minutes * 60,
31+
Offset::LocalUnknown { critical: _ } => 0,
32+
}
33+
} else {
34+
0
35+
};
36+
37+
Ok(jiff::tz::TimeZone::fixed(jiff::tz::Offset::from_seconds(
38+
o_seconds.try_into().unwrap(),
39+
)?))
40+
}
2241
}
2342

2443
impl TryFrom<crate::Date> for jiff::civil::Date {
@@ -86,21 +105,19 @@ impl TryFrom<crate::DateTime> for jiff::Zoned {
86105
let naive_time = jiff::civil::Time::try_from(dt.time.clone())?;
87106
let naive_datetime = naive_date.to_datetime(naive_time);
88107

89-
let o_seconds = match dt.time.offset {
90-
Some(o) => match o {
91-
Offset::Fixed {
92-
hours,
93-
minutes,
94-
critical: _,
95-
} => hours * 3600 + minutes * 60,
96-
Offset::LocalUnknown { critical: _ } => 0,
97-
},
98-
None => 0,
99-
};
108+
let mut tz = crate::Time::jiff_fixed_tz(dt.time.offset)?;
100109

101-
let offset = jiff::tz::Offset::from_seconds(o_seconds.try_into().unwrap())?;
110+
if let Some(z) = dt.time.time_zone {
111+
match z {
112+
crate::TimeZone::Named { zone } => {
113+
tz = jiff::tz::TimeZone::get(&zone.identifier)?;
114+
}
115+
crate::TimeZone::Fixed { offset } => {
116+
tz = crate::Time::jiff_fixed_tz(Some(offset))?;
117+
}
118+
}
119+
}
102120

103-
let tz = jiff::tz::TimeZone::fixed(offset);
104121
naive_datetime.to_zoned(tz)
105122
}
106123
}
@@ -138,6 +155,8 @@ impl TryFrom<crate::Duration> for jiff::Span {
138155

139156
#[cfg(test)]
140157
mod date_and_time {
158+
use crate::Offset;
159+
use crate::TimeZone;
141160
use core::convert::TryFrom;
142161

143162
#[test]
@@ -190,13 +209,129 @@ mod date_and_time {
190209
},
191210
};
192211

193-
let datetime = time::PrimitiveDateTime::try_from(dt).unwrap();
212+
let datetime = jiff::Zoned::try_from(dt).unwrap();
213+
assert_eq!(datetime.year(), 2024);
214+
assert_eq!(datetime.month() as u8, 3);
215+
assert_eq!(datetime.day(), 9);
216+
assert_eq!(datetime.hour(), 23);
217+
assert_eq!(datetime.minute(), 40);
218+
assert_eq!(datetime.second(), 0);
219+
}
220+
221+
#[test]
222+
fn datetime_from_ymd_hms_with_offset() {
223+
let dt = crate::DateTime {
224+
date: crate::Date::YMD {
225+
year: 2024,
226+
month: 3,
227+
day: 9,
228+
},
229+
time: crate::Time {
230+
hour: 23,
231+
minute: 40,
232+
second: 0,
233+
millisecond: 0,
234+
offset: Some(Offset::Fixed {
235+
hours: 2,
236+
minutes: 0,
237+
critical: false,
238+
}),
239+
time_zone: None,
240+
calendar: None,
241+
},
242+
};
243+
244+
let datetime = jiff::Zoned::try_from(dt).unwrap();
245+
assert_eq!(datetime.year(), 2024);
246+
assert_eq!(datetime.month() as u8, 3);
247+
assert_eq!(datetime.day(), 9);
248+
assert_eq!(datetime.hour(), 23);
249+
assert_eq!(datetime.minute(), 40);
250+
assert_eq!(datetime.second(), 0);
251+
assert_eq!(
252+
datetime.time_zone().to_fixed_offset().unwrap().seconds(),
253+
2 * 3600
254+
);
255+
}
256+
257+
#[test]
258+
fn datetime_from_ymd_hms_with_matching_offset() {
259+
let dt = crate::DateTime {
260+
date: crate::Date::YMD {
261+
year: 2024,
262+
month: 3,
263+
day: 9,
264+
},
265+
time: crate::Time {
266+
hour: 23,
267+
minute: 40,
268+
second: 0,
269+
millisecond: 0,
270+
offset: Some(Offset::Fixed {
271+
hours: 2,
272+
minutes: 0,
273+
critical: false,
274+
}),
275+
time_zone: Some(TimeZone::Fixed {
276+
offset: Offset::Fixed {
277+
hours: 3,
278+
minutes: 0,
279+
critical: false,
280+
},
281+
}),
282+
calendar: None,
283+
},
284+
};
285+
286+
let datetime = jiff::Zoned::try_from(dt).unwrap();
287+
assert_eq!(datetime.year(), 2024);
288+
assert_eq!(datetime.month() as u8, 3);
289+
assert_eq!(datetime.day(), 9);
290+
assert_eq!(datetime.hour(), 23);
291+
assert_eq!(datetime.minute(), 40);
292+
assert_eq!(datetime.second(), 0);
293+
assert_eq!(
294+
datetime.time_zone().to_fixed_offset().unwrap().seconds(),
295+
3 * 3600
296+
);
297+
}
298+
299+
#[test]
300+
fn datetime_from_ymd_hms_with_named_zone() {
301+
let dt = crate::DateTime {
302+
date: crate::Date::YMD {
303+
year: 2024,
304+
month: 3,
305+
day: 9,
306+
},
307+
time: crate::Time {
308+
hour: 23,
309+
minute: 40,
310+
second: 0,
311+
millisecond: 0,
312+
offset: Some(Offset::Fixed {
313+
hours: 2,
314+
minutes: 0,
315+
critical: false,
316+
}),
317+
time_zone: Some(TimeZone::Named {
318+
zone: crate::NamedTimeZone {
319+
identifier: "Europe/Berlin".to_string(),
320+
critical: false,
321+
},
322+
}),
323+
calendar: None,
324+
},
325+
};
326+
327+
let datetime = jiff::Zoned::try_from(dt).unwrap();
194328
assert_eq!(datetime.year(), 2024);
195329
assert_eq!(datetime.month() as u8, 3);
196330
assert_eq!(datetime.day(), 9);
197331
assert_eq!(datetime.hour(), 23);
198332
assert_eq!(datetime.minute(), 40);
199333
assert_eq!(datetime.second(), 0);
334+
assert_eq!(datetime.time_zone().iana_name().unwrap(), "Europe/Berlin");
200335
}
201336

202337
#[test]

0 commit comments

Comments
 (0)