python-dateutil-rs 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/Cargo.lock +2 -2
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/PKG-INFO +1 -1
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/Cargo.toml +1 -1
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/common.rs +13 -40
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/easter.rs +0 -44
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/isoparser.rs +21 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/parserinfo.rs +40 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser.rs +180 -82
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/relativedelta.rs +152 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule/parse.rs +25 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule/set.rs +38 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule.rs +221 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/tz/offset.rs +12 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/tz.rs +38 -14
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/Cargo.toml +1 -1
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/pyproject.toml +1 -1
- python_dateutil_rs-0.1.1/python/dateutil_rs/easter.py +15 -0
- python_dateutil_rs-0.1.1/python/dateutil_rs/relativedelta.py +5 -0
- python_dateutil_rs-0.1.1/python/dateutil_rs/rrule.py +27 -0
- python_dateutil_rs-0.1.1/python/dateutil_rs/tz.py +23 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/Cargo.toml +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/LICENSE +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/README.md +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/CLAUDE.md +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/benches/benchmarks.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/error.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/lib.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/tokenizer.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule/iter.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/tz/file.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/tz/local.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/tz/utc.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/lib.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/common.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/conv.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/easter.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/parser.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/relativedelta.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/rrule.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/tz.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py.rs +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/python/dateutil_rs/__init__.py +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/python/dateutil_rs/_native.pyi +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/python/dateutil_rs/parser.py +0 -0
- {python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/python/dateutil_rs/py.typed +0 -0
|
@@ -220,7 +220,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
|
|
220
220
|
|
|
221
221
|
[[package]]
|
|
222
222
|
name = "dateutil-core"
|
|
223
|
-
version = "0.1.
|
|
223
|
+
version = "0.1.1"
|
|
224
224
|
dependencies = [
|
|
225
225
|
"bitflags",
|
|
226
226
|
"chrono",
|
|
@@ -233,7 +233,7 @@ dependencies = [
|
|
|
233
233
|
|
|
234
234
|
[[package]]
|
|
235
235
|
name = "dateutil-py"
|
|
236
|
-
version = "0.1.
|
|
236
|
+
version = "0.1.1"
|
|
237
237
|
dependencies = [
|
|
238
238
|
"chrono",
|
|
239
239
|
"dateutil-core",
|
|
@@ -189,30 +189,6 @@ mod tests {
|
|
|
189
189
|
assert_eq!(wd_neg.to_string(), "SU(-100)");
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
#[test]
|
|
193
|
-
fn test_weekday_negative_one_n() {
|
|
194
|
-
// Last occurrence (e.g., last Friday of month)
|
|
195
|
-
let wd = FR.with_n(Some(-1));
|
|
196
|
-
assert_eq!(wd.n(), Some(-1));
|
|
197
|
-
assert_eq!(wd.to_string(), "FR(-1)");
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
#[test]
|
|
201
|
-
fn test_weekday_clone_copy() {
|
|
202
|
-
let wd = MO.with_n(Some(2));
|
|
203
|
-
let cloned = wd;
|
|
204
|
-
assert_eq!(wd, cloned); // Copy semantics — both usable
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
#[test]
|
|
208
|
-
fn test_weekday_boundary_values() {
|
|
209
|
-
// Weekday 0 (Monday) and 6 (Sunday) are boundaries
|
|
210
|
-
let mon = Weekday::new(0, Some(1)).unwrap();
|
|
211
|
-
let sun = Weekday::new(6, Some(-1)).unwrap();
|
|
212
|
-
assert_eq!(mon.weekday(), 0);
|
|
213
|
-
assert_eq!(sun.weekday(), 6);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
192
|
#[test]
|
|
217
193
|
fn test_weekday_all_invalid() {
|
|
218
194
|
for i in 7..=255 {
|
|
@@ -227,14 +203,6 @@ mod tests {
|
|
|
227
203
|
assert_ne!(a, b);
|
|
228
204
|
}
|
|
229
205
|
|
|
230
|
-
#[test]
|
|
231
|
-
fn test_weekday_eq_none_vs_zero() {
|
|
232
|
-
// n=0 is now rejected at construction time (Weekday::new).
|
|
233
|
-
// with_n() bypasses validation for internal use, so test display.
|
|
234
|
-
let a = MO.with_n(None);
|
|
235
|
-
assert_eq!(a.to_string(), "MO");
|
|
236
|
-
}
|
|
237
|
-
|
|
238
206
|
#[test]
|
|
239
207
|
fn test_weekday_hash_with_n() {
|
|
240
208
|
use std::collections::HashSet;
|
|
@@ -281,14 +249,6 @@ mod tests {
|
|
|
281
249
|
}
|
|
282
250
|
}
|
|
283
251
|
|
|
284
|
-
#[test]
|
|
285
|
-
fn test_weekday_equality_ignores_n_for_same_display() {
|
|
286
|
-
let a = MO.with_n(Some(1));
|
|
287
|
-
let b = MO.with_n(Some(-1));
|
|
288
|
-
assert_ne!(a, b);
|
|
289
|
-
assert_eq!(a.weekday(), b.weekday());
|
|
290
|
-
}
|
|
291
|
-
|
|
292
252
|
#[test]
|
|
293
253
|
fn test_weekday_hash_set_none() {
|
|
294
254
|
use std::collections::HashSet;
|
|
@@ -297,4 +257,17 @@ mod tests {
|
|
|
297
257
|
set.insert(MO.with_n(Some(1)));
|
|
298
258
|
assert_eq!(set.len(), 2);
|
|
299
259
|
}
|
|
260
|
+
|
|
261
|
+
#[test]
|
|
262
|
+
fn test_weekday_try_from_valid() {
|
|
263
|
+
let wd: Weekday = 3u8.try_into().unwrap();
|
|
264
|
+
assert_eq!(wd.weekday(), 3);
|
|
265
|
+
assert_eq!(wd.n(), None);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
#[test]
|
|
269
|
+
fn test_weekday_try_from_invalid() {
|
|
270
|
+
let result: Result<Weekday, _> = 7u8.try_into();
|
|
271
|
+
assert!(result.is_err());
|
|
272
|
+
}
|
|
300
273
|
}
|
|
@@ -71,30 +71,6 @@ mod tests {
|
|
|
71
71
|
use super::*;
|
|
72
72
|
use chrono::Datelike;
|
|
73
73
|
|
|
74
|
-
#[test]
|
|
75
|
-
fn test_western_2024() {
|
|
76
|
-
assert_eq!(
|
|
77
|
-
easter(2024, EasterMethod::Western).unwrap(),
|
|
78
|
-
NaiveDate::from_ymd_opt(2024, 3, 31).unwrap()
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
#[test]
|
|
83
|
-
fn test_orthodox_2024() {
|
|
84
|
-
assert_eq!(
|
|
85
|
-
easter(2024, EasterMethod::Orthodox).unwrap(),
|
|
86
|
-
NaiveDate::from_ymd_opt(2024, 5, 5).unwrap()
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
#[test]
|
|
91
|
-
fn test_julian_326() {
|
|
92
|
-
assert_eq!(
|
|
93
|
-
easter(326, EasterMethod::Julian).unwrap(),
|
|
94
|
-
NaiveDate::from_ymd_opt(326, 4, 3).unwrap()
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
74
|
#[test]
|
|
99
75
|
fn test_invalid_method_from_i32() {
|
|
100
76
|
assert!(matches!(
|
|
@@ -107,18 +83,6 @@ mod tests {
|
|
|
107
83
|
));
|
|
108
84
|
}
|
|
109
85
|
|
|
110
|
-
#[test]
|
|
111
|
-
fn test_invalid_year() {
|
|
112
|
-
assert!(matches!(
|
|
113
|
-
easter(0, EasterMethod::Western),
|
|
114
|
-
Err(EasterError::InvalidYear(0))
|
|
115
|
-
));
|
|
116
|
-
assert!(matches!(
|
|
117
|
-
easter(-1, EasterMethod::Western),
|
|
118
|
-
Err(EasterError::InvalidYear(-1))
|
|
119
|
-
));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
86
|
#[test]
|
|
123
87
|
fn test_western_range_1990_2050() {
|
|
124
88
|
let expected: Vec<(i32, u32, u32)> = vec![
|
|
@@ -347,14 +311,6 @@ mod tests {
|
|
|
347
311
|
);
|
|
348
312
|
}
|
|
349
313
|
|
|
350
|
-
#[test]
|
|
351
|
-
fn test_orthodox_boundary_exact_1600() {
|
|
352
|
-
let d1600 = easter(1600, EasterMethod::Orthodox).unwrap();
|
|
353
|
-
let d1601 = easter(1601, EasterMethod::Orthodox).unwrap();
|
|
354
|
-
assert!((3..=5).contains(&d1600.month()));
|
|
355
|
-
assert!((3..=5).contains(&d1601.month()));
|
|
356
|
-
}
|
|
357
|
-
|
|
358
314
|
#[test]
|
|
359
315
|
fn test_easter_always_sunday_western_wide_range() {
|
|
360
316
|
// Western (Gregorian) Easter is always Sunday across a wide year range
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/isoparser.rs
RENAMED
|
@@ -591,4 +591,25 @@ mod tests {
|
|
|
591
591
|
fn test_iso_leap_second_rejected() {
|
|
592
592
|
assert!(isoparse("2024-01-15T23:59:60").is_err());
|
|
593
593
|
}
|
|
594
|
+
|
|
595
|
+
#[test]
|
|
596
|
+
fn test_iso_hhmm_colon_format() {
|
|
597
|
+
let dt = isoparse("2024-01-15T10:30").unwrap();
|
|
598
|
+
assert_eq!(dt.hour(), 10);
|
|
599
|
+
assert_eq!(dt.minute(), 30);
|
|
600
|
+
assert_eq!(dt.second(), 0);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
#[test]
|
|
604
|
+
fn test_iso_unrecognized_time_format() {
|
|
605
|
+
let result = isoparse("2024-01-15TX");
|
|
606
|
+
assert!(result.is_err());
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
#[test]
|
|
610
|
+
fn test_iso_fractional_dot_only() {
|
|
611
|
+
let dt = isoparse("2024-01-15T10:30:45.").unwrap();
|
|
612
|
+
assert_eq!(dt.second(), 45);
|
|
613
|
+
assert_eq!(dt.nanosecond(), 0);
|
|
614
|
+
}
|
|
594
615
|
}
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/parserinfo.rs
RENAMED
|
@@ -315,4 +315,44 @@ mod tests {
|
|
|
315
315
|
assert_eq!(do_tzoffset("est", Some(&info)), Some(-18000));
|
|
316
316
|
assert_eq!(do_month("January", Some(&info)), Some(1));
|
|
317
317
|
}
|
|
318
|
+
|
|
319
|
+
#[test]
|
|
320
|
+
fn test_parserinfo_hms() {
|
|
321
|
+
let info = ParserInfo::default();
|
|
322
|
+
assert_eq!(info.hms("hour"), Some(0));
|
|
323
|
+
assert_eq!(info.hms("HOURS"), Some(0));
|
|
324
|
+
assert_eq!(info.hms("minute"), Some(1));
|
|
325
|
+
assert_eq!(info.hms("s"), Some(2));
|
|
326
|
+
assert_eq!(info.hms("xyz"), None);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#[test]
|
|
330
|
+
fn test_parserinfo_ampm() {
|
|
331
|
+
let info = ParserInfo::default();
|
|
332
|
+
assert_eq!(info.ampm("am"), Some(0));
|
|
333
|
+
assert_eq!(info.ampm("AM"), Some(0));
|
|
334
|
+
assert_eq!(info.ampm("p"), Some(1));
|
|
335
|
+
assert_eq!(info.ampm("PM"), Some(1));
|
|
336
|
+
assert_eq!(info.ampm("xyz"), None);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#[test]
|
|
340
|
+
fn test_parserinfo_pertain() {
|
|
341
|
+
let info = ParserInfo::default();
|
|
342
|
+
assert!(info.pertain("of"));
|
|
343
|
+
assert!(info.pertain("OF"));
|
|
344
|
+
assert!(!info.pertain("xyz"));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
#[test]
|
|
348
|
+
fn test_dispatch_with_some_info() {
|
|
349
|
+
let info = ParserInfo::default();
|
|
350
|
+
assert!(do_jump(",", Some(&info)));
|
|
351
|
+
assert_eq!(do_weekday("Monday", Some(&info)), Some(0));
|
|
352
|
+
assert_eq!(do_month("January", Some(&info)), Some(1));
|
|
353
|
+
assert_eq!(do_hms("hour", Some(&info)), Some(0));
|
|
354
|
+
assert_eq!(do_ampm("AM", Some(&info)), Some(0));
|
|
355
|
+
assert!(do_pertain("of", Some(&info)));
|
|
356
|
+
assert!(do_utczone("UTC", Some(&info)));
|
|
357
|
+
}
|
|
318
358
|
}
|
|
@@ -984,22 +984,6 @@ mod tests {
|
|
|
984
984
|
assert_eq!(dt.second(), 45);
|
|
985
985
|
}
|
|
986
986
|
|
|
987
|
-
#[test]
|
|
988
|
-
fn test_parse_month_name() {
|
|
989
|
-
let dt = parse("January 15, 2024", false, false, None, None).unwrap();
|
|
990
|
-
assert_eq!(dt.year(), 2024);
|
|
991
|
-
assert_eq!(dt.month(), 1);
|
|
992
|
-
assert_eq!(dt.day(), 15);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
#[test]
|
|
996
|
-
fn test_parse_month_abbrev() {
|
|
997
|
-
let dt = parse("15 Jan 2024", false, false, None, None).unwrap();
|
|
998
|
-
assert_eq!(dt.year(), 2024);
|
|
999
|
-
assert_eq!(dt.month(), 1);
|
|
1000
|
-
assert_eq!(dt.day(), 15);
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
987
|
#[test]
|
|
1004
988
|
fn test_parse_us_format() {
|
|
1005
989
|
// MM/DD/YYYY (default, dayfirst=false)
|
|
@@ -1048,24 +1032,6 @@ mod tests {
|
|
|
1048
1032
|
assert_eq!(dt.nanosecond() / 1000, 123456);
|
|
1049
1033
|
}
|
|
1050
1034
|
|
|
1051
|
-
#[test]
|
|
1052
|
-
fn test_parse_tz_offset() {
|
|
1053
|
-
let res = parse_to_result("2024-01-15 10:30:45+05:30", false, false, None).unwrap();
|
|
1054
|
-
assert_eq!(res.tzoffset, Some(5 * 3600 + 30 * 60));
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
#[test]
|
|
1058
|
-
fn test_parse_tz_negative() {
|
|
1059
|
-
let res = parse_to_result("2024-01-15 10:30:45-0800", false, false, None).unwrap();
|
|
1060
|
-
assert_eq!(res.tzoffset, Some(-(8 * 3600)));
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
#[test]
|
|
1064
|
-
fn test_parse_weekday() {
|
|
1065
|
-
let res = parse_to_result("Monday, January 15, 2024", false, false, None).unwrap();
|
|
1066
|
-
assert_eq!(res.weekday, Some(0)); // Monday
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
1035
|
#[test]
|
|
1070
1036
|
fn test_parse_empty_string() {
|
|
1071
1037
|
assert!(parse("", false, false, None, None).is_err());
|
|
@@ -1162,19 +1128,6 @@ mod tests {
|
|
|
1162
1128
|
assert_eq!(res.tzoffset, Some(0));
|
|
1163
1129
|
}
|
|
1164
1130
|
|
|
1165
|
-
#[test]
|
|
1166
|
-
fn test_parse_tz_offset_compact_positive() {
|
|
1167
|
-
let res = parse_to_result("2024-01-15 10:30:00+0530", false, false, None).unwrap();
|
|
1168
|
-
assert_eq!(res.tzoffset, Some(5 * 3600 + 30 * 60));
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
#[test]
|
|
1172
|
-
fn test_parse_tz_offset_zero() {
|
|
1173
|
-
let res = parse_to_result("2024-01-15 10:30:00+0000", false, false, None).unwrap();
|
|
1174
|
-
assert_eq!(res.tzoffset, Some(0));
|
|
1175
|
-
assert_eq!(res.tzname, Some("UTC".into()));
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
1131
|
#[test]
|
|
1179
1132
|
fn test_parse_month_all_names() {
|
|
1180
1133
|
let months = [
|
|
@@ -1361,13 +1314,6 @@ mod tests {
|
|
|
1361
1314
|
assert_eq!(res.year, Some(2024));
|
|
1362
1315
|
}
|
|
1363
1316
|
|
|
1364
|
-
#[test]
|
|
1365
|
-
fn test_parse_time_hh_only() {
|
|
1366
|
-
// "10 hours" — number + HMS word sets hour
|
|
1367
|
-
let res = parse_to_result("2024-01-15 10 hours", false, false, None).unwrap();
|
|
1368
|
-
assert_eq!(res.hour, Some(10));
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
1317
|
#[test]
|
|
1372
1318
|
fn test_lookup_non_ascii_returns_false() {
|
|
1373
1319
|
assert!(!lookup_jump("日本語"));
|
|
@@ -1578,12 +1524,6 @@ mod tests {
|
|
|
1578
1524
|
assert_eq!(dt.year(), 2000);
|
|
1579
1525
|
}
|
|
1580
1526
|
|
|
1581
|
-
#[test]
|
|
1582
|
-
fn test_parse_two_digit_year_99() {
|
|
1583
|
-
let dt = parse("01/15/99", false, false, None, None).unwrap();
|
|
1584
|
-
assert_eq!(dt.year(), 1999);
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
1527
|
// ---- Leap year edge cases ----
|
|
1588
1528
|
|
|
1589
1529
|
#[test]
|
|
@@ -1672,14 +1612,6 @@ mod tests {
|
|
|
1672
1612
|
assert_eq!(dt.second(), 0);
|
|
1673
1613
|
}
|
|
1674
1614
|
|
|
1675
|
-
#[test]
|
|
1676
|
-
fn test_parse_end_of_day_235959() {
|
|
1677
|
-
let dt = parse("2024-01-15 23:59:59", false, false, None, None).unwrap();
|
|
1678
|
-
assert_eq!(dt.hour(), 23);
|
|
1679
|
-
assert_eq!(dt.minute(), 59);
|
|
1680
|
-
assert_eq!(dt.second(), 59);
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
1615
|
// ---- Fractional seconds precision ----
|
|
1684
1616
|
|
|
1685
1617
|
#[test]
|
|
@@ -1688,20 +1620,6 @@ mod tests {
|
|
|
1688
1620
|
assert_eq!(dt.nanosecond() / 1000, 100_000);
|
|
1689
1621
|
}
|
|
1690
1622
|
|
|
1691
|
-
#[test]
|
|
1692
|
-
fn test_parse_fractional_6_digits() {
|
|
1693
|
-
let dt = parse("2024-01-15 10:30:45.123456", false, false, None, None).unwrap();
|
|
1694
|
-
assert_eq!(dt.nanosecond() / 1000, 123_456);
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
// ---- ParseResult field_count ----
|
|
1698
|
-
|
|
1699
|
-
#[test]
|
|
1700
|
-
fn test_parse_to_result_minimal() {
|
|
1701
|
-
let res = parse_to_result("2024", false, false, None).unwrap();
|
|
1702
|
-
assert!(res.year.is_some());
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
1623
|
// ---- Only time ----
|
|
1706
1624
|
|
|
1707
1625
|
#[test]
|
|
@@ -1770,4 +1688,184 @@ mod tests {
|
|
|
1770
1688
|
assert_eq!(dt.day(), 10);
|
|
1771
1689
|
assert_eq!(dt.hour(), 8);
|
|
1772
1690
|
}
|
|
1691
|
+
|
|
1692
|
+
// ---- Coverage: compact parsing (try_parse_compact) ----
|
|
1693
|
+
|
|
1694
|
+
#[test]
|
|
1695
|
+
fn test_parse_compact_yyyymmdd() {
|
|
1696
|
+
let dt = parse("20240315", false, false, None, None).unwrap();
|
|
1697
|
+
assert_eq!(dt.year(), 2024);
|
|
1698
|
+
assert_eq!(dt.month(), 3);
|
|
1699
|
+
assert_eq!(dt.day(), 15);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
#[test]
|
|
1703
|
+
fn test_parse_compact_yyyymmddt_hhmmss() {
|
|
1704
|
+
// YYYYMMDD + T separator + HHMMSS (6-digit time after date)
|
|
1705
|
+
let dt = parse("20240115T103045", false, false, None, None).unwrap();
|
|
1706
|
+
assert_eq!(dt.year(), 2024);
|
|
1707
|
+
assert_eq!(dt.month(), 1);
|
|
1708
|
+
assert_eq!(dt.day(), 15);
|
|
1709
|
+
assert_eq!(dt.hour(), 10);
|
|
1710
|
+
assert_eq!(dt.minute(), 30);
|
|
1711
|
+
assert_eq!(dt.second(), 45);
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
#[test]
|
|
1715
|
+
fn test_parse_compact_yymmdd() {
|
|
1716
|
+
let default = NaiveDate::from_ymd_opt(2024, 1, 1)
|
|
1717
|
+
.unwrap()
|
|
1718
|
+
.and_hms_opt(0, 0, 0)
|
|
1719
|
+
.unwrap();
|
|
1720
|
+
let dt = parse("240315", false, false, Some(default), None).unwrap();
|
|
1721
|
+
assert_eq!(dt.year(), 2024);
|
|
1722
|
+
assert_eq!(dt.month(), 3);
|
|
1723
|
+
assert_eq!(dt.day(), 15);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
#[test]
|
|
1727
|
+
fn test_parse_compact_yyyymm() {
|
|
1728
|
+
// "202403" → YYMMDD (20/24/03) fails because month=24 > 12
|
|
1729
|
+
// → YYYYMM fallback: year=2024, month=03
|
|
1730
|
+
let dt = parse("202403", false, false, None, None).unwrap();
|
|
1731
|
+
assert_eq!(dt.year(), 2024);
|
|
1732
|
+
assert_eq!(dt.month(), 3);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
#[test]
|
|
1736
|
+
fn test_parse_compact_hhmmss_after_date() {
|
|
1737
|
+
// Date then T then HHMMSS
|
|
1738
|
+
let dt = parse("2024-03-15T103045", false, false, None, None).unwrap();
|
|
1739
|
+
assert_eq!(dt.hour(), 10);
|
|
1740
|
+
assert_eq!(dt.minute(), 30);
|
|
1741
|
+
assert_eq!(dt.second(), 45);
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
#[test]
|
|
1745
|
+
fn test_parse_compact_yyyymmddhh() {
|
|
1746
|
+
let dt = parse("2024031510", false, false, None, None).unwrap();
|
|
1747
|
+
assert_eq!(dt.year(), 2024);
|
|
1748
|
+
assert_eq!(dt.month(), 3);
|
|
1749
|
+
assert_eq!(dt.day(), 15);
|
|
1750
|
+
assert_eq!(dt.hour(), 10);
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
#[test]
|
|
1754
|
+
fn test_parse_compact_yyyymmddhh_with_minutes() {
|
|
1755
|
+
let dt = parse("2024031510:30", false, false, None, None).unwrap();
|
|
1756
|
+
assert_eq!(dt.year(), 2024);
|
|
1757
|
+
assert_eq!(dt.month(), 3);
|
|
1758
|
+
assert_eq!(dt.day(), 15);
|
|
1759
|
+
assert_eq!(dt.hour(), 10);
|
|
1760
|
+
assert_eq!(dt.minute(), 30);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
#[test]
|
|
1764
|
+
fn test_parse_compact_yyyymmddhh_with_seconds() {
|
|
1765
|
+
let dt = parse("2024031510:30:45", false, false, None, None).unwrap();
|
|
1766
|
+
assert_eq!(dt.year(), 2024);
|
|
1767
|
+
assert_eq!(dt.month(), 3);
|
|
1768
|
+
assert_eq!(dt.day(), 15);
|
|
1769
|
+
assert_eq!(dt.hour(), 10);
|
|
1770
|
+
assert_eq!(dt.minute(), 30);
|
|
1771
|
+
assert_eq!(dt.second(), 45);
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
#[test]
|
|
1775
|
+
fn test_parse_compact_yyyymmddhh_with_decimal_seconds() {
|
|
1776
|
+
let dt = parse("2024031510:30:45.5", false, false, None, None).unwrap();
|
|
1777
|
+
assert_eq!(dt.hour(), 10);
|
|
1778
|
+
assert_eq!(dt.minute(), 30);
|
|
1779
|
+
assert_eq!(dt.second(), 45);
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
// ---- Coverage: dot-separated date (try_parse_dot_date) ----
|
|
1783
|
+
|
|
1784
|
+
#[test]
|
|
1785
|
+
fn test_parse_dot_separated_date() {
|
|
1786
|
+
let dt = parse("2003.09.25", false, false, None, None).unwrap();
|
|
1787
|
+
assert_eq!(dt.year(), 2003);
|
|
1788
|
+
assert_eq!(dt.month(), 9);
|
|
1789
|
+
assert_eq!(dt.day(), 25);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
#[test]
|
|
1793
|
+
fn test_parse_dot_separated_date_dmy() {
|
|
1794
|
+
let dt = parse("25.09.2003", true, false, None, None).unwrap();
|
|
1795
|
+
assert_eq!(dt.year(), 2003);
|
|
1796
|
+
assert_eq!(dt.month(), 9);
|
|
1797
|
+
assert_eq!(dt.day(), 25);
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// ---- Coverage: timezone parsing ----
|
|
1801
|
+
|
|
1802
|
+
#[test]
|
|
1803
|
+
fn test_parse_tz_offset_single_token() {
|
|
1804
|
+
let res = parse_to_result("2024-01-15 10:30:45 +0500", false, false, None).unwrap();
|
|
1805
|
+
assert_eq!(res.tzoffset, Some(18000));
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
#[test]
|
|
1809
|
+
fn test_parse_tz_name_with_offset() {
|
|
1810
|
+
let res = parse_to_result("2024-01-15 10:30:45 EST -0500", false, false, None).unwrap();
|
|
1811
|
+
assert_eq!(res.tzname.as_deref(), Some("EST"));
|
|
1812
|
+
assert_eq!(res.tzoffset, Some(-18000));
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
#[test]
|
|
1816
|
+
fn test_parse_tz_with_parserinfo() {
|
|
1817
|
+
let mut info = ParserInfo::default();
|
|
1818
|
+
info.tzoffset.insert("est".into(), -18000);
|
|
1819
|
+
let res = parse_to_result("2024-01-15 10:30:45 EST", false, false, Some(&info)).unwrap();
|
|
1820
|
+
assert_eq!(res.tzname.as_deref(), Some("EST"));
|
|
1821
|
+
assert_eq!(res.tzoffset, Some(-18000));
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// ---- Coverage: pertain word ----
|
|
1825
|
+
|
|
1826
|
+
#[test]
|
|
1827
|
+
fn test_parse_pertain_of() {
|
|
1828
|
+
let dt = parse("15 of January 2024", false, false, None, None).unwrap();
|
|
1829
|
+
assert_eq!(dt.month(), 1);
|
|
1830
|
+
assert_eq!(dt.day(), 15);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// ---- Coverage: HMS assign with microseconds ----
|
|
1834
|
+
|
|
1835
|
+
#[test]
|
|
1836
|
+
fn test_parse_hms_label_no_space() {
|
|
1837
|
+
// "10h30m45s" — HMS labels immediately after numbers
|
|
1838
|
+
let res = parse_to_result("10h30m45s", false, false, None).unwrap();
|
|
1839
|
+
assert_eq!(res.hour, Some(10));
|
|
1840
|
+
assert_eq!(res.minute, Some(30));
|
|
1841
|
+
assert_eq!(res.second, Some(45));
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// ---- Coverage: convertyear edge cases ----
|
|
1845
|
+
|
|
1846
|
+
#[test]
|
|
1847
|
+
fn test_parse_two_digit_year_old() {
|
|
1848
|
+
// Two-digit year far in the past gets + 100
|
|
1849
|
+
let default = NaiveDate::from_ymd_opt(2024, 1, 1)
|
|
1850
|
+
.unwrap()
|
|
1851
|
+
.and_hms_opt(0, 0, 0)
|
|
1852
|
+
.unwrap();
|
|
1853
|
+
let dt = parse("01/15/70", false, false, Some(default), None).unwrap();
|
|
1854
|
+
// 70 should map to 1970 (2070 - 100 = 1970... or 2070 if < now+50)
|
|
1855
|
+
assert!(dt.year() == 1970 || dt.year() == 2070);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
// ---- Coverage: fast_parse_int / fast_parse_decimal edge cases ----
|
|
1859
|
+
|
|
1860
|
+
#[test]
|
|
1861
|
+
fn test_fast_parse_int_empty() {
|
|
1862
|
+
assert_eq!(fast_parse_int(""), None);
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
#[test]
|
|
1866
|
+
fn test_fast_parse_decimal_empty_frac() {
|
|
1867
|
+
// "10." — dot at end, empty frac part → (10, 0)
|
|
1868
|
+
assert_eq!(fast_parse_decimal("10."), Some((10, 0)));
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1773
1871
|
}
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/relativedelta.rs
RENAMED
|
@@ -1769,4 +1769,156 @@ mod tests {
|
|
|
1769
1769
|
set.insert(rd(0, 0, 1));
|
|
1770
1770
|
assert_eq!(set.len(), 2);
|
|
1771
1771
|
}
|
|
1772
|
+
|
|
1773
|
+
// ---- Coverage: Display impl for absolute fields ----
|
|
1774
|
+
|
|
1775
|
+
#[test]
|
|
1776
|
+
fn test_display_with_absolute_fields() {
|
|
1777
|
+
let delta = RelativeDelta::builder()
|
|
1778
|
+
.year(2024)
|
|
1779
|
+
.month(3)
|
|
1780
|
+
.day(15)
|
|
1781
|
+
.hour(10)
|
|
1782
|
+
.minute(30)
|
|
1783
|
+
.second(45)
|
|
1784
|
+
.microsecond(123456)
|
|
1785
|
+
.build()
|
|
1786
|
+
.unwrap();
|
|
1787
|
+
let s = delta.to_string();
|
|
1788
|
+
assert!(s.contains("year=2024"));
|
|
1789
|
+
assert!(s.contains("month=3"));
|
|
1790
|
+
assert!(s.contains("day=15"));
|
|
1791
|
+
assert!(s.contains("hour=10"));
|
|
1792
|
+
assert!(s.contains("minute=30"));
|
|
1793
|
+
assert!(s.contains("second=45"));
|
|
1794
|
+
assert!(s.contains("microsecond=123456"));
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
#[test]
|
|
1798
|
+
fn test_display_with_weekday() {
|
|
1799
|
+
use crate::common::MO;
|
|
1800
|
+
let delta = RelativeDelta::builder()
|
|
1801
|
+
.weekday(MO)
|
|
1802
|
+
.build()
|
|
1803
|
+
.unwrap();
|
|
1804
|
+
let s = delta.to_string();
|
|
1805
|
+
assert!(s.contains("weekday=MO"));
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
#[test]
|
|
1809
|
+
fn test_display_relative_parts() {
|
|
1810
|
+
let delta = rd(1, 2, 3);
|
|
1811
|
+
let s = delta.to_string();
|
|
1812
|
+
assert!(s.starts_with("relativedelta("));
|
|
1813
|
+
assert!(s.contains("years=+1"));
|
|
1814
|
+
assert!(s.contains("months=+2"));
|
|
1815
|
+
assert!(s.contains("days=+3"));
|
|
1816
|
+
assert!(s.ends_with(')'));
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// ---- Coverage: set_weeks ----
|
|
1820
|
+
|
|
1821
|
+
#[test]
|
|
1822
|
+
fn test_set_weeks() {
|
|
1823
|
+
let mut delta = RelativeDelta::builder()
|
|
1824
|
+
.days(10)
|
|
1825
|
+
.build()
|
|
1826
|
+
.unwrap();
|
|
1827
|
+
assert_eq!(delta.weeks(), 1);
|
|
1828
|
+
delta.set_weeks(3);
|
|
1829
|
+
assert_eq!(delta.days(), 24); // 3*7 + 3 = 24
|
|
1830
|
+
assert_eq!(delta.weeks(), 3);
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// ---- Coverage: leapdays with march+ in leap year ----
|
|
1834
|
+
|
|
1835
|
+
#[test]
|
|
1836
|
+
fn test_leapdays_in_leap_year() {
|
|
1837
|
+
let delta = RelativeDelta::builder()
|
|
1838
|
+
.leapdays(1)
|
|
1839
|
+
.build()
|
|
1840
|
+
.unwrap();
|
|
1841
|
+
let base = NaiveDate::from_ymd_opt(2024, 3, 1).unwrap(); // Leap year, March
|
|
1842
|
+
let result = delta.add_to_naive_date(base);
|
|
1843
|
+
// March in a leap year: leapdays should be added
|
|
1844
|
+
assert_eq!(result, NaiveDate::from_ymd_opt(2024, 3, 2).unwrap());
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// ---- Coverage: weekday_eq / normalize_n ----
|
|
1848
|
+
|
|
1849
|
+
#[test]
|
|
1850
|
+
fn test_weekday_equality_in_relativedelta() {
|
|
1851
|
+
use crate::common::{MO, TU};
|
|
1852
|
+
let a = RelativeDelta::builder()
|
|
1853
|
+
.weekday(MO)
|
|
1854
|
+
.build()
|
|
1855
|
+
.unwrap();
|
|
1856
|
+
let b = RelativeDelta::builder()
|
|
1857
|
+
.weekday(MO)
|
|
1858
|
+
.build()
|
|
1859
|
+
.unwrap();
|
|
1860
|
+
assert_eq!(a, b);
|
|
1861
|
+
|
|
1862
|
+
let c = RelativeDelta::builder()
|
|
1863
|
+
.weekday(TU)
|
|
1864
|
+
.build()
|
|
1865
|
+
.unwrap();
|
|
1866
|
+
assert_ne!(a, c);
|
|
1867
|
+
|
|
1868
|
+
let d = RelativeDelta::builder().build().unwrap();
|
|
1869
|
+
assert_ne!(a, d);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
#[test]
|
|
1873
|
+
fn test_hash_with_weekday() {
|
|
1874
|
+
use crate::common::MO;
|
|
1875
|
+
use std::collections::hash_map::DefaultHasher;
|
|
1876
|
+
|
|
1877
|
+
let a = RelativeDelta::builder()
|
|
1878
|
+
.weekday(MO)
|
|
1879
|
+
.build()
|
|
1880
|
+
.unwrap();
|
|
1881
|
+
let b = RelativeDelta::builder()
|
|
1882
|
+
.weekday(MO)
|
|
1883
|
+
.build()
|
|
1884
|
+
.unwrap();
|
|
1885
|
+
|
|
1886
|
+
let hash_a = {
|
|
1887
|
+
let mut h = DefaultHasher::new();
|
|
1888
|
+
a.hash(&mut h);
|
|
1889
|
+
h.finish()
|
|
1890
|
+
};
|
|
1891
|
+
let hash_b = {
|
|
1892
|
+
let mut h = DefaultHasher::new();
|
|
1893
|
+
b.hash(&mut h);
|
|
1894
|
+
h.finish()
|
|
1895
|
+
};
|
|
1896
|
+
assert_eq!(hash_a, hash_b);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
#[test]
|
|
1900
|
+
fn test_hash_with_microsecond() {
|
|
1901
|
+
use std::collections::hash_map::DefaultHasher;
|
|
1902
|
+
|
|
1903
|
+
let a = RelativeDelta::builder()
|
|
1904
|
+
.microsecond(500)
|
|
1905
|
+
.build()
|
|
1906
|
+
.unwrap();
|
|
1907
|
+
let b = RelativeDelta::builder()
|
|
1908
|
+
.microsecond(500)
|
|
1909
|
+
.build()
|
|
1910
|
+
.unwrap();
|
|
1911
|
+
|
|
1912
|
+
let hash_a = {
|
|
1913
|
+
let mut h = DefaultHasher::new();
|
|
1914
|
+
a.hash(&mut h);
|
|
1915
|
+
h.finish()
|
|
1916
|
+
};
|
|
1917
|
+
let hash_b = {
|
|
1918
|
+
let mut h = DefaultHasher::new();
|
|
1919
|
+
b.hash(&mut h);
|
|
1920
|
+
h.finish()
|
|
1921
|
+
};
|
|
1922
|
+
assert_eq!(hash_a, hash_b);
|
|
1923
|
+
}
|
|
1772
1924
|
}
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule/parse.rs
RENAMED
|
@@ -1107,4 +1107,29 @@ mod tests {
|
|
|
1107
1107
|
);
|
|
1108
1108
|
assert!(err.is_err());
|
|
1109
1109
|
}
|
|
1110
|
+
|
|
1111
|
+
// ---- Coverage: error paths in parse ----
|
|
1112
|
+
|
|
1113
|
+
#[test]
|
|
1114
|
+
fn test_rrulestr_invalid_param_without_equals() {
|
|
1115
|
+
// "FREQ" without "=" should error
|
|
1116
|
+
let result = rrulestr("FREQ", None, false, false, false);
|
|
1117
|
+
assert!(result.is_err());
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
#[test]
|
|
1121
|
+
fn test_rrulestr_unknown_param_name() {
|
|
1122
|
+
// "XRULE:FREQ=DAILY" — unknown type prefix
|
|
1123
|
+
let result = rrulestr("XRULE:FREQ=DAILY;COUNT=3", None, false, false, true);
|
|
1124
|
+
assert!(result.is_err());
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
#[test]
|
|
1128
|
+
fn test_rrulestr_line_unfold() {
|
|
1129
|
+
// Test RFC line unfolding (continuation lines starting with space)
|
|
1130
|
+
let input = "DTSTART:20200101T000000\nRRULE:FREQ=DAILY;\n COUNT=3";
|
|
1131
|
+
let result = rrulestr(input, None, false, false, true);
|
|
1132
|
+
assert!(result.is_ok());
|
|
1133
|
+
assert_eq!(result.unwrap().all().len(), 3);
|
|
1134
|
+
}
|
|
1110
1135
|
}
|
|
@@ -645,4 +645,42 @@ mod tests {
|
|
|
645
645
|
]
|
|
646
646
|
);
|
|
647
647
|
}
|
|
648
|
+
|
|
649
|
+
// ---- Coverage: Default impl ----
|
|
650
|
+
|
|
651
|
+
#[test]
|
|
652
|
+
fn test_rruleset_default() {
|
|
653
|
+
let rset: RRuleSet = Default::default();
|
|
654
|
+
assert!(rset.is_finite()); // empty set is finite
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// ---- Coverage: exrule exclusion ----
|
|
658
|
+
|
|
659
|
+
#[test]
|
|
660
|
+
fn test_rruleset_exrule() {
|
|
661
|
+
let mut rset = RRuleSet::new();
|
|
662
|
+
// Daily for 10 days
|
|
663
|
+
let rule = RRuleBuilder::new(Frequency::Daily)
|
|
664
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
665
|
+
.count(10)
|
|
666
|
+
.build()
|
|
667
|
+
.unwrap();
|
|
668
|
+
rset.rrule(rule);
|
|
669
|
+
|
|
670
|
+
// Exclude every other day (interval=2)
|
|
671
|
+
let exrule = RRuleBuilder::new(Frequency::Daily)
|
|
672
|
+
.dtstart(dt(2020, 1, 2, 0, 0, 0))
|
|
673
|
+
.interval(2)
|
|
674
|
+
.count(5)
|
|
675
|
+
.build()
|
|
676
|
+
.unwrap();
|
|
677
|
+
rset.exrule(exrule);
|
|
678
|
+
|
|
679
|
+
let results = rset.all();
|
|
680
|
+
// Should exclude Jan 2, 4, 6, 8, 10
|
|
681
|
+
assert!(results.contains(&dt(2020, 1, 1, 0, 0, 0)));
|
|
682
|
+
assert!(!results.contains(&dt(2020, 1, 2, 0, 0, 0)));
|
|
683
|
+
assert!(results.contains(&dt(2020, 1, 3, 0, 0, 0)));
|
|
684
|
+
assert!(!results.contains(&dt(2020, 1, 4, 0, 0, 0)));
|
|
685
|
+
}
|
|
648
686
|
}
|
|
@@ -3344,4 +3344,225 @@ mod tests {
|
|
|
3344
3344
|
let result = rule.take_slice(5, 5, 1);
|
|
3345
3345
|
assert!(result.is_empty());
|
|
3346
3346
|
}
|
|
3347
|
+
|
|
3348
|
+
// ---- Coverage: getter methods ----
|
|
3349
|
+
|
|
3350
|
+
#[test]
|
|
3351
|
+
fn test_rrule_getters() {
|
|
3352
|
+
let rule = RRuleBuilder::new(Frequency::Monthly)
|
|
3353
|
+
.dtstart(dt(2020, 1, 1, 9, 0, 0))
|
|
3354
|
+
.interval(2)
|
|
3355
|
+
.count(5)
|
|
3356
|
+
.bymonth(vec![1, 6])
|
|
3357
|
+
.bysetpos(vec![1, -1])
|
|
3358
|
+
.byweekno(vec![1, 52])
|
|
3359
|
+
.byyearday(vec![1, 365])
|
|
3360
|
+
.byeaster(vec![0])
|
|
3361
|
+
.byhour(vec![9, 17])
|
|
3362
|
+
.byminute(vec![0, 30])
|
|
3363
|
+
.bysecond(vec![0])
|
|
3364
|
+
.build()
|
|
3365
|
+
.unwrap();
|
|
3366
|
+
|
|
3367
|
+
assert_eq!(rule.freq(), Frequency::Monthly);
|
|
3368
|
+
assert_eq!(rule.dtstart(), dt(2020, 1, 1, 9, 0, 0));
|
|
3369
|
+
assert_eq!(rule.interval(), 2);
|
|
3370
|
+
assert_eq!(rule.wkst(), 0);
|
|
3371
|
+
assert_eq!(rule.count(), Some(5));
|
|
3372
|
+
assert_eq!(rule.until(), None);
|
|
3373
|
+
assert!(rule.bysetpos().is_some());
|
|
3374
|
+
assert!(rule.bymonth().is_some());
|
|
3375
|
+
assert!(rule.byyearday().is_some());
|
|
3376
|
+
assert!(rule.byeaster().is_some());
|
|
3377
|
+
assert!(rule.byweekno().is_some());
|
|
3378
|
+
assert!(rule.byhour().is_some());
|
|
3379
|
+
assert!(rule.byminute().is_some());
|
|
3380
|
+
assert!(rule.bysecond().is_some());
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
#[test]
|
|
3384
|
+
fn test_rrule_bymonthday_and_bynmonthday() {
|
|
3385
|
+
let rule = RRuleBuilder::new(Frequency::Monthly)
|
|
3386
|
+
.dtstart(dt(2020, 1, 15, 0, 0, 0))
|
|
3387
|
+
.count(3)
|
|
3388
|
+
.build()
|
|
3389
|
+
.unwrap();
|
|
3390
|
+
// Default bymonthday should be dtstart day
|
|
3391
|
+
assert!(!rule.bymonthday().is_empty());
|
|
3392
|
+
assert!(rule.bynmonthday().is_empty());
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
#[test]
|
|
3396
|
+
fn test_rrule_byweekday_getter() {
|
|
3397
|
+
use crate::common::{MO, FR};
|
|
3398
|
+
let rule = RRuleBuilder::new(Frequency::Weekly)
|
|
3399
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3400
|
+
.count(5)
|
|
3401
|
+
.byweekday(vec![MO, FR])
|
|
3402
|
+
.build()
|
|
3403
|
+
.unwrap();
|
|
3404
|
+
assert!(rule.byweekday().is_some());
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
#[test]
|
|
3408
|
+
fn test_rrule_bynweekday_getter() {
|
|
3409
|
+
use crate::common::MO;
|
|
3410
|
+
let rule = RRuleBuilder::new(Frequency::Monthly)
|
|
3411
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3412
|
+
.count(3)
|
|
3413
|
+
.byweekday(vec![MO.with_n(Some(2))])
|
|
3414
|
+
.build()
|
|
3415
|
+
.unwrap();
|
|
3416
|
+
assert!(rule.bynweekday().is_some());
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
// ---- Coverage: is_empty, all() panic ----
|
|
3420
|
+
|
|
3421
|
+
#[test]
|
|
3422
|
+
fn test_recurrence_is_empty() {
|
|
3423
|
+
// Rule that produces no results (month 13 doesn't exist)
|
|
3424
|
+
let rule = RRuleBuilder::new(Frequency::Daily)
|
|
3425
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3426
|
+
.count(0)
|
|
3427
|
+
.build()
|
|
3428
|
+
.unwrap();
|
|
3429
|
+
assert!(rule.is_empty());
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
#[test]
|
|
3433
|
+
#[should_panic(expected = "all() called on infinite")]
|
|
3434
|
+
fn test_all_panics_on_infinite() {
|
|
3435
|
+
let rule = RRuleBuilder::new(Frequency::Daily)
|
|
3436
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3437
|
+
.build()
|
|
3438
|
+
.unwrap();
|
|
3439
|
+
let _ = rule.all();
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
// ---- Coverage: len() ----
|
|
3443
|
+
|
|
3444
|
+
#[test]
|
|
3445
|
+
fn test_recurrence_len_finite_and_infinite() {
|
|
3446
|
+
let rule = RRuleBuilder::new(Frequency::Daily)
|
|
3447
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3448
|
+
.count(5)
|
|
3449
|
+
.build()
|
|
3450
|
+
.unwrap();
|
|
3451
|
+
assert_eq!(rule.len(), Some(5));
|
|
3452
|
+
|
|
3453
|
+
let infinite = RRuleBuilder::new(Frequency::Daily)
|
|
3454
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3455
|
+
.build()
|
|
3456
|
+
.unwrap();
|
|
3457
|
+
assert_eq!(infinite.len(), None);
|
|
3458
|
+
}
|
|
3459
|
+
|
|
3460
|
+
// ---- Coverage: Arc<RRule> iter ----
|
|
3461
|
+
|
|
3462
|
+
#[test]
|
|
3463
|
+
fn test_arc_rrule_iter() {
|
|
3464
|
+
let rule = Arc::new(
|
|
3465
|
+
RRuleBuilder::new(Frequency::Daily)
|
|
3466
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3467
|
+
.count(3)
|
|
3468
|
+
.build()
|
|
3469
|
+
.unwrap(),
|
|
3470
|
+
);
|
|
3471
|
+
let results: Vec<_> = rule.iter().collect();
|
|
3472
|
+
assert_eq!(results.len(), 3);
|
|
3473
|
+
assert!(rule.is_finite());
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
// ---- Coverage: Display impl for RRULE ----
|
|
3477
|
+
|
|
3478
|
+
#[test]
|
|
3479
|
+
fn test_rrule_display_with_bysetpos() {
|
|
3480
|
+
let rule = RRuleBuilder::new(Frequency::Monthly)
|
|
3481
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3482
|
+
.count(3)
|
|
3483
|
+
.bysetpos(vec![1, -1])
|
|
3484
|
+
.bymonth(vec![1, 6])
|
|
3485
|
+
.byyearday(vec![1, 100])
|
|
3486
|
+
.byweekno(vec![1, 52])
|
|
3487
|
+
.byhour(vec![9, 17])
|
|
3488
|
+
.byminute(vec![0, 30])
|
|
3489
|
+
.bysecond(vec![0])
|
|
3490
|
+
.byeaster(vec![0, -2])
|
|
3491
|
+
.build()
|
|
3492
|
+
.unwrap();
|
|
3493
|
+
let s = rule.to_string();
|
|
3494
|
+
assert!(s.contains("BYSETPOS=1,-1"));
|
|
3495
|
+
assert!(s.contains("BYMONTH="));
|
|
3496
|
+
assert!(s.contains("BYYEARDAY="));
|
|
3497
|
+
assert!(s.contains("BYWEEKNO="));
|
|
3498
|
+
assert!(s.contains("BYHOUR="));
|
|
3499
|
+
assert!(s.contains("BYMINUTE="));
|
|
3500
|
+
assert!(s.contains("BYSECOND="));
|
|
3501
|
+
assert!(s.contains("BYEASTER="));
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3504
|
+
#[test]
|
|
3505
|
+
fn test_rrule_display_with_bymonthday() {
|
|
3506
|
+
let rule = RRuleBuilder::new(Frequency::Monthly)
|
|
3507
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3508
|
+
.count(3)
|
|
3509
|
+
.bymonthday(vec![1, 15])
|
|
3510
|
+
.build()
|
|
3511
|
+
.unwrap();
|
|
3512
|
+
let s = rule.to_string();
|
|
3513
|
+
assert!(s.contains("BYMONTHDAY="));
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
// ---- Coverage: construct_byset empty error ----
|
|
3517
|
+
|
|
3518
|
+
#[test]
|
|
3519
|
+
fn test_construct_byset_empty() {
|
|
3520
|
+
// interval=2, start=0, byxxx=[1] → gcd(2,24)=2, 1%2=1≠0 → empty
|
|
3521
|
+
let result = construct_byset(0, &[1], 24, 2);
|
|
3522
|
+
assert!(result.is_err());
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
// ---- Coverage: mod_distance returns None ----
|
|
3526
|
+
|
|
3527
|
+
#[test]
|
|
3528
|
+
fn test_mod_distance_none() {
|
|
3529
|
+
// No matching value within base iterations
|
|
3530
|
+
let result = mod_distance(0, &[], 60, 1);
|
|
3531
|
+
assert_eq!(result, None);
|
|
3532
|
+
}
|
|
3533
|
+
|
|
3534
|
+
// ---- Coverage: days_in_month invalid month ----
|
|
3535
|
+
|
|
3536
|
+
#[test]
|
|
3537
|
+
fn test_days_in_month_invalid() {
|
|
3538
|
+
assert_eq!(days_in_month(2024, 0), 0);
|
|
3539
|
+
assert_eq!(days_in_month(2024, 13), 0);
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
// ---- Coverage: invalid wkst ----
|
|
3543
|
+
|
|
3544
|
+
#[test]
|
|
3545
|
+
fn test_builder_invalid_wkst_7() {
|
|
3546
|
+
let result = RRuleBuilder::new(Frequency::Daily)
|
|
3547
|
+
.dtstart(dt(2020, 1, 1, 0, 0, 0))
|
|
3548
|
+
.wkst(7)
|
|
3549
|
+
.build();
|
|
3550
|
+
assert!(result.is_err());
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// ---- Coverage: freq > Monthly with nth weekday (flattened to plain) ----
|
|
3554
|
+
|
|
3555
|
+
#[test]
|
|
3556
|
+
fn test_weekly_nth_weekday_flattened() {
|
|
3557
|
+
use crate::common::MO;
|
|
3558
|
+
// nth weekday with freq > Monthly → pushed to plain weekday
|
|
3559
|
+
let rule = RRuleBuilder::new(Frequency::Weekly)
|
|
3560
|
+
.dtstart(dt(2020, 1, 6, 0, 0, 0)) // Monday
|
|
3561
|
+
.count(3)
|
|
3562
|
+
.byweekday(vec![MO.with_n(Some(2))])
|
|
3563
|
+
.build()
|
|
3564
|
+
.unwrap();
|
|
3565
|
+
let results = rule.all();
|
|
3566
|
+
assert_eq!(results.len(), 3);
|
|
3567
|
+
}
|
|
3347
3568
|
}
|
|
@@ -230,4 +230,16 @@ mod tests {
|
|
|
230
230
|
set.insert(a);
|
|
231
231
|
assert!(set.contains(&b));
|
|
232
232
|
}
|
|
233
|
+
|
|
234
|
+
#[test]
|
|
235
|
+
fn test_display_name_method() {
|
|
236
|
+
let named = TzOffset::new(Some("EST"), -5 * 3600);
|
|
237
|
+
assert_eq!(named.display_name(), "EST");
|
|
238
|
+
|
|
239
|
+
let unnamed = TzOffset::new(None, 5 * 3600 + 1800);
|
|
240
|
+
assert_eq!(unnamed.display_name(), "UTC+05:30");
|
|
241
|
+
|
|
242
|
+
let utc = TzOffset::new(None, 0);
|
|
243
|
+
assert_eq!(utc.display_name(), "UTC");
|
|
244
|
+
}
|
|
233
245
|
}
|
|
@@ -315,20 +315,6 @@ mod tests {
|
|
|
315
315
|
// gettz()
|
|
316
316
|
// -----------------------------------------------------------------------
|
|
317
317
|
|
|
318
|
-
#[test]
|
|
319
|
-
fn test_gettz_utc() {
|
|
320
|
-
let tz = gettz(Some("UTC")).unwrap();
|
|
321
|
-
let d = dt(2024, 1, 1, 0, 0, 0);
|
|
322
|
-
assert_eq!(tz.utcoffset(d, false), 0);
|
|
323
|
-
assert_eq!(tz.tzname(d, false), "UTC");
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
#[test]
|
|
327
|
-
fn test_gettz_gmt() {
|
|
328
|
-
let tz = gettz(Some("GMT")).unwrap();
|
|
329
|
-
assert_eq!(tz.utcoffset(dt(2024, 1, 1, 0, 0, 0), false), 0);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
318
|
#[test]
|
|
333
319
|
fn test_gettz_iana_name() {
|
|
334
320
|
let tz = gettz(Some("America/New_York")).unwrap();
|
|
@@ -589,4 +575,42 @@ mod tests {
|
|
|
589
575
|
// July is WINTER: AEST (UTC+10)
|
|
590
576
|
assert_eq!(tz.utcoffset(dt(2024, 7, 15, 12, 0, 0), false), 10 * 3600);
|
|
591
577
|
}
|
|
578
|
+
|
|
579
|
+
// ---- Coverage: TimeZone dispatch for Local variant ----
|
|
580
|
+
|
|
581
|
+
#[test]
|
|
582
|
+
fn test_timezone_local_dst() {
|
|
583
|
+
let tz = gettz(None).unwrap(); // local timezone
|
|
584
|
+
let d = dt(2024, 7, 15, 12, 0, 0);
|
|
585
|
+
// Just verify it doesn't panic
|
|
586
|
+
let _ = tz.dst(d, false);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
#[test]
|
|
590
|
+
fn test_timezone_local_tzname() {
|
|
591
|
+
let tz = gettz(None).unwrap();
|
|
592
|
+
let d = dt(2024, 1, 15, 12, 0, 0);
|
|
593
|
+
let name = tz.tzname(d, false);
|
|
594
|
+
assert!(!name.is_empty());
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
#[test]
|
|
598
|
+
fn test_timezone_local_is_ambiguous() {
|
|
599
|
+
let tz = gettz(None).unwrap();
|
|
600
|
+
let d = dt(2024, 6, 15, 12, 0, 0);
|
|
601
|
+
// Just verify it doesn't panic; midday is never ambiguous
|
|
602
|
+
let _ = tz.is_ambiguous(d);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
#[test]
|
|
606
|
+
fn test_timezone_local_fromutc() {
|
|
607
|
+
let tz = gettz(None).unwrap();
|
|
608
|
+
let d = dt(2024, 1, 15, 12, 0, 0);
|
|
609
|
+
let wall = tz.fromutc(d);
|
|
610
|
+
// fromutc should shift by the local offset
|
|
611
|
+
let offset = tz.utcoffset(wall, false);
|
|
612
|
+
// The difference should match the offset
|
|
613
|
+
let diff = (wall - d).num_seconds() as i32;
|
|
614
|
+
assert_eq!(diff, offset);
|
|
615
|
+
}
|
|
592
616
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""dateutil_rs.easter - Easter date calculation."""
|
|
2
|
+
|
|
3
|
+
from dateutil_rs._native import (
|
|
4
|
+
EASTER_JULIAN,
|
|
5
|
+
EASTER_ORTHODOX,
|
|
6
|
+
EASTER_WESTERN,
|
|
7
|
+
easter,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"EASTER_JULIAN",
|
|
12
|
+
"EASTER_ORTHODOX",
|
|
13
|
+
"EASTER_WESTERN",
|
|
14
|
+
"easter",
|
|
15
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""dateutil_rs.rrule - RFC 5545 recurrence rules."""
|
|
2
|
+
|
|
3
|
+
from dateutil_rs._native import (
|
|
4
|
+
DAILY,
|
|
5
|
+
HOURLY,
|
|
6
|
+
MINUTELY,
|
|
7
|
+
MONTHLY,
|
|
8
|
+
SECONDLY,
|
|
9
|
+
WEEKLY,
|
|
10
|
+
YEARLY,
|
|
11
|
+
rrule,
|
|
12
|
+
rruleset,
|
|
13
|
+
rrulestr,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"DAILY",
|
|
18
|
+
"HOURLY",
|
|
19
|
+
"MINUTELY",
|
|
20
|
+
"MONTHLY",
|
|
21
|
+
"SECONDLY",
|
|
22
|
+
"WEEKLY",
|
|
23
|
+
"YEARLY",
|
|
24
|
+
"rrule",
|
|
25
|
+
"rruleset",
|
|
26
|
+
"rrulestr",
|
|
27
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""dateutil_rs.tz - Timezone types and utilities."""
|
|
2
|
+
|
|
3
|
+
from dateutil_rs._native import (
|
|
4
|
+
datetime_ambiguous,
|
|
5
|
+
datetime_exists,
|
|
6
|
+
gettz,
|
|
7
|
+
resolve_imaginary,
|
|
8
|
+
tzfile,
|
|
9
|
+
tzlocal,
|
|
10
|
+
tzoffset,
|
|
11
|
+
tzutc,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"datetime_ambiguous",
|
|
16
|
+
"datetime_exists",
|
|
17
|
+
"gettz",
|
|
18
|
+
"resolve_imaginary",
|
|
19
|
+
"tzfile",
|
|
20
|
+
"tzlocal",
|
|
21
|
+
"tzoffset",
|
|
22
|
+
"tzutc",
|
|
23
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/benches/benchmarks.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/parser/tokenizer.rs
RENAMED
|
File without changes
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-core/src/rrule/iter.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_dateutil_rs-0.1.0 → python_dateutil_rs-0.1.1}/crates/dateutil-py/src/py/relativedelta.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|