Bug 1806766 - Update time to 0.3.17. r=emilio,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D165570
This commit is contained in:
Mike Hommey 2022-12-28 20:48:54 +00:00
Родитель db3417cdda
Коммит c2e9fb5416
88 изменённых файлов: 5171 добавлений и 2151 удалений

38
Cargo.lock сгенерированный
Просмотреть файл

@ -848,7 +848,7 @@ version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"time 0.3.9",
"time 0.3.17",
"version_check",
]
@ -3852,15 +3852,6 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
@ -4198,7 +4189,7 @@ dependencies = [
"indexmap",
"line-wrap",
"serde",
"time 0.3.9",
"time 0.3.17",
"xml-rs",
]
@ -5318,21 +5309,30 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.9"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"libc",
"num_threads",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.4"
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "time-macros"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
dependencies = [
"time-core",
]
[[package]]
name = "tinystr"
@ -6107,7 +6107,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"time 0.3.9",
"time 0.3.17",
"tokio 1.17.0",
"tokio-stream",
"unicode-segmentation",

Просмотреть файл

@ -1798,6 +1798,21 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-deploy"
delta = "0.1.44 -> 0.1.45"
[[audits.time]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "0.3.9 -> 0.3.17"
[[audits.time-core]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
version = "0.1.0"
[[audits.time-macros]]
who = "Mike Hommey <mh+mozilla@glandium.org>"
criteria = "safe-to-run"
delta = "0.2.4 -> 0.2.6"
[[audits.tinystr]]
who = "Zibi Braniecki <zibi@unicode.org>"
criteria = "safe-to-deploy"

Просмотреть файл

@ -889,10 +889,6 @@ criteria = "safe-to-deploy"
version = "1.13.1"
criteria = "safe-to-deploy"
[[exemptions.num_threads]]
version = "0.1.6"
criteria = "safe-to-run"
[[exemptions.objc]]
version = "0.2.7"
criteria = "safe-to-deploy"

Просмотреть файл

@ -1 +0,0 @@
{"files":{"Cargo.toml":"ed1dbfb8f9eb836549858fb0ca704eddaa480cbe576e8dd50ebca1529dca21b3","LICENSE-Apache":"c69b72788ec261765e460532016b4bb78372f238e449b5e70e3268ec1e18fd15","LICENSE-MIT":"b4bf94a9fceb8846320fda938ee53fc16b506572609da5cf1d2d289a5597a8f8","src/apple.rs":"018e729ea67e8e17428d2e8d93f28ed0f7e7c506fcf78b56f56113235ce7dfcf","src/freebsd.rs":"683636294a62d6b958a5de800b52ddfea609234921e0583906272aacc71e18e5","src/imp.rs":"8cc9d07f05b0aa70e9997648abce7f79bd758c6fc76040d1c8f7beb7bf551e9d","src/lib.rs":"c53382612069b9552846414d1508cbb1401c500a4f34e21addd336001ebd8b7e","src/linux.rs":"67e02ecd105b8a421227bf72814e8388a4f77df1d8a44b8902bc8926f7e6698c"},"package":"2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"}

36
third_party/rust/num_threads/Cargo.toml поставляемый
Просмотреть файл

@ -1,36 +0,0 @@
# 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]
name = "num_threads"
version = "0.1.6"
authors = ["Jacob Pratt <open-source@jhpratt.dev>"]
include = [
"src/**/*",
"LICENSE-*",
]
description = "A minimal library that determines the number of running threads for the current process."
categories = [
"api-bindings",
"hardware-support",
"os",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/jhpratt/num_threads"
[package.metadata.docs.rs]
all-features = true
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
[target."cfg(any(target_os = \"macos\", target_os = \"ios\", target_os = \"freebsd\"))".dependencies.libc]
version = "0.2.107"

45
third_party/rust/num_threads/src/apple.rs поставляемый
Просмотреть файл

@ -1,45 +0,0 @@
extern crate libc;
use std::num::NonZeroUsize;
use self::libc::{kern_return_t, mach_msg_type_number_t, mach_port_t, thread_t};
// This constant is from
// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/
// usr/include/mach/machine/thread_state.h.
//
// It has not been updated since Apple devices started to support 64-bit ARM (iOS), so it
// should be very stable.
const THREAD_STATE_MAX: i32 = 1296;
#[allow(non_camel_case_types)]
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L155
type task_inspect_t = mach_port_t;
#[allow(non_camel_case_types)]
// https://github.com/apple/darwin-xnu/blob/a1babec6b135d1f35b2590a1990af3c5c5393479/osfmk/mach/mach_types.defs#L238
type thread_array_t = [thread_t; THREAD_STATE_MAX as usize];
extern "C" {
// https://developer.apple.com/documentation/kernel/1537751-task_threads/
fn task_threads(
target_task: task_inspect_t,
act_list: *mut thread_array_t,
act_listCnt: *mut mach_msg_type_number_t,
) -> kern_return_t;
}
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_threads.html
let mut thread_state = [0u32; THREAD_STATE_MAX as usize];
let mut thread_count = 0;
// Safety: `mach_task_self` always returns a valid value, `thread_state` is large enough, and
// both it and `thread_count` are writable.
let result =
unsafe { task_threads(libc::mach_task_self(), &mut thread_state, &mut thread_count) };
if result == libc::KERN_SUCCESS {
NonZeroUsize::new(thread_count as usize)
} else {
None
}
}

36
third_party/rust/num_threads/src/freebsd.rs поставляемый
Просмотреть файл

@ -1,36 +0,0 @@
extern crate libc;
use std::num::NonZeroUsize;
use std::{mem, ptr};
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
// Safety: `sysctl` and `getpid` are both thread-safe.
// `kip` is only accessed if sysctl() succeeds and agrees with the expected size,
// and the data only trusted if both its embedded size and pid match expectations
unsafe {
let pid = libc::getpid();
let mib: [libc::c_int; 4] = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_PID, pid];
let mut kip: libc::kinfo_proc = mem::zeroed();
let expected_kip_len = mem::size_of_val(&kip);
let mut kip_len = expected_kip_len;
let ret = libc::sysctl(
mib.as_ptr(),
mib.len() as u32,
&mut kip as *mut _ as *mut libc::c_void,
&mut kip_len,
ptr::null(),
0,
);
if ret == 0
&& kip_len == expected_kip_len
&& kip.ki_structsize == expected_kip_len as i32
&& kip.ki_pid == pid
{
NonZeroUsize::new(kip.ki_numthreads as usize)
} else {
None
}
}
}

7
third_party/rust/num_threads/src/imp.rs поставляемый
Просмотреть файл

@ -1,7 +0,0 @@
//! Fallback if no OS matches.
use std::num::NonZeroUsize;
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
None
}

64
third_party/rust/num_threads/src/lib.rs поставляемый
Просмотреть файл

@ -1,64 +0,0 @@
//! Minimum supported Rust version: 1.28
use std::num::NonZeroUsize;
#[cfg_attr(any(target_os = "linux", target_os = "android"), path = "linux.rs")]
#[cfg_attr(target_os = "freebsd", path = "freebsd.rs")]
#[cfg_attr(any(target_os = "macos", target_os = "ios"), path = "apple.rs")]
mod imp;
/// Obtain the number of threads currently part of the active process. Returns `None` if the number
/// of threads cannot be determined.
pub fn num_threads() -> Option<NonZeroUsize> {
imp::num_threads()
}
/// Determine if the current process is single-threaded. Returns `None` if the number of threads
/// cannot be determined.
pub fn is_single_threaded() -> Option<bool> {
num_threads().map(|n| n.get() == 1)
}
#[cfg(test)]
mod test {
use std::num::NonZeroUsize;
// Run each expression in its own thread.
macro_rules! threaded {
($first:expr;) => {
$first;
};
($first:expr; $($rest:expr;)*) => {
$first;
::std::thread::spawn(|| {
threaded!($($rest;)*);
})
.join()
.unwrap();
};
}
#[test]
fn num_threads() {
threaded! {
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(1));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(2));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(3));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(4));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(5));
assert_eq!(super::num_threads().map(NonZeroUsize::get), Some(6));
}
}
#[test]
fn is_single_threaded() {
threaded! {
assert_eq!(super::is_single_threaded(), Some(true));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
assert_eq!(super::is_single_threaded(), Some(false));
}
}
}

14
third_party/rust/num_threads/src/linux.rs поставляемый
Просмотреть файл

@ -1,14 +0,0 @@
use std::fs;
use std::num::NonZeroUsize;
pub(crate) fn num_threads() -> Option<NonZeroUsize> {
fs::read_to_string("/proc/self/stat")
.ok()
.as_ref()
// Skip past the pid and (process name) fields
.and_then(|stat| stat.rsplit(')').next())
// 20th field, less the two we skipped
.and_then(|rstat| rstat.split_whitespace().nth(17))
.and_then(|num_threads| num_threads.parse::<usize>().ok())
.and_then(NonZeroUsize::new)
}

1
third_party/rust/time-core/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"d20a28a1ed2c35aff8a60f495a0ed440e022051d3909b372bf5e262fd62d7b29","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/lib.rs":"247b6ac4c2acc97e51552fd7ca1ef7cb2cbc30f8bcbbf5d029553a83a7fe2cc4","src/util.rs":"52c1fbf68b71c3582caf0d9a8255378c6c14a737e2df8d7e6d6603b0eb12ca06"},"package":"2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"}

32
third_party/rust/time-core/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,32 @@
# 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 = "2021"
rust-version = "1.60.0"
name = "time-core"
version = "0.1.0"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
description = "This crate is an implementation detail and should not be relied upon directly."
keywords = [
"date",
"time",
"calendar",
"duration",
]
categories = ["date-and-time"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
[dependencies]

Просмотреть файл

@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2021 Jacob Pratt
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.

Просмотреть файл

@ -1,4 +1,4 @@
Copyright (c) 2021 Jacob Pratt
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

52
third_party/rust/time-core/src/lib.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
//! Core items for `time`.
//!
//! This crate is an implementation detail of `time` and should not be relied upon directly.
#![no_std]
#![deny(
anonymous_parameters,
clippy::all,
clippy::alloc_instead_of_core,
clippy::explicit_auto_deref,
clippy::obfuscated_if_else,
clippy::std_instead_of_core,
clippy::undocumented_unsafe_blocks,
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_op_in_unsafe_fn,
unused_extern_crates,
rustdoc::broken_intra_doc_links,
rustdoc::private_intra_doc_links
)]
#![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))))]
pub mod util;

52
third_party/rust/time-core/src/util.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,52 @@
//! Utility functions.
/// 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,
}
}

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"c993889735eed89f265010c2ff35ffdd33c95077f53b012678d7330b7662ba9e","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ef9432de8c7250b6ffe82fe7cd4332018ce22e32b0a414d3a7d60eb18d9b6eca","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"a1de0680c8a51a05f341352e9707fe50d2c29b00c36600c2b642879cea1e89c6","src/format_description/component.rs":"a05e7549db9bab4f3836f5fd5af18cacbfa6b323d0106b027e21bf438a5885e5","src/format_description/error.rs":"f2085c5914c21bce09e8eb73b2b96584ac28a829f151e339b3f4ca7b5d5405a3","src/format_description/mod.rs":"da47af329408e9428753ad98ce433eaf026cfdd6e73e3142b23285251d32d0dd","src/format_description/modifier.rs":"c252c8a7d6608b594a6f715210ff67e804ae2f308025f62c8dd99d707627e4a9","src/format_description/parse.rs":"e131ccb7e7ad28e851a01f1b4ce8a6febcc2093e21c0a18126f15e8c17044625","src/helpers/mod.rs":"d3283903b66388efced79a4b1eca3a6b4644031d1ae211034443df5b6b78dc51","src/helpers/string.rs":"ba5699a4df344cbd71c4143f642f6bc07591f53978a9800d4b49ca1f461f87d9","src/lib.rs":"fecf82d1c8b1287fda03c3728741e477731534cf9c2609def2b525ad29683119","src/offset.rs":"fc9341648e091b4d8f7bec47006c01c21cb038c7ef98bd36a492cf78e7533023","src/quote.rs":"058ae621d7c6951c6289a3e855c74d924d9434c1f3031438587aedeb718be283","src/serde_format_description.rs":"5750a1ecf75ae80dc88d4242a6e56b39f3e25c77fdbcc2e88ec3a91ad8511434","src/time.rs":"3c06562358aed7ef624319c96e3f9c150a069606ab930de98ac379ef16b08100","src/to_tokens.rs":"825150a92396a019fee44f21da0bd257349e276d5e75a23ff86cfc625bef6f10"},"package":"42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"}
{"files":{"Cargo.toml":"eb16c06efbfbf2ff5f48260785d4ecefbae6873d9d55c0ba2d388c6762e69b1f","LICENSE-Apache":"b8929fea28678da67251fb2daf9438f67503814211051861612441806d8edb05","LICENSE-MIT":"04620bf27e4a643dd47bf27652320c205acdb776c1f9f24bb8c3bfaba10804c5","src/date.rs":"ffcd3d0998ec67abb43a3f8eccc6104172f5061b855312b89d53bb82fece2460","src/datetime.rs":"5c7f6e07dc2f0dcfcd86216664df53bc008dbc86f346df57a9ff57f52fe43bc6","src/error.rs":"b597f98f425f1628b93ffea19f5f32163aa204e4cd25351bc114853a798e14b0","src/format_description/component.rs":"a05e7549db9bab4f3836f5fd5af18cacbfa6b323d0106b027e21bf438a5885e5","src/format_description/error.rs":"41253d7a02e14597915cf588811a272a90d1ce0f857f7769914e076dd5a66774","src/format_description/mod.rs":"da47af329408e9428753ad98ce433eaf026cfdd6e73e3142b23285251d32d0dd","src/format_description/modifier.rs":"c252c8a7d6608b594a6f715210ff67e804ae2f308025f62c8dd99d707627e4a9","src/format_description/parse.rs":"d65d6e7008030414ce6a860ff37c462c07ed89176a3f1462eeb46468a38fce7e","src/helpers/mod.rs":"54ce8e93512e18ef8761687eaac898a8227852a732f92aa5e80c28e23315bd0c","src/helpers/string.rs":"ba5699a4df344cbd71c4143f642f6bc07591f53978a9800d4b49ca1f461f87d9","src/lib.rs":"f99bded51bb861be5d708a3f756407f5b936a5febb719760c253a15113687e0d","src/offset.rs":"fc9341648e091b4d8f7bec47006c01c21cb038c7ef98bd36a492cf78e7533023","src/quote.rs":"b40251b0ca68e2362aff4297b87a027e48053f1a419113d3d0f7fe089a845a9c","src/serde_format_description.rs":"aa279c8005005fc87c52fa5e8be8ef8fc13ef456a18e3cd5d702ae81194ba4d9","src/time.rs":"3c06562358aed7ef624319c96e3f9c150a069606ab930de98ac379ef16b08100","src/to_tokens.rs":"825150a92396a019fee44f21da0bd257349e276d5e75a23ff86cfc625bef6f10"},"package":"d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"}

30
third_party/rust/time-macros/Cargo.toml поставляемый
Просмотреть файл

