| 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 { |
| 224 | 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 { |
| 247 | 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 ] |
| 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 | |