e11dea430bae089e64feb4b738be866cc406a0b2
[gps-watch.git] / src / common / time.rs
1 /*
2  * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Time::from_unix_time() adapted from musl's __secs_to_tm() which is
5  * Copyright © 2005-2020 Rich Felker, et al.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining
8  * a copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  */
26
27 use fmt::fmt_u32_pad;
28
29 pub struct Time {
30     seconds: i32,
31     minutes: i32,
32     hours: i32,
33     day: i32,
34     month: i32,
35     year: i32,
36 }
37
38 // 2000-03-01 (mod 400 year, immediately after feb29
39 const LEAPOCH: i32 = (946684800 + 86400 * (31 + 29));
40
41 const DAYS_PER_400Y: i32 = (365 * 400 + 97);
42 const DAYS_PER_100Y: i32 = (365 * 100 + 24);
43 const DAYS_PER_4Y  : i32 = (365 *   4 +  1);
44
45 const DAYS_IN_MONTH : [i32; 12] = [ 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29 ];
46
47 impl Time {
48     pub fn from_unix_time(u: u32) -> Option<Time> {
49         let t = u as i32;
50
51         let secs = t - LEAPOCH;
52         let mut days = secs / 86400;
53         let mut remsecs = secs % 86400;
54
55         if remsecs < 0 {
56             remsecs += 86400;
57             days -= 1;
58         }
59
60         let mut qc_cycles = days / DAYS_PER_400Y;
61         let mut remdays = days % DAYS_PER_400Y;
62
63         if remdays < 0 {
64             remdays += DAYS_PER_400Y;
65             qc_cycles -= 1;
66         }
67
68         let mut c_cycles = remdays / DAYS_PER_100Y;
69
70         if c_cycles == 4 {
71             c_cycles -= 1;
72         }
73
74         remdays -= c_cycles * DAYS_PER_100Y;
75
76         let mut q_cycles = remdays / DAYS_PER_4Y;
77
78         if q_cycles == 25 {
79             q_cycles -= 1;
80         }
81
82         remdays -= q_cycles * DAYS_PER_4Y;
83
84         let mut remyears = remdays / 365;
85
86         if remyears == 4 {
87             remyears -= 1;
88         }
89
90         remdays -= remyears * 365;
91
92         let mut years =
93             remyears + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles;
94
95         let mut months = 0;
96
97         while remdays >= DAYS_IN_MONTH[months as usize] {
98             remdays -= DAYS_IN_MONTH[months as usize];
99             months += 1;
100         }
101
102         if months >= 10 {
103             months -= 12;
104             years += 1;
105         }
106
107         if years + 100 > i32::max_value() || years + 100 < i32::min_value() {
108             None
109         } else {
110             Some(Time {
111                 year: years + 100,
112                 month: months + 2,
113                 day: remdays + 1,
114                 hours: remsecs / 3600,
115                 minutes: remsecs / 60 % 60,
116                 seconds: remsecs % 60,
117             })
118         }
119     }
120
121     pub fn fmt_time(&self, s: &mut [u8]) {
122         let mut offset = 0;
123
124         offset += fmt_u32_pad(&mut s[offset..], self.hours as u32, 2, b'0');
125         s[offset] = b':';
126         offset += 1;
127
128         offset += fmt_u32_pad(&mut s[offset..], self.minutes as u32, 2, b'0');
129         s[offset] = b':';
130         offset += 1;
131
132         fmt_u32_pad(&mut s[offset..], self.seconds as u32, 2, b'0');
133     }
134 }