RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.

This commit is contained in:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

File diff suppressed because one or more lines are too long

104
zeroidc/vendor/time/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,104 @@
# 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.9"
authors = ["Jacob Pratt <open-source@jhpratt.dev>", "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 = "1.0.1"
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.4"
optional = true
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
[dev-dependencies.rand]
version = "0.8.4"
default-features = false
[dev-dependencies.serde]
version = "1.0.126"
features = ["derive"]
default-features = false
[dev-dependencies.serde_json]
version = "1.0.68"
[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"]
serde-well-known = ["serde/alloc", "formatting", "parsing"]
std = ["alloc"]
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
version = "=1.0.34"
[target."cfg(bench)".dev-dependencies.criterion]
version = "0.3.5"
[target."cfg(bench)".dev-dependencies.criterion-cycles-per-byte]
version = "0.1.2"
[target."cfg(target_family = \"unix\")".dependencies.libc]
version = "0.2.98"
[target."cfg(target_family = \"unix\")".dependencies.num_threads]
version = "0.1.2"

202
zeroidc/vendor/time/LICENSE-Apache vendored Normal file
View File

@@ -0,0 +1,202 @@
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 2022 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.

19
zeroidc/vendor/time/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2022 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.

48
zeroidc/vendor/time/README.md vendored Normal file
View File

@@ -0,0 +1,48 @@
# time
[![minimum rustc: 1.53](https://img.shields.io/badge/minimum%20rustc-1.53-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.

1032
zeroidc/vendor/time/src/date.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1049
zeroidc/vendor/time/src/duration.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
//! 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
}
/// Whether the value's permitted range is conditional, i.e. whether an input with this
/// value could have succeeded if the values of other components were different.
pub const fn is_conditional(self) -> bool {
self.conditional_range
}
}
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<ComponentRange> for crate::Error {
fn from(original: ComponentRange) -> Self {
Self::ComponentRange(original)
}
}
impl TryFrom<crate::Error> for ComponentRange {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ComponentRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
/// **This trait implementation is deprecated and will be removed in a future breaking release.**
#[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 {
/// Convert the error to a deserialization error.
pub(crate) fn into_de_error<E: serde::de::Error>(self) -> E {
E::invalid_value(serde::de::Unexpected::Signed(self.value), &self)
}
}
#[cfg(feature = "std")]
impl std::error::Error for ComponentRange {}

View File

@@ -0,0 +1,37 @@
//! 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<ConversionRange> for crate::Error {
fn from(err: ConversionRange) -> Self {
Self::ConversionRange(err)
}
}
impl TryFrom<crate::Error> for ConversionRange {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ConversionRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,35 @@
//! 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<DifferentVariant> for crate::Error {
fn from(err: DifferentVariant) -> Self {
Self::DifferentVariant(err)
}
}
impl TryFrom<crate::Error> for DifferentVariant {
type Error = Self;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::DifferentVariant(err) => Ok(err),
_ => Err(Self),
}
}
}

97
zeroidc/vendor/time/src/error/format.rs vendored Normal file
View File

@@ -0,0 +1,97 @@
//! 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<io::Error> for Format {
fn from(err: io::Error) -> Self {
Self::StdIo(err)
}
}
impl TryFrom<Format> for io::Error {
type Error = error::DifferentVariant;
fn try_from(err: Format) -> Result<Self, Self::Error> {
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<Format> for crate::Error {
fn from(original: Format) -> Self {
Self::Format(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "formatting")))]
impl TryFrom<crate::Error> for Format {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::Format(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "serde")]
impl Format {
/// Obtain an error type for the serializer.
#[doc(hidden)] // Exposed only for the `declare_format_string` macro
pub fn into_invalid_serde_value<S: serde::Serializer>(self) -> S::Error {
use serde::ser::Error;
S::Error::custom(format!("{}", self))
}
}

View File

@@ -0,0 +1,39 @@
//! 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<IndeterminateOffset> for crate::Error {
fn from(err: IndeterminateOffset) -> Self {
Self::IndeterminateOffset(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
impl TryFrom<crate::Error> for IndeterminateOffset {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::IndeterminateOffset(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,95 @@
//! 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<InvalidFormatDescription> 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<crate::Error> for InvalidFormatDescription {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
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 {}

View File

@@ -0,0 +1,35 @@
//! Invalid variant error
use core::convert::TryFrom;
use core::fmt;
/// An error type indicating that a [`FromStr`](core::str::FromStr) call failed because the value
/// was not a valid variant.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidVariant;
impl fmt::Display for InvalidVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "value was not a valid variant")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidVariant {}
impl From<InvalidVariant> for crate::Error {
fn from(err: InvalidVariant) -> Self {
Self::InvalidVariant(err)
}
}
impl TryFrom<crate::Error> for InvalidVariant {
type Error = crate::error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::InvalidVariant(err) => Ok(err),
_ => Err(crate::error::DifferentVariant),
}
}
}

112
zeroidc/vendor/time/src/error/mod.rs vendored Normal file
View File

@@ -0,0 +1,112 @@
//! 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;
mod invalid_variant;
#[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;
pub use invalid_variant::InvalidVariant;
#[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),
InvalidVariant(InvalidVariant),
}
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),
Self::InvalidVariant(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),
Self::InvalidVariant(err) => Some(err),
}
}
}

105
zeroidc/vendor/time/src/error/parse.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
//! 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<TryFromParsed> for Parse {
fn from(err: TryFromParsed) -> Self {
Self::TryFromParsed(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<Parse> for TryFromParsed {
type Error = error::DifferentVariant;
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<ParseFromDescription> for Parse {
fn from(err: ParseFromDescription) -> Self {
Self::ParseFromDescription(err)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<Parse> for ParseFromDescription {
type Error = error::DifferentVariant;
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<Parse> 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<crate::Error> for Parse {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
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),
}
}
}

View File

@@ -0,0 +1,51 @@
//! 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<ParseFromDescription> for crate::Error {
fn from(original: ParseFromDescription) -> Self {
Self::ParseFromDescription(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<crate::Error> for ParseFromDescription {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,75 @@
//! 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<error::ComponentRange> for TryFromParsed {
fn from(v: error::ComponentRange) -> Self {
Self::ComponentRange(v)
}
}
impl TryFrom<TryFromParsed> for error::ComponentRange {
type Error = error::DifferentVariant;
fn try_from(err: TryFromParsed) -> Result<Self, Self::Error> {
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<TryFromParsed> for crate::Error {
fn from(original: TryFromParsed) -> Self {
Self::TryFromParsed(original)
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<crate::Error> for TryFromParsed {
type Error = error::DifferentVariant;
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

279
zeroidc/vendor/time/src/ext.rs vendored Normal file
View File

@@ -0,0 +1,279 @@
//! 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

View File

@@ -0,0 +1,167 @@
//! 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<Self, InvalidFormatDescription> {
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(),
}),
}
}
}

View File

@@ -0,0 +1,186 @@
//! 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;
/// The format described in [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3).
///
/// Example: Fri, 21 Nov 1997 09:55:06 -0600
///
/// # Examples
/// ```rust
/// # use time::{format_description::well_known::Rfc2822, macros::datetime, OffsetDateTime};
/// assert_eq!(
/// OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?,
/// datetime!(1993-06-12 13:25:19 +00:00)
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
/// ```rust
/// # use time::{format_description::well_known::Rfc2822, macros::datetime};
/// assert_eq!(
/// datetime!(1997-11-21 09:55:06 -06:00).format(&Rfc2822)?,
/// "Fri, 21 Nov 1997 09:55:06 -0600"
/// );
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rfc2822;
}
/// 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<Component> for FormatItem<'_> {
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<FormatItem<'_>> for Component {
type Error = error::DifferentVariant;
fn try_from(value: FormatItem<'_>) -> Result<Self, Self::Error> {
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<FormatItem<'a>> for &[FormatItem<'a>] {
type Error = error::DifferentVariant;
fn try_from(value: FormatItem<'a>) -> Result<Self, Self::Error> {
match value {
FormatItem::Compound(items) => Ok(items),
_ => Err(error::DifferentVariant),
}
}
}
impl PartialEq<Component> for FormatItem<'_> {
fn eq(&self, rhs: &Component) -> bool {
matches!(self, FormatItem::Component(component) if component == rhs)
}
}
impl PartialEq<FormatItem<'_>> 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<FormatItem<'_>> for &[FormatItem<'_>] {
fn eq(&self, rhs: &FormatItem<'_>) -> bool {
rhs == self
}
}

View File

@@ -0,0 +1,509 @@
//! 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.
#[cfg(feature = "alloc")]
#[allow(clippy::missing_docs_in_private_items)] // fields
#[derive(Debug, Default)]
pub(crate) struct Modifiers {
pub(crate) padding: Option<Padding>,
pub(crate) hour_is_12_hour_clock: Option<bool>,
pub(crate) period_is_uppercase: Option<bool>,
pub(crate) month_repr: Option<MonthRepr>,
pub(crate) subsecond_digits: Option<SubsecondDigits>,
pub(crate) weekday_repr: Option<WeekdayRepr>,
pub(crate) weekday_is_one_indexed: Option<bool>,
pub(crate) week_number_repr: Option<WeekNumberRepr>,
pub(crate) year_repr: Option<YearRepr>,
pub(crate) year_is_iso_week_based: Option<bool>,
pub(crate) sign_is_mandatory: Option<bool>,
pub(crate) case_sensitive: Option<bool>,
}
#[cfg(feature = "alloc")]
impl Modifiers {
/// Parse the modifiers of a given component.
#[allow(clippy::too_many_lines)]
pub(crate) fn parse(
component_name: &[u8],
mut bytes: &[u8],
index: &mut usize,
) -> Result<Self, InvalidFormatDescription> {
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;
}
match (component_name, modifier) {
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:space",
) => modifiers.padding = Some(Padding::Space),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | b"year",
b"padding:zero",
) => modifiers.padding = Some(Padding::Zero),
(
b"day" | b"hour" | b"minute" | b"month" | b"offset_hour" | b"offset_minute"
| b"offset_second" | b"ordinal" | b"second" | b"week_number" | 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"period" | b"weekday", b"case_sensitive:true") => {
modifiers.case_sensitive = Some(true)
}
(b"month" | b"period" | 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"year", b"sign:automatic") => {
modifiers.sign_is_mandatory = Some(false);
}
(b"offset_hour" | 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)
}
}

View File

@@ -0,0 +1,97 @@
//! 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<Component, InvalidFormatDescription> {
// 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<ParsedItem<'a>, 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<Vec<FormatItem<'_>>, 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)
}

View File

@@ -0,0 +1,238 @@
//! 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::{Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
use crate::formatting::{
format_component, format_number_pad_zero, write, MONTH_NAMES, WEEKDAY_NAMES,
};
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 Rfc2822 {}
impl<T: Deref> 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<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format>;
/// Format the item directly to a `String`.
fn format(
&self,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<String, error::Format> {
let mut buf = Vec::new();
self.format_into(&mut buf, date, time, offset)?;
Ok(String::from_utf8_lossy(&buf).into_owned())
}
}
}
// region: custom formats
impl<'a> sealed::Sealed for FormatItem<'a> {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
Ok(match *self {
Self::Literal(literal) => write(output, literal)?,
Self::Component(component) => format_component(output, component, date, time, offset)?,
Self::Compound(items) => items.format_into(output, date, time, offset)?,
Self::Optional(item) => item.format_into(output, date, time, offset)?,
Self::First(items) => match items {
[] => 0,
[item, ..] => item.format_into(output, date, time, offset)?,
},
})
}
}
impl<'a> sealed::Sealed for [FormatItem<'a>] {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let mut bytes = 0;
for item in self.iter() {
bytes += item.format_into(output, date, time, offset)?;
}
Ok(bytes)
}
}
impl<T: Deref> sealed::Sealed for T
where
T::Target: sealed::Sealed,
{
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
self.deref().format_into(output, date, time, offset)
}
}
// endregion custom formats
// region: well-known formats
impl sealed::Sealed for Rfc2822 {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let date = date.ok_or(error::Format::InsufficientTypeInformation)?;
let time = time.ok_or(error::Format::InsufficientTypeInformation)?;
let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?;
let mut bytes = 0;
let (year, month, day) = date.to_calendar_date();
if year < 1900 {
return Err(error::Format::InvalidComponent("year"));
}
if offset.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += write(
output,
&WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
)?;
bytes += write(output, b", ")?;
bytes += format_number_pad_zero::<_, _, 2>(output, day)?;
bytes += write(output, b" ")?;
bytes += write(output, &MONTH_NAMES[month as usize - 1][..3])?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<_, _, 4>(output, year as u32)?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.hour())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.minute())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.second())?;
bytes += write(output, b" ")?;
bytes += write(output, if offset.is_negative() { b"-" } else { b"+" })?;
bytes += format_number_pad_zero::<_, _, 2>(output, offset.whole_hours().unsigned_abs())?;
bytes +=
format_number_pad_zero::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs())?;
Ok(bytes)
}
}
impl sealed::Sealed for Rfc3339 {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
let date = date.ok_or(error::Format::InsufficientTypeInformation)?;
let time = time.ok_or(error::Format::InsufficientTypeInformation)?;
let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?;
let mut bytes = 0;
let year = date.year();
if !(0..10_000).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
}
if offset.seconds_past_minute() != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += format_number_pad_zero::<_, _, 4>(output, year as u32)?;
bytes += write(output, &[b'-'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, date.month() as u8)?;
bytes += write(output, &[b'-'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, date.day())?;
bytes += write(output, &[b'T'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.hour())?;
bytes += write(output, &[b':'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.minute())?;
bytes += write(output, &[b':'])?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.second())?;
#[allow(clippy::if_not_else)]
if time.nanosecond() != 0 {
let nanos = time.nanosecond();
bytes += write(output, &[b'.'])?;
bytes += if nanos % 10 != 0 {
format_number_pad_zero::<_, _, 9>(output, nanos)
} else if (nanos / 10) % 10 != 0 {
format_number_pad_zero::<_, _, 8>(output, nanos / 10)
} else if (nanos / 100) % 10 != 0 {
format_number_pad_zero::<_, _, 7>(output, nanos / 100)
} else if (nanos / 1_000) % 10 != 0 {
format_number_pad_zero::<_, _, 6>(output, nanos / 1_000)
} else if (nanos / 10_000) % 10 != 0 {
format_number_pad_zero::<_, _, 5>(output, nanos / 10_000)
} else if (nanos / 100_000) % 10 != 0 {
format_number_pad_zero::<_, _, 4>(output, nanos / 100_000)
} else if (nanos / 1_000_000) % 10 != 0 {
format_number_pad_zero::<_, _, 3>(output, nanos / 1_000_000)
} else if (nanos / 10_000_000) % 10 != 0 {
format_number_pad_zero::<_, _, 2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
}?;
}
if offset == UtcOffset::UTC {
bytes += write(output, &[b'Z'])?;
return Ok(bytes);
}
bytes += write(
output,
if offset.is_negative() {
&[b'-']
} else {
&[b'+']
},
)?;
bytes += format_number_pad_zero::<_, _, 2>(output, offset.whole_hours().unsigned_abs())?;
bytes += write(output, &[b':'])?;
bytes +=
format_number_pad_zero::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs())?;
Ok(bytes)
}
}
// endregion well-known formats

View File