@ -10,20 +10,36 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
edition = "2021"
rust-version = "1.60.0"
name = "time-macros"
version = "0.2.4"
authors = ["Jacob Pratt <open-source@jhpratt.dev>", "Time contributors"]
description = "Procedural macros for the time crate."
readme = "../README.md"
keywords = ["date", "time", "calendar", "duration"]
version = "0.2.6"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
description = """
Procedural macros for the time crate.
This crate is an implementation detail and should not be relied upon directly.
"""
keywords = [
"date",
"time",
"calendar",
"duration",
]
categories = ["date-and-time"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
resolver = "2"
[lib]
proc-macro = true
[dependencies.time-core]
version = "=0.1.0"
[features]
formatting = []
large-dates = []
parsing = []
serde = []

4
third_party/rust/time-macros/src/date.rs поставляемый
Просмотреть файл

@ -1,10 +1,10 @@
use std::iter::Peekable;
use proc_macro::{token_stream, TokenTree};
use time_core::util::{days_in_year, weeks_in_year};
use crate::helpers::{
consume_any_ident, consume_number, consume_punct, days_in_year, days_in_year_month,
weeks_in_year, ymd_to_yo, ywd_to_yo,
consume_any_ident, consume_number, consume_punct, days_in_year_month, ymd_to_yo, ywd_to_yo,
};
use crate::to_tokens::ToTokenTree;
use crate::Error;

25
third_party/rust/time-macros/src/error.rs поставляемый
Просмотреть файл

@ -1,9 +1,9 @@
use std::borrow::Cow;
use std::fmt;
use std::iter::once;
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
#[cfg(any(feature = "formatting", feature = "parsing"))]
use crate::format_description::error::InvalidFormatDescription;
trait WithSpan {
@ -29,6 +29,7 @@ pub(crate) enum Error {
span_start: Option<Span>,
span_end: Option<Span>,
},
#[cfg(any(feature = "formatting", feature = "parsing"))]
ExpectedString {
span_start: Option<Span>,
span_end: Option<Span>,
@ -37,6 +38,7 @@ pub(crate) enum Error {
tree: TokenTree,
},
UnexpectedEndOfInput,
#[cfg(any(feature = "formatting", feature = "parsing"))]
InvalidFormatDescription {
error: InvalidFormatDescription,
span_start: Option<Span>,
@ -52,13 +54,15 @@ pub(crate) enum Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingComponent { name, .. } => write!(f, "missing component: {}", name),
Self::MissingComponent { name, .. } => write!(f, "missing component: {name}"),
Self::InvalidComponent { name, value, .. } => {
write!(f, "invalid component: {} was {}", name, value)
write!(f, "invalid component: {name} was {value}")
}
#[cfg(any(feature = "formatting", feature = "parsing"))]
Self::ExpectedString { .. } => f.write_str("expected string"),
Self::UnexpectedToken { tree } => write!(f, "unexpected token: {}", tree),
Self::UnexpectedToken { tree } => write!(f, "unexpected token: {tree}"),
Self::UnexpectedEndOfInput => f.write_str("unexpected end of input"),
#[cfg(any(feature = "formatting", feature = "parsing"))]
Self::InvalidFormatDescription { error, .. } => error.fmt(f),
Self::Custom { message, .. } => f.write_str(message),
}
@ -70,9 +74,10 @@ impl Error {
match self {
Self::MissingComponent { span_start, .. }
| Self::InvalidComponent { span_start, .. }
| Self::ExpectedString { span_start, .. }
| Self::InvalidFormatDescription { span_start, .. }
| Self::Custom { span_start, .. } => *span_start,
#[cfg(any(feature = "formatting", feature = "parsing"))]
Self::ExpectedString { span_start, .. }
| Self::InvalidFormatDescription { span_start, .. } => *span_start,
Self::UnexpectedToken { tree } => Some(tree.span()),
Self::UnexpectedEndOfInput => Some(Span::mixed_site()),
}
@ -83,9 +88,10 @@ impl Error {
match self {
Self::MissingComponent { span_end, .. }
| Self::InvalidComponent { span_end, .. }
| Self::ExpectedString { span_end, .. }
| Self::InvalidFormatDescription { span_end, .. }
| Self::Custom { span_end, .. } => *span_end,
#[cfg(any(feature = "formatting", feature = "parsing"))]
Self::ExpectedString { span_end, .. }
| Self::InvalidFormatDescription { span_end, .. } => *span_end,
Self::UnexpectedToken { tree, .. } => Some(tree.span()),
Self::UnexpectedEndOfInput => Some(Span::mixed_site()),
}
@ -117,11 +123,12 @@ impl Error {
}
/// Like `to_compile_error`, but for use in macros that produce items.
#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))]
pub(crate) fn to_compile_error_standalone(&self) -> TokenStream {
let end = self.span_end();
self.to_compile_error()
.into_iter()
.chain(once(
.chain(std::iter::once(
TokenTree::from(Punct::new(';', Spacing::Alone)).with_span(end),
))
.collect()

Просмотреть файл

@ -13,18 +13,16 @@ impl fmt::Display for InvalidFormatDescription {
use InvalidFormatDescription::*;
match self {
UnclosedOpeningBracket { index } => {
write!(f, "unclosed opening bracket at byte index {}", index)
write!(f, "unclosed opening bracket at byte index {index}")
}
InvalidComponentName { name, index } => {
write!(f, "invalid component name `{name}` 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)
write!(f, "invalid modifier `{value}` at byte index {index}")
}
MissingComponentName { index } => {
write!(f, "missing component name at byte index {}", index)
write!(f, "missing component name at byte index {index}")
}
}
}

Просмотреть файл

@ -43,12 +43,12 @@ fn parse_item<'a>(
if let [b'[', b'[', remaining @ ..] = s {
*index += 2;
return Ok(ParsedItem {
item: FormatItem::Literal(&[b'[']),
item: FormatItem::Literal(b"["),
remaining,
});
};
if s.starts_with(&[b'[']) {
if s.starts_with(b"[") {
if let Some(bracket_index) = s.iter().position(|&c| c == b']') {
*index += 1; // opening bracket
let ret_val = ParsedItem {

Просмотреть файл

@ -1,12 +1,17 @@
#[cfg(any(feature = "formatting", feature = "parsing"))]
mod string;
use std::iter::Peekable;
use std::str::FromStr;
use proc_macro::{token_stream, Span, TokenStream, TokenTree};
#[cfg(any(feature = "formatting", feature = "parsing"))]
use proc_macro::TokenStream;
use proc_macro::{token_stream, Span, TokenTree};
use time_core::util::{days_in_year, is_leap_year};
use crate::Error;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub(crate) fn get_string_literal(tokens: TokenStream) -> Result<(Span, Vec<u8>), Error> {
let mut tokens = tokens.into_iter();
@ -76,10 +81,6 @@ pub(crate) fn consume_punct(
}
}
fn is_leap_year(year: i32) -> bool {
(year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}
fn jan_weekday(year: i32, ordinal: i32) -> u8 {
macro_rules! div_floor {
($a:expr, $b:expr) => {{
@ -99,19 +100,11 @@ fn jan_weekday(year: i32, ordinal: i32) -> u8 {
.rem_euclid(7)) as _
}
pub(crate) fn days_in_year(year: i32) -> u16 {
365 + is_leap_year(year) as u16
}
pub(crate) fn days_in_year_month(year: i32, month: u8) -> u8 {
[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month as usize - 1]
+ (month == 2 && is_leap_year(year)) as u8
}
pub(crate) fn weeks_in_year(year: i32) -> u8 {
52 + (jan_weekday(year, 1) + is_leap_year(year) as u8 == 3) as u8
}
pub(crate) fn ywd_to_yo(year: i32, week: u8, iso_weekday_number: u8) -> (i32, u16) {
let (ordinal, overflow) = (u16::from(week) * 7 + u16::from(iso_weekday_number))
.overflowing_sub(u16::from(jan_weekday(year, 4)) + 4);

58
third_party/rust/time-macros/src/lib.rs поставляемый
Просмотреть файл

@ -28,7 +28,11 @@
unused_qualifications,
variant_size_differences
)]
#![allow(clippy::missing_const_for_fn, clippy::redundant_pub_crate)]
#![allow(
clippy::missing_const_for_fn, // useless in proc macro
clippy::redundant_pub_crate, // suggests bad style
clippy::option_if_let_else, // suggests terrible code
)]
#[macro_use]
mod quote;
@ -36,14 +40,18 @@ mod quote;
mod date;
mod datetime;
mod error;
#[cfg(any(feature = "formatting", feature = "parsing"))]
mod format_description;
mod helpers;
mod offset;
#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))]
mod serde_format_description;
mod time;
mod to_tokens;
use proc_macro::{TokenStream, TokenTree};
use proc_macro::TokenStream;
#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))]
use proc_macro::TokenTree;
use self::error::Error;
@ -67,8 +75,7 @@ macro_rules! impl_macros {
impl_macros![date datetime offset time];
// TODO Gate this behind the the `formatting` or `parsing` feature flag when weak dependency
// features land.
#[cfg(any(feature = "formatting", feature = "parsing"))]
#[proc_macro]
pub fn format_description(input: TokenStream) -> TokenStream {
(|| {
@ -88,6 +95,7 @@ pub fn format_description(input: TokenStream) -> TokenStream {
.unwrap_or_else(|err: Error| err.to_compile_error())
}
#[cfg(all(feature = "serde", any(feature = "formatting", feature = "parsing")))]
#[proc_macro]
pub fn serde_format_description(input: TokenStream) -> TokenStream {
(|| {
@ -112,17 +120,47 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream {
// Another comma
helpers::consume_punct(',', &mut tokens)?;
// Then, a string literal.
let (span, format_string) = helpers::get_string_literal(tokens.collect())?;
// We now have two options. The user can either provide a format description as a string or
// they can provide a path to a format description. If the latter, all remaining tokens are
// assumed to be part of the path.
let (format, raw_format_string) = match tokens.peek() {
// string literal
Some(TokenTree::Literal(_)) => {
let (span, format_string) = helpers::get_string_literal(tokens.collect())?;
let items = format_description::parse(&format_string, span)?;
let items: TokenStream =
items.into_iter().map(|item| quote! { #S(item), }).collect();
let items = quote! { &[#S(items)] };
let items = format_description::parse(&format_string, span)?;
let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect();
(
items,
Some(String::from_utf8_lossy(&format_string).into_owned()),
)
}
// path
Some(_) => (
quote! {{
// We can't just do `super::path` because the path could be an absolute path. In
// that case, we'd be generating `super::::path`, which is invalid. Even if we
// took that into account, it's not possible to know if it's an external crate,
// which would just require emitting `path` directly. By taking this approach,
// we can leave it to the compiler to do the actual resolution.
mod __path_hack {
pub(super) use super::super::*;
pub(super) use #S(tokens.collect::<TokenStream>()) as FORMAT;
}
__path_hack::FORMAT
}},
None,
),
None => return Err(Error::UnexpectedEndOfInput),
};
Ok(serde_format_description::build(
mod_name,
items,
formattable,
&String::from_utf8_lossy(&format_string),
format,
raw_format_string,
))
})()
.unwrap_or_else(|err: Error| err.to_compile_error_standalone())

2
third_party/rust/time-macros/src/quote.rs поставляемый
Просмотреть файл

@ -8,6 +8,7 @@ macro_rules! quote {
}};
}
#[cfg(any(feature = "formatting", feature = "parsing"))]
macro_rules! quote_append {
($ts:ident $($x:tt)*) => {{
quote_inner!($ts $($x)*);
@ -65,6 +66,7 @@ macro_rules! quote_inner {
($ts:ident ? $($tail:tt)*) => { sym!($ts '?'); quote_inner!($ts $($tail)*); };
($ts:ident ! $($tail:tt)*) => { sym!($ts '!'); quote_inner!($ts $($tail)*); };
($ts:ident | $($tail:tt)*) => { sym!($ts '|'); quote_inner!($ts $($tail)*); };
($ts:ident * $($tail:tt)*) => { sym!($ts '*'); quote_inner!($ts $($tail)*); };
// Identifier
($ts:ident $i:ident $($tail:tt)*) => {

Просмотреть файл

@ -1,130 +1,162 @@
use proc_macro::{Ident, TokenStream, TokenTree};
use crate::to_tokens;
pub(crate) fn build(
mod_name: Ident,
items: impl to_tokens::ToTokenStream,
ty: TokenTree,
format_string: &str,
format: TokenStream,
raw_format_string: Option<String>,
) -> TokenStream {
let ty_s = &*ty.to_string();
let visitor = quote! {
struct Visitor;
struct OptionVisitor;
let format_description_display = raw_format_string.unwrap_or_else(|| format.to_string());
impl<'a> ::serde::de::Visitor<'a> for Visitor {
type Value = __TimeSerdeType;
let visitor = if cfg!(feature = "parsing") {
quote! {
struct Visitor;
struct OptionVisitor;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"a(n) `",
#(ty_s),
"` in the format \"",
#(format_string),
"\"",
impl<'a> ::serde::de::Visitor<'a> for Visitor {
type Value = __TimeSerdeType;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"a(n) `",
#(ty_s),
"` in the format \"{}\"",
),
#(format_description_display.as_str())
)
)
}
fn visit_str<E: ::serde::de::Error>(
self,
value: &str
) -> Result<__TimeSerdeType, E> {
__TimeSerdeType::parse(value, &DESCRIPTION).map_err(E::custom)
}
}
fn visit_str<E: ::serde::de::Error>(
self,
value: &str
) -> Result<__TimeSerdeType, E> {
__TimeSerdeType::parse(value, &DESCRIPTION).map_err(E::custom)
impl<'a> ::serde::de::Visitor<'a> for OptionVisitor {
type Value = Option<__TimeSerdeType>;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"an `Option<",
#(ty_s),
">` in the format \"{}\"",
),
#(format_description_display.as_str())
)
}
fn visit_some<D: ::serde::de::Deserializer<'a>>(
self,
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
deserializer
.deserialize_any(Visitor)
.map(Some)
}
fn visit_none<E: ::serde::de::Error>(
self
) -> Result<Option<__TimeSerdeType>, E> {
Ok(None)
}
}
}
} else {
quote!()
};
impl<'a> ::serde::de::Visitor<'a> for OptionVisitor {
type Value = Option<__TimeSerdeType>;
fn expecting(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(
f,
concat!(
"an `Option<",
#(ty_s),
">` in the format \"",
#(format_string),
"\"",
)
)
let serialize_primary = if cfg!(feature = "formatting") {
quote! {
pub fn serialize<S: ::serde::Serializer>(
datetime: &__TimeSerdeType,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
datetime
.format(&DESCRIPTION)
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
}
} else {
quote!()
};
fn visit_some<D: ::serde::de::Deserializer<'a>>(
self,
let deserialize_primary = if cfg!(feature = "parsing") {
quote! {
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<__TimeSerdeType, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_any(Visitor)
}
}
} else {
quote!()
};
let serialize_option = if cfg!(feature = "formatting") {
quote! {
pub fn serialize<S: ::serde::Serializer>(
option: &Option<__TimeSerdeType>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
option.map(|datetime| datetime.format(&DESCRIPTION))
.transpose()
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
}
} else {
quote!()
};
let deserialize_option = if cfg!(feature = "parsing") {
quote! {
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
deserializer
.deserialize_any(Visitor)
.map(Some)
}
fn visit_none<E: ::serde::de::Error>(
self
) -> Result<Option<__TimeSerdeType>, E> {
Ok(None)
use ::serde::Deserialize;
deserializer.deserialize_option(OptionVisitor)
}
}
} else {
quote!()
};
let primary_fns = quote! {
pub fn serialize<S: ::serde::Serializer>(
datetime: &__TimeSerdeType,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
datetime
.format(&DESCRIPTION)
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<__TimeSerdeType, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_any(Visitor)
}
};
let options_fns = quote! {
pub fn serialize<S: ::serde::Serializer>(
option: &Option<__TimeSerdeType>,
serializer: S,
) -> Result<S::Ok, S::Error> {
use ::serde::Serialize;
option.map(|datetime| datetime.format(&DESCRIPTION))
.transpose()
.map_err(::time::error::Format::into_invalid_serde_value::<S>)?
.serialize(serializer)
}
pub fn deserialize<'a, D: ::serde::Deserializer<'a>>(
deserializer: D
) -> Result<Option<__TimeSerdeType>, D::Error> {
use ::serde::Deserialize;
deserializer.deserialize_option(OptionVisitor)
let deserialize_option_imports = if cfg!(feature = "parsing") {
quote! {
use super::{OptionVisitor, Visitor};
}
} else {
quote!()
};
quote! {
mod #(mod_name) {
use ::time::#(ty) as __TimeSerdeType;
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(items)];
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = #S(format);
#S(visitor)
#S(primary_fns)
#S(serialize_primary)
#S(deserialize_primary)
pub(super) mod option {
use super::{DESCRIPTION, OptionVisitor, Visitor, __TimeSerdeType};
use super::{DESCRIPTION, __TimeSerdeType};
#S(deserialize_option_imports)
#S(options_fns)
#S(serialize_option)
#S(deserialize_option)
}
}
}

2
third_party/rust/time/.cargo-checksum.json поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

126
third_party/rust/time/Cargo.toml поставляемый
Просмотреть файл

@ -10,42 +10,65 @@
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
edition = "2021"
rust-version = "1.60.0"
name = "time"
version = "0.3.9"
authors = ["Jacob Pratt <open-source@jhpratt.dev>", "Time contributors"]
include = ["src/**/*", "LICENSE-*", "README.md", "!src/tests.rs"]
version = "0.3.17"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
include = [
"src/**/*",
"LICENSE-*",
"README.md",
]
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"]
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
rustdoc-args = [
"--cfg",
"__time_03_docs",
]
[lib]
bench = false
[[test]]
name = "tests"
path = "../tests/main.rs"
[[bench]]
name = "benchmarks"
path = "benchmarks/main.rs"
path = "../benchmarks/main.rs"
harness = false
[dependencies.itoa]
version = "1.0.1"
optional = true
[dependencies.quickcheck-dep]
[dependencies.quickcheck]
version = "1.0.3"
optional = true
default-features = false
package = "quickcheck"
[dependencies.rand]
version = "0.8.4"
@ -57,9 +80,13 @@ version = "1.0.126"
optional = true
default-features = false
[dependencies.time-core]
version = "=0.1.0"
[dependencies.time-macros]
version = "=0.2.4"
version = "=0.2.6"
optional = true
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
@ -78,27 +105,62 @@ 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"
[dev-dependencies.time-macros]
version = "=0.2.6"
[features]
alloc = ["serde?/alloc"]
default = ["std"]
formatting = [
"dep:itoa",
"std",
"time-macros?/formatting",
]
large-dates = ["time-macros?/large-dates"]
local-offset = [
"std",
"dep:libc",
"dep:num_threads",
]
macros = ["dep:time-macros"]
parsing = ["time-macros?/parsing"]
quickcheck = [
"dep:quickcheck",
"alloc",
]
rand = ["dep:rand"]
serde = [
"dep:serde",
"time-macros?/serde",
]
serde-human-readable = [
"serde",
"formatting",
"parsing",
]
serde-well-known = [
"serde",
"formatting",
"parsing",
]
std = ["alloc"]
wasm-bindgen = ["dep:js-sys"]
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
version = "1.0.68"
[target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.js-sys]
version = "0.3.58"
optional = true
[target."cfg(bench)".dev-dependencies.criterion]
version = "0.4.0"
default-features = false
[target."cfg(bench)".dev-dependencies.criterion-cycles-per-byte]
version = "0.1.2"
[target."cfg(target_family = \"unix\")".dependencies.libc]
version = "0.2.98"
optional = true
[target."cfg(target_family = \"unix\")".dependencies.num_threads]
version = "0.1.2"
optional = true

14
third_party/rust/time/README.md поставляемый
Просмотреть файл

@ -1,11 +1,12 @@
# time
[![minimum rustc: 1.53](https://img.shields.io/badge/minimum%20rustc-1.53-yellowgreen?logo=rust&style=flat-square)](https://www.whatrustisit.com)
[![minimum rustc: 1.60](https://img.shields.io/badge/minimum%20rustc-1.60-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)
@ -22,17 +23,10 @@ Contributions are always welcome! If you have an idea, it's best to float it by
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.
If you have any questions, feel free to use [Discussions]. Don't hesitate to ask questions — that's
what I'm here for!
[Discussions]: https://github.com/time-rs/time/discussions
[Codestream]: https://codestream.com
[VS Live Share]: https://code.visualstudio.com/learn/collaboration/live-share
## License

184
third_party/rust/time/src/date.rs поставляемый
Просмотреть файл

@ -14,18 +14,17 @@ use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year}
use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday};
/// The minimum valid year.
#[cfg(feature = "large-dates")]
pub(crate) const MIN_YEAR: i32 = -999_999;
pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") {
-999_999
} else {
-9999
};
/// The maximum valid year.
#[cfg(feature = "large-dates")]
pub(crate) const MAX_YEAR: i32 = 999_999;
/// The minimum valid year.
#[cfg(not(feature = "large-dates"))]
pub(crate) const MIN_YEAR: i32 = -9999;
/// The maximum valid year.
#[cfg(not(feature = "large-dates"))]
pub(crate) const MAX_YEAR: i32 = 9999;
pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") {
999_999
} else {
9999
};
/// Date in the proleptic Gregorian calendar.
///
@ -39,16 +38,7 @@ pub struct Date {
// | 2 bits | 21 bits | 9 bits |
// | unassigned | year | ordinal |
// The year is 15 bits when `large-dates` is not enabled.
pub(crate) value: i32,
}
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Date")
.field("year", &self.year())
.field("ordinal", &self.ordinal())
.finish()
}
value: i32,
}
impl Date {
@ -67,6 +57,11 @@ impl Date {
/// guaranteed by the caller.
#[doc(hidden)]
pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self {
debug_assert!(year >= MIN_YEAR);
debug_assert!(year <= MAX_YEAR);
debug_assert!(ordinal != 0);
debug_assert!(ordinal <= days_in_year(year));
Self {
value: (year << 9) | ordinal as i32,
}
@ -176,7 +171,8 @@ impl Date {
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time::{Date, macros::date};
/// # use time::Date;
/// # use time_macros::date;
/// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24)));
/// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01)));
/// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01)));
@ -196,24 +192,27 @@ impl Date {
/// internally invalid value.
#[doc(alias = "from_julian_date_unchecked")]
pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self {
#![allow(trivial_numeric_casts)] // cast depends on type alias
/// A type that is either `i32` or `i64`. This subtle difference allows for optimization
/// based on the valid values.
#[cfg(feature = "large-dates")]
type MaybeWidened = i64;
#[allow(clippy::missing_docs_in_private_items)]
#[cfg(not(feature = "large-dates"))]
type MaybeWidened = i32;
debug_assert!(julian_day >= Self::MIN.to_julian_day());
debug_assert!(julian_day <= Self::MAX.to_julian_day());
// To avoid a potential overflow, the value may need to be widened for some arithmetic.
let z = julian_day - 1_721_119;
let g = 100 * z as MaybeWidened - 25;
let a = (g / 3_652_425) as i32;
let b = a - a / 4;
let mut year = div_floor!(100 * b as MaybeWidened + g, 36525) as i32;
let mut ordinal = (b + z - div_floor!(36525 * year as MaybeWidened, 100) as i32) as _;
let (mut year, mut ordinal) = if julian_day < -19_752_948 || julian_day > 23_195_514 {
let g = 100 * z as i64 - 25;
let a = (g / 3_652_425) as i32;
let b = a - a / 4;
let year = div_floor!(100 * b as i64 + g, 36525) as i32;
let ordinal = (b + z - div_floor!(36525 * year as i64, 100) as i32) as _;
(year, ordinal)
} else {
let g = 100 * z - 25;
let a = g / 3_652_425;
let b = a - a / 4;
let year = div_floor!(100 * b + g, 36525);
let ordinal = (b + z - div_floor!(36525 * year, 100)) as _;
(year, ordinal)
};
if is_leap_year(year) {
ordinal += 60;
@ -231,7 +230,7 @@ impl Date {
/// Get the year of the date.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).year(), 2019);
/// assert_eq!(date!(2019 - 12 - 31).year(), 2019);
/// assert_eq!(date!(2020 - 01 - 01).year(), 2020);
@ -243,7 +242,8 @@ impl Date {
/// Get the month.
///
/// ```rust
/// # use time::{macros::date, Month};
/// # use time::Month;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).month(), Month::January);
/// assert_eq!(date!(2019 - 12 - 31).month(), Month::December);
/// ```
@ -256,7 +256,7 @@ impl Date {
/// The returned value will always be in the range `1..=31`.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).day(), 1);
/// assert_eq!(date!(2019 - 12 - 31).day(), 31);
/// ```
@ -310,7 +310,7 @@ impl Date {
/// The returned value will always be in the range `1..=366` (`1..=365` for common years).
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1);
/// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365);
/// ```
@ -334,7 +334,7 @@ impl Date {
/// The returned value will always be in the range `1..=53`.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).iso_week(), 1);
/// assert_eq!(date!(2019 - 10 - 04).iso_week(), 40);
/// assert_eq!(date!(2020 - 01 - 01).iso_week(), 1);
@ -350,7 +350,7 @@ impl Date {
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).sunday_based_week(), 0);
/// assert_eq!(date!(2020 - 01 - 01).sunday_based_week(), 0);
/// assert_eq!(date!(2020 - 12 - 31).sunday_based_week(), 52);
@ -365,7 +365,7 @@ impl Date {
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).monday_based_week(), 0);
/// assert_eq!(date!(2020 - 01 - 01).monday_based_week(), 0);
/// assert_eq!(date!(2020 - 12 - 31).monday_based_week(), 52);
@ -378,7 +378,8 @@ impl Date {
/// Get the year, month, and day.
///
/// ```rust
/// # use time::{macros::date, Month};
/// # use time::Month;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2019 - 01 - 01).to_calendar_date(),
/// (2019, Month::January, 1)
@ -392,7 +393,7 @@ impl Date {
/// Get the year and ordinal day number.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).to_ordinal_date(), (2019, 1));
/// ```
pub const fn to_ordinal_date(self) -> (i32, u16) {
@ -402,7 +403,8 @@ impl Date {
/// Get the ISO 8601 year, week number, and weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::date};
/// # use time::Weekday::*;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).to_iso_week_date(), (2019, 1, Tuesday));
/// assert_eq!(date!(2019 - 10 - 04).to_iso_week_date(), (2019, 40, Friday));
/// assert_eq!(
@ -429,7 +431,8 @@ impl Date {
/// Get the weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::date};
/// # use time::Weekday::*;
/// # use time_macros::date;
/// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday);
/// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday);
/// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday);
@ -451,14 +454,18 @@ impl Date {
-3 | 4 => Weekday::Friday,
-2 | 5 => Weekday::Saturday,
-1 | 6 => Weekday::Sunday,
_ => Weekday::Monday,
val => {
debug_assert!(val == 0);
Weekday::Monday
}
}
}
/// Get the next calendar date.
///
/// ```rust
/// # use time::{Date, macros::date};
/// # use time::Date;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2019 - 01 - 01).next_day(),
/// Some(date!(2019 - 01 - 02))
@ -490,7 +497,8 @@ impl Date {
/// Get the previous calendar date.
///
/// ```rust
/// # use time::{Date, macros::date};
/// # use time::Date;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2019 - 01 - 02).previous_day(),
/// Some(date!(2019 - 01 - 01))
@ -526,7 +534,7 @@ impl Date {
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0);
/// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545);
/// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485);
@ -546,7 +554,8 @@ impl Date {
/// Computes `self + duration`, returning `None` if an overflow occurred.
///
/// ```rust
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.checked_add(1.days()), None);
/// assert_eq!(Date::MIN.checked_add((-2).days()), None);
/// assert_eq!(
@ -560,7 +569,8 @@ impl Date {
/// This function only takes whole days into account.
///
/// ```rust
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX));
/// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN));
/// assert_eq!(
@ -589,7 +599,8 @@ impl Date {
/// Computes `self - duration`, returning `None` if an overflow occurred.
///
/// ```
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.checked_sub((-2).days()), None);
/// assert_eq!(Date::MIN.checked_sub(1.days()), None);
/// assert_eq!(
@ -603,7 +614,8 @@ impl Date {
/// This function only takes whole days into account.
///
/// ```
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX));
/// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN));
/// assert_eq!(
@ -634,7 +646,8 @@ impl Date {
/// Computes `self + duration`, saturating value on overflow.
///
/// ```rust
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX);
/// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN);
/// assert_eq!(
@ -648,7 +661,8 @@ impl Date {
/// This function only takes whole days into account.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::date};
/// # use time::ext::NumericalDuration;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2020 - 12 - 31).saturating_add(23.hours()),
/// date!(2020 - 12 - 31)
@ -664,6 +678,7 @@ impl Date {
} else if duration.is_negative() {
Self::MIN
} else {
debug_assert!(duration.is_positive());
Self::MAX
}
}
@ -671,7 +686,8 @@ impl Date {
/// Computes `self - duration`, saturating value on overflow.
///
/// ```
/// # use time::{Date, ext::NumericalDuration, macros::date};
/// # use time::{Date, ext::NumericalDuration};
/// # use time_macros::date;
/// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX);
/// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN);
/// assert_eq!(
@ -685,7 +701,8 @@ impl Date {
/// This function only takes whole days into account.
///
/// ```
/// # use time::{ext::NumericalDuration, macros::date};
/// # use time::ext::NumericalDuration;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2020 - 12 - 31).saturating_sub(23.hours()),
/// date!(2020 - 12 - 31)
@ -701,6 +718,7 @@ impl Date {
} else if duration.is_negative() {
Self::MAX
} else {
debug_assert!(duration.is_positive());
Self::MIN
}
}
@ -710,7 +728,7 @@ impl Date {
/// Replace the year. The month and day will be unchanged.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_year(2019),
/// Ok(date!(2019 - 02 - 18))
@ -726,15 +744,11 @@ impl Date {
// Dates in January and February are unaffected by leap years.
if ordinal <= 59 {
return Ok(Self {
value: year << 9 | ordinal as i32,
});
return Ok(Self::__from_ordinal_date_unchecked(year, ordinal));
}
match (is_leap_year(self.year()), is_leap_year(year)) {
(false, false) | (true, true) => Ok(Self {
value: year << 9 | ordinal as i32,
}),
(false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)),
// February 29 does not exist in common years.
(true, false) if ordinal == 60 => Err(error::ComponentRange {
name: "day",
@ -745,21 +759,17 @@ impl Date {
}),
// We're going from a common year to a leap year. Shift dates in March and later by
// one day.
(false, true) => Ok(Self {
value: year << 9 | (ordinal + 1) as i32,
}),
(false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)),
// We're going from a leap year to a common year. Shift dates in January and
// February by one day.
(true, false) => Ok(Self {
value: year << 9 | (ordinal - 1) as i32,
}),
(true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)),
}
}
/// Replace the month of the year.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// # use time::Month;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_month(Month::January),
@ -780,7 +790,7 @@ impl Date {
/// Replace the day of the month.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert_eq!(
/// date!(2022 - 02 - 18).replace_day(1),
/// Ok(date!(2022 - 02 - 01))
@ -812,7 +822,7 @@ impl Date {
/// to midnight.
///
/// ```rust
/// # use time::macros::{date, datetime};
/// # use time_macros::{date, datetime};
/// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00));
/// ```
pub const fn midnight(self) -> PrimitiveDateTime {
@ -822,7 +832,7 @@ impl Date {
/// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`].
///
/// ```rust
/// # use time::macros::{date, datetime, time};
/// # use time_macros::{date, datetime, time};
/// assert_eq!(
/// date!(1970-01-01).with_time(time!(0:00)),
/// datetime!(1970-01-01 0:00),
@ -835,7 +845,7 @@ impl Date {
/// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok());
/// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err());
/// ```
@ -854,7 +864,7 @@ impl Date {
/// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok());
/// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err());
/// ```
@ -874,7 +884,7 @@ impl Date {
/// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok());
/// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err());
/// ```
@ -894,7 +904,7 @@ impl Date {
/// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time.
///
/// ```rust
/// # use time::macros::date;
/// # use time_macros::date;
/// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok());
/// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err());
/// ```
@ -928,7 +938,8 @@ impl Date {
/// Format the `Date` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::date};
/// # use time::{format_description};
/// # use time_macros::date;
/// let format = format_description::parse("[year]-[month]-[day]")?;
/// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02");
/// # Ok::<_, time::Error>(())
@ -944,8 +955,9 @@ impl Date {
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::date, Date};
/// let format = format_description::parse("[year]-[month]-[day]")?;
/// # use time::Date;
/// # use time_macros::{date, format_description};
/// let format = format_description!("[year]-[month]-[day]");
/// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02));
/// # Ok::<_, time::Error>(())
/// ```
@ -979,6 +991,12 @@ impl fmt::Display for Date {
}
}
}
impl fmt::Debug for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls

168
third_party/rust/time/src/duration.rs поставляемый
Просмотреть файл

@ -1,7 +1,6 @@
//! The [`Duration`] struct and its associated `impl`s.
use core::cmp::Ordering;
use core::convert::{TryFrom, TryInto};
use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
@ -184,11 +183,16 @@ impl Duration {
Self::new_unchecked(self.seconds.saturating_abs(), self.nanoseconds.abs())
}
/// Convert the existing `Duration` to a `std::time::Duration` and its sign. This doesn't
/// actually require the standard library, but is currently only used when it's enabled.
#[allow(clippy::missing_const_for_fn)] // false positive
#[cfg(feature = "std")]
pub(crate) fn abs_std(self) -> StdDuration {
/// Convert the existing `Duration` to a `std::time::Duration` and its sign. This returns a
/// [`std::time::Duration`] and does not saturate the returned value (unlike [`Duration::abs`]).
///
/// ```rust
/// # use time::ext::{NumericalDuration, NumericalStdDuration};
/// assert_eq!(1.seconds().unsigned_abs(), 1.std_seconds());
/// assert_eq!(0.seconds().unsigned_abs(), 0.std_seconds());
/// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds());
/// ```
pub const fn unsigned_abs(self) -> StdDuration {
StdDuration::new(self.seconds.unsigned_abs(), self.nanoseconds.unsigned_abs())
}
// endregion abs
@ -196,6 +200,16 @@ impl Duration {
// region: constructors
/// Create a new `Duration` without checking the validity of the components.
pub(crate) const fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self {
if seconds < 0 {
debug_assert!(nanoseconds <= 0);
debug_assert!(nanoseconds > -1_000_000_000);
} else if seconds > 0 {
debug_assert!(nanoseconds >= 0);
debug_assert!(nanoseconds < 1_000_000_000);
} else {
debug_assert!(nanoseconds.unsigned_abs() < 1_000_000_000);
}
Self {
seconds,
nanoseconds,
@ -213,13 +227,18 @@ impl Duration {
/// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds());
/// ```
pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self {
seconds += nanoseconds as i64 / 1_000_000_000;
seconds = expect_opt!(
seconds.checked_add(nanoseconds as i64 / 1_000_000_000),
"overflow constructing `time::Duration`"
);
nanoseconds %= 1_000_000_000;
if seconds > 0 && nanoseconds < 0 {
// `seconds` cannot overflow here because it is positive.
seconds -= 1;
nanoseconds += 1_000_000_000;
} else if seconds < 0 && nanoseconds > 0 {
// `seconds` cannot overflow here because it is negative.
seconds += 1;
nanoseconds -= 1_000_000_000;
}
@ -235,7 +254,10 @@ impl Duration {
/// assert_eq!(Duration::weeks(1), 604_800.seconds());
/// ```
pub const fn weeks(weeks: i64) -> Self {
Self::seconds(weeks * 604_800)
Self::seconds(expect_opt!(
weeks.checked_mul(604_800),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of days. Equivalent to
@ -246,7 +268,10 @@ impl Duration {
/// assert_eq!(Duration::days(1), 86_400.seconds());
/// ```
pub const fn days(days: i64) -> Self {
Self::seconds(days * 86_400)
Self::seconds(expect_opt!(
days.checked_mul(86_400),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of hours. Equivalent to
@ -257,7 +282,10 @@ impl Duration {
/// assert_eq!(Duration::hours(1), 3_600.seconds());
/// ```
pub const fn hours(hours: i64) -> Self {
Self::seconds(hours * 3_600)
Self::seconds(expect_opt!(
hours.checked_mul(3_600),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of minutes. Equivalent to
@ -268,7 +296,10 @@ impl Duration {
/// assert_eq!(Duration::minutes(1), 60.seconds());
/// ```
pub const fn minutes(minutes: i64) -> Self {
Self::seconds(minutes * 60)
Self::seconds(expect_opt!(
minutes.checked_mul(60),
"overflow constructing `time::Duration`"
))
}
/// Create a new `Duration` with the given number of seconds.
@ -289,6 +320,12 @@ impl Duration {
/// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds());
/// ```
pub fn seconds_f64(seconds: f64) -> Self {
if seconds > i64::MAX as f64 || seconds < i64::MIN as f64 {
crate::expect_failed("overflow constructing `time::Duration`");
}
if seconds.is_nan() {
crate::expect_failed("passed NaN to `time::Duration::seconds_f64`");
}
Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _)
}
@ -300,6 +337,12 @@ impl Duration {
/// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds());
/// ```
pub fn seconds_f32(seconds: f32) -> Self {
if seconds > i64::MAX as f32 || seconds < i64::MIN as f32 {
crate::expect_failed("overflow constructing `time::Duration`");
}
if seconds.is_nan() {
crate::expect_failed("passed NaN to `time::Duration::seconds_f32`");
}
Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _)
}
@ -350,10 +393,14 @@ impl Duration {
/// As the input range cannot be fully mapped to the output, this should only be used where it's
/// known to result in a valid value.
pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self {
Self::new_unchecked(
(nanoseconds / 1_000_000_000) as _,
(nanoseconds % 1_000_000_000) as _,
)
let seconds = nanoseconds / 1_000_000_000;
let nanoseconds = nanoseconds % 1_000_000_000;
if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 {
crate::expect_failed("overflow constructing `time::Duration`");
}
Self::new_unchecked(seconds as _, nanoseconds as _)
}
// endregion constructors
@ -742,38 +789,81 @@ impl Duration {
// region: trait impls
/// The format returned by this implementation is not stable and must not be relied upon.
///
/// By default this produces an exact, full-precision printout of the duration.
/// For a concise, rounded printout instead, you can use the `.N` format specifier:
///
/// ```
/// # use time::Duration;
/// #
/// let duration = Duration::new(123456, 789011223);
/// println!("{duration:.3}");
/// ```
///
/// For the purposes of this implementation, a day is exactly 24 hours and a minute is exactly 60
/// seconds.
impl fmt::Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// Format a single item.
macro_rules! item {
($name:literal, $value:expr) => {
match $value {
0 => Ok(()),
value => value.fmt(f).and(f.write_str($name)),
}
};
}
if self.is_zero() {
return f.write_str("0s");
}
let seconds = self.seconds.unsigned_abs();
let nanoseconds = self.nanoseconds.unsigned_abs();
if self.is_negative() {
f.write_str("-")?;
}
item!("d", seconds / 86_400)?;
item!("h", seconds / 3_600 % 24)?;
item!("m", seconds / 60 % 60)?;
item!("s", seconds % 60)?;
item!("ms", nanoseconds / 1_000_000)?;
item!("µs", nanoseconds / 1_000 % 1_000)?;
item!("ns", nanoseconds % 1_000)?;
if let Some(_precision) = f.precision() {
// Concise, rounded representation.
if self.is_zero() {
// Write a zero value with the requested precision.
return (0.).fmt(f).and_then(|_| f.write_str("s"));
}
/// Format the first item that produces a value greater than 1 and then break.
macro_rules! item {
($name:literal, $value:expr) => {
let value = $value;
if value >= 1.0 {
return value.fmt(f).and_then(|_| f.write_str($name));
}
};
}
// Even if this produces a de-normal float, because we're rounding we don't really care.
let seconds = self.unsigned_abs().as_secs_f64();
item!("d", seconds / 86_400.);
item!("h", seconds / 3_600.);
item!("m", seconds / 60.);
item!("s", seconds);
item!("ms", seconds * 1_000.);
item!("µs", seconds * 1_000_000.);
item!("ns", seconds * 1_000_000_000.);
} else {
// Precise, but verbose representation.
if self.is_zero() {
return f.write_str("0s");
}
/// Format a single item.
macro_rules! item {
($name:literal, $value:expr) => {
match $value {
0 => Ok(()),
value => value.fmt(f).and_then(|_| f.write_str($name)),
}
};
}
let seconds = self.seconds.unsigned_abs();
let nanoseconds = self.nanoseconds.unsigned_abs();
item!("d", seconds / 86_400)?;
item!("h", seconds / 3_600 % 24)?;
item!("m", seconds / 60 % 60)?;
item!("s", seconds % 60)?;
item!("ms", nanoseconds / 1_000_000)?;
item!("µs", nanoseconds / 1_000 % 1_000)?;
item!("ns", nanoseconds % 1_000)?;
}
Ok(())
}
}

Просмотреть файл

@ -1,6 +1,5 @@
//! Component range error
use core::convert::TryFrom;
use core::fmt;
use crate::error;

Просмотреть файл

@ -1,6 +1,5 @@
//! Conversion range error
use core::convert::TryFrom;
use core::fmt;
use crate::error;

Просмотреть файл

@ -1,6 +1,5 @@
//! Different variant error
use core::convert::TryFrom;
use core::fmt;
/// An error type indicating that a [`TryFrom`](core::convert::TryFrom) call failed because the

9
third_party/rust/time/src/error/format.rs поставляемый
Просмотреть файл

@ -1,6 +1,5 @@
//! Error formatting a struct
use core::convert::TryFrom;
use core::fmt;
use std::io;
@ -9,7 +8,6 @@ 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.
@ -32,8 +30,7 @@ impl fmt::Display for Format {
),
Self::InvalidComponent(component) => write!(
f,
"The {} component cannot be formatted into the requested format.",
component
"The {component} component cannot be formatted into the requested format."
),
Self::StdIo(err) => err.fmt(f),
}
@ -67,14 +64,12 @@ impl std::error::Error for Format {
}
}
#[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;
@ -92,6 +87,6 @@ impl Format {
#[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))
S::Error::custom(self)
}
}

Просмотреть файл

@ -1,12 +1,10 @@
//! 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;
@ -19,14 +17,12 @@ impl fmt::Display for IndeterminateOffset {
#[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;

Просмотреть файл

@ -1,16 +1,11 @@
//! 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 {
@ -44,20 +39,12 @@ pub enum InvalidFormatDescription {
},
}
#[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;
@ -74,18 +61,16 @@ impl fmt::Display for InvalidFormatDescription {
use InvalidFormatDescription::*;
match self {
UnclosedOpeningBracket { index } => {
write!(f, "unclosed opening bracket at byte index {}", index)
write!(f, "unclosed opening bracket at byte index {index}")
}
InvalidComponentName { name, index } => {
write!(f, "invalid component name `{name}` 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)
write!(f, "invalid modifier `{value}` at byte index {index}")
}
MissingComponentName { index } => {
write!(f, "missing component name at byte index {}", index)
write!(f, "missing component name at byte index {index}")
}
}
}

Просмотреть файл

@ -1,6 +1,5 @@
//! 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

8
third_party/rust/time/src/error/parse.rs поставляемый
Просмотреть файл

@ -1,12 +1,10 @@
//! 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)]
@ -41,14 +39,12 @@ impl std::error::Error for Parse {
}
}
#[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;
@ -60,14 +56,12 @@ impl TryFrom<Parse> for TryFromParsed {
}
}
#[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;
@ -79,7 +73,6 @@ impl TryFrom<Parse> for ParseFromDescription {
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl From<Parse> for crate::Error {
fn from(err: Parse) -> Self {
match err {
@ -90,7 +83,6 @@ impl From<Parse> for crate::Error {
}
}
#[cfg_attr(__time_03_docs, doc(cfg(feature = "parsing")))]
impl TryFrom<crate::Error> for Parse {
type Error = error::DifferentVariant;

Просмотреть файл

@ -1,12 +1,10 @@
//! 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 {
@ -22,7 +20,7 @@ impl fmt::Display for ParseFromDescription {
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)
write!(f, "the '{name}' component could not be parsed")
}
}
}
@ -31,14 +29,12 @@ impl fmt::Display for ParseFromDescription {
#[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;

Просмотреть файл

@ -1,13 +1,11 @@
//! 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
@ -55,14 +53,12 @@ impl std::error::Error for TryFromParsed {
}
}
#[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;

Просмотреть файл

@ -0,0 +1,106 @@
//! A format item with borrowed data.
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use core::fmt;
use crate::error;
use crate::format_description::Component;
/// 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 BorrowedFormatItem<'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 BorrowedFormatItem<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
Self::Component(component) => component.fmt(f),
Self::Compound(compound) => compound.fmt(f),
Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
Self::First(items) => f.debug_tuple("First").field(items).finish(),
}
}
}
impl From<Component> for BorrowedFormatItem<'_> {
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<BorrowedFormatItem<'_>> for Component {
type Error = error::DifferentVariant;
fn try_from(value: BorrowedFormatItem<'_>) -> Result<Self, Self::Error> {
match value {
BorrowedFormatItem::Component(component) => Ok(component),
_ => Err(error::DifferentVariant),
}
}
}
impl<'a> From<&'a [BorrowedFormatItem<'_>]> for BorrowedFormatItem<'a> {
fn from(items: &'a [BorrowedFormatItem<'_>]) -> Self {
Self::Compound(items)
}
}
impl<'a> TryFrom<BorrowedFormatItem<'a>> for &[BorrowedFormatItem<'a>] {
type Error = error::DifferentVariant;
fn try_from(value: BorrowedFormatItem<'a>) -> Result<Self, Self::Error> {
match value {
BorrowedFormatItem::Compound(items) => Ok(items),
_ => Err(error::DifferentVariant),
}
}
}
impl PartialEq<Component> for BorrowedFormatItem<'_> {
fn eq(&self, rhs: &Component) -> bool {
matches!(self, Self::Component(component) if component == rhs)
}
}
impl PartialEq<BorrowedFormatItem<'_>> for Component {
fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool {
rhs == self
}
}
impl PartialEq<&[Self]> for BorrowedFormatItem<'_> {
fn eq(&self, rhs: &&[Self]) -> bool {
matches!(self, Self::Compound(compound) if compound == rhs)
}
}
impl PartialEq<BorrowedFormatItem<'_>> for &[BorrowedFormatItem<'_>] {
fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool {
rhs == self
}
}

Просмотреть файл

@ -1,11 +1,6 @@
//! 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]
@ -40,128 +35,3 @@ pub enum Component {
/// 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(),
}),
}
}
}

Просмотреть файл

@ -5,182 +5,30 @@
//! [`format_description!`](crate::macros::format_description) macro, the
//! [`format_description::parse`](crate::format_description::parse()) function.
mod borrowed_format_item;
mod component;
pub mod modifier;
#[cfg(feature = "alloc")]
pub(crate) mod parse;
mod owned_format_item;
#[cfg(feature = "alloc")]
mod parse;
pub use borrowed_format_item::BorrowedFormatItem as FormatItem;
#[cfg(feature = "alloc")]
use alloc::string::String;
use core::convert::TryFrom;
#[cfg(feature = "alloc")]
use core::fmt;
pub use owned_format_item::OwnedFormatItem;
pub use self::component::Component;
#[cfg(feature = "alloc")]
pub use self::parse::parse;
use crate::error;
pub use self::parse::{parse, parse_owned};
/// 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.
/// Well-known formats, typically standards.
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;
pub mod iso8601;
mod rfc2822;
mod 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
}
#[doc(inline)]
pub use iso8601::Iso8601;
pub use rfc2822::Rfc2822;
pub use rfc3339::Rfc3339;
}

Просмотреть файл

@ -1,13 +1,5 @@
//! 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]
@ -361,149 +353,3 @@ impl_const_default! {
/// 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)
}
}

Просмотреть файл

@ -0,0 +1,162 @@
//! A format item with owned data.
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use crate::error;
use crate::format_description::{Component, FormatItem};
/// A complete description of how to format and parse a type.
#[non_exhaustive]
#[derive(Clone, PartialEq, Eq)]
pub enum OwnedFormatItem {
/// 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(Box<[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(Box<[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(Box<Self>),
/// A series of `FormatItem`s where, when parsing, the first successful parse is used. When
/// formatting, the first element of the [`Vec`] is used. An empty [`Vec`] is a no-op when
/// formatting or parsing.
First(Box<[Self]>),
}
impl fmt::Debug for OwnedFormatItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)),
Self::Component(component) => component.fmt(f),
Self::Compound(compound) => compound.fmt(f),
Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(),
Self::First(items) => f.debug_tuple("First").field(items).finish(),
}
}
}
// region: conversions from FormatItem
impl From<FormatItem<'_>> for OwnedFormatItem {
fn from(item: FormatItem<'_>) -> Self {
(&item).into()
}
}
impl From<&FormatItem<'_>> for OwnedFormatItem {
fn from(item: &FormatItem<'_>) -> Self {
match item {
FormatItem::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
FormatItem::Component(component) => Self::Component(*component),
FormatItem::Compound(compound) => Self::Compound(
compound
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
FormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
FormatItem::First(items) => Self::First(
items
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
}
}
}
impl From<Vec<FormatItem<'_>>> for OwnedFormatItem {
fn from(items: Vec<FormatItem<'_>>) -> Self {
items.as_slice().into()
}
}
impl<'a, T: AsRef<[FormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem {
fn from(items: &T) -> Self {
Self::Compound(
items
.as_ref()
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
)
}
}
// endregion conversions from FormatItem
// region: from variants
impl From<Component> for OwnedFormatItem {
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<OwnedFormatItem> for Component {
type Error = error::DifferentVariant;
fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
match value {
OwnedFormatItem::Component(component) => Ok(component),
_ => Err(error::DifferentVariant),
}
}
}
impl From<Vec<Self>> for OwnedFormatItem {
fn from(items: Vec<Self>) -> Self {
Self::Compound(items.into_boxed_slice())
}
}
impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
type Error = error::DifferentVariant;
fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
match value {
OwnedFormatItem::Compound(items) => Ok(items.into_vec()),
_ => Err(error::DifferentVariant),
}
}
}
// endregion from variants
// region: equality
impl PartialEq<Component> for OwnedFormatItem {
fn eq(&self, rhs: &Component) -> bool {
matches!(self, Self::Component(component) if component == rhs)
}
}
impl PartialEq<OwnedFormatItem> for Component {
fn eq(&self, rhs: &OwnedFormatItem) -> bool {
rhs == self
}
}
impl PartialEq<&[Self]> for OwnedFormatItem {
fn eq(&self, rhs: &&[Self]) -> bool {
matches!(self, Self::Compound(compound) if &&**compound == rhs)
}
}
impl PartialEq<OwnedFormatItem> for &[OwnedFormatItem] {
fn eq(&self, rhs: &OwnedFormatItem) -> bool {
rhs == self
}
}
// endregion equality

Просмотреть файл

@ -1,97 +0,0 @@
//! 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)
}

278
third_party/rust/time/src/format_description/parse/ast.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,278 @@
//! AST for parsing format descriptions.
use alloc::string::String;
use alloc::vec::Vec;
use core::iter;
use core::iter::Peekable;
use super::{lexer, Error, Location, Span};
/// One part of a complete format description.
#[allow(variant_size_differences)]
pub(super) enum Item<'a> {
/// A literal string, formatted and parsed as-is.
Literal {
/// The string itself.
value: &'a [u8],
/// Where the string originates from in the format string.
_span: Span,
},
/// A sequence of brackets. The first acts as the escape character.
EscapedBracket {
/// The first bracket.
_first: Location,
/// The second bracket.
_second: Location,
},
/// Part of a type, along with its modifiers.
Component {
/// Where the opening bracket was in the format string.
_opening_bracket: Location,
/// Whitespace between the opening bracket and name.
_leading_whitespace: Option<Whitespace<'a>>,
/// The name of the component.
name: Name<'a>,
/// The modifiers for the component.
modifiers: Vec<Modifier<'a>>,
/// Whitespace between the modifiers and closing bracket.
_trailing_whitespace: Option<Whitespace<'a>>,
/// Where the closing bracket was in the format string.
_closing_bracket: Location,
},
}
/// Whitespace within a component.
pub(super) struct Whitespace<'a> {
/// The whitespace itself.
pub(super) _value: &'a [u8],
/// Where the whitespace was in the format string.
pub(super) span: Span,
}
/// The name of a component.
pub(super) struct Name<'a> {
/// The name itself.
pub(super) value: &'a [u8],
/// Where the name was in the format string.
pub(super) span: Span,
}
/// A modifier for a component.
pub(super) struct Modifier<'a> {
/// Whitespace preceding the modifier.
pub(super) _leading_whitespace: Whitespace<'a>,
/// The key of the modifier.
pub(super) key: Key<'a>,
/// Where the colon of the modifier was in the format string.
pub(super) _colon: Location,
/// The value of the modifier.
pub(super) value: Value<'a>,
}
/// The key of a modifier.
pub(super) struct Key<'a> {
/// The key itself.
pub(super) value: &'a [u8],
/// Where the key was in the format string.
pub(super) span: Span,
}
/// The value of a modifier.
pub(super) struct Value<'a> {
/// The value itself.
pub(super) value: &'a [u8],
/// Where the value was in the format string.
pub(super) span: Span,
}
/// Parse the provided tokens into an AST.
pub(super) fn parse<'a>(
tokens: impl Iterator<Item = lexer::Token<'a>>,
) -> impl Iterator<Item = Result<Item<'a>, Error>> {
let mut tokens = tokens.peekable();
iter::from_fn(move || {
Some(match tokens.next()? {
lexer::Token::Literal { value, span } => Ok(Item::Literal { value, _span: span }),
lexer::Token::Bracket {
kind: lexer::BracketKind::Opening,
location,
} => {
// escaped bracket
if let Some(&lexer::Token::Bracket {
kind: lexer::BracketKind::Opening,
location: second_location,
}) = tokens.peek()
{
tokens.next(); // consume
Ok(Item::EscapedBracket {
_first: location,
_second: second_location,
})
}
// component
else {
parse_component(location, &mut tokens)
}
}
lexer::Token::Bracket {
kind: lexer::BracketKind::Closing,
location: _,
} => unreachable!(
"internal error: closing bracket should have been consumed by `parse_component`",
),
lexer::Token::ComponentPart {
kind: _,
value: _,
span: _,
} => unreachable!(
"internal error: component part should have been consumed by `parse_component`",
),
})
})
}
/// Parse a component. This assumes that the opening bracket has already been consumed.
fn parse_component<'a>(
opening_bracket: Location,
tokens: &mut Peekable<impl Iterator<Item = lexer::Token<'a>>>,
) -> Result<Item<'a>, Error> {
let leading_whitespace = if let Some(&lexer::Token::ComponentPart {
kind: lexer::ComponentKind::Whitespace,
value,
span,
}) = tokens.peek()
{
tokens.next(); // consume
Some(Whitespace {
_value: value,
span,
})
} else {
None
};
let name = if let Some(&lexer::Token::ComponentPart {
kind: lexer::ComponentKind::NotWhitespace,
value,
span,
}) = tokens.peek()
{
tokens.next(); // consume
Name { value, span }
} else {
let span = leading_whitespace.map_or_else(
|| Span {
start: opening_bracket,
end: opening_bracket,
},
|whitespace| whitespace.span.shrink_to_end(),
);
return Err(Error {
_inner: span.error("expected component name"),
public: crate::error::InvalidFormatDescription::MissingComponentName {
index: span.start_byte(),
},
});
};
let mut modifiers = Vec::new();
let trailing_whitespace = loop {
let whitespace = if let Some(&lexer::Token::ComponentPart {
kind: lexer::ComponentKind::Whitespace,
value,
span,
}) = tokens.peek()
{
tokens.next(); // consume
Whitespace {
_value: value,
span,
}
} else {
break None;
};
if let Some(&lexer::Token::ComponentPart {
kind: lexer::ComponentKind::NotWhitespace,
value,
span,
}) = tokens.peek()
{
tokens.next(); // consume
let colon_index = match value.iter().position(|&b| b == b':') {
Some(index) => index,
None => {
return Err(Error {
_inner: span.error("modifier must be of the form `key:value`"),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(value).into_owned(),
index: span.start_byte(),
},
});
}
};
let key = &value[..colon_index];
let value = &value[colon_index + 1..];
if key.is_empty() {
return Err(Error {
_inner: span.shrink_to_start().error("expected modifier key"),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::new(),
index: span.start_byte(),
},
});
}
if value.is_empty() {
return Err(Error {
_inner: span.shrink_to_end().error("expected modifier value"),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::new(),
index: span.shrink_to_end().start_byte(),
},
});
}
modifiers.push(Modifier {
_leading_whitespace: whitespace,
key: Key {
value: key,
span: span.subspan(..colon_index),
},
_colon: span.start.offset(colon_index),
value: Value {
value,
span: span.subspan(colon_index + 1..),
},
});
} else {
break Some(whitespace);
}
};
let closing_bracket = if let Some(&lexer::Token::Bracket {
kind: lexer::BracketKind::Closing,
location,
}) = tokens.peek()
{
tokens.next(); // consume
location
} else {
return Err(Error {
_inner: opening_bracket.error("unclosed bracket"),
public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
index: opening_bracket.byte,
},
});
};
Ok(Item::Component {
_opening_bracket: opening_bracket,
_leading_whitespace: leading_whitespace,
name,
modifiers,
_trailing_whitespace: trailing_whitespace,
_closing_bracket: closing_bracket,
})
}

Просмотреть файл

@ -0,0 +1,386 @@
//! Typed, validated representation of a parsed format description.
use alloc::string::String;
use super::{ast, Error};
/// Parse an AST iterator into a sequence of format items.
pub(super) fn parse<'a>(
ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>,
) -> impl Iterator<Item = Result<Item<'a>, Error>> {
ast_items.map(|ast_item| ast_item.and_then(Item::from_ast))
}
/// A description of how to format and parse one part of a type.
#[allow(variant_size_differences)]
pub(super) enum Item<'a> {
/// A literal string.
Literal(&'a [u8]),
/// Part of a type, along with its modifiers.
Component(Component),
}
impl Item<'_> {
/// Parse an AST item into a format item.
pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> {
Ok(match ast_item {
ast::Item::Component {
_opening_bracket: _,
_leading_whitespace: _,
name,
modifiers,
_trailing_whitespace: _,
_closing_bracket: _,
} => Item::Component(component_from_ast(&name, &modifiers)?),
ast::Item::Literal { value, _span: _ } => Item::Literal(value),
ast::Item::EscapedBracket {
_first: _,
_second: _,
} => Item::Literal(b"["),
})
}
}
impl<'a> From<Item<'a>> for crate::format_description::FormatItem<'a> {
fn from(item: Item<'a>) -> Self {
match item {
Item::Literal(literal) => Self::Literal(literal),
Item::Component(component) => Self::Component(component.into()),
}
}
}
impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
fn from(item: Item<'_>) -> Self {
match item {
Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()),
Item::Component(component) => Self::Component(component.into()),
}
}
}
/// Declare the `Component` struct.
macro_rules! component_definition {
($vis:vis enum $name:ident {
$($variant:ident = $parse_variant:literal {
$($field:ident = $parse_field:literal:
Option<$field_type:ty> => $target_field:ident),* $(,)?
}),* $(,)?
}) => {
$vis enum $name {
$($variant($variant),)*
}
$($vis struct $variant {
$($field: Option<$field_type>),*
})*
$(impl $variant {
/// Parse the component from the AST, given its modifiers.
fn with_modifiers(modifiers: &[ast::Modifier<'_>]) -> Result<Self, Error> {
let mut this = Self {
$($field: None),*
};
for modifier in modifiers {
$(if modifier.key.value.eq_ignore_ascii_case($parse_field) {
this.$field = <$field_type>::from_modifier_value(&modifier.value)?;
continue;
})*
return Err(Error {
_inner: modifier.key.span.error("invalid modifier key"),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(modifier.key.value).into_owned(),
index: modifier.key.span.start_byte(),
}
});
}
Ok(this)
}
})*
impl From<$name> for crate::format_description::Component {
fn from(component: $name) -> Self {
match component {$(
$name::$variant($variant { $($field),* }) => {
$crate::format_description::component::Component::$variant(
$crate::format_description::modifier::$variant {$(
$target_field: $field.unwrap_or_default().into()
),*}
)
}
)*}
}
}
/// Parse a component from the AST, given its name and modifiers.
fn component_from_ast(
name: &ast::Name<'_>,
modifiers: &[ast::Modifier<'_>],
) -> Result<Component, Error> {
$(if name.value.eq_ignore_ascii_case($parse_variant) {
return Ok(Component::$variant($variant::with_modifiers(&modifiers)?));
})*
Err(Error {
_inner: name.span.error("invalid component"),
public: crate::error::InvalidFormatDescription::InvalidComponentName {
name: String::from_utf8_lossy(name.value).into_owned(),
index: name.span.start_byte(),
},
})
}
}
}
// Keep in alphabetical order.
component_definition! {
pub(super) enum Component {
Day = b"day" {
padding = b"padding": Option<Padding> => padding,
},
Hour = b"hour" {
padding = b"padding": Option<Padding> => padding,
base = b"repr": Option<HourBase> => is_12_hour_clock,
},
Minute = b"minute" {
padding = b"padding": Option<Padding> => padding,
},
Month = b"month" {
padding = b"padding": Option<Padding> => padding,
repr = b"repr": Option<MonthRepr> => repr,
case_sensitive = b"case_sensitive": Option<MonthCaseSensitive> => case_sensitive,
},
OffsetHour = b"offset_hour" {
sign_behavior = b"sign": Option<SignBehavior> => sign_is_mandatory,
padding = b"padding": Option<Padding> => padding,
},
OffsetMinute = b"offset_minute" {
padding = b"padding": Option<Padding> => padding,
},
OffsetSecond = b"offset_second" {
padding = b"padding": Option<Padding> => padding,
},
Ordinal = b"ordinal" {
padding = b"padding": Option<Padding> => padding,
},
Period = b"period" {
case = b"case": Option<PeriodCase> => is_uppercase,
case_sensitive = b"case_sensitive": Option<PeriodCaseSensitive> => case_sensitive,
},
Second = b"second" {
padding = b"padding": Option<Padding> => padding,
},
Subsecond = b"subsecond" {
digits = b"digits": Option<SubsecondDigits> => digits,
},
Weekday = b"weekday" {
repr = b"repr": Option<WeekdayRepr> => repr,
one_indexed = b"one_indexed": Option<WeekdayOneIndexed> => one_indexed,
case_sensitive = b"case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive,
},
WeekNumber = b"week_number" {
padding = b"padding": Option<Padding> => padding,
repr = b"repr": Option<WeekNumberRepr> => repr,
},
Year = b"year" {
padding = b"padding": Option<Padding> => padding,
repr = b"repr": Option<YearRepr> => repr,
base = b"base": Option<YearBase> => iso_week_based,
sign_behavior = b"sign": Option<SignBehavior> => sign_is_mandatory,
},
}
}
/// Get the target type for a given enum.
macro_rules! target_ty {
($name:ident $type:ty) => {
$type
};
($name:ident) => {
$crate::format_description::modifier::$name
};
}
/// Get the target value for a given enum.
macro_rules! target_value {
($name:ident $variant:ident $value:expr) => {
$value
};
($name:ident $variant:ident) => {
$crate::format_description::modifier::$name::$variant
};
}
// TODO use `#[derive(Default)]` on enums once MSRV is 1.62 (NET 2022-12-30)
/// Simulate `#[derive(Default)]` on enums.
macro_rules! derived_default_on_enum {
($type:ty; $default:expr) => {};
($attr:meta $type:ty; $default:expr) => {
impl Default for $type {
fn default() -> Self {
$default
}
}
};
}
/// Declare the various modifiers.
///
/// For the general case, ordinary syntax can be used. Note that you _must_ declare a default
/// variant. The only significant change is that the string representation of the variant must be
/// provided after the variant name. For example, `Numerical = b"numerical"` declares a variant
/// named `Numerical` with the string representation `b"numerical"`. This is the value that will be
/// used when parsing the modifier. The value is not case sensitive.
///
/// If the type in the public API does not have the same name as the type in the internal
/// representation, then the former must be specified in parenthesis after the internal name. For
/// example, `HourBase(bool)` has an internal name "HourBase", but is represented as a boolean in
/// the public API.
///
/// By default, the internal variant name is assumed to be the same as the public variant name. If
/// this is not the case, the qualified path to the variant must be specified in parenthesis after
/// the internal variant name. For example, `Twelve(true)` has an internal variant name "Twelve",
/// but is represented as `true` in the public API.
macro_rules! modifier {
($(
enum $name:ident $(($target_ty:ty))? {
$(
$(#[$attr:meta])?
$variant:ident $(($target_value:expr))? = $parse_variant:literal
),* $(,)?
}
)+) => {$(
enum $name {
$($variant),*
}
$(derived_default_on_enum! {
$($attr)? $name; $name::$variant
})*
impl $name {
/// Parse the modifier from its string representation.
fn from_modifier_value(value: &ast::Value<'_>) -> Result<Option<Self>, Error> {
$(if value.value.eq_ignore_ascii_case($parse_variant) {
return Ok(Some(Self::$variant));
})*
Err(Error {
_inner: value.span.error("invalid modifier value"),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(value.value).into_owned(),
index: value.span.start_byte(),
},
})
}
}
impl From<$name> for target_ty!($name $($target_ty)?) {
fn from(modifier: $name) -> Self {
match modifier {
$($name::$variant => target_value!($name $variant $($target_value)?)),*
}
}
}
)+};
}
// Keep in alphabetical order.
modifier! {
enum HourBase(bool) {
Twelve(true) = b"12",
#[default]
TwentyFour(false) = b"24",
}
enum MonthCaseSensitive(bool) {
False(false) = b"false",
#[default]
True(true) = b"true",
}
enum MonthRepr {
#[default]
Numerical = b"numerical",
Long = b"long",
Short = b"short",
}
enum Padding {
Space = b"space",
#[default]
Zero = b"zero",
None = b"none",
}
enum PeriodCase(bool) {
Lower(false) = b"lower",
#[default]
Upper(true) = b"upper",
}
enum PeriodCaseSensitive(bool) {
False(false) = b"false",
#[default]
True(true) = b"true",
}
enum SignBehavior(bool) {
#[default]
Automatic(false) = b"automatic",
Mandatory(true) = b"mandatory",
}
enum SubsecondDigits {
One = b"1",
Two = b"2",
Three = b"3",
Four = b"4",
Five = b"5",
Six = b"6",
Seven = b"7",
Eight = b"8",
Nine = b"9",
#[default]
OneOrMore = b"1+",
}
enum WeekNumberRepr {
#[default]
Iso = b"iso",
Sunday = b"sunday",
Monday = b"monday",
}
enum WeekdayCaseSensitive(bool) {
False(false) = b"false",
#[default]
True(true) = b"true",
}
enum WeekdayOneIndexed(bool) {
False(false) = b"false",
#[default]
True(true) = b"true",
}
enum WeekdayRepr {
Short = b"short",
#[default]
Long = b"long",
Sunday = b"sunday",
Monday = b"monday",
}
enum YearBase(bool) {
#[default]
Calendar(false) = b"calendar",
IsoWeek(true) = b"iso_week",
}
enum YearRepr {
#[default]
Full = b"full",
LastTwo = b"last_two",
}
}

159
third_party/rust/time/src/format_description/parse/lexer.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,159 @@
//! Lexer for parsing format descriptions.
use core::iter;
use super::{Location, Span};
/// A token emitted by the lexer. There is no semantic meaning at this stage.
pub(super) enum Token<'a> {
/// A literal string, formatted and parsed as-is.
Literal {
/// The string itself.
value: &'a [u8],
/// Where the string was in the format string.
span: Span,
},
/// An opening or closing bracket. May or may not be the start or end of a component.
Bracket {
/// Whether the bracket is opening or closing.
kind: BracketKind,
/// Where the bracket was in the format string.
location: Location,
},
/// One part of a component. This could be its name, a modifier, or whitespace.
ComponentPart {
/// Whether the part is whitespace or not.
kind: ComponentKind,
/// The part itself.
value: &'a [u8],
/// Where the part was in the format string.
span: Span,
},
}
/// What type of bracket is present.
pub(super) enum BracketKind {
/// An opening bracket: `[`
Opening,
/// A closing bracket: `]`
Closing,
}
/// Indicates whether the component is whitespace or not.
pub(super) enum ComponentKind {
#[allow(clippy::missing_docs_in_private_items)]
Whitespace,
#[allow(clippy::missing_docs_in_private_items)]
NotWhitespace,
}
/// Attach [`Location`] information to each byte in the iterator.
fn attach_location(iter: impl Iterator<Item = u8>) -> impl Iterator<Item = (u8, Location)> {
let mut line = 1;
let mut column = 1;
let mut byte_pos = 0;
iter.map(move |byte| {
let location = Location {
line,
column,
byte: byte_pos,
};
column += 1;
byte_pos += 1;
if byte == b'\n' {
line += 1;
column = 1;
}
(byte, location)
})
}
/// Parse the string into a series of [`Token`]s.
pub(super) fn lex(mut input: &[u8]) -> impl Iterator<Item = Token<'_>> {
let mut depth: u8 = 0;
let mut iter = attach_location(input.iter().copied()).peekable();
let mut second_bracket_location = None;
iter::from_fn(move || {
// There is a flag set to emit the second half of an escaped bracket pair.
if let Some(location) = second_bracket_location.take() {
return Some(Token::Bracket {
kind: BracketKind::Opening,
location,
});
}
Some(match iter.next()? {
(b'[', location) => {
if let Some((_, second_location)) = iter.next_if(|&(byte, _)| byte == b'[') {
// escaped bracket
second_bracket_location = Some(second_location);
input = &input[2..];
} else {
// opening bracket
depth += 1;
input = &input[1..];
}
Token::Bracket {
kind: BracketKind::Opening,
location,
}
}
// closing bracket
(b']', location) if depth > 0 => {
depth -= 1;
input = &input[1..];
Token::Bracket {
kind: BracketKind::Closing,
location,
}
}
// literal
(_, start_location) if depth == 0 => {
let mut bytes = 1;
let mut end_location = start_location;
while let Some((_, location)) = iter.next_if(|&(byte, _)| byte != b'[') {
end_location = location;
bytes += 1;
}
let value = &input[..bytes];
input = &input[bytes..];
Token::Literal {
value,
span: Span::start_end(start_location, end_location),
}
}
// component part
(byte, start_location) => {
let mut bytes = 1;
let mut end_location = start_location;
let is_whitespace = byte.is_ascii_whitespace();
while let Some((_, location)) = iter.next_if(|&(byte, _)| {
byte != b'[' && byte != b']' && is_whitespace == byte.is_ascii_whitespace()
}) {
end_location = location;
bytes += 1;
}
let value = &input[..bytes];
input = &input[bytes..];
Token::ComponentPart {
kind: if is_whitespace {
ComponentKind::Whitespace
} else {
ComponentKind::NotWhitespace
},
value,
span: Span::start_end(start_location, end_location),
}
}
})
})
}

193
third_party/rust/time/src/format_description/parse/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,193 @@
//! Parser for format descriptions.
use alloc::vec::Vec;
use core::ops::{RangeFrom, RangeTo};
mod ast;
mod format_item;
mod lexer;
/// 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).
pub fn parse(
s: &str,
) -> Result<Vec<crate::format_description::FormatItem<'_>>, crate::error::InvalidFormatDescription>
{
let lexed = lexer::lex(s.as_bytes());
let ast = ast::parse(lexed);
let format_items = format_item::parse(ast);
Ok(format_items
.map(|res| res.map(Into::into))
.collect::<Result<Vec<_>, _>>()?)
}
/// 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).
///
/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
/// that there is no lifetime that needs to be handled.
///
/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
pub fn parse_owned(
s: &str,
) -> Result<crate::format_description::OwnedFormatItem, crate::error::InvalidFormatDescription> {
let lexed = lexer::lex(s.as_bytes());
let ast = ast::parse(lexed);
let format_items = format_item::parse(ast);
let items = format_items
.map(|res| res.map(Into::into))
.collect::<Result<Vec<_>, _>>()?
.into_boxed_slice();
Ok(crate::format_description::OwnedFormatItem::Compound(items))
}
/// A location within a string.
#[derive(Clone, Copy)]
struct Location {
/// The one-indexed line of the string.
line: usize,
/// The one-indexed column of the string.
column: usize,
/// The zero-indexed byte of the string.
byte: usize,
}
impl Location {
/// Offset the location by the provided amount.
///
/// Note that this assumes the resulting location is on the same line as the original location.
#[must_use = "this does not modify the original value"]
const fn offset(&self, offset: usize) -> Self {
Self {
line: self.line,
column: self.column + offset,
byte: self.byte + offset,
}
}
/// Create an error with the provided message at this location.
const fn error(self, message: &'static str) -> ErrorInner {
ErrorInner {
_message: message,
_span: Span {
start: self,
end: self,
},
}
}
}
/// A start and end point within a string.
#[derive(Clone, Copy)]
struct Span {
#[allow(clippy::missing_docs_in_private_items)]
start: Location,
#[allow(clippy::missing_docs_in_private_items)]
end: Location,
}
impl Span {
/// Create a new `Span` from the provided start and end locations.
const fn start_end(start: Location, end: Location) -> Self {
Self { start, end }
}
/// Reduce this span to the provided range.
#[must_use = "this does not modify the original value"]
fn subspan(&self, range: impl Subspan) -> Self {
range.subspan(self)
}
/// Obtain a `Span` pointing at the start of the pre-existing span.
#[must_use = "this does not modify the original value"]
const fn shrink_to_start(&self) -> Self {
Self {
start: self.start,
end: self.start,
}
}
/// Obtain a `Span` pointing at the end of the pre-existing span.
#[must_use = "this does not modify the original value"]
const fn shrink_to_end(&self) -> Self {
Self {
start: self.end,
end: self.end,
}
}
/// Create an error with the provided message at this span.
const fn error(self, message: &'static str) -> ErrorInner {
ErrorInner {
_message: message,
_span: self,
}
}
/// Get the byte index that the span starts at.
const fn start_byte(&self) -> usize {
self.start.byte
}
}
/// A trait for types that can be used to reduce a `Span`.
trait Subspan {
/// Reduce the provided `Span` to a new `Span`.
fn subspan(self, span: &Span) -> Span;
}
impl Subspan for RangeFrom<usize> {
fn subspan(self, span: &Span) -> Span {
assert_eq!(span.start.line, span.end.line);
Span {
start: Location {
line: span.start.line,
column: span.start.column + self.start,
byte: span.start.byte + self.start,
},
end: span.end,
}
}
}
impl Subspan for RangeTo<usize> {
fn subspan(self, span: &Span) -> Span {
assert_eq!(span.start.line, span.end.line);
Span {
start: span.start,
end: Location {
line: span.start.line,
column: span.start.column + self.end - 1,
byte: span.start.byte + self.end - 1,
},
}
}
}
/// The internal error type.
struct ErrorInner {
/// The message displayed to the user.
_message: &'static str,
/// Where the error originated.
_span: Span,
}
/// A complete error description.
struct Error {
/// The internal error.
_inner: ErrorInner,
/// The error needed for interoperability with the rest of `time`.
public: crate::error::InvalidFormatDescription,
}
impl From<Error> for crate::error::InvalidFormatDescription {
fn from(error: Error) -> Self {
error.public
}
}

Просмотреть файл

@ -0,0 +1,233 @@
//! The format described in ISO 8601.
mod adt_hack;
use core::num::NonZeroU8;
pub use self::adt_hack::{DoNotRelyOnWhatThisIs, EncodedConfig};
/// A configuration for [`Iso8601`] that only parses values.
const PARSING_ONLY: EncodedConfig = Config {
formatted_components: FormattedComponents::None,
use_separators: false,
year_is_six_digits: false,
date_kind: DateKind::Calendar,
time_precision: TimePrecision::Hour {
decimal_digits: None,
},
offset_precision: OffsetPrecision::Hour,
}
.encode();
/// The default configuration for [`Iso8601`].
const DEFAULT_CONFIG: EncodedConfig = Config::DEFAULT.encode();
/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html).
///
/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions.
///
/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`].
/// Passing any other value is **unspecified behavior**.
///
/// Example: 1997-11-21T09:55:06.000000000-06:00
///
/// # Examples
#[cfg_attr(feature = "formatting", doc = "```rust")]
#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
/// # use time::format_description::well_known::Iso8601;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?,
/// "1997-11-12T09:55:06.000000000-06:00"
/// );
/// # Ok::<_, time::Error>(())
/// ```
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Iso8601<const CONFIG: EncodedConfig = DEFAULT_CONFIG>;
impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Iso8601")
.field("config", &Config::decode(CONFIG))
.finish()
}
}
impl Iso8601<DEFAULT_CONFIG> {
/// An [`Iso8601`] with the default configuration.
///
/// The following is the default behavior:
///
/// - The configuration can be used for both formatting and parsing.
/// - The date, time, and UTC offset are all formatted.
/// - Separators (such as `-` and `:`) are included.
/// - The year contains four digits, such that the year must be between 0 and 9999.
/// - The date uses the calendar format.
/// - The time has precision to the second and nine decimal digits.
/// - The UTC offset has precision to the minute.
///
/// If you need different behavior, use [`Config::DEFAULT`] and [`Config`]'s methods to create
/// a custom configuration.
pub const DEFAULT: Self = Self;
}
impl Iso8601<PARSING_ONLY> {
/// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
/// unspecified behavior.
pub const PARSING: Self = Self;
}
/// Which components to format.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FormattedComponents {
/// The configuration can only be used for parsing. Using this to format a value is
/// unspecified behavior.
None,
/// Format only the date.
Date,
/// Format only the time.
Time,
/// Format only the UTC offset.
Offset,
/// Format the date and time.
DateTime,
/// Format the date, time, and UTC offset.
DateTimeOffset,
/// Format the time and UTC offset.
TimeOffset,
}
/// Which format to use for the date.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DateKind {
/// Use the year-month-day format.
Calendar,
/// Use the year-week-weekday format.
Week,
/// Use the week-ordinal format.
Ordinal,
}
/// The precision and number of decimal digits present for the time.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimePrecision {
/// Format the hour only. Minutes, seconds, and nanoseconds will be represented with the
/// specified number of decimal digits, if any.
Hour {
#[allow(clippy::missing_docs_in_private_items)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Minute {
#[allow(clippy::missing_docs_in_private_items)]
decimal_digits: Option<NonZeroU8>,
},
/// Format the hour, minute, and second. Nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Second {
#[allow(clippy::missing_docs_in_private_items)]
decimal_digits: Option<NonZeroU8>,
},
}
/// The precision for the UTC offset.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OffsetPrecision {
/// Format only the offset hour. Requires the offset minute to be zero.
Hour,
/// Format both the offset hour and minute.
Minute,
}
/// Configuration for [`Iso8601`].
// This is only used as a const generic, so there's no need to have a number of implementations on
// it.
#[allow(missing_copy_implementations)]
#[doc(alias = "EncodedConfig")] // People will likely search for `EncodedConfig`, so show them this.
#[derive(Debug)]
pub struct Config {
/// Which components, if any, will be formatted.
pub(crate) formatted_components: FormattedComponents,
/// Whether the format contains separators (such as `-` or `:`).
pub(crate) use_separators: bool,
/// Whether the year is six digits.
pub(crate) year_is_six_digits: bool,
/// The format used for the date.
pub(crate) date_kind: DateKind,
/// The precision and number of decimal digits present for the time.
pub(crate) time_precision: TimePrecision,
/// The precision for the UTC offset.
pub(crate) offset_precision: OffsetPrecision,
}
impl Config {
/// A configuration for the [`Iso8601`] format.
///
/// The following is the default behavior:
///
/// - The configuration can be used for both formatting and parsing.
/// - The date, time, and UTC offset are all formatted.
/// - Separators (such as `-` and `:`) are included.
/// - The year contains four digits, such that the year must be between 0 and 9999.
/// - The date uses the calendar format.
/// - The time has precision to the second and nine decimal digits.
/// - The UTC offset has precision to the minute.
///
/// If you need different behavior, use the setter methods on this struct.
pub const DEFAULT: Self = Self {
formatted_components: FormattedComponents::DateTimeOffset,
use_separators: true,
year_is_six_digits: false,
date_kind: DateKind::Calendar,
time_precision: TimePrecision::Second {
decimal_digits: NonZeroU8::new(9),
},
offset_precision: OffsetPrecision::Minute,
};
/// Set whether the format the date, time, and/or UTC offset.
pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
Self {
formatted_components,
..self
}
}
/// Set whether the format contains separators (such as `-` or `:`).
pub const fn set_use_separators(self, use_separators: bool) -> Self {
Self {
use_separators,
..self
}
}
/// Set whether the year is six digits.
pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self {
Self {
year_is_six_digits,
..self
}
}
/// Set the format used for the date.
pub const fn set_date_kind(self, date_kind: DateKind) -> Self {
Self { date_kind, ..self }
}
/// Set the precision and number of decimal digits present for the time.
pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
Self {
time_precision,
..self
}
}
/// Set the precision for the UTC offset.
pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
Self {
offset_precision,
..self
}
}
}

Просмотреть файл

@ -0,0 +1,249 @@
//! Hackery to work around not being able to use ADTs in const generics on stable.
use core::num::NonZeroU8;
#[cfg(feature = "formatting")]
use super::Iso8601;
use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision};
// This provides a way to include `EncodedConfig` in documentation without displaying the type it is
// aliased to.
#[doc(hidden)]
pub type DoNotRelyOnWhatThisIs = u128;
/// An encoded [`Config`] that can be used as a const parameter to [`Iso8601`].
///
/// The type this is aliased to must not be relied upon. It can change in any release without
/// notice.
pub type EncodedConfig = DoNotRelyOnWhatThisIs;
#[cfg(feature = "formatting")]
impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
/// The user-provided configuration for the ISO 8601 format.
const CONFIG: Config = Config::decode(CONFIG);
/// Whether the date should be formatted.
pub(crate) const FORMAT_DATE: bool = matches!(
Self::CONFIG.formatted_components,
FC::Date | FC::DateTime | FC::DateTimeOffset
);
/// Whether the time should be formatted.
pub(crate) const FORMAT_TIME: bool = matches!(
Self::CONFIG.formatted_components,
FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset
);
/// Whether the UTC offset should be formatted.
pub(crate) const FORMAT_OFFSET: bool = matches!(
Self::CONFIG.formatted_components,
FC::Offset | FC::DateTimeOffset | FC::TimeOffset
);
/// Whether the year is six digits.
pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits;
/// Whether the format contains separators (such as `-` or `:`).
pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators;
/// Which format to use for the date.
pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind;
/// The precision and number of decimal digits to use for the time.
pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision;
/// The precision for the UTC offset.
pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision;
}
impl Config {
/// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`].
///
/// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any
/// other usage is unspecified behavior.
pub const fn encode(&self) -> EncodedConfig {
let mut bytes = [0; EncodedConfig::BITS as usize / 8];
bytes[0] = match self.formatted_components {
FC::None => 0,
FC::Date => 1,
FC::Time => 2,
FC::Offset => 3,
FC::DateTime => 4,
FC::DateTimeOffset => 5,
FC::TimeOffset => 6,
};
bytes[1] = self.use_separators as _;
bytes[2] = self.year_is_six_digits as _;
bytes[3] = match self.date_kind {
DateKind::Calendar => 0,
DateKind::Week => 1,
DateKind::Ordinal => 2,
};
bytes[4] = match self.time_precision {
TimePrecision::Hour { .. } => 0,
TimePrecision::Minute { .. } => 1,
TimePrecision::Second { .. } => 2,
};
bytes[5] = match self.time_precision {
TimePrecision::Hour { decimal_digits }
| TimePrecision::Minute { decimal_digits }
| TimePrecision::Second { decimal_digits } => match decimal_digits {
None => 0,
Some(decimal_digits) => decimal_digits.get(),
},
};
bytes[6] = match self.offset_precision {
OffsetPrecision::Hour => 0,
OffsetPrecision::Minute => 1,
};
EncodedConfig::from_be_bytes(bytes)
}
/// Decode the configuration. The configuration must have been generated from
/// [`Config::encode`].
pub(super) const fn decode(encoded: EncodedConfig) -> Self {
let bytes = encoded.to_be_bytes();
let formatted_components = match bytes[0] {
0 => FC::None,
1 => FC::Date,
2 => FC::Time,
3 => FC::Offset,
4 => FC::DateTime,
5 => FC::DateTimeOffset,
6 => FC::TimeOffset,
_ => panic!("invalid configuration"),
};
let use_separators = match bytes[1] {
0 => false,
1 => true,
_ => panic!("invalid configuration"),
};
let year_is_six_digits = match bytes[2] {
0 => false,
1 => true,
_ => panic!("invalid configuration"),
};
let date_kind = match bytes[3] {
0 => DateKind::Calendar,
1 => DateKind::Week,
2 => DateKind::Ordinal,
_ => panic!("invalid configuration"),
};
let time_precision = match bytes[4] {
0 => TimePrecision::Hour {
decimal_digits: NonZeroU8::new(bytes[5]),
},
1 => TimePrecision::Minute {
decimal_digits: NonZeroU8::new(bytes[5]),
},
2 => TimePrecision::Second {
decimal_digits: NonZeroU8::new(bytes[5]),
},
_ => panic!("invalid configuration"),
};
let offset_precision = match bytes[6] {
0 => OffsetPrecision::Hour,
1 => OffsetPrecision::Minute,
_ => panic!("invalid configuration"),
};
// No `for` loops in `const fn`.
let mut idx = 7; // first unused byte
while idx < EncodedConfig::BITS as usize / 8 {
assert!(bytes[idx] == 0, "invalid configuration");
idx += 1;
}
Self {
formatted_components,
use_separators,
year_is_six_digits,
date_kind,
time_precision,
offset_precision,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! eq {
($a:expr, $b:expr) => {{
let a = $a;
let b = $b;
a.formatted_components == b.formatted_components
&& a.use_separators == b.use_separators
&& a.year_is_six_digits == b.year_is_six_digits
&& a.date_kind == b.date_kind
&& a.time_precision == b.time_precision
&& a.offset_precision == b.offset_precision
}};
}
#[test]
fn encoding_roundtrip() {
macro_rules! assert_roundtrip {
($config:expr) => {
let config = $config;
let encoded = config.encode();
let decoded = Config::decode(encoded);
assert!(eq!(config, decoded));
};
}
assert_roundtrip!(Config::DEFAULT);
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset));
assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset));
assert_roundtrip!(Config::DEFAULT.set_use_separators(false));
assert_roundtrip!(Config::DEFAULT.set_use_separators(true));
assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false));
assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week));
assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
decimal_digits: None,
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
decimal_digits: NonZeroU8::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour));
assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute));
}
macro_rules! assert_decode_fail {
($encoding:expr) => {
assert!(
std::panic::catch_unwind(|| {
Config::decode($encoding);
})
.is_err()
);
};
}
#[test]
fn decode_fail() {
assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00);
assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00);
}
}

