1use crate::SmartString;
79use quanta::Clock;
80use std::time::{SystemTime, UNIX_EPOCH};
81
82pub fn get_timestamp_ms() -> u64 {
84 SystemTime::now()
85 .duration_since(UNIX_EPOCH)
86 .unwrap_or_else(|e| {
87 log::error!("CRITICAL: System clock error: {e}. Attempting recovery...");
90
91 SystemTime::now()
96 .duration_since(UNIX_EPOCH)
97 .unwrap_or_else(|e2| {
98 log::error!(
99 "CRITICAL: System clock recovery failed: {e2}. System is unstable!"
100 );
101
102 #[cfg(feature = "panic-on-clock-error")]
103 {
104 panic!("System clock is severely broken, cannot continue: {e2}");
107 }
108
109 #[cfg(not(feature = "panic-on-clock-error"))]
110 {
111 log::error!("Using fallback timestamp due to clock error");
114 std::time::Duration::from_secs(u64::MAX / 1000 - 1)
116 }
117 })
118 })
119 .as_millis() as u64
120}
121
122pub fn get_timestamp_ns_result() -> Result<u64, std::time::SystemTimeError> {
147 let duration = SystemTime::now().duration_since(UNIX_EPOCH)?;
148 Ok(duration.as_nanos() as u64)
149}
150
151pub fn get_quanta_timestamp_ns(clock: &Clock) -> u64 {
170 clock.raw()
173}
174
175pub fn get_monotonic_timestamp_ns() -> u64 {
182 let clock = Clock::new();
183 clock.raw() }
185
186pub fn get_epoch_timestamp_ns() -> u64 {
204 SystemTime::now()
205 .duration_since(UNIX_EPOCH)
206 .unwrap_or_else(|e| {
207 log::error!("CRITICAL: System clock error: {e}. Attempting recovery...");
210
211 SystemTime::now()
216 .duration_since(UNIX_EPOCH)
217 .unwrap_or_else(|e2| {
218 log::error!(
219 "CRITICAL: System clock recovery failed: {e2}. System is unstable!"
220 );
221
222 #[cfg(feature = "panic-on-clock-error")]
223 {
224 panic!("System clock is severely broken, cannot continue: {e2}");
227 }
228
229 #[cfg(not(feature = "panic-on-clock-error"))]
230 {
231 log::error!("Using fallback timestamp due to clock error");
234 std::time::Duration::from_nanos(u64::MAX - 1_000_000_000)
236 }
237 })
238 })
239 .as_nanos() as u64
240}
241
242pub fn parse_rfc3339_timestamp(timestamp_str: &str) -> Result<u64, String> {
249 if timestamp_str.is_empty() {
254 return Err("Empty timestamp string".to_string());
255 }
256
257 if timestamp_str.len() < 19 || !timestamp_str.contains('T') {
259 return Err(format!("Invalid RFC3339 format: {timestamp_str}"));
260 }
261
262 let parts: Vec<&str> = timestamp_str.split('T').collect();
264 if parts.len() != 2 {
265 return Err(format!(
266 "Invalid RFC3339 format: missing 'T' separator in {timestamp_str}"
267 ));
268 }
269
270 let date_part = parts[0];
271 let time_part = parts[1];
272
273 let date_parts: Vec<&str> = date_part.split('-').collect();
275 if date_parts.len() != 3 {
276 return Err(format!("Invalid date format in {timestamp_str}"));
277 }
278
279 let year: i32 = date_parts[0]
280 .parse()
281 .map_err(|_| format!("Invalid year in {timestamp_str}"))?;
282 let month: u32 = date_parts[1]
283 .parse()
284 .map_err(|_| format!("Invalid month in {timestamp_str}"))?;
285 let day: u32 = date_parts[2]
286 .parse()
287 .map_err(|_| format!("Invalid day in {timestamp_str}"))?;
288
289 if !(1..=12).contains(&month) {
291 return Err(format!("Invalid month {month} in {timestamp_str}"));
292 }
293 if !(1..=31).contains(&day) {
294 return Err(format!("Invalid day {day} in {timestamp_str}"));
295 }
296
297 let (time_str, tz_offset_secs) = if let Some(stripped) = time_part.strip_suffix('Z') {
299 (stripped, 0i64)
301 } else if let Some(plus_idx) = time_part.rfind('+') {
302 let tz_str = &time_part[plus_idx + 1..];
304 let offset = parse_timezone_offset(tz_str)?;
305 (&time_part[..plus_idx], -offset) } else if let Some(minus_idx) = time_part.rfind('-') {
307 let tz_str = &time_part[minus_idx + 1..];
309 let offset = parse_timezone_offset(tz_str)?;
310 (&time_part[..minus_idx], offset) } else {
312 return Err(format!("Missing timezone in {timestamp_str}"));
313 };
314
315 let (time_base, subsec_nanos) = if let Some(dot_idx) = time_str.find('.') {
317 let subsec_str = &time_str[dot_idx + 1..];
318 let subsec_nanos = parse_subseconds(subsec_str)?;
319 (&time_str[..dot_idx], subsec_nanos)
320 } else {
321 (time_str, 0u32)
322 };
323
324 let time_parts: Vec<&str> = time_base.split(':').collect();
325 if time_parts.len() != 3 {
326 return Err(format!("Invalid time format in {timestamp_str}"));
327 }
328
329 let hour: u32 = time_parts[0]
330 .parse()
331 .map_err(|_| format!("Invalid hour in {timestamp_str}"))?;
332 let minute: u32 = time_parts[1]
333 .parse()
334 .map_err(|_| format!("Invalid minute in {timestamp_str}"))?;
335 let second: u32 = time_parts[2]
336 .parse()
337 .map_err(|_| format!("Invalid second in {timestamp_str}"))?;
338
339 if hour > 23 {
341 return Err(format!("Invalid hour {hour} in {timestamp_str}"));
342 }
343 if minute > 59 {
344 return Err(format!("Invalid minute {minute} in {timestamp_str}"));
345 }
346 if second > 59 {
347 return Err(format!("Invalid second {second} in {timestamp_str}"));
348 }
349
350 let days_since_epoch = days_since_unix_epoch(year, month, day)?;
352
353 let total_secs = (days_since_epoch as i64 * 86400)
355 + (hour as i64 * 3600)
356 + (minute as i64 * 60)
357 + (second as i64)
358 + tz_offset_secs;
359
360 if total_secs < 0 {
362 return Err(format!("Timestamp before Unix epoch: {timestamp_str}"));
363 }
364
365 let total_nanos = (total_secs as u64)
367 .checked_mul(1_000_000_000)
368 .and_then(|secs_as_nanos| secs_as_nanos.checked_add(subsec_nanos as u64))
369 .ok_or_else(|| format!("Timestamp overflow: timestamp too large to represent as nanoseconds: {timestamp_str}"))?;
370
371 Ok(total_nanos)
372}
373
374fn parse_timezone_offset(tz_str: &str) -> Result<i64, String> {
376 let (hours_str, minutes_str) = tz_str
377 .split_once(':')
378 .ok_or_else(|| format!("Invalid timezone offset format: {tz_str}"))?;
379
380 let hours: i64 = hours_str
381 .parse()
382 .map_err(|_| format!("Invalid timezone hours: {hours_str}"))?;
383 let minutes: i64 = minutes_str
384 .parse()
385 .map_err(|_| format!("Invalid timezone minutes: {minutes_str}"))?;
386
387 if !(0..=23).contains(&hours) {
389 return Err(format!("Timezone hours must be 0-23, got: {hours}"));
390 }
391 if !(0..=59).contains(&minutes) {
392 return Err(format!("Timezone minutes must be 0-59, got: {minutes}"));
393 }
394
395 Ok(hours * 3600 + minutes * 60)
396}
397
398fn parse_subseconds(subsec_str: &str) -> Result<u32, String> {
400 let mut digits = subsec_str
403 .chars()
404 .take(9)
405 .map(|c| {
406 c.to_digit(10)
407 .ok_or_else(|| format!("Invalid subsecond digit: {c}"))
408 })
409 .collect::<Result<Vec<_>, _>>()?;
410
411 while digits.len() < 9 {
413 digits.push(0);
414 }
415
416 let mut nanos = 0u32;
417 let mut multiplier = 100_000_000u32;
418 for digit in digits {
419 nanos += digit * multiplier;
420 multiplier /= 10;
421 }
422
423 Ok(nanos)
424}
425
426fn days_since_unix_epoch(year: i32, month: u32, day: u32) -> Result<u32, String> {
428 let days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
430
431 let max_days = if month == 2 && is_leap_year(year) {
433 29
434 } else {
435 days_in_month[(month - 1) as usize]
436 };
437
438 if day < 1 || day > max_days {
439 return Err(format!(
440 "Invalid day {day} for month {month} in year {year}"
441 ));
442 }
443
444 let mut total_days = 0u32;
446
447 for y in 1970..year {
449 if is_leap_year(y) {
450 total_days += 366;
451 } else {
452 total_days += 365;
453 }
454 }
455
456 for m in 1..month {
458 total_days += days_in_month[(m - 1) as usize];
459 if m == 2 && is_leap_year(year) {
461 total_days += 1;
462 }
463 }
464
465 total_days += day - 1; Ok(total_days)
469}
470
471const fn is_leap_year(year: i32) -> bool {
473 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
474}
475
476pub fn parse_timestamp_with_fallback(timestamp_str: &str, context: &str) -> u64 {
479 match parse_rfc3339_timestamp(timestamp_str) {
480 Ok(timestamp_ns) => timestamp_ns,
481 Err(e) => {
482 log::warn!(
483 "Failed to parse {context} timestamp '{timestamp_str}': {e}. Using system time fallback"
484 );
485 get_timestamp_ns_result().unwrap_or_else(|_| {
486 SystemTime::now()
487 .duration_since(UNIX_EPOCH)
488 .unwrap_or_default()
489 .as_nanos() as u64
490 })
491 }
492 }
493}
494
495pub fn get_timestamp_ms_safe() -> Option<u64> {
500 match SystemTime::now().duration_since(UNIX_EPOCH) {
501 Ok(d) => Some(d.as_millis() as u64),
502 Err(e) => {
503 log::error!("System clock error: {e}. Attempting recovery...");
504
505 match SystemTime::now().duration_since(UNIX_EPOCH) {
509 Ok(d) => Some(d.as_millis() as u64),
510 Err(e2) => {
511 log::error!("System clock recovery failed: {e2}. Returning None.");
512 None
513 }
514 }
515 }
516 }
517}
518
519pub fn get_timestamp_ns_safe() -> Option<u64> {
524 match SystemTime::now().duration_since(UNIX_EPOCH) {
525 Ok(d) => Some(d.as_nanos() as u64),
526 Err(e) => {
527 log::error!("System clock error: {e}. Attempting recovery...");
528
529 match SystemTime::now().duration_since(UNIX_EPOCH) {
533 Ok(d) => Some(d.as_nanos() as u64),
534 Err(e2) => {
535 log::error!("System clock recovery failed: {e2}. Returning None.");
536 None
537 }
538 }
539 }
540 }
541}
542
543pub fn get_epoch_timestamp_ns_safe() -> Option<u64> {
548 match SystemTime::now().duration_since(UNIX_EPOCH) {
549 Ok(d) => Some(d.as_nanos() as u64),
550 Err(e) => {
551 log::error!("System clock error: {e}. Attempting recovery...");
552
553 match SystemTime::now().duration_since(UNIX_EPOCH) {
557 Ok(d) => Some(d.as_nanos() as u64),
558 Err(e2) => {
559 log::error!("System clock recovery failed: {e2}. Returning None.");
560 None
561 }
562 }
563 }
564 }
565}
566
567pub fn system_time_to_nanos(time: SystemTime) -> u64 {
591 time.duration_since(UNIX_EPOCH)
592 .unwrap_or_else(|e| {
593 log::error!(
595 "CRITICAL: Invalid system time provided (before Unix epoch): {e}. \
596 This indicates a serious system clock issue or invalid input."
597 );
598
599 SystemTime::now()
601 .duration_since(UNIX_EPOCH)
602 .unwrap_or_else(|e2| {
603 log::error!(
604 "CRITICAL: System clock recovery failed: {e2}. System is unstable!"
605 );
606
607 #[cfg(feature = "panic-on-clock-error")]
608 {
609 panic!("System clock is severely broken, cannot continue: {e2}");
612 }
613
614 #[cfg(not(feature = "panic-on-clock-error"))]
615 {
616 log::error!("Using fallback timestamp due to clock error");
619 std::time::Duration::from_nanos(u64::MAX - 1_000_000_000)
621 }
622 })
623 })
624 .as_nanos() as u64
625}
626
627pub fn days_since_epoch_to_date(mut days: u64) -> (u32, u32, u32) {
657 let mut year = 1970u32;
659
660 loop {
661 let days_in_year = if is_leap_year(year as i32) { 366 } else { 365 };
662 if days >= days_in_year {
663 days -= days_in_year;
664 year += 1;
665 } else {
666 break;
667 }
668 }
669
670 let days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
672 let mut month = 1;
673
674 for &days_in_month in &days_per_month {
675 let adjusted_days = if month == 2 && is_leap_year(year as i32) {
676 days_in_month + 1 } else {
678 days_in_month
679 };
680
681 if days >= adjusted_days {
682 days -= adjusted_days;
683 month += 1;
684 } else {
685 break;
686 }
687 }
688
689 (year, month, days as u32 + 1) }
691
692#[must_use]
694pub fn format_timestamp_iso8601(timestamp_ms: u64) -> SmartString {
695 let secs = timestamp_ms / 1000;
696 let nanos = ((timestamp_ms % 1000) * 1_000_000) as u32;
697 let datetime = UNIX_EPOCH + std::time::Duration::new(secs, nanos);
698
699 SmartString::from(format!("{datetime:?}"))
702}
703
704#[must_use]
727pub fn format_timestamp_fix(timestamp_secs: u64) -> SmartString {
728 let days = timestamp_secs / 86400;
729 let remaining_secs = timestamp_secs % 86400;
730
731 let (year, month, day) = days_since_epoch_to_date(days);
732
733 let hours = remaining_secs / 3600;
734 let minutes = (remaining_secs % 3600) / 60;
735 let seconds = remaining_secs % 60;
736
737 SmartString::from(format!(
738 "{year:04}{month:02}{day:02}-{hours:02}:{minutes:02}:{seconds:02}"
739 ))
740}
741
742#[must_use]
765pub fn format_timestamp_fix_millis(timestamp_secs: u64) -> SmartString {
766 let fix_timestamp = format_timestamp_fix(timestamp_secs);
769 let mut result = SmartString::new();
770 result.push_str(&fix_timestamp);
771 result.push_str(".000");
772 result
773}
774
775#[must_use]
783pub fn format_current_time_fix() -> SmartString {
784 let timestamp_secs = get_epoch_timestamp_ns() / 1_000_000_000;
785 format_timestamp_fix(timestamp_secs)
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791 use std::time::{SystemTime, UNIX_EPOCH};
792
793 #[test]
794 fn test_timestamp_recovery_logic() {
795 let now_ms = get_timestamp_ms();
797 let system_now_ms = SystemTime::now()
798 .duration_since(UNIX_EPOCH)
799 .unwrap()
800 .as_millis() as u64;
801
802 let diff = now_ms.abs_diff(system_now_ms);
804
805 assert!(
806 diff < 1000,
807 "Timestamp should be within 1 second of system time"
808 );
809
810 let now_ns = get_timestamp_ns_result().unwrap();
812 let system_now_ns = SystemTime::now()
813 .duration_since(UNIX_EPOCH)
814 .unwrap()
815 .as_nanos() as u64;
816
817 let diff_ns = now_ns.abs_diff(system_now_ns);
819
820 assert!(
821 diff_ns < 1_000_000_000,
822 "Timestamp should be within 1 second of system time"
823 );
824 }
825
826 #[test]
827 fn test_quanta_timestamp_consistency() {
828 let clock = Clock::new();
829 let quanta_ns = get_quanta_timestamp_ns(&clock);
830 let raw_ns = clock.raw();
831
832 let diff = quanta_ns.abs_diff(raw_ns);
834 assert!(
835 diff < 1_000_000,
836 "Quanta timestamp should be within 1ms of raw clock, got diff: {diff}"
837 );
838 }
839
840 #[test]
841 fn test_timestamp_not_from_2020() {
842 let now_ms = get_timestamp_ms();
844 let now_ns = get_timestamp_ns_result().unwrap();
845 let epoch_ns = get_epoch_timestamp_ns();
846
847 const YEAR_2025_MS: u64 = 1735689600000;
849
850 assert!(
851 now_ms > YEAR_2025_MS,
852 "Epoch timestamp should be current time, not from 2020"
853 );
854 assert!(
855 now_ns > YEAR_2025_MS * 1_000_000,
856 "Epoch timestamp should be current time, not from 2020"
857 );
858 assert!(
859 epoch_ns > YEAR_2025_MS * 1_000_000,
860 "Epoch timestamp should be current time, not from 2020"
861 );
862
863 let monotonic_ns = get_monotonic_timestamp_ns();
865 assert!(
866 monotonic_ns < YEAR_2025_MS * 1_000_000,
867 "Monotonic timestamp should be much smaller than epoch time: {monotonic_ns}"
868 );
869 }
870
871 #[test]
872 fn test_parse_rfc3339_timestamp_valid_formats() {
873 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45Z");
875 assert!(result.is_ok(), "Basic UTC format should parse: {result:?}");
876
877 let expected_ns = 1_703_507_445_000_000_000_u64;
879 assert_eq!(result.unwrap(), expected_ns);
880
881 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.123Z");
883 assert!(
884 result.is_ok(),
885 "Milliseconds format should parse: {result:?}"
886 );
887 assert_eq!(result.unwrap(), expected_ns + 123_000_000);
888
889 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.123456Z");
891 assert!(
892 result.is_ok(),
893 "Microseconds format should parse: {result:?}"
894 );
895 assert_eq!(result.unwrap(), expected_ns + 123_456_000);
896
897 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.123456789Z");
899 assert!(
900 result.is_ok(),
901 "Nanoseconds format should parse: {result:?}"
902 );
903 assert_eq!(result.unwrap(), expected_ns + 123_456_789);
904
905 let result = parse_rfc3339_timestamp("2023-12-25T21:30:45+09:00");
907 assert!(result.is_ok(), "Positive timezone should parse: {result:?}");
908 assert_eq!(result.unwrap(), expected_ns);
910
911 let result = parse_rfc3339_timestamp("2023-12-25T07:30:45-05:00");
913 assert!(result.is_ok(), "Negative timezone should parse: {result:?}");
914 assert_eq!(result.unwrap(), expected_ns);
916
917 let result = parse_rfc3339_timestamp("2023-12-25T21:30:45.123+09:00");
919 assert!(
920 result.is_ok(),
921 "Subseconds with timezone should parse: {result:?}"
922 );
923 assert_eq!(result.unwrap(), expected_ns + 123_000_000);
924 }
925
926 #[test]
927 fn test_parse_rfc3339_timestamp_edge_cases() {
928 let result = parse_rfc3339_timestamp("2023-12-31T23:59:59.999999999Z");
930 assert!(result.is_ok(), "Year boundary should parse: {result:?}");
931
932 let result = parse_rfc3339_timestamp("2024-02-29T12:30:45Z");
934 assert!(result.is_ok(), "Leap year date should parse: {result:?}");
935
936 let result = parse_rfc3339_timestamp("2023-01-01T00:00:00Z");
938 assert!(result.is_ok(), "Midnight should parse: {result:?}");
939
940 let result = parse_rfc3339_timestamp("1970-01-01T00:00:00Z");
942 assert!(result.is_ok(), "Unix epoch should parse: {result:?}");
943 assert_eq!(result.unwrap(), 0);
944
945 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45+14:00");
947 assert!(
948 result.is_ok(),
949 "Max positive timezone should parse: {result:?}"
950 );
951
952 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45-12:00");
953 assert!(
954 result.is_ok(),
955 "Max negative timezone should parse: {result:?}"
956 );
957
958 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.1Z");
960 assert!(
961 result.is_ok(),
962 "Single digit subseconds should parse: {result:?}"
963 );
964
965 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.12Z");
966 assert!(
967 result.is_ok(),
968 "Two digit subseconds should parse: {result:?}"
969 );
970 }
971
972 #[test]
973 fn test_parse_rfc3339_timestamp_invalid_formats() {
974 assert!(parse_rfc3339_timestamp("").is_err());
976
977 assert!(parse_rfc3339_timestamp("2023-12-25").is_err());
979
980 assert!(parse_rfc3339_timestamp("2023-12-25 12:30:45Z").is_err());
982
983 assert!(parse_rfc3339_timestamp("2023-12-25T12:30:45").is_err());
985
986 assert!(parse_rfc3339_timestamp("2023-13-01T12:00:00Z").is_err()); assert!(parse_rfc3339_timestamp("2023-01-32T12:00:00Z").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T25:30:45Z").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T12:60:45Z").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T12:30:60Z").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T12:30:45+09").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T12:30:45+0900").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25T12:30:45.ABCZ").is_err());
1004
1005 assert!(parse_rfc3339_timestamp("T12:30:45Z").is_err()); assert!(parse_rfc3339_timestamp("2023-12-25TZ").is_err()); assert!(parse_rfc3339_timestamp("not-a-timestamp").is_err());
1013 }
1014
1015 #[test]
1016 fn test_parse_timezone_offset() {
1017 assert_eq!(parse_timezone_offset("09:00").unwrap(), 9 * 3600);
1019 assert_eq!(parse_timezone_offset("05:30").unwrap(), 5 * 3600 + 30 * 60);
1020 assert_eq!(parse_timezone_offset("00:00").unwrap(), 0);
1021 assert_eq!(parse_timezone_offset("14:00").unwrap(), 14 * 3600);
1022 assert_eq!(parse_timezone_offset("12:00").unwrap(), 12 * 3600);
1023
1024 assert!(parse_timezone_offset("09").is_err());
1026 assert_eq!(parse_timezone_offset("9:00").unwrap(), 9 * 3600);
1028
1029 assert!(parse_timezone_offset("25:00").is_err()); assert!(parse_timezone_offset("09:60").is_err()); assert!(parse_timezone_offset("-1:00").is_err()); assert!(parse_timezone_offset("09:-1").is_err()); assert!(parse_timezone_offset("ABC:00").is_err());
1035 assert!(parse_timezone_offset("09:XX").is_err());
1036 }
1037
1038 #[test]
1039 fn test_parse_subseconds() {
1040 assert_eq!(parse_subseconds("1").unwrap(), 100_000_000); assert_eq!(parse_subseconds("12").unwrap(), 120_000_000); assert_eq!(parse_subseconds("123").unwrap(), 123_000_000); assert_eq!(parse_subseconds("123456").unwrap(), 123_456_000); assert_eq!(parse_subseconds("123456789").unwrap(), 123_456_789); assert_eq!(parse_subseconds("1234567890123").unwrap(), 123_456_789);
1049
1050 assert_eq!(parse_subseconds("000000001").unwrap(), 1); assert_eq!(parse_subseconds("999999999").unwrap(), 999_999_999);
1053
1054 assert!(parse_subseconds("12A").is_err());
1056 assert!(parse_subseconds("ABC").is_err());
1057 assert_eq!(parse_subseconds("").unwrap(), 0);
1059 }
1060
1061 #[test]
1062 fn test_days_since_unix_epoch() {
1063 assert_eq!(days_since_unix_epoch(1970, 1, 1).unwrap(), 0);
1065
1066 assert_eq!(days_since_unix_epoch(1970, 1, 2).unwrap(), 1);
1068
1069 assert_eq!(days_since_unix_epoch(1971, 1, 1).unwrap(), 365);
1071
1072 assert_eq!(days_since_unix_epoch(1972, 1, 1).unwrap(), 365 + 365); assert_eq!(
1075 days_since_unix_epoch(1972, 3, 1).unwrap(),
1076 365 + 365 + 31 + 29
1077 ); assert_eq!(days_since_unix_epoch(2000, 1, 1).unwrap(), 10957); assert_eq!(days_since_unix_epoch(2023, 1, 1).unwrap(), 19358); assert!(days_since_unix_epoch(2024, 2, 29).is_ok()); }
1089
1090 #[test]
1091 fn test_is_leap_year() {
1092 assert!(is_leap_year(2024));
1094 assert!(is_leap_year(2020));
1095 assert!(is_leap_year(1972));
1096
1097 assert!(!is_leap_year(2023));
1099 assert!(!is_leap_year(2021));
1100 assert!(!is_leap_year(1971));
1101
1102 assert!(!is_leap_year(1900));
1104 assert!(!is_leap_year(1800));
1105 assert!(!is_leap_year(1700));
1106
1107 assert!(is_leap_year(2000));
1109 assert!(is_leap_year(1600));
1110 assert!(is_leap_year(2400));
1111 }
1112
1113 #[test]
1114 fn test_exchange_specific_formats() {
1115 let result = parse_rfc3339_timestamp("2023-12-25T21:30:45+09:00");
1117 assert!(result.is_ok(), "Upbit KST format should parse: {result:?}");
1118
1119 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.123Z");
1121 assert!(
1122 result.is_ok(),
1123 "Coinbase UTC format should parse: {result:?}"
1124 );
1125
1126 let result = parse_rfc3339_timestamp("2023-12-25T21:30:45+09:00");
1128 assert!(
1129 result.is_ok(),
1130 "Bithumb KST format should parse: {result:?}"
1131 );
1132
1133 let result = parse_rfc3339_timestamp("2023-12-25T12:30:45.000Z");
1135 assert!(
1136 result.is_ok(),
1137 "ISO8601 with exact milliseconds should parse: {result:?}"
1138 );
1139 }
1140
1141 #[test]
1142 fn test_parse_rfc3339_timestamp_performance() {
1143 let timestamps = vec![
1145 "2023-12-25T12:30:45Z",
1146 "2023-12-25T12:30:45.123Z",
1147 "2023-12-25T12:30:45.123456Z",
1148 "2023-12-25T12:30:45.123456789Z",
1149 "2023-12-25T21:30:45+09:00",
1150 "2023-12-25T07:30:45-05:00",
1151 ];
1152
1153 let start = std::time::Instant::now();
1154 for _ in 0..1000 {
1155 for timestamp in ×tamps {
1156 let result = parse_rfc3339_timestamp(timestamp);
1157 assert!(result.is_ok(), "Timestamp should parse: {timestamp}");
1158 }
1159 }
1160 let duration = start.elapsed();
1161
1162 assert!(
1164 duration.as_millis() < 50,
1165 "Parsing should be fast: {duration:?}"
1166 );
1167 }
1168
1169 #[test]
1170 fn test_parse_rfc3339_timestamp_basic_validation() {
1171 assert!(parse_rfc3339_timestamp("").is_err());
1173 assert!(parse_rfc3339_timestamp("invalid").is_err());
1174 assert!(parse_rfc3339_timestamp("not-a-timestamp").is_err());
1175 }
1176
1177 #[test]
1178 fn test_parse_timestamp_with_fallback() {
1179 let result = parse_timestamp_with_fallback("", "test");
1181 assert!(result > 0, "Fallback should return a valid timestamp");
1182
1183 let result2 = parse_timestamp_with_fallback("invalid", "test");
1184 assert!(result2 > 0, "Fallback should return a valid timestamp");
1185
1186 let now = get_timestamp_ns_result().unwrap();
1188 let diff = result2.abs_diff(now);
1189 assert!(diff < 5_000_000_000, "Fallback timestamp should be recent"); }
1191
1192 #[test]
1193 fn test_monotonic_vs_epoch_timestamps() {
1194 let monotonic_ns = get_monotonic_timestamp_ns();
1196 let epoch_ns = get_epoch_timestamp_ns();
1197
1198 assert!(
1201 monotonic_ns < epoch_ns,
1202 "Monotonic time ({monotonic_ns}) should be less than epoch time ({epoch_ns})"
1203 );
1204
1205 let year_2020_ns = 1_577_836_800_000_000_000u64; assert!(
1208 epoch_ns > year_2020_ns,
1209 "Epoch timestamp should be after 2020: {epoch_ns}"
1210 );
1211 }
1212
1213 #[test]
1214 fn test_clock_raw_documentation() {
1215 let clock = Clock::new();
1217 let monotonic1 = get_quanta_timestamp_ns(&clock);
1218 let monotonic2 = clock.raw();
1219
1220 let diff = monotonic2.abs_diff(monotonic1);
1222 assert!(diff < 1_000_000, "Clock::raw() calls should be very close");
1223
1224 let epoch_ns = get_epoch_timestamp_ns();
1226 assert!(
1227 monotonic1 < epoch_ns / 1000,
1228 "Monotonic time should be orders of magnitude smaller than epoch time"
1229 );
1230 }
1231
1232 #[test]
1233 fn test_epoch_timestamp_functions() {
1234 let epoch_ns = get_epoch_timestamp_ns();
1236 let system_ns = get_timestamp_ns_result().unwrap();
1237
1238 let diff = epoch_ns.abs_diff(system_ns);
1240 assert!(
1241 diff < 1_000_000_000,
1242 "Epoch timestamp functions should return similar values"
1243 );
1244
1245 let year_2020_ns = 1_577_836_800_000_000_000u64;
1247 assert!(
1248 epoch_ns > year_2020_ns,
1249 "Epoch timestamp should be after 2020"
1250 );
1251 }
1252
1253 #[test]
1254 fn test_safe_timestamp_functions() {
1255 let ms_safe = get_timestamp_ms_safe();
1257 assert!(ms_safe.is_some(), "Safe version should return Some");
1258
1259 let ns_safe = get_timestamp_ns_safe();
1260 assert!(ns_safe.is_some(), "Safe version should return Some");
1261
1262 let epoch_safe = get_epoch_timestamp_ns_safe();
1263 assert!(epoch_safe.is_some(), "Safe version should return Some");
1264 }
1265
1266 #[test]
1267 fn test_clock_error_behavior() {
1268 #[cfg(feature = "panic-on-clock-error")]
1272 {
1273 }
1277
1278 #[cfg(not(feature = "panic-on-clock-error"))]
1279 {
1280 }
1284
1285 }
1288
1289 #[test]
1290 fn test_system_time_to_nanos() {
1291 let now = SystemTime::now();
1293 let timestamp_ns = system_time_to_nanos(now);
1294
1295 let year_2020_ns = 1_577_836_800_000_000_000u64; assert!(
1298 timestamp_ns > year_2020_ns,
1299 "Timestamp should be after 2020: {timestamp_ns}"
1300 );
1301
1302 let epoch_ns = get_epoch_timestamp_ns();
1304 let diff = timestamp_ns.abs_diff(epoch_ns);
1305 assert!(
1306 diff < 1_000_000_000, "system_time_to_nanos should be close to get_epoch_timestamp_ns: diff={diff}ns"
1308 );
1309
1310 let epoch_time = UNIX_EPOCH;
1312 let epoch_timestamp = system_time_to_nanos(epoch_time);
1313 assert_eq!(
1314 epoch_timestamp, 0,
1315 "Unix epoch should convert to 0 nanoseconds"
1316 );
1317
1318 let known_time = UNIX_EPOCH + std::time::Duration::from_secs(1_672_531_200); let known_timestamp = system_time_to_nanos(known_time);
1321 let expected_ns = 1_672_531_200_000_000_000_u64;
1322 assert_eq!(
1323 known_timestamp, expected_ns,
1324 "Known timestamp should convert correctly"
1325 );
1326 }
1327
1328 #[test]
1329 fn test_format_timestamp_fix() {
1330 assert_eq!(format_timestamp_fix(0), "19700101-00:00:00");
1332
1333 let timestamp = 1703507445;
1335 assert_eq!(format_timestamp_fix(timestamp), "20231225-12:30:45");
1336
1337 let timestamp = 1704067200;
1339 assert_eq!(format_timestamp_fix(timestamp), "20240101-00:00:00");
1340
1341 let current_secs = get_epoch_timestamp_ns() / 1_000_000_000;
1343 let formatted = format_timestamp_fix(current_secs);
1344 assert!(formatted.len() == 17); assert!(formatted.contains('-'));
1346 assert!(formatted.contains(':'));
1347 }
1348
1349 #[test]
1350 fn test_format_timestamp_fix_millis() {
1351 assert_eq!(format_timestamp_fix_millis(0), "19700101-00:00:00.000");
1353
1354 let timestamp = 1703507445;
1356 assert_eq!(
1357 format_timestamp_fix_millis(timestamp),
1358 "20231225-12:30:45.000"
1359 );
1360
1361 let result = format_timestamp_fix_millis(timestamp);
1363 assert!(result.ends_with(".000"));
1364 assert_eq!(result.len(), 21); }
1366
1367 #[test]
1368 fn test_format_current_time_fix() {
1369 let current_fix = format_current_time_fix();
1371
1372 assert_eq!(current_fix.len(), 17); assert!(current_fix.contains('-'));
1375 assert!(current_fix.contains(':'));
1376
1377 let year_str = ¤t_fix[0..4];
1379 let year: u32 = year_str.parse().expect("Should parse year");
1380 assert!(year >= 2025, "Year should be 2025 or later, got {year}");
1381
1382 assert_eq!(¤t_fix[8..9], "-");
1384 assert_eq!(¤t_fix[11..12], ":");
1385 assert_eq!(¤t_fix[14..15], ":");
1386 }
1387
1388 #[test]
1391 fn test_format_timestamp_fix_comprehensive_edge_cases() {
1392 let y2k_timestamp = 946684800;
1394 assert_eq!(format_timestamp_fix(y2k_timestamp), "20000101-00:00:00");
1395
1396 let leap_day_2000 = 951782400; assert_eq!(format_timestamp_fix(leap_day_2000), "20000229-00:00:00");
1399
1400 let leap_day_2024 = 1709164800; assert_eq!(format_timestamp_fix(leap_day_2024), "20240229-00:00:00");
1403
1404 let end_of_year = 1704067199;
1406 assert_eq!(format_timestamp_fix(end_of_year), "20231231-23:59:59");
1407
1408 let start_of_year = 1704067200;
1410 assert_eq!(format_timestamp_fix(start_of_year), "20240101-00:00:00");
1411
1412 let future_timestamp = 2524608000;
1414 assert_eq!(format_timestamp_fix(future_timestamp), "20500101-00:00:00");
1415
1416 let utc_midnight = 1703980800; assert_eq!(format_timestamp_fix(utc_midnight), "20231231-00:00:00");
1419 }
1420
1421 #[test]
1422 fn test_format_timestamp_fix_protocol_compliance() {
1423 let test_cases = [
1425 (0, "19700101-00:00:00"), (3661, "19700101-01:01:01"), (86400, "19700102-00:00:00"), (31536000, "19710101-00:00:00"), (1609459200, "20210101-00:00:00"), ];
1431
1432 for (timestamp, expected) in test_cases {
1433 let result = format_timestamp_fix(timestamp);
1434 assert_eq!(result, expected, "Timestamp {timestamp} failed FIX format");
1435
1436 assert_eq!(
1438 result.len(),
1439 17,
1440 "FIX timestamp must be exactly 17 characters"
1441 );
1442 assert_eq!(&result[8..9], "-", "Must have dash separator at position 8");
1443 assert_eq!(&result[11..12], ":", "Must have colon at position 11");
1444 assert_eq!(&result[14..15], ":", "Must have colon at position 14");
1445
1446 for (i, ch) in result.chars().enumerate() {
1448 match i {
1449 8 => assert_eq!(ch, '-', "Position 8 must be dash"),
1450 11 | 14 => assert_eq!(ch, ':', "Positions 11,14 must be colons"),
1451 _ => assert!(
1452 ch.is_ascii_digit(),
1453 "Position {i} must be digit, got '{ch}'"
1454 ),
1455 }
1456 }
1457 }
1458 }
1459
1460 #[test]
1461 fn test_format_timestamp_fix_millis_precision() {
1462 let timestamp = 1703507445; let result = format_timestamp_fix_millis(timestamp);
1466 assert_eq!(result, "20231225-12:30:45.000");
1467
1468 assert_eq!(
1470 result.len(),
1471 21,
1472 "FIX millis timestamp must be 21 characters"
1473 );
1474 assert!(
1475 result.ends_with(".000"),
1476 "Must end with .000 for second precision"
1477 );
1478 assert_eq!(
1479 &result[17..18],
1480 ".",
1481 "Must have dot separator at position 17"
1482 );
1483
1484 let base_result = format_timestamp_fix(timestamp);
1486 assert_eq!(&result[0..17], base_result.as_str());
1487 }
1488
1489 #[test]
1490 fn test_format_timestamp_fix_performance() {
1491 let timestamp = 1703507445;
1493 let iterations = 10000;
1494
1495 let start = std::time::Instant::now();
1496 for _ in 0..iterations {
1497 let _ = format_timestamp_fix(timestamp);
1498 }
1499 let duration = start.elapsed();
1500
1501 assert!(
1503 duration.as_millis() < 50,
1504 "FIX timestamp formatting too slow: {duration:?} for {iterations} iterations"
1505 );
1506
1507 let start_millis = std::time::Instant::now();
1509 for _ in 0..iterations {
1510 let _ = format_timestamp_fix_millis(timestamp);
1511 }
1512 let duration_millis = start_millis.elapsed();
1513
1514 assert!(
1515 duration_millis.as_millis() < 100,
1516 "FIX millis timestamp formatting too slow: {duration_millis:?}"
1517 );
1518 }
1519
1520 #[test]
1521 fn test_format_timestamp_fix_trading_session_times() {
1522 let nyse_open = 1704205800; assert_eq!(format_timestamp_fix(nyse_open), "20240102-14:30:00");
1527
1528 let london_close = 1704213000; assert_eq!(format_timestamp_fix(london_close), "20240102-16:30:00");
1531
1532 let tokyo_open = 1704240000; assert_eq!(format_timestamp_fix(tokyo_open), "20240103-00:00:00");
1535
1536 let after_hours = 1704250800; assert_eq!(format_timestamp_fix(after_hours), "20240103-03:00:00");
1539 }
1540
1541 #[test]
1542 fn test_format_timestamp_fix_consistency() {
1543 let timestamp = 1703507445;
1545 let results: Vec<SmartString> = (0..100).map(|_| format_timestamp_fix(timestamp)).collect();
1546
1547 let first = &results[0];
1548 for (i, result) in results.iter().enumerate() {
1549 assert_eq!(
1550 result, first,
1551 "Inconsistent result at iteration {i}: got '{result}', expected '{first}'"
1552 );
1553 }
1554 }
1555
1556 #[test]
1557 fn test_format_timestamp_fix_memory_efficiency() {
1558 let timestamp = 1703507445;
1560 let result = format_timestamp_fix(timestamp);
1561
1562 assert_eq!(result.len(), 17);
1564
1565 assert!(
1567 result.capacity() >= 17,
1568 "SmartString should have sufficient capacity"
1569 );
1570 }
1571
1572 #[test]
1573 fn test_format_current_time_fix_real_time() {
1574 let before = std::time::SystemTime::now()
1576 .duration_since(std::time::UNIX_EPOCH)
1577 .unwrap()
1578 .as_secs();
1579
1580 let fix_formatted = format_current_time_fix();
1581
1582 let after = std::time::SystemTime::now()
1583 .duration_since(std::time::UNIX_EPOCH)
1584 .unwrap()
1585 .as_secs();
1586
1587 let year: u32 = fix_formatted[0..4].parse().expect("Should parse year");
1589 let month: u32 = fix_formatted[4..6].parse().expect("Should parse month");
1590 let day: u32 = fix_formatted[6..8].parse().expect("Should parse day");
1591 let hour: u32 = fix_formatted[9..11].parse().expect("Should parse hour");
1592 let minute: u32 = fix_formatted[12..14].parse().expect("Should parse minute");
1593 let second: u32 = fix_formatted[15..17].parse().expect("Should parse second");
1594
1595 assert!(year >= 2025, "Year should be current or future");
1597 assert!((1..=12).contains(&month), "Month should be 1-12");
1598 assert!((1..=31).contains(&day), "Day should be 1-31");
1599 assert!(hour <= 23, "Hour should be 0-23");
1600 assert!(minute <= 59, "Minute should be 0-59");
1601 assert!(second <= 59, "Second should be 0-59");
1602
1603 assert!(after >= before, "Time should move forward");
1606 }
1607}