@@ -0,0 +1,457 @@
//! Formatting for various types.
pub(crate) mod formattable;
use std::io;
pub use self::formattable::Formattable;
use crate::format_description::{modifier, Component};
use crate::{error, Date, Time, UtcOffset};
#[allow(clippy::missing_docs_in_private_items)]
const MONTH_NAMES: [&[u8]; 12] = [
b"January",
b"February",
b"March",
b"April",
b"May",
b"June",
b"July",
b"August",
b"September",
b"October",
b"November",
b"December",
];
#[allow(clippy::missing_docs_in_private_items)]
const WEEKDAY_NAMES: [&[u8]; 7] = [
b"Monday",
b"Tuesday",
b"Wednesday",
b"Thursday",
b"Friday",
b"Saturday",
b"Sunday",
];
// region: extension trait
/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
pub(crate) trait DigitCount {
/// The number of digits in the stringified value.
fn num_digits(self) -> u8;
}
impl DigitCount for u8 {
fn num_digits(self) -> u8 {
// Using a lookup table as with u32 is *not* faster in standalone benchmarks.
if self < 10 {
1
} else if self < 100 {
2
} else {
3
}
}
}
impl DigitCount for u16 {
fn num_digits(self) -> u8 {
// Using a lookup table as with u32 is *not* faster in standalone benchmarks.
if self < 10 {
1
} else if self < 100 {
2
} else if self < 1_000 {
3
} else if self < 10_000 {
4
} else {
5
}
}
}
impl DigitCount for u32 {
fn num_digits(self) -> u8 {
/// Lookup table
const TABLE: &[u64] = &[
0x0001_0000_0000,
0x0001_0000_0000,
0x0001_0000_0000,
0x0001_FFFF_FFF6,
0x0002_0000_0000,
0x0002_0000_0000,
0x0002_FFFF_FF9C,
0x0003_0000_0000,
0x0003_0000_0000,
0x0003_FFFF_FC18,
0x0004_0000_0000,
0x0004_0000_0000,
0x0004_0000_0000,
0x0004_FFFF_D8F0,
0x0005_0000_0000,
0x0005_0000_0000,
0x0005_FFFE_7960,
0x0006_0000_0000,
0x0006_0000_0000,
0x0006_FFF0_BDC0,
0x0007_0000_0000,
0x0007_0000_0000,
0x0007_0000_0000,
0x0007_FF67_6980,
0x0008_0000_0000,
0x0008_0000_0000,
0x0008_FA0A_1F00,
0x0009_0000_0000,
0x0009_0000_0000,
0x0009_C465_3600,
0x000A_0000_0000,
0x000A_0000_0000,
];
((self as u64 + TABLE[31_u32.saturating_sub(self.leading_zeros()) as usize]) >> 32) as _
}
}
// endregion extension trait
/// Write all bytes to the output, returning the number of bytes written.
fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> {
output.write_all(bytes)?;
Ok(bytes.len())
}
/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
pub(crate) fn format_number<W: io::Write, V: itoa::Integer + DigitCount + Copy, const WIDTH: u8>(
output: &mut W,
value: V,
padding: modifier::Padding,
) -> Result<usize, io::Error> {
match padding {
modifier::Padding::Space => format_number_pad_space::<_, _, WIDTH>(output, value),
modifier::Padding::Zero => format_number_pad_zero::<_, _, WIDTH>(output, value),
modifier::Padding::None => write(output, itoa::Buffer::new().format(value).as_bytes()),
}
}
/// Format a number with the provided width and spaces as padding.
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_space<
W: io::Write,
V: itoa::Integer + DigitCount + Copy,
const WIDTH: u8,
>(
output: &mut W,
value: V,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, &[b' '])?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format a number with the provided width and zeros as padding.
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_zero<
W: io::Write,
V: itoa::Integer + DigitCount + Copy,
const WIDTH: u8,
>(
output: &mut W,
value: V,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, &[b'0'])?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
pub(crate) fn format_component(
output: &mut impl io::Write,
component: Component,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
use Component::*;
Ok(match (component, date, time, offset) {
(Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?,
(Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?,
(Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?,
(Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?,
(WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?,
(Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?,
(Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?,
(Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?,
(Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?,
(Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?,
(Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?,
(OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?,
(OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?,
(OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?,
_ => return Err(error::Format::InsufficientTypeInformation),
})
}
// region: date formatters
/// Format the day into the designated output.
fn fmt_day(
output: &mut impl io::Write,
date: Date,
modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, date.day(), padding)
}
/// Format the month into the designated output.
fn fmt_month(
output: &mut impl io::Write,
date: Date,
modifier::Month {
padding,
repr,
case_sensitive: _, // no effect on formatting
}: modifier::Month,
) -> Result<usize, io::Error> {
match repr {
modifier::MonthRepr::Numerical => {
format_number::<_, _, 2>(output, date.month() as u8, padding)
}
modifier::MonthRepr::Long => write(output, MONTH_NAMES[date.month() as usize - 1]),
modifier::MonthRepr::Short => write(output, &MONTH_NAMES[date.month() as usize - 1][..3]),
}
}
/// Format the ordinal into the designated output.
fn fmt_ordinal(
output: &mut impl io::Write,
date: Date,
modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
format_number::<_, _, 3>(output, date.ordinal(), padding)
}
/// Format the weekday into the designated output.
fn fmt_weekday(
output: &mut impl io::Write,
date: Date,
modifier::Weekday {
repr,
one_indexed,
case_sensitive: _, // no effect on formatting
}: modifier::Weekday,
) -> Result<usize, io::Error> {
match repr {
modifier::WeekdayRepr::Short => write(
output,
&WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
),
modifier::WeekdayRepr::Long => write(
output,
WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize],
),
modifier::WeekdayRepr::Sunday => format_number::<_, _, 1>(
output,
date.weekday().number_days_from_sunday() + one_indexed as u8,
modifier::Padding::None,
),
modifier::WeekdayRepr::Monday => format_number::<_, _, 1>(
output,
date.weekday().number_days_from_monday() + one_indexed as u8,
modifier::Padding::None,
),
}
}
/// Format the week number into the designated output.
fn fmt_week_number(
output: &mut impl io::Write,
date: Date,
modifier::WeekNumber { padding, repr }: modifier::WeekNumber,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(
output,
match repr {
modifier::WeekNumberRepr::Iso => date.iso_week(),
modifier::WeekNumberRepr::Sunday => date.sunday_based_week(),
modifier::WeekNumberRepr::Monday => date.monday_based_week(),
},
padding,
)
}
/// Format the year into the designated output.
fn fmt_year(
output: &mut impl io::Write,
date: Date,
modifier::Year {
padding,
repr,
iso_week_based,
sign_is_mandatory,
}: modifier::Year,
) -> Result<usize, io::Error> {
let full_year = if iso_week_based {
date.iso_year_week().0
} else {
date.year()
};
let value = match repr {
modifier::YearRepr::Full => full_year,
modifier::YearRepr::LastTwo => (full_year % 100).abs(),
};
let format_number = match repr {
#[cfg(feature = "large-dates")]
modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<_, _, 6>,
#[cfg(feature = "large-dates")]
modifier::YearRepr::Full if value.abs() >= 10_000 => format_number::<_, _, 5>,
modifier::YearRepr::Full => format_number::<_, _, 4>,
modifier::YearRepr::LastTwo => format_number::<_, _, 2>,
};
let mut bytes = 0;
if repr != modifier::YearRepr::LastTwo {
if full_year < 0 {
bytes += write(output, &[b'-'])?;
} else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
bytes += write(output, &[b'+'])?;
}
}
bytes += format_number(output, value.unsigned_abs(), padding)?;
Ok(bytes)
}
// endregion date formatters
// region: time formatters
/// Format the hour into the designated output.
fn fmt_hour(
output: &mut impl io::Write,
time: Time,
modifier::Hour {
padding,
is_12_hour_clock,
}: modifier::Hour,
) -> Result<usize, io::Error> {
let value = match (time.hour(), is_12_hour_clock) {
(hour, false) => hour,
(0 | 12, true) => 12,
(hour, true) if hour < 12 => hour,
(hour, true) => hour - 12,
};
format_number::<_, _, 2>(output, value, padding)
}
/// Format the minute into the designated output.
fn fmt_minute(
output: &mut impl io::Write,
time: Time,
modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.minute(), padding)
}
/// Format the period into the designated output.
fn fmt_period(
output: &mut impl io::Write,
time: Time,
modifier::Period {
is_uppercase,
case_sensitive: _, // no effect on formatting
}: modifier::Period,
) -> Result<usize, io::Error> {
match (time.hour() >= 12, is_uppercase) {
(false, false) => write(output, b"am"),
(false, true) => write(output, b"AM"),
(true, false) => write(output, b"pm"),
(true, true) => write(output, b"PM"),
}
}
/// Format the second into the designated output.
fn fmt_second(
output: &mut impl io::Write,
time: Time,
modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.second(), padding)
}
/// Format the subsecond into the designated output.
fn fmt_subsecond<W: io::Write>(
output: &mut W,
time: Time,
modifier::Subsecond { digits }: modifier::Subsecond,
) -> Result<usize, io::Error> {
use modifier::SubsecondDigits::*;
let nanos = time.nanosecond();
if digits == Nine || (digits == OneOrMore && nanos % 10 != 0) {
format_number_pad_zero::<_, _, 9>(output, nanos)
} else if digits == Eight || (digits == OneOrMore && (nanos / 10) % 10 != 0) {
format_number_pad_zero::<_, _, 8>(output, nanos / 10)
} else if digits == Seven || (digits == OneOrMore && (nanos / 100) % 10 != 0) {
format_number_pad_zero::<_, _, 7>(output, nanos / 100)
} else if digits == Six || (digits == OneOrMore && (nanos / 1_000) % 10 != 0) {
format_number_pad_zero::<_, _, 6>(output, nanos / 1_000)
} else if digits == Five || (digits == OneOrMore && (nanos / 10_000) % 10 != 0) {
format_number_pad_zero::<_, _, 5>(output, nanos / 10_000)
} else if digits == Four || (digits == OneOrMore && (nanos / 100_000) % 10 != 0) {
format_number_pad_zero::<_, _, 4>(output, nanos / 100_000)
} else if digits == Three || (digits == OneOrMore && (nanos / 1_000_000) % 10 != 0) {
format_number_pad_zero::<_, _, 3>(output, nanos / 1_000_000)
} else if digits == Two || (digits == OneOrMore && (nanos / 10_000_000) % 10 != 0) {
format_number_pad_zero::<_, _, 2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
}
}
// endregion time formatters
// region: offset formatters
/// Format the offset hour into the designated output.
fn fmt_offset_hour(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetHour {
padding,
sign_is_mandatory,
}: modifier::OffsetHour,
) -> Result<usize, io::Error> {
let mut bytes = 0;
if offset.is_negative() {
bytes += write(output, &[b'-'])?;
} else if sign_is_mandatory {
bytes += write(output, &[b'+'])?;
}
bytes += format_number::<_, _, 2>(output, offset.whole_hours().unsigned_abs(), padding)?;
Ok(bytes)
}
/// Format the offset minute into the designated output.
fn fmt_offset_minute(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs(), padding)
}
/// Format the offset second into the designated output.
fn fmt_offset_second(
output: &mut impl io::Write,
offset: UtcOffset,
modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.seconds_past_minute().unsigned_abs(), padding)
}
// endregion offset formatters

262
zeroidc/vendor/time/src/instant.rs vendored Normal file
View File

@@ -0,0 +1,262 @@
//! The [`Instant`] struct and its associated `impl`s.
use core::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use core::convert::{TryFrom, TryInto};
use core::ops::{Add, Sub};
use core::time::Duration as StdDuration;
use std::borrow::Borrow;
use std::time::Instant as StdInstant;
use crate::Duration;
/// A measurement of a monotonically non-decreasing clock. Opaque and useful only with [`Duration`].
///
/// Instants are always guaranteed to be no less than any previously measured instant when created,
/// and are often useful for tasks such as measuring benchmarks or timing how long an operation
/// takes.
///
/// Note, however, that instants are not guaranteed to be **steady**. In other words, each tick of
/// the underlying clock may not be the same length (e.g. some seconds may be longer than others).
/// An instant may jump forwards or experience time dilation (slow down or speed up), but it will
/// never go backwards.
///
/// Instants are opaque types that can only be compared to one another. There is no method to get
/// "the number of seconds" from an instant. Instead, it only allows measuring the duration between
/// two instants (or comparing two instants).
///
/// This implementation allows for operations with signed [`Duration`]s, but is otherwise identical
/// to [`std::time::Instant`].
#[cfg_attr(__time_03_docs, doc(cfg(feature = "std")))]
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant(pub StdInstant);
impl Instant {
// region: delegation
/// Returns an `Instant` corresponding to "now".
///
/// ```rust
/// # use time::Instant;
/// println!("{:?}", Instant::now());
/// ```
pub fn now() -> Self {
Self(StdInstant::now())
}
/// Returns the amount of time elapsed since this instant was created. The duration will always
/// be nonnegative if the instant is not synthetically created.
///
/// ```rust
/// # use time::{Instant, ext::{NumericalStdDuration, NumericalDuration}};
/// # use std::thread;
/// let instant = Instant::now();
/// thread::sleep(1.std_milliseconds());
/// assert!(instant.elapsed() >= 1.milliseconds());
/// ```
pub fn elapsed(self) -> Duration {
Self::now() - self
}
// endregion delegation
// region: checked arithmetic
/// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// ```rust
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_add(5.seconds()), Some(now + 5.seconds()));
/// assert_eq!(now.checked_add((-5).seconds()), Some(now + (-5).seconds()));
/// ```
pub fn checked_add(self, duration: Duration) -> Option<Self> {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_add(duration.abs_std()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_sub(duration.abs_std()).map(Self)
}
}
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
/// otherwise.
///
/// ```rust
/// # use time::{Instant, ext::NumericalDuration};
/// let now = Instant::now();
/// assert_eq!(now.checked_sub(5.seconds()), Some(now - 5.seconds()));
/// assert_eq!(now.checked_sub((-5).seconds()), Some(now - (-5).seconds()));
/// ```
pub fn checked_sub(self, duration: Duration) -> Option<Self> {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_sub(duration.abs_std()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_add(duration.abs_std()).map(Self)
}
}
// endregion checked arithmetic
/// Obtain the inner [`std::time::Instant`].
///
/// ```rust
/// # use time::Instant;
/// let now = Instant::now();
/// assert_eq!(now.into_inner(), now.0);
/// ```
pub const fn into_inner(self) -> StdInstant {
self.0
}
}
// region: trait impls
impl From<StdInstant> for Instant {
fn from(instant: StdInstant) -> Self {
Self(instant)
}
}
impl From<Instant> for StdInstant {
fn from(instant: Instant) -> Self {
instant.0
}
}
impl Sub for Instant {
type Output = Duration;
fn sub(self, other: Self) -> Self::Output {
match self.0.cmp(&other.0) {
Ordering::Equal => Duration::ZERO,
Ordering::Greater => (self.0 - other.0)
.try_into()
.expect("overflow converting `std::time::Duration` to `time::Duration`"),
Ordering::Less => -Duration::try_from(other.0 - self.0)
.expect("overflow converting `std::time::Duration` to `time::Duration`"),
}
}
}
impl Sub<StdInstant> for Instant {
type Output = Duration;
fn sub(self, other: StdInstant) -> Self::Output {
self - Self(other)
}
}
impl Sub<Instant> for StdInstant {
type Output = Duration;
fn sub(self, other: Instant) -> Self::Output {
Instant(self) - other
}
}
impl Add<Duration> for Instant {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 + duration.abs_std())
} else if duration.is_negative() {
Self(self.0 - duration.abs_std())
} else {
self
}
}
}
impl Add<Duration> for StdInstant {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
(Instant(self) + duration).0
}
}
impl Add<StdDuration> for Instant {
type Output = Self;
fn add(self, duration: StdDuration) -> Self::Output {
Self(self.0 + duration)
}
}
impl_add_assign!(Instant: Duration, StdDuration);
impl_add_assign!(StdInstant: Duration);
impl Sub<Duration> for Instant {
type Output = Self;
fn sub(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 - duration.abs_std())
} else if duration.is_negative() {
Self(self.0 + duration.abs_std())
} else {
self
}
}
}
impl Sub<Duration> for StdInstant {
type Output = Self;
fn sub(self, duration: Duration) -> Self::Output {
(Instant(self) - duration).0
}
}
impl Sub<StdDuration> for Instant {
type Output = Self;
fn sub(self, duration: StdDuration) -> Self::Output {
Self(self.0 - duration)
}
}
impl_sub_assign!(Instant: Duration, StdDuration);
impl_sub_assign!(StdInstant: Duration);
impl PartialEq<StdInstant> for Instant {
fn eq(&self, rhs: &StdInstant) -> bool {
self.0.eq(rhs)
}
}
impl PartialEq<Instant> for StdInstant {
fn eq(&self, rhs: &Instant) -> bool {
self.eq(&rhs.0)
}
}
impl PartialOrd<StdInstant> for Instant {
fn partial_cmp(&self, rhs: &StdInstant) -> Option<Ordering> {
self.0.partial_cmp(rhs)
}
}
impl PartialOrd<Instant> for StdInstant {
fn partial_cmp(&self, rhs: &Instant) -> Option<Ordering> {
self.partial_cmp(&rhs.0)
}
}
impl AsRef<StdInstant> for Instant {
fn as_ref(&self) -> &StdInstant {
&self.0
}
}
impl Borrow<StdInstant> for Instant {
fn borrow(&self) -> &StdInstant {
&self.0
}
}
// endregion trait impls

330
zeroidc/vendor/time/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,330 @@
//! # Feature flags
//!
//! This crate exposes a number of features. These can be enabled or disabled as shown
//! [in Cargo's documentation](https://doc.rust-lang.org/cargo/reference/features.html). Features
//! are _disabled_ by default unless otherwise noted.
//!
//! Reliance on a given feature is always indicated alongside the item definition.
//!
//! - `std` (_enabled by default, implicitly enables `alloc`_)
//!
//! This enables a number of features that depend on the standard library.
//!
//! - `alloc` (_enabled by default via `std`_)
//!
//! Enables a number of features that require the ability to dynamically allocate memory.
//!
//! - `macros`
//!
//! Enables macros that provide compile-time verification of values and intuitive syntax.
//!
//! - `formatting` (_implicitly enables `std`_)
//!
//! Enables formatting of most structs.
//!
//! - `parsing`
//!
//! Enables parsing of most structs.
//!
//! - `local-offset` (_implicitly enables `std`_)
//!
//! This feature enables a number of methods that allow obtaining the system's UTC offset.
//!
//! - `large-dates`
//!
//! By default, only years within the ±9999 range (inclusive) are supported. If you need support
//! for years outside this range, consider enabling this feature; the supported range will be
//! increased to ±999,999.
//!
//! Note that enabling this feature has some costs, as it means forgoing some optimizations.
//! Ambiguities may be introduced when parsing that would not otherwise exist.
//!
//! If you are using this feature, **please leave a comment**
//! [on this discussion](https://github.com/time-rs/time/discussions/306) with your use case. If
//! there is not sufficient demand for this feature, it will be dropped in a future release.
//!
//! - `serde`
//!
//! Enables [serde](https://docs.rs/serde) support for all types except [`Instant`].
//!
//! - `serde-human-readable` (_implicitly enables `serde`, `formatting`, and `parsing`_)
//!
//! Allows serde representations to use a human-readable format. This is determined by the
//! serializer, not the user. If this feature is not enabled or if the serializer requests a
//! non-human-readable format, a format optimized for binary representation will be used.
//!
//! Libraries should never enable this feature, as the decision of what format to use should be up
//! to the user.
//!
//! - `serde-well-known` (_implicitly enables `serde/alloc`, `formatting`, and `parsing`_)
//!
//! Enables support for serializing and deserializing well-known formats using serde's
//! [`#[with]` attribute](https://serde.rs/field-attrs.html#with).
//!
//! - `rand`
//!
//! Enables [rand](https://docs.rs/rand) support for all types.
//!
//! - `quickcheck` (_implicitly enables `alloc`_)
//!
//! Enables [quickcheck](https://docs.rs/quickcheck) support for all types except [`Instant`].
//!
//! One pseudo-feature flag that is only available to end users is the `unsound_local_offset` cfg.
//! As the name indicates, using the feature is unsound, and [may cause unexpected segmentation
//! faults](https://github.com/time-rs/time/issues/293). Unlike other flags, this is deliberately
//! only available to end users; this is to ensure that a user doesn't have unsound behavior without
//! knowing it. To enable this behavior, you must use `RUSTFLAGS="--cfg unsound_local_offset" cargo
//! build` or similar. Note: This flag is _not tested anywhere_, including in the regular test of
//! the powerset of all feature flags. Use at your own risk. Without this flag, any method that
//! requires the local offset will return the `Err` variant.
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(__time_03_docs, feature(doc_cfg, doc_auto_cfg, doc_notable_trait))]
#![cfg_attr(
__time_03_docs,
deny(rustdoc::broken_intra_doc_links, rustdoc::private_intra_doc_links)
)]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
anonymous_parameters,
clippy::all,
const_err,
illegal_floating_point_literal_pattern,
late_bound_lifetime_arguments,
path_statements,
patterns_in_fns_without_body,
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unsafe_code,
unsafe_op_in_unsafe_fn,
unused_extern_crates
)]
#![warn(
clippy::dbg_macro,
clippy::decimal_literal_representation,
clippy::get_unwrap,
clippy::missing_docs_in_private_items,
clippy::nursery,
clippy::print_stdout,
clippy::todo,
clippy::unimplemented,
clippy::unnested_or_patterns,
clippy::unwrap_in_result,
clippy::unwrap_used,
clippy::use_debug,
deprecated_in_future,
missing_copy_implementations,
missing_debug_implementations,
unused_qualifications,
variant_size_differences
)]
#![allow(clippy::redundant_pub_crate)]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]
#[allow(unused_extern_crates)]
#[cfg(feature = "alloc")]
extern crate alloc;
// region: macros
/// Helper macro for easily implementing `OpAssign`.
macro_rules! __impl_assign {
($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
#[allow(unused_qualifications)]
$(#[$attr])*
impl core::ops::$op<$t> for $target {
fn $fn(&mut self, rhs: $t) {
*self = *self $sym rhs;
}
}
)+};
}
/// Implement `AddAssign` for the provided types.
macro_rules! impl_add_assign {
($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
__impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+);
};
}
/// Implement `SubAssign` for the provided types.
macro_rules! impl_sub_assign {
($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
__impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+);
};
}
/// Implement `MulAssign` for the provided types.
macro_rules! impl_mul_assign {
($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
__impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+);
};
}
/// Implement `DivAssign` for the provided types.
macro_rules! impl_div_assign {
($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
__impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+);
};
}
/// Division of integers, rounding the resulting value towards negative infinity.
macro_rules! div_floor {
($a:expr, $b:expr) => {{
let _a = $a;
let _b = $b;
let (_quotient, _remainder) = (_a / _b, _a % _b);
if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
_quotient - 1
} else {
_quotient
}
}};
}
/// Cascade an out-of-bounds value.
macro_rules! cascade {
(@ordinal ordinal) => {};
(@year year) => {};
// Cascade an out-of-bounds value from "from" to "to".
($from:ident in $min:literal.. $max:literal => $to:tt) => {
#[allow(unused_comparisons, unused_assignments)]
if $from >= $max {
$from -= $max - $min;
$to += 1;
} else if $from < $min {
$from += $max - $min;
$to -= 1;
}
};
// Special case the ordinal-to-year cascade, as it has different behavior.
($ordinal:ident => $year:ident) => {
// We need to actually capture the idents. Without this, macro hygiene causes errors.
cascade!(@ordinal $ordinal);
cascade!(@year $year);
#[allow(unused_assignments)]
if $ordinal > crate::util::days_in_year($year) {
$year += 1;
$ordinal = 1;
} else if $ordinal == 0 {
$year -= 1;
$ordinal = crate::util::days_in_year($year);
}
};
}
/// Returns `Err(error::ComponentRange)` if the value is not in range.
macro_rules! ensure_value_in_range {
($value:ident in $start:expr => $end:expr) => {{
let _start = $start;
let _end = $end;
#[allow(trivial_numeric_casts, unused_comparisons)]
if $value < _start || $value > _end {
return Err(crate::error::ComponentRange {
name: stringify!($value),
minimum: _start as _,
maximum: _end as _,
value: $value as _,
conditional_range: false,
});
}
}};
($value:ident conditionally in $start:expr => $end:expr) => {{
let _start = $start;
let _end = $end;
#[allow(trivial_numeric_casts, unused_comparisons)]
if $value < _start || $value > _end {
return Err(crate::error::ComponentRange {
name: stringify!($value),
minimum: _start as _,
maximum: _end as _,
value: $value as _,
conditional_range: true,
});
}
}};
}
/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
/// usable in `const` contexts.
macro_rules! const_try {
($e:expr) => {
match $e {
Ok(value) => value,
Err(error) => return Err(error),
}
};
}
/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but is usable in `const` contexts.
macro_rules! const_try_opt {
($e:expr) => {
match $e {
Some(value) => value,
None => return None,
}
};
}
// endregion macros
mod date;
mod duration;
pub mod error;
pub mod ext;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod format_description;
#[cfg(feature = "formatting")]
pub mod formatting;
#[cfg(feature = "std")]
mod instant;
#[cfg(feature = "macros")]
pub mod macros;
mod month;
mod offset_date_time;
#[cfg(feature = "parsing")]
pub mod parsing;
mod primitive_date_time;
#[cfg(feature = "quickcheck")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "quickcheck")))]
mod quickcheck;
#[cfg(feature = "rand")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "rand")))]
mod rand;
#[cfg(feature = "serde")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "serde")))]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub mod serde;
mod sys;
#[cfg(test)]
mod tests;
mod time;
mod utc_offset;
pub mod util;
mod weekday;
pub use crate::date::Date;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[cfg(feature = "std")]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
pub use crate::primitive_date_time::PrimitiveDateTime;
pub use crate::time::Time;
pub use crate::utc_offset::UtcOffset;
pub use crate::weekday::Weekday;
/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result<T> = core::result::Result<T, Error>;

114
zeroidc/vendor/time/src/macros.rs vendored Normal file
View File

@@ -0,0 +1,114 @@
//! Macros to construct statically known values.
/// Construct a [`Date`](crate::Date) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// Three formats are supported: year-week-weekday, year-ordinal, and year-month-day.
///
/// ```rust
/// # use time::{Date, Weekday::*, Month, macros::date};
/// assert_eq!(
/// date!(2020 - W 01 - 3),
/// Date::from_iso_week_date(2020, 1, Wednesday)?
/// );
/// assert_eq!(date!(2020 - 001), Date::from_ordinal_date(2020, 1)?);
/// assert_eq!(
/// date!(2020 - 01 - 01),
/// Date::from_calendar_date(2020, Month::January, 1)?
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::date;
/// Construct a [`PrimitiveDateTime`] or [`OffsetDateTime`] with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// The syntax accepted by this macro is the same as [`date!`] and [`time!`], with an optional
/// [`offset!`], all space-separated. If an [`offset!`] is provided, the resulting value will
/// be an [`OffsetDateTime`]; otherwise it will be a [`PrimitiveDateTime`].
///
/// [`OffsetDateTime`]: crate::OffsetDateTime
/// [`PrimitiveDateTime`]: crate::PrimitiveDateTime
pub use time_macros::datetime;
/// Equivalent of performing [`format_description::parse()`] at compile time.
///
/// Using the macro instead of the function results in a static slice rather than a [`Vec`],
/// such that it can be used in `#![no_alloc]` situations.
///
/// The resulting expression can be used in `const` or `static` declarations, and implements
/// the sealed traits required for both formatting and parsing.
///
/// ```rust
/// # use time::{format_description, macros::format_description};
/// assert_eq!(
/// format_description!("[hour]:[minute]:[second]"),
/// format_description::parse("[hour]:[minute]:[second]")?
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can
/// be found in [the book](https://time-rs.github.io/book/api/format-description.html).
///
/// [`format_description::parse()`]: crate::format_description::parse()
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub use time_macros::format_description;
/// Construct a [`UtcOffset`](crate::UtcOffset) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// A sign and the hour must be provided; minutes and seconds default to zero. `UTC` (both
/// uppercase and lowercase) is also allowed.
///
/// ```rust
/// # use time::{UtcOffset, macros::offset};
/// assert_eq!(offset!(UTC), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(utc), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(+0), UtcOffset::from_hms(0, 0, 0)?);
/// assert_eq!(offset!(+1), UtcOffset::from_hms(1, 0, 0)?);
/// assert_eq!(offset!(-1), UtcOffset::from_hms(-1, 0, 0)?);
/// assert_eq!(offset!(+1:30), UtcOffset::from_hms(1, 30, 0)?);
/// assert_eq!(offset!(-1:30), UtcOffset::from_hms(-1, -30, 0)?);
/// assert_eq!(offset!(+1:30:59), UtcOffset::from_hms(1, 30, 59)?);
/// assert_eq!(offset!(-1:30:59), UtcOffset::from_hms(-1, -30, -59)?);
/// assert_eq!(offset!(+23:59:59), UtcOffset::from_hms(23, 59, 59)?);
/// assert_eq!(offset!(-23:59:59), UtcOffset::from_hms(-23, -59, -59)?);
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::offset;
/// Construct a [`Time`](crate::Time) with a statically known value.
///
/// The resulting expression can be used in `const` or `static` declarations.
///
/// Hours and minutes must be provided, while seconds defaults to zero. AM/PM is allowed
/// (either uppercase or lowercase). Any number of subsecond digits may be provided (though any
/// past nine will be discarded).
///
/// All components are validated at compile-time. An error will be raised if any value is
/// invalid.
///
/// ```rust
/// # use time::{Time, macros::time};
/// assert_eq!(time!(0:00), Time::from_hms(0, 0, 0)?);
/// assert_eq!(time!(1:02:03), Time::from_hms(1, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006),
/// Time::from_hms_nano(1, 2, 3, 4_005_006)?
/// );
/// assert_eq!(time!(12:00 am), Time::from_hms(0, 0, 0)?);
/// assert_eq!(time!(1:02:03 am), Time::from_hms(1, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006 am),
/// Time::from_hms_nano(1, 2, 3, 4_005_006)?
/// );
/// assert_eq!(time!(12 pm), Time::from_hms(12, 0, 0)?);
/// assert_eq!(time!(12:00 pm), Time::from_hms(12, 0, 0)?);
/// assert_eq!(time!(1:02:03 pm), Time::from_hms(13, 2, 3)?);
/// assert_eq!(
/// time!(1:02:03.004_005_006 pm),
/// Time::from_hms_nano(13, 2, 3, 4_005_006)?
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::time;