Просмотреть файл

@ -0,0 +1,30 @@
//! The format described in RFC 2822.
/// 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
#[cfg_attr(feature = "parsing", doc = "```rust")]
#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")]
/// # use time::{format_description::well_known::Rfc2822, OffsetDateTime};
/// use time_macros::datetime;
/// 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>(())
/// ```
///
#[cfg_attr(feature = "formatting", doc = "```rust")]
#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
/// # use time::format_description::well_known::Rfc2822;
/// # use time_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;

Просмотреть файл

@ -0,0 +1,30 @@
//! The format described in RFC 3339.
/// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6).
///
/// Format example: 1985-04-12T23:20:50.52Z
///
/// # Examples
#[cfg_attr(feature = "parsing", doc = "```rust")]
#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")]
/// # use time::{format_description::well_known::Rfc3339, OffsetDateTime};
/// # use time_macros::datetime;
/// assert_eq!(
/// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?,
/// datetime!(1985-04-12 23:20:50.52 +00:00)
/// );
/// # Ok::<_, time::Error>(())
/// ```
///
#[cfg_attr(feature = "formatting", doc = "```rust")]
#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")]
/// # use time::format_description::well_known::Rfc3339;
/// # use time_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;

Просмотреть файл

@ -3,10 +3,11 @@
use core::ops::Deref;
use std::io;
use crate::format_description::well_known::{Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
use crate::format_description::{FormatItem, OwnedFormatItem};
use crate::formatting::{
format_component, format_number_pad_zero, write, MONTH_NAMES, WEEKDAY_NAMES,
format_component, format_number_pad_zero, iso8601, write, MONTH_NAMES, WEEKDAY_NAMES,
};
use crate::{error, Date, Time, UtcOffset};
@ -15,8 +16,11 @@ use crate::{error, Date, Time, UtcOffset};
pub trait Formattable: sealed::Sealed {}
impl Formattable for FormatItem<'_> {}
impl Formattable for [FormatItem<'_>] {}
impl Formattable for OwnedFormatItem {}
impl Formattable for [OwnedFormatItem] {}
impl Formattable for Rfc3339 {}
impl Formattable for Rfc2822 {}
impl<const CONFIG: EncodedConfig> Formattable for Iso8601<CONFIG> {}
impl<T: Deref> Formattable for T where T::Target: Formattable {}
/// Seal the trait to prevent downstream users from implementing it.
@ -25,7 +29,6 @@ mod sealed {
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(
@ -88,6 +91,43 @@ impl<'a> sealed::Sealed for [FormatItem<'a>] {
}
}
impl sealed::Sealed for OwnedFormatItem {
fn format_into(
&self,
output: &mut impl io::Write,
date: Option<Date>,
time: Option<Time>,
offset: Option<UtcOffset>,
) -> Result<usize, error::Format> {
match self {
Self::Literal(literal) => Ok(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 {
[] => Ok(0),
[item, ..] => item.format_into(output, date, time, offset),
},
}
}
}
impl sealed::Sealed for [OwnedFormatItem] {
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,
@ -133,22 +173,22 @@ impl sealed::Sealed for Rfc2822 {
&WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize][..3],
)?;
bytes += write(output, b", ")?;
bytes += format_number_pad_zero::<_, _, 2>(output, day)?;
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 += format_number_pad_zero::<4, _, _>(output, year as u32)?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.hour())?;
bytes += format_number_pad_zero::<2, _, _>(output, time.hour())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.minute())?;
bytes += format_number_pad_zero::<2, _, _>(output, time.minute())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<_, _, 2>(output, time.second())?;
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.whole_hours().unsigned_abs())?;
bytes +=
format_number_pad_zero::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs())?;
format_number_pad_zero::<2, _, _>(output, offset.minutes_past_hour().unsigned_abs())?;
Ok(bytes)
}
@ -177,60 +217,86 @@ impl sealed::Sealed for Rfc3339 {
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())?;
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 += write(output, b".")?;
bytes += if nanos % 10 != 0 {
format_number_pad_zero::<_, _, 9>(output, nanos)
format_number_pad_zero::<9, _, _>(output, nanos)
} else if (nanos / 10) % 10 != 0 {
format_number_pad_zero::<_, _, 8>(output, nanos / 10)
format_number_pad_zero::<8, _, _>(output, nanos / 10)
} else if (nanos / 100) % 10 != 0 {
format_number_pad_zero::<_, _, 7>(output, nanos / 100)
format_number_pad_zero::<7, _, _>(output, nanos / 100)
} else if (nanos / 1_000) % 10 != 0 {
format_number_pad_zero::<_, _, 6>(output, nanos / 1_000)
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)
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)
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)
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)
format_number_pad_zero::<2, _, _>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
format_number_pad_zero::<1, _, _>(output, nanos / 100_000_000)
}?;
}
if offset == UtcOffset::UTC {
bytes += write(output, &[b'Z'])?;
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 += 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())?;
format_number_pad_zero::<2, _, _>(output, offset.minutes_past_hour().unsigned_abs())?;
Ok(bytes)
}
}
impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
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;
if Self::FORMAT_DATE {
let date = date.ok_or(error::Format::InsufficientTypeInformation)?;
bytes += iso8601::format_date::<_, CONFIG>(output, date)?;
}
if Self::FORMAT_TIME {
let time = time.ok_or(error::Format::InsufficientTypeInformation)?;
bytes += iso8601::format_time::<_, CONFIG>(output, time)?;
}
if Self::FORMAT_OFFSET {
let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?;
bytes += iso8601::format_offset::<_, CONFIG>(output, offset)?;
}
if bytes == 0 {
// The only reason there would be no bytes written is if the format was only for
// parsing.
panic!("attempted to format a parsing-only format description");
}
Ok(bytes)
}

