Skip to main content

low_level_date/
lib.rs

1//! Low-Level Date Algorithms
2//! Rust implementation from https://howardhinnant.github.io/date_algorithms.html
3//!
4//! Calculates the date from std::time::SystemTime only
5//! 
6//! as described in the [great article by Howard Hinnant](https://howardhinnant.github.io/date_algorithms.html)
7//! 
8use std::time::{Duration, SystemTime};
9
10/// Shorter type for result tuple
11/// (year, month, day, hours, minutes, seconds, milliseconds)
12/// 
13pub type Date = (i64, u64, u64, u64, u64, u64, u32);
14
15
16/// Calculates the days ellapsed from a given date 
17pub fn days_from_civil(mut y:i64, m: u64, d: u64) -> i64 {
18
19    if m <= 2 {
20        y -= 1;
21    }
22
23    let era: i64 = ( if y >= 0 { y } else { y - 399 } ).div_euclid(400);
24    let yoe: u64 = (y - era * 400) as u64;
25    let doy        = ( 153* ( if m > 2 { m - 3 } else { m + 9 }) + 2 ) / 5 + d -1 ;
26    let doe        = yoe * 365 + yoe / 4 - yoe / 100 + doy;
27
28    let r = era * 146097 + (doe as i64) - 719468;
29
30    r
31}
32
33/// Calculates the civil date from days 
34pub fn civil_from_days(mut z: i64) -> (i64, u64, u64) {
35
36    z += 719468;
37    let era: i64 = ( if z >= 0 { z } else { z - 146096 } ).div_euclid(146097);
38    let doe: u64        = ( z - era * 146097) as u64;
39    let yoe        = ( doe - doe / 1460 + doe / 36524 - doe / 146096 ) / 365;
40    let mut y: i64   = yoe as i64 + era * 400;
41
42    let doy: u64 = doe - ( 365 * yoe + yoe / 4 - yoe /100);
43    let mp: u64  = (5 * doy + 2) / 153;
44    let d: u64   = doy - (153*mp+2)/5 + 1;
45    let m: u64   = if mp < 10 { mp + 3 } else { mp - 9 };
46
47    if m <= 2 {
48        y+=1;
49    }
50    (y, m, d)
51    
52}
53
54pub fn date_from_duration(d: Duration) -> Date {
55
56    let ms = d.subsec_millis();
57
58    let seconds = d.as_secs();
59
60    date_from_timestamp(seconds as i64, ms)
61    
62}
63pub fn date_from_timestamp(ts: i64, millis: u32) -> Date {
64
65    let mut seconds = ts;
66    let days = ts.div_euclid(86400);
67    let today = days * 86400;
68
69    seconds -= today;
70
71    if seconds < 0 {
72        seconds = 86400 + seconds;
73    }
74
75    let mut seconds_u64 = seconds as u64;
76
77    let hour = seconds_u64 / 3600;
78    seconds_u64 -= hour * 3600;
79
80    let min = seconds_u64 / 60;
81    seconds_u64 -= min * 60;
82
83    let sec = seconds_u64;
84
85    
86    let (year, month, day) = civil_from_days(days);
87    (year, month, day, hour, min, sec, millis)
88
89}
90
91/// Convenience methode to calculate the cicil date from SystemTime::now
92///
93/// argument: utc_offset (offset + summertime)
94///
95/// # Examples
96///
97/// ```
98/// use low_level_date::date_now;
99/// 
100/// let offset = 2; // e.x: middle europe utc+01::00  + summertime (1 + 1)
101/// let r = date_now(offset);
102///
103/// let offset = -8; // e.x: Los Angeles without summertime utc-08:00
104/// let r = date_now(offset);
105/// 
106/// ```
107pub fn date_now(utc_offset: i64) -> Date {
108    let mut now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("could not get system time since unix epoch");
109
110    if utc_offset < 0 {
111        let offset = utc_offset.abs() as u64; 
112        now = now.saturating_sub(Duration::new(offset*60*60, 0));
113    } else if utc_offset > 0 {
114        now = now.saturating_add(Duration::new(utc_offset as u64 *60*60, 0));
115    }
116    date_from_duration(now)
117    
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn test_days_from_civil() {
126        let r = days_from_civil(1970, 1, 1);
127        assert_eq!(r, 0);
128
129        let r = days_from_civil(1971, 1, 1);
130        assert_eq!(r, 365);
131
132        let r = days_from_civil(1969, 1, 1);
133        assert_eq!(r, -365);
134    }
135
136    #[test]
137    fn test_civil_from_days() {
138        let r = civil_from_days(0);
139        assert_eq!(r, (1970, 1, 1));
140    }
141
142    #[test]
143    fn test_date_from_timestamp() {
144
145
146        let ts = 939945600;
147        let r = date_from_timestamp(ts, 0);
148        assert_eq!(r, (1999, 10, 15, 0, 0, 0, 0));
149        let ts = -14182940;
150        let r = date_from_timestamp(ts, 0);
151        assert_eq!(r, (1969, 7, 20, 20, 17, 40, 0)); 
152
153        let r = date_now(2);
154        println!("Now Utc(+2): {}/{:0>2}/{:0>2} {:0>2}:{:0>2}:{:0>2}.{:0>3}", r.0, r.1, r.2, r.3, r.4, r.5, r.6);
155
156        let r = date_now(-8);
157        println!("Now Utc(-8): {}/{:0>2}/{:0>2} {:0>2}:{:0>2}:{:0>2}.{:0>3}", r.0, r.1, r.2, r.3, r.4, r.5, r.6);
158    }
159}