165
zeroidc/vendor/time/src/month.rs vendored Normal file
View File

@@ -0,0 +1,165 @@
//! The `Month` enum and its associated `impl`s.
use core::convert::TryFrom;
use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;
use self::Month::*;
use crate::error;
/// Months of the year.
#[allow(clippy::missing_docs_in_private_items)] // variants
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Month {
January = 1,
February = 2,
March = 3,
April = 4,
May = 5,
June = 6,
July = 7,
August = 8,
September = 9,
October = 10,
November = 11,
December = 12,
}
impl Month {
/// Create a `Month` from its numerical value.
pub(crate) const fn from_number(n: NonZeroU8) -> Result<Self, error::ComponentRange> {
match n.get() {
1 => Ok(January),
2 => Ok(February),
3 => Ok(March),
4 => Ok(April),
5 => Ok(May),
6 => Ok(June),
7 => Ok(July),
8 => Ok(August),
9 => Ok(September),
10 => Ok(October),
11 => Ok(November),
12 => Ok(December),
n => Err(error::ComponentRange {
name: "month",
minimum: 1,
maximum: 12,
value: n as _,
conditional_range: false,
}),
}
}
/// Get the previous month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.previous(), Month::December);
/// ```
pub const fn previous(self) -> Self {
match self {
January => December,
February => January,
March => February,
April => March,
May => April,
June => May,
July => June,
August => July,
September => August,
October => September,
November => October,
December => November,
}
}
/// Get the next month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.next(), Month::February);
/// ```
pub const fn next(self) -> Self {
match self {
January => February,
February => March,
March => April,
April => May,
May => June,
June => July,
July => August,
August => September,
September => October,
October => November,
November => December,
December => January,
}
}
}
impl fmt::Display for Month {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
January => "January",
February => "February",
March => "March",
April => "April",
May => "May",
June => "June",
July => "July",
August => "August",
September => "September",
October => "October",
November => "November",
December => "December",
})
}
}
impl FromStr for Month {
type Err = error::InvalidVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"January" => Ok(January),
"February" => Ok(February),
"March" => Ok(March),
"April" => Ok(April),
"May" => Ok(May),
"June" => Ok(June),
"July" => Ok(July),
"August" => Ok(August),
"September" => Ok(September),
"October" => Ok(October),
"November" => Ok(November),
"December" => Ok(December),
_ => Err(error::InvalidVariant),
}
}
}
impl From<Month> for u8 {
fn from(month: Month) -> Self {
month as _
}
}
impl TryFrom<u8> for Month {
type Error = error::ComponentRange;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match NonZeroU8::new(value) {
Some(value) => Self::from_number(value),
None => Err(error::ComponentRange {
name: "month",
minimum: 1,
maximum: 12,
value: 0,
conditional_range: false,
}),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,192 @@
//! Implementations of the low-level parser combinators.
pub(crate) mod rfc;
use crate::format_description::modifier::Padding;
use crate::parsing::shim::{Integer, IntegerParseBytes};
use crate::parsing::ParsedItem;
/// Parse a "+" or "-" sign. Returns the ASCII byte representing the sign, if present.
pub(crate) const fn sign(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[sign @ (b'-' | b'+'), remaining @ ..] => Some(ParsedItem(remaining, *sign)),
_ => None,
}
}
/// Consume the first matching item, returning its associated value.
pub(crate) fn first_match<'a, T>(
options: impl IntoIterator<Item = (&'a [u8], T)>,
case_sensitive: bool,
) -> impl FnMut(&'a [u8]) -> Option<ParsedItem<'a, T>> {
let mut options = options.into_iter();
move |input| {
options.find_map(|(expected, t)| {
if case_sensitive {
Some(ParsedItem(input.strip_prefix(expected)?, t))
} else {
let n = expected.len();
if n <= input.len() {
let (head, tail) = input.split_at(n);
if head.eq_ignore_ascii_case(expected) {
return Some(ParsedItem(tail, t));
}
}
None
}
})
}
}
/// Consume zero or more instances of the provided parser. The parser must return the unit value.
pub(crate) fn zero_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>(
parser: P,
) -> impl FnMut(&'a [u8]) -> ParsedItem<'a, ()> {
move |mut input| {
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
ParsedItem(input, ())
}
}
/// Consume one of or more instances of the provided parser. The parser must produce the unit value.
pub(crate) fn one_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>(
parser: P,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>> {
move |mut input| {
input = parser(input)?.into_inner();
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume between `n` and `m` instances of the provided parser.
pub(crate) fn n_to_m<
'a,
T,
P: Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
const N: u8,
const M: u8,
>(
parser: P,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, &'a [u8]>> {
debug_assert!(M >= N);
move |mut input| {
// We need to keep this to determine the total length eventually consumed.
let orig_input = input;
// Mandatory
for _ in 0..N {
input = parser(input)?.0;
}
// Optional
for _ in N..M {
match parser(input) {
Some(parsed) => input = parsed.0,
None => break,
}
}
Some(ParsedItem(
input,
&orig_input[..(orig_input.len() - input.len())],
))
}
}
/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits<T: Integer, const N: u8, const M: u8>(
input: &[u8],
) -> Option<ParsedItem<'_, T>> {
debug_assert!(M >= N);
n_to_m::<_, _, N, M>(any_digit)(input)?.flat_map(|value| value.parse_bytes())
}
/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits<T: Integer, const N: u8>(input: &[u8]) -> Option<ParsedItem<'_, T>> {
n_to_m_digits::<_, N, N>(input)
}
/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits_padded<'a, T: Integer, const N: u8>(
padding: Padding,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> {
n_to_m_digits_padded::<_, N, N>(padding)
}
/// Consume between `n` and `m` digits, returning the numerical value.
pub(crate) fn n_to_m_digits_padded<'a, T: Integer, const N: u8, const M: u8>(
padding: Padding,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> {
debug_assert!(M >= N);
move |mut input| match padding {
Padding::None => n_to_m_digits::<_, 1, M>(input),
Padding::Space => {
debug_assert!(N > 0);
let mut orig_input = input;
for _ in 0..(N - 1) {
match ascii_char::<b' '>(input) {
Some(parsed) => input = parsed.0,
None => break,
}
}
let pad_width = (orig_input.len() - input.len()) as u8;
orig_input = input;
for _ in 0..(N - pad_width) {
input = any_digit(input)?.0;
}
for _ in N..M {
match any_digit(input) {
Some(parsed) => input = parsed.0,
None => break,
}
}
ParsedItem(input, &orig_input[..(orig_input.len() - input.len())])
.flat_map(|value| value.parse_bytes())
}
Padding::Zero => n_to_m_digits::<_, N, M>(input),
}
}
/// Consume exactly one digit.
pub(crate) const fn any_digit(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[c, remaining @ ..] if c.is_ascii_digit() => Some(ParsedItem(remaining, *c)),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters.
pub(crate) fn ascii_char<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
match input {
[c, remaining @ ..] if *c == CHAR => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters, case-insensitive.
pub(crate) fn ascii_char_ignore_case<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
match input {
[c, remaining @ ..] if c.eq_ignore_ascii_case(&CHAR) => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Optionally consume an input with a given parser.
pub(crate) fn opt<'a, T>(
parser: impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
) -> impl Fn(&'a [u8]) -> ParsedItem<'a, Option<T>> {
move |input| match parser(input) {
Some(value) => value.map(Some),
None => ParsedItem(input, None),
}
}

View File

@@ -0,0 +1,8 @@
//! Combinators for rules as defined in an RFC.
//!
//! These rules have been converted strictly following the ABNF syntax as specified in [RFC 2234].
//!
//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234
pub(crate) mod rfc2234;
pub(crate) mod rfc2822;

View File

@@ -0,0 +1,13 @@
//! Rules defined in [RFC 2234].
//!
//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234
use crate::parsing::ParsedItem;
/// Consume exactly one space or tab.
pub(crate) const fn wsp(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[b' ' | b'\t', rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
}
}

View File

@@ -0,0 +1,115 @@
//! Rules defined in [RFC 2822].
//!
//! [RFC 2822]: https://datatracker.ietf.org/doc/html/rfc2822
use crate::parsing::combinator::rfc::rfc2234::wsp;
use crate::parsing::combinator::{ascii_char, one_or_more, zero_or_more};
use crate::parsing::ParsedItem;
/// Consume the `fws` rule.
// The full rule is equivalent to /\r\n[ \t]+|[ \t]+(?:\r\n[ \t]+)*/
pub(crate) fn fws(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
if let [b'\r', b'\n', rest @ ..] = input {
one_or_more(wsp)(rest)
} else {
input = one_or_more(wsp)(input)?.into_inner();
while let [b'\r', b'\n', rest @ ..] = input {
input = one_or_more(wsp)(rest)?.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume the `cfws` rule.
// The full rule is equivalent to any combination of `fws` and `comment` so long as it is not empty.
pub(crate) fn cfws(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
one_or_more(|input| fws(input).or_else(|| comment(input)))(input)
}
/// Consume the `comment` rule.
fn comment(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
input = ascii_char::<b'('>(input)?.into_inner();
input = zero_or_more(fws)(input).into_inner();
while let Some(rest) = ccontent(input) {
input = rest.into_inner();
input = zero_or_more(fws)(input).into_inner();
}
input = ascii_char::<b')'>(input)?.into_inner();
Some(ParsedItem(input, ()))
}
/// Consume the `ccontent` rule.
fn ccontent(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
ctext(input)
.or_else(|| quoted_pair(input))
.or_else(|| comment(input))
}
/// Consume the `ctext` rule.
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
fn ctext(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
no_ws_ctl(input).or_else(|| match input {
[33..=39 | 42..=91 | 93..=126, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
})
}
/// Consume the `quoted_pair` rule.
fn quoted_pair(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
input = ascii_char::<b'\\'>(input)?.into_inner();
let old_input_len = input.len();
input = text(input).into_inner();
// If nothing is parsed, this means we hit the `obs-text` rule and nothing matched. This is
// technically a success, but we should still check the `obs-qp` rule to ensure we consume
// everything possible.
if input.len() == old_input_len {
match input {
[0..=127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => Some(ParsedItem(input, ())),
}
} else {
Some(ParsedItem(input, ()))
}
}
/// Consume the `no_ws_ctl` rule.
const fn no_ws_ctl(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[1..=8 | 11..=12 | 14..=31 | 127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
}
}
/// Consume the `text` rule.
fn text<'a>(input: &'a [u8]) -> ParsedItem<'a, ()> {
let new_text = |input: &'a [u8]| match input {
[1..=9 | 11..=12 | 14..=127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
};
let obs_char = |input: &'a [u8]| match input {
// This is technically allowed, but consuming this would mean the rest of the string is
// eagerly consumed without consideration for where the comment actually ends.
[b')', ..] => None,
[0..=9 | 11..=12 | 14..=127, rest @ ..] => Some(rest),
_ => None,
};
let obs_text = |mut input| {
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
while let Some(rest) = obs_char(input) {
input = rest;
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
}
ParsedItem(input, ())
};
new_text(input).unwrap_or_else(|| obs_text(input))
}

View File

@@ -0,0 +1,290 @@
//! Parsing implementations for all [`Component`](crate::format_description::Component)s.
use core::num::{NonZeroU16, NonZeroU8};
use crate::format_description::modifier;
#[cfg(feature = "large-dates")]
use crate::parsing::combinator::n_to_m_digits_padded;
use crate::parsing::combinator::{
any_digit, exactly_n_digits, exactly_n_digits_padded, first_match, opt, sign,
};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
// region: date components
/// Parse the "year" component of a `Date`.
pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<ParsedItem<'_, i32>> {
match modifiers.repr {
modifier::YearRepr::Full => {
let ParsedItem(input, sign) = opt(sign)(input);
#[cfg(not(feature = "large-dates"))]
let ParsedItem(input, year) =
exactly_n_digits_padded::<u32, 4>(modifiers.padding)(input)?;
#[cfg(feature = "large-dates")]
let ParsedItem(input, year) =
n_to_m_digits_padded::<u32, 4, 6>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(year as i32))),
None if modifiers.sign_is_mandatory || year >= 10_000 => None,
_ => Some(ParsedItem(input, year as i32)),
}
}
modifier::YearRepr::LastTwo => {
Some(exactly_n_digits_padded::<u32, 2>(modifiers.padding)(input)?.map(|v| v as i32))
}
}
}
/// Parse the "month" component of a `Date`.
pub(crate) fn parse_month(
input: &[u8],
modifiers: modifier::Month,
) -> Option<ParsedItem<'_, Month>> {
use Month::*;
let ParsedItem(remaining, value) = first_match(
match modifiers.repr {
modifier::MonthRepr::Numerical => {
return exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)?
.flat_map(|n| Month::from_number(n).ok());
}
modifier::MonthRepr::Long => [
(&b"January"[..], January),
(&b"February"[..], February),
(&b"March"[..], March),
(&b"April"[..], April),
(&b"May"[..], May),
(&b"June"[..], June),
(&b"July"[..], July),
(&b"August"[..], August),
(&b"September"[..], September),
(&b"October"[..], October),
(&b"November"[..], November),
(&b"December"[..], December),
],
modifier::MonthRepr::Short => [
(&b"Jan"[..], January),
(&b"Feb"[..], February),
(&b"Mar"[..], March),
(&b"Apr"[..], April),
(&b"May"[..], May),
(&b"Jun"[..], June),
(&b"Jul"[..], July),
(&b"Aug"[..], August),
(&b"Sep"[..], September),
(&b"Oct"[..], October),
(&b"Nov"[..], November),
(&b"Dec"[..], December),
],
},
modifiers.case_sensitive,
)(input)?;
Some(ParsedItem(remaining, value))
}
/// Parse the "week number" component of a `Date`.
pub(crate) fn parse_week_number(
input: &[u8],
modifiers: modifier::WeekNumber,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "weekday" component of a `Date`.
pub(crate) fn parse_weekday(
input: &[u8],
modifiers: modifier::Weekday,
) -> Option<ParsedItem<'_, Weekday>> {
first_match(
match (modifiers.repr, modifiers.one_indexed) {
(modifier::WeekdayRepr::Short, _) => [
(&b"Mon"[..], Weekday::Monday),
(&b"Tue"[..], Weekday::Tuesday),
(&b"Wed"[..], Weekday::Wednesday),
(&b"Thu"[..], Weekday::Thursday),
(&b"Fri"[..], Weekday::Friday),
(&b"Sat"[..], Weekday::Saturday),
(&b"Sun"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Long, _) => [
(&b"Monday"[..], Weekday::Monday),
(&b"Tuesday"[..], Weekday::Tuesday),
(&b"Wednesday"[..], Weekday::Wednesday),
(&b"Thursday"[..], Weekday::Thursday),
(&b"Friday"[..], Weekday::Friday),
(&b"Saturday"[..], Weekday::Saturday),
(&b"Sunday"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Sunday, false) => [
(&b"1"[..], Weekday::Monday),
(&b"2"[..], Weekday::Tuesday),
(&b"3"[..], Weekday::Wednesday),
(&b"4"[..], Weekday::Thursday),
(&b"5"[..], Weekday::Friday),
(&b"6"[..], Weekday::Saturday),
(&b"0"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Sunday, true) => [
(&b"2"[..], Weekday::Monday),
(&b"3"[..], Weekday::Tuesday),
(&b"4"[..], Weekday::Wednesday),
(&b"5"[..], Weekday::Thursday),
(&b"6"[..], Weekday::Friday),
(&b"7"[..], Weekday::Saturday),
(&b"1"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Monday, false) => [
(&b"0"[..], Weekday::Monday),
(&b"1"[..], Weekday::Tuesday),
(&b"2"[..], Weekday::Wednesday),
(&b"3"[..], Weekday::Thursday),
(&b"4"[..], Weekday::Friday),
(&b"5"[..], Weekday::Saturday),
(&b"6"[..], Weekday::Sunday),
],
(modifier::WeekdayRepr::Monday, true) => [
(&b"1"[..], Weekday::Monday),
(&b"2"[..], Weekday::Tuesday),
(&b"3"[..], Weekday::Wednesday),
(&b"4"[..], Weekday::Thursday),
(&b"5"[..], Weekday::Friday),
(&b"6"[..], Weekday::Saturday),
(&b"7"[..], Weekday::Sunday),
],
},
modifiers.case_sensitive,
)(input)
}
/// Parse the "ordinal" component of a `Date`.
pub(crate) fn parse_ordinal(
input: &[u8],
modifiers: modifier::Ordinal,
) -> Option<ParsedItem<'_, NonZeroU16>> {
exactly_n_digits_padded::<_, 3>(modifiers.padding)(input)
}
/// Parse the "day" component of a `Date`.
pub(crate) fn parse_day(
input: &[u8],
modifiers: modifier::Day,
) -> Option<ParsedItem<'_, NonZeroU8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
// endregion date components
// region: time components
/// Indicate whether the hour is "am" or "pm".
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Period {
#[allow(clippy::missing_docs_in_private_items)]
Am,
#[allow(clippy::missing_docs_in_private_items)]
Pm,
}
/// Parse the "hour" component of a `Time`.
pub(crate) fn parse_hour(input: &[u8], modifiers: modifier::Hour) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "minute" component of a `Time`.
pub(crate) fn parse_minute(
input: &[u8],
modifiers: modifier::Minute,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "second" component of a `Time`.
pub(crate) fn parse_second(
input: &[u8],
modifiers: modifier::Second,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
}
/// Parse the "period" component of a `Time`. Required if the hour is on a 12-hour clock.
pub(crate) fn parse_period(
input: &[u8],
modifiers: modifier::Period,
) -> Option<ParsedItem<'_, Period>> {
first_match(
if modifiers.is_uppercase {
[(&b"AM"[..], Period::Am), (&b"PM"[..], Period::Pm)]
} else {
[(&b"am"[..], Period::Am), (&b"pm"[..], Period::Pm)]
},
modifiers.case_sensitive,
)(input)
}
/// Parse the "subsecond" component of a `Time`.
pub(crate) fn parse_subsecond(
input: &[u8],
modifiers: modifier::Subsecond,
) -> Option<ParsedItem<'_, u32>> {
use modifier::SubsecondDigits::*;
Some(match modifiers.digits {
One => exactly_n_digits::<u32, 1>(input)?.map(|v| v * 100_000_000),
Two => exactly_n_digits::<u32, 2>(input)?.map(|v| v * 10_000_000),
Three => exactly_n_digits::<u32, 3>(input)?.map(|v| v * 1_000_000),
Four => exactly_n_digits::<u32, 4>(input)?.map(|v| v * 100_000),
Five => exactly_n_digits::<u32, 5>(input)?.map(|v| v * 10_000),
Six => exactly_n_digits::<u32, 6>(input)?.map(|v| v * 1_000),
Seven => exactly_n_digits::<u32, 7>(input)?.map(|v| v * 100),
Eight => exactly_n_digits::<u32, 8>(input)?.map(|v| v * 10),
Nine => exactly_n_digits::<u32, 9>(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
any_digit(input)?.map(|v| (v - b'0') as u32 * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0') as u32 * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
}
})
}
// endregion time components
// region: offset components
/// Parse the "hour" component of a `UtcOffset`.
pub(crate) fn parse_offset_hour(
input: &[u8],
modifiers: modifier::OffsetHour,
) -> Option<ParsedItem<'_, i8>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, hour) = exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(hour as i8))),
None if modifiers.sign_is_mandatory => None,
_ => Some(ParsedItem(input, hour as i8)),
}
}
/// Parse the "minute" component of a `UtcOffset`.
pub(crate) fn parse_offset_minute(
input: &[u8],
modifiers: modifier::OffsetMinute,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
.map(|offset_minute| offset_minute as _),
)
}
/// Parse the "second" component of a `UtcOffset`.
pub(crate) fn parse_offset_second(
input: &[u8],
modifiers: modifier::OffsetSecond,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
.map(|offset_second| offset_second as _),
)
}
// endregion offset components

49
zeroidc/vendor/time/src/parsing/mod.rs vendored Normal file
View File

@@ -0,0 +1,49 @@
//! Parsing for various types.
pub(crate) mod combinator;
pub(crate) mod component;
pub(crate) mod parsable;
mod parsed;
pub(crate) mod shim;
pub use self::parsable::Parsable;
pub use self::parsed::Parsed;
/// An item that has been parsed. Represented as a `(remaining, value)` pair.
#[derive(Debug)]
pub(crate) struct ParsedItem<'a, T>(pub(crate) &'a [u8], pub(crate) T);
impl<'a, T> ParsedItem<'a, T> {
/// Map the value to a new value, preserving the remaining input.
pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> ParsedItem<'a, U> {
ParsedItem(self.0, f(self.1))
}
/// Map the value to a new, optional value, preserving the remaining input.
pub(crate) fn flat_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<ParsedItem<'a, U>> {
Some(ParsedItem(self.0, f(self.1)?))
}
/// Consume the stored value with the provided function. The remaining input is returned.
#[must_use = "this returns the remaining input"]
pub(crate) fn consume_value(self, f: impl FnOnce(T) -> Option<()>) -> Option<&'a [u8]> {
f(self.1)?;
Some(self.0)
}
}
impl<'a> ParsedItem<'a, ()> {
/// Discard the unit value, returning the remaining input.
#[must_use = "this returns the remaining input"]
pub(crate) const fn into_inner(self) -> &'a [u8] {
self.0
}
}
impl<'a> ParsedItem<'a, Option<()>> {
/// Discard the potential unit value, returning the remaining input.
#[must_use = "this returns the remaining input"]
pub(crate) const fn into_inner(self) -> &'a [u8] {
self.0
}
}

