stamp.rs 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. //! UTC timestamps without a chrono dependency: Howard Hinnant's
  2. //! civil_from_days for the calendar math, unix-epoch seconds everywhere
  3. //! else (the run ledger stores epochs and renders through [`fmt_ts`]).
  4. use std::time::{SystemTime, UNIX_EPOCH};
  5. /// Unix-epoch seconds right now (UTC).
  6. pub(crate) fn now_secs() -> i64 {
  7. SystemTime::now()
  8. .duration_since(UNIX_EPOCH)
  9. .map(|d| d.as_secs() as i64)
  10. .unwrap_or(0)
  11. }
  12. /// `YYYY-MM-DD_HHMMSS` in UTC (bench report file names).
  13. pub(crate) fn utc_stamp() -> String {
  14. let secs = now_secs();
  15. let (h, m, s) = ((secs % 86400) / 3600, (secs % 3600) / 60, secs % 60);
  16. let (y, mo, d) = civil_date(secs / 86400);
  17. format!(
  18. "{y:04}-{mo:02}-{d:02}_{h:02}{m:02}{s:02}",
  19. y = y,
  20. mo = mo,
  21. d = d
  22. )
  23. }
  24. /// `YYYY-MM-DD HH:MM` in UTC (run-ledger display).
  25. pub(crate) fn fmt_ts(secs: i64) -> String {
  26. let (h, m, _) = ((secs % 86400) / 3600, (secs % 3600) / 60, secs % 60);
  27. let (y, mo, d) = civil_date(secs / 86400);
  28. format!("{y:04}-{mo:02}-{d:02} {h:02}:{m:02}")
  29. }
  30. /// Days since 1970-01-01 → (year, month, day); Howard Hinnant's civil_from_days.
  31. pub(crate) fn civil_date(z: i64) -> (i64, i64, i64) {
  32. let z = z + 719_468;
  33. let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
  34. let doe = z - era * 146_097;
  35. let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
  36. let y = yoe + era * 400;
  37. let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
  38. let mp = (5 * doy + 2) / 153;
  39. let d = doy - (153 * mp + 2) / 5 + 1;
  40. let mo = if mp < 10 { mp + 3 } else { mp - 9 };
  41. let y = if mo <= 2 { y + 1 } else { y };
  42. (y, mo, d)
  43. }
  44. #[cfg(test)]
  45. mod tests {
  46. use super::*;
  47. #[test]
  48. fn stamp_format() {
  49. let s = utc_stamp();
  50. assert_eq!(s.len(), 17, "{s}");
  51. assert_eq!(&s[4..5], "-");
  52. assert_eq!(&s[7..8], "-");
  53. assert_eq!(&s[10..11], "_");
  54. }
  55. #[test]
  56. fn civil_date_known_values() {
  57. assert_eq!(civil_date(0), (1970, 1, 1));
  58. assert_eq!(civil_date(19_723), (2024, 1, 1));
  59. assert_eq!(civil_date(20_818), (2026, 12, 31));
  60. }
  61. #[test]
  62. fn fmt_ts_renders_date_and_minute() {
  63. // 2026-09-14 10:03:00 UTC
  64. assert_eq!(fmt_ts(1_789_380_180), "2026-09-14 10:03");
  65. }
  66. }