139
third_party/rust/time/src/formatting/iso8601.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,139 @@
//! Helpers for implementing formatting for ISO 8601.
use std::io;
use crate::format_description::well_known::iso8601::{
DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
};
use crate::format_description::well_known::Iso8601;
use crate::formatting::{format_float, format_number_pad_zero, write, write_if, write_if_else};
use crate::{error, Date, Time, UtcOffset};
/// Format the date portion of ISO 8601.
pub(super) fn format_date<W: io::Write, const CONFIG: EncodedConfig>(
output: &mut W,
date: Date,
) -> Result<usize, error::Format> {
let mut bytes = 0;
match Iso8601::<CONFIG>::DATE_KIND {
DateKind::Calendar => {
let (year, month, day) = date.to_calendar_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6, _, _>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
bytes += format_number_pad_zero::<4, _, _>(output, year as u32)?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2, _, _>(output, month as u8)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2, _, _>(output, day)?;
}
DateKind::Week => {
let (year, week, day) = date.to_iso_week_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6, _, _>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
bytes += format_number_pad_zero::<4, _, _>(output, year as u32)?;
}
bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?;
bytes += format_number_pad_zero::<2, _, _>(output, week)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<1, _, _>(output, day.number_from_monday())?;
}
DateKind::Ordinal => {
let (year, day) = date.to_ordinal_date();
if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS {
bytes += write_if_else(output, year < 0, b"-", b"+")?;
bytes += format_number_pad_zero::<6, _, _>(output, year.unsigned_abs())?;
} else if !(0..=9999).contains(&year) {
return Err(error::Format::InvalidComponent("year"));
} else {
bytes += format_number_pad_zero::<4, _, _>(output, year as u32)?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<3, _, _>(output, day)?;
}
}
Ok(bytes)
}
/// Format the time portion of ISO 8601.
pub(super) fn format_time<W: io::Write, const CONFIG: EncodedConfig>(
output: &mut W,
time: Time,
) -> Result<usize, error::Format> {
let mut bytes = 0;
// The "T" can only be omitted in extended format where there is no date being formatted.
bytes += write_if(
output,
Iso8601::<CONFIG>::USE_SEPARATORS || Iso8601::<CONFIG>::FORMAT_DATE,
b"T",
)?;
let (hours, minutes, seconds, nanoseconds) = time.as_hms_nano();
match Iso8601::<CONFIG>::TIME_PRECISION {
TimePrecision::Hour { decimal_digits } => {
let hours = (hours as f64)
+ (minutes as f64) / 60.
+ (seconds as f64) / 3_600.
+ (nanoseconds as f64) / 3_600. / 1_000_000_000.;
format_float(output, hours, 2, decimal_digits)?;
}
TimePrecision::Minute { decimal_digits } => {
bytes += format_number_pad_zero::<2, _, _>(output, hours)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let minutes = (minutes as f64)
+ (seconds as f64) / 60.
+ (nanoseconds as f64) / 60. / 1_000_000_000.;
bytes += format_float(output, minutes, 2, decimal_digits)?;
}
TimePrecision::Second { decimal_digits } => {
bytes += format_number_pad_zero::<2, _, _>(output, hours)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
bytes += format_number_pad_zero::<2, _, _>(output, minutes)?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let seconds = (seconds as f64) + (nanoseconds as f64) / 1_000_000_000.;
bytes += format_float(output, seconds, 2, decimal_digits)?;
}
}
Ok(bytes)
}
/// Format the UTC offset portion of ISO 8601.
pub(super) fn format_offset<W: io::Write, const CONFIG: EncodedConfig>(
output: &mut W,
offset: UtcOffset,
) -> Result<usize, error::Format> {
if Iso8601::<CONFIG>::FORMAT_TIME && offset.is_utc() {
return Ok(write(output, b"Z")?);
}
let mut bytes = 0;
let (hours, minutes, seconds) = offset.as_hms();
if seconds != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += write_if_else(output, offset.is_negative(), b"-", b"+")?;
bytes += format_number_pad_zero::<2, _, _>(output, hours.unsigned_abs())?;
if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Hour && minutes != 0 {
return Err(error::Format::InvalidComponent("offset_minute"));
} else if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Minute {
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
bytes += format_number_pad_zero::<2, _, _>(output, minutes.unsigned_abs())?;
}
Ok(bytes)
}