View File

@@ -0,0 +1,492 @@
//! A trait that can be used to parse an item from an input.
use core::convert::TryInto;
use core::ops::Deref;
use crate::error::TryFromParsed;
use crate::format_description::well_known::{Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
use crate::parsing::{Parsed, ParsedItem};
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// A type that can be parsed.
#[cfg_attr(__time_03_docs, doc(notable_trait))]
pub trait Parsable: sealed::Sealed {}
impl Parsable for FormatItem<'_> {}
impl Parsable for [FormatItem<'_>] {}
impl Parsable for Rfc2822 {}
impl Parsable for Rfc3339 {}
impl<T: Deref> Parsable for T where T::Target: Parsable {}
/// Seal the trait to prevent downstream users from implementing it, while still allowing it to
/// exist in generic bounds.
mod sealed {
#[allow(clippy::wildcard_imports)]
use super::*;
/// Parse the item using a format description and an input.
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
pub trait Sealed {
/// Parse the item into the provided [`Parsed`] struct.
///
/// This method can be used to parse a single component without parsing the full value.
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse>;
/// Parse the item into a new [`Parsed`] struct.
///
/// This method can only be used to parse a complete value of a type. If any characters
/// remain after parsing, an error will be returned.
fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
let mut parsed = Parsed::new();
if self.parse_into(input, &mut parsed)?.is_empty() {
Ok(parsed)
} else {
Err(error::Parse::UnexpectedTrailingCharacters)
}
}
/// Parse a [`Date`] from the format description.
fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`Time`] from the format description.
fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`UtcOffset`] from the format description.
fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`PrimitiveDateTime`] from the format description.
fn parse_date_time(&self, input: &[u8]) -> Result<PrimitiveDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`OffsetDateTime`] from the format description.
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
}
}
// region: custom formats
impl sealed::Sealed for FormatItem<'_> {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_item(input, self)?)
}
}
impl sealed::Sealed for [FormatItem<'_>] {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_items(input, self)?)
}
}
impl<T: Deref> sealed::Sealed for T
where
T::Target: sealed::Sealed,
{
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
self.deref().parse_into(input, parsed)
}
}
// endregion custom formats
// region: well-known formats
impl sealed::Sealed for Rfc2822 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws};
use crate::parsing::combinator::{
ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign,
};
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
let input = opt(fws)(input).into_inner();
let input = first_match(
[
(&b"Mon"[..], Weekday::Monday),
(&b"Tue"[..], Weekday::Tuesday),
(&b"Wed"[..], Weekday::Wednesday),
(&b"Thu"[..], Weekday::Thursday),
(&b"Fri"[..], Weekday::Friday),
(&b"Sat"[..], Weekday::Saturday),
(&b"Sun"[..], Weekday::Sunday),
],
false,
)(input)
.and_then(|item| item.consume_value(|value| parsed.set_weekday(value)))
.ok_or(InvalidComponent("weekday"))?;
let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let input = n_to_m_digits::<_, 1, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(value)))
.ok_or(InvalidComponent("day"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let input = first_match(
[
(&b"Jan"[..], Month::January),
(&b"Feb"[..], Month::February),
(&b"Mar"[..], Month::March),
(&b"Apr"[..], Month::April),
(&b"May"[..], Month::May),
(&b"Jun"[..], Month::June),
(&b"Jul"[..], Month::July),
(&b"Aug"[..], Month::August),
(&b"Sep"[..], Month::September),
(&b"Oct"[..], Month::October),
(&b"Nov"[..], Month::November),
(&b"Dec"[..], Month::December),
],
false,
)(input)
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let input = match exactly_n_digits::<u32, 4>(input) {
Some(item) => {
let input = item
.flat_map(|year| if year >= 1900 { Some(year) } else { None })
.and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
.ok_or(InvalidComponent("year"))?;
let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
input
}
None => {
let input = exactly_n_digits::<u32, 2>(input)
.and_then(|item| {
item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
.map(|year| year as _)
.consume_value(|value| parsed.set_year(value))
})
.ok_or(InvalidComponent("year"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
input
}
};
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
.ok_or(InvalidComponent("hour"))?;
let input = opt(cfws)(input).into_inner();
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = opt(cfws)(input).into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
.ok_or(InvalidComponent("minute"))?;
let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) {
let input = input.into_inner(); // discard the colon
let input = opt(cfws)(input).into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
input
} else {
cfws(input).ok_or(InvalidLiteral)?.into_inner()
};
// The RFC explicitly allows leap seconds.
parsed.set_leap_second_allowed(true);
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
let zone_literal = first_match(
[
(&b"UT"[..], 0),
(&b"GMT"[..], 0),
(&b"EST"[..], -5),
(&b"EDT"[..], -4),
(&b"CST"[..], -6),
(&b"CDT"[..], -5),
(&b"MST"[..], -7),
(&b"MDT"[..], -6),
(&b"PST"[..], -8),
(&b"PDT"[..], -7),
],
false,
)(input)
.or_else(|| match input {
[
b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z',
rest @ ..,
] => Some(ParsedItem(rest, 0)),
_ => None,
});
if let Some(zone_literal) = zone_literal {
let input = zone_literal
.consume_value(|value| parsed.set_offset_hour(value))
.ok_or(InvalidComponent("offset hour"))?;
parsed
.set_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))?;
parsed
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
}
})
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
})
.ok_or(InvalidComponent("offset minute"))?;
Ok(input)
}
}
impl sealed::Sealed for Rfc3339 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::{
any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
};
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let input = exactly_n_digits::<u32, 4>(input)
.and_then(|item| item.consume_value(|value| parsed.set_year(value as _)))
.ok_or(InvalidComponent("year"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.flat_map(|value| Month::from_number(value).ok()))
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(value)))
.ok_or(InvalidComponent("day"))?;
let input = ascii_char_ignore_case::<b'T'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
.ok_or(InvalidComponent("hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
.ok_or(InvalidComponent("minute"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<_, 2>(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))?;
let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
.map(|v| (v - b'0') as u32 * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0') as u32 * multiplier;
input = new_input;
multiplier /= 10;
}
parsed
.set_subsecond(value)
.ok_or(InvalidComponent("subsecond"))?;
input
} else {
input
};
// The RFC explicitly allows leap seconds.
parsed.set_leap_second_allowed(true);
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
parsed
.set_offset_hour(0)
.ok_or(InvalidComponent("offset hour"))?;
parsed
.set_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))?;
parsed
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
}
})
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<u8, 2>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
})
.ok_or(InvalidComponent("offset minute"))?;
Ok(input)
}
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::parsing::combinator::{
any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign,
};
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let ParsedItem(input, year) =
exactly_n_digits::<u32, 4>(input).ok_or(InvalidComponent("year"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, month) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("month"))?;
let input = dash(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, day) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("day"))?;
let input = ascii_char_ignore_case::<b'T'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
let ParsedItem(input, hour) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, minute) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("minute"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, mut second) =
exactly_n_digits::<_, 2>(input).ok_or(InvalidComponent("second"))?;
let ParsedItem(input, mut nanosecond) =
if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) = any_digit(input)
.ok_or(InvalidComponent("subsecond"))?
.map(|v| (v - b'0') as u32 * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0') as u32 * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
} else {
ParsedItem(input, 0)
};
let ParsedItem(input, offset) = {
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
ParsedItem(input, UtcOffset::UTC)
} else {
let ParsedItem(input, offset_sign) =
sign(input).ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_hour) =
exactly_n_digits::<u8, 2>(input).ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, offset_minute) =
exactly_n_digits::<u8, 2>(input).ok_or(InvalidComponent("offset minute"))?;
UtcOffset::from_hms(
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
},
offset_minute as _,
0,
)
.map(|offset| ParsedItem(input, offset))
.map_err(|mut err| {
// Provide the user a more accurate error.
if err.name == "hours" {
err.name = "offset hour";
} else if err.name == "minutes" {
err.name = "offset minute";
}
err
})
.map_err(TryFromParsed::ComponentRange)?
}
};
if !input.is_empty() {
return Err(error::Parse::UnexpectedTrailingCharacters);
}
// The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
// the preceding nanosecond. However, leap seconds can only occur as the last second of the
// month UTC.
let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let dt = Month::from_number(month)
.and_then(|month| Date::from_calendar_date(year as _, month, day))
.and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond))
.map(|date| date.assume_offset(offset))
.map_err(TryFromParsed::ComponentRange)?;
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
error::ComponentRange {
name: "second",
minimum: 0,
maximum: 59,
value: 60,
conditional_range: true,
},
)));
}
Ok(dt)
}
}
// endregion well-known formats

View File

@@ -0,0 +1,625 @@
//! Information parsed from an input and format description.
use core::convert::{TryFrom, TryInto};
use core::num::{NonZeroU16, NonZeroU8};
use crate::error::TryFromParsed::InsufficientInformation;
use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
use crate::format_description::{Component, FormatItem};
use crate::parsing::component::{
parse_day, parse_hour, parse_minute, parse_month, parse_offset_hour, parse_offset_minute,
parse_offset_second, parse_ordinal, parse_period, parse_second, parse_subsecond,
parse_week_number, parse_weekday, parse_year, Period,
};
use crate::parsing::ParsedItem;
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// All information parsed.
///
/// This information is directly used to construct the final values.
///
/// Most users will not need think about this struct in any way. It is public to allow for manual
/// control over values, in the instance that the default parser is insufficient.
#[derive(Debug, Clone, Copy)]
pub struct Parsed {
/// Calendar year.
year: Option<i32>,
/// The last two digits of the calendar year.
year_last_two: Option<u8>,
/// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
iso_year: Option<i32>,
/// The last two digits of the ISO week year.
iso_year_last_two: Option<u8>,
/// Month of the year.
month: Option<Month>,
/// Week of the year, where week one begins on the first Sunday of the calendar year.
sunday_week_number: Option<u8>,
/// Week of the year, where week one begins on the first Monday of the calendar year.
monday_week_number: Option<u8>,
/// Week of the year, where week one is the Monday-to-Sunday period containing January 4.
iso_week_number: Option<NonZeroU8>,
/// Day of the week.
weekday: Option<Weekday>,
/// Day of the year.
ordinal: Option<NonZeroU16>,
/// Day of the month.
day: Option<NonZeroU8>,
/// Hour within the day.
hour_24: Option<u8>,
/// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in
/// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field.
hour_12: Option<NonZeroU8>,
/// Whether the `hour_12` field indicates a time that "PM".
hour_12_is_pm: Option<bool>,
/// Minute within the hour.
minute: Option<u8>,
/// Second within the minute.
second: Option<u8>,
/// Nanosecond within the second.
subsecond: Option<u32>,
/// Whole hours of the UTC offset.
offset_hour: Option<i8>,
/// Minutes within the hour of the UTC offset.
offset_minute: Option<i8>,
/// Seconds within the minute of the UTC offset.
offset_second: Option<i8>,
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
leap_second_allowed: bool,
}
impl Parsed {
/// Create a new instance of `Parsed` with no information known.
pub const fn new() -> Self {
Self {
year: None,
year_last_two: None,
iso_year: None,
iso_year_last_two: None,
month: None,
sunday_week_number: None,
monday_week_number: None,
iso_week_number: None,
weekday: None,
ordinal: None,
day: None,
hour_24: None,
hour_12: None,
hour_12_is_pm: None,
minute: None,
second: None,
subsecond: None,
offset_hour: None,
offset_minute: None,
offset_second: None,
leap_second_allowed: false,
}
}
/// Parse a single [`FormatItem`], mutating the struct. The remaining input is returned as the
/// `Ok` value.
///
/// If a [`FormatItem::Optional`] is passed, parsing will not fail; the input will be returned
/// as-is if the expected format is not present.
pub fn parse_item<'a>(
&mut self,
input: &'a [u8],
item: &FormatItem<'_>,
) -> Result<&'a [u8], error::ParseFromDescription> {
match item {
FormatItem::Literal(literal) => Self::parse_literal(input, literal),
FormatItem::Component(component) => self.parse_component(input, *component),
FormatItem::Compound(compound) => self.parse_items(input, compound),
FormatItem::Optional(item) => self.parse_item(input, item).or(Ok(input)),
FormatItem::First(items) => {
let mut first_err = None;
for item in items.iter() {
match self.parse_item(input, item) {
Ok(remaining_input) => return Ok(remaining_input),
Err(err) if first_err.is_none() => first_err = Some(err),
Err(_) => {}
}
}
match first_err {
Some(err) => Err(err),
// This location will be reached if the slice is empty, skipping the `for` loop.
// As this case is expected to be uncommon, there's no need to check up front.
None => Ok(input),
}
}
}
}
/// Parse a sequence of [`FormatItem`]s, mutating the struct. The remaining input is returned as
/// the `Ok` value.
///
/// This method will fail if any of the contained [`FormatItem`]s fail to parse. `self` will not
/// be mutated in this instance.
pub fn parse_items<'a>(
&mut self,
mut input: &'a [u8],
items: &[FormatItem<'_>],
) -> Result<&'a [u8], error::ParseFromDescription> {
// Make a copy that we can mutate. It will only be set to the user's copy if everything
// succeeds.
let mut this = *self;
for item in items {
input = this.parse_item(input, item)?;
}
*self = this;
Ok(input)
}
/// Parse a literal byte sequence. The remaining input is returned as the `Ok` value.
pub fn parse_literal<'a>(
input: &'a [u8],
literal: &[u8],
) -> Result<&'a [u8], error::ParseFromDescription> {
input
.strip_prefix(literal)
.ok_or(error::ParseFromDescription::InvalidLiteral)
}
/// Parse a single component, mutating the struct. The remaining input is returned as the `Ok`
/// value.
pub fn parse_component<'a>(
&mut self,
input: &'a [u8],
component: Component,
) -> Result<&'a [u8], error::ParseFromDescription> {
use error::ParseFromDescription::InvalidComponent;
match component {
Component::Day(modifiers) => parse_day(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_day(value)))
.ok_or(InvalidComponent("day")),
Component::Month(modifiers) => parse_month(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_month(value)))
.ok_or(InvalidComponent("month")),
Component::Ordinal(modifiers) => parse_ordinal(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value)))
.ok_or(InvalidComponent("ordinal")),
Component::Weekday(modifiers) => parse_weekday(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value)))
.ok_or(InvalidComponent("weekday")),
Component::WeekNumber(modifiers) => {
let ParsedItem(remaining, value) =
parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?;
match modifiers.repr {
WeekNumberRepr::Iso => {
NonZeroU8::new(value).and_then(|value| self.set_iso_week_number(value))
}
WeekNumberRepr::Sunday => self.set_sunday_week_number(value),
WeekNumberRepr::Monday => self.set_monday_week_number(value),
}
.ok_or(InvalidComponent("week number"))?;
Ok(remaining)
}
Component::Year(modifiers) => {
let ParsedItem(remaining, value) =
parse_year(input, modifiers).ok_or(InvalidComponent("year"))?;
match (modifiers.iso_week_based, modifiers.repr) {
(false, YearRepr::Full) => self.set_year(value),
(false, YearRepr::LastTwo) => self.set_year_last_two(value as _),
(true, YearRepr::Full) => self.set_iso_year(value),
(true, YearRepr::LastTwo) => self.set_iso_year_last_two(value as _),
}
.ok_or(InvalidComponent("year"))?;
Ok(remaining)
}
Component::Hour(modifiers) => {
let ParsedItem(remaining, value) =
parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?;
if modifiers.is_12_hour_clock {
NonZeroU8::new(value).and_then(|value| self.set_hour_12(value))
} else {
self.set_hour_24(value)
}
.ok_or(InvalidComponent("hour"))?;
Ok(remaining)
}
Component::Minute(modifiers) => parse_minute(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_minute(value)))
.ok_or(InvalidComponent("minute")),
Component::Period(modifiers) => parse_period(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm))
})
.ok_or(InvalidComponent("period")),
Component::Second(modifiers) => parse_second(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_second(value)))
.ok_or(InvalidComponent("second")),
Component::Subsecond(modifiers) => parse_subsecond(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value)))
.ok_or(InvalidComponent("subsecond")),
Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers)
.and_then(|parsed| parsed.consume_value(|value| self.set_offset_hour(value)))
.ok_or(InvalidComponent("offset hour")),
Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_offset_minute_signed(value))
})
.ok_or(InvalidComponent("offset minute")),
Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers)
.and_then(|parsed| {
parsed.consume_value(|value| self.set_offset_second_signed(value))
})
.ok_or(InvalidComponent("offset second")),
}
}
}
/// Generate getters for each of the fields.
macro_rules! getters {
($($name:ident: $ty:ty),+ $(,)?) => {$(
/// Obtain the named component.
pub const fn $name(&self) -> Option<$ty> {
self.$name
}
)*}
}
/// Getter methods
impl Parsed {
getters! {
year: i32,
year_last_two: u8,
iso_year: i32,
iso_year_last_two: u8,
month: Month,
sunday_week_number: u8,
monday_week_number: u8,
iso_week_number: NonZeroU8,
weekday: Weekday,
ordinal: NonZeroU16,
day: NonZeroU8,
hour_24: u8,
hour_12: NonZeroU8,
hour_12_is_pm: bool,
minute: u8,
second: u8,
subsecond: u32,
offset_hour: i8,
}
/// Obtain the absolute value of the offset minute.
#[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")]
pub const fn offset_minute(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs())
}
/// Obtain the offset minute as an `i8`.
pub const fn offset_minute_signed(&self) -> Option<i8> {
self.offset_minute
}
/// Obtain the absolute value of the offset second.
#[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")]
pub const fn offset_second(&self) -> Option<u8> {
Some(const_try_opt!(self.offset_second_signed()).unsigned_abs())
}
/// Obtain the offset second as an `i8`.
pub const fn offset_second_signed(&self) -> Option<i8> {
self.offset_second
}
/// Obtain whether leap seconds are permitted in the current format.
pub(crate) const fn leap_second_allowed(&self) -> bool {
self.leap_second_allowed
}
}
/// Generate setters for each of the fields.
///
/// This macro should only be used for fields where the value is not validated beyond its type.
macro_rules! setters {
($($setter_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
/// Set the named component.
pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
self.$name = Some(value);
Some(())
}
)*}
}
/// Setter methods
///
/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The
/// setters _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
setters! {
set_year year: i32,
set_year_last_two year_last_two: u8,
set_iso_year iso_year: i32,
set_iso_year_last_two iso_year_last_two: u8,
set_month month: Month,
set_sunday_week_number sunday_week_number: u8,
set_monday_week_number monday_week_number: u8,
set_iso_week_number iso_week_number: NonZeroU8,
set_weekday weekday: Weekday,
set_ordinal ordinal: NonZeroU16,
set_day day: NonZeroU8,
set_hour_24 hour_24: u8,
set_hour_12 hour_12: NonZeroU8,
set_hour_12_is_pm hour_12_is_pm: bool,
set_minute minute: u8,
set_second second: u8,
set_subsecond subsecond: u32,
set_offset_hour offset_hour: i8,
}
/// Set the named component.
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_minute_signed()` instead"
)]
pub fn set_offset_minute(&mut self, value: u8) -> Option<()> {
if value > i8::MAX as u8 {
None
} else {
self.set_offset_minute_signed(value as _)
}
}
/// Set the `offset_minute` component.
pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
self.offset_minute = Some(value);
Some(())
}
/// Set the named component.
#[deprecated(
since = "0.3.8",
note = "use `parsed.set_offset_second_signed()` instead"
)]
pub fn set_offset_second(&mut self, value: u8) -> Option<()> {
if value > i8::MAX as u8 {
None
} else {
self.set_offset_second_signed(value as _)
}
}
/// Set the `offset_second` component.
pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
self.offset_second = Some(value);
Some(())
}
/// Set the leap second allowed flag.
pub(crate) fn set_leap_second_allowed(&mut self, value: bool) {
self.leap_second_allowed = value;
}
}
/// Generate build methods for each of the fields.
///
/// This macro should only be used for fields where the value is not validated beyond its type.
macro_rules! builders {
($($builder_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
/// Set the named component and return `self`.
pub const fn $builder_name(mut self, value: $ty) -> Option<Self> {
self.$name = Some(value);
Some(self)
}
)*}
}
/// Builder methods
///
/// All builder methods return `Option<Self>`, which is `Some` if the value was set, and `None` if
/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed.
impl Parsed {
builders! {
with_year year: i32,
with_year_last_two year_last_two: u8,
with_iso_year iso_year: i32,
with_iso_year_last_two iso_year_last_two: u8,
with_month month: Month,
with_sunday_week_number sunday_week_number: u8,
with_monday_week_number monday_week_number: u8,
with_iso_week_number iso_week_number: NonZeroU8,
with_weekday weekday: Weekday,
with_ordinal ordinal: NonZeroU16,
with_day day: NonZeroU8,
with_hour_24 hour_24: u8,
with_hour_12 hour_12: NonZeroU8,
with_hour_12_is_pm hour_12_is_pm: bool,
with_minute minute: u8,
with_second second: u8,
with_subsecond subsecond: u32,
with_offset_hour offset_hour: i8,
}
/// Set the named component and return `self`.
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_minute_signed()` instead"
)]
pub const fn with_offset_minute(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_minute_signed(value as _)
}
}
/// Set the `offset_minute` component and return `self`.
pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> {
self.offset_minute = Some(value);
Some(self)
}
/// Set the named component and return `self`.
#[deprecated(
since = "0.3.8",
note = "use `parsed.with_offset_second_signed()` instead"
)]
pub const fn with_offset_second(self, value: u8) -> Option<Self> {
if value > i8::MAX as u8 {
None
} else {
self.with_offset_second_signed(value as _)
}
}
/// Set the `offset_second` component and return `self`.
pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> {
self.offset_second = Some(value);
Some(self)
}
}
impl TryFrom<Parsed> for Date {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
/// Match on the components that need to be present.
macro_rules! match_ {
(_ => $catch_all:expr $(,)?) => {
$catch_all
};
(($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => {
if let ($(Some($name)),*) = ($(parsed.$name()),*) {
$arm
} else {
match_!($($rest)*)
}
};
}
/// Get the value needed to adjust the ordinal day for Sunday and Monday-based week
/// numbering.
const fn adjustment(year: i32) -> i16 {
match Date::__from_ordinal_date_unchecked(year, 1).weekday() {
Weekday::Monday => 7,
Weekday::Tuesday => 1,
Weekday::Wednesday => 2,
Weekday::Thursday => 3,
Weekday::Friday => 4,
Weekday::Saturday => 5,
Weekday::Sunday => 6,
}
}
// TODO Only the basics have been covered. There are many other valid values that are not
// currently constructed from the information known.
match_! {
(year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?),
(year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?),
(iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date(
iso_year,
iso_week_number.get(),
weekday,
)?),
(year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
(sunday_week_number as i16 * 7 + weekday.number_days_from_sunday() as i16
- adjustment(year)
+ 1) as u16,
)?),
(year, monday_week_number, weekday) => Ok(Self::from_ordinal_date(
year,
(monday_week_number as i16 * 7 + weekday.number_days_from_monday() as i16
- adjustment(year)
+ 1) as u16,
)?),
_ => Err(InsufficientInformation),
}
}
}
impl TryFrom<Parsed> for Time {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) {
(Some(hour), _, _) => hour,
(_, Some(hour), Some(false)) if hour.get() == 12 => 0,
(_, Some(hour), Some(true)) if hour.get() == 12 => 12,
(_, Some(hour), Some(false)) => hour.get(),
(_, Some(hour), Some(true)) => hour.get() + 12,
_ => return Err(InsufficientInformation),
};
if parsed.hour_24().is_none()
&& parsed.hour_12().is_some()
&& parsed.hour_12_is_pm().is_some()
&& parsed.minute().is_none()
&& parsed.second().is_none()
&& parsed.subsecond().is_none()
{
return Ok(Self::from_hms_nano(hour, 0, 0, 0)?);
}
let minute = parsed.minute().ok_or(InsufficientInformation)?;
let second = parsed.second().unwrap_or(0);
let subsecond = parsed.subsecond().unwrap_or(0);
Ok(Self::from_hms_nano(hour, minute, second, subsecond)?)
}
}
impl TryFrom<Parsed> for UtcOffset {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
let hour = parsed.offset_hour().ok_or(InsufficientInformation)?;
let minute = parsed.offset_minute_signed().unwrap_or(0);
let second = parsed.offset_second_signed().unwrap_or(0);
Self::from_hms(hour, minute, second).map_err(|mut err| {
// Provide the user a more accurate error.
if err.name == "hours" {
err.name = "offset hour";
} else if err.name == "minutes" {
err.name = "offset minute";
} else if err.name == "seconds" {
err.name = "offset second";
}
err.into()
})
}
}
impl TryFrom<Parsed> for PrimitiveDateTime {
type Error = error::TryFromParsed;
fn try_from(parsed: Parsed) -> Result<Self, Self::Error> {
Ok(Self::new(parsed.try_into()?, parsed.try_into()?))
}
}
impl TryFrom<Parsed> for OffsetDateTime {
type Error = error::TryFromParsed;
#[allow(clippy::unwrap_in_result)] // We know the values are valid.
fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> {
// Some well-known formats explicitly allow leap seconds. We don't currently support them,
// so treat it as the nearest preceding moment that can be represented. Because leap seconds
// always fall at the end of a month UTC, reject any that are at other times.
let leap_second_input = if parsed.leap_second_allowed() && parsed.second() == Some(60) {
parsed.set_second(59).expect("59 is a valid second");
parsed
.set_subsecond(999_999_999)
.expect("999_999_999 is a valid subsecond");
true
} else {
false
};
let dt = PrimitiveDateTime::try_from(parsed)?.assume_offset(parsed.try_into()?);
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::TryFromParsed::ComponentRange(
error::ComponentRange {
name: "second",
minimum: 0,
maximum: 59,
value: 60,
conditional_range: true,
},
));
}
Ok(dt)
}
}

