time-0.3.5/.cargo_vcs_info.json0000644000000001120000000000100117710ustar { "git": { "sha1": "6b8cdd3d9ec07c027e3aa6b9c8a69f4e6fd568dd" } } time-0.3.5/Cargo.toml0000644000000047320000000000100100030ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "time" version = "0.3.5" authors = ["Jacob Pratt ", "Time contributors"] include = ["src/**/*", "LICENSE-*", "README.md", "!src/tests.rs"] description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]." homepage = "https://time-rs.github.io" readme = "README.md" keywords = ["date", "time", "calendar", "duration"] categories = ["date-and-time", "no-std", "parser-implementations", "value-formatting"] license = "MIT OR Apache-2.0" repository = "https://github.com/time-rs/time" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "__time_03_docs"] targets = ["x86_64-unknown-linux-gnu"] [profile.dev] debug = 0 [lib] bench = false [[bench]] name = "benchmarks" path = "benchmarks/main.rs" harness = false [dependencies.itoa] version = "0.4.7" optional = true [dependencies.quickcheck-dep] version = "1.0.3" optional = true default-features = false package = "quickcheck" [dependencies.rand] version = "0.8.4" optional = true default-features = false [dependencies.serde] version = "1.0.126" optional = true default-features = false [dependencies.time-macros] version = "=0.2.3" optional = true [dev-dependencies.rand] version = "0.8.4" default-features = false [dev-dependencies.serde] version = "1.0.126" default-features = false [dev-dependencies.serde_test] version = "1.0.126" [features] alloc = [] default = ["std"] formatting = ["itoa", "std"] large-dates = ["time-macros/large-dates"] local-offset = ["std"] macros = ["time-macros"] parsing = [] quickcheck = ["quickcheck-dep", "alloc"] serde-human-readable = ["serde", "formatting", "parsing"] std = ["alloc"] [target."cfg(__ui_tests)".dev-dependencies.trybuild] version = "=1.0.34" [target."cfg(any(target_os = \"linux\", unsound_local_offset))".dependencies.libc] version = "0.2.98" [target."cfg(bench)".dev-dependencies.criterion] version = "0.3.5" [target."cfg(bench)".dev-dependencies.criterion-cycles-per-byte] version = "0.1.2" time-0.3.5/Cargo.toml.orig000064400000000000000000000041170072674642500135110ustar 00000000000000[workspace] members = ["time-macros"] resolver = "2" [package] name = "time" version = "0.3.5" authors = ["Jacob Pratt ", "Time contributors"] edition = "2018" repository = "https://github.com/time-rs/time" homepage = "https://time-rs.github.io" keywords = ["date", "time", "calendar", "duration"] categories = ["date-and-time", "no-std", "parser-implementations", "value-formatting"] readme = "README.md" license = "MIT OR Apache-2.0" description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]." include = ["src/**/*", "LICENSE-*", "README.md", "!src/tests.rs"] [lib] bench = false [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "__time_03_docs"] [features] default = ["std"] alloc = [] formatting = ["itoa", "std"] large-dates = ["time-macros/large-dates"] # use case for weak feature dependencies (rust-lang/cargo#8832) local-offset = ["std"] macros = ["time-macros"] parsing = [] quickcheck = ["quickcheck-dep", "alloc"] serde-human-readable = ["serde", "formatting", "parsing"] std = ["alloc"] [dependencies] itoa = { version = "0.4.7", optional = true } quickcheck-dep = { package = "quickcheck", version = "1.0.3", default-features = false, optional = true } rand = { version = "0.8.4", optional = true, default-features = false } serde = { version = "1.0.126", optional = true, default-features = false } time-macros = { version = "=0.2.3", path = "time-macros", optional = true } [target.'cfg(any(target_os = "linux", unsound_local_offset))'.dependencies] libc = "0.2.98" [dev-dependencies] rand = { version = "0.8.4", default-features = false } serde = { version = "1.0.126", default-features = false } serde_test = "1.0.126" [target.'cfg(__ui_tests)'.dev-dependencies] trybuild = "=1.0.34" [target.'cfg(bench)'.dev-dependencies] criterion = "0.3.5" criterion-cycles-per-byte = "0.1.2" # Significant contributions to the benchmarks provided by Emil Lundberg. [[bench]] name = "benchmarks" harness = false path = "benchmarks/main.rs" [profile.dev] debug = 0 time-0.3.5/LICENSE-Apache000064400000000000000000000261250072674642500130110ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2021 Jacob Pratt et al. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. time-0.3.5/LICENSE-MIT000064400000000000000000000020460072674642500122550ustar 00000000000000Copyright (c) 2021 Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. time-0.3.5/README.md000064400000000000000000000045120072674642500121000ustar 00000000000000# time [![minimum rustc: 1.51](https://img.shields.io/badge/minimum%20rustc-1.51-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com) [![version](https://img.shields.io/crates/v/time?color=blue&logo=rust&style=flat-square)](https://crates.io/crates/time) [![build status](https://img.shields.io/github/workflow/status/time-rs/time/Build/main?style=flat-square)](https://github.com/time-rs/time/actions) [![codecov](https://codecov.io/gh/time-rs/time/branch/main/graph/badge.svg?token=yt4XSmQNKQ)](https://codecov.io/gh/time-rs/time) Documentation: - [latest release](https://docs.rs/time) - [main branch](https://time-rs.github.io/api/time) - [book](https://time-rs.github.io/book) ## Minimum Rust version policy The time crate is guaranteed to compile with any release of rustc from the past six months. Optional feature flags that enable interoperability with third-party crates (e.g. rand) follow the policy of that crate if stricter. ## Contributing Contributions are always welcome! If you have an idea, it's best to float it by me before working on it to ensure no effort is wasted. If there's already an open issue for it, knock yourself out. Internal documentation can be viewed [here](https://time-rs.github.io/internal-api/time). If you have any questions, feel free to use [Discussions] or [Codestream]. There are a few notes inline with Codestream, though questions can be asked directly! As a bonus, they are visible and searchable for others. Feedback (prior to opening a pull request) can be provided with Codestream and [VS Live Share]. Don't hesitate to ask questions — that's what I'm here for! If using Codestream, just open up a local copy of this repository. It _should_ add you automatically. [Discussions]: https://github.com/time-rs/time/discussions [Codestream]: https://codestream.com [VS Live Share]: https://code.visualstudio.com/learn/collaboration/live-share ## License This project is licensed under either of - [Apache License, Version 2.0](https://github.com/time-rs/time/blob/main/LICENSE-Apache) - [MIT license](https://github.com/time-rs/time/blob/main/LICENSE-MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in time by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. time-0.3.5/src/date.rs000064400000000000000000000725530072674642500127050ustar 00000000000000//! The [`Date`] struct and its associated `impl`s. use core::fmt; use core::ops::{Add, Sub}; use core::time::Duration as StdDuration; #[cfg(feature = "formatting")] use std::io; #[cfg(feature = "formatting")] use crate::formatting::Formattable; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year}; use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday}; /// The minimum valid year. #[cfg(feature = "large-dates")] pub(crate) const MIN_YEAR: i32 = -999_999; /// The maximum valid year. #[cfg(feature = "large-dates")] pub(crate) const MAX_YEAR: i32 = 999_999; /// The minimum valid year. #[cfg(not(feature = "large-dates"))] pub(crate) const MIN_YEAR: i32 = -9999; /// The maximum valid year. #[cfg(not(feature = "large-dates"))] pub(crate) const MAX_YEAR: i32 = 9999; /// Date in the proleptic Gregorian calendar. /// /// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999 /// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications /// and introduces some ambiguities when parsing. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Date { /// Bitpacked field containing both the year and ordinal. // | xx | xxxxxxxxxxxxxxxxxxxxx | xxxxxxxxx | // | 2 bits | 21 bits | 9 bits | // | unassigned | year | ordinal | // The year is 15 bits when `large-dates` is not enabled. pub(crate) value: i32, } impl fmt::Debug for Date { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("Date") .field("year", &self.year()) .field("ordinal", &self.ordinal()) .finish() } } impl Date { /// The minimum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1); /// The maximum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)); // region: constructors /// Construct a `Date` from the year and ordinal values, the validity of which must be /// guaranteed by the caller. #[doc(hidden)] pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { Self { value: (year << 9) | ordinal as i32, } } /// Attempt to create a `Date` from the year, month, and day. /// /// ```rust /// # use time::{Date, Month}; /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok()); /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok()); /// ``` /// /// ```rust /// # use time::{Date, Month}; /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year. /// ``` pub const fn from_calendar_date( year: i32, month: Month, day: u8, ) -> Result { /// Cumulative days through the beginning of a month in both common and leap years. const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month)); Ok(Self::__from_ordinal_date_unchecked( year, DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] + day as u16, )) } /// Attempt to create a `Date` from the year and ordinal day number. /// /// ```rust /// # use time::Date; /// assert!(Date::from_ordinal_date(2019, 1).is_ok()); /// assert!(Date::from_ordinal_date(2019, 365).is_ok()); /// ``` /// /// ```rust /// # use time::Date; /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year. /// ``` pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result { ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year)); Ok(Self::__from_ordinal_date_unchecked(year, ordinal)) } /// Attempt to create a `Date` from the ISO year, week, and weekday. /// /// ```rust /// # use time::{Date, Weekday::*}; /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok()); /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok()); /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok()); /// ``` /// /// ```rust /// # use time::{Date, Weekday::*}; /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks. /// ``` pub const fn from_iso_week_date( year: i32, week: u8, weekday: Weekday, ) -> Result { ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year)); let adj_year = year - 1; let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + div_floor!(adj_year, 400); let jan_4 = match (raw % 7) as i8 { -6 | 1 => 8, -5 | 2 => 9, -4 | 3 => 10, -3 | 4 => 4, -2 | 5 => 5, -1 | 6 => 6, _ => 7, }; let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; Ok(if ordinal <= 0 { Self::__from_ordinal_date_unchecked( year - 1, (ordinal as u16).wrapping_add(days_in_year(year - 1)), ) } else if ordinal > days_in_year(year) as i16 { Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) } else { Self::__from_ordinal_date_unchecked(year, ordinal as _) }) } /// Create a `Date` from the Julian day. /// /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). /// /// ```rust /// # use time::{Date, macros::date}; /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24))); /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01))); /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01))); /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019 - 12 - 31))); /// ``` #[doc(alias = "from_julian_date")] pub const fn from_julian_day(julian_day: i32) -> Result { ensure_value_in_range!( julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day() ); Ok(Self::from_julian_day_unchecked(julian_day)) } /// Create a `Date` from the Julian day. /// /// This does not check the validity of the provided Julian day, and as such may result in an /// internally invalid value. #[doc(alias = "from_julian_date_unchecked")] pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self { #![allow(trivial_numeric_casts)] // cast depends on type alias /// A type that is either `i32` or `i64`. This subtle difference allows for optimization /// based on the valid values. #[cfg(feature = "large-dates")] type MaybeWidened = i64; #[allow(clippy::missing_docs_in_private_items)] #[cfg(not(feature = "large-dates"))] type MaybeWidened = i32; // To avoid a potential overflow, the value may need to be widened for some arithmetic. let z = julian_day - 1_721_119; let g = 100 * z as MaybeWidened - 25; let a = (g / 3_652_425) as i32; let b = a - a / 4; let mut year = div_floor!(100 * b as MaybeWidened + g, 36525) as i32; let mut ordinal = (b + z - div_floor!(36525 * year as MaybeWidened, 100) as i32) as _; if is_leap_year(year) { ordinal += 60; cascade!(ordinal in 1..367 => year); } else { ordinal += 59; cascade!(ordinal in 1..366 => year); } Self::__from_ordinal_date_unchecked(year, ordinal) } // endregion constructors // region: getters /// Get the year of the date. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).year(), 2019); /// assert_eq!(date!(2019 - 12 - 31).year(), 2019); /// assert_eq!(date!(2020 - 01 - 01).year(), 2020); /// ``` pub const fn year(self) -> i32 { self.value >> 9 } /// Get the month. /// /// ```rust /// # use time::{macros::date, Month}; /// assert_eq!(date!(2019 - 01 - 01).month(), Month::January); /// assert_eq!(date!(2019 - 12 - 31).month(), Month::December); /// ``` pub const fn month(self) -> Month { self.month_day().0 } /// Get the day of the month. /// /// The returned value will always be in the range `1..=31`. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).day(), 1); /// assert_eq!(date!(2019 - 12 - 31).day(), 31); /// ``` pub const fn day(self) -> u8 { self.month_day().1 } /// Get the month and day. This is more efficient than fetching the components individually. // For whatever reason, rustc has difficulty optimizing this function. It's significantly faster // to write the statements out by hand. pub(crate) const fn month_day(self) -> (Month, u8) { /// The number of days up to and including the given month. Common years /// are first, followed by leap years. const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [ [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year()) as usize]; let ordinal = self.ordinal(); if ordinal > days[10] { (Month::December, (ordinal - days[10]) as _) } else if ordinal > days[9] { (Month::November, (ordinal - days[9]) as _) } else if ordinal > days[8] { (Month::October, (ordinal - days[8]) as _) } else if ordinal > days[7] { (Month::September, (ordinal - days[7]) as _) } else if ordinal > days[6] { (Month::August, (ordinal - days[6]) as _) } else if ordinal > days[5] { (Month::July, (ordinal - days[5]) as _) } else if ordinal > days[4] { (Month::June, (ordinal - days[4]) as _) } else if ordinal > days[3] { (Month::May, (ordinal - days[3]) as _) } else if ordinal > days[2] { (Month::April, (ordinal - days[2]) as _) } else if ordinal > days[1] { (Month::March, (ordinal - days[1]) as _) } else if ordinal > days[0] { (Month::February, (ordinal - days[0]) as _) } else { (Month::January, ordinal as _) } } /// Get the day of the year. /// /// The returned value will always be in the range `1..=366` (`1..=365` for common years). /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1); /// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365); /// ``` pub const fn ordinal(self) -> u16 { (self.value & 0x1FF) as _ } /// Get the ISO 8601 year and week number. pub(crate) const fn iso_year_week(self) -> (i32, u8) { let (year, ordinal) = self.to_ordinal_date(); match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ { 0 => (year - 1, weeks_in_year(year - 1)), 53 if weeks_in_year(year) == 52 => (year + 1, 1), week => (year, week), } } /// Get the ISO week number. /// /// The returned value will always be in the range `1..=53`. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).iso_week(), 1); /// assert_eq!(date!(2019 - 10 - 04).iso_week(), 40); /// assert_eq!(date!(2020 - 01 - 01).iso_week(), 1); /// assert_eq!(date!(2020 - 12 - 31).iso_week(), 53); /// assert_eq!(date!(2021 - 01 - 01).iso_week(), 53); /// ``` pub const fn iso_week(self) -> u8 { self.iso_year_week().1 } /// Get the week number where week 1 begins on the first Sunday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).sunday_based_week(), 0); /// assert_eq!(date!(2020 - 01 - 01).sunday_based_week(), 0); /// assert_eq!(date!(2020 - 12 - 31).sunday_based_week(), 52); /// assert_eq!(date!(2021 - 01 - 01).sunday_based_week(), 0); /// ``` pub const fn sunday_based_week(self) -> u8 { ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as _ } /// Get the week number where week 1 begins on the first Monday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).monday_based_week(), 0); /// assert_eq!(date!(2020 - 01 - 01).monday_based_week(), 0); /// assert_eq!(date!(2020 - 12 - 31).monday_based_week(), 52); /// assert_eq!(date!(2021 - 01 - 01).monday_based_week(), 0); /// ``` pub const fn monday_based_week(self) -> u8 { ((self.ordinal() as i16 - self.weekday().number_days_from_monday() as i16 + 6) / 7) as _ } /// Get the year, month, and day. /// /// ```rust /// # use time::{macros::date, Month}; /// assert_eq!( /// date!(2019 - 01 - 01).to_calendar_date(), /// (2019, Month::January, 1) /// ); /// ``` pub const fn to_calendar_date(self) -> (i32, Month, u8) { let (month, day) = self.month_day(); (self.year(), month, day) } /// Get the year and ordinal day number. /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(2019 - 01 - 01).to_ordinal_date(), (2019, 1)); /// ``` pub const fn to_ordinal_date(self) -> (i32, u16) { (self.year(), self.ordinal()) } /// Get the ISO 8601 year, week number, and weekday. /// /// ```rust /// # use time::{Weekday::*, macros::date}; /// assert_eq!(date!(2019 - 01 - 01).to_iso_week_date(), (2019, 1, Tuesday)); /// assert_eq!(date!(2019 - 10 - 04).to_iso_week_date(), (2019, 40, Friday)); /// assert_eq!( /// date!(2020 - 01 - 01).to_iso_week_date(), /// (2020, 1, Wednesday) /// ); /// assert_eq!( /// date!(2020 - 12 - 31).to_iso_week_date(), /// (2020, 53, Thursday) /// ); /// assert_eq!(date!(2021 - 01 - 01).to_iso_week_date(), (2020, 53, Friday)); /// ``` pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { let (year, ordinal) = self.to_ordinal_date(); let weekday = self.weekday(); match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ { 0 => (year - 1, weeks_in_year(year - 1), weekday), 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday), week => (year, week, weekday), } } /// Get the weekday. /// /// ```rust /// # use time::{Weekday::*, macros::date}; /// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday); /// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday); /// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday); /// assert_eq!(date!(2019 - 04 - 01).weekday(), Monday); /// assert_eq!(date!(2019 - 05 - 01).weekday(), Wednesday); /// assert_eq!(date!(2019 - 06 - 01).weekday(), Saturday); /// assert_eq!(date!(2019 - 07 - 01).weekday(), Monday); /// assert_eq!(date!(2019 - 08 - 01).weekday(), Thursday); /// assert_eq!(date!(2019 - 09 - 01).weekday(), Sunday); /// assert_eq!(date!(2019 - 10 - 01).weekday(), Tuesday); /// assert_eq!(date!(2019 - 11 - 01).weekday(), Friday); /// assert_eq!(date!(2019 - 12 - 01).weekday(), Sunday); /// ``` pub const fn weekday(self) -> Weekday { match self.to_julian_day() % 7 { -6 | 1 => Weekday::Tuesday, -5 | 2 => Weekday::Wednesday, -4 | 3 => Weekday::Thursday, -3 | 4 => Weekday::Friday, -2 | 5 => Weekday::Saturday, -1 | 6 => Weekday::Sunday, _ => Weekday::Monday, } } /// Get the next calendar date. /// /// ```rust /// # use time::{Date, macros::date}; /// assert_eq!( /// date!(2019 - 01 - 01).next_day(), /// Some(date!(2019 - 01 - 02)) /// ); /// assert_eq!( /// date!(2019 - 01 - 31).next_day(), /// Some(date!(2019 - 02 - 01)) /// ); /// assert_eq!( /// date!(2019 - 12 - 31).next_day(), /// Some(date!(2020 - 01 - 01)) /// ); /// assert_eq!(Date::MAX.next_day(), None); /// ``` pub const fn next_day(self) -> Option { if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) { if self.value == Self::MAX.value { None } else { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) } } else { Some(Self { value: self.value + 1, }) } } /// Get the previous calendar date. /// /// ```rust /// # use time::{Date, macros::date}; /// assert_eq!( /// date!(2019 - 01 - 02).previous_day(), /// Some(date!(2019 - 01 - 01)) /// ); /// assert_eq!( /// date!(2019 - 02 - 01).previous_day(), /// Some(date!(2019 - 01 - 31)) /// ); /// assert_eq!( /// date!(2020 - 01 - 01).previous_day(), /// Some(date!(2019 - 12 - 31)) /// ); /// assert_eq!(Date::MIN.previous_day(), None); /// ``` pub const fn previous_day(self) -> Option { if self.ordinal() != 1 { Some(Self { value: self.value - 1, }) } else if self.value == Self::MIN.value { None } else { Some(Self::__from_ordinal_date_unchecked( self.year() - 1, days_in_year(self.year() - 1), )) } } /// Get the Julian day for the date. /// /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). /// /// ```rust /// # use time::macros::date; /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0); /// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545); /// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485); /// assert_eq!(date!(2019 - 12 - 31).to_julian_day(), 2_458_849); /// ``` pub const fn to_julian_day(self) -> i32 { let year = self.year() - 1; let ordinal = self.ordinal() as i32; ordinal + 365 * year + div_floor!(year, 4) - div_floor!(year, 100) + div_floor!(year, 400) + 1_721_425 } // endregion getters // region: checked arithmetic /// Computes `self + duration`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Date, ext::NumericalDuration, macros::date}; /// assert_eq!(Date::MAX.checked_add(1.days()), None); /// assert_eq!(Date::MIN.checked_add((-2).days()), None); /// assert_eq!( /// date!(2020 - 12 - 31).checked_add(2.days()), /// Some(date!(2021 - 01 - 02)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ```rust /// # use time::{Date, ext::NumericalDuration, macros::date}; /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX)); /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN)); /// assert_eq!( /// date!(2020 - 12 - 31).checked_add(23.hours()), /// Some(date!(2020 - 12 - 31)) /// ); /// assert_eq!( /// date!(2020 - 12 - 31).checked_add(47.hours()), /// Some(date!(2021 - 01 - 01)) /// ); /// ``` pub const fn checked_add(self, duration: Duration) -> Option { let whole_days = duration.whole_days(); if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration, macros::date}; /// assert_eq!(Date::MAX.checked_sub((-2).days()), None); /// assert_eq!(Date::MIN.checked_sub(1.days()), None); /// assert_eq!( /// date!(2020 - 12 - 31).checked_sub(2.days()), /// Some(date!(2020 - 12 - 29)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ``` /// # use time::{Date, ext::NumericalDuration, macros::date}; /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX)); /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN)); /// assert_eq!( /// date!(2020 - 12 - 31).checked_sub(23.hours()), /// Some(date!(2020 - 12 - 31)) /// ); /// assert_eq!( /// date!(2020 - 12 - 31).checked_sub(47.hours()), /// Some(date!(2020 - 12 - 30)) /// ); /// ``` pub const fn checked_sub(self, duration: Duration) -> Option { let whole_days = duration.whole_days(); if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } // endregion: checked arithmetic } // region: attach time /// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`]. impl Date { /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set /// to midnight. /// /// ```rust /// # use time::macros::{date, datetime}; /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00)); /// ``` pub const fn midnight(self) -> PrimitiveDateTime { PrimitiveDateTime::new(self, Time::MIDNIGHT) } /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`]. /// /// ```rust /// # use time::macros::{date, datetime, time}; /// assert_eq!( /// date!(1970-01-01).with_time(time!(0:00)), /// datetime!(1970-01-01 0:00), /// ); /// ``` pub const fn with_time(self, time: Time) -> PrimitiveDateTime { PrimitiveDateTime::new(self, time) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time::macros::date; /// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok()); /// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err()); /// ``` pub const fn with_hms( self, hour: u8, minute: u8, second: u8, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms(hour, minute, second)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time::macros::date; /// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok()); /// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err()); /// ``` pub const fn with_hms_milli( self, hour: u8, minute: u8, second: u8, millisecond: u16, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_milli(hour, minute, second, millisecond)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time::macros::date; /// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok()); /// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err()); /// ``` pub const fn with_hms_micro( self, hour: u8, minute: u8, second: u8, microsecond: u32, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_micro(hour, minute, second, microsecond)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time::macros::date; /// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok()); /// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err()); /// ``` pub const fn with_hms_nano( self, hour: u8, minute: u8, second: u8, nanosecond: u32, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)), )) } } // endregion attach time // region: formatting & parsing #[cfg(feature = "formatting")] impl Date { /// Format the `Date` using the provided [format description](crate::format_description). pub fn format_into( self, output: &mut impl io::Write, format: &(impl Formattable + ?Sized), ) -> Result { format.format_into(output, Some(self), None, None) } /// Format the `Date` using the provided [format description](crate::format_description). /// /// ```rust /// # use time::{format_description, macros::date}; /// let format = format_description::parse("[year]-[month]-[day]")?; /// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02"); /// # Ok::<_, time::Error>(()) /// ``` pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { format.format(Some(self), None, None) } } #[cfg(feature = "parsing")] impl Date { /// Parse a `Date` from the input using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::{format_description, macros::date, Date}; /// let format = format_description::parse("[year]-[month]-[day]")?; /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02)); /// # Ok::<_, time::Error>(()) /// ``` pub fn parse( input: &str, description: &(impl Parsable + ?Sized), ) -> Result { description.parse_date(input.as_bytes()) } } impl fmt::Display for Date { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if cfg!(feature = "large-dates") && self.year().abs() >= 10_000 { write!( f, "{:+}-{:02}-{:02}", self.year(), self.month() as u8, self.day() ) } else { write!( f, "{:0width$}-{:02}-{:02}", self.year(), self.month() as u8, self.day(), width = 4 + (self.year() < 0) as usize ) } } } // endregion formatting & parsing // region: trait impls impl Add for Date { type Output = Self; fn add(self, duration: Duration) -> Self::Output { self.checked_add(duration) .expect("overflow adding duration to date") } } impl Add for Date { type Output = Self; fn add(self, duration: StdDuration) -> Self::Output { Self::from_julian_day(self.to_julian_day() + (duration.as_secs() / 86_400) as i32) .expect("overflow adding duration to date") } } impl_add_assign!(Date: Duration, StdDuration); impl Sub for Date { type Output = Self; fn sub(self, duration: Duration) -> Self::Output { self.checked_sub(duration) .expect("overflow subtracting duration from date") } } impl Sub for Date { type Output = Self; fn sub(self, duration: StdDuration) -> Self::Output { Self::from_julian_day(self.to_julian_day() - (duration.as_secs() / 86_400) as i32) .expect("overflow subtracting duration from date") } } impl_sub_assign!(Date: Duration, StdDuration); impl Sub for Date { type Output = Duration; fn sub(self, other: Self) -> Self::Output { Duration::days((self.to_julian_day() - other.to_julian_day()) as _) } } // endregion trait impls time-0.3.5/src/duration.rs000064400000000000000000001010030072674642500135740ustar 00000000000000//! The [`Duration`] struct and its associated `impl`s. use core::cmp::Ordering; use core::convert::{TryFrom, TryInto}; use core::fmt; use core::iter::Sum; use core::ops::{Add, Div, Mul, Neg, Sub, SubAssign}; use core::time::Duration as StdDuration; use crate::error; #[cfg(feature = "std")] use crate::Instant; /// By explicitly inserting this enum where padding is expected, the compiler is able to better /// perform niche value optimization. #[repr(u32)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Padding { #[allow(clippy::missing_docs_in_private_items)] Optimize, } impl Default for Padding { fn default() -> Self { Self::Optimize } } /// A span of time with nanosecond precision. /// /// Each `Duration` is composed of a whole number of seconds and a fractional part represented in /// nanoseconds. /// /// This implementation allows for negative durations, unlike [`core::time::Duration`]. #[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Duration { /// Number of whole seconds. seconds: i64, /// Number of nanoseconds within the second. The sign always matches the `seconds` field. nanoseconds: i32, // always -10^9 < nanoseconds < 10^9 #[allow(clippy::missing_docs_in_private_items)] padding: Padding, } impl fmt::Debug for Duration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Duration") .field("seconds", &self.seconds) .field("nanoseconds", &self.nanoseconds) .finish() } } impl Duration { // region: constants /// Equivalent to `0.seconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::ZERO, 0.seconds()); /// ``` pub const ZERO: Self = Self::seconds(0); /// Equivalent to `1.nanoseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::NANOSECOND, 1.nanoseconds()); /// ``` pub const NANOSECOND: Self = Self::nanoseconds(1); /// Equivalent to `1.microseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MICROSECOND, 1.microseconds()); /// ``` pub const MICROSECOND: Self = Self::microseconds(1); /// Equivalent to `1.milliseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MILLISECOND, 1.milliseconds()); /// ``` pub const MILLISECOND: Self = Self::milliseconds(1); /// Equivalent to `1.seconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::SECOND, 1.seconds()); /// ``` pub const SECOND: Self = Self::seconds(1); /// Equivalent to `1.minutes()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MINUTE, 1.minutes()); /// ``` pub const MINUTE: Self = Self::minutes(1); /// Equivalent to `1.hours()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::HOUR, 1.hours()); /// ``` pub const HOUR: Self = Self::hours(1); /// Equivalent to `1.days()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::DAY, 1.days()); /// ``` pub const DAY: Self = Self::days(1); /// Equivalent to `1.weeks()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::WEEK, 1.weeks()); /// ``` pub const WEEK: Self = Self::weeks(1); /// The minimum possible duration. Adding any negative duration to this will cause an overflow. pub const MIN: Self = Self::new_unchecked(i64::MIN, -999_999_999); /// The maximum possible duration. Adding any positive duration to this will cause an overflow. pub const MAX: Self = Self::new_unchecked(i64::MAX, 999_999_999); // endregion constants // region: is_{sign} /// Check if a duration is exactly zero. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!(0.seconds().is_zero()); /// assert!(!1.nanoseconds().is_zero()); /// ``` pub const fn is_zero(self) -> bool { self.seconds == 0 && self.nanoseconds == 0 } /// Check if a duration is negative. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!((-1).seconds().is_negative()); /// assert!(!0.seconds().is_negative()); /// assert!(!1.seconds().is_negative()); /// ``` pub const fn is_negative(self) -> bool { self.seconds < 0 || self.nanoseconds < 0 } /// Check if a duration is positive. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!(1.seconds().is_positive()); /// assert!(!0.seconds().is_positive()); /// assert!(!(-1).seconds().is_positive()); /// ``` pub const fn is_positive(self) -> bool { self.seconds > 0 || self.nanoseconds > 0 } // endregion is_{sign} // region: abs /// Get the absolute value of the duration. /// /// This method saturates the returned value if it would otherwise overflow. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().abs(), 1.seconds()); /// assert_eq!(0.seconds().abs(), 0.seconds()); /// assert_eq!((-1).seconds().abs(), 1.seconds()); /// ``` pub const fn abs(self) -> Self { Self::new_unchecked(self.seconds.saturating_abs(), self.nanoseconds.abs()) } /// Convert the existing `Duration` to a `std::time::Duration` and its sign. This doesn't /// actually require the standard library, but is currently only used when it's enabled. #[allow(clippy::missing_const_for_fn)] // false positive #[cfg(feature = "std")] pub(crate) fn abs_std(self) -> StdDuration { StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs()) } // endregion abs // region: constructors /// Create a new `Duration` without checking the validity of the components. pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { Self { seconds, nanoseconds, padding: Padding::Optimize, } } /// Create a new `Duration` with the provided seconds and nanoseconds. If nanoseconds is at /// least ±109, it will wrap to the number of seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::new(1, 0), 1.seconds()); /// assert_eq!(Duration::new(-1, 0), (-1).seconds()); /// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds()); /// ``` pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { seconds += nanoseconds as i64 / 1_000_000_000; nanoseconds %= 1_000_000_000; if seconds > 0 && nanoseconds < 0 { seconds -= 1; nanoseconds += 1_000_000_000; } else if seconds < 0 && nanoseconds > 0 { seconds += 1; nanoseconds -= 1_000_000_000; } Self::new_unchecked(seconds, nanoseconds) } /// Create a new `Duration` with the given number of weeks. Equivalent to /// `Duration::seconds(weeks * 604_800)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::weeks(1), 604_800.seconds()); /// ``` pub const fn weeks(weeks: i64) -> Self { Self::seconds(weeks * 604_800) } /// Create a new `Duration` with the given number of days. Equivalent to /// `Duration::seconds(days * 86_400)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::days(1), 86_400.seconds()); /// ``` pub const fn days(days: i64) -> Self { Self::seconds(days * 86_400) } /// Create a new `Duration` with the given number of hours. Equivalent to /// `Duration::seconds(hours * 3_600)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::hours(1), 3_600.seconds()); /// ``` pub const fn hours(hours: i64) -> Self { Self::seconds(hours * 3_600) } /// Create a new `Duration` with the given number of minutes. Equivalent to /// `Duration::seconds(minutes * 60)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::minutes(1), 60.seconds()); /// ``` pub const fn minutes(minutes: i64) -> Self { Self::seconds(minutes * 60) } /// Create a new `Duration` with the given number of seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds(1), 1_000.milliseconds()); /// ``` pub const fn seconds(seconds: i64) -> Self { Self::new_unchecked(seconds, 0) } /// Creates a new `Duration` from the specified number of seconds represented as `f64`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds()); /// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds()); /// ``` pub fn seconds_f64(seconds: f64) -> Self { Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _) } /// Creates a new `Duration` from the specified number of seconds represented as `f32`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds_f32(0.5), 0.5.seconds()); /// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds()); /// ``` pub fn seconds_f32(seconds: f32) -> Self { Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _) } /// Create a new `Duration` with the given number of milliseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::milliseconds(1), 1_000.microseconds()); /// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds()); /// ``` pub const fn milliseconds(milliseconds: i64) -> Self { Self::new_unchecked( milliseconds / 1_000, ((milliseconds % 1_000) * 1_000_000) as _, ) } /// Create a new `Duration` with the given number of microseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::microseconds(1), 1_000.nanoseconds()); /// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds()); /// ``` pub const fn microseconds(microseconds: i64) -> Self { Self::new_unchecked( microseconds / 1_000_000, ((microseconds % 1_000_000) * 1_000) as _, ) } /// Create a new `Duration` with the given number of nanoseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::nanoseconds(1), 1.microseconds() / 1_000); /// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000); /// ``` pub const fn nanoseconds(nanoseconds: i64) -> Self { Self::new_unchecked( nanoseconds / 1_000_000_000, (nanoseconds % 1_000_000_000) as _, ) } /// Create a new `Duration` with the given number of nanoseconds. /// /// As the input range cannot be fully mapped to the output, this should only be used where it's /// known to result in a valid value. pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self { Self::new_unchecked( (nanoseconds / 1_000_000_000) as _, (nanoseconds % 1_000_000_000) as _, ) } // endregion constructors // region: getters /// Get the number of whole weeks in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.weeks().whole_weeks(), 1); /// assert_eq!((-1).weeks().whole_weeks(), -1); /// assert_eq!(6.days().whole_weeks(), 0); /// assert_eq!((-6).days().whole_weeks(), 0); /// ``` pub const fn whole_weeks(self) -> i64 { self.whole_seconds() / 604_800 } /// Get the number of whole days in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.days().whole_days(), 1); /// assert_eq!((-1).days().whole_days(), -1); /// assert_eq!(23.hours().whole_days(), 0); /// assert_eq!((-23).hours().whole_days(), 0); /// ``` pub const fn whole_days(self) -> i64 { self.whole_seconds() / 86_400 } /// Get the number of whole hours in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.hours().whole_hours(), 1); /// assert_eq!((-1).hours().whole_hours(), -1); /// assert_eq!(59.minutes().whole_hours(), 0); /// assert_eq!((-59).minutes().whole_hours(), 0); /// ``` pub const fn whole_hours(self) -> i64 { self.whole_seconds() / 3_600 } /// Get the number of whole minutes in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.minutes().whole_minutes(), 1); /// assert_eq!((-1).minutes().whole_minutes(), -1); /// assert_eq!(59.seconds().whole_minutes(), 0); /// assert_eq!((-59).seconds().whole_minutes(), 0); /// ``` pub const fn whole_minutes(self) -> i64 { self.whole_seconds() / 60 } /// Get the number of whole seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().whole_seconds(), 1); /// assert_eq!((-1).seconds().whole_seconds(), -1); /// assert_eq!(1.minutes().whole_seconds(), 60); /// assert_eq!((-1).minutes().whole_seconds(), -60); /// ``` pub const fn whole_seconds(self) -> i64 { self.seconds } /// Get the number of fractional seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5); /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); /// ``` pub fn as_seconds_f64(self) -> f64 { self.seconds as f64 + self.nanoseconds as f64 / 1_000_000_000. } /// Get the number of fractional seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5); /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); /// ``` pub fn as_seconds_f32(self) -> f32 { self.seconds as f32 + self.nanoseconds as f32 / 1_000_000_000. } /// Get the number of whole milliseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().whole_milliseconds(), 1_000); /// assert_eq!((-1).seconds().whole_milliseconds(), -1_000); /// assert_eq!(1.milliseconds().whole_milliseconds(), 1); /// assert_eq!((-1).milliseconds().whole_milliseconds(), -1); /// ``` pub const fn whole_milliseconds(self) -> i128 { self.seconds as i128 * 1_000 + self.nanoseconds as i128 / 1_000_000 } /// Get the number of milliseconds past the number of whole seconds. /// /// Always in the range `-1_000..1_000`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.4.seconds().subsec_milliseconds(), 400); /// assert_eq!((-1.4).seconds().subsec_milliseconds(), -400); /// ``` // Allow the lint, as the value is guaranteed to be less than 1000. pub const fn subsec_milliseconds(self) -> i16 { (self.nanoseconds / 1_000_000) as _ } /// Get the number of whole microseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.milliseconds().whole_microseconds(), 1_000); /// assert_eq!((-1).milliseconds().whole_microseconds(), -1_000); /// assert_eq!(1.microseconds().whole_microseconds(), 1); /// assert_eq!((-1).microseconds().whole_microseconds(), -1); /// ``` pub const fn whole_microseconds(self) -> i128 { self.seconds as i128 * 1_000_000 + self.nanoseconds as i128 / 1_000 } /// Get the number of microseconds past the number of whole seconds. /// /// Always in the range `-1_000_000..1_000_000`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.0004.seconds().subsec_microseconds(), 400); /// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400); /// ``` pub const fn subsec_microseconds(self) -> i32 { self.nanoseconds / 1_000 } /// Get the number of nanoseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000); /// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000); /// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1); /// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1); /// ``` pub const fn whole_nanoseconds(self) -> i128 { self.seconds as i128 * 1_000_000_000 + self.nanoseconds as i128 } /// Get the number of nanoseconds past the number of whole seconds. /// /// The returned value will always be in the range `-1_000_000_000..1_000_000_000`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.000_000_400.seconds().subsec_nanoseconds(), 400); /// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400); /// ``` pub const fn subsec_nanoseconds(self) -> i32 { self.nanoseconds } // endregion getters // region: checked arithmetic /// Computes `self + rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_add(5.seconds()), Some(10.seconds())); /// assert_eq!(Duration::MAX.checked_add(1.nanoseconds()), None); /// assert_eq!((-5).seconds().checked_add(5.seconds()), Some(0.seconds())); /// ``` pub const fn checked_add(self, rhs: Self) -> Option { let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds)); let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { nanoseconds -= 1_000_000_000; seconds = const_try_opt!(seconds.checked_add(1)); } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { nanoseconds += 1_000_000_000; seconds = const_try_opt!(seconds.checked_sub(1)); } Some(Self::new_unchecked(seconds, nanoseconds)) } /// Computes `self - rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_sub(5.seconds()), Some(Duration::ZERO)); /// assert_eq!(Duration::MIN.checked_sub(1.nanoseconds()), None); /// assert_eq!(5.seconds().checked_sub(10.seconds()), Some((-5).seconds())); /// ``` pub const fn checked_sub(self, rhs: Self) -> Option { let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds)); let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { nanoseconds -= 1_000_000_000; seconds = const_try_opt!(seconds.checked_add(1)); } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { nanoseconds += 1_000_000_000; seconds = const_try_opt!(seconds.checked_sub(1)); } Some(Self::new_unchecked(seconds, nanoseconds)) } /// Computes `self * rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_mul(2), Some(10.seconds())); /// assert_eq!(5.seconds().checked_mul(-2), Some((-10).seconds())); /// assert_eq!(5.seconds().checked_mul(0), Some(0.seconds())); /// assert_eq!(Duration::MAX.checked_mul(2), None); /// assert_eq!(Duration::MIN.checked_mul(2), None); /// ``` pub const fn checked_mul(self, rhs: i32) -> Option { // Multiply nanoseconds as i64, because it cannot overflow that way. let total_nanos = self.nanoseconds as i64 * rhs as i64; let extra_secs = total_nanos / 1_000_000_000; let nanoseconds = (total_nanos % 1_000_000_000) as _; let seconds = const_try_opt!( const_try_opt!(self.seconds.checked_mul(rhs as _)).checked_add(extra_secs) ); Some(Self::new_unchecked(seconds, nanoseconds)) } /// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(10.seconds().checked_div(2), Some(5.seconds())); /// assert_eq!(10.seconds().checked_div(-2), Some((-5).seconds())); /// assert_eq!(1.seconds().checked_div(0), None); /// ``` #[allow(clippy::missing_const_for_fn)] // requires Rust 1.52 pub fn checked_div(self, rhs: i32) -> Option { let seconds = const_try_opt!(self.seconds.checked_div(rhs as i64)); let carry = self.seconds - seconds * (rhs as i64); let extra_nanos = const_try_opt!((carry * 1_000_000_000).checked_div(rhs as i64)); let nanoseconds = const_try_opt!(self.nanoseconds.checked_div(rhs)) + (extra_nanos as i32); Some(Self::new_unchecked(seconds, nanoseconds)) } // endregion checked arithmetic // region: saturating arithmetic /// Computes `self + rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_add(5.seconds()), 10.seconds()); /// assert_eq!(Duration::MAX.saturating_add(1.nanoseconds()), Duration::MAX); /// assert_eq!( /// Duration::MIN.saturating_add((-1).nanoseconds()), /// Duration::MIN /// ); /// assert_eq!((-5).seconds().saturating_add(5.seconds()), Duration::ZERO); /// ``` pub const fn saturating_add(self, rhs: Self) -> Self { let (mut seconds, overflow) = self.seconds.overflowing_add(rhs.seconds); if overflow { if self.seconds > 0 { return Self::MAX; } return Self::MIN; } let mut nanoseconds = self.nanoseconds + rhs.nanoseconds; if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { nanoseconds -= 1_000_000_000; seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { nanoseconds += 1_000_000_000; seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } Self::new_unchecked(seconds, nanoseconds) } /// Computes `self - rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_sub(5.seconds()), Duration::ZERO); /// assert_eq!(Duration::MIN.saturating_sub(1.nanoseconds()), Duration::MIN); /// assert_eq!( /// Duration::MAX.saturating_sub((-1).nanoseconds()), /// Duration::MAX /// ); /// assert_eq!(5.seconds().saturating_sub(10.seconds()), (-5).seconds()); /// ``` pub const fn saturating_sub(self, rhs: Self) -> Self { let (mut seconds, overflow) = self.seconds.overflowing_sub(rhs.seconds); if overflow { if self.seconds > 0 { return Self::MAX; } return Self::MIN; } let mut nanoseconds = self.nanoseconds - rhs.nanoseconds; if nanoseconds >= 1_000_000_000 || seconds < 0 && nanoseconds > 0 { nanoseconds -= 1_000_000_000; seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; } else if nanoseconds <= -1_000_000_000 || seconds > 0 && nanoseconds < 0 { nanoseconds += 1_000_000_000; seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } Self::new_unchecked(seconds, nanoseconds) } /// Computes `self * rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_mul(2), 10.seconds()); /// assert_eq!(5.seconds().saturating_mul(-2), (-10).seconds()); /// assert_eq!(5.seconds().saturating_mul(0), Duration::ZERO); /// assert_eq!(Duration::MAX.saturating_mul(2), Duration::MAX); /// assert_eq!(Duration::MIN.saturating_mul(2), Duration::MIN); /// assert_eq!(Duration::MAX.saturating_mul(-2), Duration::MIN); /// assert_eq!(Duration::MIN.saturating_mul(-2), Duration::MAX); /// ``` pub const fn saturating_mul(self, rhs: i32) -> Self { // Multiply nanoseconds as i64, because it cannot overflow that way. let total_nanos = self.nanoseconds as i64 * rhs as i64; let extra_secs = total_nanos / 1_000_000_000; let nanoseconds = (total_nanos % 1_000_000_000) as _; let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as _); if overflow1 { if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { return Self::MAX; } return Self::MIN; } let (seconds, overflow2) = seconds.overflowing_add(extra_secs); if overflow2 { if self.seconds > 0 && rhs > 0 { return Self::MAX; } return Self::MIN; } Self::new_unchecked(seconds, nanoseconds) } // endregion saturating arithmetic /// Runs a closure, returning the duration of time it took to run. The return value of the /// closure is provided in the second part of the tuple. #[cfg(feature = "std")] pub fn time_fn(f: impl FnOnce() -> T) -> (Self, T) { let start = Instant::now(); let return_value = f(); let end = Instant::now(); (end - start, return_value) } } // region: trait impls impl TryFrom for Duration { type Error = error::ConversionRange; fn try_from(original: StdDuration) -> Result { Ok(Self::new( original .as_secs() .try_into() .map_err(|_| error::ConversionRange)?, original.subsec_nanos() as _, )) } } impl TryFrom for StdDuration { type Error = error::ConversionRange; fn try_from(duration: Duration) -> Result { Ok(Self::new( duration .seconds .try_into() .map_err(|_| error::ConversionRange)?, duration .nanoseconds .try_into() .map_err(|_| error::ConversionRange)?, )) } } impl Add for Duration { type Output = Self; fn add(self, rhs: Self) -> Self::Output { self.checked_add(rhs) .expect("overflow when adding durations") } } impl Add for Duration { type Output = Self; fn add(self, std_duration: StdDuration) -> Self::Output { self + Self::try_from(std_duration) .expect("overflow converting `std::time::Duration` to `time::Duration`") } } impl Add for StdDuration { type Output = Duration; fn add(self, rhs: Duration) -> Self::Output { rhs + self } } impl_add_assign!(Duration: Duration, StdDuration); impl Neg for Duration { type Output = Self; fn neg(self) -> Self::Output { Self::new_unchecked(-self.seconds, -self.nanoseconds) } } impl Sub for Duration { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { self.checked_sub(rhs) .expect("overflow when subtracting durations") } } impl Sub for Duration { type Output = Self; fn sub(self, rhs: StdDuration) -> Self::Output { self - Self::try_from(rhs) .expect("overflow converting `std::time::Duration` to `time::Duration`") } } impl Sub for StdDuration { type Output = Duration; fn sub(self, rhs: Duration) -> Self::Output { Duration::try_from(self) .expect("overflow converting `std::time::Duration` to `time::Duration`") - rhs } } impl_sub_assign!(Duration: Duration, StdDuration); impl SubAssign for StdDuration { fn sub_assign(&mut self, rhs: Duration) { *self = (*self - rhs).try_into().expect( "Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \ change the type.", ); } } /// Implement `Mul` (reflexively) and `Div` for `Duration` for various types. macro_rules! duration_mul_div_int { ($($type:ty),+) => {$( impl Mul<$type> for Duration { type Output = Self; fn mul(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128( self.whole_nanoseconds() .checked_mul(rhs as _) .expect("overflow when multiplying duration") ) } } impl Mul for $type { type Output = Duration; fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl Div<$type> for Duration { type Output = Self; fn div(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128(self.whole_nanoseconds() / rhs as i128) } } )+}; } duration_mul_div_int![i8, i16, i32, u8, u16, u32]; impl Mul for Duration { type Output = Self; fn mul(self, rhs: f32) -> Self::Output { Self::seconds_f32(self.as_seconds_f32() * rhs) } } impl Mul for f32 { type Output = Duration; fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl Mul for Duration { type Output = Self; fn mul(self, rhs: f64) -> Self::Output { Self::seconds_f64(self.as_seconds_f64() * rhs) } } impl Mul for f64 { type Output = Duration; fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl_mul_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); impl Div for Duration { type Output = Self; fn div(self, rhs: f32) -> Self::Output { Self::seconds_f32(self.as_seconds_f32() / rhs) } } impl Div for Duration { type Output = Self; fn div(self, rhs: f64) -> Self::Output { Self::seconds_f64(self.as_seconds_f64() / rhs) } } impl_div_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); impl Div for Duration { type Output = f64; fn div(self, rhs: Self) -> Self::Output { self.as_seconds_f64() / rhs.as_seconds_f64() } } impl Div for Duration { type Output = f64; fn div(self, rhs: StdDuration) -> Self::Output { self.as_seconds_f64() / rhs.as_secs_f64() } } impl Div for StdDuration { type Output = f64; fn div(self, rhs: Duration) -> Self::Output { self.as_secs_f64() / rhs.as_seconds_f64() } } impl PartialEq for Duration { fn eq(&self, rhs: &StdDuration) -> bool { Ok(*self) == Self::try_from(*rhs) } } impl PartialEq for StdDuration { fn eq(&self, rhs: &Duration) -> bool { rhs == self } } impl PartialOrd for Duration { fn partial_cmp(&self, rhs: &StdDuration) -> Option { if rhs.as_secs() > i64::MAX as _ { return Some(Ordering::Less); } Some( self.seconds .cmp(&(rhs.as_secs() as _)) .then_with(|| self.nanoseconds.cmp(&(rhs.subsec_nanos() as _))), ) } } impl PartialOrd for StdDuration { fn partial_cmp(&self, rhs: &Duration) -> Option { rhs.partial_cmp(self).map(Ordering::reverse) } } impl Sum for Duration { fn sum>(iter: I) -> Self { iter.reduce(|a, b| a + b).unwrap_or_default() } } impl<'a> Sum<&'a Self> for Duration { fn sum>(iter: I) -> Self { iter.copied().sum() } } // endregion trait impls time-0.3.5/src/error/component_range.rs000064400000000000000000000046400072674642500162670ustar 00000000000000//! Component range error use core::convert::TryFrom; use core::fmt; use crate::error; /// An error type indicating that a component provided to a method was out of range, causing a /// failure. // i64 is the narrowest type fitting all use cases. This eliminates the need for a type parameter. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ComponentRange { /// Name of the component. pub(crate) name: &'static str, /// Minimum allowed value, inclusive. pub(crate) minimum: i64, /// Maximum allowed value, inclusive. pub(crate) maximum: i64, /// Value that was provided. pub(crate) value: i64, /// The minimum and/or maximum value is conditional on the value of other /// parameters. pub(crate) conditional_range: bool, } impl ComponentRange { /// Obtain the name of the component whose value was out of range. pub const fn name(self) -> &'static str { self.name } } impl fmt::Display for ComponentRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} must be in the range {}..={}", self.name, self.minimum, self.maximum )?; if self.conditional_range { f.write_str(", given values of other parameters")?; } Ok(()) } } impl From for crate::Error { fn from(original: ComponentRange) -> Self { Self::ComponentRange(original) } } impl TryFrom for ComponentRange { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::ComponentRange(err) => Ok(err), _ => Err(error::DifferentVariant), } } } #[cfg(feature = "serde")] impl serde::de::Expected for ComponentRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "a value in the range {}..={}", self.minimum, self.maximum ) } } #[cfg(feature = "serde")] impl ComponentRange { /// Obtain an "invalid value" error type for the deserializer. pub(crate) fn to_invalid_serde_value<'a, D: serde::Deserializer<'a>>(self) -> D::Error { ::invalid_value( serde::de::Unexpected::Signed(self.value), &self, ) } } #[cfg(feature = "std")] impl std::error::Error for ComponentRange {} time-0.3.5/src/error/conversion_range.rs000064400000000000000000000017150072674642500164520ustar 00000000000000//! Conversion range error use core::convert::TryFrom; use core::fmt; use crate::error; /// An error type indicating that a conversion failed because the target type could not store the /// initial value. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ConversionRange; impl fmt::Display for ConversionRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Source value is out of range for the target type") } } #[cfg(feature = "std")] impl std::error::Error for ConversionRange {} impl From for crate::Error { fn from(err: ConversionRange) -> Self { Self::ConversionRange(err) } } impl TryFrom for ConversionRange { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::ConversionRange(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.5/src/error/different_variant.rs000064400000000000000000000016630072674642500166050ustar 00000000000000//! Different variant error use core::convert::TryFrom; use core::fmt; /// An error type indicating that a [`TryFrom`](core::convert::TryFrom) call failed because the /// original value was of a different variant. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DifferentVariant; impl fmt::Display for DifferentVariant { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "value was of a different variant than required") } } #[cfg(feature = "std")] impl std::error::Error for DifferentVariant {} impl From for crate::Error { fn from(err: DifferentVariant) -> Self { Self::DifferentVariant(err) } } impl TryFrom for DifferentVariant { type Error = Self; fn try_from(err: crate::Error) -> Result { match err { crate::Error::DifferentVariant(err) => Ok(err), _ => Err(Self), } } } time-0.3.5/src/error/format.rs000064400000000000000000000047330072674642500144040ustar 00000000000000//! Error formatting a struct use core::convert::TryFrom; use core::fmt; use std::io; use crate::error; /// An error occurred when formatting. #[non_exhaustive] #[allow(missing_copy_implementations)] #[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))] #[derive(Debug)] pub enum Format { /// The type being formatted does not contain sufficient information to format a component. #[non_exhaustive] InsufficientTypeInformation, /// The component named has a value that cannot be formatted into the requested format. /// /// This variant is only returned when using well-known formats. InvalidComponent(&'static str), /// A value of `std::io::Error` was returned internally. StdIo(io::Error), } impl fmt::Display for Format { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::InsufficientTypeInformation => f.write_str( "The type being formatted does not contain sufficient information to format a \ component.", ), Self::InvalidComponent(component) => write!( f, "The {} component cannot be formatted into the requested format.", component ), Self::StdIo(err) => err.fmt(f), } } } impl From for Format { fn from(err: io::Error) -> Self { Self::StdIo(err) } } impl TryFrom for io::Error { type Error = error::DifferentVariant; fn try_from(err: Format) -> Result { match err { Format::StdIo(err) => Ok(err), _ => Err(error::DifferentVariant), } } } #[cfg(feature = "std")] impl std::error::Error for Format { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match *self { Self::InsufficientTypeInformation | Self::InvalidComponent(_) => None, Self::StdIo(ref err) => Some(err), } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))] impl From for crate::Error { fn from(original: Format) -> Self { Self::Format(original) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))] impl TryFrom for Format { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::Format(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.5/src/error/indeterminate_offset.rs000064400000000000000000000021670072674642500173110ustar 00000000000000//! Indeterminate offset use core::convert::TryFrom; use core::fmt; use crate::error; /// The system's UTC offset could not be determined at the given datetime. #[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IndeterminateOffset; impl fmt::Display for IndeterminateOffset { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("The system's UTC offset could not be determined") } } #[cfg(feature = "std")] impl std::error::Error for IndeterminateOffset {} #[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))] impl From for crate::Error { fn from(err: IndeterminateOffset) -> Self { Self::IndeterminateOffset(err) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))] impl TryFrom for IndeterminateOffset { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::IndeterminateOffset(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.5/src/error/invalid_format_description.rs000064400000000000000000000055070072674642500205150ustar 00000000000000//! Invalid format description use alloc::string::String; use core::convert::TryFrom; use core::fmt; use crate::error; /// The format description provided was not valid. #[cfg_attr( __time_03_docs, doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))) )] #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq)] pub enum InvalidFormatDescription { /// There was a bracket pair that was opened but not closed. #[non_exhaustive] UnclosedOpeningBracket { /// The zero-based index of the opening bracket. index: usize, }, /// A component name is not valid. #[non_exhaustive] InvalidComponentName { /// The name of the invalid component name. name: String, /// The zero-based index the component name starts at. index: usize, }, /// A modifier is not valid. #[non_exhaustive] InvalidModifier { /// The value of the invalid modifier. value: String, /// The zero-based index the modifier starts at. index: usize, }, /// A component name is missing. #[non_exhaustive] MissingComponentName { /// The zero-based index where the component name should start. index: usize, }, } #[cfg_attr( __time_03_docs, doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))) )] impl From for crate::Error { fn from(original: InvalidFormatDescription) -> Self { Self::InvalidFormatDescription(original) } } #[cfg_attr( __time_03_docs, doc(cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))) )] impl TryFrom for InvalidFormatDescription { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::InvalidFormatDescription(err) => Ok(err), _ => Err(error::DifferentVariant), } } } impl fmt::Display for InvalidFormatDescription { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use InvalidFormatDescription::*; match self { UnclosedOpeningBracket { index } => { write!(f, "unclosed opening bracket at byte index {}", index) } InvalidComponentName { name, index } => write!( f, "invalid component name `{}` at byte index {}", name, index ), InvalidModifier { value, index } => { write!(f, "invalid modifier `{}` at byte index {}", value, index) } MissingComponentName { index } => { write!(f, "missing component name at byte index {}", index) } } } } #[cfg(feature = "std")] impl std::error::Error for InvalidFormatDescription {} time-0.3.5/src/error/mod.rs000064400000000000000000000077740072674642500137030ustar 00000000000000//! Various error types returned by methods in the time crate. mod component_range; mod conversion_range; mod different_variant; #[cfg(feature = "formatting")] mod format; #[cfg(feature = "local-offset")] mod indeterminate_offset; #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] mod invalid_format_description; #[cfg(feature = "parsing")] mod parse; #[cfg(feature = "parsing")] mod parse_from_description; #[cfg(feature = "parsing")] mod try_from_parsed; use core::fmt; pub use component_range::ComponentRange; pub use conversion_range::ConversionRange; pub use different_variant::DifferentVariant; #[cfg(feature = "formatting")] pub use format::Format; #[cfg(feature = "local-offset")] pub use indeterminate_offset::IndeterminateOffset; #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] pub use invalid_format_description::InvalidFormatDescription; #[cfg(feature = "parsing")] pub use parse::Parse; #[cfg(feature = "parsing")] pub use parse_from_description::ParseFromDescription; #[cfg(feature = "parsing")] pub use try_from_parsed::TryFromParsed; /// A unified error type for anything returned by a method in the time crate. /// /// This can be used when you either don't know or don't care about the exact error returned. /// `Result<_, time::Error>` (or its alias `time::Result<_>`) will work in these situations. #[allow(missing_copy_implementations, variant_size_differences)] #[allow(clippy::missing_docs_in_private_items)] // variants only #[non_exhaustive] #[derive(Debug)] pub enum Error { ConversionRange(ConversionRange), ComponentRange(ComponentRange), #[cfg(feature = "local-offset")] IndeterminateOffset(IndeterminateOffset), #[cfg(feature = "formatting")] Format(Format), #[cfg(feature = "parsing")] ParseFromDescription(ParseFromDescription), #[cfg(feature = "parsing")] #[non_exhaustive] UnexpectedTrailingCharacters, #[cfg(feature = "parsing")] TryFromParsed(TryFromParsed), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] InvalidFormatDescription(InvalidFormatDescription), DifferentVariant(DifferentVariant), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::ConversionRange(e) => e.fmt(f), Self::ComponentRange(e) => e.fmt(f), #[cfg(feature = "local-offset")] Self::IndeterminateOffset(e) => e.fmt(f), #[cfg(feature = "formatting")] Self::Format(e) => e.fmt(f), #[cfg(feature = "parsing")] Self::ParseFromDescription(e) => e.fmt(f), #[cfg(feature = "parsing")] Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"), #[cfg(feature = "parsing")] Self::TryFromParsed(e) => e.fmt(f), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] Self::InvalidFormatDescription(e) => e.fmt(f), Self::DifferentVariant(e) => e.fmt(f), } } } #[cfg(feature = "std")] impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::ConversionRange(err) => Some(err), Self::ComponentRange(err) => Some(err), #[cfg(feature = "local-offset")] Self::IndeterminateOffset(err) => Some(err), #[cfg(feature = "formatting")] Self::Format(err) => Some(err), #[cfg(feature = "parsing")] Self::ParseFromDescription(err) => Some(err), #[cfg(feature = "parsing")] Self::UnexpectedTrailingCharacters => None, #[cfg(feature = "parsing")] Self::TryFromParsed(err) => Some(err), #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] Self::InvalidFormatDescription(err) => Some(err), Self::DifferentVariant(err) => Some(err), } } } time-0.3.5/src/error/parse.rs000064400000000000000000000112210072674642500142140ustar 00000000000000//! Error that occurred at some stage of parsing use core::convert::TryFrom; use core::fmt; use crate::error::{self, ParseFromDescription, TryFromParsed}; /// An error that occurred at some stage of parsing. #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] #[allow(variant_size_differences)] #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Parse { #[allow(clippy::missing_docs_in_private_items)] TryFromParsed(TryFromParsed), #[allow(clippy::missing_docs_in_private_items)] ParseFromDescription(ParseFromDescription), /// The input should have ended, but there were characters remaining. #[non_exhaustive] UnexpectedTrailingCharacters, } impl fmt::Display for Parse { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::TryFromParsed(err) => err.fmt(f), Self::ParseFromDescription(err) => err.fmt(f), Self::UnexpectedTrailingCharacters => f.write_str("unexpected trailing characters"), } } } #[cfg(feature = "std")] impl std::error::Error for Parse { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::TryFromParsed(err) => Some(err), Self::ParseFromDescription(err) => Some(err), Self::UnexpectedTrailingCharacters => None, } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl From for Parse { fn from(err: TryFromParsed) -> Self { Self::TryFromParsed(err) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl TryFrom for TryFromParsed { type Error = error::DifferentVariant; fn try_from(err: Parse) -> Result { match err { Parse::TryFromParsed(err) => Ok(err), _ => Err(error::DifferentVariant), } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl From for Parse { fn from(err: ParseFromDescription) -> Self { Self::ParseFromDescription(err) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl TryFrom for ParseFromDescription { type Error = error::DifferentVariant; fn try_from(err: Parse) -> Result { match err { Parse::ParseFromDescription(err) => Ok(err), _ => Err(error::DifferentVariant), } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl From for crate::Error { fn from(err: Parse) -> Self { match err { Parse::TryFromParsed(err) => Self::TryFromParsed(err), Parse::ParseFromDescription(err) => Self::ParseFromDescription(err), Parse::UnexpectedTrailingCharacters => Self::UnexpectedTrailingCharacters, } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl TryFrom for Parse { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)), crate::Error::UnexpectedTrailingCharacters => Ok(Self::UnexpectedTrailingCharacters), crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)), _ => Err(error::DifferentVariant), } } } #[cfg(feature = "serde-human-readable")] impl Parse { /// Obtain an error type for the deserializer. pub(crate) fn to_invalid_serde_value<'a, D: serde::Deserializer<'a>>(self) -> D::Error { #[cfg(not(feature = "std"))] use alloc::format; use serde::de::Error; match self { Self::TryFromParsed(TryFromParsed::InsufficientInformation) => unreachable!( "The deserializing format contains all information needed to construct a `Time`." ), Self::TryFromParsed(TryFromParsed::ComponentRange(err)) => { err.to_invalid_serde_value::() } Self::ParseFromDescription(ParseFromDescription::InvalidLiteral) => { D::Error::invalid_value(serde::de::Unexpected::Other("literal"), &"valid format") } Self::ParseFromDescription(ParseFromDescription::InvalidComponent(component)) => { D::Error::invalid_value( serde::de::Unexpected::Other(component), &&*format!("valid {}", component), ) } Self::UnexpectedTrailingCharacters => D::Error::invalid_value( serde::de::Unexpected::Other("literal"), &"no extraneous characters", ), } } } time-0.3.5/src/error/parse_from_description.rs000064400000000000000000000031040072674642500176430ustar 00000000000000//! Error parsing an input into a [`Parsed`](crate::parsing::Parsed) struct use core::convert::TryFrom; use core::fmt; use crate::error; /// An error that occurred while parsing the input into a [`Parsed`](crate::parsing::Parsed) struct. #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ParseFromDescription { /// A string literal was not what was expected. #[non_exhaustive] InvalidLiteral, /// A dynamic component was not valid. InvalidComponent(&'static str), } impl fmt::Display for ParseFromDescription { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::InvalidLiteral => f.write_str("a character literal was not valid"), Self::InvalidComponent(name) => { write!(f, "the '{}' component could not be parsed", name) } } } } #[cfg(feature = "std")] impl std::error::Error for ParseFromDescription {} #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl From for crate::Error { fn from(original: ParseFromDescription) -> Self { Self::ParseFromDescription(original) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl TryFrom for ParseFromDescription { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::ParseFromDescription(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.5/src/error/try_from_parsed.rs000064400000000000000000000043620072674642500163110ustar 00000000000000//! Error converting a [`Parsed`](crate::parsing::Parsed) struct to another type use core::convert::TryFrom; use core::fmt; use crate::error; /// An error that occurred when converting a [`Parsed`](crate::parsing::Parsed) to another type. #[non_exhaustive] #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TryFromParsed { /// The [`Parsed`](crate::parsing::Parsed) did not include enough information to construct the /// type. InsufficientInformation, /// Some component contained an invalid value for the type. ComponentRange(error::ComponentRange), } impl fmt::Display for TryFromParsed { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::InsufficientInformation => f.write_str( "the `Parsed` struct did not include enough information to construct the type", ), Self::ComponentRange(err) => err.fmt(f), } } } impl From for TryFromParsed { fn from(v: error::ComponentRange) -> Self { Self::ComponentRange(v) } } impl TryFrom for error::ComponentRange { type Error = error::DifferentVariant; fn try_from(err: TryFromParsed) -> Result { match err { TryFromParsed::ComponentRange(err) => Ok(err), _ => Err(error::DifferentVariant), } } } #[cfg(feature = "std")] impl std::error::Error for TryFromParsed { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::InsufficientInformation => None, Self::ComponentRange(err) => Some(err), } } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl From for crate::Error { fn from(original: TryFromParsed) -> Self { Self::TryFromParsed(original) } } #[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))] impl TryFrom for TryFromParsed { type Error = error::DifferentVariant; fn try_from(err: crate::Error) -> Result { match err { crate::Error::TryFromParsed(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.5/src/ext.rs000064400000000000000000000206070072674642500125610ustar 00000000000000//! Extension traits. use core::time::Duration as StdDuration; use crate::Duration; /// Sealed trait to prevent downstream implementations. mod sealed { /// A trait that cannot be implemented by downstream users. pub trait Sealed {} impl Sealed for i64 {} impl Sealed for u64 {} impl Sealed for f64 {} } // region: NumericalDuration /// Create [`Duration`]s from numeric literals. /// /// # Examples /// /// Basic construction of [`Duration`]s. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5)); /// assert_eq!(5.microseconds(), Duration::microseconds(5)); /// assert_eq!(5.milliseconds(), Duration::milliseconds(5)); /// assert_eq!(5.seconds(), Duration::seconds(5)); /// assert_eq!(5.minutes(), Duration::minutes(5)); /// assert_eq!(5.hours(), Duration::hours(5)); /// assert_eq!(5.days(), Duration::days(5)); /// assert_eq!(5.weeks(), Duration::weeks(5)); /// ``` /// /// Signed integers work as well! /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5)); /// assert_eq!((-5).microseconds(), Duration::microseconds(-5)); /// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5)); /// assert_eq!((-5).seconds(), Duration::seconds(-5)); /// assert_eq!((-5).minutes(), Duration::minutes(-5)); /// assert_eq!((-5).hours(), Duration::hours(-5)); /// assert_eq!((-5).days(), Duration::days(-5)); /// assert_eq!((-5).weeks(), Duration::weeks(-5)); /// ``` /// /// Just like any other [`Duration`], they can be added, subtracted, etc. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds()); /// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds()); /// ``` /// /// When called on floating point values, any remainder of the floating point value will be /// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited /// capacity. pub trait NumericalDuration: sealed::Sealed { /// Create a [`Duration`] from the number of nanoseconds. fn nanoseconds(self) -> Duration; /// Create a [`Duration`] from the number of microseconds. fn microseconds(self) -> Duration; /// Create a [`Duration`] from the number of milliseconds. fn milliseconds(self) -> Duration; /// Create a [`Duration`] from the number of seconds. fn seconds(self) -> Duration; /// Create a [`Duration`] from the number of minutes. fn minutes(self) -> Duration; /// Create a [`Duration`] from the number of hours. fn hours(self) -> Duration; /// Create a [`Duration`] from the number of days. fn days(self) -> Duration; /// Create a [`Duration`] from the number of weeks. fn weeks(self) -> Duration; } impl NumericalDuration for i64 { fn nanoseconds(self) -> Duration { Duration::nanoseconds(self) } fn microseconds(self) -> Duration { Duration::microseconds(self) } fn milliseconds(self) -> Duration { Duration::milliseconds(self) } fn seconds(self) -> Duration { Duration::seconds(self) } fn minutes(self) -> Duration { Duration::minutes(self) } fn hours(self) -> Duration { Duration::hours(self) } fn days(self) -> Duration { Duration::days(self) } fn weeks(self) -> Duration { Duration::weeks(self) } } impl NumericalDuration for f64 { fn nanoseconds(self) -> Duration { Duration::nanoseconds(self as _) } fn microseconds(self) -> Duration { Duration::nanoseconds((self * 1_000.) as _) } fn milliseconds(self) -> Duration { Duration::nanoseconds((self * 1_000_000.) as _) } fn seconds(self) -> Duration { Duration::nanoseconds((self * 1_000_000_000.) as _) } fn minutes(self) -> Duration { Duration::nanoseconds((self * 60_000_000_000.) as _) } fn hours(self) -> Duration { Duration::nanoseconds((self * 3_600_000_000_000.) as _) } fn days(self) -> Duration { Duration::nanoseconds((self * 86_400_000_000_000.) as _) } fn weeks(self) -> Duration { Duration::nanoseconds((self * 604_800_000_000_000.) as _) } } // endregion NumericalDuration // region: NumericalStdDuration /// Create [`std::time::Duration`]s from numeric literals. /// /// # Examples /// /// Basic construction of [`std::time::Duration`]s. /// /// ```rust /// # use time::ext::NumericalStdDuration; /// # use core::time::Duration; /// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5)); /// assert_eq!(5.std_microseconds(), Duration::from_micros(5)); /// assert_eq!(5.std_milliseconds(), Duration::from_millis(5)); /// assert_eq!(5.std_seconds(), Duration::from_secs(5)); /// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60)); /// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600)); /// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400)); /// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800)); /// ``` /// /// Just like any other [`std::time::Duration`], they can be added, subtracted, etc. /// /// ```rust /// # use time::ext::NumericalStdDuration; /// assert_eq!( /// 2.std_seconds() + 500.std_milliseconds(), /// 2_500.std_milliseconds() /// ); /// assert_eq!( /// 2.std_seconds() - 500.std_milliseconds(), /// 1_500.std_milliseconds() /// ); /// ``` /// /// When called on floating point values, any remainder of the floating point value will be /// truncated. Keep in mind that floating point numbers are inherently imprecise and have limited /// capacity. pub trait NumericalStdDuration: sealed::Sealed { /// Create a [`std::time::Duration`] from the number of nanoseconds. fn std_nanoseconds(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of microseconds. fn std_microseconds(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of milliseconds. fn std_milliseconds(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of seconds. fn std_seconds(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of minutes. fn std_minutes(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of hours. fn std_hours(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of days. fn std_days(self) -> StdDuration; /// Create a [`std::time::Duration`] from the number of weeks. fn std_weeks(self) -> StdDuration; } impl NumericalStdDuration for u64 { fn std_nanoseconds(self) -> StdDuration { StdDuration::from_nanos(self) } fn std_microseconds(self) -> StdDuration { StdDuration::from_micros(self) } fn std_milliseconds(self) -> StdDuration { StdDuration::from_millis(self) } fn std_seconds(self) -> StdDuration { StdDuration::from_secs(self) } fn std_minutes(self) -> StdDuration { StdDuration::from_secs(self * 60) } fn std_hours(self) -> StdDuration { StdDuration::from_secs(self * 3_600) } fn std_days(self) -> StdDuration { StdDuration::from_secs(self * 86_400) } fn std_weeks(self) -> StdDuration { StdDuration::from_secs(self * 604_800) } } impl NumericalStdDuration for f64 { fn std_nanoseconds(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos(self as _) } fn std_microseconds(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 1_000.) as _) } fn std_milliseconds(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 1_000_000.) as _) } fn std_seconds(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 1_000_000_000.) as _) } fn std_minutes(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 60_000_000_000.) as _) } fn std_hours(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 3_600_000_000_000.) as _) } fn std_days(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 86_400_000_000_000.) as _) } fn std_weeks(self) -> StdDuration { assert!(self >= 0.); StdDuration::from_nanos((self * 604_800_000_000_000.) as _) } } // endregion NumericalStdDuration time-0.3.5/src/format_description/component.rs000064400000000000000000000144510072674642500176560ustar 00000000000000//! Part of a format description. #[cfg(feature = "alloc")] use alloc::string::String; use crate::format_description::modifier; #[cfg(feature = "alloc")] use crate::{error::InvalidFormatDescription, format_description::modifier::Modifiers}; /// A component of a larger format description. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Component { /// Day of the month. Day(modifier::Day), /// Month of the year. Month(modifier::Month), /// Ordinal day of the year. Ordinal(modifier::Ordinal), /// Day of the week. Weekday(modifier::Weekday), /// Week within the year. WeekNumber(modifier::WeekNumber), /// Year of the date. Year(modifier::Year), /// Hour of the day. Hour(modifier::Hour), /// Minute within the hour. Minute(modifier::Minute), /// AM/PM part of the time. Period(modifier::Period), /// Second within the minute. Second(modifier::Second), /// Subsecond within the second. Subsecond(modifier::Subsecond), /// Hour of the UTC offset. OffsetHour(modifier::OffsetHour), /// Minute within the hour of the UTC offset. OffsetMinute(modifier::OffsetMinute), /// Second within the minute of the UTC offset. OffsetSecond(modifier::OffsetSecond), } /// A component with no modifiers present. #[cfg(feature = "alloc")] pub(crate) enum NakedComponent { /// Day of the month. Day, /// Month of the year. Month, /// Ordinal day of the year. Ordinal, /// Day of the week. Weekday, /// Week within the year. WeekNumber, /// Year of the date. Year, /// Hour of the day. Hour, /// Minute within the hour. Minute, /// AM/PM part of the time. Period, /// Second within the minute. Second, /// Subsecond within the second. Subsecond, /// Hour of the UTC offset. OffsetHour, /// Minute within the hour of the UTC offset. OffsetMinute, /// Second within the minute of the UTC offset. OffsetSecond, } #[cfg(feature = "alloc")] impl NakedComponent { /// Parse a component (without its modifiers) from the provided name. pub(crate) fn parse( component_name: &[u8], component_index: usize, ) -> Result { match component_name { b"day" => Ok(Self::Day), b"month" => Ok(Self::Month), b"ordinal" => Ok(Self::Ordinal), b"weekday" => Ok(Self::Weekday), b"week_number" => Ok(Self::WeekNumber), b"year" => Ok(Self::Year), b"hour" => Ok(Self::Hour), b"minute" => Ok(Self::Minute), b"period" => Ok(Self::Period), b"second" => Ok(Self::Second), b"subsecond" => Ok(Self::Subsecond), b"offset_hour" => Ok(Self::OffsetHour), b"offset_minute" => Ok(Self::OffsetMinute), b"offset_second" => Ok(Self::OffsetSecond), b"" => Err(InvalidFormatDescription::MissingComponentName { index: component_index, }), _ => Err(InvalidFormatDescription::InvalidComponentName { name: String::from_utf8_lossy(component_name).into_owned(), index: component_index, }), } } /// Attach the necessary modifiers to the component. pub(crate) fn attach_modifiers(self, modifiers: &Modifiers) -> Component { match self { Self::Day => Component::Day(modifier::Day { padding: modifiers.padding.unwrap_or_default(), }), Self::Month => Component::Month(modifier::Month { padding: modifiers.padding.unwrap_or_default(), repr: modifiers.month_repr.unwrap_or_default(), case_sensitive: modifiers.case_sensitive.unwrap_or(true), }), Self::Ordinal => Component::Ordinal(modifier::Ordinal { padding: modifiers.padding.unwrap_or_default(), }), Self::Weekday => Component::Weekday(modifier::Weekday { repr: modifiers.weekday_repr.unwrap_or_default(), one_indexed: modifiers.weekday_is_one_indexed.unwrap_or(true), case_sensitive: modifiers.case_sensitive.unwrap_or(true), }), Self::WeekNumber => Component::WeekNumber(modifier::WeekNumber { padding: modifiers.padding.unwrap_or_default(), repr: modifiers.week_number_repr.unwrap_or_default(), }), Self::Year => Component::Year(modifier::Year { padding: modifiers.padding.unwrap_or_default(), repr: modifiers.year_repr.unwrap_or_default(), iso_week_based: modifiers.year_is_iso_week_based.unwrap_or_default(), sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(), }), Self::Hour => Component::Hour(modifier::Hour { padding: modifiers.padding.unwrap_or_default(), is_12_hour_clock: modifiers.hour_is_12_hour_clock.unwrap_or_default(), }), Self::Minute => Component::Minute(modifier::Minute { padding: modifiers.padding.unwrap_or_default(), }), Self::Period => Component::Period(modifier::Period { is_uppercase: modifiers.period_is_uppercase.unwrap_or(true), case_sensitive: modifiers.case_sensitive.unwrap_or(true), }), Self::Second => Component::Second(modifier::Second { padding: modifiers.padding.unwrap_or_default(), }), Self::Subsecond => Component::Subsecond(modifier::Subsecond { digits: modifiers.subsecond_digits.unwrap_or_default(), }), Self::OffsetHour => Component::OffsetHour(modifier::OffsetHour { sign_is_mandatory: modifiers.sign_is_mandatory.unwrap_or_default(), padding: modifiers.padding.unwrap_or_default(), }), Self::OffsetMinute => Component::OffsetMinute(modifier::OffsetMinute { padding: modifiers.padding.unwrap_or_default(), }), Self::OffsetSecond => Component::OffsetSecond(modifier::OffsetSecond { padding: modifiers.padding.unwrap_or_default(), }), } } } time-0.3.5/src/format_description/mod.rs000064400000000000000000000124000072674642500164230ustar 00000000000000//! Description of how types should be formatted and parsed. //! //! The formatted value will be output to the provided writer. Format descriptions can be //! [well-known](crate::format_description::well_known) or obtained by using the //! [`format_description!`](crate::macros::format_description) macro, the //! [`format_description::parse`](crate::format_description::parse()) function. mod component; pub mod modifier; #[cfg(feature = "alloc")] pub(crate) mod parse; #[cfg(feature = "alloc")] use alloc::string::String; use core::convert::TryFrom; #[cfg(feature = "alloc")] use core::fmt; pub use self::component::Component; #[cfg(feature = "alloc")] pub use self::parse::parse; use crate::error; /// Helper methods. #[cfg(feature = "alloc")] mod helper { /// Consume all leading whitespace, advancing `index` as appropriate. #[must_use = "This does not modify the original slice."] pub(crate) fn consume_whitespace<'a>(bytes: &'a [u8], index: &mut usize) -> &'a [u8] { let first_non_whitespace = bytes .iter() .position(|c| !c.is_ascii_whitespace()) .unwrap_or(bytes.len()); *index += first_non_whitespace; &bytes[first_non_whitespace..] } } /// Well-known formats, typically RFCs. pub mod well_known { /// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6). /// /// Format example: 1985-04-12T23:20:50.52Z /// /// ```rust /// # use time::{format_description::well_known::Rfc3339, macros::datetime, OffsetDateTime}; /// assert_eq!( /// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?, /// datetime!(1985-04-12 23:20:50.52 +00:00) /// ); /// # Ok::<_, time::Error>(()) /// ``` /// /// ```rust /// # use time::{format_description::well_known::Rfc3339, macros::datetime}; /// assert_eq!( /// datetime!(1985-04-12 23:20:50.52 +00:00).format(&Rfc3339)?, /// "1985-04-12T23:20:50.52Z" /// ); /// # Ok::<_, time::Error>(()) /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Rfc3339; } /// A complete description of how to format and parse a type. #[non_exhaustive] #[cfg_attr(not(feature = "alloc"), derive(Debug))] #[derive(Clone, PartialEq, Eq)] pub enum FormatItem<'a> { /// Bytes that are formatted as-is. /// /// **Note**: If you call the `format` method that returns a `String`, these bytes will be /// passed through `String::from_utf8_lossy`. Literal(&'a [u8]), /// A minimal representation of a single non-literal item. Component(Component), /// A series of literals or components that collectively form a partial or complete /// description. Compound(&'a [Self]), /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there will be /// no effect on the resulting `struct`. /// /// This variant has no effect on formatting, as the value is guaranteed to be present. Optional(&'a Self), /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When /// formatting, the first element of the slice is used. An empty slice is a no-op when /// formatting or parsing. First(&'a [Self]), } #[cfg(feature = "alloc")] impl fmt::Debug for FormatItem<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FormatItem::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), FormatItem::Component(component) => component.fmt(f), FormatItem::Compound(compound) => compound.fmt(f), FormatItem::Optional(item) => f.debug_tuple("Optional").field(item).finish(), FormatItem::First(items) => f.debug_tuple("First").field(items).finish(), } } } impl From for FormatItem<'_> { fn from(component: Component) -> Self { Self::Component(component) } } impl TryFrom> for Component { type Error = error::DifferentVariant; fn try_from(value: FormatItem<'_>) -> Result { match value { FormatItem::Component(component) => Ok(component), _ => Err(error::DifferentVariant), } } } impl<'a> From<&'a [FormatItem<'_>]> for FormatItem<'a> { fn from(items: &'a [FormatItem<'_>]) -> FormatItem<'a> { FormatItem::Compound(items) } } impl<'a> TryFrom> for &[FormatItem<'a>] { type Error = error::DifferentVariant; fn try_from(value: FormatItem<'a>) -> Result { match value { FormatItem::Compound(items) => Ok(items), _ => Err(error::DifferentVariant), } } } impl PartialEq for FormatItem<'_> { fn eq(&self, rhs: &Component) -> bool { matches!(self, FormatItem::Component(component) if component == rhs) } } impl PartialEq> for Component { fn eq(&self, rhs: &FormatItem<'_>) -> bool { rhs == self } } impl PartialEq<&[FormatItem<'_>]> for FormatItem<'_> { fn eq(&self, rhs: &&[FormatItem<'_>]) -> bool { matches!(self, FormatItem::Compound(compound) if compound == rhs) } } impl PartialEq> for &[FormatItem<'_>] { fn eq(&self, rhs: &FormatItem<'_>) -> bool { rhs == self } } time-0.3.5/src/format_description/modifier.rs000064400000000000000000000476610072674642500174630ustar 00000000000000//! Various modifiers for components. #[cfg(feature = "alloc")] use alloc::string::String; #[cfg(feature = "alloc")] use core::mem; #[cfg(feature = "alloc")] use crate::{error::InvalidFormatDescription, format_description::helper}; // region: date modifiers /// Day of the month. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Day { /// The padding to obtain the minimum width. pub padding: Padding, } /// The representation of a month. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MonthRepr { /// The number of the month (January is 1, December is 12). Numerical, /// The long form of the month name (e.g. "January"). Long, /// The short form of the month name (e.g. "Jan"). Short, } /// Month of the year. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Month { /// The padding to obtain the minimum width. pub padding: Padding, /// What form of representation should be used? pub repr: MonthRepr, /// Is the value case sensitive when parsing? pub case_sensitive: bool, } /// Ordinal day of the year. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Ordinal { /// The padding to obtain the minimum width. pub padding: Padding, } /// The representation used for the day of the week. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WeekdayRepr { /// The short form of the weekday (e.g. "Mon"). Short, /// The long form of the weekday (e.g. "Monday"). Long, /// A numerical representation using Sunday as the first day of the week. /// /// Sunday is either 0 or 1, depending on the other modifier's value. Sunday, /// A numerical representation using Monday as the first day of the week. /// /// Monday is either 0 or 1, depending on the other modifier's value. Monday, } /// Day of the week. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Weekday { /// What form of representation should be used? pub repr: WeekdayRepr, /// When using a numerical representation, should it be zero or one-indexed? pub one_indexed: bool, /// Is the value case sensitive when parsing? pub case_sensitive: bool, } /// The representation used for the week number. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WeekNumberRepr { /// Week 1 is the week that contains January 4. Iso, /// Week 1 begins on the first Sunday of the calendar year. Sunday, /// Week 1 begins on the first Monday of the calendar year. Monday, } /// Week within the year. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct WeekNumber { /// The padding to obtain the minimum width. pub padding: Padding, /// What kind of representation should be used? pub repr: WeekNumberRepr, } /// The representation used for a year value. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum YearRepr { /// The full value of the year. Full, /// Only the last two digits of the year. LastTwo, } /// Year of the date. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Year { /// The padding to obtain the minimum width. pub padding: Padding, /// What kind of representation should be used? pub repr: YearRepr, /// Whether the value is based on the ISO week number or the Gregorian calendar. pub iso_week_based: bool, /// Whether the `+` sign is present when a positive year contains fewer than five digits. pub sign_is_mandatory: bool, } // endregion date modifiers // region: time modifiers /// Hour of the day. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Hour { /// The padding to obtain the minimum width. pub padding: Padding, /// Is the hour displayed using a 12 or 24-hour clock? pub is_12_hour_clock: bool, } /// Minute within the hour. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Minute { /// The padding to obtain the minimum width. pub padding: Padding, } /// AM/PM part of the time. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Period { /// Is the period uppercase or lowercase? pub is_uppercase: bool, /// Is the value case sensitive when parsing? /// /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior. pub case_sensitive: bool, } /// Second within the minute. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Second { /// The padding to obtain the minimum width. pub padding: Padding, } /// The number of digits present in a subsecond representation. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SubsecondDigits { /// Exactly one digit. One, /// Exactly two digits. Two, /// Exactly three digits. Three, /// Exactly four digits. Four, /// Exactly five digits. Five, /// Exactly six digits. Six, /// Exactly seven digits. Seven, /// Exactly eight digits. Eight, /// Exactly nine digits. Nine, /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits /// necessary will be used. OneOrMore, } /// Subsecond within the second. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Subsecond { /// How many digits are present in the component? pub digits: SubsecondDigits, } // endregion time modifiers // region: offset modifiers /// Hour of the UTC offset. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct OffsetHour { /// Whether the `+` sign is present on positive values. pub sign_is_mandatory: bool, /// The padding to obtain the minimum width. pub padding: Padding, } /// Minute within the hour of the UTC offset. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct OffsetMinute { /// The padding to obtain the minimum width. pub padding: Padding, } /// Second within the minute of the UTC offset. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct OffsetSecond { /// The padding to obtain the minimum width. pub padding: Padding, } // endregion offset modifiers /// Type of padding to ensure a minimum width. #[non_exhaustive] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Padding { /// A space character (` `) should be used as padding. Space, /// A zero character (`0`) should be used as padding. Zero, /// There is no padding. This can result in a width below the otherwise minimum number of /// characters. None, } /// Generate the provided code if and only if `pub` is present. macro_rules! if_pub { (pub $(#[$attr:meta])*; $($x:tt)*) => { $(#[$attr])* /// /// This function exists since [`Default::default()`] cannot be used in a `const` context. /// It may be removed once that becomes possible. As the [`Default`] trait is in the /// prelude, removing this function in the future will not cause any resolution failures for /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be /// affected. As such it will not be considered a breaking change. $($x)* }; ($($_:tt)*) => {}; } /// Implement `Default` for the given type. This also generates an inherent implementation of a /// `default` method that is `const fn`, permitting the default value to be used in const contexts. // Every modifier should use this macro rather than a derived `Default`. macro_rules! impl_const_default { ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$( impl $type { if_pub! { $($pub)? $(#[$doc])*; pub const fn default() -> Self { $default } } } $(#[$doc])* impl Default for $type { fn default() -> Self { $default } } )*}; } impl_const_default! { /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub Day => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the value uses the /// [`Numerical`](Self::Numerical) representation. MonthRepr => Self::Numerical; /// Creates an instance of this type that indicates the value uses the /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero), /// and is case-sensitive when parsing. @pub Month => Self { padding: Padding::Zero, repr: MonthRepr::Numerical, case_sensitive: true, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub Ordinal => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation. WeekdayRepr => Self::Long; /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long) /// representation and is case-sensitive when parsing. If the representation is changed to a /// numerical one, the instance defaults to one-based indexing. @pub Weekday => Self { repr: WeekdayRepr::Long, one_indexed: true, case_sensitive: true, }; /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation. WeekNumberRepr => Self::Iso; /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero) /// and uses the [`Iso`](WeekNumberRepr::Iso) representation. @pub WeekNumber => Self { padding: Padding::Zero, repr: WeekNumberRepr::Iso, }; /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation. YearRepr => Self::Full; /// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full) /// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its /// base, and only includes the year's sign if necessary. @pub Year => Self { padding: Padding::Zero, repr: YearRepr::Full, iso_week_based: false, sign_is_mandatory: false, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and /// has the 24-hour representation. @pub Hour => Self { padding: Padding::Zero, is_12_hour_clock: false, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub Minute => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the value uses the upper-case representation and is /// case-sensitive when parsing. @pub Period => Self { is_uppercase: true, case_sensitive: true, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub Second => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the stringified value contains [one or more /// digits](Self::OneOrMore). SubsecondDigits => Self::OneOrMore; /// Creates a modifier that indicates the stringified value contains [one or more /// digits](SubsecondDigits::OneOrMore). @pub Subsecond => Self { digits: SubsecondDigits::OneOrMore }; /// Creates a modifier that indicates the value uses the `+` sign for all positive values /// and is [padded with zeroes](Padding::Zero). @pub OffsetHour => Self { sign_is_mandatory: true, padding: Padding::Zero, }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub OffsetMinute => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). @pub OffsetSecond => Self { padding: Padding::Zero }; /// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero). Padding => Self::Zero; } /// The modifiers parsed for any given component. `None` indicates the modifier was not present. #[allow(clippy::missing_docs_in_private_items)] // fields #[derive(Debug, Default)] pub(crate) struct Modifiers { pub(crate) padding: Option, pub(crate) hour_is_12_hour_clock: Option, pub(crate) period_is_uppercase: Option, pub(crate) month_repr: Option, pub(crate) subsecond_digits: Option, pub(crate) weekday_repr: Option, pub(crate) weekday_is_one_indexed: Option, pub(crate) week_number_repr: Option, pub(crate) year_repr: Option, pub(crate) year_is_iso_week_based: Option, pub(crate) sign_is_mandatory: Option, pub(crate) case_sensitive: Option, } impl Modifiers { /// Parse the modifiers of a given component. #[cfg(feature = "alloc")] #[allow(clippy::too_many_lines)] pub(crate) fn parse( component_name: &[u8], mut bytes: &[u8], index: &mut usize, ) -> Result { let mut modifiers = Self::default(); while !bytes.is_empty() { // Trim any whitespace between modifiers. bytes = helper::consume_whitespace(bytes, index); let modifier; if let Some(whitespace_loc) = bytes.iter().position(u8::is_ascii_whitespace) { *index += whitespace_loc; modifier = &bytes[..whitespace_loc]; bytes = &bytes[whitespace_loc..]; } else { modifier = mem::take(&mut bytes); } if modifier.is_empty() { break; } #[allow(clippy::unnested_or_patterns)] match (component_name, modifier) { (b"day", b"padding:space") | (b"hour", b"padding:space") | (b"minute", b"padding:space") | (b"month", b"padding:space") | (b"offset_hour", b"padding:space") | (b"offset_minute", b"padding:space") | (b"offset_second", b"padding:space") | (b"ordinal", b"padding:space") | (b"second", b"padding:space") | (b"week_number", b"padding:space") | (b"year", b"padding:space") => modifiers.padding = Some(Padding::Space), (b"day", b"padding:zero") | (b"hour", b"padding:zero") | (b"minute", b"padding:zero") | (b"month", b"padding:zero") | (b"offset_hour", b"padding:zero") | (b"offset_minute", b"padding:zero") | (b"offset_second", b"padding:zero") | (b"ordinal", b"padding:zero") | (b"second", b"padding:zero") | (b"week_number", b"padding:zero") | (b"year", b"padding:zero") => modifiers.padding = Some(Padding::Zero), (b"day", b"padding:none") | (b"hour", b"padding:none") | (b"minute", b"padding:none") | (b"month", b"padding:none") | (b"offset_hour", b"padding:none") | (b"offset_minute", b"padding:none") | (b"offset_second", b"padding:none") | (b"ordinal", b"padding:none") | (b"second", b"padding:none") | (b"week_number", b"padding:none") | (b"year", b"padding:none") => modifiers.padding = Some(Padding::None), (b"hour", b"repr:24") => modifiers.hour_is_12_hour_clock = Some(false), (b"hour", b"repr:12") => modifiers.hour_is_12_hour_clock = Some(true), (b"month", b"case_sensitive:true") | (b"period", b"case_sensitive:true") | (b"weekday", b"case_sensitive:true") => modifiers.case_sensitive = Some(true), (b"month", b"case_sensitive:false") | (b"period", b"case_sensitive:false") | (b"weekday", b"case_sensitive:false") => modifiers.case_sensitive = Some(false), (b"month", b"repr:numerical") => modifiers.month_repr = Some(MonthRepr::Numerical), (b"month", b"repr:long") => modifiers.month_repr = Some(MonthRepr::Long), (b"month", b"repr:short") => modifiers.month_repr = Some(MonthRepr::Short), (b"offset_hour", b"sign:automatic") | (b"year", b"sign:automatic") => { modifiers.sign_is_mandatory = Some(false); } (b"offset_hour", b"sign:mandatory") | (b"year", b"sign:mandatory") => { modifiers.sign_is_mandatory = Some(true); } (b"period", b"case:upper") => modifiers.period_is_uppercase = Some(true), (b"period", b"case:lower") => modifiers.period_is_uppercase = Some(false), (b"subsecond", b"digits:1") => { modifiers.subsecond_digits = Some(SubsecondDigits::One); } (b"subsecond", b"digits:2") => { modifiers.subsecond_digits = Some(SubsecondDigits::Two); } (b"subsecond", b"digits:3") => { modifiers.subsecond_digits = Some(SubsecondDigits::Three); } (b"subsecond", b"digits:4") => { modifiers.subsecond_digits = Some(SubsecondDigits::Four); } (b"subsecond", b"digits:5") => { modifiers.subsecond_digits = Some(SubsecondDigits::Five); } (b"subsecond", b"digits:6") => { modifiers.subsecond_digits = Some(SubsecondDigits::Six); } (b"subsecond", b"digits:7") => { modifiers.subsecond_digits = Some(SubsecondDigits::Seven); } (b"subsecond", b"digits:8") => { modifiers.subsecond_digits = Some(SubsecondDigits::Eight); } (b"subsecond", b"digits:9") => { modifiers.subsecond_digits = Some(SubsecondDigits::Nine); } (b"subsecond", b"digits:1+") => { modifiers.subsecond_digits = Some(SubsecondDigits::OneOrMore); } (b"weekday", b"repr:short") => modifiers.weekday_repr = Some(WeekdayRepr::Short), (b"weekday", b"repr:long") => modifiers.weekday_repr = Some(WeekdayRepr::Long), (b"weekday", b"repr:sunday") => modifiers.weekday_repr = Some(WeekdayRepr::Sunday), (b"weekday", b"repr:monday") => modifiers.weekday_repr = Some(WeekdayRepr::Monday), (b"weekday", b"one_indexed:true") => modifiers.weekday_is_one_indexed = Some(true), (b"weekday", b"one_indexed:false") => { modifiers.weekday_is_one_indexed = Some(false); } (b"week_number", b"repr:iso") => { modifiers.week_number_repr = Some(WeekNumberRepr::Iso); } (b"week_number", b"repr:sunday") => { modifiers.week_number_repr = Some(WeekNumberRepr::Sunday); } (b"week_number", b"repr:monday") => { modifiers.week_number_repr = Some(WeekNumberRepr::Monday); } (b"year", b"repr:full") => modifiers.year_repr = Some(YearRepr::Full), (b"year", b"repr:last_two") => modifiers.year_repr = Some(YearRepr::LastTwo), (b"year", b"base:calendar") => modifiers.year_is_iso_week_based = Some(false), (b"year", b"base:iso_week") => modifiers.year_is_iso_week_based = Some(true), _ => { return Err(InvalidFormatDescription::InvalidModifier { value: String::from_utf8_lossy(modifier).into_owned(), index: *index, }); } } } Ok(modifiers) } } time-0.3.5/src/format_description/parse.rs000064400000000000000000000064150072674642500167670ustar 00000000000000//! Parse a format description into a standardized representation. use alloc::vec::Vec; use crate::error::InvalidFormatDescription; use crate::format_description::component::{Component, NakedComponent}; use crate::format_description::{helper, modifier, FormatItem}; /// The item parsed and remaining chunk of the format description after one iteration. #[derive(Debug)] pub(crate) struct ParsedItem<'a> { /// The item that was parsed. pub(crate) item: FormatItem<'a>, /// What is left of the input string after the item was parsed. pub(crate) remaining: &'a [u8], } /// Parse a component from the format description. Neither the leading nor trailing bracket should /// be present in the parameter. fn parse_component(mut s: &[u8], index: &mut usize) -> Result { // Trim any whitespace between the opening bracket and the component name. s = helper::consume_whitespace(s, index); // Everything before the first whitespace is the component name. let component_index = *index; let whitespace_loc = s .iter() .position(u8::is_ascii_whitespace) .unwrap_or(s.len()); *index += whitespace_loc; let component_name = &s[..whitespace_loc]; s = &s[whitespace_loc..]; s = helper::consume_whitespace(s, index); Ok(NakedComponent::parse(component_name, component_index)? .attach_modifiers(&modifier::Modifiers::parse(component_name, s, index)?)) } /// Parse a literal string from the format description. fn parse_literal<'a>(s: &'a [u8], index: &mut usize) -> ParsedItem<'a> { let loc = s.iter().position(|&c| c == b'[').unwrap_or(s.len()); *index += loc; ParsedItem { item: FormatItem::Literal(&s[..loc]), remaining: &s[loc..], } } /// Parse either a literal or a component from the format description. fn parse_item<'a>( s: &'a [u8], index: &mut usize, ) -> Result, InvalidFormatDescription> { if let [b'[', b'[', remaining @ ..] = s { *index += 2; return Ok(ParsedItem { item: FormatItem::Literal(&[b'[']), remaining, }); }; if s.starts_with(&[b'[']) { if let Some(bracket_index) = s.iter().position(|&c| c == b']') { *index += 1; // opening bracket let ret_val = ParsedItem { item: FormatItem::Component(parse_component(&s[1..bracket_index], index)?), remaining: &s[bracket_index + 1..], }; *index += 1; // closing bracket Ok(ret_val) } else { Err(InvalidFormatDescription::UnclosedOpeningBracket { index: *index }) } } else { Ok(parse_literal(s, index)) } } /// Parse a sequence of items from the format description. /// /// The syntax for the format description can be found in [the /// book](https://time-rs.github.io/book/api/format-description.html). #[cfg_attr(__time_03_docs, doc(cfg(feature = "alloc")))] pub fn parse(s: &str) -> Result>, InvalidFormatDescription> { let mut compound = Vec::new(); let mut loc = 0; let mut s = s.as_bytes(); while !s.is_empty() { let ParsedItem { item, remaining } = parse_item(s, &mut loc)?; s = remaining; compound.push(item); } Ok(compound) } time-0.3.5/src/formatting/formattable.rs000064400000000000000000000150040072674642500164260ustar 00000000000000//! A trait that can be used to format an item from its components. use core::ops::Deref; use std::io; use crate::format_description::well_known::Rfc3339; use crate::format_description::FormatItem; use crate::formatting::{format_component, format_number_pad_zero, write}; use crate::{error, Date, Time, UtcOffset}; /// A type that can be formatted. #[cfg_attr(__time_03_docs, doc(notable_trait))] pub trait Formattable: sealed::Sealed {} impl Formattable for FormatItem<'_> {} impl Formattable for [FormatItem<'_>] {} impl Formattable for Rfc3339 {} impl Formattable for T where T::Target: Formattable {} /// Seal the trait to prevent downstream users from implementing it. mod sealed { #[allow(clippy::wildcard_imports)] use super::*; /// Format the item using a format description, the intended output, and the various components. #[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))] pub trait Sealed { /// Format the item into the provided output, returning the number of bytes written. fn format_into( &self, output: &mut impl io::Write, date: Option, time: Option