123
third_party/rust/time/src/formatting/mod.rs поставляемый
Просмотреть файл

@ -1,7 +1,9 @@
//! Formatting for various types.
pub(crate) mod formattable;
mod iso8601;
use core::num::NonZeroU8;
use std::io;
pub use self::formattable::Formattable;
@ -116,22 +118,69 @@ impl DigitCount for u32 {
// 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> {
pub(crate) fn write(output: &mut impl io::Write, bytes: &[u8]) -> io::Result<usize> {
output.write_all(bytes)?;
Ok(bytes.len())
}
/// If `pred` is true, write all bytes to the output, returning the number of bytes written.
pub(crate) fn write_if(output: &mut impl io::Write, pred: bool, bytes: &[u8]) -> io::Result<usize> {
if pred { write(output, bytes) } else { Ok(0) }
}
/// If `pred` is true, write `true_bytes` to the output. Otherwise, write `false_bytes`.
pub(crate) fn write_if_else(
output: &mut impl io::Write,
pred: bool,
true_bytes: &[u8],
false_bytes: &[u8],
) -> io::Result<usize> {
write(output, if pred { true_bytes } else { false_bytes })
}
/// Write the floating point number to the output, returning the number of bytes written.
///
/// This method accepts the number of digits before and after the decimal. The value will be padded
/// with zeroes to the left if necessary.
pub(crate) fn format_float(
output: &mut impl io::Write,
value: f64,
digits_before_decimal: u8,
digits_after_decimal: Option<NonZeroU8>,
) -> io::Result<usize> {
match digits_after_decimal {
Some(digits_after_decimal) => {
let digits_after_decimal = digits_after_decimal.get() as usize;
let width = digits_before_decimal as usize + 1 + digits_after_decimal;
write!(
output,
"{value:0>width$.digits_after_decimal$}",
value = value,
width = width,
digits_after_decimal = digits_after_decimal,
)?;
Ok(width)
}
None => {
let value = value.trunc() as u64;
let width = digits_before_decimal as usize;
write!(output, "{value:0>width$?}", value = value, width = width)?;
Ok(width)
}
}
}
/// 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>(
pub(crate) fn format_number<const WIDTH: u8, W: io::Write, V: itoa::Integer + DigitCount + Copy>(
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::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()),
}
}
@ -140,16 +189,16 @@ pub(crate) fn format_number<W: io::Write, V: itoa::Integer + DigitCount + Copy,
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_space<
const WIDTH: u8,
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, b" ")?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
@ -159,16 +208,16 @@ pub(crate) fn format_number_pad_space<
///
/// The sign must be written by the caller.
pub(crate) fn format_number_pad_zero<
const WIDTH: u8,
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, b"0")?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
@ -211,7 +260,7 @@ fn fmt_day(
date: Date,
modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, date.day(), padding)
format_number::<2, _, _>(output, date.day(), padding)
}
/// Format the month into the designated output.
@ -226,7 +275,7 @@ fn fmt_month(
) -> Result<usize, io::Error> {
match repr {
modifier::MonthRepr::Numerical => {
format_number::<_, _, 2>(output, date.month() as u8, padding)
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]),
@ -239,7 +288,7 @@ fn fmt_ordinal(
date: Date,
modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
format_number::<_, _, 3>(output, date.ordinal(), padding)
format_number::<3, _, _>(output, date.ordinal(), padding)
}
/// Format the weekday into the designated output.
@ -261,12 +310,12 @@ fn fmt_weekday(
output,
WEEKDAY_NAMES[date.weekday().number_days_from_monday() as usize],
),
modifier::WeekdayRepr::Sunday => format_number::<_, _, 1>(
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>(
modifier::WeekdayRepr::Monday => format_number::<1, _, _>(
output,
date.weekday().number_days_from_monday() + one_indexed as u8,
modifier::Padding::None,
@ -280,7 +329,7 @@ fn fmt_week_number(
date: Date,
modifier::WeekNumber { padding, repr }: modifier::WeekNumber,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(
format_number::<2, _, _>(
output,
match repr {
modifier::WeekNumberRepr::Iso => date.iso_week(),
@ -313,18 +362,18 @@ fn fmt_year(
};
let format_number = match repr {
#[cfg(feature = "large-dates")]
modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<_, _, 6>,
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>,
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'-'])?;
bytes += write(output, b"-")?;
} else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
bytes += write(output, &[b'+'])?;
bytes += write(output, b"+")?;
}
}
bytes += format_number(output, value.unsigned_abs(), padding)?;
@ -348,7 +397,7 @@ fn fmt_hour(
(hour, true) if hour < 12 => hour,
(hour, true) => hour - 12,
};
format_number::<_, _, 2>(output, value, padding)
format_number::<2, _, _>(output, value, padding)
}
/// Format the minute into the designated output.
@ -357,7 +406,7 @@ fn fmt_minute(
time: Time,
modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.minute(), padding)
format_number::<2, _, _>(output, time.minute(), padding)
}
/// Format the period into the designated output.
@ -383,7 +432,7 @@ fn fmt_second(
time: Time,
modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, time.second(), padding)
format_number::<2, _, _>(output, time.second(), padding)
}
/// Format the subsecond into the designated output.
@ -396,23 +445,23 @@ fn fmt_subsecond<W: io::Write>(
let nanos = time.nanosecond();
if digits == Nine || (digits == OneOrMore && nanos % 10 != 0) {
format_number_pad_zero::<_, _, 9>(output, nanos)
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)
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)
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)
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)
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)
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)
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)
format_number_pad_zero::<2, _, _>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<_, _, 1>(output, nanos / 100_000_000)
format_number_pad_zero::<1, _, _>(output, nanos / 100_000_000)
}
}
// endregion time formatters
@ -429,11 +478,11 @@ fn fmt_offset_hour(
) -> Result<usize, io::Error> {
let mut bytes = 0;
if offset.is_negative() {
bytes += write(output, &[b'-'])?;
bytes += write(output, b"-")?;
} else if sign_is_mandatory {
bytes += write(output, &[b'+'])?;
bytes += write(output, b"+")?;
}
bytes += format_number::<_, _, 2>(output, offset.whole_hours().unsigned_abs(), padding)?;
bytes += format_number::<2, _, _>(output, offset.whole_hours().unsigned_abs(), padding)?;
Ok(bytes)
}
@ -443,7 +492,7 @@ fn fmt_offset_minute(
offset: UtcOffset,
modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.minutes_past_hour().unsigned_abs(), padding)
format_number::<2, _, _>(output, offset.minutes_past_hour().unsigned_abs(), padding)
}
/// Format the offset second into the designated output.
@ -452,6 +501,6 @@ fn fmt_offset_second(
offset: UtcOffset,
modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
format_number::<_, _, 2>(output, offset.seconds_past_minute().unsigned_abs(), padding)
format_number::<2, _, _>(output, offset.seconds_past_minute().unsigned_abs(), padding)
}
// endregion offset formatters

22
third_party/rust/time/src/instant.rs поставляемый
Просмотреть файл

@ -1,10 +1,9 @@
//! The [`Instant`] struct and its associated `impl`s.
use core::borrow::Borrow;
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;
@ -26,7 +25,6 @@ use crate::Duration;
///
/// 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);
@ -73,10 +71,10 @@ impl Instant {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_add(duration.abs_std()).map(Self)
self.0.checked_add(duration.unsigned_abs()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_sub(duration.abs_std()).map(Self)
self.0.checked_sub(duration.unsigned_abs()).map(Self)
}
}
@ -94,10 +92,10 @@ impl Instant {
if duration.is_zero() {
Some(self)
} else if duration.is_positive() {
self.0.checked_sub(duration.abs_std()).map(Self)
self.0.checked_sub(duration.unsigned_abs()).map(Self)
} else {
debug_assert!(duration.is_negative());
self.0.checked_add(duration.abs_std()).map(Self)
self.0.checked_add(duration.unsigned_abs()).map(Self)
}
}
// endregion checked arithmetic
@ -163,10 +161,11 @@ impl Add<Duration> for Instant {
fn add(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 + duration.abs_std())
Self(self.0 + duration.unsigned_abs())
} else if duration.is_negative() {
Self(self.0 - duration.abs_std())
Self(self.0 - duration.unsigned_abs())
} else {
debug_assert!(duration.is_zero());
self
}
}
@ -196,10 +195,11 @@ impl Sub<Duration> for Instant {
fn sub(self, duration: Duration) -> Self::Output {
if duration.is_positive() {
Self(self.0 - duration.abs_std())
Self(self.0 - duration.unsigned_abs())
} else if duration.is_negative() {
Self(self.0 + duration.abs_std())
Self(self.0 + duration.unsigned_abs())
} else {
debug_assert!(duration.is_zero());
self
}
}

83
third_party/rust/time/src/lib.rs поставляемый
Просмотреть файл

@ -39,10 +39,6 @@
//! 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`].
@ -56,7 +52,10 @@
//! 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`_)
//! - `serde-well-known` (_implicitly enables `serde-human-readable`_)
//!
//! _This feature flag is deprecated and will be removed in a future breaking release. Use the
//! `serde-human-readable` feature instead._
//!
//! Enables support for serializing and deserializing well-known formats using serde's
//! [`#[with]` attribute](https://serde.rs/field-attrs.html#with).
@ -69,25 +68,30 @@
//!
//! 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.
//! - `wasm-bindgen`
//!
//! Enables [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) support for converting
//! [JavaScript dates](https://rustwasm.github.io/wasm-bindgen/api/js_sys/struct.Date.html), as
//! well as obtaining the UTC offset from JavaScript.
//!
//! <small>
//! One feature only available to end users is the <code>unsound_local_offset</code> cfg. This
//! enables obtaining the system's UTC offset even when it is unsound. To enable this, use the
//! <code>RUSTFLAGS</code> environment variable. This is untested and officially unsupported. Do not
//! use this unless you understand the risk.
//! </small>
#![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(__time_03_docs, feature(doc_auto_cfg, doc_notable_trait))]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
anonymous_parameters,
clippy::all,
clippy::alloc_instead_of_core,
clippy::explicit_auto_deref,
clippy::obfuscated_if_else,
clippy::std_instead_of_core,
clippy::undocumented_unsafe_blocks,
const_err,
illegal_floating_point_literal_pattern,
late_bound_lifetime_arguments,
@ -97,9 +101,10 @@
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unsafe_code,
unsafe_op_in_unsafe_fn,
unused_extern_crates
unused_extern_crates,
rustdoc::broken_intra_doc_links,
rustdoc::private_intra_doc_links
)]
#![warn(
clippy::dbg_macro,
@ -120,7 +125,12 @@
unused_qualifications,
variant_size_differences
)]
#![allow(clippy::redundant_pub_crate)]
#![allow(
clippy::redundant_pub_crate, // suggests bad style
clippy::option_if_let_else, // suggests terrible code
clippy::unused_peekable, // temporary due to bug: remove when Rust 1.66 is released
clippy::std_instead_of_core, // temporary due to bug: remove when Rust 1.66 is released
)]
#![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))))]
@ -210,12 +220,12 @@ macro_rules! cascade {
cascade!(@ordinal $ordinal);
cascade!(@year $year);
#[allow(unused_assignments)]
if $ordinal > crate::util::days_in_year($year) {
if $ordinal > crate::util::days_in_year($year) as i16 {
$ordinal -= crate::util::days_in_year($year) as i16;
$year += 1;
$ordinal = 1;
} else if $ordinal == 0 {
} else if $ordinal < 1 {
$year -= 1;
$ordinal = crate::util::days_in_year($year);
$ordinal += crate::util::days_in_year($year) as i16;
}
};
}
@ -277,6 +287,18 @@ macro_rules! const_try_opt {
}
};
}
/// Try to unwrap an expression, panicking if not possible.
///
/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
macro_rules! expect_opt {
($e:expr, $message:literal) => {
match $e {
Some(value) => value,
None => crate::expect_failed($message),
}
};
}
// endregion macros
mod date;
@ -297,13 +319,10 @@ mod offset_date_time;
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;
@ -328,3 +347,11 @@ 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>;
/// This is a separate function to reduce the code size of `expect_opt!`.
#[inline(never)]
#[cold]
#[track_caller]
const fn expect_failed(message: &str) -> ! {
panic!("{}", message)
}

24
third_party/rust/time/src/macros.rs поставляемый
Просмотреть файл

@ -30,6 +30,24 @@ pub use time_macros::date;
///
/// [`OffsetDateTime`]: crate::OffsetDateTime
/// [`PrimitiveDateTime`]: crate::PrimitiveDateTime
///
/// ```rust
/// # use time::{Date, Month, macros::datetime, UtcOffset};
/// assert_eq!(
/// datetime!(2020-01-01 0:00),
/// Date::from_calendar_date(2020, Month::January, 1)?.midnight()
/// );
/// assert_eq!(
/// datetime!(2020-01-01 0:00 UTC),
/// Date::from_calendar_date(2020, Month::January, 1)?.midnight().assume_utc()
/// );
/// assert_eq!(
/// datetime!(2020-01-01 0:00 -1),
/// Date::from_calendar_date(2020, Month::January, 1)?.midnight()
/// .assume_offset(UtcOffset::from_hms(-1, 0, 0)?)
/// );
/// # Ok::<_, time::Error>(())
/// ```
pub use time_macros::datetime;
/// Equivalent of performing [`format_description::parse()`] at compile time.
///
@ -38,8 +56,8 @@ pub use time_macros::datetime;
///
/// The resulting expression can be used in `const` or `static` declarations, and implements
/// the sealed traits required for both formatting and parsing.
///
/// ```rust
#[cfg_attr(feature = "alloc", doc = "```rust")]
#[cfg_attr(not(feature = "alloc"), doc = "```rust,ignore")]
/// # use time::{format_description, macros::format_description};
/// assert_eq!(
/// format_description!("[hour]:[minute]:[second]"),
@ -47,7 +65,7 @@ pub use time_macros::datetime;
/// );
/// # 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).
///

1
third_party/rust/time/src/month.rs поставляемый
Просмотреть файл

@ -1,6 +1,5 @@
//! The `Month` enum and its associated `impl`s.
use core::convert::TryFrom;
use core::fmt;
use core::num::NonZeroU8;
use core::str::FromStr;

565
third_party/rust/time/src/offset_date_time.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -66,10 +66,10 @@ pub(crate) fn one_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>(
/// 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,
T,
P: Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
>(
parser: P,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, &'a [u8]>> {
@ -99,32 +99,32 @@ pub(crate) fn n_to_m<
}
/// 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>(
pub(crate) fn n_to_m_digits<const N: u8, const M: u8, T: Integer>(
input: &[u8],
) -> Option<ParsedItem<'_, T>> {
debug_assert!(M >= N);
n_to_m::<_, _, N, M>(any_digit)(input)?.flat_map(|value| value.parse_bytes())
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)
pub(crate) fn exactly_n_digits<const N: u8, T: Integer>(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>(
pub(crate) fn exactly_n_digits_padded<'a, const N: u8, T: Integer>(
padding: Padding,
) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> {
n_to_m_digits_padded::<_, N, N>(padding)
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>(
pub(crate) fn n_to_m_digits_padded<'a, const N: u8, const M: u8, T: Integer>(
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::None => n_to_m_digits::<1, M, _>(input),
Padding::Space => {
debug_assert!(N > 0);
@ -151,7 +151,7 @@ pub(crate) fn n_to_m_digits_padded<'a, T: Integer, const N: u8, const M: u8>(
ParsedItem(input, &orig_input[..(orig_input.len() - input.len())])
.flat_map(|value| value.parse_bytes())
}
Padding::Zero => n_to_m_digits::<_, N, M>(input),
Padding::Zero => n_to_m_digits::<N, M, _>(input),
}
}

173
third_party/rust/time/src/parsing/combinator/rfc/iso8601.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,173 @@
//! Rules defined in [ISO 8601].
//!
//! [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
use core::num::{NonZeroU16, NonZeroU8};
use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign};
use crate::parsing::ParsedItem;
use crate::{Month, Weekday};
/// What kind of format is being parsed. This is used to ensure each part of the format (date, time,
/// offset) is the same kind.
#[derive(Debug, Clone, Copy)]
pub(crate) enum ExtendedKind {
/// The basic format.
Basic,
/// The extended format.
Extended,
/// ¯\_(ツ)_/¯
Unknown,
}
impl ExtendedKind {
/// Is it possible that the format is extended?
pub(crate) const fn maybe_extended(self) -> bool {
matches!(self, Self::Extended | Self::Unknown)
}
/// Is the format known for certain to be extended?
pub(crate) const fn is_extended(self) -> bool {
matches!(self, Self::Extended)
}
/// If the kind is `Unknown`, make it `Basic`. Otherwise, do nothing. Returns `Some` if and only
/// if the kind is now `Basic`.
pub(crate) fn coerce_basic(&mut self) -> Option<()> {
match self {
Self::Basic => Some(()),
Self::Extended => None,
Self::Unknown => {
*self = Self::Basic;
Some(())
}
}
}
/// If the kind is `Unknown`, make it `Extended`. Otherwise, do nothing. Returns `Some` if and
/// only if the kind is now `Extended`.
pub(crate) fn coerce_extended(&mut self) -> Option<()> {
match self {
Self::Basic => None,
Self::Extended => Some(()),
Self::Unknown => {
*self = Self::Extended;
Some(())
}
}
}
}
/// Parse a possibly expanded year.
pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
Some(match sign(input) {
Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| {
let val = val as i32;
if sign == b'-' { -val } else { val }
}),
None => exactly_n_digits::<4, u32>(input)?.map(|val| val as _),
})
}
/// Parse a month.
pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
first_match(
[
(b"01".as_slice(), Month::January),
(b"02".as_slice(), Month::February),
(b"03".as_slice(), Month::March),
(b"04".as_slice(), Month::April),
(b"05".as_slice(), Month::May),
(b"06".as_slice(), Month::June),
(b"07".as_slice(), Month::July),
(b"08".as_slice(), Month::August),
(b"09".as_slice(), Month::September),
(b"10".as_slice(), Month::October),
(b"11".as_slice(), Month::November),
(b"12".as_slice(), Month::December),
],
true,
)(input)
}
/// Parse a week number.
pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
exactly_n_digits::<2, _>(input)
}
/// Parse a day of the month.
pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> {
exactly_n_digits::<2, _>(input)
}
/// Parse a day of the week.
pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
first_match(
[
(b"1".as_slice(), Weekday::Monday),
(b"2".as_slice(), Weekday::Tuesday),
(b"3".as_slice(), Weekday::Wednesday),
(b"4".as_slice(), Weekday::Thursday),
(b"5".as_slice(), Weekday::Friday),
(b"6".as_slice(), Weekday::Saturday),
(b"7".as_slice(), Weekday::Sunday),
],
true,
)(input)
}
/// Parse a day of the year.
pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU16>> {
exactly_n_digits::<3, _>(input)
}
/// Parse the hour.
pub(crate) fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits::<2, _>(input)
}
/// Parse the minute.
pub(crate) fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits::<2, _>(input)
}
/// Parse a floating point number as its integer and optional fractional parts.
///
/// The number must have two digits before the decimal point. If a decimal point is present, at
/// least one digit must follow.
///
/// The return type is a tuple of the integer part and optional fraction part.
pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> {
// Two digits before the decimal.
let ParsedItem(input, integer_part) = match input {
[
first_digit @ b'0'..=b'9',
second_digit @ b'0'..=b'9',
input @ ..,
] => ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0')),
_ => return None,
};
if let Some(ParsedItem(input, ())) = decimal_sign(input) {
// Mandatory post-decimal digit.
let ParsedItem(mut input, mut fractional_part) =
any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.);
let mut divisor = 10.;
// Any number of subsequent digits.
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
input = new_input;
divisor *= 10.;
fractional_part += (digit - b'0') as f64 / divisor;
}
Some(ParsedItem(input, (integer_part, Some(fractional_part))))
} else {
Some(ParsedItem(input, (integer_part, None)))
}
}
/// Parse a "decimal sign", which is either a comma or a period.
fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
ascii_char::<b'.'>(input).or_else(|| ascii_char::<b','>(input))
}

Просмотреть файл

@ -1,8 +1,10 @@
//! Combinators for rules as defined in an RFC.
//! Combinators for rules as defined in a standard.
//!
//! These rules have been converted strictly following the ABNF syntax as specified in [RFC 2234].
//! When applicable, 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 iso8601;
pub(crate) mod rfc2234;
pub(crate) mod rfc2822;

186
third_party/rust/time/src/parsing/component.rs поставляемый
Просмотреть файл