50
zeroidc/vendor/time/src/parsing/shim.rs vendored Normal file
View File

@@ -0,0 +1,50 @@
//! Extension traits for things either not implemented or not yet stable in the MSRV.
/// Equivalent of `foo.parse()` for slices.
pub(crate) trait IntegerParseBytes<T> {
#[allow(clippy::missing_docs_in_private_items)]
fn parse_bytes(&self) -> Option<T>;
}
impl<T: Integer> IntegerParseBytes<T> for [u8] {
fn parse_bytes(&self) -> Option<T> {
T::parse_bytes(self)
}
}
/// Marker trait for all integer types, including `NonZero*`
pub(crate) trait Integer: Sized {
#[allow(clippy::missing_docs_in_private_items)]
fn parse_bytes(src: &[u8]) -> Option<Self>;
}
/// Parse the given types from bytes.
macro_rules! impl_parse_bytes {
($($t:ty)*) => ($(
impl Integer for $t {
#[allow(trivial_numeric_casts)]
fn parse_bytes(src: &[u8]) -> Option<Self> {
src.iter().try_fold::<Self, _, _>(0, |result, c| {
result.checked_mul(10)?.checked_add((c - b'0') as Self)
})
}
}
)*)
}
impl_parse_bytes! { u8 u16 u32 }
/// Parse the given types from bytes.
macro_rules! impl_parse_bytes_nonzero {
($($t:ty)*) => {$(
impl Integer for $t {
fn parse_bytes(src: &[u8]) -> Option<Self> {
Self::new(src.parse_bytes()?)
}
}
)*}
}
impl_parse_bytes_nonzero! {
core::num::NonZeroU8
core::num::NonZeroU16
}

View File

@@ -0,0 +1,936 @@
//! The [`PrimitiveDateTime`] 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::{error, util, Date, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday};
/// Combined date and time.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PrimitiveDateTime {
#[allow(clippy::missing_docs_in_private_items)]
pub(crate) date: Date,
#[allow(clippy::missing_docs_in_private_items)]
pub(crate) time: Time,
}
impl PrimitiveDateTime {
/// The smallest value that can be represented by `PrimitiveDateTime`.
///
/// Depending on `large-dates` feature flag, value of this constant may vary.
///
/// 1. With `large-dates` disabled it is equal to `-9999 - 01 - 01 00:00:00.0`
/// 2. With `large-dates` enabled it is equal to `-999999 - 01 - 01 00:00:00.0`
///
/// ```rust
/// # use time::{PrimitiveDateTime, macros::datetime};
/// // Assuming `large-dates` feature is enabled.
/// assert_eq!(PrimitiveDateTime::MIN, datetime!(-999999 - 01 - 01 0:00));
/// ```
pub const MIN: Self = Self::new(Date::MIN, Time::MIN);
/// The largest value that can be represented by `PrimitiveDateTime`.
///
/// Depending on `large-dates` feature flag, value of this constant may vary.
///
/// 1. With `large-dates` disabled it is equal to `9999 - 12 - 31 23:59:59.999_999_999`
/// 2. With `large-dates` enabled it is equal to `999999 - 12 - 31 23:59:59.999_999_999`
///
/// ```rust
/// # use time::{PrimitiveDateTime, macros::datetime};
/// // Assuming `large-dates` feature is enabled.
/// assert_eq!(PrimitiveDateTime::MAX, datetime!(+999999 - 12 - 31 23:59:59.999_999_999));
/// ```
pub const MAX: Self = Self::new(Date::MAX, Time::MAX);
/// Create a new `PrimitiveDateTime` from the provided [`Date`] and [`Time`].
///
/// ```rust
/// # use time::{PrimitiveDateTime, macros::{date, datetime, time}};
/// assert_eq!(
/// PrimitiveDateTime::new(date!(2019-01-01), time!(0:00)),
/// datetime!(2019-01-01 0:00),
/// );
/// ```
pub const fn new(date: Date, time: Time) -> Self {
Self { date, time }
}
// region: component getters
/// Get the [`Date`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time::macros::{date, datetime};
/// assert_eq!(datetime!(2019-01-01 0:00).date(), date!(2019-01-01));
/// ```
pub const fn date(self) -> Date {
self.date
}
/// Get the [`Time`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time::macros::{datetime, time};
/// assert_eq!(datetime!(2019-01-01 0:00).time(), time!(0:00));
pub const fn time(self) -> Time {
self.time
}
// endregion component getters
// region: date getters
/// Get the year of the date.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).year(), 2019);
/// assert_eq!(datetime!(2019-12-31 0:00).year(), 2019);
/// assert_eq!(datetime!(2020-01-01 0:00).year(), 2020);
/// ```
pub const fn year(self) -> i32 {
self.date.year()
}
/// Get the month of the date.
///
/// ```rust
/// # use time::{macros::datetime, Month};
/// assert_eq!(datetime!(2019-01-01 0:00).month(), Month::January);
/// assert_eq!(datetime!(2019-12-31 0:00).month(), Month::December);
/// ```
pub const fn month(self) -> Month {
self.date.month()
}
/// Get the day of the date.
///
/// The returned value will always be in the range `1..=31`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).day(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).day(), 31);
/// ```
pub const fn day(self) -> u8 {
self.date.day()
}
/// 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::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).ordinal(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365);
/// ```
pub const fn ordinal(self) -> u16 {
self.date.ordinal()
}
/// Get the ISO week number.
///
/// The returned value will always be in the range `1..=53`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).iso_week(), 1);
/// assert_eq!(datetime!(2019-10-04 0:00).iso_week(), 40);
/// assert_eq!(datetime!(2020-01-01 0:00).iso_week(), 1);
/// assert_eq!(datetime!(2020-12-31 0:00).iso_week(), 53);
/// assert_eq!(datetime!(2021-01-01 0:00).iso_week(), 53);
/// ```
pub const fn iso_week(self) -> u8 {
self.date.iso_week()
}
/// 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::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00).sunday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00).sunday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00).sunday_based_week(), 0);
/// ```
pub const fn sunday_based_week(self) -> u8 {
self.date.sunday_based_week()
}
/// 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::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-01-01 0:00).monday_based_week(), 0);
/// assert_eq!(datetime!(2020-12-31 0:00).monday_based_week(), 52);
/// assert_eq!(datetime!(2021-01-01 0:00).monday_based_week(), 0);
/// ```
pub const fn monday_based_week(self) -> u8 {
self.date.monday_based_week()
}
/// Get the year, month, and day.
///
/// ```rust
/// # use time::{macros::datetime, Month};
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_calendar_date(),
/// (2019, Month::January, 1)
/// );
/// ```
pub const fn to_calendar_date(self) -> (i32, Month, u8) {
self.date.to_calendar_date()
}
/// Get the year and ordinal day number.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1));
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
self.date.to_ordinal_date()
}
/// Get the ISO 8601 year, week number, and weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::datetime};
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_iso_week_date(),
/// (2019, 1, Tuesday)
/// );
/// assert_eq!(
/// datetime!(2019-10-04 0:00).to_iso_week_date(),
/// (2019, 40, Friday)
/// );
/// assert_eq!(
/// datetime!(2020-01-01 0:00).to_iso_week_date(),
/// (2020, 1, Wednesday)
/// );
/// assert_eq!(
/// datetime!(2020-12-31 0:00).to_iso_week_date(),
/// (2020, 53, Thursday)
/// );
/// assert_eq!(
/// datetime!(2021-01-01 0:00).to_iso_week_date(),
/// (2020, 53, Friday)
/// );
/// ```
pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) {
self.date.to_iso_week_date()
}
/// Get the weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::datetime};
/// assert_eq!(datetime!(2019-01-01 0:00).weekday(), Tuesday);
/// assert_eq!(datetime!(2019-02-01 0:00).weekday(), Friday);
/// assert_eq!(datetime!(2019-03-01 0:00).weekday(), Friday);
/// assert_eq!(datetime!(2019-04-01 0:00).weekday(), Monday);
/// assert_eq!(datetime!(2019-05-01 0:00).weekday(), Wednesday);
/// assert_eq!(datetime!(2019-06-01 0:00).weekday(), Saturday);
/// assert_eq!(datetime!(2019-07-01 0:00).weekday(), Monday);
/// assert_eq!(datetime!(2019-08-01 0:00).weekday(), Thursday);
/// assert_eq!(datetime!(2019-09-01 0:00).weekday(), Sunday);
/// assert_eq!(datetime!(2019-10-01 0:00).weekday(), Tuesday);
/// assert_eq!(datetime!(2019-11-01 0:00).weekday(), Friday);
/// assert_eq!(datetime!(2019-12-01 0:00).weekday(), Sunday);
/// ```
pub const fn weekday(self) -> Weekday {
self.date.weekday()
}
/// Get the Julian day for the date. The time is not taken into account for this calculation.
///
/// 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::datetime;
/// assert_eq!(datetime!(-4713-11-24 0:00).to_julian_day(), 0);
/// assert_eq!(datetime!(2000-01-01 0:00).to_julian_day(), 2_451_545);
/// assert_eq!(datetime!(2019-01-01 0:00).to_julian_day(), 2_458_485);
/// assert_eq!(datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849);
/// ```
pub const fn to_julian_day(self) -> i32 {
self.date.to_julian_day()
}
// endregion date getters
// region: time getters
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms(), (0, 0, 0));
/// assert_eq!(datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
self.time.as_hms()
}
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_milli(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999).as_hms_milli(),
/// (23, 59, 59, 999)
/// );
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
self.time.as_hms_milli()
}
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_micro(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999).as_hms_micro(),
/// (23, 59, 59, 999_999)
/// );
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
self.time.as_hms_micro()
}
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_nano(), (0, 0, 0, 0));
/// assert_eq!(
/// datetime!(2020-01-01 23:59:59.999_999_999).as_hms_nano(),
/// (23, 59, 59, 999_999_999)
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
self.time.as_hms_nano()
}
/// Get the clock hour.
///
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).hour(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
self.time.hour()
}
/// Get the minute within the hour.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).minute(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
self.time.minute()
}
/// Get the second within the minute.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).second(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
self.time.second()
}
/// Get the milliseconds within the second.
///
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).millisecond(), 0);
/// assert_eq!(datetime!(2019-01-01 23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
self.time.millisecond()
}
/// Get the microseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).microsecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999).microsecond(),
/// 999_999
/// );
/// ```
pub const fn microsecond(self) -> u32 {
self.time.microsecond()
}
/// Get the nanoseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).nanosecond(), 0);
/// assert_eq!(
/// datetime!(2019-01-01 23:59:59.999_999_999).nanosecond(),
/// 999_999_999,
/// );
/// ```
pub const fn nanosecond(self) -> u32 {
self.time.nanosecond()
}
// endregion time getters
// region: attach offset
/// Assuming that the existing `PrimitiveDateTime` represents a moment in the provided
/// [`UtcOffset`], return an [`OffsetDateTime`].
///
/// ```rust
/// # use time::macros::{datetime, offset};
/// assert_eq!(
/// datetime!(2019-01-01 0:00)
/// .assume_offset(offset!(UTC))
/// .unix_timestamp(),
/// 1_546_300_800,
/// );
/// assert_eq!(
/// datetime!(2019-01-01 0:00)
/// .assume_offset(offset!(-1))
/// .unix_timestamp(),
/// 1_546_304_400,
/// );
/// ```
pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime {
OffsetDateTime {
utc_datetime: self.offset_to_utc(offset),
offset,
}
}
/// Assuming that the existing `PrimitiveDateTime` represents a moment in UTC, return an
/// [`OffsetDateTime`].
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).assume_utc().unix_timestamp(),
/// 1_546_300_800,
/// );
/// ```
pub const fn assume_utc(self) -> OffsetDateTime {
OffsetDateTime {
utc_datetime: self,
offset: UtcOffset::UTC,
}
}
// endregion attach offset
// region: checked arithmetic
/// Computes `self + duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time::macros::datetime;
/// let datetime = Date::MIN.midnight();
/// assert_eq!(datetime.checked_add((-2).days()), None);
///
/// let datetime = Date::MAX.midnight();
/// assert_eq!(datetime.checked_add(1.days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).checked_add(27.hours()),
/// Some(datetime!(2019 - 11 - 26 18:30))
/// );
/// ```
pub const fn checked_add(self, duration: Duration) -> Option<Self> {
let (date_adjustment, time) = self.time.adjusting_add(duration);
let date = const_try_opt!(self.date.checked_add(duration));
Some(Self {
date: match date_adjustment {
util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
util::DateAdjustment::Next => const_try_opt!(date.next_day()),
util::DateAdjustment::None => date,
},
time,
})
}
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time::macros::datetime;
/// let datetime = Date::MIN.midnight();
/// assert_eq!(datetime.checked_sub(2.days()), None);
///
/// let datetime = Date::MAX.midnight();
/// assert_eq!(datetime.checked_sub((-1).days()), None);
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).checked_sub(27.hours()),
/// Some(datetime!(2019 - 11 - 24 12:30))
/// );
/// ```
pub const fn checked_sub(self, duration: Duration) -> Option<Self> {
let (date_adjustment, time) = self.time.adjusting_sub(duration);
let date = const_try_opt!(self.date.checked_sub(duration));
Some(Self {
date: match date_adjustment {
util::DateAdjustment::Previous => const_try_opt!(date.previous_day()),
util::DateAdjustment::Next => const_try_opt!(date.next_day()),
util::DateAdjustment::None => date,
},
time,
})
}
// endregion: checked arithmetic
// region: saturating arithmetic
/// Computes `self + duration`, saturating value on overflow.
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time::macros::datetime;
/// assert_eq!(
/// PrimitiveDateTime::MIN.saturating_add((-2).days()),
/// PrimitiveDateTime::MIN
/// );
///
/// assert_eq!(
/// PrimitiveDateTime::MAX.saturating_add(2.days()),
/// PrimitiveDateTime::MAX
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).saturating_add(27.hours()),
/// datetime!(2019 - 11 - 26 18:30)
/// );
/// ```
pub const fn saturating_add(self, duration: Duration) -> Self {
if let Some(datetime) = self.checked_add(duration) {
datetime
} else if duration.is_negative() {
Self::MIN
} else {
Self::MAX
}
}
/// Computes `self - duration`, saturating value on overflow.
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time::macros::datetime;
/// assert_eq!(
/// PrimitiveDateTime::MIN.saturating_sub(2.days()),
/// PrimitiveDateTime::MIN
/// );
///
/// assert_eq!(
/// PrimitiveDateTime::MAX.saturating_sub((-2).days()),
/// PrimitiveDateTime::MAX
/// );
///
/// assert_eq!(
/// datetime!(2019 - 11 - 25 15:30).saturating_sub(27.hours()),
/// datetime!(2019 - 11 - 24 12:30)
/// );
/// ```
pub const fn saturating_sub(self, duration: Duration) -> Self {
if let Some(datetime) = self.checked_sub(duration) {
datetime
} else if duration.is_negative() {
Self::MAX
} else {
Self::MIN
}
}
// endregion: saturating arithmetic
}
// region: replacement
/// Methods that replace part of the `PrimitiveDateTime`.
impl PrimitiveDateTime {
/// Replace the time, preserving the date.
///
/// ```rust
/// # use time::macros::{datetime, time};
/// assert_eq!(
/// datetime!(2020-01-01 17:00).replace_time(time!(5:00)),
/// datetime!(2020-01-01 5:00)
/// );
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_time(self, time: Time) -> Self {
self.date.with_time(time)
}
/// Replace the date, preserving the time.
///
/// ```rust
/// # use time::macros::{datetime, date};
/// assert_eq!(
/// datetime!(2020-01-01 12:00).replace_date(date!(2020-01-30)),
/// datetime!(2020-01-30 12:00)
/// );
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_date(self, date: Date) -> Self {
date.with_time(self.time)
}
/// Replace the year. The month and day will be unchanged.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_year(2019),
/// Ok(datetime!(2019 - 02 - 18 12:00))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> {
Ok(const_try!(self.date.replace_year(year)).with_time(self.time))
}
/// Replace the month of the year.
///
/// ```rust
/// # use time::macros::datetime;
/// # use time::Month;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_month(Month::January),
/// Ok(datetime!(2022 - 01 - 18 12:00))
/// );
/// assert!(datetime!(2022 - 01 - 30 12:00).replace_month(Month::February).is_err()); // 30 isn't a valid day in February
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> {
Ok(const_try!(self.date.replace_month(month)).with_time(self.time))
}
/// Replace the day of the month.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_day(1),
/// Ok(datetime!(2022 - 02 - 01 12:00))
/// );
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_day(0).is_err()); // 00 isn't a valid day
/// assert!(datetime!(2022 - 02 - 18 12:00).replace_day(30).is_err()); // 30 isn't a valid day in February
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> {
Ok(const_try!(self.date.replace_day(day)).with_time(self.time))
}
/// Replace the clock hour.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_hour(7),
/// Ok(datetime!(2022 - 02 - 18 07:02:03.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_hour(hour))))
}
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_minute(7),
/// Ok(datetime!(2022 - 02 - 18 01:07:03.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_minute(minute))))
}
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_second(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:07.004_005_006))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_second(second))))
}
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_millisecond(7),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_millisecond(
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_millisecond(millisecond))))
}
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_microsecond(7_008),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_microsecond(
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_microsecond(microsecond))))
}
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_nanosecond(7_008_009),
/// Ok(datetime!(2022 - 02 - 18 01:02:03.007_008_009))
/// );
/// assert!(datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
#[must_use = "This method does not mutate the original `PrimitiveDateTime`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
Ok(self
.date()
.with_time(const_try!(self.time.replace_nanosecond(nanosecond))))
}
}
// endregion replacement
// region: offset conversion helpers
/// Helper methods to adjust a [`PrimitiveDateTime`] to a given [`UtcOffset`].
impl PrimitiveDateTime {
/// Assuming that the current [`PrimitiveDateTime`] is a value in the provided [`UtcOffset`],
/// obtain the equivalent value in the UTC.
pub(crate) const fn offset_to_utc(self, offset: UtcOffset) -> Self {
let mut second = self.second() as i8 - offset.seconds_past_minute();
let mut minute = self.minute() as i8 - offset.minutes_past_hour();
let mut hour = self.hour() as i8 - offset.whole_hours();
let (mut year, mut ordinal) = self.date.to_ordinal_date();
cascade!(second in 0..60 => minute);
cascade!(minute in 0..60 => hour);
cascade!(hour in 0..24 => ordinal);
cascade!(ordinal => year);
Self {
date: Date::__from_ordinal_date_unchecked(year, ordinal),
time: Time::__from_hms_nanos_unchecked(
hour as _,
minute as _,
second as _,
self.nanosecond(),
),
}
}
/// Assuming that the current [`PrimitiveDateTime`] is a value in UTC, obtain the equivalent
/// value in the provided [`UtcOffset`].
pub(crate) const fn utc_to_offset(self, offset: UtcOffset) -> Self {
self.offset_to_utc(UtcOffset::__from_hms_unchecked(
-offset.whole_hours(),
-offset.minutes_past_hour(),
-offset.seconds_past_minute(),
))
}
}
// endregion offset conversion helpers
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl PrimitiveDateTime {
/// Format the `PrimitiveDateTime` using the provided [format
/// description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
format.format_into(output, Some(self.date), Some(self.time), None)
}
/// Format the `PrimitiveDateTime` using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::datetime};
/// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
/// assert_eq!(
/// datetime!(2020-01-02 03:04:05).format(&format)?,
/// "2020-01-02 03:04:05"
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
format.format(Some(self.date), Some(self.time), None)
}
}
#[cfg(feature = "parsing")]
impl PrimitiveDateTime {
/// Parse a `PrimitiveDateTime` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::datetime, PrimitiveDateTime};
/// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
/// assert_eq!(
/// PrimitiveDateTime::parse("2020-01-02 03:04:05", &format)?,
/// datetime!(2020-01-02 03:04:05)
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_date_time(input.as_bytes())
}
}
impl fmt::Display for PrimitiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.date, self.time)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for PrimitiveDateTime {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
self.checked_add(duration)
.expect("resulting value is out of range")
}
}
impl Add<StdDuration> for PrimitiveDateTime {
type Output = Self;
fn add(self, duration: StdDuration) -> Self::Output {
let (is_next_day, time) = self.time.adjusting_add_std(duration);
Self {
date: if is_next_day {
(self.date + duration)
.next_day()
.expect("resulting value is out of range")
} else {
self.date + duration
},
time,
}
}
}
impl_add_assign!(PrimitiveDateTime: Duration, StdDuration);
impl Sub<Duration> for PrimitiveDateTime {
type Output = Self;
fn sub(self, duration: Duration) -> Self::Output {
self.checked_sub(duration)
.expect("resulting value is out of range")
}
}
impl Sub<StdDuration> for PrimitiveDateTime {
type Output = Self;
fn sub(self, duration: StdDuration) -> Self::Output {
let (is_previous_day, time) = self.time.adjusting_sub_std(duration);
Self {
date: if is_previous_day {
(self.date - duration)
.previous_day()
.expect("resulting value is out of range")
} else {
self.date - duration
},
time,
}
}
}
impl_sub_assign!(PrimitiveDateTime: Duration, StdDuration);
impl Sub for PrimitiveDateTime {
type Output = Duration;
fn sub(self, rhs: Self) -> Self::Output {
(self.date - rhs.date) + (self.time - rhs.time)
}
}
// endregion trait impls

