Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_datime/tests/rfc9557_demo.rs

10.7 KiB, 4 runs

created by r1870400018:8660, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
2//! Anthropic Claude
3
4use oxedyne_fe2o3_datime::{
5 time::{CalClock, CalClockZone},
6 clock::ClockTime,
7 calendar::CalendarDate,
8 format::{Rfc9557Format, Rfc9557Config, PrecisionLevel},
9};
10
11use oxedyne_fe2o3_core::prelude::*;
12
13#[test]
14fn test_rfc9557_basic_format() -> Outcome<()> {
15 let zone = CalClockZone::utc();
16 let calclock = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 123_456_789, zone));
17
18 // Basic RFC 3339 format (no timezone name).
19 let basic = res!(calclock.to_rfc9557_basic());
20 assert!(basic.starts_with("2024-06-15T14:30:45"));
21 assert!(basic.ends_with("Z"));
22 assert!(!basic.contains('['));
23
24 println!("RFC 9557 Basic: {}", basic);
25 Ok(())
26}
27
28#[test]
29fn test_rfc9557_extended_format() -> Outcome<()> {
30 let zone = res!(CalClockZone::new("America/New_York"));
31 let calclock = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 123_456_789, zone));
32
33 // Extended RFC 9557 format (with timezone name).
34 let extended = res!(calclock.to_rfc9557_extended());
35 assert!(extended.starts_with("2024-06-15T14:30:45"));
36 assert!(extended.contains("[America/New_York]"));
37
38 println!("RFC 9557 Extended: {}", extended);
39 Ok(())
40}
41
42#[test]
43fn test_rfc9557_precision_indicators() -> Outcome<()> {
44 let zone = CalClockZone::utc();
45 let calclock = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 0, zone));
46
47 // Test different precision levels.
48 let config_approximate = Rfc9557Config {
49 include_precision_indicators: true,
50 precision_level: PrecisionLevel::Approximate,
51 ..Default::default()
52 };
53
54 let approximate = res!(calclock.to_rfc9557_with_config(&config_approximate));
55 assert!(approximate.contains('~'));
56 println!("Approximate time: {}", approximate);
57
58 let config_uncertain = Rfc9557Config {
59 include_precision_indicators: true,
60 precision_level: PrecisionLevel::Uncertain,
61 ..Default::default()
62 };
63
64 let uncertain = res!(calclock.to_rfc9557_with_config(&config_uncertain));
65 assert!(uncertain.contains('%'));
66 println!("Uncertain time: {}", uncertain);
67
68 Ok(())
69}
70
71#[test]
72fn test_rfc9557_nanosecond_handling() -> Outcome<()> {
73 let zone = CalClockZone::utc();
74
75 // Test with zero nanoseconds.
76 let calclock_no_nanos = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 0, zone.clone()));
77 let basic_no_nanos = res!(calclock_no_nanos.to_rfc9557_basic());
78 assert!(!basic_no_nanos.contains('.'));
79
80 // Test with nanoseconds.
81 let calclock_with_nanos = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 123_456_789, zone.clone()));
82 let basic_with_nanos = res!(calclock_with_nanos.to_rfc9557_basic());
83 assert!(basic_with_nanos.contains(".123456789"));
84
85 // Test forcing nanoseconds even when zero.
86 let config_force_nanos = Rfc9557Config {
87 always_include_nanoseconds: true,
88 ..Default::default()
89 };
90 let forced_nanos = res!(calclock_no_nanos.to_rfc9557_with_config(&config_force_nanos));
91 assert!(forced_nanos.contains(".000000000"));
92
93 println!("No nanos: {}", basic_no_nanos);
94 println!("With nanos: {}", basic_with_nanos);
95 println!("Forced nanos: {}", forced_nanos);
96
97 Ok(())
98}
99
100#[test]
101fn test_rfc9557_timezone_offset_formats() -> Outcome<()> {
102 // Test UTC representation.
103 let utc_zone = CalClockZone::utc();
104 let utc_calclock = res!(CalClock::new(2024, 6, 15, 14, 30, 0, 0, utc_zone));
105 let utc_format = res!(utc_calclock.to_rfc9557_basic());
106 assert!(utc_format.ends_with('Z'));
107
108 // Test with use_z_for_utc disabled.
109 let config_no_z = Rfc9557Config {
110 use_z_for_utc: false,
111 ..Default::default()
112 };
113 let utc_no_z = res!(utc_calclock.to_rfc9557_with_config(&config_no_z));
114 assert!(utc_no_z.contains("+00:00"));
115
116 println!("UTC with Z: {}", utc_format);
117 println!("UTC without Z: {}", utc_no_z);
118
119 Ok(())
120}
121
122#[test]
123fn test_rfc9557_parsing_basic() -> Outcome<()> {
124 // Test parsing basic RFC 3339 format.
125 let input = "2024-06-15T14:30:45Z";
126 let parsed = res!(CalClock::from_rfc9557(input));
127
128 assert_eq!(parsed.year(), 2024);
129 assert_eq!(parsed.month(), 6);
130 assert_eq!(parsed.day(), 15);
131 assert_eq!(parsed.hour(), 14);
132 assert_eq!(parsed.minute(), 30);
133 assert_eq!(parsed.second(), 45);
134 assert_eq!(parsed.zone().id(), "UTC");
135
136 println!("Parsed basic: {:?}", parsed);
137 Ok(())
138}
139
140#[test]
141fn test_rfc9557_parsing_extended() -> Outcome<()> {
142 // Test parsing extended format with timezone.
143 let input = "2024-06-15T14:30:45.123456789-04:00[America/New_York]";
144 let parsed = res!(CalClock::from_rfc9557(input));
145
146 assert_eq!(parsed.year(), 2024);
147 assert_eq!(parsed.month(), 6);
148 assert_eq!(parsed.day(), 15);
149 assert_eq!(parsed.hour(), 14);
150 assert_eq!(parsed.minute(), 30);
151 assert_eq!(parsed.second(), 45);
152 assert_eq!(parsed.nanosecond(), 123_456_789);
153 assert_eq!(parsed.zone().id(), "America/New_York");
154
155 println!("Parsed extended: {:?}", parsed);
156 Ok(())
157}
158
159#[test]
160fn test_rfc9557_parsing_with_precision() -> Outcome<()> {
161 // Test parsing with precision indicators.
162 let inputs = [
163 "2024-06-15T14:30:45~[UTC]",
164 "2024-06-15T14:30:45%[UTC]",
165 "2024-06-15T14:30:45@[UTC]",
166 "2024-06-15T14:30:45*[UTC]",
167 ];
168
169 for input in &inputs {
170 let parsed = res!(CalClock::from_rfc9557(input));
171 assert_eq!(parsed.year(), 2024);
172 assert_eq!(parsed.zone().id(), "UTC");
173 println!("Parsed with precision: {} -> {:?}", input, parsed);
174 }
175
176 Ok(())
177}
178
179#[test]
180fn test_rfc9557_round_trip() -> Outcome<()> {
181 let zone = res!(CalClockZone::new("Europe/London"));
182 let original = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 123_456_789, zone));
183
184 // Test round-trip with extended format.
185 let serialised = res!(original.to_rfc9557_extended());
186 let parsed = res!(CalClock::from_rfc9557(&serialised));
187
188 // Should preserve the same instant in time.
189 let original_utc = res!(original.to_millis());
190 let parsed_utc = res!(parsed.to_millis());
191 assert_eq!(original_utc, parsed_utc);
192
193 // Should preserve timezone.
194 assert_eq!(original.zone().id(), parsed.zone().id());
195
196 println!("Original: {}", res!(original.to_rfc9557_extended()));
197 println!("Serialised: {}", serialised);
198 println!("Parsed: {}", res!(parsed.to_rfc9557_extended()));
199
200 Ok(())
201}
202
203#[test]
204fn test_rfc9557_time_only() -> Outcome<()> {
205 let zone = res!(CalClockZone::new("Asia/Tokyo"));
206 let time = res!(ClockTime::new(14, 30, 45, 123_456_789, zone));
207
208 // Test time-only formatting.
209 let basic = res!(time.to_rfc9557_basic());
210 let extended = res!(time.to_rfc9557_extended());
211
212 assert!(basic.starts_with("14:30:45"));
213 assert!(!basic.contains('['));
214 assert!(extended.contains("[Asia/Tokyo]"));
215
216 println!("Time basic: {}", basic);
217 println!("Time extended: {}", extended);
218
219 // Test parsing.
220 let parsed = res!(ClockTime::from_rfc9557(&extended));
221 assert_eq!(parsed.hour().of(), 14);
222 assert_eq!(parsed.zone().id(), "Asia/Tokyo");
223
224 Ok(())
225}
226
227#[test]
228fn test_rfc9557_date_only() -> Outcome<()> {
229 let zone = res!(CalClockZone::new("Australia/Sydney"));
230 let date = res!(CalendarDate::new(2024, 6, 15, zone));
231
232 // Test date-only formatting.
233 let basic = res!(date.to_rfc9557_basic());
234 let extended = res!(date.to_rfc9557_extended());
235
236 assert_eq!(basic, "2024-06-15");
237 assert_eq!(extended, "2024-06-15[Australia/Sydney]");
238
239 println!("Date basic: {}", basic);
240 println!("Date extended: {}", extended);
241
242 // Test parsing.
243 let parsed = res!(CalendarDate::from_rfc9557(&extended));
244 assert_eq!(parsed.year(), 2024);
245 assert_eq!(parsed.month(), 6);
246 assert_eq!(parsed.day(), 15);
247 assert_eq!(parsed.zone().id(), "Australia/Sydney");
248
249 Ok(())
250}
251
252#[test]
253fn test_rfc9557_utility_functions() -> Outcome<()> {
254 use oxedyne_fe2o3_datime::format::rfc9557::utils;
255
256 // Test validation.
257 let valid_timestamp = "2024-06-15T14:30:45Z[UTC]";
258 assert!(utils::validate_rfc9557(valid_timestamp).is_ok());
259
260 let invalid_timestamp = "not-a-timestamp";
261 assert!(utils::validate_rfc9557(invalid_timestamp).is_err());
262
263 // Test timezone extraction.
264 assert_eq!(utils::extract_timezone_name(valid_timestamp), Some("UTC"));
265 assert_eq!(utils::extract_timezone_name("2024-06-15T14:30:45Z"), None);
266
267 // Test precision extraction.
268 assert_eq!(utils::extract_precision_indicator("2024-06-15T14:30:45~"), Some(PrecisionLevel::Approximate));
269 assert_eq!(utils::extract_precision_indicator("2024-06-15T14:30:45"), None);
270
271 println!("✓ Utility functions work correctly");
272 Ok(())
273}
274
275#[test]
276fn test_rfc9557_complex_scenarios() -> Outcome<()> {
277 // Test with different timezone formats.
278 let scenarios = [
279 ("UTC", "2024-06-15T14:30:45Z[UTC]"),
280 ("America/New_York", "2024-06-15T14:30:45-04:00[America/New_York]"), // Summer time.
281 ("Europe/Paris", "2024-06-15T14:30:45+02:00[Europe/Paris]"), // Summer time.
282 ("Asia/Tokyo", "2024-06-15T14:30:45+09:00[Asia/Tokyo]"),
283 ];
284
285 for (zone_name, expected_pattern) in &scenarios {
286 let zone = res!(CalClockZone::new(*zone_name));
287 let calclock = res!(CalClock::new(2024, 6, 15, 14, 30, 45, 0, zone));
288 let formatted = res!(calclock.to_rfc9557_extended());
289
290 assert!(formatted.starts_with("2024-06-15T14:30:45"));
291 assert!(formatted.contains(&format!("[{}]", zone_name)));
292
293 // Test parsing back.
294 let parsed = res!(CalClock::from_rfc9557(&formatted));
295 assert_eq!(parsed.zone().id(), *zone_name);
296
297 println!("Zone {}: {}", zone_name, formatted);
298 }
299
300 Ok(())
301}
302
303pub fn test_rfc9557_integration(filter: &str) -> Outcome<()> {
304 println!("=== RFC 9557 Timezone-Preserving Serialisation Demo ===");
305
306 res!(test_it(filter, &["basic", "all", "rfc9557"], || {
307 test_rfc9557_basic_format()
308 }));
309
310 res!(test_it(filter, &["extended", "all", "rfc9557"], || {
311 test_rfc9557_extended_format()
312 }));
313
314 res!(test_it(filter, &["precision", "all", "rfc9557"], || {
315 test_rfc9557_precision_indicators()
316 }));
317
318 res!(test_it(filter, &["nanoseconds", "all", "rfc9557"], || {
319 test_rfc9557_nanosecond_handling()
320 }));
321
322 res!(test_it(filter, &["offsets", "all", "rfc9557"], || {
323 test_rfc9557_timezone_offset_formats()
324 }));
325
326 res!(test_it(filter, &["parsing_basic", "all", "rfc9557"], || {
327 test_rfc9557_parsing_basic()
328 }));
329
330 res!(test_it(filter, &["parsing_extended", "all", "rfc9557"], || {
331 test_rfc9557_parsing_extended()
332 }));
333
334 res!(test_it(filter, &["parsing_precision", "all", "rfc9557"], || {
335 test_rfc9557_parsing_with_precision()
336 }));
337
338 res!(test_it(filter, &["round_trip", "all", "rfc9557"], || {
339 test_rfc9557_round_trip()
340 }));
341
342 res!(test_it(filter, &["time_only", "all", "rfc9557"], || {
343 test_rfc9557_time_only()
344 }));
345
346 res!(test_it(filter, &["date_only", "all", "rfc9557"], || {
347 test_rfc9557_date_only()
348 }));
349
350 res!(test_it(filter, &["utilities", "all", "rfc9557"], || {
351 test_rfc9557_utility_functions()
352 }));
353
354 res!(test_it(filter, &["complex", "all", "rfc9557"], || {
355 test_rfc9557_complex_scenarios()
356 }));
357
358 println!("✓ All RFC 9557 tests passed!");
359 Ok(())
360}
361
362fn test_it<F>(filter: &str, keywords: &[&str], test_fn: F) -> Outcome<()>
363where
364 F: FnOnce() -> Outcome<()>,
365{
366 if keywords.iter().any(|&kw| filter.contains(kw)) {
367 test_fn()
368 } else {
369 Ok(())
370 }
371}