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