216
zeroidc/vendor/time/src/quickcheck.rs vendored Normal file
View File

@@ -0,0 +1,216 @@
//! Implementations of the [`quickcheck::Arbitrary`](quickcheck_dep::Arbitrary) trait.
//!
//! This enables users to write tests such as this, and have test values provided automatically:
//!
//! ```
//! # #![allow(dead_code)]
//! # use quickcheck_dep::quickcheck;
//! # #[cfg(pretend_we_didnt_rename_the_dependency)]
//! use quickcheck::quickcheck;
//! use time::Date;
//!
//! struct DateRange {
//! from: Date,
//! to: Date,
//! }
//!
//! impl DateRange {
//! fn new(from: Date, to: Date) -> Result<Self, ()> {
//! Ok(DateRange { from, to })
//! }
//! }
//!
//! quickcheck! {
//! fn date_range_is_well_defined(from: Date, to: Date) -> bool {
//! let r = DateRange::new(from, to);
//! if from <= to {
//! r.is_ok()
//! } else {
//! r.is_err()
//! }
//! }
//! }
//! ```
//!
//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in
//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple
//! anyway.
use alloc::boxed::Box;
use quickcheck_dep::{empty_shrinker, single_shrinker, Arbitrary, Gen};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// Obtain an arbitrary value between the minimum and maximum inclusive.
macro_rules! arbitrary_between {
($type:ty; $gen:expr, $min:expr, $max:expr) => {{
let min = $min;
let max = $max;
let range = max - min;
<$type>::arbitrary($gen).rem_euclid(range + 1) + min
}};
}
impl Arbitrary for Date {
fn arbitrary(g: &mut Gen) -> Self {
Self::from_julian_day_unchecked(arbitrary_between!(
i32;
g,
Self::MIN.to_julian_day(),
Self::MAX.to_julian_day()
))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.to_ordinal_date()
.shrink()
.flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
)
}
}
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
Self::nanoseconds_i128(arbitrary_between!(
i128;
g,
Self::MIN.whole_nanoseconds(),
Self::MAX.whole_nanoseconds()
))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.subsec_nanoseconds(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
// Coerce the sign if necessary.
if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) {
nanoseconds *= -1;
}
Self::new_unchecked(seconds, nanoseconds)
}),
)
}
}
impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
Self::__from_hms_nanos_unchecked(
arbitrary_between!(u8; g, 0, 23),
arbitrary_between!(u8; g, 0, 59),
arbitrary_between!(u8; g, 0, 59),
arbitrary_between!(u32; g, 0, 999_999_999),
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_nano()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
}),
)
}
}
impl Arbitrary for PrimitiveDateTime {
fn arbitrary(g: &mut Gen) -> Self {
Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date, self.time)
.shrink()
.map(|(date, time)| Self { date, time }),
)
}
}
impl Arbitrary for UtcOffset {
fn arbitrary(g: &mut Gen) -> Self {
let seconds = arbitrary_between!(i32; g, -86_399, 86_399);
Self::__from_hms_unchecked(
(seconds / 3600) as _,
((seconds % 3600) / 60) as _,
(seconds % 60) as _,
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms().shrink().map(|(hours, minutes, seconds)| {
Self::__from_hms_unchecked(hours, minutes, seconds)
}),
)
}
}
impl Arbitrary for OffsetDateTime {
fn arbitrary(g: &mut Gen) -> Self {
let datetime = PrimitiveDateTime::arbitrary(g);
datetime.assume_offset(<_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.utc_datetime.utc_to_offset(self.offset), self.offset)
.shrink()
.map(|(utc_datetime, offset)| utc_datetime.assume_offset(offset)),
)
}
}
impl Arbitrary for Weekday {
fn arbitrary(g: &mut Gen) -> Self {
use Weekday::*;
match arbitrary_between!(u8; g, 0, 6) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
_ => Sunday,
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::Monday => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}
impl Arbitrary for Month {
fn arbitrary(g: &mut Gen) -> Self {
use Month::*;
match arbitrary_between!(u8; g, 1, 12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
_ => December,
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::January => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}

93
zeroidc/vendor/time/src/rand.rs vendored Normal file
View File

@@ -0,0 +1,93 @@
//! Implementation of [`Distribution`] for various structs.
use rand::distributions::{Distribution, Standard};
use rand::Rng;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
impl Distribution<Time> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time {
Time::__from_hms_nanos_unchecked(
rng.gen_range(0..24),
rng.gen_range(0..60),
rng.gen_range(0..60),
rng.gen_range(0..1_000_000_000),
)
}
}
impl Distribution<Date> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Date {
Date::from_julian_day_unchecked(
rng.gen_range(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()),
)
}
}
impl Distribution<UtcOffset> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UtcOffset {
let seconds = rng.gen_range(-86399..=86399);
UtcOffset::__from_hms_unchecked(
(seconds / 3600) as _,
((seconds % 3600) / 60) as _,
(seconds % 60) as _,
)
}
}
impl Distribution<PrimitiveDateTime> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PrimitiveDateTime {
PrimitiveDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<OffsetDateTime> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> OffsetDateTime {
let date_time: PrimitiveDateTime = Self.sample(rng);
date_time.assume_offset(Self.sample(rng))
}
}
impl Distribution<Duration> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
Duration::nanoseconds_i128(
rng.gen_range(Duration::MIN.whole_nanoseconds()..=Duration::MAX.whole_nanoseconds()),
)
}
}
impl Distribution<Weekday> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Weekday {
use Weekday::*;
match rng.gen_range(0u8..7) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
_ => Sunday,
}
}
}
impl Distribution<Month> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Month {
use Month::*;
match rng.gen_range(1u8..=12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
_ => December,
}
}
}

318
zeroidc/vendor/time/src/serde/mod.rs vendored Normal file
View File