@ -19,10 +19,10 @@ pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<Pars
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)?;
exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
#[cfg(feature = "large-dates")]
let ParsedItem(input, year) =
n_to_m_digits_padded::<u32, 4, 6>(modifiers.padding)(input)?;
n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(year as i32))),
None if modifiers.sign_is_mandatory || year >= 10_000 => None,
@ -30,7 +30,7 @@ pub(crate) fn parse_year(input: &[u8], modifiers: modifier::Year) -> Option<Pars
}
}
modifier::YearRepr::LastTwo => {
Some(exactly_n_digits_padded::<u32, 2>(modifiers.padding)(input)?.map(|v| v as i32))
Some(exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?.map(|v| v as i32))
}
}
}
@ -44,36 +44,36 @@ pub(crate) fn parse_month(
let ParsedItem(remaining, value) = first_match(
match modifiers.repr {
modifier::MonthRepr::Numerical => {
return exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)?
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),
(b"January".as_slice(), January),
(b"February".as_slice(), February),
(b"March".as_slice(), March),
(b"April".as_slice(), April),
(b"May".as_slice(), May),
(b"June".as_slice(), June),
(b"July".as_slice(), July),
(b"August".as_slice(), August),
(b"September".as_slice(), September),
(b"October".as_slice(), October),
(b"November".as_slice(), November),
(b"December".as_slice(), 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),
(b"Jan".as_slice(), January),
(b"Feb".as_slice(), February),
(b"Mar".as_slice(), March),
(b"Apr".as_slice(), April),
(b"May".as_slice(), May),
(b"Jun".as_slice(), June),
(b"Jul".as_slice(), July),
(b"Aug".as_slice(), August),
(b"Sep".as_slice(), September),
(b"Oct".as_slice(), October),
(b"Nov".as_slice(), November),
(b"Dec".as_slice(), December),
],
},
modifiers.case_sensitive,
@ -86,7 +86,7 @@ pub(crate) fn parse_week_number(
input: &[u8],
modifiers: modifier::WeekNumber,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "weekday" component of a `Date`.
@ -97,58 +97,58 @@ pub(crate) fn parse_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),
(b"Mon".as_slice(), Weekday::Monday),
(b"Tue".as_slice(), Weekday::Tuesday),
(b"Wed".as_slice(), Weekday::Wednesday),
(b"Thu".as_slice(), Weekday::Thursday),
(b"Fri".as_slice(), Weekday::Friday),
(b"Sat".as_slice(), Weekday::Saturday),
(b"Sun".as_slice(), 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),
(b"Monday".as_slice(), Weekday::Monday),
(b"Tuesday".as_slice(), Weekday::Tuesday),
(b"Wednesday".as_slice(), Weekday::Wednesday),
(b"Thursday".as_slice(), Weekday::Thursday),
(b"Friday".as_slice(), Weekday::Friday),
(b"Saturday".as_slice(), Weekday::Saturday),
(b"Sunday".as_slice(), 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),
(b"1".as_slice(), Weekday::Monday),
(b"2".as_slice(), Weekday::Tuesday),
(b"3".as_slice(), Weekday::Wednesday),
(b"4".as_slice(), Weekday::Thursday),
(b"5".as_slice(), Weekday::Friday),
(b"6".as_slice(), Weekday::Saturday),
(b"0".as_slice(), 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),
(b"2".as_slice(), Weekday::Monday),
(b"3".as_slice(), Weekday::Tuesday),
(b"4".as_slice(), Weekday::Wednesday),
(b"5".as_slice(), Weekday::Thursday),
(b"6".as_slice(), Weekday::Friday),
(b"7".as_slice(), Weekday::Saturday),
(b"1".as_slice(), 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),
(b"0".as_slice(), Weekday::Monday),
(b"1".as_slice(), Weekday::Tuesday),
(b"2".as_slice(), Weekday::Wednesday),
(b"3".as_slice(), Weekday::Thursday),
(b"4".as_slice(), Weekday::Friday),
(b"5".as_slice(), Weekday::Saturday),
(b"6".as_slice(), 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),
(b"1".as_slice(), Weekday::Monday),
(b"2".as_slice(), Weekday::Tuesday),
(b"3".as_slice(), Weekday::Wednesday),
(b"4".as_slice(), Weekday::Thursday),
(b"5".as_slice(), Weekday::Friday),
(b"6".as_slice(), Weekday::Saturday),
(b"7".as_slice(), Weekday::Sunday),
],
},
modifiers.case_sensitive,
@ -160,7 +160,7 @@ pub(crate) fn parse_ordinal(
input: &[u8],
modifiers: modifier::Ordinal,
) -> Option<ParsedItem<'_, NonZeroU16>> {
exactly_n_digits_padded::<_, 3>(modifiers.padding)(input)
exactly_n_digits_padded::<3, _>(modifiers.padding)(input)
}
/// Parse the "day" component of a `Date`.
@ -168,7 +168,7 @@ pub(crate) fn parse_day(
input: &[u8],
modifiers: modifier::Day,
) -> Option<ParsedItem<'_, NonZeroU8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
// endregion date components
@ -184,7 +184,7 @@ pub(crate) enum Period {
/// 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)
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "minute" component of a `Time`.
@ -192,7 +192,7 @@ pub(crate) fn parse_minute(
input: &[u8],
modifiers: modifier::Minute,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "second" component of a `Time`.
@ -200,7 +200,7 @@ pub(crate) fn parse_second(
input: &[u8],
modifiers: modifier::Second,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<_, 2>(modifiers.padding)(input)
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.
@ -210,9 +210,15 @@ pub(crate) fn parse_period(
) -> Option<ParsedItem<'_, Period>> {
first_match(
if modifiers.is_uppercase {
[(&b"AM"[..], Period::Am), (&b"PM"[..], Period::Pm)]
[
(b"AM".as_slice(), Period::Am),
(b"PM".as_slice(), Period::Pm),
]
} else {
[(&b"am"[..], Period::Am), (&b"pm"[..], Period::Pm)]
[
(b"am".as_slice(), Period::Am),
(b"pm".as_slice(), Period::Pm),
]
},
modifiers.case_sensitive,
)(input)
@ -225,15 +231,15 @@ pub(crate) fn parse_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)?,
One => exactly_n_digits::<1, u32>(input)?.map(|v| v * 100_000_000),
Two => exactly_n_digits::<2, u32>(input)?.map(|v| v * 10_000_000),
Three => exactly_n_digits::<3, u32>(input)?.map(|v| v * 1_000_000),
Four => exactly_n_digits::<4, u32>(input)?.map(|v| v * 100_000),
Five => exactly_n_digits::<5, u32>(input)?.map(|v| v * 10_000),
Six => exactly_n_digits::<6, u32>(input)?.map(|v| v * 1_000),
Seven => exactly_n_digits::<7, u32>(input)?.map(|v| v * 100),
Eight => exactly_n_digits::<8, u32>(input)?.map(|v| v * 10),
Nine => exactly_n_digits::<9, _>(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
any_digit(input)?.map(|v| (v - b'0') as u32 * 100_000_000);
@ -258,7 +264,7 @@ pub(crate) fn parse_offset_hour(
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)?;
let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?;
match sign {
Some(b'-') => Some(ParsedItem(input, -(hour as i8))),
None if modifiers.sign_is_mandatory => None,
@ -272,7 +278,7 @@ pub(crate) fn parse_offset_minute(
modifiers: modifier::OffsetMinute,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
.map(|offset_minute| offset_minute as _),
)
}
@ -283,7 +289,7 @@ pub(crate) fn parse_offset_second(
modifiers: modifier::OffsetSecond,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<u8, 2>(modifiers.padding)(input)?
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
.map(|offset_second| offset_second as _),
)
}

308
third_party/rust/time/src/parsing/iso8601.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,308 @@
//! Parse parts of an ISO 8601-formatted value.
use crate::error;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::Iso8601;
use crate::parsing::combinator::rfc::iso8601::{
day, dayk, dayo, float, hour, min, month, week, year, ExtendedKind,
};
use crate::parsing::combinator::{ascii_char, sign};
use crate::parsing::{Parsed, ParsedItem};
impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
// Basic: [year][month][day]
// Extended: [year]["-"][month]["-"][day]
// Basic: [year][dayo]
// Extended: [year]["-"][dayo]
// Basic: [year]["W"][week][dayk]
// Extended: [year]["-"]["W"][week]["-"][dayk]
/// Parse a date in the basic or extended format. Reduced precision is permitted.
pub(crate) fn parse_date<'a>(
parsed: &'a mut Parsed,
extended_kind: &'a mut ExtendedKind,
) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
move |input| {
// Same for any acceptable format.
let ParsedItem(mut input, year) = year(input).ok_or(InvalidComponent("year"))?;
*extended_kind = match ascii_char::<b'-'>(input) {
Some(ParsedItem(new_input, ())) => {
input = new_input;
ExtendedKind::Extended
}
None => ExtendedKind::Basic, // no separator before mandatory month/ordinal/week
};
let mut ret_error = match (|| {
let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?;
if extended_kind.is_extended() {
input = ascii_char::<b'-'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
}
let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?;
Ok(ParsedItem(input, (month, day)))
})() {
Ok(ParsedItem(input, (month, day))) => {
*parsed = parsed
.with_year(year)
.ok_or(InvalidComponent("year"))?
.with_month(month)
.ok_or(InvalidComponent("month"))?
.with_day(day)
.ok_or(InvalidComponent("day"))?;
return Ok(input);
}
Err(err) => err,
};
// Don't check for `None`, as the error from year-month-day will always take priority.
if let Some(ParsedItem(input, ordinal)) = dayo(input) {
*parsed = parsed
.with_year(year)
.ok_or(InvalidComponent("year"))?
.with_ordinal(ordinal)
.ok_or(InvalidComponent("ordinal"))?;
return Ok(input);
}
match (|| {
let input = ascii_char::<b'W'>(input)
.ok_or((false, InvalidLiteral))?
.into_inner();
let ParsedItem(mut input, week) =
week(input).ok_or((true, InvalidComponent("week")))?;
if extended_kind.is_extended() {
input = ascii_char::<b'-'>(input)
.ok_or((true, InvalidLiteral))?
.into_inner();
}
let ParsedItem(input, weekday) =
dayk(input).ok_or((true, InvalidComponent("weekday")))?;
Ok(ParsedItem(input, (week, weekday)))
})() {
Ok(ParsedItem(input, (week, weekday))) => {
*parsed = parsed
.with_iso_year(year)
.ok_or(InvalidComponent("year"))?
.with_iso_week_number(week)
.ok_or(InvalidComponent("week"))?
.with_weekday(weekday)
.ok_or(InvalidComponent("weekday"))?;
return Ok(input);
}
Err((false, _err)) => {}
// This error is more accurate than the one from year-month-day.
Err((true, err)) => ret_error = err,
}
Err(ret_error.into())
}
}
// Basic: ["T"][hour][min][sec]
// Extended: ["T"][hour][":"][min][":"][sec]
// Reduced precision: components after [hour] (including their preceding separator) can be
// omitted. ["T"] can be omitted if there is no date present.
/// Parse a time in the basic or extended format. Reduced precision is permitted.
pub(crate) fn parse_time<'a>(
parsed: &'a mut Parsed,
extended_kind: &'a mut ExtendedKind,
date_is_present: bool,
) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
move |mut input| {
if date_is_present {
input = ascii_char::<b'T'>(input)
.ok_or(InvalidLiteral)?
.into_inner();
}
let ParsedItem(mut input, hour) = float(input).ok_or(InvalidComponent("hour"))?;
match hour {
(hour, None) => parsed.set_hour_24(hour).ok_or(InvalidComponent("hour"))?,
(hour, Some(fractional_part)) => {
*parsed = parsed
.with_hour_24(hour)
.ok_or(InvalidComponent("hour"))?
.with_minute((fractional_part * 60.0) as _)
.ok_or(InvalidComponent("minute"))?
.with_second((fractional_part * 3600.0 % 60.) as _)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
(fractional_part * 3_600. * 1_000_000_000. % 1_000_000_000.) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
};
if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
extended_kind
.coerce_extended()
.ok_or(InvalidComponent("minute"))?;
input = new_input;
};
let mut input = match float(input) {
Some(ParsedItem(input, (minute, None))) => {
extended_kind.coerce_basic();
parsed
.set_minute(minute)
.ok_or(InvalidComponent("minute"))?;
input
}
Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
// `None` is valid behavior, so don't error if this fails.
extended_kind.coerce_basic();
*parsed = parsed
.with_minute(minute)
.ok_or(InvalidComponent("minute"))?
.with_second((fractional_part * 60.) as _)
.ok_or(InvalidComponent("second"))?
.with_subsecond(
(fractional_part * 60. * 1_000_000_000. % 1_000_000_000.) as _,
)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
// colon was present, so minutes are required
None if extended_kind.is_extended() => {
return Err(error::Parse::ParseFromDescription(InvalidComponent(
"minute",
)));
}
None => {
// Missing components are assumed to be zero.
*parsed = parsed
.with_minute(0)
.ok_or(InvalidComponent("minute"))?
.with_second(0)
.ok_or(InvalidComponent("second"))?
.with_subsecond(0)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
};
if extended_kind.is_extended() {
match ascii_char::<b':'>(input) {
Some(ParsedItem(new_input, ())) => input = new_input,
None => {
*parsed = parsed
.with_second(0)
.ok_or(InvalidComponent("second"))?
.with_subsecond(0)
.ok_or(InvalidComponent("subsecond"))?;
return Ok(input);
}
}
}
let (input, second, subsecond) = match float(input) {
Some(ParsedItem(input, (second, None))) => (input, second, 0),
Some(ParsedItem(input, (second, Some(fractional_part)))) => {
(input, second, round(fractional_part * 1_000_000_000.) as _)
}
None if extended_kind.is_extended() => {
return Err(error::Parse::ParseFromDescription(InvalidComponent(
"second",
)));
}
// Missing components are assumed to be zero.
None => (input, 0, 0),
};
*parsed = parsed
.with_second(second)
.ok_or(InvalidComponent("second"))?
.with_subsecond(subsecond)
.ok_or(InvalidComponent("subsecond"))?;
Ok(input)
}
}
// Basic: [±][hour][min] or ["Z"]
// Extended: [±][hour][":"][min] or ["Z"]
// Reduced precision: [±][hour] or ["Z"]
/// Parse a UTC offset in the basic or extended format. Reduced precision is supported.
pub(crate) fn parse_offset<'a>(
parsed: &'a mut Parsed,
extended_kind: &'a mut ExtendedKind,
) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
move |input| {
if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
*parsed = parsed
.with_offset_hour(0)
.ok_or(InvalidComponent("offset hour"))?
.with_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))?
.with_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))?;
return Ok(input);
}
let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let mut input = hour(input)
.and_then(|parsed_item| {
parsed_item.consume_value(|hour| {
parsed.set_offset_hour(if sign == b'-' {
-(hour as i8)
} else {
hour as _
})
})
})
.ok_or(InvalidComponent("offset hour"))?;
if extended_kind.maybe_extended() {
if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
extended_kind
.coerce_extended()
.ok_or(InvalidComponent("offset minute"))?;
input = new_input;
};
}
let input = min(input)
.and_then(|parsed_item| {
parsed_item.consume_value(|min| {
parsed.set_offset_minute_signed(if sign == b'-' {
-(min as i8)
} else {
min as _
})
})
})
.ok_or(InvalidComponent("offset minute"))?;
// If `:` was present, the format has already been set to extended. As such, this call
// will do nothing in that case. If there wasn't `:` but minutes were
// present, we know it's the basic format. Do not use `?` on the call, as
// returning `None` is valid behavior.
extended_kind.coerce_basic();
Ok(input)
}
}
}
/// Round wrapper that uses hardware implementation if `std` is available, falling back to manual
/// implementation for `no_std`
fn round(value: f64) -> f64 {
#[cfg(feature = "std")]
{
value.round()
}
#[cfg(not(feature = "std"))]
{
round_impl(value)
}
}
#[cfg(not(feature = "std"))]
#[allow(clippy::missing_docs_in_private_items)]
fn round_impl(value: f64) -> f64 {
debug_assert!(value.is_sign_positive() && !value.is_nan());
let f = value % 1.;
if f < 0.5 { value - f } else { value - f + 1. }
}

1
third_party/rust/time/src/parsing/mod.rs поставляемый
Просмотреть файл

@ -2,6 +2,7 @@
pub(crate) mod combinator;
pub(crate) mod component;
mod iso8601;
pub(crate) mod parsable;
mod parsed;
pub(crate) mod shim;

390
third_party/rust/time/src/parsing/parsable.rs поставляемый
Просмотреть файл

@ -1,11 +1,13 @@
//! 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::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
use crate::format_description::FormatItem;
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
use crate::parsing::{Parsed, ParsedItem};
use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
@ -14,8 +16,13 @@ use crate::{error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffs
pub trait Parsable: sealed::Sealed {}
impl Parsable for FormatItem<'_> {}
impl Parsable for [FormatItem<'_>] {}
#[cfg(feature = "alloc")]
impl Parsable for OwnedFormatItem {}
#[cfg(feature = "alloc")]
impl Parsable for [OwnedFormatItem] {}
impl Parsable for Rfc2822 {}
impl Parsable for Rfc3339 {}
impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {}
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
@ -26,7 +33,6 @@ mod sealed {
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.
///
@ -98,6 +104,28 @@ impl sealed::Sealed for [FormatItem<'_>] {
}
}
#[cfg(feature = "alloc")]
impl sealed::Sealed for OwnedFormatItem {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_item(input, self)?)
}
}
#[cfg(feature = "alloc")]
impl sealed::Sealed for [OwnedFormatItem] {
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,
@ -131,13 +159,13 @@ impl sealed::Sealed for Rfc2822 {
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),
(b"Mon".as_slice(), Weekday::Monday),
(b"Tue".as_slice(), Weekday::Tuesday),
(b"Wed".as_slice(), Weekday::Wednesday),
(b"Thu".as_slice(), Weekday::Thursday),
(b"Fri".as_slice(), Weekday::Friday),
(b"Sat".as_slice(), Weekday::Saturday),
(b"Sun".as_slice(), Weekday::Sunday),
],
false,
)(input)
@ -145,70 +173,67 @@ impl sealed::Sealed for Rfc2822 {
.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)
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),
(b"Jan".as_slice(), Month::January),
(b"Feb".as_slice(), Month::February),
(b"Mar".as_slice(), Month::March),
(b"Apr".as_slice(), Month::April),
(b"May".as_slice(), Month::May),
(b"Jun".as_slice(), Month::June),
(b"Jul".as_slice(), Month::July),
(b"Aug".as_slice(), Month::August),
(b"Sep".as_slice(), Month::September),
(b"Oct".as_slice(), Month::October),
(b"Nov".as_slice(), Month::November),
(b"Dec".as_slice(), 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) {
let input = match exactly_n_digits::<4, u32>(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
fws(input).ok_or(InvalidLiteral)?.into_inner()
}
None => {
let input = exactly_n_digits::<u32, 2>(input)
let input = exactly_n_digits::<2, u32>(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
cfws(input).ok_or(InvalidLiteral)?.into_inner()
}
};
let input = exactly_n_digits::<_, 2>(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)
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)
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
cfws(input).ok_or(InvalidLiteral)?.into_inner()
} else {
cfws(input).ok_or(InvalidLiteral)?.into_inner()
};
@ -219,16 +244,16 @@ impl sealed::Sealed for Rfc2822 {
#[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),
(b"UT".as_slice(), 0),
(b"GMT".as_slice(), 0),
(b"EST".as_slice(), -5),
(b"EDT".as_slice(), -4),
(b"CST".as_slice(), -6),
(b"CDT".as_slice(), -5),
(b"MST".as_slice(), -7),
(b"MDT".as_slice(), -6),
(b"PST".as_slice(), -8),
(b"PDT".as_slice(), -7),
],
false,
)(input)
@ -253,7 +278,7 @@ impl sealed::Sealed for Rfc2822 {
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
@ -265,7 +290,7 @@ impl sealed::Sealed for Rfc2822 {
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
})
@ -273,6 +298,174 @@ impl sealed::Sealed for Rfc2822 {
Ok(input)
}
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, 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();
// This parses the weekday, but we don't actually use the value anywhere. Because of this,
// just return `()` to avoid unnecessary generated code.
let ParsedItem(input, ()) = first_match(
[
(b"Mon".as_slice(), ()),
(b"Tue".as_slice(), ()),
(b"Wed".as_slice(), ()),
(b"Thu".as_slice(), ()),
(b"Fri".as_slice(), ()),
(b"Sat".as_slice(), ()),
(b"Sun".as_slice(), ()),
],
false,
)(input)
.ok_or(InvalidComponent("weekday"))?;
let input = comma(input).ok_or(InvalidLiteral)?.into_inner();
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, day) =
n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let ParsedItem(input, month) = first_match(
[
(b"Jan".as_slice(), Month::January),
(b"Feb".as_slice(), Month::February),
(b"Mar".as_slice(), Month::March),
(b"Apr".as_slice(), Month::April),
(b"May".as_slice(), Month::May),
(b"Jun".as_slice(), Month::June),
(b"Jul".as_slice(), Month::July),
(b"Aug".as_slice(), Month::August),
(b"Sep".as_slice(), Month::September),
(b"Oct".as_slice(), Month::October),
(b"Nov".as_slice(), Month::November),
(b"Dec".as_slice(), Month::December),
],
false,
)(input)
.ok_or(InvalidComponent("month"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
let (input, year) = match exactly_n_digits::<4, u32>(input) {
Some(item) => {
let ParsedItem(input, year) = item
.flat_map(|year| if year >= 1900 { Some(year) } else { None })
.ok_or(InvalidComponent("year"))?;
let input = fws(input).ok_or(InvalidLiteral)?.into_inner();
(input, year)
}
None => {
let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input)
.map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }))
.ok_or(InvalidComponent("year"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
(input, year)
}
};
let ParsedItem(input, hour) =
exactly_n_digits::<2, _>(input).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 ParsedItem(input, minute) =
exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?;
let (input, mut second) = 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 ParsedItem(input, second) =
exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?;
let input = cfws(input).ok_or(InvalidLiteral)?.into_inner();
(input, second)
} else {
(cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0)
};
#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522
let zone_literal = first_match(
[
(b"UT".as_slice(), 0),
(b"GMT".as_slice(), 0),
(b"EST".as_slice(), -5),
(b"EDT".as_slice(), -4),
(b"CST".as_slice(), -6),
(b"CDT".as_slice(), -5),
(b"MST".as_slice(), -7),
(b"MDT".as_slice(), -6),
(b"PST".as_slice(), -8),
(b"PDT".as_slice(), -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,
});
let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal {
let ParsedItem(input, offset_hour) = zone_literal;
(input, offset_hour, 0)
} else {
let ParsedItem(input, offset_sign) =
sign(input).ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input)
.map(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
}
})
})
.ok_or(InvalidComponent("offset hour"))?;
let ParsedItem(input, offset_minute) =
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
(input, offset_hour, offset_minute as i8)
};
if !input.is_empty() {
return Err(error::Parse::UnexpectedTrailingCharacters);
}
let mut nanosecond = 0;
let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let dt = (|| {
let date = Date::from_calendar_date(year as _, month, day)?;
let time = Time::from_hms_nano(hour, minute, second, nanosecond)?;
let offset = UtcOffset::from_hms(offset_hour, offset_minute, 0)?;
Ok(date.with_time(time).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)
}
}
impl sealed::Sealed for Rfc3339 {
@ -289,30 +482,30 @@ impl sealed::Sealed for Rfc3339 {
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let input = exactly_n_digits::<u32, 4>(input)
let input = exactly_n_digits::<4, u32>(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)
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)
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)
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)
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)
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) {
@ -352,7 +545,7 @@ impl sealed::Sealed for Rfc3339 {
}
let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
let input = exactly_n_digits::<u8, 2>(input)
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.map(|offset_hour| {
if offset_sign == b'-' {
@ -365,9 +558,16 @@ impl sealed::Sealed for Rfc3339 {
})
.ok_or(InvalidComponent("offset hour"))?;
let input = colon(input).ok_or(InvalidLiteral)?.into_inner();
let input = exactly_n_digits::<u8, 2>(input)
let input = exactly_n_digits::<2, u8>(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value as _))
item.map(|offset_minute| {
if offset_sign == b'-' {
-(offset_minute as i8)
} else {
offset_minute as _
}
})
.consume_value(|value| parsed.set_offset_minute_signed(value))
})
.ok_or(InvalidComponent("offset minute"))?;
@ -384,24 +584,24 @@ impl sealed::Sealed for Rfc3339 {
let colon = ascii_char::<b':'>;
let ParsedItem(input, year) =
exactly_n_digits::<u32, 4>(input).ok_or(InvalidComponent("year"))?;
exactly_n_digits::<4, u32>(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"))?;
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"))?;
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"))?;
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"))?;
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"))?;
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)
@ -426,17 +626,21 @@ impl sealed::Sealed for Rfc3339 {
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"))?;
exactly_n_digits::<2, u8>(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"))?;
exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?;
UtcOffset::from_hms(
if offset_sign == b'-' {
-(offset_hour as i8)
} else {
offset_hour as _
},
offset_minute as _,
if offset_sign == b'-' {
-(offset_minute as i8)
} else {
offset_minute as _
},
0,
)
.map(|offset| ParsedItem(input, offset))
@ -489,4 +693,62 @@ impl sealed::Sealed for Rfc3339 {
Ok(dt)
}
}
impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
fn parse_into<'a>(
&self,
mut input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
use crate::parsing::combinator::rfc::iso8601::ExtendedKind;
let mut extended_kind = ExtendedKind::Unknown;
let mut date_is_present = false;
let mut time_is_present = false;
let mut offset_is_present = false;
let mut first_error = None;
match Self::parse_date(parsed, &mut extended_kind)(input) {
Ok(new_input) => {
input = new_input;
date_is_present = true;
}
Err(err) => {
first_error.get_or_insert(err);
}
}
match Self::parse_time(parsed, &mut extended_kind, date_is_present)(input) {
Ok(new_input) => {
input = new_input;
time_is_present = true;
}
Err(err) => {
first_error.get_or_insert(err);
}
}
// If a date and offset are present, a time must be as well.
if !date_is_present || time_is_present {
match Self::parse_offset(parsed, &mut extended_kind)(input) {
Ok(new_input) => {
input = new_input;
offset_is_present = true;
}
Err(err) => {
first_error.get_or_insert(err);
}
}
}
if !date_is_present && !time_is_present && !offset_is_present {
match first_error {
Some(err) => return Err(err),
None => unreachable!("an error should be present if no components were parsed"),
}
}
Ok(input)
}
}
// endregion well-known formats

430
third_party/rust/time/src/parsing/parsed.rs поставляемый
Просмотреть файл

