Skip to main content

engram_core/
timestamp.rs

1pub fn current_utc_timestamp() -> String {
2    let duration = std::time::SystemTime::now()
3        .duration_since(std::time::UNIX_EPOCH)
4        .unwrap_or_default();
5    format_utc_timestamp(duration.as_secs())
6}
7
8pub fn format_utc_timestamp(timestamp: u64) -> String {
9    let second = timestamp % 60;
10    let minute = (timestamp / 60) % 60;
11    let hour = (timestamp / 3600) % 24;
12    let mut days = timestamp / 86400;
13    let mut year = 1970u64;
14    loop {
15        let days_in_year = if is_leap_year(year) { 366 } else { 365 };
16        if days < days_in_year {
17            break;
18        }
19        days -= days_in_year;
20        year += 1;
21    }
22    let month_days = month_lengths(is_leap_year(year));
23    let mut month = 1u64;
24    for &length in &month_days {
25        if days < length {
26            break;
27        }
28        days -= length;
29        month += 1;
30    }
31    let day = days + 1;
32    format!("{year:04}-{month:02}-{day:02}T{hour:02}:{minute:02}:{second:02}Z")
33}
34
35pub fn parse_timestamp_to_epoch(timestamp: &str) -> Option<u64> {
36    let parts: Vec<&str> = timestamp.split('T').collect();
37    if parts.len() != 2 {
38        return None;
39    }
40    let date_parts: Vec<u64> = parts[0].split('-').filter_map(|p| p.parse().ok()).collect();
41    if date_parts.len() != 3 {
42        return None;
43    }
44    let time_str = parts[1].trim_end_matches('Z');
45    let time_parts: Vec<u64> = time_str.split(':').filter_map(|p| p.parse().ok()).collect();
46    if time_parts.len() != 3 {
47        return None;
48    }
49    let (year, month, day) = (date_parts[0], date_parts[1], date_parts[2]);
50    let (hour, minute, second) = (time_parts[0], time_parts[1], time_parts[2]);
51    let mut total_days: u64 = 0;
52    for y in 1970..year {
53        total_days += if is_leap_year(y) { 366 } else { 365 };
54    }
55    let months = month_lengths(is_leap_year(year));
56    for &month_days in months.iter().take(month.saturating_sub(1) as usize) {
57        total_days += month_days;
58    }
59    total_days += day.saturating_sub(1);
60    Some(total_days * 86400 + hour * 3600 + minute * 60 + second)
61}
62
63fn is_leap_year(year: u64) -> bool {
64    (year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
65}
66
67fn month_lengths(leap: bool) -> [u64; 12] {
68    let feb = if leap { 29 } else { 28 };
69    [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
70}