@@ -0,0 +1,318 @@
//! Differential formats for serde.
// This also includes the serde implementations for all types. This doesn't need to be externally
// documented, though.
// Types with guaranteed stable serde representations. Strings are avoided to allow for optimal
// representations in various binary forms.
/// Consume the next item in a sequence.
macro_rules! item {
($seq:expr, $name:literal) => {
$seq.next_element()?
.ok_or_else(|| <A::Error as serde::de::Error>::custom(concat!("expected ", $name)))
};
}
#[cfg(feature = "serde-well-known")]
pub mod rfc2822;
#[cfg(feature = "serde-well-known")]
pub mod rfc3339;
pub mod timestamp;
mod visitor;
use core::marker::PhantomData;
#[cfg(feature = "serde-human-readable")]
use serde::ser::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Generate a custom serializer and deserializer from the provided string.
///
/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can
/// be found in [the book](https://time-rs.github.io/book/api/format-description.html).
///
/// # Usage
///
/// Invoked as `serde::format_description!(mod_name, Date, "<format string>")`. This puts a
/// module named `mod_name` in the current scope that can be used to format `Date` structs. A
/// submodule (`mod_name::option`) is also generated for `Option<Date>`. Both modules are only
/// visible in the current scope.
///
/// # Examples
///
/// ```
/// # use time::OffsetDateTime;
/// # use ::serde::{Serialize, Deserialize};
/// use time::serde;
///
/// // Makes a module `mod my_format { ... }`.
/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]");
///
/// #[derive(Serialize, Deserialize)]
/// struct SerializesWithCustom {
/// #[serde(with = "my_format")]
/// dt: OffsetDateTime,
/// #[serde(with = "my_format::option")]
/// maybe_dt: Option<OffsetDateTime>,
/// }
/// #
/// # // otherwise rustdoc tests don't work because we put a module in `main()`
/// # fn main() {}
/// ```
///
/// [`format_description::parse()`]: crate::format_description::parse()
#[cfg(all(feature = "macros", feature = "serde-human-readable"))]
pub use time_macros::serde_format_description as format_description;
use self::visitor::Visitor;
#[cfg(feature = "parsing")]
use crate::format_description::{modifier, Component, FormatItem};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
// region: Date
/// The format used when serializing and deserializing a human-readable `Date`.
#[cfg(feature = "parsing")]
const DATE_FORMAT: &[FormatItem<'_>] = &[
FormatItem::Component(Component::Year(modifier::Year::default())),
FormatItem::Literal(b"-"),
FormatItem::Component(Component::Month(modifier::Month::default())),
FormatItem::Literal(b"-"),
FormatItem::Component(Component::Day(modifier::Day::default())),
];
impl Serialize for Date {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.serialize_str(&match self.format(&DATE_FORMAT) {
Ok(s) => s,
Err(_) => return Err(S::Error::custom("failed formatting `Date`")),
});
}
(self.year(), self.ordinal()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Date {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion date
// region: Duration
impl Serialize for Duration {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.collect_str(&format_args!(
"{}.{:>09}",
self.whole_seconds(),
self.subsec_nanoseconds().abs()
));
}
(self.whole_seconds(), self.subsec_nanoseconds()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Duration {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion Duration
// region: OffsetDateTime
/// The format used when serializing and deserializing a human-readable `OffsetDateTime`.
#[cfg(feature = "parsing")]
const OFFSET_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
FormatItem::Compound(DATE_FORMAT),
FormatItem::Literal(b" "),
FormatItem::Compound(TIME_FORMAT),
FormatItem::Literal(b" "),
FormatItem::Compound(UTC_OFFSET_FORMAT),
];
impl Serialize for OffsetDateTime {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.serialize_str(&match self.format(&OFFSET_DATE_TIME_FORMAT) {
Ok(s) => s,
Err(_) => return Err(S::Error::custom("failed formatting `OffsetDateTime`")),
});
}
(
self.year(),
self.ordinal(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
self.offset.whole_hours(),
self.offset.minutes_past_hour(),
self.offset.seconds_past_minute(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for OffsetDateTime {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion OffsetDateTime
// region: PrimitiveDateTime
/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`.
#[cfg(feature = "parsing")]
const PRIMITIVE_DATE_TIME_FORMAT: &[FormatItem<'_>] = &[
FormatItem::Compound(DATE_FORMAT),
FormatItem::Literal(b" "),
FormatItem::Compound(TIME_FORMAT),
];
impl Serialize for PrimitiveDateTime {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.serialize_str(&match self.format(&PRIMITIVE_DATE_TIME_FORMAT) {
Ok(s) => s,
Err(_) => return Err(<S::Error>::custom("failed formatting `PrimitiveDateTime`")),
});
}
(
self.year(),
self.ordinal(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for PrimitiveDateTime {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion PrimitiveDateTime
// region: Time
/// The format used when serializing and deserializing a human-readable `Time`.
#[cfg(feature = "parsing")]
const TIME_FORMAT: &[FormatItem<'_>] = &[
FormatItem::Component(Component::Hour(<modifier::Hour>::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Minute(<modifier::Minute>::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::Second(<modifier::Second>::default())),
FormatItem::Literal(b"."),
FormatItem::Component(Component::Subsecond(<modifier::Subsecond>::default())),
];
impl Serialize for Time {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.serialize_str(&match self.format(&TIME_FORMAT) {
Ok(s) => s,
Err(_) => return Err(S::Error::custom("failed formatting `Time`")),
});
}
(self.hour(), self.minute(), self.second(), self.nanosecond()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Time {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion Time
// region: UtcOffset
/// The format used when serializing and deserializing a human-readable `UtcOffset`.
#[cfg(feature = "parsing")]
const UTC_OFFSET_FORMAT: &[FormatItem<'_>] = &[
FormatItem::Component(Component::OffsetHour(modifier::OffsetHour::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::OffsetMinute(modifier::OffsetMinute::default())),
FormatItem::Literal(b":"),
FormatItem::Component(Component::OffsetSecond(modifier::OffsetSecond::default())),
];
impl Serialize for UtcOffset {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.serialize_str(&match self.format(&UTC_OFFSET_FORMAT) {
Ok(s) => s,
Err(_) => return Err(S::Error::custom("failed formatting `UtcOffset`")),
});
}
(
self.whole_hours(),
self.minutes_past_hour(),
self.seconds_past_minute(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for UtcOffset {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion UtcOffset
// region: Weekday
impl Serialize for Weekday {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
return self.to_string().serialize(serializer);
}
self.number_from_monday().serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Weekday {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion Weekday
// region: Month
impl Serialize for Month {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::String;
return self.to_string().serialize(serializer);
}
(*self as u8).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Month {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
}
}
// endregion Month

View File

@@ -0,0 +1,62 @@
//! Use the well-known [RFC2822 format] when serializing and deserializing an [`OffsetDateTime`].
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3
//! [with]: https://serde.rs/field-attrs.html#with
use core::marker::PhantomData;
use serde::ser::Error as _;
use serde::{Deserializer, Serialize, Serializer};
use super::Visitor;
use crate::format_description::well_known::Rfc2822;
use crate::OffsetDateTime;
/// Serialize an [`OffsetDateTime`] using the well-known RFC2822 format.
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime
.format(&Rfc2822)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its RFC2822 representation.
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
deserializer.deserialize_any(Visitor::<Rfc2822>(PhantomData))
}
/// Use the well-known [RFC2822 format] when serializing and deserializing an
/// [`Option<OffsetDateTime>`].
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3
/// [with]: https://serde.rs/field-attrs.html#with
pub mod option {
#[allow(clippy::wildcard_imports)]
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC2822 format.
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error> {
option
.map(|odt| odt.format(&Rfc2822))
.transpose()
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC2822 representation.
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer.deserialize_option(Visitor::<Option<Rfc2822>>(PhantomData))
}
}

View File

@@ -0,0 +1,62 @@
//! Use the well-known [RFC3339 format] when serializing and deserializing an [`OffsetDateTime`].
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6
//! [with]: https://serde.rs/field-attrs.html#with
use core::marker::PhantomData;
use serde::ser::Error as _;
use serde::{Deserializer, Serialize, Serializer};
use super::Visitor;
use crate::format_description::well_known::Rfc3339;
use crate::OffsetDateTime;
/// Serialize an [`OffsetDateTime`] using the well-known RFC3339 format.
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime
.format(&Rfc3339)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its RFC3339 representation.
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
deserializer.deserialize_any(Visitor::<Rfc3339>(PhantomData))
}
/// Use the well-known [RFC3339 format] when serializing and deserializing an
/// [`Option<OffsetDateTime>`].
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6
/// [with]: https://serde.rs/field-attrs.html#with
pub mod option {
#[allow(clippy::wildcard_imports)]
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC3339 format.
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error> {
option
.map(|odt| odt.format(&Rfc3339))
.transpose()
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC3339 representation.
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer.deserialize_option(Visitor::<Option<Rfc3339>>(PhantomData))
}
}

View File

@@ -0,0 +1,60 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
/// Serialize an `OffsetDateTime` as its Unix timestamp
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime.unix_timestamp().serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?)
.map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] for the purposes of
/// serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
pub mod option {
#[allow(clippy::wildcard_imports)]
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error> {
option
.map(OffsetDateTime::unix_timestamp)
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
Option::deserialize(deserializer)?
.map(OffsetDateTime::from_unix_timestamp)
.transpose()
.map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err))
}
}

329
zeroidc/vendor/time/src/serde/visitor.rs vendored Normal file
View File

@@ -0,0 +1,329 @@
//! Serde visitor for various types.
use core::fmt;
use core::marker::PhantomData;
use serde::de;
#[cfg(feature = "serde-well-known")]
use serde::Deserializer;
#[cfg(feature = "parsing")]
use super::{
DATE_FORMAT, OFFSET_DATE_TIME_FORMAT, PRIMITIVE_DATE_TIME_FORMAT, TIME_FORMAT,
UTC_OFFSET_FORMAT,
};
use crate::error::ComponentRange;
#[cfg(feature = "serde-well-known")]
use crate::format_description::well_known;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// A serde visitor for various types.
pub(super) struct Visitor<T: ?Sized>(pub(super) PhantomData<T>);
impl<'a> de::Visitor<'a> for Visitor<Date> {
type Value = Date;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Date`")
}
#[cfg(feature = "parsing")]
fn visit_str<E: de::Error>(self, value: &str) -> Result<Date, E> {
Date::parse(value, &DATE_FORMAT).map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Date, A::Error> {
let year = item!(seq, "year")?;
let ordinal = item!(seq, "day of year")?;
Date::from_ordinal_date(year, ordinal).map_err(ComponentRange::into_de_error)
}
}
impl<'a> de::Visitor<'a> for Visitor<Duration> {
type Value = Duration;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Duration`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Duration, E> {
let (seconds, nanoseconds) = value.split_once('.').ok_or_else(|| {
de::Error::invalid_value(de::Unexpected::Str(value), &"a decimal point")
})?;
let seconds = seconds
.parse()
.map_err(|_| de::Error::invalid_value(de::Unexpected::Str(seconds), &"seconds"))?;
let mut nanoseconds = nanoseconds.parse().map_err(|_| {
de::Error::invalid_value(de::Unexpected::Str(nanoseconds), &"nanoseconds")
})?;
if seconds < 0 {
nanoseconds *= -1;
}
Ok(Duration::new(seconds, nanoseconds))
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Duration, A::Error> {
let seconds = item!(seq, "seconds")?;
let nanoseconds = item!(seq, "nanoseconds")?;
Ok(Duration::new(seconds, nanoseconds))
}
}
impl<'a> de::Visitor<'a> for Visitor<OffsetDateTime> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an `OffsetDateTime`")
}
#[cfg(feature = "parsing")]
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &OFFSET_DATE_TIME_FORMAT).map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<OffsetDateTime, A::Error> {
let year = item!(seq, "year")?;
let ordinal = item!(seq, "day of year")?;
let hour = item!(seq, "hour")?;
let minute = item!(seq, "minute")?;
let second = item!(seq, "second")?;
let nanosecond = item!(seq, "nanosecond")?;
let offset_hours = item!(seq, "offset hours")?;
let offset_minutes = item!(seq, "offset minutes")?;
let offset_seconds = item!(seq, "offset seconds")?;
Date::from_ordinal_date(year, ordinal)
.and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond))
.and_then(|datetime| {
UtcOffset::from_hms(offset_hours, offset_minutes, offset_seconds)
.map(|offset| datetime.assume_offset(offset))
})
.map_err(ComponentRange::into_de_error)
}
}
impl<'a> de::Visitor<'a> for Visitor<PrimitiveDateTime> {
type Value = PrimitiveDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `PrimitiveDateTime`")
}
#[cfg(feature = "parsing")]
fn visit_str<E: de::Error>(self, value: &str) -> Result<PrimitiveDateTime, E> {
PrimitiveDateTime::parse(value, &PRIMITIVE_DATE_TIME_FORMAT).map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<PrimitiveDateTime, A::Error> {
let year = item!(seq, "year")?;
let ordinal = item!(seq, "day of year")?;
let hour = item!(seq, "hour")?;
let minute = item!(seq, "minute")?;
let second = item!(seq, "second")?;
let nanosecond = item!(seq, "nanosecond")?;
Date::from_ordinal_date(year, ordinal)
.and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond))
.map_err(ComponentRange::into_de_error)
}
}
impl<'a> de::Visitor<'a> for Visitor<Time> {
type Value = Time;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Time`")
}
#[cfg(feature = "parsing")]
fn visit_str<E: de::Error>(self, value: &str) -> Result<Time, E> {
Time::parse(value, &TIME_FORMAT).map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Time, A::Error> {
let hour = item!(seq, "hour")?;
let minute = item!(seq, "minute")?;
let second = item!(seq, "second")?;
let nanosecond = item!(seq, "nanosecond")?;
Time::from_hms_nano(hour, minute, second, nanosecond).map_err(ComponentRange::into_de_error)
}
}
impl<'a> de::Visitor<'a> for Visitor<UtcOffset> {
type Value = UtcOffset;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `UtcOffset`")
}
#[cfg(feature = "parsing")]
fn visit_str<E: de::Error>(self, value: &str) -> Result<UtcOffset, E> {
UtcOffset::parse(value, &UTC_OFFSET_FORMAT).map_err(E::custom)
}
fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<UtcOffset, A::Error> {
let hours = item!(seq, "offset hours")?;
let minutes = item!(seq, "offset minutes")?;
let seconds = item!(seq, "offset seconds")?;
UtcOffset::from_hms(hours, minutes, seconds).map_err(ComponentRange::into_de_error)
}
}
impl<'a> de::Visitor<'a> for Visitor<Weekday> {
type Value = Weekday;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Weekday`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Weekday, E> {
match value {
"Monday" => Ok(Weekday::Monday),
"Tuesday" => Ok(Weekday::Tuesday),
"Wednesday" => Ok(Weekday::Wednesday),
"Thursday" => Ok(Weekday::Thursday),
"Friday" => Ok(Weekday::Friday),
"Saturday" => Ok(Weekday::Saturday),
"Sunday" => Ok(Weekday::Sunday),
_ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Weekday`")),
}
}
fn visit_u8<E: de::Error>(self, value: u8) -> Result<Weekday, E> {
match value {
1 => Ok(Weekday::Monday),
2 => Ok(Weekday::Tuesday),
3 => Ok(Weekday::Wednesday),
4 => Ok(Weekday::Thursday),
5 => Ok(Weekday::Friday),
6 => Ok(Weekday::Saturday),
7 => Ok(Weekday::Sunday),
_ => Err(E::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"a value in the range 1..=7",
)),
}
}
}
impl<'a> de::Visitor<'a> for Visitor<Month> {
type Value = Month;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a `Month`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Month, E> {
match value {
"January" => Ok(Month::January),
"February" => Ok(Month::February),
"March" => Ok(Month::March),
"April" => Ok(Month::April),
"May" => Ok(Month::May),
"June" => Ok(Month::June),
"July" => Ok(Month::July),
"August" => Ok(Month::August),
"September" => Ok(Month::September),
"October" => Ok(Month::October),
"November" => Ok(Month::November),
"December" => Ok(Month::December),
_ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Month`")),
}
}
fn visit_u8<E: de::Error>(self, value: u8) -> Result<Month, E> {
match value {
1 => Ok(Month::January),
2 => Ok(Month::February),
3 => Ok(Month::March),
4 => Ok(Month::April),
5 => Ok(Month::May),
6 => Ok(Month::June),
7 => Ok(Month::July),
8 => Ok(Month::August),
9 => Ok(Month::September),
10 => Ok(Month::October),
11 => Ok(Month::November),
12 => Ok(Month::December),
_ => Err(E::invalid_value(
de::Unexpected::Unsigned(value.into()),
&"a value in the range 1..=12",
)),
}
}
}
#[cfg(feature = "serde-well-known")]
impl<'a> de::Visitor<'a> for Visitor<well_known::Rfc2822> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an RFC2822-formatted `OffsetDateTime`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &well_known::Rfc2822).map_err(E::custom)
}
}
#[cfg(feature = "serde-well-known")]
impl<'a> de::Visitor<'a> for Visitor<Option<well_known::Rfc2822>> {
type Value = Option<OffsetDateTime>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an RFC2822-formatted `Option<OffsetDateTime>`")
}
fn visit_some<D: Deserializer<'a>>(
self,
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer
.deserialize_any(Visitor::<well_known::Rfc2822>(PhantomData))
.map(Some)
}
fn visit_none<E: de::Error>(self) -> Result<Option<OffsetDateTime>, E> {
Ok(None)
}
}
#[cfg(feature = "serde-well-known")]
impl<'a> de::Visitor<'a> for Visitor<well_known::Rfc3339> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an RFC3339-formatted `OffsetDateTime`")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &well_known::Rfc3339).map_err(E::custom)
}
}
#[cfg(feature = "serde-well-known")]
impl<'a> de::Visitor<'a> for Visitor<Option<well_known::Rfc3339>> {
type Value = Option<OffsetDateTime>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an RFC3339-formatted `Option<OffsetDateTime>`")
}
fn visit_some<D: Deserializer<'a>>(
self,
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer
.deserialize_any(Visitor::<well_known::Rfc3339>(PhantomData))
.map(Some)
}
fn visit_none<E: de::Error>(self) -> Result<Option<OffsetDateTime>, E> {
Ok(None)
}
}

View File

@@ -0,0 +1,7 @@
//! A fallback for any OS not covered.
use crate::{OffsetDateTime, UtcOffset};
pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> {
None
}

View File

@@ -0,0 +1,13 @@
//! A method to obtain the local offset from UTC.
#[cfg_attr(target_family = "windows", path = "windows.rs")]
#[cfg_attr(target_family = "unix", path = "unix.rs")]
mod imp;
use crate::{OffsetDateTime, UtcOffset};
/// Attempt to obtain the system's UTC offset. If the offset cannot be determined, `None` is
/// returned.
pub(crate) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
imp::local_offset_at(datetime)
}

View File

@@ -0,0 +1,126 @@
//! Get the system's UTC offset on Unix.
use core::convert::TryInto;
use core::mem::MaybeUninit;
use crate::{OffsetDateTime, UtcOffset};
/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error.
///
/// # Safety
///
/// This method must only be called when the process is single-threaded.
///
/// This method will remain `unsafe` until `std::env::set_var` is deprecated or has its behavior
/// altered. This method is, on its own, safe. It is the presence of a safe, unsound way to set
/// environment variables that makes it unsafe.
unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
extern "C" {
#[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
fn tzset();
}
// The exact type of `timestamp` beforehand can vary, so this conversion is necessary.
#[allow(clippy::useless_conversion)]
let timestamp = timestamp.try_into().ok()?;
let mut tm = MaybeUninit::uninit();
// Update timezone information from system. `localtime_r` does not do this for us.
//
// Safety: tzset is thread-safe.
unsafe { tzset() };
// Safety: We are calling a system API, which mutates the `tm` variable. If a null
// pointer is returned, an error occurred.
let tm_ptr = unsafe { libc::localtime_r(&timestamp, tm.as_mut_ptr()) };
if tm_ptr.is_null() {
None
} else {
// Safety: The value was initialized, as we no longer have a null pointer.
Some(unsafe { tm.assume_init() })
}
}
/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// `tm_gmtoff` extension
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
UtcOffset::from_hms(
(seconds / 3_600) as _,
((seconds / 60) % 60) as _,
(seconds % 60) as _,
)
.ok()
}
/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// Solaris/Illumos is unsound and requires opting into.
#[cfg(all(
not(unsound_local_offset),
any(target_os = "solaris", target_os = "illumos")
))]
#[allow(unused_variables, clippy::missing_const_for_fn)]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
None
}
/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
#[cfg(all(
unsound_local_offset,
any(target_os = "solaris", target_os = "illumos")
))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
use core::convert::TryFrom;
use crate::Date;
let mut tm = tm;
if tm.tm_sec == 60 {
// Leap seconds are not currently supported.
tm.tm_sec = 59;
}
let local_timestamp =
Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
.ok()?
.with_hms(
tm.tm_hour.try_into().ok()?,
tm.tm_min.try_into().ok()?,
tm.tm_sec.try_into().ok()?,
)
.ok()?
.assume_utc()
.unix_timestamp();
let diff_secs: i32 = (local_timestamp - datetime.unix_timestamp())
.try_into()
.ok()?;
UtcOffset::from_hms(
(diff_secs / 3_600) as _,
((diff_secs / 60) % 60) as _,
(diff_secs % 60) as _,
)
.ok()
}
/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
// Ensure that the process is single-threaded unless the user has explicitly opted out of this
// check. This is to prevent issues with the environment being mutated by a different thread in
// the process while execution of this function is taking place, which can cause a segmentation
// fault by dereferencing a dangling pointer.
// If the `num_threads` crate is incapable of determining the number of running threads, then
// we conservatively return `None` to avoid a soundness bug.
if !cfg!(unsound_local_offset) && num_threads::is_single_threaded() != Some(true) {
return None;
}
// Safety: We have just confirmed that the process is single-threaded or the user has explicitly
// opted out of soundness.
let tm = unsafe { timestamp_to_tm(datetime.unix_timestamp()) }?;
tm_to_offset(tm)
}

View File

@@ -0,0 +1,115 @@
//! Get the system's UTC offset on Windows.
use core::convert::TryInto;
use core::mem::MaybeUninit;
use crate::{OffsetDateTime, UtcOffset};
// ffi: WINAPI FILETIME struct
#[repr(C)]
#[allow(non_snake_case)]
struct FileTime {
dwLowDateTime: u32,
dwHighDateTime: u32,
}
// ffi: WINAPI SYSTEMTIME struct
#[repr(C)]
#[allow(non_snake_case)]
struct SystemTime {
wYear: u16,
wMonth: u16,
wDayOfWeek: u16,
wDay: u16,
wHour: u16,
wMinute: u16,
wSecond: u16,
wMilliseconds: u16,
}
#[link(name = "kernel32")]
extern "system" {
// https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime
fn SystemTimeToFileTime(lpSystemTime: *const SystemTime, lpFileTime: *mut FileTime) -> i32;
// https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime
fn SystemTimeToTzSpecificLocalTime(
lpTimeZoneInformation: *const std::ffi::c_void, // We only pass `nullptr` here
lpUniversalTime: *const SystemTime,
lpLocalTime: *mut SystemTime,
) -> i32;
}
/// Convert a `SYSTEMTIME` to a `FILETIME`. Returns `None` if any error occurred.
fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> {
let mut ft = MaybeUninit::uninit();
// Safety: `SystemTimeToFileTime` is thread-safe.
if 0 == unsafe { SystemTimeToFileTime(systime, ft.as_mut_ptr()) } {
// failed
None
} else {
// Safety: The call succeeded.
Some(unsafe { ft.assume_init() })
}
}
/// Convert a `FILETIME` to an `i64`, representing a number of seconds.
fn filetime_to_secs(filetime: &FileTime) -> i64 {
/// FILETIME represents 100-nanosecond intervals
const FT_TO_SECS: i64 = 10_000_000;
((filetime.dwHighDateTime as i64) << 32 | filetime.dwLowDateTime as i64) / FT_TO_SECS
}
/// Convert an [`OffsetDateTime`] to a `SYSTEMTIME`.
fn offset_to_systemtime(datetime: OffsetDateTime) -> SystemTime {
let (_, month, day_of_month) = datetime.to_offset(UtcOffset::UTC).date().to_calendar_date();
SystemTime {
wYear: datetime.year() as _,
wMonth: month as _,
wDay: day_of_month as _,
wDayOfWeek: 0, // ignored
wHour: datetime.hour() as _,
wMinute: datetime.minute() as _,
wSecond: datetime.second() as _,
wMilliseconds: datetime.millisecond(),
}
}
/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
// This function falls back to UTC if any system call fails.
let systime_utc = offset_to_systemtime(datetime.to_offset(UtcOffset::UTC));
// Safety: `local_time` is only read if it is properly initialized, and
// `SystemTimeToTzSpecificLocalTime` is thread-safe.
let systime_local = unsafe {
let mut local_time = MaybeUninit::uninit();
if 0 == SystemTimeToTzSpecificLocalTime(
core::ptr::null(), // use system's current timezone
&systime_utc,
local_time.as_mut_ptr(),
) {
// call failed
return None;
} else {
local_time.assume_init()
}
};
// Convert SYSTEMTIMEs to FILETIMEs so we can perform arithmetic on them.
let ft_system = systemtime_to_filetime(&systime_utc)?;
let ft_local = systemtime_to_filetime(&systime_local)?;
let diff_secs: i32 = (filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system))
.try_into()
.ok()?;
UtcOffset::from_hms(
(diff_secs / 3_600) as _,
((diff_secs / 60) % 60) as _,
(diff_secs % 60) as _,
)
.ok()
}

9
zeroidc/vendor/time/src/sys/mod.rs vendored Normal file
View File

@@ -0,0 +1,9 @@
//! Functions with a common interface that rely on system calls.
#![allow(unsafe_code)] // We're interfacing with system calls.
#[cfg(feature = "local-offset")]
mod local_offset_at;
#[cfg(feature = "local-offset")]
pub(crate) use local_offset_at::local_offset_at;

752
zeroidc/vendor/time/src/time.rs vendored Normal file
View File

@@ -0,0 +1,752 @@
//! The [`Time`] 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::DateAdjustment;
use crate::{error, Duration};
/// By explicitly inserting this enum where padding is expected, the compiler is able to better
/// perform niche value optimization.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum Padding {
#[allow(clippy::missing_docs_in_private_items)]
Optimize,
}
/// The clock time within a given date. Nanosecond precision.
///
/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds
/// (either positive or negative).
///
/// When comparing two `Time`s, they are assumed to be in the same calendar date.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Time {
#[allow(clippy::missing_docs_in_private_items)]
hour: u8,
#[allow(clippy::missing_docs_in_private_items)]
minute: u8,
#[allow(clippy::missing_docs_in_private_items)]
second: u8,
#[allow(clippy::missing_docs_in_private_items)]
nanosecond: u32,
#[allow(clippy::missing_docs_in_private_items)]
padding: Padding,
}
impl fmt::Debug for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Time")
.field("hour", &self.hour)
.field("minute", &self.minute)
.field("second", &self.second)
.field("nanosecond", &self.nanosecond)
.finish()
}
}
impl Time {
/// Create a `Time` that is exactly midnight.
///
/// ```rust
/// # use time::{Time, macros::time};
/// assert_eq!(Time::MIDNIGHT, time!(0:00));
/// ```
pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
/// The smallest value that can be represented by `Time`.
///
/// `00:00:00.0`
pub(crate) const MIN: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
/// The largest value that can be represented by `Time`.
///
/// `23:59:59.999_999_999`
pub(crate) const MAX: Self = Self::__from_hms_nanos_unchecked(23, 59, 59, 999_999_999);
// region: constructors
/// Create a `Time` from its components.
#[doc(hidden)]
pub const fn __from_hms_nanos_unchecked(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Self {
Self {
hour,
minute,
second,
nanosecond,
padding: Padding::Optimize,
}
}
/// Attempt to create a `Time` from the hour, minute, and second.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms(1, 2, 3).is_ok());
/// ```
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour.
/// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second.
/// ```
pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hour in 0 => 23);
ensure_value_in_range!(minute in 0 => 59);
ensure_value_in_range!(second in 0 => 59);
Ok(Self::__from_hms_nanos_unchecked(hour, minute, second, 0))
}
/// Attempt to create a `Time` from the hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok());
/// ```
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_milli(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
/// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond.
/// ```
pub const fn from_hms_milli(
hour: u8,
minute: u8,
second: u8,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hour in 0 => 23);
ensure_value_in_range!(minute in 0 => 59);
ensure_value_in_range!(second in 0 => 59);
ensure_value_in_range!(millisecond in 0 => 999);
Ok(Self::__from_hms_nanos_unchecked(
hour,
minute,
second,
millisecond as u32 * 1_000_000,
))
}
/// Attempt to create a `Time` from the hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok());
/// ```
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_micro(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
/// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond.
/// ```
pub const fn from_hms_micro(
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hour in 0 => 23);
ensure_value_in_range!(minute in 0 => 59);
ensure_value_in_range!(second in 0 => 59);
ensure_value_in_range!(microsecond in 0 => 999_999);
Ok(Self::__from_hms_nanos_unchecked(
hour,
minute,
second,
microsecond * 1_000,
))
}
/// Attempt to create a `Time` from the hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok());
/// ```
///
/// ```rust
/// # use time::Time;
/// assert!(Time::from_hms_nano(24, 0, 0, 0).is_err()); // 24 isn't a valid hour.
/// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute.
/// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second.
/// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond.
/// ```
pub const fn from_hms_nano(
hour: u8,
minute: u8,
second: u8,
nanosecond: u32,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hour in 0 => 23);
ensure_value_in_range!(minute in 0 => 59);
ensure_value_in_range!(second in 0 => 59);
ensure_value_in_range!(nanosecond in 0 => 999_999_999);
Ok(Self::__from_hms_nanos_unchecked(
hour, minute, second, nanosecond,
))
}
// endregion constructors
// region: getters
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0));
/// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59));
/// ```
pub const fn as_hms(self) -> (u8, u8, u8) {
(self.hour, self.minute, self.second)
}
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0));
/// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999));
/// ```
pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) {
(
self.hour,
self.minute,
self.second,
(self.nanosecond / 1_000_000) as u16,
)
}
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0));
/// assert_eq!(
/// time!(23:59:59.999_999).as_hms_micro(),
/// (23, 59, 59, 999_999)
/// );
/// ```
pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) {
(self.hour, self.minute, self.second, self.nanosecond / 1_000)
}
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0));
/// assert_eq!(
/// time!(23:59:59.999_999_999).as_hms_nano(),
/// (23, 59, 59, 999_999_999)
/// );
/// ```
pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) {
(self.hour, self.minute, self.second, self.nanosecond)
}
/// Get the clock hour.
///
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).hour(), 0);
/// assert_eq!(time!(23:59:59).hour(), 23);
/// ```
pub const fn hour(self) -> u8 {
self.hour
}
/// Get the minute within the hour.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).minute(), 0);
/// assert_eq!(time!(23:59:59).minute(), 59);
/// ```
pub const fn minute(self) -> u8 {
self.minute
}
/// Get the second within the minute.
///
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00:00).second(), 0);
/// assert_eq!(time!(23:59:59).second(), 59);
/// ```
pub const fn second(self) -> u8 {
self.second
}
/// Get the milliseconds within the second.
///
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00).millisecond(), 0);
/// assert_eq!(time!(23:59:59.999).millisecond(), 999);
/// ```
pub const fn millisecond(self) -> u16 {
(self.nanosecond / 1_000_000) as _
}
/// Get the microseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00).microsecond(), 0);
/// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999);
/// ```
pub const fn microsecond(self) -> u32 {
self.nanosecond / 1_000
}
/// Get the nanoseconds within the second.
///
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(time!(0:00).nanosecond(), 0);
/// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999);
/// ```
pub const fn nanosecond(self) -> u32 {
self.nanosecond
}
// endregion getters
// region: arithmetic helpers
/// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether
/// the date is different.
pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) {
let mut nanoseconds = self.nanosecond as i32 + duration.subsec_nanoseconds();
let mut seconds = self.second as i8 + (duration.whole_seconds() % 60) as i8;
let mut minutes = self.minute as i8 + (duration.whole_minutes() % 60) as i8;
let mut hours = self.hour as i8 + (duration.whole_hours() % 24) as i8;
let mut date_adjustment = DateAdjustment::None;
cascade!(nanoseconds in 0..1_000_000_000 => seconds);
cascade!(seconds in 0..60 => minutes);
cascade!(minutes in 0..60 => hours);
if hours >= 24 {
hours -= 24;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
hours += 24;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
Self::__from_hms_nanos_unchecked(
hours as _,
minutes as _,
seconds as _,
nanoseconds as _,
),
)
}
/// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning
/// whether the date is different.
pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) {
let mut nanoseconds = self.nanosecond as i32 - duration.subsec_nanoseconds();
let mut seconds = self.second as i8 - (duration.whole_seconds() % 60) as i8;
let mut minutes = self.minute as i8 - (duration.whole_minutes() % 60) as i8;
let mut hours = self.hour as i8 - (duration.whole_hours() % 24) as i8;
let mut date_adjustment = DateAdjustment::None;
cascade!(nanoseconds in 0..1_000_000_000 => seconds);
cascade!(seconds in 0..60 => minutes);
cascade!(minutes in 0..60 => hours);
if hours >= 24 {
hours -= 24;
date_adjustment = DateAdjustment::Next;
} else if hours < 0 {
hours += 24;
date_adjustment = DateAdjustment::Previous;
}
(
date_adjustment,
Self::__from_hms_nanos_unchecked(
hours as _,
minutes as _,
seconds as _,
nanoseconds as _,
),
)
}
/// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) {
let mut nanosecond = self.nanosecond + duration.subsec_nanos();
let mut second = self.second + (duration.as_secs() % 60) as u8;
let mut minute = self.minute + ((duration.as_secs() / 60) % 60) as u8;
let mut hour = self.hour + ((duration.as_secs() / 3_600) % 24) as u8;
let mut is_next_day = false;
cascade!(nanosecond in 0..1_000_000_000 => second);
cascade!(second in 0..60 => minute);
cascade!(minute in 0..60 => hour);
if hour >= 24 {
hour -= 24;
is_next_day = true;
}
(
is_next_day,
Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond),
)
}
/// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow,
/// returning whether the date is the previous date as the first element of the tuple.
pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) {
let mut nanosecond = self.nanosecond as i32 - duration.subsec_nanos() as i32;
let mut second = self.second as i8 - (duration.as_secs() % 60) as i8;
let mut minute = self.minute as i8 - ((duration.as_secs() / 60) % 60) as i8;
let mut hour = self.hour as i8 - ((duration.as_secs() / 3_600) % 24) as i8;
let mut is_previous_day = false;
cascade!(nanosecond in 0..1_000_000_000 => second);
cascade!(second in 0..60 => minute);
cascade!(minute in 0..60 => hour);
if hour < 0 {
hour += 24;
is_previous_day = true;
}
(
is_previous_day,
Self::__from_hms_nanos_unchecked(hour as _, minute as _, second as _, nanosecond as _),
)
}
// endregion arithmetic helpers
// region: replacement
/// Replace the clock hour.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_hour(7),
/// Ok(time!(07:02:03.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hour in 0 => 23);
Ok(Self::__from_hms_nanos_unchecked(
hour,
self.minute,
self.second,
self.nanosecond,
))
}
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_minute(7),
/// Ok(time!(01:07:03.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(minute in 0 => 59);
Ok(Self::__from_hms_nanos_unchecked(
self.hour,
minute,
self.second,
self.nanosecond,
))
}
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_second(7),
/// Ok(time!(01:02:07.004_005_006))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(second in 0 => 59);
Ok(Self::__from_hms_nanos_unchecked(
self.hour,
self.minute,
second,
self.nanosecond,
))
}
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_millisecond(7),
/// Ok(time!(01:02:03.007))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_millisecond(
self,
millisecond: u16,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(millisecond in 0 => 999);
Ok(Self::__from_hms_nanos_unchecked(
self.hour,
self.minute,
self.second,
millisecond as u32 * 1_000_000,
))
}
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_microsecond(7_008),
/// Ok(time!(01:02:03.007_008))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_microsecond(
self,
microsecond: u32,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(microsecond in 0 => 999_999);
Ok(Self::__from_hms_nanos_unchecked(
self.hour,
self.minute,
self.second,
microsecond * 1000,
))
}
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_nanosecond(7_008_009),
/// Ok(time!(01:02:03.007_008_009))
/// );
/// assert!(time!(01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond
/// ```
#[must_use = "This method does not mutate the original `Time`."]
pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(nanosecond in 0 => 999_999_999);
Ok(Self::__from_hms_nanos_unchecked(
self.hour,
self.minute,
self.second,
nanosecond,
))
}
// endregion replacement
}
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl Time {
/// Format the `Time` using the provided [format description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, crate::error::Format> {
format.format_into(output, None, Some(self), None)
}
/// Format the `Time` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::time};
/// let format = format_description::parse("[hour]:[minute]:[second]")?;
/// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(
self,
format: &(impl Formattable + ?Sized),
) -> Result<String, crate::error::Format> {
format.format(None, Some(self), None)
}
}
#[cfg(feature = "parsing")]
impl Time {
/// Parse a `Time` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::time, Time};
/// let format = format_description::parse("[hour]:[minute]:[second]")?;
/// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00));
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_time(input.as_bytes())
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (value, width) = match self.nanosecond() {
nanos if nanos % 10 != 0 => (nanos, 9),
nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8),
nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7),
nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6),
nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5),
nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4),
nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3),
nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2),
nanos => (nanos / 100_000_000, 1),
};
write!(
f,
"{}:{:02}:{:02}.{:0width$}",
self.hour,
self.minute,
self.second,
value,
width = width
)
}
}
// endregion formatting & parsing
// region: trait impls
impl Add<Duration> for Time {
type Output = Self;
/// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// assert_eq!(time!(12:00) + 2.hours(), time!(14:00));
/// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59));
/// ```
fn add(self, duration: Duration) -> Self::Output {
self.adjusting_add(duration).1
}
}
impl Add<StdDuration> for Time {
type Output = Self;
/// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalStdDuration, macros::time};
/// assert_eq!(time!(12:00) + 2.std_hours(), time!(14:00));
/// assert_eq!(time!(23:59:59) + 2.std_seconds(), time!(0:00:01));
/// ```
fn add(self, duration: StdDuration) -> Self::Output {
self.adjusting_add_std(duration).1
}
}
impl_add_assign!(Time: Duration, StdDuration);
impl Sub<Duration> for Time {
type Output = Self;
/// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// assert_eq!(time!(14:00) - 2.hours(), time!(12:00));
/// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01));
/// ```
fn sub(self, duration: Duration) -> Self::Output {
self.adjusting_sub(duration).1
}
}
impl Sub<StdDuration> for Time {
type Output = Self;
/// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalStdDuration, macros::time};
/// assert_eq!(time!(14:00) - 2.std_hours(), time!(12:00));
/// assert_eq!(time!(0:00:01) - 2.std_seconds(), time!(23:59:59));
/// ```
fn sub(self, duration: StdDuration) -> Self::Output {
self.adjusting_sub_std(duration).1
}
}
impl_sub_assign!(Time: Duration, StdDuration);
impl Sub for Time {
type Output = Duration;
/// Subtract two `Time`s, returning the [`Duration`] between. This assumes both `Time`s are in
/// the same calendar day.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// assert_eq!(time!(0:00) - time!(0:00), 0.seconds());
/// assert_eq!(time!(1:00) - time!(0:00), 1.hours());
/// assert_eq!(time!(0:00) - time!(1:00), (-1).hours());
/// assert_eq!(time!(0:00) - time!(23:00), (-23).hours());
/// ```
fn sub(self, rhs: Self) -> Self::Output {
let hour_diff = (self.hour as i8) - (rhs.hour as i8);
let minute_diff = (self.minute as i8) - (rhs.minute as i8);
let mut second_diff = (self.second as i8) - (rhs.second as i8);
let mut nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32);
cascade!(nanosecond_diff in 0..1_000_000_000 => second_diff);
Duration::new_unchecked(
hour_diff as i64 * 3_600 + minute_diff as i64 * 60 + second_diff as i64,
nanosecond_diff,
)
}
}
// endregion trait impls