@ -1,10 +1,12 @@
//! Information parsed from an input and format description.
use core::convert::{TryFrom, TryInto};
use core::mem::MaybeUninit;
use core::num::{NonZeroU16, NonZeroU8};
use crate::error::TryFromParsed::InsufficientInformation;
use crate::format_description::modifier::{WeekNumberRepr, YearRepr};
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
use crate::format_description::{Component, FormatItem};
use crate::parsing::component::{
parse_day, parse_hour, parse_minute, parse_month, parse_offset_hour, parse_offset_minute,
@ -14,108 +16,37 @@ use crate::parsing::component::{
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,
/// Sealed to prevent downstream implementations.
mod sealed {
use super::*;
/// A trait to allow `parse_item` to be generic.
pub trait AnyFormatItem {
/// Parse a single item, returning the remaining input on success.
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
input: &'a [u8],
) -> Result<&'a [u8], error::ParseFromDescription>;
}
}
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,
impl sealed::AnyFormatItem for FormatItem<'_> {
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
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) => {
match self {
Self::Literal(literal) => Parsed::parse_literal(input, literal),
Self::Component(component) => parsed.parse_component(input, *component),
Self::Compound(compound) => parsed.parse_items(input, compound),
Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)),
Self::First(items) => {
let mut first_err = None;
for item in items.iter() {
match self.parse_item(input, item) {
match parsed.parse_item(input, item) {
Ok(remaining_input) => return Ok(remaining_input),
Err(err) if first_err.is_none() => first_err = Some(err),
Err(_) => {}
@ -131,16 +62,165 @@ impl Parsed {
}
}
}
}
/// Parse a sequence of [`FormatItem`]s, mutating the struct. The remaining input is returned as
/// the `Ok` value.
#[cfg(feature = "alloc")]
impl sealed::AnyFormatItem for OwnedFormatItem {
fn parse_item<'a>(
&self,
parsed: &mut Parsed,
input: &'a [u8],
) -> Result<&'a [u8], error::ParseFromDescription> {
match self {
Self::Literal(literal) => Parsed::parse_literal(input, literal),
Self::Component(component) => parsed.parse_component(input, *component),
Self::Compound(compound) => parsed.parse_items(input, compound),
Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)),
Self::First(items) => {
let mut first_err = None;
for item in items.iter() {
match parsed.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),
}
}
}
}
}
/// 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 {
/// Bitflags indicating whether a particular field is present.
flags: u16,
/// Calendar year.
year: MaybeUninit<i32>,
/// The last two digits of the calendar year.
year_last_two: MaybeUninit<u8>,
/// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date).
iso_year: MaybeUninit<i32>,
/// The last two digits of the ISO week year.
iso_year_last_two: MaybeUninit<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: MaybeUninit<u8>,
/// Week of the year, where week one begins on the first Monday of the calendar year.
monday_week_number: MaybeUninit<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: MaybeUninit<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: MaybeUninit<u8>,
/// Second within the minute.
second: MaybeUninit<u8>,
/// Nanosecond within the second.
subsecond: MaybeUninit<u32>,
/// Whole hours of the UTC offset.
offset_hour: MaybeUninit<i8>,
/// Minutes within the hour of the UTC offset.
offset_minute: MaybeUninit<i8>,
/// Seconds within the minute of the UTC offset.
offset_second: MaybeUninit<i8>,
}
#[allow(clippy::missing_docs_in_private_items)]
impl Parsed {
const YEAR_FLAG: u16 = 1 << 0;
const YEAR_LAST_TWO_FLAG: u16 = 1 << 1;
const ISO_YEAR_FLAG: u16 = 1 << 2;
const ISO_YEAR_LAST_TWO_FLAG: u16 = 1 << 3;
const SUNDAY_WEEK_NUMBER_FLAG: u16 = 1 << 4;
const MONDAY_WEEK_NUMBER_FLAG: u16 = 1 << 5;
const HOUR_24_FLAG: u16 = 1 << 6;
const MINUTE_FLAG: u16 = 1 << 7;
const SECOND_FLAG: u16 = 1 << 8;
const SUBSECOND_FLAG: u16 = 1 << 9;
const OFFSET_HOUR_FLAG: u16 = 1 << 10;
const OFFSET_MINUTE_FLAG: u16 = 1 << 11;
const OFFSET_SECOND_FLAG: u16 = 1 << 12;
/// Indicates whether a leap second is permitted to be parsed. This is required by some
/// well-known formats.
const LEAP_SECOND_ALLOWED_FLAG: u16 = 1 << 13;
}
impl Parsed {
/// Create a new instance of `Parsed` with no information known.
pub const fn new() -> Self {
Self {
flags: 0,
year: MaybeUninit::uninit(),
year_last_two: MaybeUninit::uninit(),
iso_year: MaybeUninit::uninit(),
iso_year_last_two: MaybeUninit::uninit(),
month: None,
sunday_week_number: MaybeUninit::uninit(),
monday_week_number: MaybeUninit::uninit(),
iso_week_number: None,
weekday: None,
ordinal: None,
day: None,
hour_24: MaybeUninit::uninit(),
hour_12: None,
hour_12_is_pm: None,
minute: MaybeUninit::uninit(),
second: MaybeUninit::uninit(),
subsecond: MaybeUninit::uninit(),
offset_hour: MaybeUninit::uninit(),
offset_minute: MaybeUninit::uninit(),
offset_second: MaybeUninit::uninit(),
}
}
/// Parse a single [`FormatItem`] or [`OwnedFormatItem`], 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.
/// If a [`FormatItem::Optional`] or [`OwnedFormatItem::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: &impl sealed::AnyFormatItem,
) -> Result<&'a [u8], error::ParseFromDescription> {
item.parse_item(self, input)
}
/// Parse a sequence of [`FormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. The
/// remaining input is returned as the `Ok` value.
///
/// This method will fail if any of the contained [`FormatItem`]s or [`OwnedFormatItem`]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<'_>],
items: &[impl sealed::AnyFormatItem],
) -> 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.
@ -253,35 +333,49 @@ impl Parsed {
/// Generate getters for each of the fields.
macro_rules! getters {
($($name:ident: $ty:ty),+ $(,)?) => {$(
($($(@$flag:ident)? $name:ident: $ty:ty),+ $(,)?) => {$(
getters!(! $(@$flag)? $name: $ty);
)*};
(! $name:ident : $ty:ty) => {
/// Obtain the named component.
pub const fn $name(&self) -> Option<$ty> {
self.$name
}
)*}
};
(! @$flag:ident $name:ident : $ty:ty) => {
/// Obtain the named component.
pub const fn $name(&self) -> Option<$ty> {
if self.flags & Self::$flag != Self::$flag {
None
} else {
// SAFETY: We just checked if the field is present.
Some(unsafe { self.$name.assume_init() })
}
}
};
}
/// Getter methods
impl Parsed {
getters! {
year: i32,
year_last_two: u8,
iso_year: i32,
iso_year_last_two: u8,
@YEAR_FLAG year: i32,
@YEAR_LAST_TWO_FLAG year_last_two: u8,
@ISO_YEAR_FLAG iso_year: i32,
@ISO_YEAR_LAST_TWO_FLAG iso_year_last_two: u8,
month: Month,
sunday_week_number: u8,
monday_week_number: u8,
@SUNDAY_WEEK_NUMBER_FLAG sunday_week_number: u8,
@MONDAY_WEEK_NUMBER_FLAG monday_week_number: u8,
iso_week_number: NonZeroU8,
weekday: Weekday,
ordinal: NonZeroU16,
day: NonZeroU8,
hour_24: u8,
@HOUR_24_FLAG hour_24: u8,
hour_12: NonZeroU8,
hour_12_is_pm: bool,
minute: u8,
second: u8,
subsecond: u32,
offset_hour: i8,
@MINUTE_FLAG minute: u8,
@SECOND_FLAG second: u8,
@SUBSECOND_FLAG subsecond: u32,
@OFFSET_HOUR_FLAG offset_hour: i8,
}
/// Obtain the absolute value of the offset minute.
@ -292,7 +386,12 @@ impl Parsed {
/// Obtain the offset minute as an `i8`.
pub const fn offset_minute_signed(&self) -> Option<i8> {
self.offset_minute
if self.flags & Self::OFFSET_MINUTE_FLAG != Self::OFFSET_MINUTE_FLAG {
None
} else {
// SAFETY: We just checked if the field is present.
Some(unsafe { self.offset_minute.assume_init() })
}
}
/// Obtain the absolute value of the offset second.
@ -303,12 +402,17 @@ impl Parsed {
/// Obtain the offset second as an `i8`.
pub const fn offset_second_signed(&self) -> Option<i8> {
self.offset_second
if self.flags & Self::OFFSET_SECOND_FLAG != Self::OFFSET_SECOND_FLAG {
None
} else {
// SAFETY: We just checked if the field is present.
Some(unsafe { self.offset_second.assume_init() })
}
}
/// Obtain whether leap seconds are permitted in the current format.
pub(crate) const fn leap_second_allowed(&self) -> bool {
self.leap_second_allowed
self.flags & Self::LEAP_SECOND_ALLOWED_FLAG == Self::LEAP_SECOND_ALLOWED_FLAG
}
}
@ -316,13 +420,24 @@ impl Parsed {
///
/// 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),+ $(,)?) => {$(
($($(@$flag:ident)? $setter_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
setters!(! $(@$flag)? $setter_name $name: $ty);
)*};
(! $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(())
}
)*}
};
(! @$flag:ident $setter_name:ident $name:ident : $ty:ty) => {
/// Set the named component.
pub fn $setter_name(&mut self, value: $ty) -> Option<()> {
self.$name = MaybeUninit::new(value);
self.flags |= Self::$flag;
Some(())
}
};
}
/// Setter methods
@ -331,24 +446,24 @@ macro_rules! setters {
/// 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,
@YEAR_FLAG set_year year: i32,
@YEAR_LAST_TWO_FLAG set_year_last_two year_last_two: u8,
@ISO_YEAR_FLAG set_iso_year iso_year: i32,
@ISO_YEAR_LAST_TWO_FLAG 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,
@SUNDAY_WEEK_NUMBER_FLAG set_sunday_week_number sunday_week_number: u8,
@MONDAY_WEEK_NUMBER_FLAG 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,
@HOUR_24_FLAG 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,
@MINUTE_FLAG set_minute minute: u8,
@SECOND_FLAG set_second second: u8,
@SUBSECOND_FLAG set_subsecond subsecond: u32,
@OFFSET_HOUR_FLAG set_offset_hour offset_hour: i8,
}
/// Set the named component.
@ -366,7 +481,8 @@ impl Parsed {
/// Set the `offset_minute` component.
pub fn set_offset_minute_signed(&mut self, value: i8) -> Option<()> {
self.offset_minute = Some(value);
self.offset_minute = MaybeUninit::new(value);
self.flags |= Self::OFFSET_MINUTE_FLAG;
Some(())
}
@ -385,13 +501,18 @@ impl Parsed {
/// Set the `offset_second` component.
pub fn set_offset_second_signed(&mut self, value: i8) -> Option<()> {
self.offset_second = Some(value);
self.offset_second = MaybeUninit::new(value);
self.flags |= Self::OFFSET_SECOND_FLAG;
Some(())
}
/// Set the leap second allowed flag.
pub(crate) fn set_leap_second_allowed(&mut self, value: bool) {
self.leap_second_allowed = value;
if value {
self.flags |= Self::LEAP_SECOND_ALLOWED_FLAG;
} else {
self.flags &= !Self::LEAP_SECOND_ALLOWED_FLAG;
}
}
}
@ -399,13 +520,24 @@ impl Parsed {
///
/// 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),+ $(,)?) => {$(
($($(@$flag:ident)? $builder_name:ident $name:ident: $ty:ty),+ $(,)?) => {$(
builders!(! $(@$flag)? $builder_name $name: $ty);
)*};
(! $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)
}
)*}
};
(! @$flag:ident $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 = MaybeUninit::new(value);
self.flags |= Self::$flag;
Some(self)
}
};
}
/// Builder methods
@ -414,24 +546,24 @@ macro_rules! builders {
/// 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,
@YEAR_FLAG with_year year: i32,
@YEAR_LAST_TWO_FLAG with_year_last_two year_last_two: u8,
@ISO_YEAR_FLAG with_iso_year iso_year: i32,
@ISO_YEAR_LAST_TWO_FLAG 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,
@SUNDAY_WEEK_NUMBER_FLAG with_sunday_week_number sunday_week_number: u8,
@MONDAY_WEEK_NUMBER_FLAG 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,
@HOUR_24_FLAG 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,
@MINUTE_FLAG with_minute minute: u8,
@SECOND_FLAG with_second second: u8,
@SUBSECOND_FLAG with_subsecond subsecond: u32,
@OFFSET_HOUR_FLAG with_offset_hour offset_hour: i8,
}
/// Set the named component and return `self`.
@ -449,7 +581,8 @@ impl Parsed {
/// 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);
self.offset_minute = MaybeUninit::new(value);
self.flags |= Self::OFFSET_MINUTE_FLAG;
Some(self)
}
@ -468,7 +601,8 @@ impl Parsed {
/// 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);
self.offset_second = MaybeUninit::new(value);
self.flags |= Self::OFFSET_SECOND_FLAG;
Some(self)
}
}

Просмотреть файл

@ -13,7 +13,7 @@ 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)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct PrimitiveDateTime {
#[allow(clippy::missing_docs_in_private_items)]
pub(crate) date: Date,
@ -26,13 +26,28 @@ impl 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`
/// 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));
/// # use time::PrimitiveDateTime;
/// # use time_macros::datetime;
#[cfg_attr(
feature = "large-dates",
doc = "// Assuming `large-dates` feature is enabled."
)]
#[cfg_attr(
feature = "large-dates",
doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-999999-01-01 0:00));"
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "// Assuming `large-dates` feature is disabled."
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-9999-01-01 0:00));"
)]
/// ```
pub const MIN: Self = Self::new(Date::MIN, Time::MIN);
@ -40,20 +55,36 @@ impl 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`
/// 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));
/// # use time::PrimitiveDateTime;
/// # use time_macros::datetime;
#[cfg_attr(
feature = "large-dates",
doc = "// Assuming `large-dates` feature is enabled."
)]
#[cfg_attr(
feature = "large-dates",
doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+999999-12-31 23:59:59.999_999_999));"
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "// Assuming `large-dates` feature is disabled."
)]
#[cfg_attr(
not(feature = "large-dates"),
doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+9999-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}};
/// # use time::PrimitiveDateTime;
/// # use time_macros::{date, datetime, time};
/// assert_eq!(
/// PrimitiveDateTime::new(date!(2019-01-01), time!(0:00)),
/// datetime!(2019-01-01 0:00),
@ -67,7 +98,7 @@ impl PrimitiveDateTime {
/// Get the [`Date`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time::macros::{date, datetime};
/// # use time_macros::{date, datetime};
/// assert_eq!(datetime!(2019-01-01 0:00).date(), date!(2019-01-01));
/// ```
pub const fn date(self) -> Date {
@ -77,7 +108,7 @@ impl PrimitiveDateTime {
/// Get the [`Time`] component of the `PrimitiveDateTime`.
///
/// ```rust
/// # use time::macros::{datetime, time};
/// # 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
@ -88,7 +119,7 @@ impl PrimitiveDateTime {
/// Get the year of the date.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
@ -100,7 +131,8 @@ impl PrimitiveDateTime {
/// Get the month of the date.
///
/// ```rust
/// # use time::{macros::datetime, Month};
/// # use time::Month;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).month(), Month::January);
/// assert_eq!(datetime!(2019-12-31 0:00).month(), Month::December);
/// ```
@ -113,7 +145,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `1..=31`.
///
/// ```rust
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).day(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).day(), 31);
/// ```
@ -126,7 +158,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `1..=366` (`1..=365` for common years).
///
/// ```rust
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(datetime!(2019-01-01 0:00).ordinal(), 1);
/// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365);
/// ```
@ -139,7 +171,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `1..=53`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
@ -155,7 +187,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
@ -170,7 +202,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..=53`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
@ -183,7 +215,8 @@ impl PrimitiveDateTime {
/// Get the year, month, and day.
///
/// ```rust
/// # use time::{macros::datetime, Month};
/// # use time::Month;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_calendar_date(),
/// (2019, Month::January, 1)
@ -196,7 +229,7 @@ impl PrimitiveDateTime {
/// Get the year and ordinal day number.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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) {
@ -206,7 +239,8 @@ impl PrimitiveDateTime {
/// Get the ISO 8601 year, week number, and weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::datetime};
/// # use time::Weekday::*;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2019-01-01 0:00).to_iso_week_date(),
/// (2019, 1, Tuesday)
@ -235,7 +269,8 @@ impl PrimitiveDateTime {
/// Get the weekday.
///
/// ```rust
/// # use time::{Weekday::*, macros::datetime};
/// # use time::Weekday::*;
/// # use time_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);
@ -259,7 +294,7 @@ impl PrimitiveDateTime {
/// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms).
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
@ -274,7 +309,7 @@ impl PrimitiveDateTime {
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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));
/// ```
@ -285,7 +320,7 @@ impl PrimitiveDateTime {
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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(),
@ -299,7 +334,7 @@ impl PrimitiveDateTime {
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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(),
@ -313,7 +348,7 @@ impl PrimitiveDateTime {
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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(),
@ -329,7 +364,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
/// ```
@ -342,7 +377,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
/// ```
@ -355,7 +390,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
/// ```
@ -368,7 +403,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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);
/// ```
@ -381,7 +416,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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(),
@ -397,7 +432,7 @@ impl PrimitiveDateTime {
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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(),
@ -414,7 +449,7 @@ impl PrimitiveDateTime {
/// [`UtcOffset`], return an [`OffsetDateTime`].
///
/// ```rust
/// # use time::macros::{datetime, offset};
/// # use time_macros::{datetime, offset};
/// assert_eq!(
/// datetime!(2019-01-01 0:00)
/// .assume_offset(offset!(UTC))
@ -430,7 +465,7 @@ impl PrimitiveDateTime {
/// ```
pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime {
OffsetDateTime {
utc_datetime: self.offset_to_utc(offset),
local_datetime: self,
offset,
}
}
@ -439,17 +474,14 @@ impl PrimitiveDateTime {
/// [`OffsetDateTime`].
///
/// ```rust
/// # use time::macros::datetime;
/// # 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,
}
self.assume_offset(UtcOffset::UTC)
}
// endregion attach offset
@ -458,7 +490,7 @@ impl PrimitiveDateTime {
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// let datetime = Date::MIN.midnight();
/// assert_eq!(datetime.checked_add((-2).days()), None);
///
@ -488,7 +520,7 @@ impl PrimitiveDateTime {
///
/// ```
/// # use time::{Date, ext::NumericalDuration};
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// let datetime = Date::MIN.midnight();
/// assert_eq!(datetime.checked_sub(2.days()), None);
///
@ -520,7 +552,7 @@ impl PrimitiveDateTime {
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(
/// PrimitiveDateTime::MIN.saturating_add((-2).days()),
/// PrimitiveDateTime::MIN
@ -550,7 +582,7 @@ impl PrimitiveDateTime {
///
/// ```
/// # use time::{PrimitiveDateTime, ext::NumericalDuration};
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(
/// PrimitiveDateTime::MIN.saturating_sub(2.days()),
/// PrimitiveDateTime::MIN
@ -584,7 +616,7 @@ impl PrimitiveDateTime {
/// Replace the time, preserving the date.
///
/// ```rust
/// # use time::macros::{datetime, time};
/// # use time_macros::{datetime, time};
/// assert_eq!(
/// datetime!(2020-01-01 17:00).replace_time(time!(5:00)),
/// datetime!(2020-01-01 5:00)
@ -598,7 +630,7 @@ impl PrimitiveDateTime {
/// Replace the date, preserving the time.
///
/// ```rust
/// # use time::macros::{datetime, date};
/// # 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)
@ -612,7 +644,7 @@ impl PrimitiveDateTime {
/// Replace the year. The month and day will be unchanged.
///
/// ```rust
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_year(2019),
/// Ok(datetime!(2019 - 02 - 18 12:00))
@ -628,7 +660,7 @@ impl PrimitiveDateTime {
/// Replace the month of the year.
///
/// ```rust
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// # use time::Month;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_month(Month::January),
@ -644,7 +676,7 @@ impl PrimitiveDateTime {
/// Replace the day of the month.
///
/// ```rust
/// # use time::macros::datetime;
/// # use time_macros::datetime;
/// assert_eq!(
/// datetime!(2022 - 02 - 18 12:00).replace_day(1),
/// Ok(datetime!(2022 - 02 - 01 12:00))
@ -660,7 +692,7 @@ impl PrimitiveDateTime {
/// Replace the clock hour.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -677,7 +709,7 @@ impl PrimitiveDateTime {
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -694,7 +726,7 @@ impl PrimitiveDateTime {
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -711,7 +743,7 @@ impl PrimitiveDateTime {
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -731,7 +763,7 @@ impl PrimitiveDateTime {
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -751,7 +783,7 @@ impl PrimitiveDateTime {
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time::macros::datetime;
/// # 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))
@ -767,45 +799,6 @@ impl PrimitiveDateTime {
}
// 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 {
@ -823,7 +816,8 @@ impl PrimitiveDateTime {
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::datetime};
/// # use time::format_description;
/// # use time_macros::datetime;
/// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
/// assert_eq!(
/// datetime!(2020-01-02 03:04:05).format(&format)?,
@ -842,8 +836,9 @@ impl PrimitiveDateTime {
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::datetime, PrimitiveDateTime};
/// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?;
/// # use time::PrimitiveDateTime;
/// # use time_macros::{datetime, format_description};
/// let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
/// assert_eq!(
/// PrimitiveDateTime::parse("2020-01-02 03:04:05", &format)?,
/// datetime!(2020-01-02 03:04:05)
@ -863,6 +858,12 @@ impl fmt::Display for PrimitiveDateTime {
write!(f, "{} {}", self.date, self.time)
}
}
impl fmt::Debug for PrimitiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls

20
third_party/rust/time/src/quickcheck.rs поставляемый
Просмотреть файл

@ -1,11 +1,9 @@
//! Implementations of the [`quickcheck::Arbitrary`](quickcheck_dep::Arbitrary) trait.
//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::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;
//!
@ -38,7 +36,7 @@
use alloc::boxed::Box;
use quickcheck_dep::{empty_shrinker, single_shrinker, Arbitrary, Gen};
use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
@ -159,9 +157,9 @@ impl Arbitrary for OffsetDateTime {
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.utc_datetime.utc_to_offset(self.offset), self.offset)
(self.local_datetime, self.offset)
.shrink()
.map(|(utc_datetime, offset)| utc_datetime.assume_offset(offset)),
.map(|(local_datetime, offset)| local_datetime.assume_offset(offset)),
)
}
}
@ -176,7 +174,10 @@ impl Arbitrary for Weekday {
3 => Thursday,
4 => Friday,
5 => Saturday,
_ => Sunday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
@ -203,7 +204,10 @@ impl Arbitrary for Month {
9 => September,
10 => October,
11 => November,
_ => December,
val => {
debug_assert!(val == 12);
December
}
}
}

10
third_party/rust/time/src/rand.rs поставляемый
Просмотреть файл

@ -67,7 +67,10 @@ impl Distribution<Weekday> for Standard {
3 => Thursday,
4 => Friday,
5 => Saturday,
_ => Sunday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
}
@ -87,7 +90,10 @@ impl Distribution<Month> for Standard {
9 => September,
10 => October,
11 => November,
_ => December,
val => {
debug_assert!(val == 12);
December
}
}
}
}

77
third_party/rust/time/src/serde/iso8601.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,77 @@
//! Use the well-known [ISO 8601 format] when serializing and deserializing an [`OffsetDateTime`].
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html
//! [with]: https://serde.rs/field-attrs.html#with
#[cfg(feature = "parsing")]
use core::marker::PhantomData;
#[cfg(feature = "formatting")]
use serde::ser::Error as _;
#[cfg(feature = "parsing")]
use serde::Deserializer;
#[cfg(feature = "formatting")]
use serde::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::format_description::well_known::iso8601::{Config, EncodedConfig};
use crate::format_description::well_known::Iso8601;
use crate::OffsetDateTime;
/// The configuration of ISO 8601 used for serde implementations.
pub(crate) const SERDE_CONFIG: EncodedConfig =
Config::DEFAULT.set_year_is_six_digits(true).encode();
/// Serialize an [`OffsetDateTime`] using the well-known ISO 8601 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
) -> Result<S::Ok, S::Error> {
datetime
.format(&Iso8601::<SERDE_CONFIG>)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its ISO 8601 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
deserializer.deserialize_any(Visitor::<Iso8601<SERDE_CONFIG>>(PhantomData))
}
/// Use the well-known ISO 8601 format when serializing and deserializing an
/// [`Option<OffsetDateTime>`].
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html
/// [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 ISO 8601 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
) -> Result<S::Ok, S::Error> {
option
.map(|odt| odt.format(&Iso8601::<SERDE_CONFIG>))
.transpose()
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`Option<OffsetDateTime>`] from its ISO 8601 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer.deserialize_option(Visitor::<Option<Iso8601<SERDE_CONFIG>>>(PhantomData))
}
}

95
third_party/rust/time/src/serde/mod.rs поставляемый
Просмотреть файл

@ -13,9 +13,11 @@ macro_rules! item {
};
}
#[cfg(feature = "serde-well-known")]
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod iso8601;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc2822;
#[cfg(feature = "serde-well-known")]
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc3339;
pub mod timestamp;
mod visitor;
@ -37,30 +39,53 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// submodule (`mod_name::option`) is also generated for `Option<Date>`. Both modules are only
/// visible in the current scope.
///
/// The returned `Option` will contain a deserialized value if present and `None` if the field
/// is present but the value is `null` (or the equivalent in other formats). To return `None`
/// when the field is not present, you should use `#[serde(default)]` on the field.
///
/// # Examples
///
/// ```
/// ```rust,no_run
/// # use time::OffsetDateTime;
/// # use ::serde::{Serialize, Deserialize};
#[cfg_attr(
all(feature = "formatting", feature = "parsing"),
doc = "use ::serde::{Serialize, Deserialize};"
)]
#[cfg_attr(
all(feature = "formatting", not(feature = "parsing")),
doc = "use ::serde::Serialize;"
)]
#[cfg_attr(
all(not(feature = "formatting"), feature = "parsing"),
doc = "use ::serde::Deserialize;"
)]
/// use time::serde;
///
/// // Makes a module `mod my_format { ... }`.
/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]");
///
/// #[derive(Serialize, Deserialize)]
#[cfg_attr(
all(feature = "formatting", feature = "parsing"),
doc = "#[derive(Serialize, Deserialize)]"
)]
#[cfg_attr(
all(feature = "formatting", not(feature = "parsing")),
doc = "#[derive(Serialize)]"
)]
#[cfg_attr(
all(not(feature = "formatting"), feature = "parsing"),
doc = "#[derive(Deserialize)]"
)]
/// # #[allow(dead_code)]
/// 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"))]
#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing"),))]
pub use time_macros::serde_format_description as format_description;
use self::visitor::Visitor;
@ -95,7 +120,11 @@ impl Serialize for Date {
impl<'a> Deserialize<'a> for Date {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
}
}
}
// endregion date
@ -118,7 +147,11 @@ impl Serialize for Duration {
impl<'a> Deserialize<'a> for Duration {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
}
}
}
// endregion Duration
@ -161,7 +194,11 @@ impl Serialize for OffsetDateTime {
impl<'a> Deserialize<'a> for OffsetDateTime {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(9, Visitor::<Self>(PhantomData))
}
}
}
// endregion OffsetDateTime
@ -199,7 +236,11 @@ impl Serialize for PrimitiveDateTime {
impl<'a> Deserialize<'a> for PrimitiveDateTime {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
}
}
}
// endregion PrimitiveDateTime
@ -233,7 +274,11 @@ impl Serialize for Time {
impl<'a> Deserialize<'a> for Time {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(4, Visitor::<Self>(PhantomData))
}
}
}
// endregion Time
@ -270,7 +315,11 @@ impl Serialize for UtcOffset {
impl<'a> Deserialize<'a> for UtcOffset {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(3, Visitor::<Self>(PhantomData))
}
}
}
// endregion UtcOffset
@ -291,7 +340,11 @@ impl Serialize for Weekday {
impl<'a> Deserialize<'a> for Weekday {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
}
}
}
// endregion Weekday
@ -312,7 +365,11 @@ impl Serialize for Month {
impl<'a> Deserialize<'a> for Month {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
}
}
}
// endregion Month

