1 // musl as a whole is licensed under the following standard MIT license:
2 //
3 // ----------------------------------------------------------------------
4 // Copyright © 2005-2020 Rich Felker, et al.
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 // ----------------------------------------------------------------------
25 //
26 // Authors/contributors include:
27 //
28 // A. Wilcox
29 // Ada Worcester
30 // Alex Dowad
31 // Alex Suykov
32 // Alexander Monakov
33 // Andre McCurdy
34 // Andrew Kelley
35 // Anthony G. Basile
36 // Aric Belsito
37 // Arvid Picciani
38 // Bartosz Brachaczek
39 // Benjamin Peterson
40 // Bobby Bingham
41 // Boris Brezillon
42 // Brent Cook
43 // Chris Spiegel
44 // Clément Vasseur
45 // Daniel Micay
46 // Daniel Sabogal
47 // Daurnimator
48 // David Carlier
49 // David Edelsohn
50 // Denys Vlasenko
51 // Dmitry Ivanov
52 // Dmitry V. Levin
53 // Drew DeVault
54 // Emil Renner Berthing
55 // Fangrui Song
56 // Felix Fietkau
57 // Felix Janda
58 // Gianluca Anzolin
59 // Hauke Mehrtens
60 // He X
61 // Hiltjo Posthuma
62 // Isaac Dunham
63 // Jaydeep Patil
64 // Jens Gustedt
65 // Jeremy Huntwork
66 // Jo-Philipp Wich
67 // Joakim Sindholt
68 // John Spencer
69 // Julien Ramseier
70 // Justin Cormack
71 // Kaarle Ritvanen
72 // Khem Raj
73 // Kylie McClain
74 // Leah Neukirchen
75 // Luca Barbato
76 // Luka Perkov
77 // M Farkas-Dyck (Strake)
78 // Mahesh Bodapati
79 // Markus Wichmann
80 // Masanori Ogino
81 // Michael Clark
82 // Michael Forney
83 // Mikhail Kremnyov
84 // Natanael Copa
85 // Nicholas J. Kain
86 // orc
87 // Pascal Cuoq
88 // Patrick Oppenlander
89 // Petr Hosek
90 // Petr Skocik
91 // Pierre Carrier
92 // Reini Urban
93 // Rich Felker
94 // Richard Pennington
95 // Ryan Fairfax
96 // Samuel Holland
97 // Segev Finer
98 // Shiz
99 // sin
100 // Solar Designer
101 // Stefan Kristiansson
102 // Stefan O'Rear
103 // Szabolcs Nagy
104 // Timo Teräs
105 // Trutz Behn
106 // Valentin Ochs
107 // Will Dietz
108 // William Haddon
109 // William Pitcock
110 //
111 // Portions of this software are derived from third-party works licensed
112 // under terms compatible with the above MIT license:
113 //
114 // The TRE regular expression implementation (src/regex/reg* and
115 // src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed
116 // under a 2-clause BSD license (license text in the source files). The
117 // included version has been heavily modified by Rich Felker in 2012, in
118 // the interests of size, simplicity, and namespace cleanliness.
119 //
120 // Much of the math library code (src/math/* and src/complex/*) is
121 // Copyright © 1993,2004 Sun Microsystems or
122 // Copyright © 2003-2011 David Schultz or
123 // Copyright © 2003-2009 Steven G. Kargl or
124 // Copyright © 2003-2009 Bruce D. Evans or
125 // Copyright © 2008 Stephen L. Moshier or
126 // Copyright © 2017-2018 Arm Limited
127 // and labelled as such in comments in the individual source files. All
128 // have been licensed under extremely permissive terms.
129 //
130 // The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008
131 // The Android Open Source Project and is licensed under a two-clause BSD
132 // license. It was taken from Bionic libc, used on Android.
133 //
134 // The AArch64 memcpy and memset code (src/string/aarch64/*) are
135 // Copyright © 1999-2019, Arm Limited.
136 //
137 // The implementation of DES for crypt (src/crypt/crypt_des.c) is
138 // Copyright © 1994 David Burren. It is licensed under a BSD license.
139 //
140 // The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was
141 // originally written by Solar Designer and placed into the public
142 // domain. The code also comes with a fallback permissive license for use
143 // in jurisdictions that may not recognize the public domain.
144 //
145 // The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011
146 // Valentin Ochs and is licensed under an MIT-style license.
147 //
148 // The x86_64 port was written by Nicholas J. Kain and is licensed under
149 // the standard MIT terms.
150 //
151 // The mips and microblaze ports were originally written by Richard
152 // Pennington for use in the ellcc project. The original code was adapted
153 // by Rich Felker for build system and code conventions during upstream
154 // integration. It is licensed under the standard MIT terms.
155 //
156 // The mips64 port was contributed by Imagination Technologies and is
157 // licensed under the standard MIT terms.
158 //
159 // The powerpc port was also originally written by Richard Pennington,
160 // and later supplemented and integrated by John Spencer. It is licensed
161 // under the standard MIT terms.
162 //
163 // All other files which have no copyright comments are original works
164 // produced specifically for use as part of this library, written either
165 // by Rich Felker, the main author of the library, or by one or more
166 // contibutors listed above. Details on authorship of individual files
167 // can be found in the git version control history of the project. The
168 // omission of copyright and license comments in each file is in the
169 // interest of source tree size.
170 //
171 // In addition, permission is hereby granted for all public header files
172 // (include/* and arch/*/bits/*) and crt files intended to be linked into
173 // applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit
174 // the copyright notice and permission notice otherwise required by the
175 // license, and to use these files without any requirement of
176 // attribution. These files include substantial contributions from:
177 //
178 // Bobby Bingham
179 // John Spencer
180 // Nicholas J. Kain
181 // Rich Felker
182 // Richard Pennington
183 // Stefan Kristiansson
184 // Szabolcs Nagy
185 //
186 // all of whom have explicitly granted such permission.
187 //
188 // This file previously contained text expressing a belief that most of
189 // the files covered by the above exception were sufficiently trivial not
190 // to be subject to copyright, resulting in confusion over whether it
191 // negated the permissions granted in the license. In the spirit of
192 // permissive licensing, and of not having licensing issues being an
193 // obstacle to adoption, that text has been removed.
194 
195 
196 use std::fmt;
197 
198 /// A date/time type which exists primarily to convert `SystemTime` timestamps into an ISO 8601
199 /// formatted string.
200 ///
201 /// Yes, this exists. Before you have a heart attack, understand that the meat of this is musl's
202 /// [`__secs_to_tm`][1] converted to Rust via [c2rust][2] and then cleaned up by hand as part of
203 /// the [kudu-rs project][3], [released under MIT][4].
204 ///
205 /// [1] http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c
206 /// [2] https://c2rust.com/
207 /// [3] https://github.com/danburkert/kudu-rs/blob/c9660067e5f4c1a54143f169b5eeb49446f82e54/src/timestamp.rs#L5-L18
208 /// [4] https://github.com/tokio-rs/tracing/issues/1644#issuecomment-963888244
209 ///
210 /// All existing `strftime`-like APIs I found were unable to handle the full range of timestamps representable
211 /// by `SystemTime`, including `strftime` itself, since tm.tm_year is an int.
212 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
213 pub(crate) struct DateTime {
214     year: i64,
215     month: u8,
216     day: u8,
217     hour: u8,
218     minute: u8,
219     second: u8,
220     nanos: u32,
221 }
222 
223 impl fmt::Display for DateTime {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result224     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225         if self.year > 9999 {
226             write!(f, "+{}", self.year)?;
227         } else if self.year < 0 {
228             write!(f, "{:05}", self.year)?;
229         } else {
230             write!(f, "{:04}", self.year)?;
231         }
232 
233         write!(
234             f,
235             "-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z",
236             self.month,
237             self.day,
238             self.hour,
239             self.minute,
240             self.second,
241             self.nanos / 1_000
242         )
243     }
244 }
245 
246 impl From<std::time::SystemTime> for DateTime {
from(timestamp: std::time::SystemTime) -> DateTime247     fn from(timestamp: std::time::SystemTime) -> DateTime {
248         let (t, nanos) = match timestamp.duration_since(std::time::UNIX_EPOCH) {
249             Ok(duration) => {
250                 debug_assert!(duration.as_secs() <= std::i64::MAX as u64);
251                 (duration.as_secs() as i64, duration.subsec_nanos())
252             }
253             Err(error) => {
254                 let duration = error.duration();
255                 debug_assert!(duration.as_secs() <= std::i64::MAX as u64);
256                 let (secs, nanos) = (duration.as_secs() as i64, duration.subsec_nanos());
257                 if nanos == 0 {
258                     (-secs, 0)
259                 } else {
260                     (-secs - 1, 1_000_000_000 - nanos)
261                 }
262             }
263         };
264 
265         // 2000-03-01 (mod 400 year, immediately after feb29
266         const LEAPOCH: i64 = 946_684_800 + 86400 * (31 + 29);
267         const DAYS_PER_400Y: i32 = 365 * 400 + 97;
268         const DAYS_PER_100Y: i32 = 365 * 100 + 24;
269         const DAYS_PER_4Y: i32 = 365 * 4 + 1;
270         static DAYS_IN_MONTH: [i8; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];
271 
272         // Note(dcb): this bit is rearranged slightly to avoid integer overflow.
273         let mut days: i64 = (t / 86_400) - (LEAPOCH / 86_400);
274         let mut remsecs: i32 = (t % 86_400) as i32;
275         if remsecs < 0i32 {
276             remsecs += 86_400;
277             days -= 1
278         }
279 
280         let mut qc_cycles: i32 = (days / i64::from(DAYS_PER_400Y)) as i32;
281         let mut remdays: i32 = (days % i64::from(DAYS_PER_400Y)) as i32;
282         if remdays < 0 {
283             remdays += DAYS_PER_400Y;
284             qc_cycles -= 1;
285         }
286 
287         let mut c_cycles: i32 = remdays / DAYS_PER_100Y;
288         if c_cycles == 4 {
289             c_cycles -= 1;
290         }
291         remdays -= c_cycles * DAYS_PER_100Y;
292 
293         let mut q_cycles: i32 = remdays / DAYS_PER_4Y;
294         if q_cycles == 25 {
295             q_cycles -= 1;
296         }
297         remdays -= q_cycles * DAYS_PER_4Y;
298 
299         let mut remyears: i32 = remdays / 365;
300         if remyears == 4 {
301             remyears -= 1;
302         }
303         remdays -= remyears * 365;
304 
305         let mut years: i64 = i64::from(remyears)
306             + 4 * i64::from(q_cycles)
307             + 100 * i64::from(c_cycles)
308             + 400 * i64::from(qc_cycles);
309 
310         let mut months: i32 = 0;
311         while i32::from(DAYS_IN_MONTH[months as usize]) <= remdays {
312             remdays -= i32::from(DAYS_IN_MONTH[months as usize]);
313             months += 1
314         }
315 
316         if months >= 10 {
317             months -= 12;
318             years += 1;
319         }
320 
321         DateTime {
322             year: years + 2000,
323             month: (months + 3) as u8,
324             day: (remdays + 1) as u8,
325             hour: (remsecs / 3600) as u8,
326             minute: (remsecs / 60 % 60) as u8,
327             second: (remsecs % 60) as u8,
328             nanos,
329         }
330     }
331 }
332 
333 #[cfg(test)]
334 mod tests {
335     use std::i32;
336     use std::time::{Duration, UNIX_EPOCH};
337 
338     use super::*;
339 
340     #[test]
test_datetime()341     fn test_datetime() {
342         let case = |expected: &str, secs: i64, micros: u32| {
343             let timestamp = if secs >= 0 {
344                 UNIX_EPOCH + Duration::new(secs as u64, micros * 1_000)
345             } else {
346                 (UNIX_EPOCH - Duration::new(!secs as u64 + 1, 0)) + Duration::new(0, micros * 1_000)
347             };
348             assert_eq!(
349                 expected,
350                 format!("{}", DateTime::from(timestamp)),
351                 "secs: {}, micros: {}",
352                 secs,
353                 micros
354             )
355         };
356 
357         // Mostly generated with:
358         //  - date -jur <secs> +"%Y-%m-%dT%H:%M:%S.000000Z"
359         //  - http://unixtimestamp.50x.eu/
360 
361         case("1970-01-01T00:00:00.000000Z", 0, 0);
362 
363         case("1970-01-01T00:00:00.000001Z", 0, 1);
364         case("1970-01-01T00:00:00.500000Z", 0, 500_000);
365         case("1970-01-01T00:00:01.000001Z", 1, 1);
366         case("1970-01-01T00:01:01.000001Z", 60 + 1, 1);
367         case("1970-01-01T01:01:01.000001Z", 60 * 60 + 60 + 1, 1);
368         case(
369             "1970-01-02T01:01:01.000001Z",
370             24 * 60 * 60 + 60 * 60 + 60 + 1,
371             1,
372         );
373 
374         case("1969-12-31T23:59:59.000000Z", -1, 0);
375         case("1969-12-31T23:59:59.000001Z", -1, 1);
376         case("1969-12-31T23:59:59.500000Z", -1, 500_000);
377         case("1969-12-31T23:58:59.000001Z", -60 - 1, 1);
378         case("1969-12-31T22:58:59.000001Z", -60 * 60 - 60 - 1, 1);
379         case(
380             "1969-12-30T22:58:59.000001Z",
381             -24 * 60 * 60 - 60 * 60 - 60 - 1,
382             1,
383         );
384 
385         case("2038-01-19T03:14:07.000000Z", std::i32::MAX as i64, 0);
386         case("2038-01-19T03:14:08.000000Z", std::i32::MAX as i64 + 1, 0);
387         case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0);
388         case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0);
389 
390         // Skipping these tests on windows as std::time::SysteTime range is low
391         // on Windows compared with that of Unix which can cause the following
392         // high date value tests to panic
393         #[cfg(not(target_os = "windows"))]
394         {
395             case("+292277026596-12-04T15:30:07.000000Z", std::i64::MAX, 0);
396             case("+292277026596-12-04T15:30:06.000000Z", std::i64::MAX - 1, 0);
397             case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0);
398         }
399 
400         case("1900-01-01T00:00:00.000000Z", -2208988800, 0);
401         case("1899-12-31T23:59:59.000000Z", -2208988801, 0);
402         case("0000-01-01T00:00:00.000000Z", -62167219200, 0);
403         case("-0001-12-31T23:59:59.000000Z", -62167219201, 0);
404 
405         case("1234-05-06T07:08:09.000000Z", -23215049511, 0);
406         case("-1234-05-06T07:08:09.000000Z", -101097651111, 0);
407         case("2345-06-07T08:09:01.000000Z", 11847456541, 0);
408         case("-2345-06-07T08:09:01.000000Z", -136154620259, 0);
409     }
410 }
411