321
zeroidc/vendor/time/src/utc_offset.rs vendored Normal file
View File

@@ -0,0 +1,321 @@
//! The [`UtcOffset`] struct and its associated `impl`s.
use core::fmt;
use core::ops::Neg;
#[cfg(feature = "formatting")]
use std::io;
use crate::error;
#[cfg(feature = "formatting")]
use crate::formatting::Formattable;
#[cfg(feature = "parsing")]
use crate::parsing::Parsable;
#[cfg(feature = "local-offset")]
use crate::sys::local_offset_at;
#[cfg(feature = "local-offset")]
use crate::OffsetDateTime;
/// An offset from UTC.
///
/// This struct can store values up to ±23:59:59. If you need support outside this range, please
/// file an issue with your use case.
// All three components _must_ have the same sign.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UtcOffset {
#[allow(clippy::missing_docs_in_private_items)]
hours: i8,
#[allow(clippy::missing_docs_in_private_items)]
minutes: i8,
#[allow(clippy::missing_docs_in_private_items)]
seconds: i8,
}
impl UtcOffset {
/// A `UtcOffset` that is UTC.
///
/// ```rust
/// # use time::{UtcOffset, macros::offset};
/// assert_eq!(UtcOffset::UTC, offset!(UTC));
/// ```
pub const UTC: Self = Self::__from_hms_unchecked(0, 0, 0);
// region: constructors
/// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the
/// validity of which must be guaranteed by the caller. All three parameters must have the same
/// sign.
#[doc(hidden)]
pub const fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
Self {
hours,
minutes,
seconds,
}
}
/// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds
/// provided.
///
/// The sign of all three components should match. If they do not, all smaller components will
/// have their signs flipped.
///
/// ```rust
/// # use time::UtcOffset;
/// assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3));
/// assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3));
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_hms(
hours: i8,
mut minutes: i8,
mut seconds: i8,
) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(hours in -23 => 23);
ensure_value_in_range!(minutes in -59 => 59);
ensure_value_in_range!(seconds in -59 => 59);
if (hours > 0 && minutes < 0) || (hours < 0 && minutes > 0) {
minutes *= -1;
}
if (hours > 0 && seconds < 0)
|| (hours < 0 && seconds > 0)
|| (minutes > 0 && seconds < 0)
|| (minutes < 0 && seconds > 0)
{
seconds *= -1;
}
Ok(Self::__from_hms_unchecked(hours, minutes, seconds))
}
/// Create a `UtcOffset` representing an offset by the number of seconds provided.
///
/// ```rust
/// # use time::UtcOffset;
/// assert_eq!(UtcOffset::from_whole_seconds(3_723)?.as_hms(), (1, 2, 3));
/// # Ok::<_, time::Error>(())
/// ```
pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> {
ensure_value_in_range!(seconds in -86_399 => 86_399);
Ok(Self::__from_hms_unchecked(
(seconds / 3_600) as _,
((seconds / 60) % 60) as _,
(seconds % 60) as _,
))
}
// endregion constructors
// region: getters
/// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components
/// will always match. A positive value indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).as_hms(), (1, 2, 3));
/// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3));
/// ```
pub const fn as_hms(self) -> (i8, i8, i8) {
(self.hours, self.minutes, self.seconds)
}
/// Obtain the number of whole hours the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_hours(), 1);
/// assert_eq!(offset!(-1:02:03).whole_hours(), -1);
/// ```
pub const fn whole_hours(self) -> i8 {
self.hours
}
/// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
/// assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
/// ```
pub const fn whole_minutes(self) -> i16 {
self.hours as i16 * 60 + self.minutes as i16
}
/// Obtain the number of minutes past the hour the offset is from UTC. A positive value
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
/// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
/// ```
pub const fn minutes_past_hour(self) -> i8 {
self.minutes
}
/// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_seconds(), 3723);
/// assert_eq!(offset!(-1:02:03).whole_seconds(), -3723);
/// ```
// This may be useful for anyone manually implementing arithmetic, as it
// would let them construct a `Duration` directly.
pub const fn whole_seconds(self) -> i32 {
self.hours as i32 * 3_600 + self.minutes as i32 * 60 + self.seconds as i32
}
/// Obtain the number of seconds past the minute the offset is from UTC. A positive value
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
/// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
/// ```
pub const fn seconds_past_minute(self) -> i8 {
self.seconds
}
// endregion getters
// region: is_{sign}
/// Check if the offset is exactly UTC.
///
///
/// ```rust
/// # use time::macros::offset;
/// assert!(!offset!(+1:02:03).is_utc());
/// assert!(!offset!(-1:02:03).is_utc());
/// assert!(offset!(UTC).is_utc());
/// ```
pub const fn is_utc(self) -> bool {
self.hours == 0 && self.minutes == 0 && self.seconds == 0
}
/// Check if the offset is positive, or east of UTC.
///
/// ```rust
/// # use time::macros::offset;
/// assert!(offset!(+1:02:03).is_positive());
/// assert!(!offset!(-1:02:03).is_positive());
/// assert!(!offset!(UTC).is_positive());
/// ```
pub const fn is_positive(self) -> bool {
self.hours > 0 || self.minutes > 0 || self.seconds > 0
}
/// Check if the offset is negative, or west of UTC.
///
/// ```rust
/// # use time::macros::offset;
/// assert!(!offset!(+1:02:03).is_negative());
/// assert!(offset!(-1:02:03).is_negative());
/// assert!(!offset!(UTC).is_negative());
/// ```
pub const fn is_negative(self) -> bool {
self.hours < 0 || self.minutes < 0 || self.seconds < 0
}
// endregion is_{sign}
// region: local offset
/// Attempt to obtain the system's UTC offset at a known moment in time. If the offset cannot be
/// determined, an error is returned.
///
/// ```rust
/// # use time::{UtcOffset, OffsetDateTime};
/// let local_offset = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH);
/// # if false {
/// assert!(local_offset.is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
pub fn local_offset_at(datetime: OffsetDateTime) -> Result<Self, error::IndeterminateOffset> {
local_offset_at(datetime).ok_or(error::IndeterminateOffset)
}
/// Attempt to obtain the system's current UTC offset. If the offset cannot be determined, an
/// error is returned.
///
/// ```rust
/// # use time::UtcOffset;
/// let local_offset = UtcOffset::current_local_offset();
/// # if false {
/// assert!(local_offset.is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
pub fn current_local_offset() -> Result<Self, error::IndeterminateOffset> {
let now = OffsetDateTime::now_utc();
local_offset_at(now).ok_or(error::IndeterminateOffset)
}
// endregion: local offset
}
// region: formatting & parsing
#[cfg(feature = "formatting")]
impl UtcOffset {
/// Format the `UtcOffset` using the provided [format description](crate::format_description).
pub fn format_into(
self,
output: &mut impl io::Write,
format: &(impl Formattable + ?Sized),
) -> Result<usize, error::Format> {
format.format_into(output, None, None, Some(self))
}
/// Format the `UtcOffset` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::offset};
/// let format = format_description::parse("[offset_hour sign:mandatory]:[offset_minute]")?;
/// assert_eq!(offset!(+1).format(&format)?, "+01:00");
/// # Ok::<_, time::Error>(())
/// ```
pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> {
format.format(None, None, Some(self))
}
}
#[cfg(feature = "parsing")]
impl UtcOffset {
/// Parse a `UtcOffset` from the input using the provided [format
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::offset, UtcOffset};
/// let format = format_description::parse("[offset_hour]:[offset_minute]")?;
/// assert_eq!(UtcOffset::parse("-03:42", &format)?, offset!(-3:42));
/// # Ok::<_, time::Error>(())
/// ```
pub fn parse(
input: &str,
description: &(impl Parsable + ?Sized),
) -> Result<Self, error::Parse> {
description.parse_offset(input.as_bytes())
}
}
impl fmt::Display for UtcOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}{:02}:{:02}:{:02}",
if self.is_negative() { '-' } else { '+' },
self.hours.abs(),
self.minutes.abs(),
self.seconds.abs()
)
}
}
// endregion formatting & parsing
impl Neg for UtcOffset {
type Output = Self;
fn neg(self) -> Self::Output {
Self::__from_hms_unchecked(-self.hours, -self.minutes, -self.seconds)
}
}

80
zeroidc/vendor/time/src/util.rs vendored Normal file
View File

@@ -0,0 +1,80 @@
//! Utility functions.
use crate::Month;
/// Whether to adjust the date, and in which direction. Useful when implementing arithmetic.
pub(crate) enum DateAdjustment {
/// The previous day should be used.
Previous,
/// The next day should be used.
Next,
/// The date should be used as-is.
None,
}
/// Get the number of days in the month of a given year.
///
/// ```rust
/// # use time::{Month, util};
/// assert_eq!(util::days_in_year_month(2020, Month::February), 29);
/// ```
pub const fn days_in_year_month(year: i32, month: Month) -> u8 {
use Month::*;
match month {
January | March | May | July | August | October | December => 31,
April | June | September | November => 30,
February if is_leap_year(year) => 29,
February => 28,
}
}
/// Returns if the provided year is a leap year in the proleptic Gregorian calendar. Uses
/// [astronomical year numbering](https://en.wikipedia.org/wiki/Astronomical_year_numbering).
///
/// ```rust
/// # use time::util::is_leap_year;
/// assert!(!is_leap_year(1900));
/// assert!(is_leap_year(2000));
/// assert!(is_leap_year(2004));
/// assert!(!is_leap_year(2005));
/// assert!(!is_leap_year(2100));
/// ```
pub const fn is_leap_year(year: i32) -> bool {
year % 4 == 0 && (year % 25 != 0 || year % 16 == 0)
}
/// Get the number of calendar days in a given year.
///
/// The returned value will always be either 365 or 366.
///
/// ```rust
/// # use time::util::days_in_year;
/// assert_eq!(days_in_year(1900), 365);
/// assert_eq!(days_in_year(2000), 366);
/// assert_eq!(days_in_year(2004), 366);
/// assert_eq!(days_in_year(2005), 365);
/// assert_eq!(days_in_year(2100), 365);
/// ```
pub const fn days_in_year(year: i32) -> u16 {
if is_leap_year(year) { 366 } else { 365 }
}
/// Get the number of weeks in the ISO year.
///
/// The returned value will always be either 52 or 53.
///
/// ```rust
/// # use time::util::weeks_in_year;
/// assert_eq!(weeks_in_year(2019), 52);
/// assert_eq!(weeks_in_year(2020), 53);
/// ```
pub const fn weeks_in_year(year: i32) -> u8 {
match year.rem_euclid(400) {
4 | 9 | 15 | 20 | 26 | 32 | 37 | 43 | 48 | 54 | 60 | 65 | 71 | 76 | 82 | 88 | 93 | 99
| 105 | 111 | 116 | 122 | 128 | 133 | 139 | 144 | 150 | 156 | 161 | 167 | 172 | 178
| 184 | 189 | 195 | 201 | 207 | 212 | 218 | 224 | 229 | 235 | 240 | 246 | 252 | 257
| 263 | 268 | 274 | 280 | 285 | 291 | 296 | 303 | 308 | 314 | 320 | 325 | 331 | 336
| 342 | 348 | 353 | 359 | 364 | 370 | 376 | 381 | 387 | 392 | 398 => 53,
_ => 52,
}
}

148
zeroidc/vendor/time/src/weekday.rs vendored Normal file
View File

@@ -0,0 +1,148 @@
//! Days of the week.
use core::fmt::{self, Display};
use core::str::FromStr;
use Weekday::*;
use crate::error;
/// Days of the week.
///
/// As order is dependent on context (Sunday could be either two days after or five days before
/// Friday), this type does not implement `PartialOrd` or `Ord`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Weekday {
#[allow(clippy::missing_docs_in_private_items)]
Monday,
#[allow(clippy::missing_docs_in_private_items)]
Tuesday,
#[allow(clippy::missing_docs_in_private_items)]
Wednesday,
#[allow(clippy::missing_docs_in_private_items)]
Thursday,
#[allow(clippy::missing_docs_in_private_items)]
Friday,
#[allow(clippy::missing_docs_in_private_items)]
Saturday,
#[allow(clippy::missing_docs_in_private_items)]
Sunday,
}
impl Weekday {
/// Get the previous weekday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Tuesday.previous(), Weekday::Monday);
/// ```
pub const fn previous(self) -> Self {
match self {
Monday => Sunday,
Tuesday => Monday,
Wednesday => Tuesday,
Thursday => Wednesday,
Friday => Thursday,
Saturday => Friday,
Sunday => Saturday,
}
}
/// Get the next weekday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Monday.next(), Weekday::Tuesday);
/// ```
pub const fn next(self) -> Self {
match self {
Monday => Tuesday,
Tuesday => Wednesday,
Wednesday => Thursday,
Thursday => Friday,
Friday => Saturday,
Saturday => Sunday,
Sunday => Monday,
}
}
/// Get the one-indexed number of days from Monday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Monday.number_from_monday(), 1);
/// ```
#[doc(alias = "iso_weekday_number")]
pub const fn number_from_monday(self) -> u8 {
self.number_days_from_monday() + 1
}
/// Get the one-indexed number of days from Sunday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Monday.number_from_sunday(), 2);
/// ```
pub const fn number_from_sunday(self) -> u8 {
self.number_days_from_sunday() + 1
}
/// Get the zero-indexed number of days from Monday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Monday.number_days_from_monday(), 0);
/// ```
pub const fn number_days_from_monday(self) -> u8 {
self as _
}
/// Get the zero-indexed number of days from Sunday.
///
/// ```rust
/// # use time::Weekday;
/// assert_eq!(Weekday::Monday.number_days_from_sunday(), 1);
/// ```
pub const fn number_days_from_sunday(self) -> u8 {
match self {
Monday => 1,
Tuesday => 2,
Wednesday => 3,
Thursday => 4,
Friday => 5,
Saturday => 6,
Sunday => 0,
}
}
}
impl Display for Weekday {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Monday => "Monday",
Tuesday => "Tuesday",
Wednesday => "Wednesday",
Thursday => "Thursday",
Friday => "Friday",
Saturday => "Saturday",
Sunday => "Sunday",
})
}
}
impl FromStr for Weekday {
type Err = error::InvalidVariant;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Monday" => Ok(Monday),
"Tuesday" => Ok(Tuesday),
"Wednesday" => Ok(Wednesday),
"Thursday" => Ok(Thursday),
"Friday" => Ok(Friday),
"Saturday" => Ok(Saturday),
"Sunday" => Ok(Sunday),
_ => Err(error::InvalidVariant),
}
}
}