14
third_party/rust/time/src/serde/rfc2822.rs поставляемый
Просмотреть файл

@ -5,16 +5,23 @@
//! [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3
//! [with]: https://serde.rs/field-attrs.html#with
#[cfg(feature = "parsing")]
use core::marker::PhantomData;
#[cfg(feature = "formatting")]
use serde::ser::Error as _;
use serde::{Deserializer, Serialize, Serializer};
#[cfg(feature = "parsing")]
use serde::Deserializer;
#[cfg(feature = "formatting")]
use serde::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::format_description::well_known::Rfc2822;
use crate::OffsetDateTime;
/// Serialize an [`OffsetDateTime`] using the well-known RFC2822 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
@ -26,8 +33,9 @@ pub fn serialize<S: Serializer>(
}
/// Deserialize an [`OffsetDateTime`] from its RFC2822 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
deserializer.deserialize_any(Visitor::<Rfc2822>(PhantomData))
deserializer.deserialize_str(Visitor::<Rfc2822>(PhantomData))
}
/// Use the well-known [RFC2822 format] when serializing and deserializing an
@ -42,6 +50,7 @@ pub mod option {
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC2822 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
@ -54,6 +63,7 @@ pub mod option {
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC2822 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {

14
third_party/rust/time/src/serde/rfc3339.rs поставляемый
Просмотреть файл

@ -5,16 +5,23 @@
//! [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6
//! [with]: https://serde.rs/field-attrs.html#with
#[cfg(feature = "parsing")]
use core::marker::PhantomData;
#[cfg(feature = "formatting")]
use serde::ser::Error as _;
use serde::{Deserializer, Serialize, Serializer};
#[cfg(feature = "parsing")]
use serde::Deserializer;
#[cfg(feature = "formatting")]
use serde::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::format_description::well_known::Rfc3339;
use crate::OffsetDateTime;
/// Serialize an [`OffsetDateTime`] using the well-known RFC3339 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
datetime: &OffsetDateTime,
serializer: S,
@ -26,8 +33,9 @@ pub fn serialize<S: Serializer>(
}
/// Deserialize an [`OffsetDateTime`] from its RFC3339 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> {
deserializer.deserialize_any(Visitor::<Rfc3339>(PhantomData))
deserializer.deserialize_str(Visitor::<Rfc3339>(PhantomData))
}
/// Use the well-known [RFC3339 format] when serializing and deserializing an
@ -42,6 +50,7 @@ pub mod option {
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC3339 format.
#[cfg(feature = "formatting")]
pub fn serialize<S: Serializer>(
option: &Option<OffsetDateTime>,
serializer: S,
@ -54,6 +63,7 @@ pub mod option {
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC3339 representation.
#[cfg(feature = "parsing")]
pub fn deserialize<'a, D: Deserializer<'a>>(
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {

131
third_party/rust/time/src/serde/visitor.rs поставляемый
Просмотреть файл

@ -4,7 +4,7 @@ use core::fmt;
use core::marker::PhantomData;
use serde::de;
#[cfg(feature = "serde-well-known")]
#[cfg(feature = "parsing")]
use serde::Deserializer;
#[cfg(feature = "parsing")]
@ -13,8 +13,8 @@ use super::{
UTC_OFFSET_FORMAT,
};
use crate::error::ComponentRange;
#[cfg(feature = "serde-well-known")]
use crate::format_description::well_known;
#[cfg(feature = "parsing")]
use crate::format_description::well_known::*;
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// A serde visitor for various types.
@ -194,7 +194,7 @@ impl<'a> de::Visitor<'a> for Visitor<Weekday> {
}
}
fn visit_u8<E: de::Error>(self, value: u8) -> Result<Weekday, E> {
fn visit_u64<E: de::Error>(self, value: u64) -> Result<Weekday, E> {
match value {
1 => Ok(Weekday::Monday),
2 => Ok(Weekday::Tuesday),
@ -204,7 +204,7 @@ impl<'a> de::Visitor<'a> for Visitor<Weekday> {
6 => Ok(Weekday::Saturday),
7 => Ok(Weekday::Sunday),
_ => Err(E::invalid_value(
de::Unexpected::Unsigned(value.into()),
de::Unexpected::Unsigned(value),
&"a value in the range 1..=7",
)),
}
@ -236,7 +236,7 @@ impl<'a> de::Visitor<'a> for Visitor<Month> {
}
}
fn visit_u8<E: de::Error>(self, value: u8) -> Result<Month, E> {
fn visit_u64<E: de::Error>(self, value: u64) -> Result<Month, E> {
match value {
1 => Ok(Month::January),
2 => Ok(Month::February),
@ -251,79 +251,66 @@ impl<'a> de::Visitor<'a> for Visitor<Month> {
11 => Ok(Month::November),
12 => Ok(Month::December),
_ => Err(E::invalid_value(
de::Unexpected::Unsigned(value.into()),
de::Unexpected::Unsigned(value),
&"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;
/// Implement a visitor for a well-known format.
macro_rules! well_known {
($article:literal, $name:literal, $($ty:tt)+) => {
#[cfg(feature = "parsing")]
impl<'a> de::Visitor<'a> for Visitor<$($ty)+> {
type Value = OffsetDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an RFC2822-formatted `OffsetDateTime`")
}
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(concat!($article, " ", $name, "-formatted `OffsetDateTime`"))
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &well_known::Rfc2822).map_err(E::custom)
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> {
OffsetDateTime::parse(value, &$($ty)+).map_err(E::custom)
}
}
#[cfg(feature = "parsing")]
impl<'a> de::Visitor<'a> for Visitor<Option<$($ty)+>> {
type Value = Option<OffsetDateTime>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(concat!(
$article,
" ",
$name,
"-formatted `Option<OffsetDateTime>`"
))
}
fn visit_some<D: Deserializer<'a>>(
self,
deserializer: D,
) -> Result<Option<OffsetDateTime>, D::Error> {
deserializer
.deserialize_any(Visitor::<$($ty)+>(PhantomData))
.map(Some)
}
fn visit_none<E: de::Error>(self) -> Result<Option<OffsetDateTime>, E> {
Ok(None)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(None)
}
}
};
}
#[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)
}
}
well_known!("an", "RFC2822", Rfc2822);
well_known!("an", "RFC3339", Rfc3339);
well_known!(
"an",
"ISO 8601",
Iso8601::<{ super::iso8601::SERDE_CONFIG }>
);

Просмотреть файл

@ -2,6 +2,7 @@
use crate::{OffsetDateTime, UtcOffset};
#[allow(clippy::missing_docs_in_private_items)]
pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> {
None
}

Просмотреть файл

@ -1,7 +1,17 @@
//! A method to obtain the local offset from UTC.
#![allow(clippy::missing_const_for_fn)]
#[cfg_attr(target_family = "windows", path = "windows.rs")]
#[cfg_attr(target_family = "unix", path = "unix.rs")]
#[cfg_attr(
all(
target_arch = "wasm32",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
),
path = "wasm_js.rs"
)]
mod imp;
use crate::{OffsetDateTime, UtcOffset};

Просмотреть файл

@ -1,6 +1,5 @@
//! Get the system's UTC offset on Unix.
use core::convert::TryInto;
use core::mem::MaybeUninit;
use crate::{OffsetDateTime, UtcOffset};
@ -44,8 +43,22 @@ unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
}
/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// `tm_gmtoff` extension
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
// This is available to any target known to have the `tm_gmtoff` extension.
#[cfg(any(
target_os = "redox",
target_os = "linux",
target_os = "l4re",
target_os = "android",
target_os = "emscripten",
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
target_os = "haiku",
))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
UtcOffset::from_hms(
@ -57,10 +70,23 @@ fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
}
/// 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")
not(any(
target_os = "redox",
target_os = "linux",
target_os = "l4re",
target_os = "android",
target_os = "emscripten",
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
target_os = "haiku",
))
))]
#[allow(unused_variables, clippy::missing_const_for_fn)]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
@ -68,13 +94,30 @@ fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
}
/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// This method can return an incorrect value, as it only approximates the `tm_gmtoff` field. As such
// it is gated behind `--cfg unsound_local_offset`. The reason it can return an incorrect value is
// that daylight saving time does not start on the same date every year, nor are the rules for
// daylight saving time the same for every year. This implementation assumes 1970 is equivalent to
// every other year, which is not always the case.
#[cfg(all(
unsound_local_offset,
any(target_os = "solaris", target_os = "illumos")
not(any(
target_os = "redox",
target_os = "linux",
target_os = "l4re",
target_os = "android",
target_os = "emscripten",
target_os = "macos",
target_os = "ios",
target_os = "watchos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd",
target_os = "haiku",
))
))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
use core::convert::TryFrom;
use crate::Date;
let mut tm = tm;

12
third_party/rust/time/src/sys/local_offset_at/wasm_js.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,12 @@
use crate::{OffsetDateTime, UtcOffset};
/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
let js_date: js_sys::Date = datetime.into();
// The number of minutes returned by getTimezoneOffset() is positive if the local time zone
// is behind UTC, and negative if the local time zone is ahead of UTC. For example,
// for UTC+10, -600 will be returned.
let timezone_offset = (js_date.get_timezone_offset() as i32) * -60;
UtcOffset::from_whole_seconds(timezone_offset).ok()
}

Просмотреть файл

@ -1,13 +1,12 @@
//! 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)]
#[allow(non_snake_case, clippy::missing_docs_in_private_items)]
struct FileTime {
dwLowDateTime: u32,
dwHighDateTime: u32,
@ -15,7 +14,7 @@ struct FileTime {
// ffi: WINAPI SYSTEMTIME struct
#[repr(C)]
#[allow(non_snake_case)]
#[allow(non_snake_case, clippy::missing_docs_in_private_items)]
struct SystemTime {
wYear: u16,
wMonth: u16,
@ -34,7 +33,7 @@ extern "system" {
// 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
lpTimeZoneInformation: *const core::ffi::c_void, // We only pass `nullptr` here
lpUniversalTime: *const SystemTime,
lpLocalTime: *mut SystemTime,
) -> i32;

113
third_party/rust/time/src/tests.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,113 @@
#![cfg(all( // require `--all-features` to be passed
feature = "default",
feature = "alloc",
feature = "formatting",
feature = "large-dates",
feature = "local-offset",
feature = "macros",
feature = "parsing",
feature = "quickcheck",
feature = "serde-human-readable",
feature = "serde-well-known",
feature = "std",
feature = "rand",
feature = "serde",
))]
#![allow(
clippy::let_underscore_drop,
clippy::clone_on_copy,
clippy::cognitive_complexity,
clippy::std_instead_of_core
)]
//! Tests for internal details.
//!
//! This module should only be used when it is not possible to test the implementation in a
//! reasonable manner externally.
use std::num::NonZeroU8;
use crate::formatting::DigitCount;
use crate::parsing::combinator::rfc::iso8601;
use crate::parsing::shim::Integer;
use crate::{duration, parsing};
#[test]
fn digit_count() {
assert_eq!(1_u8.num_digits(), 1);
assert_eq!(9_u8.num_digits(), 1);
assert_eq!(10_u8.num_digits(), 2);
assert_eq!(99_u8.num_digits(), 2);
assert_eq!(100_u8.num_digits(), 3);
assert_eq!(1_u16.num_digits(), 1);
assert_eq!(9_u16.num_digits(), 1);
assert_eq!(10_u16.num_digits(), 2);
assert_eq!(99_u16.num_digits(), 2);
assert_eq!(100_u16.num_digits(), 3);
assert_eq!(999_u16.num_digits(), 3);
assert_eq!(1_000_u16.num_digits(), 4);
assert_eq!(9_999_u16.num_digits(), 4);
assert_eq!(10_000_u16.num_digits(), 5);
assert_eq!(1_u32.num_digits(), 1);
assert_eq!(9_u32.num_digits(), 1);
assert_eq!(10_u32.num_digits(), 2);
assert_eq!(99_u32.num_digits(), 2);
assert_eq!(100_u32.num_digits(), 3);
assert_eq!(999_u32.num_digits(), 3);
assert_eq!(1_000_u32.num_digits(), 4);
assert_eq!(9_999_u32.num_digits(), 4);
assert_eq!(10_000_u32.num_digits(), 5);
assert_eq!(99_999_u32.num_digits(), 5);
assert_eq!(100_000_u32.num_digits(), 6);
assert_eq!(999_999_u32.num_digits(), 6);
assert_eq!(1_000_000_u32.num_digits(), 7);
assert_eq!(9_999_999_u32.num_digits(), 7);
assert_eq!(10_000_000_u32.num_digits(), 8);
assert_eq!(99_999_999_u32.num_digits(), 8);
assert_eq!(100_000_000_u32.num_digits(), 9);
assert_eq!(999_999_999_u32.num_digits(), 9);
assert_eq!(1_000_000_000_u32.num_digits(), 10);
}
#[test]
fn default() {
assert_eq!(
duration::Padding::Optimize.clone(),
duration::Padding::default()
);
}
#[test]
fn debug() {
let _ = format!("{:?}", duration::Padding::Optimize);
let _ = format!("{:?}", parsing::ParsedItem(b"", 0));
let _ = format!("{:?}", parsing::component::Period::Am);
let _ = format!("{:?}", iso8601::ExtendedKind::Basic);
}
#[test]
fn clone() {
assert_eq!(
parsing::component::Period::Am.clone(),
parsing::component::Period::Am
);
// does not impl Debug
assert!(crate::time::Padding::Optimize.clone() == crate::time::Padding::Optimize);
// does not impl PartialEq
assert!(matches!(
iso8601::ExtendedKind::Basic.clone(),
iso8601::ExtendedKind::Basic
));
}
#[test]
fn parsing_internals() {
assert!(
parsing::ParsedItem(b"", ())
.flat_map(|_| None::<()>)
.is_none()
);
assert!(<NonZeroU8 as Integer>::parse_bytes(b"256").is_none());
}

107
third_party/rust/time/src/time.rs поставляемый
Просмотреть файл

@ -42,22 +42,12 @@ pub struct Time {
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};
/// # use time::Time;
/// # use time_macros::time;
/// assert_eq!(Time::MIDNIGHT, time!(0:00));
/// ```
pub const MIDNIGHT: Self = Self::__from_hms_nanos_unchecked(0, 0, 0, 0);
@ -81,6 +71,11 @@ impl Time {
second: u8,
nanosecond: u32,
) -> Self {
debug_assert!(hour < 24);
debug_assert!(minute < 60);
debug_assert!(second < 60);
debug_assert!(nanosecond < 1_000_000_000);
Self {
hour,
minute,
@ -208,7 +203,7 @@ impl Time {
/// Get the clock hour, minute, and second.
///
/// ```rust
/// # use time::macros::time;
/// # 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));
/// ```
@ -219,7 +214,7 @@ impl Time {
/// Get the clock hour, minute, second, and millisecond.
///
/// ```rust
/// # use time::macros::time;
/// # 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));
/// ```
@ -235,7 +230,7 @@ impl Time {
/// Get the clock hour, minute, second, and microsecond.
///
/// ```rust
/// # use time::macros::time;
/// # 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(),
@ -249,7 +244,7 @@ impl Time {
/// Get the clock hour, minute, second, and nanosecond.
///
/// ```rust
/// # use time::macros::time;
/// # 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(),
@ -265,7 +260,7 @@ impl Time {
/// The returned value will always be in the range `0..24`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).hour(), 0);
/// assert_eq!(time!(23:59:59).hour(), 23);
/// ```
@ -278,7 +273,7 @@ impl Time {
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).minute(), 0);
/// assert_eq!(time!(23:59:59).minute(), 59);
/// ```
@ -291,7 +286,7 @@ impl Time {
/// The returned value will always be in the range `0..60`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00:00).second(), 0);
/// assert_eq!(time!(23:59:59).second(), 59);
/// ```
@ -304,7 +299,7 @@ impl Time {
/// The returned value will always be in the range `0..1_000`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00).millisecond(), 0);
/// assert_eq!(time!(23:59:59.999).millisecond(), 999);
/// ```
@ -317,7 +312,7 @@ impl Time {
/// The returned value will always be in the range `0..1_000_000`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00).microsecond(), 0);
/// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999);
/// ```
@ -330,7 +325,7 @@ impl Time {
/// The returned value will always be in the range `0..1_000_000_000`.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(time!(0:00).nanosecond(), 0);
/// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999);
/// ```
@ -453,7 +448,7 @@ impl Time {
/// Replace the clock hour.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_hour(7),
/// Ok(time!(07:02:03.004_005_006))
@ -474,7 +469,7 @@ impl Time {
/// Replace the minutes within the hour.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_minute(7),
/// Ok(time!(01:07:03.004_005_006))
@ -495,7 +490,7 @@ impl Time {
/// Replace the seconds within the minute.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_second(7),
/// Ok(time!(01:02:07.004_005_006))
@ -516,7 +511,7 @@ impl Time {
/// Replace the milliseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_millisecond(7),
/// Ok(time!(01:02:03.007))
@ -540,7 +535,7 @@ impl Time {
/// Replace the microseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// # use time_macros::time;
/// assert_eq!(
/// time!(01:02:03.004_005_006).replace_microsecond(7_008),
/// Ok(time!(01:02:03.007_008))
@ -564,7 +559,7 @@ impl Time {
/// Replace the nanoseconds within the second.
///
/// ```rust
/// # use time::macros::time;
/// # 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))
@ -599,7 +594,8 @@ impl Time {
/// Format the `Time` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::time};
/// # use time::format_description;
/// # use time_macros::time;
/// let format = format_description::parse("[hour]:[minute]:[second]")?;
/// assert_eq!(time!(12:00).format(&format)?, "12:00:00");
/// # Ok::<_, time::Error>(())
@ -618,8 +614,9 @@ impl Time {
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::time, Time};
/// let format = format_description::parse("[hour]:[minute]:[second]")?;
/// # use time::Time;
/// # use time_macros::{time, format_description};
/// let format = format_description!("[hour]:[minute]:[second]");
/// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00));
/// # Ok::<_, time::Error>(())
/// ```
@ -646,15 +643,17 @@ impl fmt::Display for Time {
};
write!(
f,
"{}:{:02}:{:02}.{:0width$}",
self.hour,
self.minute,
self.second,
value,
width = width
"{}:{:02}:{:02}.{value:0width$}",
self.hour, self.minute, self.second,
)
}
}
impl fmt::Debug for Time {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
// region: trait impls
@ -664,7 +663,8 @@ impl Add<Duration> for Time {
/// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// # use time::ext::NumericalDuration;
/// # use time_macros::time;
/// assert_eq!(time!(12:00) + 2.hours(), time!(14:00));
/// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59));
/// ```
@ -679,7 +679,8 @@ impl Add<StdDuration> for Time {
/// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalStdDuration, macros::time};
/// # use time::ext::NumericalStdDuration;
/// # use time_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));
/// ```
@ -696,7 +697,8 @@ impl Sub<Duration> for Time {
/// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// # use time::ext::NumericalDuration;
/// # use time_macros::time;
/// assert_eq!(time!(14:00) - 2.hours(), time!(12:00));
/// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01));
/// ```
@ -711,7 +713,8 @@ impl Sub<StdDuration> for Time {
/// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow.
///
/// ```rust
/// # use time::{ext::NumericalStdDuration, macros::time};
/// # use time::ext::NumericalStdDuration;
/// # use time_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));
/// ```
@ -729,7 +732,8 @@ impl Sub for Time {
/// the same calendar day.
///
/// ```rust
/// # use time::{ext::NumericalDuration, macros::time};
/// # use time::ext::NumericalDuration;
/// # use time_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());
@ -738,15 +742,20 @@ impl Sub for Time {
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);
let second_diff = (self.second as i8) - (rhs.second as i8);
let nanosecond_diff = (self.nanosecond as i32) - (rhs.nanosecond as i32);
cascade!(nanosecond_diff in 0..1_000_000_000 => second_diff);
let seconds = hour_diff as i64 * 3_600 + minute_diff as i64 * 60 + second_diff as i64;
Duration::new_unchecked(
hour_diff as i64 * 3_600 + minute_diff as i64 * 60 + second_diff as i64,
nanosecond_diff,
)
let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 {
(seconds - 1, nanosecond_diff + 1_000_000_000)
} else if seconds < 0 && nanosecond_diff > 0 {
(seconds + 1, nanosecond_diff - 1_000_000_000)
} else {
(seconds, nanosecond_diff)
};
Duration::new_unchecked(seconds, nanoseconds)
}
}
// endregion trait impls

53
third_party/rust/time/src/utc_offset.rs поставляемый
Просмотреть файл

@ -20,7 +20,7 @@ use crate::OffsetDateTime;
/// 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)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UtcOffset {
#[allow(clippy::missing_docs_in_private_items)]
hours: i8,
@ -34,7 +34,8 @@ impl UtcOffset {
/// A `UtcOffset` that is UTC.
///
/// ```rust
/// # use time::{UtcOffset, macros::offset};
/// # use time::UtcOffset;
/// # use time_macros::offset;
/// assert_eq!(UtcOffset::UTC, offset!(UTC));
/// ```
pub const UTC: Self = Self::__from_hms_unchecked(0, 0, 0);
@ -45,6 +46,22 @@ impl UtcOffset {
/// sign.
#[doc(hidden)]
pub const fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self {
if hours < 0 {
debug_assert!(minutes <= 0);
debug_assert!(seconds <= 0);
} else if hours > 0 {
debug_assert!(minutes >= 0);
debug_assert!(seconds >= 0);
}
if minutes < 0 {
debug_assert!(seconds <= 0);
} else if minutes > 0 {
debug_assert!(seconds >= 0);
}
debug_assert!(hours.unsigned_abs() < 24);
debug_assert!(minutes.unsigned_abs() < 60);
debug_assert!(seconds.unsigned_abs() < 60);
Self {
hours,
minutes,
@ -110,7 +127,7 @@ impl UtcOffset {
/// will always match. A positive value indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # 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));
/// ```
@ -122,7 +139,7 @@ impl UtcOffset {
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_hours(), 1);
/// assert_eq!(offset!(-1:02:03).whole_hours(), -1);
/// ```
@ -134,7 +151,7 @@ impl UtcOffset {
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_minutes(), 62);
/// assert_eq!(offset!(-1:02:03).whole_minutes(), -62);
/// ```
@ -146,7 +163,7 @@ impl UtcOffset {
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2);
/// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2);
/// ```
@ -158,7 +175,7 @@ impl UtcOffset {
/// offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).whole_seconds(), 3723);
/// assert_eq!(offset!(-1:02:03).whole_seconds(), -3723);
/// ```
@ -172,7 +189,7 @@ impl UtcOffset {
/// indicates an offset to the east; a negative to the west.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3);
/// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3);
/// ```
@ -186,7 +203,7 @@ impl UtcOffset {
///
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert!(!offset!(+1:02:03).is_utc());
/// assert!(!offset!(-1:02:03).is_utc());
/// assert!(offset!(UTC).is_utc());
@ -198,7 +215,7 @@ impl UtcOffset {
/// Check if the offset is positive, or east of UTC.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert!(offset!(+1:02:03).is_positive());
/// assert!(!offset!(-1:02:03).is_positive());
/// assert!(!offset!(UTC).is_positive());
@ -210,7 +227,7 @@ impl UtcOffset {
/// Check if the offset is negative, or west of UTC.
///
/// ```rust
/// # use time::macros::offset;
/// # use time_macros::offset;
/// assert!(!offset!(+1:02:03).is_negative());
/// assert!(offset!(-1:02:03).is_negative());
/// assert!(!offset!(UTC).is_negative());
@ -269,7 +286,8 @@ impl UtcOffset {
/// Format the `UtcOffset` using the provided [format description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::offset};
/// # use time::format_description;
/// # use time_macros::offset;
/// let format = format_description::parse("[offset_hour sign:mandatory]:[offset_minute]")?;
/// assert_eq!(offset!(+1).format(&format)?, "+01:00");
/// # Ok::<_, time::Error>(())
@ -285,8 +303,9 @@ impl UtcOffset {
/// description](crate::format_description).
///
/// ```rust
/// # use time::{format_description, macros::offset, UtcOffset};
/// let format = format_description::parse("[offset_hour]:[offset_minute]")?;
/// # use time::UtcOffset;
/// # use time_macros::{offset, format_description};
/// let format = format_description!("[offset_hour]:[offset_minute]");
/// assert_eq!(UtcOffset::parse("-03:42", &format)?, offset!(-3:42));
/// # Ok::<_, time::Error>(())
/// ```
@ -310,6 +329,12 @@ impl fmt::Display for UtcOffset {
)
}
}
impl fmt::Debug for UtcOffset {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
// endregion formatting & parsing
impl Neg for UtcOffset {

53
third_party/rust/time/src/util.rs поставляемый
Просмотреть файл

@ -1,5 +1,7 @@
//! Utility functions.
pub use time_core::util::{days_in_year, is_leap_year, weeks_in_year};
use crate::Month;
/// Whether to adjust the date, and in which direction. Useful when implementing arithmetic.
@ -27,54 +29,3 @@ pub const fn days_in_year_month(year: i32, month: Month) -> u8 {
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,
}
}