chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/time/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

6
vendor/time/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "d5144cd2874862d46466c900910cd8577d066019"
},
"path_in_vcs": "time"
}

861
vendor/time/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,861 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "alloca"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4"
dependencies = [
"cc",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bumpalo"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstyle",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "criterion"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf"
dependencies = [
"alloca",
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools",
"num-traits",
"oorandom",
"page_size",
"regex",
"serde",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "deranged"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
dependencies = [
"powerfmt",
"quickcheck",
"rand 0.8.5",
"rand 0.9.2",
"serde_core",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "half"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
"cfg-if",
"crunchy",
"zerocopy",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "js-sys"
version = "0.3.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "oorandom"
version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "page_size"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quickcheck"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
"rand 0.8.5",
]
[[package]]
name = "quickcheck_macros"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f71ee38b42f8459a88d3362be6f9b841ad2d5421844f61eb1c59c11bff3ac14a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_core 0.9.4",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_core"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa"
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "rstest"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49"
dependencies = [
"rstest_macros",
]
[[package]]
name = "rstest_macros"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0"
dependencies = [
"cfg-if",
"glob",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "rstest_reuse"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14"
dependencies = [
"quote",
"rand 0.8.5",
"syn",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_spanned"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_test"
version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-triple"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "time"
version = "0.3.47"
dependencies = [
"criterion",
"deranged",
"itoa",
"js-sys",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"quickcheck",
"quickcheck_macros",
"rand 0.8.5",
"rand 0.9.2",
"rstest",
"rstest_reuse",
"serde",
"serde_core",
"serde_json",
"serde_test",
"time-core",
"time-macros",
"trybuild",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "toml"
version = "0.9.10+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "trybuild"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "zerocopy"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d6085d62852e35540689d1f97ad663e3971fc19cf5eceab364d62c646ea167"

332
vendor/time/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,332 @@
# 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 = "2024"
rust-version = "1.88.0"
name = "time"
version = "0.3.47"
authors = [
"Jacob Pratt <open-source@jhpratt.dev>",
"Time contributors",
]
build = false
include = [
"{src,tests,benchmarks}/**/*",
"LICENSE-*",
"README.md",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]."
homepage = "https://time-rs.github.io"
readme = "README.md"
keywords = [
"date",
"time",
"calendar",
"duration",
]
categories = [
"date-and-time",
"no-std",
"parser-implementations",
"value-formatting",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/time-rs/time"
[package.metadata.docs.rs]
all-features = true
targets = ["x86_64-unknown-linux-gnu"]
rustdoc-args = ["--generate-link-to-definition"]
[features]
alloc = ["serde_core?/alloc"]
default = ["std"]
formatting = [
"dep:itoa",
"std",
"time-macros?/formatting",
]
large-dates = [
"time-core/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",
"deranged/quickcheck",
]
rand = [
"rand08",
"rand09",
]
rand08 = [
"dep:rand08",
"deranged/rand08",
]
rand09 = [
"dep:rand09",
"deranged/rand09",
]
serde = [
"dep:serde_core",
"time-macros?/serde",
"deranged/serde",
]
serde-human-readable = [
"serde",
"formatting",
"parsing",
]
serde-well-known = [
"serde",
"formatting",
"parsing",
]
std = ["alloc"]
wasm-bindgen = ["dep:js-sys"]
[lib]
name = "time"
path = "src/lib.rs"
bench = false
[[test]]
name = "tests"
path = "tests/integration/main.rs"
[[bench]]
name = "benchmarks"
path = "benchmarks/main.rs"
harness = false
[dependencies.deranged]
version = "0.5.2"
features = ["powerfmt"]
[dependencies.itoa]
version = "1.0.1"
optional = true
[dependencies.num-conv]
version = "0.2.0"
[dependencies.powerfmt]
version = "0.2.0"
default-features = false
[dependencies.quickcheck]
version = "1.0.3"
optional = true
default-features = false
[dependencies.rand08]
version = "0.8.4"
optional = true
default-features = false
package = "rand"
[dependencies.rand09]
version = "0.9.2"
optional = true
default-features = false
package = "rand"
[dependencies.serde_core]
version = "1.0.220"
optional = true
default-features = false
[dependencies.time-core]
version = "=0.1.8"
[dependencies.time-macros]
version = "=0.2.27"
optional = true
[dev-dependencies.num-conv]
version = "0.2.0"
[dev-dependencies.quickcheck_macros]
version = "1.0.0"
[dev-dependencies.rand08]
version = "0.8.4"
default-features = false
package = "rand"
[dev-dependencies.rand09]
version = "0.9.2"
features = ["small_rng"]
default-features = false
package = "rand"
[dev-dependencies.rstest]
version = "0.26.1"
default-features = false
[dev-dependencies.rstest_reuse]
version = "0.7.0"
[dev-dependencies.serde]
version = "1.0.184"
features = ["derive"]
default-features = false
[dev-dependencies.serde_json]
version = "1.0.68"
[dev-dependencies.serde_test]
version = "1.0.126"
[dev-dependencies.time-macros]
version = "=0.2.27"
[target."cfg(__ui_tests)".dev-dependencies.trybuild]
version = "1.0.102"
[target.'cfg(all(target_family = "wasm", 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.8.1"
default-features = false
[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
[lints.clippy]
alloc-instead-of-core = "deny"
as-underscore = "warn"
dbg-macro = "warn"
decimal-literal-representation = "warn"
explicit-auto-deref = "warn"
get-unwrap = "warn"
manual-let-else = "warn"
missing-docs-in-private-items = "warn"
missing-enforced-import-renames = "warn"
obfuscated-if-else = "warn"
print-stdout = "warn"
semicolon-outside-block = "warn"
std-instead-of-core = "deny"
todo = "warn"
undocumented-unsafe-blocks = "deny"
unimplemented = "warn"
uninlined-format-args = "warn"
unnested-or-patterns = "warn"
unwrap-in-result = "warn"
unwrap-used = "warn"
use-debug = "warn"
[lints.clippy.all]
level = "warn"
priority = -1
[lints.clippy.incompatible-msrv]
level = "allow"
priority = 1
[lints.clippy.nursery]
level = "warn"
priority = -1
[lints.clippy.option-if-let-else]
level = "allow"
priority = 1
[lints.clippy.redundant-pub-crate]
level = "allow"
priority = 1
[lints.clippy.uninhabited-references]
level = "allow"
priority = 1
[lints.rust]
ambiguous-glob-reexports = "deny"
clashing-extern-declarations = "deny"
const-item-mutation = "deny"
dangling-pointers-from-temporaries = "deny"
deref-nullptr = "deny"
drop-bounds = "deny"
future-incompatible = "deny"
hidden-glob-reexports = "deny"
improper-ctypes = "deny"
improper-ctypes-definitions = "deny"
invalid-from-utf8 = "deny"
invalid-macro-export-arguments = "deny"
invalid-nan-comparisons = "deny"
invalid-reference-casting = "deny"
invalid-value = "deny"
keyword-idents = "warn"
let-underscore = "warn"
macro-use-extern-crate = "warn"
meta-variable-misuse = "warn"
missing-abi = "warn"
missing-copy-implementations = "warn"
missing-debug-implementations = "warn"
missing-docs = "warn"
named-arguments-used-positionally = "deny"
non-ascii-idents = "deny"
noop-method-call = "warn"
opaque-hidden-inferred-bound = "deny"
overlapping-range-endpoints = "deny"
single-use-lifetimes = "warn"
suspicious-double-ref-op = "deny"
trivial-casts = "warn"
trivial-numeric-casts = "warn"
unconditional-recursion = "deny"
unnameable-test-items = "deny"
unreachable-pub = "warn"
unsafe-op-in-unsafe-fn = "deny"
unstable-syntax-pre-expansion = "deny"
unused-import-braces = "warn"
unused-lifetimes = "warn"
unused-qualifications = "warn"
variant-size-differences = "warn"
[lints.rust.unexpected_cfgs]
level = "deny"
priority = 0
check-cfg = [
"cfg(__ui_tests)",
"cfg(bench)",
]
[lints.rust.unstable-name-collisions]
level = "allow"
priority = 1
[lints.rust.unused]
level = "warn"
priority = -1
[lints.rustdoc]
private-doc-tests = "warn"
unescaped-backticks = "warn"

177
vendor/time/LICENSE-Apache vendored Normal file
View File

@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

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

@@ -0,0 +1,19 @@
Copyright (c) Jacob Pratt et al.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

51
vendor/time/README.md vendored Normal file
View File

@@ -0,0 +1,51 @@
# time
[![minimum rustc: 1.88.0](https://img.shields.io/badge/minimum%20rustc-1.88.0-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/actions/workflow/status/time-rs/time/build.yaml?branch=main&style=flat-square)](https://github.com/time-rs/time/actions)
[![codecov](https://codecov.io/gh/time-rs/time/branch/main/graph/badge.svg?token=yt4XSmQNKQ)](https://codecov.io/gh/time-rs/time)
Documentation:
- [latest release](https://docs.rs/time)
- [main branch](https://time-rs.github.io/api/time)
- [book](https://time-rs.github.io/book)
## Minimum Rust version policy
`time` is guaranteed to compile with the latest stable release of Rust in addition to the two prior
minor releases. For example, if the latest stable Rust release is 1.70, then `time` is guaranteed to
compile with Rust 1.68, 1.69, and 1.70.
The minimum supported Rust version may be increased to one of the aforementioned versions if doing
so provides the end user a benefit. However, the minimum supported Rust version may also be bumped
to a version four minor releases prior to the most recent stable release if doing so improves code
quality or maintainability.
For interoperability with third-party crates, it is guaranteed that there exists a version of that
crate that supports the minimum supported Rust version of `time`. This does not mean that the latest
version of the third-party crate supports the minimum supported Rust version of `time`.
## Contributing
Contributions are always welcome! If you have an idea, it's best to float it by me before working on
it to ensure no effort is wasted. If there's already an open issue for it, knock yourself out.
Internal documentation can be viewed [here](https://time-rs.github.io/internal-api/time).
If you have any questions, feel free to use [Discussions]. Don't hesitate to ask questions — that's
what I'm here for!
[Discussions]: https://github.com/time-rs/time/discussions
## License
This project is licensed under either of
- [Apache License, Version 2.0](https://github.com/time-rs/time/blob/main/LICENSE-Apache)
- [MIT license](https://github.com/time-rs/time/blob/main/LICENSE-MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
time by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
additional terms or conditions.

358
vendor/time/benchmarks/date.rs vendored Normal file
View File

@@ -0,0 +1,358 @@
#![expect(
clippy::large_stack_frames,
reason = "iterating over large array; does not cause stack overflow"
)]
use std::hint::black_box as bb;
use std::sync::LazyLock;
use criterion::Bencher;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::date;
use time::{Date, Time};
/// Generate a representative sample of all dates.
///
/// The ratio of month sizes, week sizes, year sign, leap years, etc. are all identical to the full
/// range. This ensures that benchmarks accurately reflect random data.
//
// Note that this is a _very_ large array (over 1 MiB), so we silence the warning about large stack
// frames at the top of this file.
fn representative_dates() -> [Date; 292_194] {
static DATES: LazyLock<[Date; 292_194]> = LazyLock::new(|| {
let mut dates = [Date::MIN; _];
let mut current = date!(-0400-01-01);
let mut i = 0;
while current < date!(0400-01-01) {
dates[i] = current;
current = current.next_day().expect("date is in range");
i += 1;
}
crate::shuffle(dates)
});
*DATES
}
setup_benchmark! {
"Date",
fn noop(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(date);
}
});
}
fn noop_windows(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates().windows(2) {
let first = date[0];
let second = date[1];
let _ = bb((bb(first), bb(second)));
}
});
}
fn from_calendar_date(ben: &mut Bencher<'_>) {
let dates = representative_dates().map(Date::to_calendar_date);
ben.iter(|| {
for (year, month, day) in dates {
let _ = bb(Date::from_calendar_date(bb(year), bb(month), bb(day)));
}
});
}
fn from_ordinal_date(ben: &mut Bencher<'_>) {
let dates = representative_dates().map(Date::to_ordinal_date);
ben.iter(|| {
for (year, ordinal) in dates {
let _ = bb(Date::from_ordinal_date(bb(year), bb(ordinal)));
}
});
}
fn from_iso_week_date(ben: &mut Bencher<'_>) {
let dates = representative_dates().map(Date::to_iso_week_date);
ben.iter(|| {
for (year, week, weekday) in dates {
let _ = bb(Date::from_iso_week_date(bb(year), bb(week), bb(weekday)));
}
});
}
fn from_julian_day(ben: &mut Bencher<'_>) {
let dates = representative_dates().map(Date::to_julian_day);
ben.iter(|| {
for julian_day in dates {
let _ = bb(Date::from_julian_day(bb(julian_day)));
}
});
}
fn year(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).year());
}
});
}
fn month(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).month());
}
});
}
fn day(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).day());
}
});
}
fn ordinal(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).ordinal());
}
});
}
fn iso_week(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).iso_week());
}
});
}
fn sunday_based_week(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).sunday_based_week());
}
});
}
fn monday_based_week(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).monday_based_week());
}
});
}
fn to_calendar_date(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).to_calendar_date());
}
});
}
fn to_ordinal_date(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).to_ordinal_date());
}
});
}
fn to_iso_week_date(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).to_iso_week_date());
}
});
}
fn weekday(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).weekday());
}
});
}
fn next_day(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).next_day());
}
});
}
fn previous_day(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).previous_day());
}
});
}
fn to_julian_day(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).to_julian_day());
}
});
}
fn midnight(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).midnight());
}
});
}
fn with_time(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).with_time(bb(Time::MIDNIGHT)));
}
});
}
fn with_hms(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).with_hms(bb(0), bb(0), bb(0)));
}
});
}
fn with_hms_milli(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).with_hms_milli(bb(0), bb(0), bb(0), bb(0)));
}
});
}
fn with_hms_micro(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).with_hms_micro(bb(0), bb(0), bb(0), bb(0)));
}
});
}
fn with_hms_nano(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date).with_hms_nano(bb(0), bb(0), bb(0), bb(0)));
}
});
}
fn add(ben: &mut Bencher<'_>) {
let dt = 5.days();
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date) + bb(dt));
}
});
}
fn add_std(ben: &mut Bencher<'_>) {
let dt = 5.std_days();
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date) - bb(dt));
}
});
}
fn add_assign(ben: &mut Bencher<'_>) {
let dt = 1.days();
ben.iter(|| {
for mut date in representative_dates() {
date += bb(dt);
let _ = bb(date);
}
});
}
fn add_assign_std(ben: &mut Bencher<'_>) {
let dt = 1.std_days();
ben.iter(|| {
for mut date in representative_dates() {
date += bb(dt);
let _ = bb(date);
}
});
}
fn sub(ben: &mut Bencher<'_>) {
let dt = 5.days();
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date) - bb(dt));
}
});
}
fn sub_std(ben: &mut Bencher<'_>) {
let dt = 5.std_days();
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date) - bb(dt));
}
});
}
fn sub_assign(ben: &mut Bencher<'_>) {
let dt = 1.days();
ben.iter(|| {
for mut date in representative_dates() {
date -= bb(dt);
let _ = bb(date);
}
});
}
fn sub_assign_std(ben: &mut Bencher<'_>) {
let dt = 1.std_days();
ben.iter(|| {
for mut date in representative_dates() {
date -= bb(dt);
let _ = bb(date);
}
});
}
fn sub_self(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates() {
let _ = bb(bb(date) - bb(date));
}
});
}
fn partial_ord(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates().windows(2) {
let first = date[0];
let second = date[1];
let _ = bb(bb(first).partial_cmp(&bb(second)));
}
});
}
fn ord(ben: &mut Bencher<'_>) {
ben.iter(|| {
for date in representative_dates().windows(2) {
let first = date[0];
let second = date[1];
let _ = bb(bb(first).cmp(&bb(second)));
}
});
}
}

714
vendor/time/benchmarks/duration.rs vendored Normal file
View File

@@ -0,0 +1,714 @@
use std::time::Duration as StdDuration;
use criterion::Bencher;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::Duration;
setup_benchmark! {
"Duration",
fn is_zero(ben: &mut Bencher<'_>) {
let a = (-1).nanoseconds();
let b = 0.seconds();
let c = 1.nanoseconds();
ben.iter(|| a.is_zero());
ben.iter(|| b.is_zero());
ben.iter(|| c.is_zero());
}
fn is_negative(ben: &mut Bencher<'_>) {
let a = (-1).seconds();
let b = 0.seconds();
let c = 1.seconds();
ben.iter(|| a.is_negative());
ben.iter(|| b.is_negative());
ben.iter(|| c.is_negative());
}
fn is_positive(ben: &mut Bencher<'_>) {
let a = (-1).seconds();
let b = 0.seconds();
let c = 1.seconds();
ben.iter(|| a.is_positive());
ben.iter(|| b.is_positive());
ben.iter(|| c.is_positive());
}
fn abs(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 0.seconds();
let c = (-1).seconds();
ben.iter(|| a.abs());
ben.iter(|| b.abs());
ben.iter(|| c.abs());
}
fn unsigned_abs(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 0.seconds();
let c = (-1).seconds();
ben.iter(|| a.unsigned_abs());
ben.iter(|| b.unsigned_abs());
ben.iter(|| c.unsigned_abs());
}
fn new(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::new(1, 0));
ben.iter(|| Duration::new(-1, 0));
ben.iter(|| Duration::new(1, 2_000_000_000));
ben.iter(|| Duration::new(0, 0));
ben.iter(|| Duration::new(0, 1_000_000_000));
ben.iter(|| Duration::new(-1, 1_000_000_000));
ben.iter(|| Duration::new(-2, 1_000_000_000));
ben.iter(|| Duration::new(1, -1));
ben.iter(|| Duration::new(-1, 1));
ben.iter(|| Duration::new(1, 1));
ben.iter(|| Duration::new(-1, -1));
ben.iter(|| Duration::new(0, 1));
ben.iter(|| Duration::new(0, -1));
ben.iter(|| Duration::new(-1, 1_400_000_000));
ben.iter(|| Duration::new(-2, 1_400_000_000));
ben.iter(|| Duration::new(-3, 1_400_000_000));
ben.iter(|| Duration::new(1, -1_400_000_000));
ben.iter(|| Duration::new(2, -1_400_000_000));
ben.iter(|| Duration::new(3, -1_400_000_000));
}
fn weeks(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::weeks(1));
ben.iter(|| Duration::weeks(2));
ben.iter(|| Duration::weeks(-1));
ben.iter(|| Duration::weeks(-2));
}
fn days(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::days(1));
ben.iter(|| Duration::days(2));
ben.iter(|| Duration::days(-1));
ben.iter(|| Duration::days(-2));
}
fn hours(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::hours(1));
ben.iter(|| Duration::hours(2));
ben.iter(|| Duration::hours(-1));
ben.iter(|| Duration::hours(-2));
}
fn minutes(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::minutes(1));
ben.iter(|| Duration::minutes(2));
ben.iter(|| Duration::minutes(-1));
ben.iter(|| Duration::minutes(-2));
}
fn seconds(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::seconds(1));
ben.iter(|| Duration::seconds(2));
ben.iter(|| Duration::seconds(-1));
ben.iter(|| Duration::seconds(-2));
}
fn seconds_f64(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::seconds_f64(0.5));
ben.iter(|| Duration::seconds_f64(-0.5));
}
fn seconds_f32(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::seconds_f32(0.5));
ben.iter(|| Duration::seconds_f32(-0.5));
}
fn saturating_seconds_f64(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::saturating_seconds_f64(0.5));
ben.iter(|| Duration::saturating_seconds_f64(-0.5));
}
fn saturating_seconds_f32(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::saturating_seconds_f32(0.5));
ben.iter(|| Duration::saturating_seconds_f32(-0.5));
}
fn checked_seconds_f64(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::checked_seconds_f64(0.5));
ben.iter(|| Duration::checked_seconds_f64(-0.5));
}
fn checked_seconds_f32(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::checked_seconds_f32(0.5));
ben.iter(|| Duration::checked_seconds_f32(-0.5));
}
fn milliseconds(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::milliseconds(1));
ben.iter(|| Duration::milliseconds(-1));
}
fn microseconds(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::microseconds(1));
ben.iter(|| Duration::microseconds(-1));
}
fn nanoseconds(ben: &mut Bencher<'_>) {
ben.iter(|| Duration::nanoseconds(1));
ben.iter(|| Duration::nanoseconds(-1));
}
fn whole_weeks(ben: &mut Bencher<'_>) {
let a = Duration::weeks(1);
let b = Duration::weeks(-1);
let c = Duration::days(6);
let d = Duration::days(-6);
ben.iter(|| a.whole_weeks());
ben.iter(|| b.whole_weeks());
ben.iter(|| c.whole_weeks());
ben.iter(|| d.whole_weeks());
}
fn whole_days(ben: &mut Bencher<'_>) {
let a = Duration::days(1);
let b = Duration::days(-1);
let c = Duration::hours(23);
let d = Duration::hours(-23);
ben.iter(|| a.whole_days());
ben.iter(|| b.whole_days());
ben.iter(|| c.whole_days());
ben.iter(|| d.whole_days());
}
fn whole_hours(ben: &mut Bencher<'_>) {
let a = Duration::hours(1);
let b = Duration::hours(-1);
let c = Duration::minutes(59);
let d = Duration::minutes(-59);
ben.iter(|| a.whole_hours());
ben.iter(|| b.whole_hours());
ben.iter(|| c.whole_hours());
ben.iter(|| d.whole_hours());
}
fn whole_minutes(ben: &mut Bencher<'_>) {
let a = 1.minutes();
let b = (-1).minutes();
let c = 59.seconds();
let d = (-59).seconds();
ben.iter(|| a.whole_minutes());
ben.iter(|| b.whole_minutes());
ben.iter(|| c.whole_minutes());
ben.iter(|| d.whole_minutes());
}
fn whole_seconds(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = (-1).seconds();
let c = 1.minutes();
let d = (-1).minutes();
ben.iter(|| a.whole_seconds());
ben.iter(|| b.whole_seconds());
ben.iter(|| c.whole_seconds());
ben.iter(|| d.whole_seconds());
}
fn as_seconds_f64(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = (-1).seconds();
let c = 1.minutes();
let d = (-1).minutes();
let e = 1.5.seconds();
let f = (-1.5).seconds();
ben.iter(|| a.as_seconds_f64());
ben.iter(|| b.as_seconds_f64());
ben.iter(|| c.as_seconds_f64());
ben.iter(|| d.as_seconds_f64());
ben.iter(|| e.as_seconds_f64());
ben.iter(|| f.as_seconds_f64());
}
fn as_seconds_f32(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = (-1).seconds();
let c = 1.minutes();
let d = (-1).minutes();
let e = 1.5.seconds();
let f = (-1.5).seconds();
ben.iter(|| a.as_seconds_f32());
ben.iter(|| b.as_seconds_f32());
ben.iter(|| c.as_seconds_f32());
ben.iter(|| d.as_seconds_f32());
ben.iter(|| e.as_seconds_f32());
ben.iter(|| f.as_seconds_f32());
}
fn whole_milliseconds(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = (-1).seconds();
let c = 1.milliseconds();
let d = (-1).milliseconds();
ben.iter(|| a.whole_milliseconds());
ben.iter(|| b.whole_milliseconds());
ben.iter(|| c.whole_milliseconds());
ben.iter(|| d.whole_milliseconds());
}
fn subsec_milliseconds(ben: &mut Bencher<'_>) {
let a = 1.4.seconds();
let b = (-1.4).seconds();
ben.iter(|| a.subsec_milliseconds());
ben.iter(|| b.subsec_milliseconds());
}
fn whole_microseconds(ben: &mut Bencher<'_>) {
let a = 1.milliseconds();
let b = (-1).milliseconds();
let c = 1.microseconds();
let d = (-1).microseconds();
ben.iter(|| a.whole_microseconds());
ben.iter(|| b.whole_microseconds());
ben.iter(|| c.whole_microseconds());
ben.iter(|| d.whole_microseconds());
}
fn subsec_microseconds(ben: &mut Bencher<'_>) {
let a = 1.0004.seconds();
let b = (-1.0004).seconds();
ben.iter(|| a.subsec_microseconds());
ben.iter(|| b.subsec_microseconds());
}
fn whole_nanoseconds(ben: &mut Bencher<'_>) {
let a = 1.microseconds();
let b = (-1).microseconds();
let c = 1.nanoseconds();
let d = (-1).nanoseconds();
ben.iter(|| a.whole_nanoseconds());
ben.iter(|| b.whole_nanoseconds());
ben.iter(|| c.whole_nanoseconds());
ben.iter(|| d.whole_nanoseconds());
}
fn subsec_nanoseconds(ben: &mut Bencher<'_>) {
let a = 1.000_000_4.seconds();
let b = (-1.000_000_4).seconds();
ben.iter(|| a.subsec_nanoseconds());
ben.iter(|| b.subsec_nanoseconds());
}
fn checked_add(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = Duration::MAX;
let c = (-5).seconds();
let a2 = 5.seconds();
let b2 = 1.nanoseconds();
let c2 = 5.seconds();
ben.iter(|| a.checked_add(a2));
ben.iter(|| b.checked_add(b2));
ben.iter(|| c.checked_add(c2));
}
fn checked_sub(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = Duration::MIN;
let c = 5.seconds();
let a2 = 5.seconds();
let b2 = 1.nanoseconds();
let c2 = 10.seconds();
ben.iter(|| a.checked_sub(a2));
ben.iter(|| b.checked_sub(b2));
ben.iter(|| c.checked_sub(c2));
}
fn checked_mul(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = Duration::MAX;
ben.iter(|| a.checked_mul(2));
ben.iter(|| b.checked_mul(2));
}
fn checked_div(ben: &mut Bencher<'_>) {
let a = 10.seconds();
ben.iter(|| a.checked_div(2));
ben.iter(|| a.checked_div(0));
}
fn saturating_add(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = Duration::MAX;
let c = Duration::MIN;
let d = (-5).seconds();
let a2 = 5.seconds();
let b2 = 1.nanoseconds();
let c2 = (-1).nanoseconds();
let d2 = 5.seconds();
ben.iter(|| a.saturating_add(a2));
ben.iter(|| b.saturating_add(b2));
ben.iter(|| c.saturating_add(c2));
ben.iter(|| d.saturating_add(d2));
}
fn saturating_sub(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = Duration::MIN;
let c = Duration::MAX;
let d = 5.seconds();
let a2 = 5.seconds();
let b2 = 1.nanoseconds();
let c2 = (-1).nanoseconds();
let d2 = 10.seconds();
ben.iter(|| a.saturating_sub(a2));
ben.iter(|| b.saturating_sub(b2));
ben.iter(|| c.saturating_sub(c2));
ben.iter(|| d.saturating_sub(d2));
}
fn saturating_mul(ben: &mut Bencher<'_>) {
let a = 5.seconds();
let b = 5.seconds();
let c = 5.seconds();
let d = Duration::MAX;
let e = Duration::MIN;
let f = Duration::MAX;
let g = Duration::MIN;
ben.iter(|| a.saturating_mul(2));
ben.iter(|| b.saturating_mul(-2));
ben.iter(|| c.saturating_mul(0));
ben.iter(|| d.saturating_mul(2));
ben.iter(|| e.saturating_mul(2));
ben.iter(|| f.saturating_mul(-2));
ben.iter(|| g.saturating_mul(-2));
}
fn try_from_std_duration(ben: &mut Bencher<'_>) {
let a = 0.std_seconds();
let b = 1.std_seconds();
ben.iter(|| Duration::try_from(a));
ben.iter(|| Duration::try_from(b));
}
fn try_to_std_duration(ben: &mut Bencher<'_>) {
let a = 0.seconds();
let b = 1.seconds();
let c = (-1).seconds();
ben.iter(|| StdDuration::try_from(a));
ben.iter(|| StdDuration::try_from(b));
ben.iter(|| StdDuration::try_from(c));
}
fn add(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 2.seconds();
let c = 500.milliseconds();
let d = (-1).seconds();
ben.iter(|| a + b + c + d);
}
fn add_std(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 2.std_seconds();
ben.iter(|| a + b);
}
fn std_add(ben: &mut Bencher<'_>) {
let a = 1.std_seconds();
let b = 2.seconds();
ben.iter(|| a + b);
}
fn add_assign(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 500.milliseconds();
let c = (-1).seconds();
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration += a,
|duration| *duration += b,
|duration| *duration += c,
]
);
}
fn add_assign_std(ben: &mut Bencher<'_>) {
let a = 1.std_seconds();
let b = 500.std_milliseconds();
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration += a,
|duration| *duration += b,
]
);
}
fn neg(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = (-1).seconds();
let c = 0.seconds();
ben.iter(|| -a);
ben.iter(|| -b);
ben.iter(|| -c);
}
fn sub(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 1.seconds();
let c = 1_500.milliseconds();
let d = 500.milliseconds();
let e = 1.seconds();
let f = (-1).seconds();
ben.iter(|| a - b);
ben.iter(|| b - c);
ben.iter(|| c - d);
ben.iter(|| d - e);
ben.iter(|| e - f);
ben.iter(|| f - a);
}
fn sub_std(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 2.std_seconds();
ben.iter(|| a - b);
}
fn std_sub(ben: &mut Bencher<'_>) {
let a = 1.std_seconds();
let b = 2.seconds();
ben.iter(|| a - b);
}
fn sub_assign(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 500.milliseconds();
let c = (-1).seconds();
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration -= a,
|duration| *duration -= b,
|duration| *duration -= c,
]
);
}
fn mul_int(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| d * 2);
ben.iter(|| d * -2);
}
fn mul_int_assign(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration *= 2,
|duration| *duration *= -2,
]
);
}
fn int_mul(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| 2 * d);
ben.iter(|| -2 * d);
}
fn div_int(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| d / 2);
ben.iter(|| d / -2);
}
fn div_int_assign(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration /= 2,
|duration| *duration /= -2,
]
);
}
fn div(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 0.5.seconds();
ben.iter(|| a / b);
}
fn mul_float(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| d * 1.5_f32);
ben.iter(|| d * 2.5_f32);
ben.iter(|| d * -1.5_f32);
ben.iter(|| d * 0_f32);
ben.iter(|| d * 1.5_f64);
ben.iter(|| d * 2.5_f64);
ben.iter(|| d * -1.5_f64);
ben.iter(|| d * 0_f64);
}
fn float_mul(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| 1.5_f32 * d);
ben.iter(|| 2.5_f32 * d);
ben.iter(|| -1.5_f32 * d);
ben.iter(|| 0_f32 * d);
ben.iter(|| 1.5_f64 * d);
ben.iter(|| 2.5_f64 * d);
ben.iter(|| -1.5_f64 * d);
ben.iter(|| 0_f64 * d);
}
fn mul_float_assign(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| 1.seconds(),
[
|duration| *duration *= 1.5_f32,
|duration| *duration *= 2.5_f32,
|duration| *duration *= -1.5_f32,
|duration| *duration *= 3.15_f32,
|duration| *duration *= 1.5_f64,
|duration| *duration *= 2.5_f64,
|duration| *duration *= -1.5_f64,
|duration| *duration *= 0_f64,
]
);
}
fn div_float(ben: &mut Bencher<'_>) {
let d = 1.seconds();
ben.iter(|| d / 1_f32);
ben.iter(|| d / 2_f32);
ben.iter(|| d / -1_f32);
ben.iter(|| d / 1_f64);
ben.iter(|| d / 2_f64);
ben.iter(|| d / -1_f64);
}
fn div_float_assign(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| 10.seconds(),
[
|duration| *duration /= 1_f32,
|duration| *duration /= 2_f32,
|duration| *duration /= -1_f32,
|duration| *duration /= 1_f64,
|duration| *duration /= 2_f64,
|duration| *duration /= -1_f64,
]
);
}
fn partial_eq(ben: &mut Bencher<'_>) {
let a = 1.minutes();
let b = (-1).minutes();
let c = 40.seconds();
ben.iter(|| a == b);
ben.iter(|| c == a);
}
fn partial_eq_std(ben: &mut Bencher<'_>) {
let a = (-1).seconds();
let b = 1.std_seconds();
let c = (-1).minutes();
let d = 1.std_minutes();
let e = 40.seconds();
ben.iter(|| a == b);
ben.iter(|| c == d);
ben.iter(|| e == d);
}
fn std_partial_eq(ben: &mut Bencher<'_>) {
let a = 1.std_seconds();
let b = (-1).seconds();
let c = 1.std_minutes();
let d = (-1).minutes();
let e = 40.std_seconds();
let f = 1.minutes();
ben.iter(|| a == b);
ben.iter(|| c == d);
ben.iter(|| e == f);
}
fn partial_ord(ben: &mut Bencher<'_>) {
let a = 0.seconds();
let b = 1.seconds();
let c = (-1).seconds();
let d = 1.minutes();
let e = (-1).minutes();
ben.iter(|| a.partial_cmp(&a));
ben.iter(|| b.partial_cmp(&a));
ben.iter(|| b.partial_cmp(&c));
ben.iter(|| c.partial_cmp(&b));
ben.iter(|| a.partial_cmp(&c));
ben.iter(|| a.partial_cmp(&b));
ben.iter(|| c.partial_cmp(&a));
ben.iter(|| d.partial_cmp(&b));
ben.iter(|| e.partial_cmp(&c));
}
fn partial_ord_std(ben: &mut Bencher<'_>) {
let a = 0.seconds();
let b = 0.std_seconds();
let c = 1.seconds();
let d = (-1).seconds();
let e = 1.std_seconds();
let f = 1.minutes();
let g = u64::MAX.std_seconds();
ben.iter(|| a.partial_cmp(&b));
ben.iter(|| c.partial_cmp(&b));
ben.iter(|| d.partial_cmp(&e));
ben.iter(|| a.partial_cmp(&e));
ben.iter(|| d.partial_cmp(&b));
ben.iter(|| f.partial_cmp(&e));
ben.iter(|| a.partial_cmp(&g));
}
fn std_partial_ord(ben: &mut Bencher<'_>) {
let a = 0.std_seconds();
let b = 0.seconds();
let c = 1.std_seconds();
let d = (-1).seconds();
let e = 1.seconds();
let f = 1.std_minutes();
ben.iter(|| a.partial_cmp(&b));
ben.iter(|| c.partial_cmp(&b));
ben.iter(|| c.partial_cmp(&d));
ben.iter(|| a.partial_cmp(&d));
ben.iter(|| a.partial_cmp(&e));
ben.iter(|| f.partial_cmp(&e));
}
fn ord(ben: &mut Bencher<'_>) {
let a = 1.seconds();
let b = 0.seconds();
let c = (-1).seconds();
let d = 1.minutes();
let e = (-1).minutes();
ben.iter(|| a > b);
ben.iter(|| a > c);
ben.iter(|| c < a);
ben.iter(|| b > c);
ben.iter(|| b < a);
ben.iter(|| c < b);
ben.iter(|| d > a);
ben.iter(|| e < c);
}
}

190
vendor/time/benchmarks/formatting.rs vendored Normal file
View File

@@ -0,0 +1,190 @@
use std::io;
use criterion::Bencher;
use time::format_description;
use time::format_description::well_known::{Rfc2822, Rfc3339};
use time::macros::{date, datetime, format_description as fd, offset, time};
setup_benchmark! {
"Formatting",
fn format_rfc3339(ben: &mut Bencher<'_>) {
macro_rules! item {
($value:expr) => {
$value.format_into(&mut io::sink(), &Rfc3339)
}
}
ben.iter(|| item!(datetime!(2021-01-02 03:04:05 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.1 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.12 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_4 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_45 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456_7 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456_78 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456_789 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456_789 -01:02)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05.123_456_789 +01:02)));
}
fn format_rfc2822(ben: &mut Bencher<'_>) {
macro_rules! item {
($value:expr) => {
$value.format_into(&mut io::sink(), &Rfc2822)
}
}
ben.iter(|| item!(datetime!(2021-01-02 03:04:05 UTC)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05 +06:07)));
ben.iter(|| item!(datetime!(2021-01-02 03:04:05 -06:07)));
}
fn format_time(ben: &mut Bencher<'_>) {
macro_rules! item {
($format:expr) => {
time!(13:02:03.456_789_012).format_into(
&mut io::sink(),
&$format,
)
}
}
ben.iter(|| item!(fd!("[hour]")));
ben.iter(|| item!(fd!("[hour repr:12]")));
ben.iter(|| item!(fd!("[hour repr:12 padding:none]")));
ben.iter(|| item!(fd!("[hour repr:12 padding:space]")));
ben.iter(|| item!(fd!("[hour repr:24]")));
ben.iter(|| item!(fd!("[hour repr:24]")));
ben.iter(|| item!(fd!("[hour repr:24 padding:none]")));
ben.iter(|| item!(fd!("[hour repr:24 padding:space]")));
ben.iter(|| item!(fd!("[minute]")));
ben.iter(|| item!(fd!("[minute padding:none]")));
ben.iter(|| item!(fd!("[minute padding:space]")));
ben.iter(|| item!(fd!("[minute padding:zero]")));
ben.iter(|| item!(fd!("[period]")));
ben.iter(|| item!(fd!("[period case:upper]")));
ben.iter(|| item!(fd!("[period case:lower]")));
ben.iter(|| item!(fd!("[second]")));
ben.iter(|| item!(fd!("[second padding:none]")));
ben.iter(|| item!(fd!("[second padding:space]")));
ben.iter(|| item!(fd!("[second padding:zero]")));
ben.iter(|| item!(fd!("[subsecond]")));
ben.iter(|| item!(fd!("[subsecond digits:1]")));
ben.iter(|| item!(fd!("[subsecond digits:2]")));
ben.iter(|| item!(fd!("[subsecond digits:3]")));
ben.iter(|| item!(fd!("[subsecond digits:4]")));
ben.iter(|| item!(fd!("[subsecond digits:5]")));
ben.iter(|| item!(fd!("[subsecond digits:6]")));
ben.iter(|| item!(fd!("[subsecond digits:7]")));
ben.iter(|| item!(fd!("[subsecond digits:8]")));
ben.iter(|| item!(fd!("[subsecond digits:9]")));
ben.iter(|| item!(fd!("[subsecond digits:1+]")));
}
fn display_time(ben: &mut Bencher<'_>) {
ben.iter(|| time!(0:00).to_string());
ben.iter(|| time!(23:59).to_string());
ben.iter(|| time!(23:59:59).to_string());
ben.iter(|| time!(0:00:01).to_string());
ben.iter(|| time!(0:00:00.001).to_string());
ben.iter(|| time!(0:00:00.000_001).to_string());
ben.iter(|| time!(0:00:00.000_000_001).to_string());
}
fn format_date(ben: &mut Bencher<'_>) {
macro_rules! item {
($format:expr) => {
date!(2019-12-31).format_into(&mut io::sink(), &$format)
}
}
ben.iter(|| item!(fd!("[day]")));
ben.iter(|| item!(fd!("[month]")));
ben.iter(|| item!(fd!("[month repr:short]")));
ben.iter(|| item!(fd!("[month repr:long]")));
ben.iter(|| item!(fd!("[ordinal]")));
ben.iter(|| item!(fd!("[weekday]")));
ben.iter(|| item!(fd!("[weekday repr:short]")));
ben.iter(|| item!(fd!("[weekday repr:sunday]")));
ben.iter(|| item!(fd!("[weekday repr:sunday one_indexed:false]")));
ben.iter(|| item!(fd!("[weekday repr:monday]")));
ben.iter(|| item!(fd!("[weekday repr:monday one_indexed:false]")));
ben.iter(|| item!(fd!("[week_number]")));
ben.iter(|| item!(fd!("[week_number padding:none]")));
ben.iter(|| item!(fd!("[week_number padding:space]")));
ben.iter(|| item!(fd!("[week_number repr:sunday]")));
ben.iter(|| item!(fd!("[week_number repr:monday]")));
ben.iter(|| item!(fd!("[year]")));
ben.iter(|| item!(fd!("[year base:iso_week]")));
ben.iter(|| item!(fd!("[year sign:mandatory]")));
ben.iter(|| item!(fd!("[year base:iso_week sign:mandatory]")));
ben.iter(|| item!(fd!("[year repr:last_two]")));
ben.iter(|| item!(fd!("[year base:iso_week repr:last_two]")));
}
fn display_date(ben: &mut Bencher<'_>) {
ben.iter(|| date!(2019-01-01).to_string());
ben.iter(|| date!(2019-12-31).to_string());
ben.iter(|| date!(-4713-11-24).to_string());
ben.iter(|| date!(-0001-01-01).to_string());
}
fn format_offset(ben: &mut Bencher<'_>) {
macro_rules! item {
($value:expr, $format:expr) => {
$value.format_into(&mut io::sink(), &$format)
}
}
ben.iter(|| item!(offset!(+01:02:03), fd!("[offset_hour sign:automatic]")));
ben.iter(|| item!(offset!(+01:02:03), fd!("[offset_hour sign:mandatory]")));
ben.iter(|| item!(offset!(-01:02:03), fd!("[offset_hour sign:automatic]")));
ben.iter(|| item!(offset!(-01:02:03), fd!("[offset_hour sign:mandatory]")));
ben.iter(|| item!(offset!(+01:02:03), fd!("[offset_minute]")));
ben.iter(|| item!(offset!(+01:02:03), fd!("[offset_second]")));
}
fn display_offset(ben: &mut Bencher<'_>) {
ben.iter(|| offset!(UTC).to_string());
ben.iter(|| offset!(+0:00:01).to_string());
ben.iter(|| offset!(-0:00:01).to_string());
ben.iter(|| offset!(+1).to_string());
ben.iter(|| offset!(-1).to_string());
ben.iter(|| offset!(+23:59).to_string());
ben.iter(|| offset!(-23:59).to_string());
ben.iter(|| offset!(+23:59:59).to_string());
ben.iter(|| offset!(-23:59:59).to_string());
}
fn format_pdt(ben: &mut Bencher<'_>) {
ben.iter(|| {
datetime!(1970-01-01 0:00).format_into(
&mut io::sink(),
fd!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond]"),
)
});
}
fn display_pdt(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(1970-01-01 0:00).to_string());
ben.iter(|| datetime!(1970-01-01 0:00:01).to_string());
}
fn format_odt(ben: &mut Bencher<'_>) {
let format_description = format_description::parse(
"[year]-[month]-[day] [hour]:[minute]:[second].[subsecond] [offset_hour \
sign:mandatory]:[offset_minute]:[offset_second]",
).expect("invalid format description");
ben.iter(|| {
datetime!(1970-01-01 0:00 UTC).format_into(&mut io::sink(), &format_description)
});
}
fn display_odt(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(1970-01-01 0:00 UTC).to_string());
}
}

89
vendor/time/benchmarks/instant.rs vendored Normal file
View File

@@ -0,0 +1,89 @@
#![expect(deprecated)]
use std::time::Instant as StdInstant;
use criterion::Bencher;
use time::ext::NumericalDuration;
use time::{Duration, Instant};
setup_benchmark! {
"Instant",
fn checked_add(ben: &mut Bencher<'_>) {
let instant = Instant::now();
let dt = 5.seconds();
ben.iter(|| instant.checked_add(dt));
}
fn checked_sub(ben: &mut Bencher<'_>) {
let instant = Instant::now();
let dt = 5.seconds();
ben.iter(|| instant.checked_sub(dt));
}
fn sub(ben: &mut Bencher<'_>) {
let start: Instant = Instant::now();
let end: Instant = start + 1.milliseconds();
ben.iter(|| end - start);
}
fn add_duration(ben: &mut Bencher<'_>) {
let start = Instant::now();
let dt: Duration = 1.seconds();
ben.iter(|| start + dt);
}
fn std_add_duration(ben: &mut Bencher<'_>) {
let start = StdInstant::now();
let dt: Duration = 1.milliseconds();
ben.iter(|| start + dt);
}
fn add_assign_duration(ben: &mut Bencher<'_>) {
let dt: Duration = 1.milliseconds();
iter_batched_ref!(
ben,
Instant::now,
[|start| *start += dt]
);
}
fn std_add_assign_duration(ben: &mut Bencher<'_>) {
let dt: Duration = 1.milliseconds();
iter_batched_ref!(
ben,
StdInstant::now,
[|start| *start += dt]
);
}
fn sub_duration(ben: &mut Bencher<'_>) {
let instant = Instant::now();
let dt: Duration = 100.milliseconds();
ben.iter(|| instant - dt);
}
fn std_sub_duration(ben: &mut Bencher<'_>) {
let instant = StdInstant::now();
let dt: Duration = 100.milliseconds();
ben.iter(|| instant - dt);
}
fn sub_assign_duration(ben: &mut Bencher<'_>) {
let dt: Duration = 100.milliseconds();
iter_batched_ref!(
ben,
Instant::now,
[|instant| *instant -= dt]
);
}
fn std_sub_assign_duration(ben: &mut Bencher<'_>) {
let dt: Duration = 100.milliseconds();
iter_batched_ref!(
ben,
StdInstant::now,
[|instant| *instant -= dt]
);
}
}

118
vendor/time/benchmarks/main.rs vendored Normal file
View File

@@ -0,0 +1,118 @@
//! Benchmarks for `time`.
//!
//! These benchmarks are not very precise, but they're good enough to catch major performance
//! regressions. Run them if you think that may be the case. CI **does not** run benchmarks.
#![allow(
clippy::std_instead_of_core,
clippy::std_instead_of_alloc,
clippy::alloc_instead_of_core,
reason = "irrelevant for benchmarks"
)]
#![allow(
clippy::missing_docs_in_private_items,
reason = "may be removed in the future"
)]
#[cfg(not(all(
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",
bench,
)))]
compile_error!(
"benchmarks must be run as `RUSTFLAGS=\"--cfg bench\" cargo criterion --all-features`"
);
macro_rules! setup_benchmark {
(
$group_prefix:literal,
$(
$(#[$fn_attr:meta])*
fn $fn_name:ident ($bencher:ident : $bencher_type:ty)
$code:block
)*
) => {
$(
$(#[$fn_attr])*
fn $fn_name(
c: &mut ::criterion::Criterion
) {
c.bench_function(
concat!($group_prefix, ": ", stringify!($fn_name)),
|$bencher: $bencher_type| $code
);
}
)*
::criterion::criterion_group! {
name = benches;
config = ::criterion::Criterion::default()
// Set a stricter statistical significance threshold ("p-value")
// for deciding what's an actual performance change vs. noise.
// The more benchmarks, the lower this needs to be in order to
// not get lots of false positives.
.significance_level(0.0001)
// Ignore any performance change less than this (0.05 = 5%) as
// noise, regardless of statistical significance.
.noise_threshold(0.05)
// Reduce the time taken to run each benchmark
.warm_up_time(::std::time::Duration::from_millis(100))
.measurement_time(::std::time::Duration::from_millis(500));
targets = $($fn_name,)*
}
};
}
macro_rules! iter_batched_ref {
($ben:ident, $initializer:expr,[$($routine:expr),+ $(,)?]) => {$(
$ben.iter_batched_ref(
$initializer,
$routine,
::criterion::BatchSize::SmallInput,
);
)+};
}
macro_rules! mods {
($(mod $mod:ident;)+) => {
$(mod $mod;)+
::criterion::criterion_main!($($mod::benches),+);
}
}
mods![
mod date;
mod duration;
mod formatting;
mod instant;
mod month;
mod offset_date_time;
mod parsing;
mod primitive_date_time;
mod rand08;
mod rand09;
mod time;
mod utc_offset;
mod util;
mod weekday;
];
/// Shuffle a slice in a random but deterministic manner.
fn shuffle<T, const N: usize>(mut slice: [T; N]) -> [T; N] {
use ::rand09::prelude::*;
let mut seed = SmallRng::seed_from_u64(0);
slice.shuffle(&mut seed);
slice
}

66
vendor/time/benchmarks/month.rs vendored Normal file
View File

@@ -0,0 +1,66 @@
use criterion::Bencher;
use time::Month::*;
setup_benchmark! {
"Month",
fn previous(ben: &mut Bencher<'_>) {
ben.iter(|| January.previous());
ben.iter(|| February.previous());
ben.iter(|| March.previous());
ben.iter(|| April.previous());
ben.iter(|| May.previous());
ben.iter(|| June.previous());
ben.iter(|| July.previous());
ben.iter(|| August.previous());
ben.iter(|| September.previous());
ben.iter(|| October.previous());
ben.iter(|| November.previous());
ben.iter(|| December.previous());
}
fn next(ben: &mut Bencher<'_>) {
ben.iter(|| January.next());
ben.iter(|| February.next());
ben.iter(|| March.next());
ben.iter(|| April.next());
ben.iter(|| May.next());
ben.iter(|| June.next());
ben.iter(|| July.next());
ben.iter(|| August.next());
ben.iter(|| September.next());
ben.iter(|| October.next());
ben.iter(|| November.next());
ben.iter(|| December.next());
}
fn length(ben: &mut Bencher<'_>) {
// Common year
ben.iter(|| January.length(2019));
ben.iter(|| February.length(2019));
ben.iter(|| March.length(2019));
ben.iter(|| April.length(2019));
ben.iter(|| May.length(2019));
ben.iter(|| June.length(2019));
ben.iter(|| July.length(2019));
ben.iter(|| August.length(2019));
ben.iter(|| September.length(2019));
ben.iter(|| October.length(2019));
ben.iter(|| November.length(2019));
ben.iter(|| December.length(2019));
// Leap year
ben.iter(|| January.length(2020));
ben.iter(|| February.length(2020));
ben.iter(|| March.length(2020));
ben.iter(|| April.length(2020));
ben.iter(|| May.length(2020));
ben.iter(|| June.length(2020));
ben.iter(|| July.length(2020));
ben.iter(|| August.length(2020));
ben.iter(|| September.length(2020));
ben.iter(|| October.length(2020));
ben.iter(|| November.length(2020));
ben.iter(|| December.length(2020));
}
}

View File

@@ -0,0 +1,424 @@
use std::hint::black_box;
use std::time::SystemTime;
use criterion::Bencher;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::{date, datetime, offset, time};
use time::OffsetDateTime;
setup_benchmark! {
"OffsetDateTime",
fn now_utc(ben: &mut Bencher<'_>) {
ben.iter(OffsetDateTime::now_utc);
}
fn now_local(ben: &mut Bencher<'_>) {
ben.iter(OffsetDateTime::now_local);
}
fn to_offset(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2000-01-01 0:00 +11).to_offset(offset!(-5)));
ben.iter(|| datetime!(2000-01-01 0:00 +11).to_offset(offset!(-8)));
}
fn to_utc(ben: &mut Bencher<'_>) {
ben.iter(|| black_box(datetime!(2000-01-01 0:00 +11).to_utc()));
}
fn from_unix_timestamp(ben: &mut Bencher<'_>) {
ben.iter(|| OffsetDateTime::from_unix_timestamp(0));
ben.iter(|| OffsetDateTime::from_unix_timestamp(1_546_300_800));
}
fn from_unix_timestamp_nanos(ben: &mut Bencher<'_>) {
ben.iter(|| OffsetDateTime::from_unix_timestamp_nanos(0));
ben.iter(|| OffsetDateTime::from_unix_timestamp_nanos(1_546_300_800_000_000_000));
}
fn offset(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).offset());
ben.iter(|| datetime!(2019-01-01 0:00 +1).offset());
ben.iter(|| datetime!(2019-01-01 1:00 +1).offset());
}
fn unix_timestamp(ben: &mut Bencher<'_>) {
ben.iter(|| OffsetDateTime::UNIX_EPOCH.unix_timestamp());
ben.iter(|| datetime!(1970-01-01 1:00 +1).unix_timestamp());
ben.iter(|| datetime!(1970-01-01 0:00 -1).unix_timestamp());
}
fn unix_timestamp_nanos(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(1970-01-01 0:00 UTC).unix_timestamp_nanos());
ben.iter(|| datetime!(1970-01-01 1:00 +1).unix_timestamp_nanos());
ben.iter(|| datetime!(1970-01-01 0:00 -1).unix_timestamp_nanos());
}
fn date(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).date());
ben.iter(|| datetime!(2018-12-31 23:00 -1).date());
}
fn time(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).time());
ben.iter(|| datetime!(2018-12-31 23:00 -1).time());
}
fn year(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).year());
ben.iter(|| datetime!(2018-12-31 23:00 -1).year());
}
fn ordinal(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).ordinal());
ben.iter(|| datetime!(2018-12-31 23:00 -1).ordinal());
}
fn hour(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).hour());
ben.iter(|| datetime!(2018-12-31 23:00 -1).hour());
}
fn minute(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).minute());
ben.iter(|| datetime!(2018-12-31 23:00 -1).minute());
}
fn second(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC).second());
ben.iter(|| datetime!(2018-12-31 23:00 -1).second());
}
fn replace_time(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2020-01-01 5:00 UTC).replace_time(time!(12:00)));
ben.iter(|| datetime!(2020-01-01 12:00 -5).replace_time(time!(7:00)));
ben.iter(|| datetime!(2020-01-01 0:00 +1).replace_time(time!(12:00)));
}
fn replace_date(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2020-01-01 12:00 UTC).replace_date(date!(2020-01-30)));
ben.iter(|| datetime!(2020-01-01 0:00 +1).replace_date(date!(2020-01-30)));
}
fn replace_date_time(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2020-01-01 12:00 UTC).replace_date_time(datetime!(2020-01-30 16:00)));
ben.iter(|| datetime!(2020-01-01 12:00 +1).replace_date_time(datetime!(2020-01-30 0:00)));
}
fn replace_offset(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2020-01-01 0:00 UTC).replace_offset(offset!(-5)));
}
fn partial_eq(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(1999-12-31 23:00 -1) == datetime!(2000-01-01 0:00 UTC));
}
fn partial_ord(ben: &mut Bencher<'_>) {
ben.iter(||
datetime!(2019-01-01 0:00 UTC).partial_cmp(&datetime!(1999-12-31 23:00 -1))
);
}
fn ord(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00 UTC) == datetime!(2018-12-31 23:00 -1));
ben.iter(|| datetime!(2019-01-01 0:00:00.000_000_001 UTC) > datetime!(2019-01-01 0:00 UTC));
}
fn hash(ben: &mut Bencher<'_>) {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
iter_batched_ref!(
ben,
DefaultHasher::new,
[
|hasher| datetime!(2019-01-01 0:00 UTC).hash(hasher),
|hasher| datetime!(2018-12-31 23:00 -1).hash(hasher),
]
);
}
fn add_duration(ben: &mut Bencher<'_>) {
let a = 5.days();
let b = 1.days();
let c = 2.seconds();
let d = (-2).seconds();
let e = 1.hours();
ben.iter(|| datetime!(2019-01-01 0:00 UTC) + a);
ben.iter(|| datetime!(2019-12-31 0:00 UTC) + b);
ben.iter(|| datetime!(2019-12-31 23:59:59 UTC) + c);
ben.iter(|| datetime!(2020-01-01 0:00:01 UTC) + d);
ben.iter(|| datetime!(1999-12-31 23:00 UTC) + e);
}
fn add_std_duration(ben: &mut Bencher<'_>) {
let a = 5.std_days();
let b = 1.std_days();
let c = 2.std_seconds();
ben.iter(|| datetime!(2019-01-01 0:00 UTC) + a);
ben.iter(|| datetime!(2019-12-31 0:00 UTC) + b);
ben.iter(|| datetime!(2019-12-31 23:59:59 UTC) + c);
}
fn add_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00 UTC),
[
|datetime| *datetime += a,
|datetime| *datetime += b,
]
);
}
fn add_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_days();
let b = 1.std_seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00 UTC),
[
|datetime| *datetime += a,
|datetime| *datetime += b,
]
);
}
fn sub_duration(ben: &mut Bencher<'_>) {
let a = 5.days();
let b = 1.days();
let c = 2.seconds();
ben.iter(|| datetime!(2019-01-06 0:00 UTC) - a);
ben.iter(|| datetime!(2020-01-01 0:00 UTC) - b);
ben.iter(|| datetime!(2020-01-01 0:00:01 UTC) - c);
}
fn sub_std_duration(ben: &mut Bencher<'_>) {
let a = 5.std_days();
let b = 1.std_days();
let c = 2.std_seconds();
ben.iter(|| datetime!(2019-01-06 0:00 UTC) - a);
ben.iter(|| datetime!(2020-01-01 0:00 UTC) - b);
ben.iter(|| datetime!(2020-01-01 0:00:01 UTC) - c);
}
fn sub_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00 UTC),
[
|datetime| *datetime -= a,
|datetime| *datetime -= b,
]
);
}
fn sub_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_days();
let b = 1.std_seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00 UTC),
[
|datetime| *datetime -= a,
|datetime| *datetime -= b,
]
);
}
fn std_add_duration(ben: &mut Bencher<'_>) {
let a1 = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let a2 = 0.seconds();
let b1 = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let b2 = 5.days();
let c1 = SystemTime::from(datetime!(2019-12-31 0:00 UTC));
let c2 = 1.days();
let d1 = SystemTime::from(datetime!(2019-12-31 23:59:59 UTC));
let d2 = 2.seconds();
let e1 = SystemTime::from(datetime!(2020-01-01 0:00:01 UTC));
let e2 = (-2).seconds();
ben.iter(|| a1 + a2);
ben.iter(|| b1 + b2);
ben.iter(|| c1 + c2);
ben.iter(|| d1 + d2);
ben.iter(|| e1 + e2);
}
fn std_add_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| SystemTime::from(datetime!(2019-01-01 0:00 UTC)),
[
|datetime| *datetime += a,
|datetime| *datetime += b,
]
);
}
fn std_sub_duration(ben: &mut Bencher<'_>) {
let a1 = SystemTime::from(datetime!(2019-01-06 0:00 UTC));
let a2 = 5.days();
let b1 = SystemTime::from(datetime!(2020-01-01 0:00 UTC));
let b2 = 1.days();
let c1 = SystemTime::from(datetime!(2020-01-01 0:00:01 UTC));
let c2 = 2.seconds();
let d1 = SystemTime::from(datetime!(2019-12-31 23:59:59 UTC));
let d2 = (-2).seconds();
ben.iter(|| a1 - a2);
ben.iter(|| b1 - b2);
ben.iter(|| c1 - c2);
ben.iter(|| d1 - d2);
}
fn std_sub_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| SystemTime::from(datetime!(2019-01-01 0:00 UTC)),
[
|datetime| *datetime -= a,
|datetime| *datetime -= b,
]
);
}
fn sub_self(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-02 0:00 UTC) - datetime!(2019-01-01 0:00 UTC));
ben.iter(|| datetime!(2019-01-01 0:00 UTC) - datetime!(2019-01-02 0:00 UTC));
ben.iter(|| datetime!(2020-01-01 0:00 UTC) - datetime!(2019-12-31 0:00 UTC));
ben.iter(|| datetime!(2019-12-31 0:00 UTC) - datetime!(2020-01-01 0:00 UTC));
}
fn std_sub(ben: &mut Bencher<'_>) {
let a = SystemTime::from(datetime!(2019-01-02 0:00 UTC));
let b = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let c = SystemTime::from(datetime!(2020-01-01 0:00 UTC));
let d = SystemTime::from(datetime!(2019-12-31 0:00 UTC));
ben.iter(|| a - datetime!(2019-01-01 0:00 UTC));
ben.iter(|| b - datetime!(2019-01-02 0:00 UTC));
ben.iter(|| c - datetime!(2019-12-31 0:00 UTC));
ben.iter(|| d - datetime!(2020-01-01 0:00 UTC));
}
fn sub_std(ben: &mut Bencher<'_>) {
let a = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let b = SystemTime::from(datetime!(2019-01-02 0:00 UTC));
let c = SystemTime::from(datetime!(2019-12-31 0:00 UTC));
let d = SystemTime::from(datetime!(2020-01-01 0:00 UTC));
ben.iter(|| datetime!(2019-01-02 0:00 UTC) - a);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) - b);
ben.iter(|| datetime!(2020-01-01 0:00 UTC) - c);
ben.iter(|| datetime!(2019-12-31 0:00 UTC) - d);
}
fn eq_std(ben: &mut Bencher<'_>) {
let a = OffsetDateTime::now_utc();
let b = SystemTime::from(a);
ben.iter(|| a == b);
}
fn std_eq(ben: &mut Bencher<'_>) {
let a = OffsetDateTime::now_utc();
let b = SystemTime::from(a);
ben.iter(|| b == a);
}
fn ord_std(ben: &mut Bencher<'_>) {
let a = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let b = SystemTime::from(datetime!(2020-01-01 0:00 UTC));
let c = SystemTime::from(datetime!(2019-02-01 0:00 UTC));
let d = SystemTime::from(datetime!(2019-01-02 0:00 UTC));
let e = SystemTime::from(datetime!(2019-01-01 1:00:00 UTC));
let f = SystemTime::from(datetime!(2019-01-01 0:01:00 UTC));
let g = SystemTime::from(datetime!(2019-01-01 0:00:01 UTC));
let h = SystemTime::from(datetime!(2019-01-01 0:00:00.001 UTC));
let i = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let j = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let k = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let l = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let m = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let n = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let o = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
ben.iter(|| datetime!(2019-01-01 0:00 UTC) == a);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < b);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < c);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < d);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < e);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < f);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < g);
ben.iter(|| datetime!(2019-01-01 0:00 UTC) < h);
ben.iter(|| datetime!(2020-01-01 0:00 UTC) > i);
ben.iter(|| datetime!(2019-02-01 0:00 UTC) > j);
ben.iter(|| datetime!(2019-01-02 0:00 UTC) > k);
ben.iter(|| datetime!(2019-01-01 1:00:00 UTC) > l);
ben.iter(|| datetime!(2019-01-01 0:01:00 UTC) > m);
ben.iter(|| datetime!(2019-01-01 0:00:01 UTC) > n);
ben.iter(|| datetime!(2019-01-01 0:00:00.000_000_001 UTC) > o);
}
fn std_ord(ben: &mut Bencher<'_>) {
let a = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let b = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let c = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let d = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let e = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let f = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let g = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let h = SystemTime::from(datetime!(2019-01-01 0:00 UTC));
let i = SystemTime::from(datetime!(2020-01-01 0:00 UTC));
let j = SystemTime::from(datetime!(2019-02-01 0:00 UTC));
let k = SystemTime::from(datetime!(2019-01-02 0:00 UTC));
let l = SystemTime::from(datetime!(2019-01-01 1:00:00 UTC));
let m = SystemTime::from(datetime!(2019-01-01 0:01:00 UTC));
let n = SystemTime::from(datetime!(2019-01-01 0:00:01 UTC));
let o = SystemTime::from(datetime!(2019-01-01 0:00:00.001 UTC));
ben.iter(|| a == datetime!(2019-01-01 0:00 UTC));
ben.iter(|| b < datetime!(2020-01-01 0:00 UTC));
ben.iter(|| c < datetime!(2019-02-01 0:00 UTC));
ben.iter(|| d < datetime!(2019-01-02 0:00 UTC));
ben.iter(|| e < datetime!(2019-01-01 1:00:00 UTC));
ben.iter(|| f < datetime!(2019-01-01 0:01:00 UTC));
ben.iter(|| g < datetime!(2019-01-01 0:00:01 UTC));
ben.iter(|| h < datetime!(2019-01-01 0:00:00.000_000_001 UTC));
ben.iter(|| i > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| j > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| k > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| l > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| m > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| n > datetime!(2019-01-01 0:00 UTC));
ben.iter(|| o > datetime!(2019-01-01 0:00 UTC));
}
fn from_std(ben: &mut Bencher<'_>) {
let a = SystemTime::UNIX_EPOCH;
let b = SystemTime::UNIX_EPOCH - 1.std_days();
let c = SystemTime::UNIX_EPOCH + 1.std_days();
ben.iter(|| OffsetDateTime::from(a));
ben.iter(|| OffsetDateTime::from(b));
ben.iter(|| OffsetDateTime::from(c));
}
fn to_std(ben: &mut Bencher<'_>) {
let a = OffsetDateTime::UNIX_EPOCH;
let b = OffsetDateTime::UNIX_EPOCH + 1.days();
let c = OffsetDateTime::UNIX_EPOCH - 1.days();
ben.iter(|| SystemTime::from(a));
ben.iter(|| SystemTime::from(b));
ben.iter(|| SystemTime::from(c));
}
}

230
vendor/time/benchmarks/parsing.rs vendored Normal file
View File

@@ -0,0 +1,230 @@
use criterion::Bencher;
use time::OffsetDateTime;
use time::format_description::well_known::{Rfc2822, Rfc3339};
use time::format_description::{Component, modifier};
use time::parsing::Parsed;
macro_rules! component {
($name:ident {$($field:ident : $value:expr),* $(,)? }) => {{
const COMPONENT: Component = Component::$name({
#[allow(unused_mut, reason = "macro-generated code")]
let mut modifier = modifier::$name::default();
$(modifier.$field = $value;)*
modifier
});
COMPONENT
}};
}
setup_benchmark! {
"Parsing",
fn parse_component_year(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b"2021", component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
iso_week_based: false,
sign_is_mandatory: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"21", component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::LastTwo,
iso_week_based: false,
sign_is_mandatory: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"2021", component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
iso_week_based: true,
sign_is_mandatory: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"21", component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::LastTwo,
iso_week_based: true,
sign_is_mandatory: false,
}))
});
}
fn parse_component_month(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b" 1", component!(Month {
padding: modifier::Padding::Space,
repr: modifier::MonthRepr::Numerical,
}))
});
ben.iter(|| {
parsed.parse_component(b"Jan", component!(Month {
padding: modifier::Padding::None,
repr: modifier::MonthRepr::Short,
}))
});
ben.iter(|| {
parsed.parse_component(b"January", component!(Month {
padding: modifier::Padding::None,
repr: modifier::MonthRepr::Long,
}))
});
}
fn parse_component_ordinal(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b"012", component!(Ordinal {
padding: modifier::Padding::Zero,
}))
});
}
fn parse_component_weekday(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b"Sun", component!(Weekday {
repr: modifier::WeekdayRepr::Short,
one_indexed: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"Sunday", component!(Weekday {
repr: modifier::WeekdayRepr::Long,
one_indexed: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"0", component!(Weekday {
repr: modifier::WeekdayRepr::Sunday,
one_indexed: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"1", component!(Weekday {
repr: modifier::WeekdayRepr::Sunday,
one_indexed: true,
}))
});
ben.iter(|| {
parsed.parse_component(b"6", component!(Weekday {
repr: modifier::WeekdayRepr::Monday,
one_indexed: false,
}))
});
ben.iter(|| {
parsed.parse_component(b"7", component!(Weekday {
repr: modifier::WeekdayRepr::Monday,
one_indexed: true,
}))
});
}
fn parse_component_week_number(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b"2", component!(WeekNumber {
padding: modifier::Padding::None,
repr: modifier::WeekNumberRepr::Sunday,
}))
});
ben.iter(|| {
parsed.parse_component(b"2", component!(WeekNumber {
padding: modifier::Padding::None,
repr: modifier::WeekNumberRepr::Monday,
}))
});
ben.iter(|| {
parsed.parse_component(b"2", component!(WeekNumber {
padding: modifier::Padding::None,
repr: modifier::WeekNumberRepr::Iso,
}))
});
}
fn parse_component_subsecond(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| {
parsed.parse_component(b"1", component!(Subsecond {
digits: modifier::SubsecondDigits::One,
}))
});
ben.iter(|| {
parsed.parse_component(b"12", component!(Subsecond {
digits: modifier::SubsecondDigits::Two,
}))
});
ben.iter(|| {
parsed.parse_component(b"123", component!(Subsecond {
digits: modifier::SubsecondDigits::Three,
}))
});
ben.iter(|| {
parsed.parse_component(b"1234", component!(Subsecond {
digits: modifier::SubsecondDigits::Four,
}))
});
ben.iter(|| {
parsed.parse_component(b"12345", component!(Subsecond {
digits: modifier::SubsecondDigits::Five,
}))
});
ben.iter(|| {
parsed.parse_component(b"123456", component!(Subsecond {
digits: modifier::SubsecondDigits::Six,
}))
});
ben.iter(|| {
parsed.parse_component(b"1234567", component!(Subsecond {
digits: modifier::SubsecondDigits::Seven,
}))
});
ben.iter(|| {
parsed.parse_component(b"12345678", component!(Subsecond {
digits: modifier::SubsecondDigits::Eight,
}))
});
ben.iter(|| {
parsed.parse_component(b"123456789", component!(Subsecond {
digits: modifier::SubsecondDigits::Nine,
}))
});
ben.iter(|| {
parsed.parse_component(b"123456789", component!(Subsecond {
digits: modifier::SubsecondDigits::OneOrMore,
}))
});
}
fn parse_component_unix_timestamp(ben: &mut Bencher<'_>) {
let mut parsed = Parsed::new();
ben.iter(|| parsed.parse_component(std::hint::black_box(b"1234567890"), component!(UnixTimestamp {})));
}
fn parse_rfc3339(ben: &mut Bencher<'_>) {
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.1Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.12Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.123Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.1234Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.12345Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.123456Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.1234567Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.12345678Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.123456789Z", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.123456789-01:02", &Rfc3339));
ben.iter(|| OffsetDateTime::parse("2021-01-02T03:04:05.123456789+01:02", &Rfc3339));
}
fn parse_rfc2822(ben: &mut Bencher<'_>) {
ben.iter(|| OffsetDateTime::parse("Sat, 02 Jan 2021 03:04:05 +0000", &Rfc2822));
ben.iter(|| OffsetDateTime::parse("Sat, 02 Jan 2021 03:04:05 +0607", &Rfc2822));
ben.iter(|| OffsetDateTime::parse("Sat, 02 Jan 2021 03:04:05 -0607", &Rfc2822));
}
}

View File

@@ -0,0 +1,144 @@
use criterion::Bencher;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::{datetime, offset};
setup_benchmark! {
"PrimitiveDateTime",
// All getters are trivially dispatched to the relevant field, and do not need to be benchmarked
// a second time.
fn assume_offset(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00).assume_offset(offset!(UTC)));
ben.iter(|| datetime!(2019-01-01 0:00).assume_offset(offset!(-1)));
}
fn assume_utc(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00).assume_utc());
}
fn add_duration(ben: &mut Bencher<'_>) {
let a = 5.days();
let b = 1.days();
let c = 2.seconds();
let d = (-2).seconds();
let e = 1.hours();
ben.iter(|| datetime!(2019-01-01 0:00) + a);
ben.iter(|| datetime!(2019-12-31 0:00) + b);
ben.iter(|| datetime!(2019-12-31 23:59:59) + c);
ben.iter(|| datetime!(2020-01-01 0:00:01) + d);
ben.iter(|| datetime!(1999-12-31 23:00) + e);
}
fn add_std_duration(ben: &mut Bencher<'_>) {
let a = 5.std_days();
let b = 1.std_days();
let c = 2.std_seconds();
ben.iter(|| datetime!(2019-01-01 0:00) + a);
ben.iter(|| datetime!(2019-12-31 0:00) + b);
ben.iter(|| datetime!(2019-12-31 23:59:59) + c);
}
fn add_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00),
[
|datetime| *datetime += a,
|datetime| *datetime += b,
]
);
}
fn add_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_days();
let b = 1.std_seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00),
[
|datetime| *datetime += a,
|datetime| *datetime += b,
]
);
}
fn sub_duration(ben: &mut Bencher<'_>) {
let a = 5.days();
let b = 1.days();
let c = 2.seconds();
let d = (-2).seconds();
let e = (-1).hours();
ben.iter(|| datetime!(2019-01-06 0:00) - a);
ben.iter(|| datetime!(2020-01-01 0:00) - b);
ben.iter(|| datetime!(2020-01-01 0:00:01) - c);
ben.iter(|| datetime!(2019-12-31 23:59:59) - d);
ben.iter(|| datetime!(1999-12-31 23:00) - e);
}
fn sub_std_duration(ben: &mut Bencher<'_>) {
let a = 5.std_days();
let b = 1.std_days();
let c = 2.std_seconds();
ben.iter(|| datetime!(2019-01-06 0:00) - a);
ben.iter(|| datetime!(2020-01-01 0:00) - b);
ben.iter(|| datetime!(2020-01-01 0:00:01) - c);
}
fn sub_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.days();
let b = 1.seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00),
[
|datetime| *datetime -= a,
|datetime| *datetime -= b,
]
);
}
fn sub_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_days();
let b = 1.std_seconds();
iter_batched_ref!(
ben,
|| datetime!(2019-01-01 0:00),
[
|datetime| *datetime -= a,
|datetime| *datetime -= b,
]
);
}
fn sub_datetime(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-02 0:00) - datetime!(2019-01-01 0:00));
ben.iter(|| datetime!(2019-01-01 0:00) - datetime!(2019-01-02 0:00));
ben.iter(|| datetime!(2020-01-01 0:00) - datetime!(2019-12-31 0:00));
ben.iter(|| datetime!(2019-12-31 0:00) - datetime!(2020-01-01 0:00));
}
fn ord(ben: &mut Bencher<'_>) {
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2020-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-02-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-02 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-01 1:00)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-01 0:01)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-01 0:00:01)));
ben.iter(|| datetime!(2019-01-01 0:00).partial_cmp(&datetime!(2019-01-01 0:00:00.000_000_001)));
ben.iter(|| datetime!(2020-01-01 0:00).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-02-01 0:00).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-02 0:00).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 1:00).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:01).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00:01).partial_cmp(&datetime!(2019-01-01 0:00)));
ben.iter(|| datetime!(2019-01-01 0:00:00.000_000_001).partial_cmp(&datetime!(2019-01-01 0:00)));
}
}

30
vendor/time/benchmarks/rand08.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
use criterion::Bencher;
use rand08::rngs::mock::StepRng;
use rand08::Rng;
use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
macro_rules! bench_rand {
($($name:ident : $type:ty),* $(,)?) => {
setup_benchmark! {
"Random",
$(fn $name(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| StepRng::new(0, 1),
[|rng| rng.r#gen::<$type>()]
);
})*
}
}
}
bench_rand![
time: Time,
date: Date,
utc_offset: UtcOffset,
primitive_date_time: PrimitiveDateTime,
offset_date_time: OffsetDateTime,
duration: Duration,
weekday: Weekday,
month: Month,
];

61
vendor/time/benchmarks/rand09.rs vendored Normal file
View File

@@ -0,0 +1,61 @@
use criterion::Bencher;
use rand09::Rng;
use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
macro_rules! bench_rand {
($($name:ident : $type:ty),* $(,)?) => {
setup_benchmark! {
"Random",
$(fn $name(ben: &mut Bencher<'_>) {
iter_batched_ref!(
ben,
|| StepRng::new(0, 1),
[|rng| rng.random::<$type>()]
);
})*
}
}
}
bench_rand![
time: Time,
date: Date,
utc_offset: UtcOffset,
primitive_date_time: PrimitiveDateTime,
offset_date_time: OffsetDateTime,
duration: Duration,
weekday: Weekday,
month: Month,
];
// copy of `StepRng` from rand 0.8 to avoid deprecation warnings
#[derive(Debug, Clone)]
struct StepRng {
v: u64,
a: u64,
}
impl StepRng {
const fn new(initial: u64, increment: u64) -> Self {
Self {
v: initial,
a: increment,
}
}
}
impl rand09::RngCore for StepRng {
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}
fn next_u64(&mut self) -> u64 {
let res = self.v;
self.v = self.v.wrapping_add(self.a);
res
}
fn fill_bytes(&mut self, dst: &mut [u8]) {
rand09::rand_core::impls::fill_bytes_via_next(self, dst)
}
}

271
vendor/time/benchmarks/time.rs vendored Normal file
View File

@@ -0,0 +1,271 @@
use std::hint::black_box;
use criterion::Bencher;
use time::ext::{NumericalDuration, NumericalStdDuration};
use time::macros::time;
use time::Time;
setup_benchmark! {
"Time",
fn from_hms(ben: &mut Bencher<'_>) {
ben.iter(|| Time::from_hms(1, 2, 3));
}
fn from_hms_milli(ben: &mut Bencher<'_>) {
ben.iter(|| Time::from_hms_milli(1, 2, 3, 4));
}
fn from_hms_micro(ben: &mut Bencher<'_>) {
ben.iter(|| Time::from_hms_micro(1, 2, 3, 4));
}
fn from_hms_nano(ben: &mut Bencher<'_>) {
ben.iter(|| Time::from_hms_nano(1, 2, 3, 4));
}
fn as_hms(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.as_hms());
}
fn as_hms_milli(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.as_hms_milli());
}
fn as_hms_micro(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.as_hms_micro());
}
fn as_hms_nano(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.as_hms_nano());
}
fn hour(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.hour());
}
fn minute(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.minute());
}
fn second(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.second());
}
fn millisecond(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.millisecond());
}
fn microsecond(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.microsecond());
}
fn nanosecond(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT.nanosecond());
}
fn add_duration(ben: &mut Bencher<'_>) {
let a = 1.milliseconds();
let b = 1.seconds();
let c = 1.minutes();
let d = 1.hours();
let e = 1.days();
ben.iter(|| Time::MIDNIGHT + a);
ben.iter(|| Time::MIDNIGHT + b);
ben.iter(|| Time::MIDNIGHT + c);
ben.iter(|| Time::MIDNIGHT + d);
ben.iter(|| Time::MIDNIGHT + e);
}
fn add_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.milliseconds();
let b = 1.seconds();
let c = 1.minutes();
let d = 1.hours();
let e = 1.days();
iter_batched_ref!(
ben,
|| Time::MIDNIGHT,
[
|time| *time += a,
|time| *time += b,
|time| *time += c,
|time| *time += d,
|time| *time += e,
]
);
}
fn sub_duration(ben: &mut Bencher<'_>) {
let a = 1.milliseconds();
let b = 1.seconds();
let c = 1.minutes();
let d = 1.hours();
let e = 1.days();
ben.iter(|| Time::MIDNIGHT - a);
ben.iter(|| Time::MIDNIGHT - b);
ben.iter(|| Time::MIDNIGHT - c);
ben.iter(|| Time::MIDNIGHT - d);
ben.iter(|| Time::MIDNIGHT - e);
}
fn sub_assign_duration(ben: &mut Bencher<'_>) {
let a = 1.milliseconds();
let b = 1.seconds();
let c = 1.minutes();
let d = 1.hours();
let e = 1.days();
iter_batched_ref!(
ben,
|| Time::MIDNIGHT,
[
|time| *time -= a,
|time| *time -= b,
|time| *time -= c,
|time| *time -= d,
|time| *time -= e,
]
);
}
fn add_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_milliseconds();
let b = 1.std_seconds();
let c = 1.std_minutes();
let d = 1.std_hours();
let e = 1.std_days();
ben.iter(|| Time::MIDNIGHT + a);
ben.iter(|| Time::MIDNIGHT + b);
ben.iter(|| Time::MIDNIGHT + c);
ben.iter(|| Time::MIDNIGHT + d);
ben.iter(|| Time::MIDNIGHT + e);
}
fn add_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_milliseconds();
let b = 1.std_seconds();
let c = 1.std_minutes();
let d = 1.std_hours();
let e = 1.std_days();
iter_batched_ref!(
ben,
|| Time::MIDNIGHT,
[
|time| *time += a,
|time| *time += b,
|time| *time += c,
|time| *time += d,
|time| *time += e,
]
);
}
fn sub_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_milliseconds();
let b = 1.std_seconds();
let c = 1.std_minutes();
let d = 1.std_hours();
let e = 1.std_days();
ben.iter(|| Time::MIDNIGHT - a);
ben.iter(|| Time::MIDNIGHT - b);
ben.iter(|| Time::MIDNIGHT - c);
ben.iter(|| Time::MIDNIGHT - d);
ben.iter(|| Time::MIDNIGHT - e);
}
fn sub_assign_std_duration(ben: &mut Bencher<'_>) {
let a = 1.std_milliseconds();
let b = 1.std_seconds();
let c = 1.std_minutes();
let d = 1.std_hours();
let e = 1.std_days();
iter_batched_ref!(
ben,
|| Time::MIDNIGHT,
[
|time| *time -= a,
|time| *time -= b,
|time| *time -= c,
|time| *time -= d,
|time| *time -= e,
]
);
}
fn sub_time(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT - time!(0:00:01));
ben.iter(|| time!(1:00) - Time::MIDNIGHT);
ben.iter(|| time!(1:00) - time!(0:00:01));
}
fn ordering(ben: &mut Bencher<'_>) {
ben.iter(|| Time::MIDNIGHT < time!(0:00:00.000_000_001));
ben.iter(|| Time::MIDNIGHT < time!(0:00:01));
ben.iter(|| time!(12:00) > time!(11:00));
ben.iter(|| Time::MIDNIGHT == time!(0:00:00.000_000_001));
}
fn sort_align_8(ben: &mut Bencher<'_>) {
ben.iter_batched_ref(
|| {
#[repr(C,align(8))]
struct Padder {
arr: [Time;4096],
}
let mut res = Padder {
arr: [Time::MIDNIGHT;4096]
};
let mut last = Time::MIDNIGHT;
let mut last_hour = 0;
for t in &mut res.arr {
*t = last;
t.replace_hour(last_hour).expect("failed to replace hour");
last += 997.std_milliseconds();
last_hour = (last_hour + 5) % 24;
}
res.arr.sort_unstable_by_key(|t|
(t.nanosecond(),t.second(),t.minute(),t.hour())
);
res
},
|v| black_box(v).arr.sort_unstable(),
criterion::BatchSize::SmallInput
)
}
fn sort_align_4(ben: &mut Bencher<'_>) {
ben.iter_batched_ref(
|| {
#[repr(C,align(8))]
struct Padder {
pad: u32,
arr: [Time;4096],
}
let mut res = Padder {
pad: 0,
arr: [Time::MIDNIGHT;4096]
};
let mut last = Time::MIDNIGHT;
let mut last_hour = 0;
for t in &mut res.arr {
*t = last;
t.replace_hour(last_hour).expect("failed to replace hour");
last += 997.std_milliseconds();
last_hour = (last_hour + 5) % 24;
}
res.arr.sort_unstable_by_key(|t|
(t.nanosecond(),t.second(),t.minute(),t.hour())
);
res
},
|v| black_box(v).arr.sort_unstable(),
criterion::BatchSize::SmallInput
)
}
fn duration_until(ben: &mut Bencher<'_>) {
let a = black_box(time!(1:02:03.004_005_006));
let b = black_box(time!(4:05:06.007_008_009));
ben.iter(|| black_box(a.duration_until(b)));
}
}

12
vendor/time/benchmarks/utc_date_time.rs vendored Normal file
View File

@@ -0,0 +1,12 @@
use std::hint::black_box;
use criterion::Bencher;
use time::macros::{offset, utc_datetime};
setup_benchmark! {
"UtcDateTime",
fn to_offset(ben: &mut Bencher<'_>) {
ben.iter(|| black_box(utc_datetime!(2000-01-01 0:00)).to_offset(black_box(offset!(-5))));
}
}

58
vendor/time/benchmarks/utc_offset.rs vendored Normal file
View File

@@ -0,0 +1,58 @@
use criterion::Bencher;
use time::{OffsetDateTime, UtcOffset};
setup_benchmark! {
"UtcOffset",
fn from_hms(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::from_hms(0, 0, 0));
}
fn from_whole_seconds(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::from_whole_seconds(0));
}
fn as_hms(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.as_hms());
}
fn whole_hours(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.whole_hours());
}
fn whole_minutes(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.whole_minutes());
}
fn minutes_past_hour(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.minutes_past_hour());
}
fn whole_seconds(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.whole_seconds());
}
fn seconds_past_minute(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.seconds_past_minute());
}
fn is_utc(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.is_utc());
}
fn is_positive(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.is_positive());
}
fn is_negative(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::UTC.is_negative());
}
fn local_offset_at(ben: &mut Bencher<'_>) {
ben.iter(|| UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH));
}
fn current_local_offset(ben: &mut Bencher<'_>) {
ben.iter(UtcOffset::current_local_offset);
}
}

54
vendor/time/benchmarks/util.rs vendored Normal file
View File

@@ -0,0 +1,54 @@
use std::hint::black_box as bb;
use std::sync::LazyLock;
use criterion::Bencher;
use time::util;
/// Generate a representative sample of all years.
fn representative_years() -> [i32; 800] {
static DATES: LazyLock<[i32; 800]> = LazyLock::new(|| {
let mut years = [0; _];
for year in -400..400 {
years[(year + 400) as usize] = year;
}
crate::shuffle(years)
});
*DATES
}
setup_benchmark! {
"Utils",
fn noop(ben: &mut Bencher<'_>) {
ben.iter(|| {
for i in representative_years() {
let _ = bb(i);
}
});
}
fn is_leap_year(ben: &mut Bencher<'_>) {
ben.iter(|| {
for year in representative_years() {
let _ = bb(util::is_leap_year(bb(year)));
}
});
}
fn days_in_year(ben: &mut Bencher<'_>) {
ben.iter(|| {
for year in representative_years() {
let _ = bb(util::days_in_year(bb(year)));
}
});
}
fn weeks_in_year(ben: &mut Bencher<'_>) {
ben.iter(|| {
for year in representative_years() {
let _ = bb(util::weeks_in_year(bb(year)));
}
});
}
}

81
vendor/time/benchmarks/weekday.rs vendored Normal file
View File

@@ -0,0 +1,81 @@
use criterion::Bencher;
use time::Weekday::*;
setup_benchmark! {
"Weekday",
fn previous(ben: &mut Bencher<'_>) {
ben.iter(|| Sunday.previous());
ben.iter(|| Monday.previous());
ben.iter(|| Tuesday.previous());
ben.iter(|| Wednesday.previous());
ben.iter(|| Thursday.previous());
ben.iter(|| Friday.previous());
ben.iter(|| Saturday.previous());
}
fn next(ben: &mut Bencher<'_>) {
ben.iter(|| Sunday.next());
ben.iter(|| Monday.next());
ben.iter(|| Tuesday.next());
ben.iter(|| Wednesday.next());
ben.iter(|| Thursday.next());
ben.iter(|| Friday.next());
ben.iter(|| Saturday.next());
}
fn nth(ben: &mut Bencher<'_>) {
ben.iter(|| Sunday.nth_next(0));
ben.iter(|| Sunday.nth_next(1));
ben.iter(|| Sunday.nth_next(2));
ben.iter(|| Sunday.nth_next(3));
ben.iter(|| Sunday.nth_next(4));
ben.iter(|| Sunday.nth_next(5));
ben.iter(|| Sunday.nth_next(6));
ben.iter(|| Sunday.nth_next(7));
ben.iter(|| Sunday.nth_next(u8::MAX));
ben.iter(|| Monday.nth_next(7));
ben.iter(|| Monday.nth_next(u8::MAX));
}
fn number_from_monday(ben: &mut Bencher<'_>) {
ben.iter(|| Monday.number_from_monday());
ben.iter(|| Tuesday.number_from_monday());
ben.iter(|| Wednesday.number_from_monday());
ben.iter(|| Thursday.number_from_monday());
ben.iter(|| Friday.number_from_monday());
ben.iter(|| Saturday.number_from_monday());
ben.iter(|| Sunday.number_from_monday());
}
fn number_from_sunday(ben: &mut Bencher<'_>) {
ben.iter(|| Sunday.number_from_sunday());
ben.iter(|| Monday.number_from_sunday());
ben.iter(|| Tuesday.number_from_sunday());
ben.iter(|| Wednesday.number_from_sunday());
ben.iter(|| Thursday.number_from_sunday());
ben.iter(|| Friday.number_from_sunday());
ben.iter(|| Saturday.number_from_sunday());
}
fn number_days_from_monday(ben: &mut Bencher<'_>) {
ben.iter(|| Monday.number_days_from_monday());
ben.iter(|| Tuesday.number_days_from_monday());
ben.iter(|| Wednesday.number_days_from_monday());
ben.iter(|| Thursday.number_days_from_monday());
ben.iter(|| Friday.number_days_from_monday());
ben.iter(|| Saturday.number_days_from_monday());
ben.iter(|| Sunday.number_days_from_monday());
}
fn number_days_from_sunday(ben: &mut Bencher<'_>) {
ben.iter(|| Sunday.number_days_from_sunday());
ben.iter(|| Monday.number_days_from_sunday());
ben.iter(|| Tuesday.number_days_from_sunday());
ben.iter(|| Wednesday.number_days_from_sunday());
ben.iter(|| Thursday.number_days_from_sunday());
ben.iter(|| Friday.number_days_from_sunday());
ben.iter(|| Saturday.number_days_from_sunday());
}
}

1621
vendor/time/src/date.rs vendored Normal file

File diff suppressed because it is too large Load Diff

1913
vendor/time/src/duration.rs vendored Normal file

File diff suppressed because it is too large Load Diff

102
vendor/time/src/error/component_range.rs vendored Normal file
View File

@@ -0,0 +1,102 @@
//! Component range error
use core::fmt;
use crate::error;
/// An error type indicating that a component provided to a method was out of range, causing a
/// failure.
// i64 is the narrowest type fitting all use cases. This eliminates the need for a type parameter.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ComponentRange {
/// Name of the component.
pub(crate) name: &'static str,
/// Whether an input with the same value could have succeeded if the values of other components
/// were different.
pub(crate) is_conditional: bool,
}
impl ComponentRange {
/// Create a new `ComponentRange` error that is not conditional.
#[inline]
pub(crate) const fn unconditional(name: &'static str) -> Self {
Self {
name,
is_conditional: false,
}
}
/// Create a new `ComponentRange` error that is conditional.
#[inline]
pub(crate) const fn conditional(name: &'static str) -> Self {
Self {
name,
is_conditional: true,
}
}
/// Obtain the name of the component whose value was out of range.
#[inline]
pub const fn name(self) -> &'static str {
self.name
}
/// Whether the value's permitted range is conditional, i.e. whether an input with this
/// value could have succeeded if the values of other components were different.
#[inline]
pub const fn is_conditional(self) -> bool {
self.is_conditional
}
}
impl fmt::Display for ComponentRange {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} was not in range", self.name)
}
}
impl From<ComponentRange> for crate::Error {
#[inline]
fn from(original: ComponentRange) -> Self {
Self::ComponentRange(original)
}
}
impl TryFrom<crate::Error> for ComponentRange {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ComponentRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
/// **This trait implementation is deprecated and will be removed in a future breaking release.**
#[cfg(feature = "serde")]
impl serde_core::de::Expected for ComponentRange {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an in-range value")
}
}
#[cfg(feature = "serde")]
impl ComponentRange {
/// Convert the error to a deserialization error.
#[inline]
pub(crate) fn into_de_error<E>(self) -> E
where
E: serde_core::de::Error,
{
serde_core::de::Error::custom(format_args!(
"invalid {}, expected an in-range value",
self.name
))
}
}
impl core::error::Error for ComponentRange {}

View File

@@ -0,0 +1,38 @@
//! Conversion range error
use core::fmt;
use crate::error;
/// An error type indicating that a conversion failed because the target type could not store the
/// initial value.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ConversionRange;
impl fmt::Display for ConversionRange {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Source value is out of range for the target type")
}
}
impl core::error::Error for ConversionRange {}
impl From<ConversionRange> for crate::Error {
#[inline]
fn from(err: ConversionRange) -> Self {
Self::ConversionRange(err)
}
}
impl TryFrom<crate::Error> for ConversionRange {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ConversionRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

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

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

@@ -0,0 +1,124 @@
//! Error formatting a struct
use alloc::boxed::Box;
use core::fmt;
use std::io;
use crate::error;
/// An error occurred when formatting.
#[non_exhaustive]
#[derive(Debug)]
pub enum Format {
/// The type being formatted does not contain sufficient information to format a component.
#[non_exhaustive]
InsufficientTypeInformation,
/// The component named has a value that cannot be formatted into the requested format.
///
/// This variant is only returned when using well-known formats.
InvalidComponent(&'static str),
/// A component provided was out of range.
ComponentRange(Box<error::ComponentRange>),
/// A value of `std::io::Error` was returned internally.
StdIo(io::Error),
}
impl fmt::Display for Format {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsufficientTypeInformation => f.write_str(
"The type being formatted does not contain sufficient information to format a \
component.",
),
Self::InvalidComponent(component) => write!(
f,
"The {component} component cannot be formatted into the requested format."
),
Self::ComponentRange(err) => err.fmt(f),
Self::StdIo(err) => err.fmt(f),
}
}
}
impl From<error::ComponentRange> for Format {
#[inline]
fn from(err: error::ComponentRange) -> Self {
Self::ComponentRange(Box::new(err))
}
}
impl From<io::Error> for Format {
#[inline]
fn from(err: io::Error) -> Self {
Self::StdIo(err)
}
}
impl TryFrom<Format> for error::ComponentRange {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: Format) -> Result<Self, Self::Error> {
match err {
Format::ComponentRange(err) => Ok(*err),
_ => Err(error::DifferentVariant),
}
}
}
impl TryFrom<Format> for io::Error {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: Format) -> Result<Self, Self::Error> {
match err {
Format::StdIo(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl core::error::Error for Format {
#[inline]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::InsufficientTypeInformation | Self::InvalidComponent(_) => None,
Self::ComponentRange(err) => Some(&**err),
Self::StdIo(err) => Some(err),
}
}
}
impl From<Format> for crate::Error {
#[inline]
fn from(original: Format) -> Self {
Self::Format(original)
}
}
impl TryFrom<crate::Error> for Format {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::Format(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
#[cfg(feature = "serde")]
impl Format {
/// Obtain an error type for the serializer.
#[doc(hidden)] // Exposed only for the `declare_format_string` macro
#[inline]
pub fn into_invalid_serde_value<S>(self) -> S::Error
where
S: serde_core::Serializer,
{
use serde_core::ser::Error;
S::Error::custom(self)
}
}

View File

@@ -0,0 +1,37 @@
//! Indeterminate offset
use core::fmt;
use crate::error;
/// The system's UTC offset could not be determined at the given datetime.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IndeterminateOffset;
impl fmt::Display for IndeterminateOffset {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The system's UTC offset could not be determined")
}
}
impl core::error::Error for IndeterminateOffset {}
impl From<IndeterminateOffset> for crate::Error {
#[inline]
fn from(err: IndeterminateOffset) -> Self {
Self::IndeterminateOffset(err)
}
}
impl TryFrom<crate::Error> for IndeterminateOffset {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::IndeterminateOffset(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,134 @@
//! Invalid format description
use alloc::string::String;
use core::fmt;
use crate::error;
/// The format description provided was not valid.
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InvalidFormatDescription {
/// There was a bracket pair that was opened but not closed.
#[non_exhaustive]
UnclosedOpeningBracket {
/// The zero-based index of the opening bracket.
index: usize,
},
/// A component name is not valid.
#[non_exhaustive]
InvalidComponentName {
/// The name of the invalid component name.
name: String,
/// The zero-based index the component name starts at.
index: usize,
},
/// A modifier is not valid.
#[non_exhaustive]
InvalidModifier {
/// The value of the invalid modifier.
value: String,
/// The zero-based index the modifier starts at.
index: usize,
},
/// A component name is missing.
#[non_exhaustive]
MissingComponentName {
/// The zero-based index where the component name should start.
index: usize,
},
/// A required modifier is missing.
#[non_exhaustive]
MissingRequiredModifier {
/// The name of the modifier that is missing.
name: &'static str,
/// The zero-based index of the component.
index: usize,
},
/// Something was expected, but not found.
#[non_exhaustive]
Expected {
/// What was expected to be present, but wasn't.
what: &'static str,
/// The zero-based index the item was expected to be found at.
index: usize,
},
/// Certain behavior is not supported in the given context.
#[non_exhaustive]
NotSupported {
/// The behavior that is not supported.
what: &'static str,
/// The context in which the behavior is not supported.
context: &'static str,
/// The zero-based index the error occurred at.
index: usize,
},
}
impl From<InvalidFormatDescription> for crate::Error {
#[inline]
fn from(original: InvalidFormatDescription) -> Self {
Self::InvalidFormatDescription(original)
}
}
impl TryFrom<crate::Error> for InvalidFormatDescription {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::InvalidFormatDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl fmt::Display for InvalidFormatDescription {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use InvalidFormatDescription::*;
match self {
UnclosedOpeningBracket { index } => {
write!(f, "unclosed opening bracket at byte index {index}")
}
InvalidComponentName { name, index } => {
write!(f, "invalid component name `{name}` at byte index {index}")
}
InvalidModifier { value, index } => {
write!(f, "invalid modifier `{value}` at byte index {index}")
}
MissingComponentName { index } => {
write!(f, "missing component name at byte index {index}")
}
MissingRequiredModifier { name, index } => {
write!(
f,
"missing required modifier `{name}` for component at byte index {index}"
)
}
Expected {
what: expected,
index,
} => {
write!(f, "expected {expected} at byte index {index}")
}
NotSupported {
what,
context,
index,
} => {
if context.is_empty() {
write!(f, "{what} is not supported at byte index {index}")
} else {
write!(
f,
"{what} is not supported in {context} at byte index {index}"
)
}
}
}
}
}
impl core::error::Error for InvalidFormatDescription {}

View File

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

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

@@ -0,0 +1,132 @@
//! Various error types returned by methods in the time crate.
mod component_range;
mod conversion_range;
mod different_variant;
#[cfg(feature = "formatting")]
mod format;
#[cfg(feature = "local-offset")]
mod indeterminate_offset;
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
mod invalid_format_description;
mod invalid_variant;
#[cfg(feature = "parsing")]
mod parse;
#[cfg(feature = "parsing")]
mod parse_from_description;
#[cfg(feature = "parsing")]
mod try_from_parsed;
#[cfg(feature = "parsing")]
use core::convert::Infallible;
use core::fmt;
pub use component_range::ComponentRange;
pub use conversion_range::ConversionRange;
pub use different_variant::DifferentVariant;
#[cfg(feature = "formatting")]
pub use format::Format;
#[cfg(feature = "local-offset")]
pub use indeterminate_offset::IndeterminateOffset;
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
pub use invalid_format_description::InvalidFormatDescription;
pub use invalid_variant::InvalidVariant;
#[cfg(feature = "parsing")]
pub use parse::Parse;
#[cfg(feature = "parsing")]
pub use parse_from_description::ParseFromDescription;
#[cfg(feature = "parsing")]
pub use try_from_parsed::TryFromParsed;
/// A unified error type for anything returned by a method in the time crate.
///
/// This can be used when you either don't know or don't care about the exact error returned.
/// `Result<_, time::Error>` (or its alias `time::Result<_>`) will work in these situations.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
#[expect(missing_docs)]
ConversionRange(ConversionRange),
#[expect(missing_docs)]
ComponentRange(ComponentRange),
#[cfg(feature = "local-offset")]
#[expect(missing_docs)]
IndeterminateOffset(IndeterminateOffset),
#[cfg(feature = "formatting")]
#[expect(missing_docs)]
Format(Format),
#[cfg(feature = "parsing")]
#[expect(missing_docs)]
ParseFromDescription(ParseFromDescription),
#[cfg(feature = "parsing")]
#[expect(missing_docs)]
#[non_exhaustive]
#[deprecated(
since = "0.3.28",
note = "no longer output. moved to the `ParseFromDescription` variant"
)]
UnexpectedTrailingCharacters {
#[doc(hidden)]
never: Infallible,
},
#[cfg(feature = "parsing")]
#[expect(missing_docs)]
TryFromParsed(TryFromParsed),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
#[expect(missing_docs)]
InvalidFormatDescription(InvalidFormatDescription),
#[expect(missing_docs)]
DifferentVariant(DifferentVariant),
#[expect(missing_docs)]
InvalidVariant(InvalidVariant),
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ConversionRange(e) => e.fmt(f),
Self::ComponentRange(e) => e.fmt(f),
#[cfg(feature = "local-offset")]
Self::IndeterminateOffset(e) => e.fmt(f),
#[cfg(feature = "formatting")]
Self::Format(e) => e.fmt(f),
#[cfg(feature = "parsing")]
Self::ParseFromDescription(e) => e.fmt(f),
#[cfg(feature = "parsing")]
#[allow(deprecated)]
Self::UnexpectedTrailingCharacters { never } => match *never {},
#[cfg(feature = "parsing")]
Self::TryFromParsed(e) => e.fmt(f),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
Self::InvalidFormatDescription(e) => e.fmt(f),
Self::DifferentVariant(e) => e.fmt(f),
Self::InvalidVariant(e) => e.fmt(f),
}
}
}
impl core::error::Error for Error {
#[inline]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::ConversionRange(err) => Some(err),
Self::ComponentRange(err) => Some(err),
#[cfg(feature = "local-offset")]
Self::IndeterminateOffset(err) => Some(err),
#[cfg(feature = "formatting")]
Self::Format(err) => Some(err),
#[cfg(feature = "parsing")]
Self::ParseFromDescription(err) => Some(err),
#[cfg(feature = "parsing")]
#[allow(deprecated)]
Self::UnexpectedTrailingCharacters { never } => match *never {},
#[cfg(feature = "parsing")]
Self::TryFromParsed(err) => Some(err),
#[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))]
Self::InvalidFormatDescription(err) => Some(err),
Self::DifferentVariant(err) => Some(err),
Self::InvalidVariant(err) => Some(err),
}
}
}

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

@@ -0,0 +1,116 @@
//! Error that occurred at some stage of parsing
use core::convert::Infallible;
use core::fmt;
use crate::error::{self, ParseFromDescription, TryFromParsed};
/// An error that occurred at some stage of parsing.
#[non_exhaustive]
#[allow(variant_size_differences, reason = "only triggers on some platforms")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Parse {
#[expect(missing_docs)]
TryFromParsed(TryFromParsed),
#[expect(missing_docs)]
ParseFromDescription(ParseFromDescription),
#[expect(missing_docs)]
#[non_exhaustive]
#[deprecated(
since = "0.3.28",
note = "no longer output. moved to the `ParseFromDescription` variant"
)]
UnexpectedTrailingCharacters {
#[doc(hidden)]
never: Infallible,
},
}
impl fmt::Display for Parse {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TryFromParsed(err) => err.fmt(f),
Self::ParseFromDescription(err) => err.fmt(f),
#[allow(deprecated)]
Self::UnexpectedTrailingCharacters { never } => match *never {},
}
}
}
impl core::error::Error for Parse {
#[inline]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::TryFromParsed(err) => Some(err),
Self::ParseFromDescription(err) => Some(err),
#[allow(deprecated)]
Self::UnexpectedTrailingCharacters { never } => match *never {},
}
}
}
impl From<TryFromParsed> for Parse {
#[inline]
fn from(err: TryFromParsed) -> Self {
Self::TryFromParsed(err)
}
}
impl TryFrom<Parse> for TryFromParsed {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl From<ParseFromDescription> for Parse {
#[inline]
fn from(err: ParseFromDescription) -> Self {
Self::ParseFromDescription(err)
}
}
impl TryFrom<Parse> for ParseFromDescription {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: Parse) -> Result<Self, Self::Error> {
match err {
Parse::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl From<Parse> for crate::Error {
#[inline]
fn from(err: Parse) -> Self {
match err {
Parse::TryFromParsed(err) => Self::TryFromParsed(err),
Parse::ParseFromDescription(err) => Self::ParseFromDescription(err),
#[allow(deprecated)]
Parse::UnexpectedTrailingCharacters { never } => match never {},
}
}
}
impl TryFrom<crate::Error> for Parse {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)),
#[allow(deprecated)]
crate::Error::UnexpectedTrailingCharacters { never } => match never {},
crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,55 @@
//! Error parsing an input into a [`Parsed`](crate::parsing::Parsed) struct
use core::fmt;
use crate::error;
/// An error that occurred while parsing the input into a [`Parsed`](crate::parsing::Parsed) struct.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParseFromDescription {
/// A string literal was not what was expected.
#[non_exhaustive]
InvalidLiteral,
/// A dynamic component was not valid.
InvalidComponent(&'static str),
/// The input was expected to have ended, but there are characters that remain.
#[non_exhaustive]
UnexpectedTrailingCharacters,
}
impl fmt::Display for ParseFromDescription {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidLiteral => f.write_str("a character literal was not valid"),
Self::InvalidComponent(name) => {
write!(f, "the '{name}' component could not be parsed")
}
Self::UnexpectedTrailingCharacters => {
f.write_str("unexpected trailing characters; the end of input was expected")
}
}
}
}
impl core::error::Error for ParseFromDescription {}
impl From<ParseFromDescription> for crate::Error {
#[inline]
fn from(original: ParseFromDescription) -> Self {
Self::ParseFromDescription(original)
}
}
impl TryFrom<crate::Error> for ParseFromDescription {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::ParseFromDescription(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

View File

@@ -0,0 +1,77 @@
//! Error converting a [`Parsed`](crate::parsing::Parsed) struct to another type
use core::fmt;
use crate::error;
/// An error that occurred when converting a [`Parsed`](crate::parsing::Parsed) to another type.
#[non_exhaustive]
#[allow(variant_size_differences, reason = "only triggers on some platforms")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryFromParsed {
/// The [`Parsed`](crate::parsing::Parsed) did not include enough information to construct the
/// type.
InsufficientInformation,
/// Some component contained an invalid value for the type.
ComponentRange(error::ComponentRange),
}
impl fmt::Display for TryFromParsed {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsufficientInformation => f.write_str(
"the `Parsed` struct did not include enough information to construct the type",
),
Self::ComponentRange(err) => err.fmt(f),
}
}
}
impl From<error::ComponentRange> for TryFromParsed {
#[inline]
fn from(v: error::ComponentRange) -> Self {
Self::ComponentRange(v)
}
}
impl TryFrom<TryFromParsed> for error::ComponentRange {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: TryFromParsed) -> Result<Self, Self::Error> {
match err {
TryFromParsed::ComponentRange(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}
impl core::error::Error for TryFromParsed {
#[inline]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::InsufficientInformation => None,
Self::ComponentRange(err) => Some(err),
}
}
}
impl From<TryFromParsed> for crate::Error {
#[inline]
fn from(original: TryFromParsed) -> Self {
Self::TryFromParsed(original)
}
}
impl TryFrom<crate::Error> for TryFromParsed {
type Error = error::DifferentVariant;
#[inline]
fn try_from(err: crate::Error) -> Result<Self, Self::Error> {
match err {
crate::Error::TryFromParsed(err) => Ok(err),
_ => Err(error::DifferentVariant),
}
}
}

27
vendor/time/src/ext/digit_count.rs vendored Normal file
View File

@@ -0,0 +1,27 @@
use num_conv::prelude::*;
/// A trait that indicates the formatted width of the value can be determined.
///
/// Note that this should not be implemented for any signed integers. This forces the caller to
/// write the sign if desired.
pub(crate) trait DigitCount {
/// The number of digits in the stringified value.
fn num_digits(self) -> u8;
}
/// A macro to generate implementations of `DigitCount` for unsigned integers.
macro_rules! impl_digit_count {
($($t:ty),* $(,)?) => {
$(impl DigitCount for $t {
#[inline]
fn num_digits(self) -> u8 {
match self.checked_ilog10() {
Some(n) => n.truncate::<u8>() + 1,
None => 1,
}
}
})*
};
}
impl_digit_count!(u8, u16, u32);

105
vendor/time/src/ext/instant.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
use std::time::Instant as StdInstant;
use crate::Duration;
/// Sealed trait to prevent downstream implementations.
mod sealed {
/// A trait that cannot be implemented by downstream users.
pub trait Sealed: Sized {}
impl Sealed for std::time::Instant {}
}
/// An extension trait for [`std::time::Instant`] that adds methods for
/// [`time::Duration`](Duration)s.
pub trait InstantExt: sealed::Sealed {
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking
/// version.
#[inline]
#[track_caller]
fn add_signed(self, duration: Duration) -> Self {
self.checked_add_signed(duration)
.expect("overflow when adding duration to instant")
}
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`InstantExt::checked_sub_signed`] for a non-panicking
/// version.
#[inline]
#[track_caller]
fn sub_signed(self, duration: Duration) -> Self {
self.checked_sub_signed(duration)
.expect("overflow when subtracting duration from instant")
}
/// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be
/// represented as `Instant` (which means it's inside the bounds of the underlying data
/// structure), `None` otherwise.
fn checked_add_signed(&self, duration: Duration) -> Option<Self>;
/// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be
/// represented as `Instant` (which means it's inside the bounds of the underlying data
/// structure), `None` otherwise.
fn checked_sub_signed(&self, duration: Duration) -> Option<Self>;
/// Returns the amount of time elapsed from another instant to this one. This will be negative
/// if `earlier` is later than `self`.
///
/// # Example
///
/// ```rust
/// # use std::thread::sleep;
/// # use std::time::{Duration, Instant};
/// # use time::ext::InstantExt;
/// let now = Instant::now();
/// sleep(Duration::new(1, 0));
/// let new_now = Instant::now();
/// println!("{:?}", new_now.signed_duration_since(now)); // positive
/// println!("{:?}", now.signed_duration_since(new_now)); // negative
/// ```
fn signed_duration_since(&self, earlier: Self) -> Duration;
}
impl InstantExt for StdInstant {
#[inline]
fn checked_add_signed(&self, duration: Duration) -> Option<Self> {
if duration.is_positive() {
self.checked_add(duration.unsigned_abs())
} else if duration.is_negative() {
self.checked_sub(duration.unsigned_abs())
} else {
debug_assert!(duration.is_zero());
Some(*self)
}
}
#[inline]
fn checked_sub_signed(&self, duration: Duration) -> Option<Self> {
if duration.is_positive() {
self.checked_sub(duration.unsigned_abs())
} else if duration.is_negative() {
self.checked_add(duration.unsigned_abs())
} else {
debug_assert!(duration.is_zero());
Some(*self)
}
}
#[inline]
fn signed_duration_since(&self, earlier: Self) -> Duration {
if *self > earlier {
self.saturating_duration_since(earlier)
.try_into()
.unwrap_or(Duration::MAX)
} else {
earlier
.saturating_duration_since(*self)
.try_into()
.map_or(Duration::MIN, |d: Duration| -d)
}
}
}

17
vendor/time/src/ext/mod.rs vendored Normal file
View File

@@ -0,0 +1,17 @@
//! Extension traits.
mod digit_count;
#[cfg(feature = "std")]
mod instant;
mod numerical_duration;
mod numerical_std_duration;
#[cfg(feature = "std")]
mod systemtime;
pub(crate) use self::digit_count::DigitCount;
#[cfg(feature = "std")]
pub use self::instant::InstantExt;
pub use self::numerical_duration::NumericalDuration;
pub use self::numerical_std_duration::NumericalStdDuration;
#[cfg(feature = "std")]
pub use self::systemtime::SystemTimeExt;

View File

@@ -0,0 +1,166 @@
use crate::Duration;
use crate::convert::*;
/// Sealed trait to prevent downstream implementations.
mod sealed {
/// A trait that cannot be implemented by downstream users.
pub trait Sealed {}
impl Sealed for i64 {}
impl Sealed for f64 {}
}
/// Create [`Duration`]s from numeric literals.
///
/// # Examples
///
/// Basic construction of [`Duration`]s.
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!(5.nanoseconds(), Duration::nanoseconds(5));
/// assert_eq!(5.microseconds(), Duration::microseconds(5));
/// assert_eq!(5.milliseconds(), Duration::milliseconds(5));
/// assert_eq!(5.seconds(), Duration::seconds(5));
/// assert_eq!(5.minutes(), Duration::minutes(5));
/// assert_eq!(5.hours(), Duration::hours(5));
/// assert_eq!(5.days(), Duration::days(5));
/// assert_eq!(5.weeks(), Duration::weeks(5));
/// ```
///
/// Signed integers work as well!
///
/// ```rust
/// # use time::{Duration, ext::NumericalDuration};
/// assert_eq!((-5).nanoseconds(), Duration::nanoseconds(-5));
/// assert_eq!((-5).microseconds(), Duration::microseconds(-5));
/// assert_eq!((-5).milliseconds(), Duration::milliseconds(-5));
/// assert_eq!((-5).seconds(), Duration::seconds(-5));
/// assert_eq!((-5).minutes(), Duration::minutes(-5));
/// assert_eq!((-5).hours(), Duration::hours(-5));
/// assert_eq!((-5).days(), Duration::days(-5));
/// assert_eq!((-5).weeks(), Duration::weeks(-5));
/// ```
///
/// Just like any other [`Duration`], they can be added, subtracted, etc.
///
/// ```rust
/// # use time::ext::NumericalDuration;
/// assert_eq!(2.seconds() + 500.milliseconds(), 2_500.milliseconds());
/// assert_eq!(2.seconds() - 500.milliseconds(), 1_500.milliseconds());
/// ```
///
/// When called on floating point values, any remainder of the floating point value will be
/// truncated. Keep in mind that floating point numbers are inherently imprecise and have
/// limited capacity.
#[diagnostic::on_unimplemented(note = "this extension trait is intended to be used with numeric \
literals, such as `5.seconds()`")]
pub trait NumericalDuration: sealed::Sealed {
/// Create a [`Duration`] from the number of nanoseconds.
fn nanoseconds(self) -> Duration;
/// Create a [`Duration`] from the number of microseconds.
fn microseconds(self) -> Duration;
/// Create a [`Duration`] from the number of milliseconds.
fn milliseconds(self) -> Duration;
/// Create a [`Duration`] from the number of seconds.
fn seconds(self) -> Duration;
/// Create a [`Duration`] from the number of minutes.
fn minutes(self) -> Duration;
/// Create a [`Duration`] from the number of hours.
fn hours(self) -> Duration;
/// Create a [`Duration`] from the number of days.
fn days(self) -> Duration;
/// Create a [`Duration`] from the number of weeks.
fn weeks(self) -> Duration;
}
impl NumericalDuration for i64 {
#[inline]
fn nanoseconds(self) -> Duration {
Duration::nanoseconds(self)
}
#[inline]
fn microseconds(self) -> Duration {
Duration::microseconds(self)
}
#[inline]
fn milliseconds(self) -> Duration {
Duration::milliseconds(self)
}
#[inline]
fn seconds(self) -> Duration {
Duration::seconds(self)
}
#[inline]
#[track_caller]
fn minutes(self) -> Duration {
Duration::minutes(self)
}
#[inline]
#[track_caller]
fn hours(self) -> Duration {
Duration::hours(self)
}
#[inline]
#[track_caller]
fn days(self) -> Duration {
Duration::days(self)
}
#[inline]
#[track_caller]
fn weeks(self) -> Duration {
Duration::weeks(self)
}
}
impl NumericalDuration for f64 {
#[inline]
fn nanoseconds(self) -> Duration {
Duration::nanoseconds(self as i64)
}
#[inline]
fn microseconds(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Microsecond)) as i64)
}
#[inline]
fn milliseconds(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Millisecond)) as i64)
}
#[inline]
fn seconds(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Second)) as i64)
}
#[inline]
#[track_caller]
fn minutes(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Minute)) as i64)
}
#[inline]
#[track_caller]
fn hours(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Hour)) as i64)
}
#[inline]
#[track_caller]
fn days(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Day)) as i64)
}
#[inline]
#[track_caller]
fn weeks(self) -> Duration {
Duration::nanoseconds((self * Nanosecond::per_t::<Self>(Week)) as i64)
}
}

View File

@@ -0,0 +1,220 @@
use core::time::Duration as StdDuration;
use crate::convert::*;
/// Sealed trait to prevent downstream implementations.
mod sealed {
/// A trait that cannot be implemented by downstream users.
pub trait Sealed {}
impl Sealed for u64 {}
impl Sealed for f64 {}
}
/// Create [`std::time::Duration`]s from numeric literals.
///
/// # Examples
///
/// Basic construction of [`std::time::Duration`]s.
///
/// ```rust
/// # use time::ext::NumericalStdDuration;
/// # use core::time::Duration;
/// assert_eq!(5.std_nanoseconds(), Duration::from_nanos(5));
/// assert_eq!(5.std_microseconds(), Duration::from_micros(5));
/// assert_eq!(5.std_milliseconds(), Duration::from_millis(5));
/// assert_eq!(5.std_seconds(), Duration::from_secs(5));
/// assert_eq!(5.std_minutes(), Duration::from_secs(5 * 60));
/// assert_eq!(5.std_hours(), Duration::from_secs(5 * 3_600));
/// assert_eq!(5.std_days(), Duration::from_secs(5 * 86_400));
/// assert_eq!(5.std_weeks(), Duration::from_secs(5 * 604_800));
/// ```
///
/// Just like any other [`std::time::Duration`], they can be added, subtracted, etc.
///
/// ```rust
/// # use time::ext::NumericalStdDuration;
/// assert_eq!(
/// 2.std_seconds() + 500.std_milliseconds(),
/// 2_500.std_milliseconds()
/// );
/// assert_eq!(
/// 2.std_seconds() - 500.std_milliseconds(),
/// 1_500.std_milliseconds()
/// );
/// ```
///
/// When called on floating point values, any remainder of the floating point value will be
/// truncated. Keep in mind that floating point numbers are inherently imprecise and have
/// limited capacity.
#[diagnostic::on_unimplemented(note = "this extension trait is intended to be used with numeric \
literals, such as `5.std_seconds()`")]
pub trait NumericalStdDuration: sealed::Sealed {
/// Create a [`std::time::Duration`] from the number of nanoseconds.
fn std_nanoseconds(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of microseconds.
fn std_microseconds(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of milliseconds.
fn std_milliseconds(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of seconds.
fn std_seconds(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of minutes.
fn std_minutes(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of hours.
fn std_hours(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of days.
fn std_days(self) -> StdDuration;
/// Create a [`std::time::Duration`] from the number of weeks.
fn std_weeks(self) -> StdDuration;
}
impl NumericalStdDuration for u64 {
#[inline]
fn std_nanoseconds(self) -> StdDuration {
StdDuration::from_nanos(self)
}
#[inline]
fn std_microseconds(self) -> StdDuration {
StdDuration::from_micros(self)
}
#[inline]
fn std_milliseconds(self) -> StdDuration {
StdDuration::from_millis(self)
}
#[inline]
fn std_seconds(self) -> StdDuration {
StdDuration::from_secs(self)
}
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn std_minutes(self) -> StdDuration {
StdDuration::from_secs(
self.checked_mul(Second::per_t(Minute))
.expect("overflow constructing `time::Duration`"),
)
}
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn std_hours(self) -> StdDuration {
StdDuration::from_secs(
self.checked_mul(Second::per_t(Hour))
.expect("overflow constructing `time::Duration`"),
)
}
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn std_days(self) -> StdDuration {
StdDuration::from_secs(
self.checked_mul(Second::per_t(Day))
.expect("overflow constructing `time::Duration`"),
)
}
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn std_weeks(self) -> StdDuration {
StdDuration::from_secs(
self.checked_mul(Second::per_t(Week))
.expect("overflow constructing `time::Duration`"),
)
}
}
impl NumericalStdDuration for f64 {
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_nanoseconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos(self as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_microseconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Microsecond)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_milliseconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Millisecond)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_seconds(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Second)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_minutes(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Minute)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_hours(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Hour)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_days(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Day)) as u64)
}
/// # Panics
///
/// This will panic if self is negative.
#[inline]
#[track_caller]
fn std_weeks(self) -> StdDuration {
assert!(self >= 0.);
StdDuration::from_nanos((self * Nanosecond::per_t::<Self>(Week)) as u64)
}
}

82
vendor/time/src/ext/systemtime.rs vendored Normal file
View File

@@ -0,0 +1,82 @@
use std::time::SystemTime;
use crate::Duration;
/// Sealed trait to prevent downstream implementations.
mod sealed {
/// A trait that cannot be implemented by downstream users.
pub trait Sealed: Sized {}
impl Sealed for std::time::SystemTime {}
}
/// An extension trait for [`std::time::SystemTime`] that adds methods for
/// [`time::Duration`](Duration)s.
pub trait SystemTimeExt: sealed::Sealed {
/// Adds the given [`Duration`] to the [`SystemTime`], returning `None` is the result cannot be
/// represented by the underlying data structure.
fn checked_add_signed(&self, duration: Duration) -> Option<Self>;
/// Subtracts the given [`Duration`] from the [`SystemTime`], returning `None` is the result
/// cannot be represented by the underlying data structure.
fn checked_sub_signed(&self, duration: Duration) -> Option<Self>;
/// Returns the amount of time elapsed from another [`SystemTime`] to this one. This will be
/// negative if `earlier` is later than `self.`
///
/// If the duration cannot be stored by [`Duration`], the value will be saturated to
/// [`Duration::MIN`] or [`Duration::MAX`] as appropriate.
///
/// # Example
///
/// ```rust
/// # use std::time::SystemTime;
/// # use time::ext::{NumericalDuration, SystemTimeExt};
/// let epoch = SystemTime::UNIX_EPOCH;
/// let other = epoch + 1.seconds();
/// assert_eq!(other.signed_duration_since(epoch), 1.seconds());
/// assert_eq!(epoch.signed_duration_since(other), (-1).seconds());
/// ```
fn signed_duration_since(&self, earlier: Self) -> Duration;
}
impl SystemTimeExt for SystemTime {
#[inline]
fn checked_add_signed(&self, duration: Duration) -> Option<Self> {
if duration.is_positive() {
self.checked_add(duration.unsigned_abs())
} else if duration.is_negative() {
self.checked_sub(duration.unsigned_abs())
} else {
Some(*self)
}
}
#[inline]
fn checked_sub_signed(&self, duration: Duration) -> Option<Self> {
if duration.is_positive() {
self.checked_sub(duration.unsigned_abs())
} else if duration.is_negative() {
self.checked_add(duration.unsigned_abs())
} else {
Some(*self)
}
}
#[inline]
fn signed_duration_since(&self, earlier: Self) -> Duration {
match self.duration_since(earlier) {
Ok(duration) => duration.try_into().unwrap_or(Duration::MAX),
Err(err) => {
let seconds = match i64::try_from(err.duration().as_secs()) {
Ok(seconds) => -seconds,
Err(_) => return Duration::MIN,
};
let nanoseconds = -err.duration().subsec_nanos().cast_signed();
// Safety: `nanoseconds` is guaranteed to be between -999_999_999 and 0
// inclusive.
unsafe { Duration::new_unchecked(seconds, nanoseconds) }
}
}
}
}

View File

@@ -0,0 +1,115 @@
//! 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**: These bytes **should** be UTF-8, but are not required to be. The value is passed
/// through `String::from_utf8_lossy` when necessary.
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<'_> {
#[inline]
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<'_> {
#[inline]
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<BorrowedFormatItem<'_>> for Component {
type Error = error::DifferentVariant;
#[inline]
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> {
#[inline]
fn from(items: &'a [BorrowedFormatItem<'_>]) -> Self {
Self::Compound(items)
}
}
impl<'a> TryFrom<BorrowedFormatItem<'a>> for &[BorrowedFormatItem<'a>] {
type Error = error::DifferentVariant;
#[inline]
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<'_> {
#[inline]
fn eq(&self, rhs: &Component) -> bool {
matches!(self, Self::Component(component) if component == rhs)
}
}
impl PartialEq<BorrowedFormatItem<'_>> for Component {
#[inline]
fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool {
rhs == self
}
}
impl PartialEq<&[Self]> for BorrowedFormatItem<'_> {
#[inline]
fn eq(&self, rhs: &&[Self]) -> bool {
matches!(self, Self::Compound(compound) if compound == rhs)
}
}
impl PartialEq<BorrowedFormatItem<'_>> for &[BorrowedFormatItem<'_>] {
#[inline]
fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool {
rhs == self
}
}

View File

@@ -0,0 +1,53 @@
//! Part of a format description.
use crate::format_description::modifier;
/// Indicate whether the hour is "am" or "pm".
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Period {
#[allow(clippy::missing_docs_in_private_items)]
Am,
#[allow(clippy::missing_docs_in_private_items)]
Pm,
}
/// A component of a larger format description.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Component {
/// Day of the month.
Day(modifier::Day),
/// Month of the year.
Month(modifier::Month),
/// Ordinal day of the year.
Ordinal(modifier::Ordinal),
/// Day of the week.
Weekday(modifier::Weekday),
/// Week within the year.
WeekNumber(modifier::WeekNumber),
/// Year of the date.
Year(modifier::Year),
/// Hour of the day.
Hour(modifier::Hour),
/// Minute within the hour.
Minute(modifier::Minute),
/// AM/PM part of the time.
Period(modifier::Period),
/// Second within the minute.
Second(modifier::Second),
/// Subsecond within the second.
Subsecond(modifier::Subsecond),
/// Hour of the UTC offset.
OffsetHour(modifier::OffsetHour),
/// Minute within the hour of the UTC offset.
OffsetMinute(modifier::OffsetMinute),
/// Second within the minute of the UTC offset.
OffsetSecond(modifier::OffsetSecond),
/// A number of bytes to ignore when parsing. This has no effect on formatting.
Ignore(modifier::Ignore),
/// A Unix timestamp.
UnixTimestamp(modifier::UnixTimestamp),
/// The end of input. Parsing this component will fail if there is any input remaining. This
/// component neither affects formatting nor consumes any input when parsing.
End(modifier::End),
}

View File

@@ -0,0 +1,45 @@
//! Description of how types should be formatted and parsed.
//!
//! The formatted value will be output to the provided writer. Format descriptions can be
//! [well-known](crate::format_description::well_known) or obtained by using the
//! [`format_description!`](crate::macros::format_description) macro or a function listed below.
//!
//! For examples, see the implementors of [Formattable](crate::formatting::Formattable),
//! e.g. [`well_known::Rfc3339`].
mod borrowed_format_item;
mod component;
pub mod modifier;
#[cfg(feature = "alloc")]
mod owned_format_item;
#[cfg(feature = "alloc")]
mod parse;
pub use borrowed_format_item::BorrowedFormatItem;
#[doc(hidden)]
#[deprecated(since = "0.3.37", note = "use `BorrowedFormatItem` for clarity")]
pub use borrowed_format_item::BorrowedFormatItem as FormatItem;
#[cfg(feature = "alloc")]
pub use owned_format_item::OwnedFormatItem;
pub use self::component::Component;
pub(crate) use self::component::Period;
#[cfg(feature = "alloc")]
pub use self::parse::{
parse, parse_borrowed, parse_owned, parse_strftime_borrowed, parse_strftime_owned,
};
/// The type output by the [`format_description!`](crate::macros::format_description) macro.
pub type StaticFormatDescription = &'static [BorrowedFormatItem<'static>];
/// Well-known formats, typically standards.
pub mod well_known {
pub mod iso8601;
mod rfc2822;
mod rfc3339;
#[doc(inline)]
pub use iso8601::Iso8601;
pub use rfc2822::Rfc2822;
pub use rfc3339::Rfc3339;
}

View File

@@ -0,0 +1,727 @@
//! Various modifiers for components.
use core::num::NonZero;
/// Generate the provided code if and only if `pub` is present.
macro_rules! if_pub {
(pub $(#[$attr:meta])*; $($x:tt)*) => {
$(#[$attr])*
///
/// This function exists since [`Default::default()`] cannot be used in a `const` context.
/// It may be removed once that becomes possible. As the [`Default`] trait is in the
/// prelude, removing this function in the future will not cause any resolution failures for
/// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be
/// affected. As such it will not be considered a breaking change.
$($x)*
};
($($_:tt)*) => {};
}
/// Implement `Default` for the given type. This also generates an inherent implementation of a
/// `default` method that is `const fn`, permitting the default value to be used in const contexts.
// Every modifier should use this macro rather than a derived `Default`.
macro_rules! impl_const_default {
($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$(
impl $type {
if_pub! {
$($pub)?
$(#[$doc])*;
#[inline]
pub const fn default() -> Self {
$default
}
}
}
$(#[$doc])*
impl Default for $type {
#[inline]
fn default() -> Self {
$default
}
}
)*};
}
// Keep this first so that it's shown at the top of documentation.
impl_const_default! {
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Day => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the
/// [`Numerical`](Self::Numerical) representation.
MonthRepr => Self::Numerical;
/// Creates an instance of this type that indicates the value uses the
/// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero),
/// and is case-sensitive when parsing.
@pub Month => Self {
padding: Padding::Zero,
repr: MonthRepr::Numerical,
case_sensitive: true,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Ordinal => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation.
WeekdayRepr => Self::Long;
/// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long)
/// representation and is case-sensitive when parsing. If the representation is changed to a
/// numerical one, the instance defaults to one-based indexing.
@pub Weekday => Self {
repr: WeekdayRepr::Long,
one_indexed: true,
case_sensitive: true,
};
/// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation.
WeekNumberRepr => Self::Iso;
/// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero)
/// and uses the [`Iso`](WeekNumberRepr::Iso) representation.
@pub WeekNumber => Self {
padding: Padding::Zero,
repr: WeekNumberRepr::Iso,
};
/// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation.
YearRepr => Self::Full;
/// Creates a modifier that indicates the value uses the [`Extended`](Self::Extended) range.
YearRange => Self::Extended;
/// Creates a modifier that indicates the value uses the [`Full`](YearRepr::Full)
/// representation, is [padded with zeroes](Padding::Zero), uses the Gregorian calendar as its
/// base, and only includes the year's sign if necessary.
@pub Year => Self {
padding: Padding::Zero,
repr: YearRepr::Full,
range: YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero) and
/// has the 24-hour representation.
@pub Hour => Self {
padding: Padding::Zero,
is_12_hour_clock: false,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Minute => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value uses the upper-case representation and is
/// case-sensitive when parsing.
@pub Period => Self {
is_uppercase: true,
case_sensitive: true,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub Second => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the stringified value contains [one or more
/// digits](Self::OneOrMore).
SubsecondDigits => Self::OneOrMore;
/// Creates a modifier that indicates the stringified value contains [one or more
/// digits](SubsecondDigits::OneOrMore).
@pub Subsecond => Self { digits: SubsecondDigits::OneOrMore };
/// Creates a modifier that indicates the value only uses a sign for negative values and is
/// [padded with zeroes](Padding::Zero).
@pub OffsetHour => Self {
sign_is_mandatory: false,
padding: Padding::Zero,
};
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub OffsetMinute => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero).
@pub OffsetSecond => Self { padding: Padding::Zero };
/// Creates a modifier that indicates the value is [padded with zeroes](Self::Zero).
Padding => Self::Zero;
/// Creates a modifier that indicates the value represents the [number of seconds](Self::Second)
/// since the Unix epoch.
UnixTimestampPrecision => Self::Second;
/// Creates a modifier that indicates the value represents the [number of
/// seconds](UnixTimestampPrecision::Second) since the Unix epoch. The sign is not mandatory.
@pub UnixTimestamp => Self {
precision: UnixTimestampPrecision::Second,
sign_is_mandatory: false,
};
/// Indicate that any trailing characters after the end of input are prohibited and will cause
/// an error when used with `parse`.
TrailingInput => Self::Prohibit;
/// Creates a modifier used to represent the end of input, not allowing any trailing input (i.e.
/// the input must be fully consumed).
@pub End => Self { trailing_input: TrailingInput::Prohibit };
}
/// Day of the month.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Day {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl Day {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// The representation of a month.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonthRepr {
/// The number of the month (January is 1, December is 12).
Numerical,
/// The long form of the month name (e.g. "January").
Long,
/// The short form of the month name (e.g. "Jan").
Short,
}
/// Month of the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Month {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What form of representation should be used?
pub repr: MonthRepr,
/// Is the value case sensitive when parsing?
pub case_sensitive: bool,
}
impl Month {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding, ..self }
}
/// Set the manner in which the month is represented.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_repr(self, repr: MonthRepr) -> Self {
Self { repr, ..self }
}
/// Set whether the value is case sensitive when parsing.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_case_sensitive(self, case_sensitive: bool) -> Self {
Self {
case_sensitive,
..self
}
}
}
/// Ordinal day of the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ordinal {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl Ordinal {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// The representation used for the day of the week.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WeekdayRepr {
/// The short form of the weekday (e.g. "Mon").
Short,
/// The long form of the weekday (e.g. "Monday").
Long,
/// A numerical representation using Sunday as the first day of the week.
///
/// Sunday is either 0 or 1, depending on the other modifier's value.
Sunday,
/// A numerical representation using Monday as the first day of the week.
///
/// Monday is either 0 or 1, depending on the other modifier's value.
Monday,
}
/// Day of the week.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Weekday {
/// What form of representation should be used?
pub repr: WeekdayRepr,
/// When using a numerical representation, should it be zero or one-indexed?
pub one_indexed: bool,
/// Is the value case sensitive when parsing?
pub case_sensitive: bool,
}
impl Weekday {
/// Set the manner in which the weekday is represented.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_repr(self, repr: WeekdayRepr) -> Self {
Self { repr, ..self }
}
/// Set whether the value is one-indexed when using a numerical representation.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_one_indexed(self, one_indexed: bool) -> Self {
Self {
one_indexed,
..self
}
}
/// Set whether the value is case sensitive when parsing.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_case_sensitive(self, case_sensitive: bool) -> Self {
Self {
case_sensitive,
..self
}
}
}
/// The representation used for the week number.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WeekNumberRepr {
/// Week 1 is the week that contains January 4.
Iso,
/// Week 1 begins on the first Sunday of the calendar year.
Sunday,
/// Week 1 begins on the first Monday of the calendar year.
Monday,
}
/// Week within the year.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WeekNumber {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What kind of representation should be used?
pub repr: WeekNumberRepr,
}
impl WeekNumber {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding, ..self }
}
/// Set the manner in which the week number is represented.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_repr(self, repr: WeekNumberRepr) -> Self {
Self { repr, ..self }
}
}
/// The representation used for a year value.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum YearRepr {
/// The full value of the year.
Full,
/// All digits except the last two. Includes the sign, if any.
Century,
/// Only the last two digits of the year.
LastTwo,
}
/// The range of years that are supported.
///
/// This modifier has no effect when the year repr is [`LastTwo`](YearRepr::LastTwo).
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum YearRange {
/// Years between -9999 and 9999 are supported.
Standard,
/// Years between -999_999 and 999_999 are supported, with the sign being required if the year
/// contains more than four digits.
///
/// If the `large-dates` feature is not enabled, this variant is equivalent to `Standard`.
Extended,
}
/// Year of the date.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Year {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// What kind of representation should be used?
pub repr: YearRepr,
/// What range of years is supported?
pub range: YearRange,
/// Whether the value is based on the ISO week number or the Gregorian calendar.
pub iso_week_based: bool,
/// Whether the `+` sign is present when a positive year contains fewer than five digits.
pub sign_is_mandatory: bool,
}
impl Year {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding, ..self }
}
/// Set the manner in which the year is represented.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_repr(self, repr: YearRepr) -> Self {
Self { repr, ..self }
}
/// Set the range of years that are supported.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_range(self, range: YearRange) -> Self {
Self { range, ..self }
}
/// Set whether the year is based on the ISO week number.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_iso_week_based(self, iso_week_based: bool) -> Self {
Self {
iso_week_based,
..self
}
}
/// Set whether the `+` sign is mandatory for positive years with fewer than five digits.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_sign_is_mandatory(self, sign_is_mandatory: bool) -> Self {
Self {
sign_is_mandatory,
..self
}
}
}
/// Hour of the day.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Hour {
/// The padding to obtain the minimum width.
pub padding: Padding,
/// Is the hour displayed using a 12 or 24-hour clock?
pub is_12_hour_clock: bool,
}
impl Hour {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding, ..self }
}
/// Set whether the hour uses a 12-hour clock.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_is_12_hour_clock(self, is_12_hour_clock: bool) -> Self {
Self {
is_12_hour_clock,
..self
}
}
}
/// Minute within the hour.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Minute {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl Minute {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// AM/PM part of the time.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Period {
/// Is the period uppercase or lowercase?
pub is_uppercase: bool,
/// Is the value case sensitive when parsing?
///
/// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior.
pub case_sensitive: bool,
}
impl Period {
/// Set whether the period is uppercase.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_is_uppercase(self, is_uppercase: bool) -> Self {
Self {
is_uppercase,
..self
}
}
/// Set whether the value is case sensitive when parsing.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_case_sensitive(self, case_sensitive: bool) -> Self {
Self {
case_sensitive,
..self
}
}
}
/// Second within the minute.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Second {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl Second {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// The number of digits present in a subsecond representation.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SubsecondDigits {
/// Exactly one digit.
One,
/// Exactly two digits.
Two,
/// Exactly three digits.
Three,
/// Exactly four digits.
Four,
/// Exactly five digits.
Five,
/// Exactly six digits.
Six,
/// Exactly seven digits.
Seven,
/// Exactly eight digits.
Eight,
/// Exactly nine digits.
Nine,
/// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits
/// necessary will be used.
OneOrMore,
}
/// Subsecond within the second.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Subsecond {
/// How many digits are present in the component?
pub digits: SubsecondDigits,
}
impl Subsecond {
/// Set the number of digits present in the subsecond representation.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_digits(self, digits: SubsecondDigits) -> Self {
Self { digits }
}
}
/// Hour of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetHour {
/// Whether the `+` sign is present on positive values.
pub sign_is_mandatory: bool,
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl OffsetHour {
/// Set whether the `+` sign is mandatory for positive values.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_sign_is_mandatory(self, sign_is_mandatory: bool) -> Self {
Self {
sign_is_mandatory,
..self
}
}
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding, ..self }
}
}
/// Minute within the hour of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetMinute {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl OffsetMinute {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// Second within the minute of the UTC offset.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OffsetSecond {
/// The padding to obtain the minimum width.
pub padding: Padding,
}
impl OffsetSecond {
/// Set the padding type.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_padding(self, padding: Padding) -> Self {
Self { padding }
}
}
/// Type of padding to ensure a minimum width.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Padding {
/// A space character (` `) should be used as padding.
Space,
/// A zero character (`0`) should be used as padding.
Zero,
/// There is no padding. This can result in a width below the otherwise minimum number of
/// characters.
None,
}
/// Ignore some number of bytes.
///
/// This has no effect when formatting.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ignore {
/// The number of bytes to ignore.
pub count: NonZero<u16>,
}
// Needed as `Default` is deliberately not implemented for `Ignore`. The number of bytes to ignore
// must be explicitly provided.
impl Ignore {
/// Create an instance of `Ignore` with the provided number of bytes to ignore.
#[inline]
pub const fn count(count: NonZero<u16>) -> Self {
Self { count }
}
/// Set the number of bytes to ignore.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_count(self, count: NonZero<u16>) -> Self {
Self { count }
}
}
/// The precision of a Unix timestamp.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UnixTimestampPrecision {
/// Seconds since the Unix epoch.
Second,
/// Milliseconds since the Unix epoch.
Millisecond,
/// Microseconds since the Unix epoch.
Microsecond,
/// Nanoseconds since the Unix epoch.
Nanosecond,
}
/// A Unix timestamp.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnixTimestamp {
/// The precision of the timestamp.
pub precision: UnixTimestampPrecision,
/// Whether the `+` sign must be present for a non-negative timestamp.
pub sign_is_mandatory: bool,
}
impl UnixTimestamp {
/// Set the precision of the timestamp.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_precision(self, precision: UnixTimestampPrecision) -> Self {
Self { precision, ..self }
}
/// Set whether the `+` sign is mandatory for non-negative timestamps.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_sign_is_mandatory(self, sign_is_mandatory: bool) -> Self {
Self {
sign_is_mandatory,
..self
}
}
}
/// Whether trailing input after the declared end is permitted.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrailingInput {
/// Trailing input is not permitted and will cause an error.
Prohibit,
/// Trailing input is permitted but discarded.
Discard,
}
/// The end of input.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct End {
/// How to handle any input after this component.
pub(crate) trailing_input: TrailingInput,
}
impl End {
/// Set how to handle any input after this component.
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub const fn with_trailing_input(self, trailing_input: TrailingInput) -> Self {
Self {
trailing_input,
..self
}
}
}

View File

@@ -0,0 +1,174 @@
//! 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::{BorrowedFormatItem, Component};
/// 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**: These bytes **should** be UTF-8, but are not required to be. The value is passed
/// through `String::from_utf8_lossy` when necessary.
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 {
#[inline]
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<BorrowedFormatItem<'_>> for OwnedFormatItem {
#[inline]
fn from(item: BorrowedFormatItem<'_>) -> Self {
(&item).into()
}
}
impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem {
#[inline]
fn from(item: &BorrowedFormatItem<'_>) -> Self {
match item {
BorrowedFormatItem::Literal(literal) => {
Self::Literal(literal.to_vec().into_boxed_slice())
}
BorrowedFormatItem::Component(component) => Self::Component(*component),
BorrowedFormatItem::Compound(compound) => Self::Compound(
compound
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())),
BorrowedFormatItem::First(items) => Self::First(
items
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
),
}
}
}
impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem {
#[inline]
fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self {
items.as_slice().into()
}
}
impl<'a, T> From<&T> for OwnedFormatItem
where
T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized,
{
#[inline]
fn from(items: &T) -> Self {
Self::Compound(
items
.as_ref()
.iter()
.cloned()
.map(Into::into)
.collect::<Vec<_>>()
.into_boxed_slice(),
)
}
}
impl From<Component> for OwnedFormatItem {
#[inline]
fn from(component: Component) -> Self {
Self::Component(component)
}
}
impl TryFrom<OwnedFormatItem> for Component {
type Error = error::DifferentVariant;
#[inline]
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 {
#[inline]
fn from(items: Vec<Self>) -> Self {
Self::Compound(items.into_boxed_slice())
}
}
impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> {
type Error = error::DifferentVariant;
#[inline]
fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> {
match value {
OwnedFormatItem::Compound(items) => Ok(items.into_vec()),
_ => Err(error::DifferentVariant),
}
}
}
impl PartialEq<Component> for OwnedFormatItem {
#[inline]
fn eq(&self, rhs: &Component) -> bool {
matches!(self, Self::Component(component) if component == rhs)
}
}
impl PartialEq<OwnedFormatItem> for Component {
#[inline]
fn eq(&self, rhs: &OwnedFormatItem) -> bool {
rhs == self
}
}
impl PartialEq<&[Self]> for OwnedFormatItem {
#[inline]
fn eq(&self, rhs: &&[Self]) -> bool {
matches!(self, Self::Compound(compound) if &&**compound == rhs)
}
}
impl PartialEq<OwnedFormatItem> for &[OwnedFormatItem] {
#[inline]
fn eq(&self, rhs: &OwnedFormatItem) -> bool {
rhs == self
}
}

View File

@@ -0,0 +1,386 @@
//! AST for parsing format descriptions.
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::iter;
use super::{Error, Location, Spanned, SpannedValue, Unused, lexer, unused};
use crate::internal_macros::bug;
/// One part of a complete format description.
pub(super) enum Item<'a> {
/// A literal string, formatted and parsed as-is.
///
/// This should never be present inside a nested format description.
Literal(Spanned<&'a [u8]>),
/// A sequence of brackets. The first acts as the escape character.
///
/// This should never be present if the lexer has `BACKSLASH_ESCAPE` set to `true`.
EscapedBracket {
/// The first bracket.
_first: Unused<Location>,
/// The second bracket.
_second: Unused<Location>,
},
/// Part of a type, along with its modifiers.
Component {
/// Where the opening bracket was in the format string.
_opening_bracket: Unused<Location>,
/// Whitespace between the opening bracket and name.
_leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
/// The name of the component.
name: Spanned<&'a [u8]>,
/// The modifiers for the component.
modifiers: Box<[Modifier<'a>]>,
/// Whitespace between the modifiers and closing bracket.
_trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
/// Where the closing bracket was in the format string.
_closing_bracket: Unused<Location>,
},
/// An optional sequence of items.
Optional {
/// Where the opening bracket was in the format string.
opening_bracket: Location,
/// Whitespace between the opening bracket and "optional".
_leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
/// The "optional" keyword.
_optional_kw: Unused<Spanned<&'a [u8]>>,
/// Whitespace between the "optional" keyword and the opening bracket.
_whitespace: Unused<Spanned<&'a [u8]>>,
/// The items within the optional sequence.
nested_format_description: NestedFormatDescription<'a>,
/// Where the closing bracket was in the format string.
closing_bracket: Location,
},
/// The first matching parse of a sequence of items.
First {
/// Where the opening bracket was in the format string.
opening_bracket: Location,
/// Whitespace between the opening bracket and "first".
_leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
/// The "first" keyword.
_first_kw: Unused<Spanned<&'a [u8]>>,
/// Whitespace between the "first" keyword and the opening bracket.
_whitespace: Unused<Spanned<&'a [u8]>>,
/// The sequences of items to try.
nested_format_descriptions: Box<[NestedFormatDescription<'a>]>,
/// Where the closing bracket was in the format string.
closing_bracket: Location,
},
}
/// A format description that is nested within another format description.
pub(super) struct NestedFormatDescription<'a> {
/// Where the opening bracket was in the format string.
pub(super) _opening_bracket: Unused<Location>,
/// The items within the nested format description.
pub(super) items: Box<[Item<'a>]>,
/// Where the closing bracket was in the format string.
pub(super) _closing_bracket: Unused<Location>,
/// Whitespace between the closing bracket and the next item.
pub(super) _trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>,
}
/// A modifier for a component.
pub(super) struct Modifier<'a> {
/// Whitespace preceding the modifier.
pub(super) _leading_whitespace: Unused<Spanned<&'a [u8]>>,
/// The key of the modifier.
pub(super) key: Spanned<&'a [u8]>,
/// Where the colon of the modifier was in the format string.
pub(super) _colon: Unused<Location>,
/// The value of the modifier.
pub(super) value: Spanned<&'a [u8]>,
}
/// Parse the provided tokens into an AST.
#[inline]
pub(super) fn parse<'item, 'iter, I, const VERSION: usize>(
tokens: &'iter mut lexer::Lexed<I>,
) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'item, 'iter, I, VERSION>
where
'item: 'iter,
I: Iterator<Item = Result<lexer::Token<'item>, Error>>,
{
validate_version!(VERSION);
parse_inner::<_, false, VERSION>(tokens)
}
/// Parse the provided tokens into an AST. The const generic indicates whether the resulting
/// [`Item`] will be used directly or as part of a [`NestedFormatDescription`].
#[inline]
fn parse_inner<'item, I, const NESTED: bool, const VERSION: usize>(
tokens: &mut lexer::Lexed<I>,
) -> impl Iterator<Item = Result<Item<'item>, Error>> + use<'_, 'item, I, NESTED, VERSION>
where
I: Iterator<Item = Result<lexer::Token<'item>, Error>>,
{
validate_version!(VERSION);
iter::from_fn(move || {
if NESTED && tokens.peek_closing_bracket().is_some() {
return None;
}
let next = match tokens.next()? {
Ok(token) => token,
Err(err) => return Some(Err(err)),
};
Some(match next {
lexer::Token::Literal(Spanned { value: _, span: _ }) if NESTED => {
bug!("literal should not be present in nested description")
}
lexer::Token::Literal(value) => Ok(Item::Literal(value)),
lexer::Token::Bracket {
kind: lexer::BracketKind::Opening,
location,
} => {
if version!(..=1) {
if let Some(second_location) = tokens.next_if_opening_bracket() {
Ok(Item::EscapedBracket {
_first: unused(location),
_second: unused(second_location),
})
} else {
parse_component::<_, VERSION>(location, tokens)
}
} else {
parse_component::<_, VERSION>(location, tokens)
}
}
lexer::Token::Bracket {
kind: lexer::BracketKind::Closing,
location: _,
} if NESTED => {
bug!("closing bracket should be caught by the `if` statement")
}
lexer::Token::Bracket {
kind: lexer::BracketKind::Closing,
location: _,
} => {
bug!("closing bracket should have been consumed by `parse_component`")
}
lexer::Token::ComponentPart {
kind: _, // whitespace is significant in nested components
value,
} if NESTED => Ok(Item::Literal(value)),
lexer::Token::ComponentPart { kind: _, value: _ } => {
bug!("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, I, const VERSION: usize>(
opening_bracket: Location,
tokens: &mut lexer::Lexed<I>,
) -> Result<Item<'a>, Error>
where
I: Iterator<Item = Result<lexer::Token<'a>, Error>>,
{
validate_version!(VERSION);
let leading_whitespace = tokens.next_if_whitespace();
let Some(name) = tokens.next_if_not_whitespace() else {
let span = match leading_whitespace {
Some(Spanned { value: _, span }) => span,
None => opening_bracket.to_self(),
};
return Err(Error {
_inner: unused(span.error("expected component name")),
public: crate::error::InvalidFormatDescription::MissingComponentName {
index: span.start.byte as usize,
},
});
};
if *name == b"optional" {
let Some(whitespace) = tokens.next_if_whitespace() else {
return Err(Error {
_inner: unused(name.span.error("expected whitespace after `optional`")),
public: crate::error::InvalidFormatDescription::Expected {
what: "whitespace after `optional`",
index: name.span.end.byte as usize,
},
});
};
let nested = parse_nested::<_, VERSION>(whitespace.span.end, tokens)?;
let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
return Err(Error {
_inner: unused(opening_bracket.error("unclosed bracket")),
public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
index: opening_bracket.byte as usize,
},
});
};
return Ok(Item::Optional {
opening_bracket,
_leading_whitespace: unused(leading_whitespace),
_optional_kw: unused(name),
_whitespace: unused(whitespace),
nested_format_description: nested,
closing_bracket,
});
}
if *name == b"first" {
let Some(whitespace) = tokens.next_if_whitespace() else {
return Err(Error {
_inner: unused(name.span.error("expected whitespace after `first`")),
public: crate::error::InvalidFormatDescription::Expected {
what: "whitespace after `first`",
index: name.span.end.byte as usize,
},
});
};
let mut nested_format_descriptions = Vec::new();
while let Ok(description) = parse_nested::<_, VERSION>(whitespace.span.end, tokens) {
nested_format_descriptions.push(description);
}
let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
return Err(Error {
_inner: unused(opening_bracket.error("unclosed bracket")),
public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
index: opening_bracket.byte as usize,
},
});
};
return Ok(Item::First {
opening_bracket,
_leading_whitespace: unused(leading_whitespace),
_first_kw: unused(name),
_whitespace: unused(whitespace),
nested_format_descriptions: nested_format_descriptions.into_boxed_slice(),
closing_bracket,
});
}
let mut modifiers = Vec::new();
let trailing_whitespace = loop {
let Some(whitespace) = tokens.next_if_whitespace() else {
break None;
};
// This is not necessary for proper parsing, but provides a much better error when a nested
// description is used where it's not allowed.
if let Some(location) = tokens.next_if_opening_bracket() {
return Err(Error {
_inner: unused(
location
.to_self()
.error("modifier must be of the form `key:value`"),
),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from("["),
index: location.byte as usize,
},
});
}
let Some(Spanned { value, span }) = tokens.next_if_not_whitespace() else {
break Some(whitespace);
};
let Some(colon_index) = value.iter().position(|&b| b == b':') else {
return Err(Error {
_inner: unused(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 as usize,
},
});
};
let key = &value[..colon_index];
let value = &value[colon_index + 1..];
if key.is_empty() {
return Err(Error {
_inner: unused(span.shrink_to_start().error("expected modifier key")),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::new(),
index: span.start.byte as usize,
},
});
}
if value.is_empty() {
return Err(Error {
_inner: unused(span.shrink_to_end().error("expected modifier value")),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::new(),
index: span.shrink_to_end().start.byte as usize,
},
});
}
modifiers.push(Modifier {
_leading_whitespace: unused(whitespace),
key: key.spanned(span.shrink_to_before(colon_index as u32)),
_colon: unused(span.start.offset(colon_index as u32)),
value: value.spanned(span.shrink_to_after(colon_index as u32)),
});
};
let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
return Err(Error {
_inner: unused(opening_bracket.error("unclosed bracket")),
public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
index: opening_bracket.byte as usize,
},
});
};
Ok(Item::Component {
_opening_bracket: unused(opening_bracket),
_leading_whitespace: unused(leading_whitespace),
name,
modifiers: modifiers.into_boxed_slice(),
_trailing_whitespace: unused(trailing_whitespace),
_closing_bracket: unused(closing_bracket),
})
}
/// Parse a nested format description. The location provided is the most recent one consumed.
#[inline]
fn parse_nested<'a, I, const VERSION: usize>(
last_location: Location,
tokens: &mut lexer::Lexed<I>,
) -> Result<NestedFormatDescription<'a>, Error>
where
I: Iterator<Item = Result<lexer::Token<'a>, Error>>,
{
validate_version!(VERSION);
let Some(opening_bracket) = tokens.next_if_opening_bracket() else {
return Err(Error {
_inner: unused(last_location.error("expected opening bracket")),
public: crate::error::InvalidFormatDescription::Expected {
what: "opening bracket",
index: last_location.byte as usize,
},
});
};
let items = parse_inner::<_, true, VERSION>(tokens).collect::<Result<_, _>>()?;
let Some(closing_bracket) = tokens.next_if_closing_bracket() else {
return Err(Error {
_inner: unused(opening_bracket.error("unclosed bracket")),
public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket {
index: opening_bracket.byte as usize,
},
});
};
let trailing_whitespace = tokens.next_if_whitespace();
Ok(NestedFormatDescription {
_opening_bracket: unused(opening_bracket),
items,
_closing_bracket: unused(closing_bracket),
_trailing_whitespace: unused(trailing_whitespace),
})
}

View File

@@ -0,0 +1,570 @@
//! Typed, validated representation of a parsed format description.
use alloc::boxed::Box;
use alloc::string::String;
use core::num::NonZero;
use core::str::{self, FromStr};
use super::{Error, Span, Spanned, ast, unused};
use crate::internal_macros::bug;
/// Parse an AST iterator into a sequence of format items.
#[inline]
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.
pub(super) enum Item<'a> {
/// A literal string.
Literal(&'a [u8]),
/// Part of a type, along with its modifiers.
Component(Component),
/// A sequence of optional items.
Optional {
/// The items themselves.
value: Box<[Self]>,
/// The span of the full sequence.
span: Span,
},
/// The first matching parse of a sequence of format descriptions.
First {
/// The sequence of format descriptions.
value: Box<[Box<[Self]>]>,
/// The span of the full sequence.
span: Span,
},
}
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(Spanned { value, span: _ }) => Item::Literal(value),
ast::Item::EscapedBracket {
_first: _,
_second: _,
} => Item::Literal(b"["),
ast::Item::Optional {
opening_bracket,
_leading_whitespace: _,
_optional_kw: _,
_whitespace: _,
nested_format_description,
closing_bracket,
} => {
let items = nested_format_description
.items
.into_vec()
.into_iter()
.map(Item::from_ast)
.collect::<Result<_, _>>()?;
Item::Optional {
value: items,
span: opening_bracket.to(closing_bracket),
}
}
ast::Item::First {
opening_bracket,
_leading_whitespace: _,
_first_kw: _,
_whitespace: _,
nested_format_descriptions,
closing_bracket,
} => {
let items = nested_format_descriptions
.into_vec()
.into_iter()
.map(|nested_format_description| {
nested_format_description
.items
.into_vec()
.into_iter()
.map(Item::from_ast)
.collect()
})
.collect::<Result<_, _>>()?;
Item::First {
value: items,
span: opening_bracket.to(closing_bracket),
}
}
})
}
}
impl<'a> TryFrom<Item<'a>> for crate::format_description::BorrowedFormatItem<'a> {
type Error = Error;
#[inline]
fn try_from(item: Item<'a>) -> Result<Self, Self::Error> {
match item {
Item::Literal(literal) => Ok(Self::Literal(literal)),
Item::Component(component) => Ok(Self::Component(component.into())),
Item::Optional { value: _, span } => Err(Error {
_inner: unused(span.error(
"optional items are not supported in runtime-parsed format descriptions",
)),
public: crate::error::InvalidFormatDescription::NotSupported {
what: "optional item",
context: "runtime-parsed format descriptions",
index: span.start.byte as usize,
},
}),
Item::First { value: _, span } => Err(Error {
_inner: unused(span.error(
"'first' items are not supported in runtime-parsed format descriptions",
)),
public: crate::error::InvalidFormatDescription::NotSupported {
what: "'first' item",
context: "runtime-parsed format descriptions",
index: span.start.byte as usize,
},
}),
}
}
}
impl From<Item<'_>> for crate::format_description::OwnedFormatItem {
#[inline]
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()),
Item::Optional { value, span: _ } => Self::Optional(Box::new(value.into())),
Item::First { value, span: _ } => {
Self::First(value.into_vec().into_iter().map(Into::into).collect())
}
}
}
}
impl<'a> From<Box<[Item<'a>]>> for crate::format_description::OwnedFormatItem {
#[inline]
fn from(items: Box<[Item<'a>]>) -> Self {
let items = items.into_vec();
match <[_; 1]>::try_from(items) {
Ok([item]) => item.into(),
Err(vec) => Self::Compound(vec.into_iter().map(Into::into).collect()),
}
}
}
/// Declare the `Component` struct.
macro_rules! component_definition {
(@if_required required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
(@if_required then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
(@if_from_str from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($then)* };
(@if_from_str then { $($then:tt)* } $(else { $($else:tt)* })?) => { $($($else)*)? };
($vis:vis enum $name:ident {
$($variant:ident = $parse_variant:literal {$(
$(#[$required:tt])?
$field:ident = $parse_field:literal:
Option<$(#[$from_str:tt])? $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.
#[inline]
fn with_modifiers(
modifiers: &[ast::Modifier<'_>],
_component_span: Span,
) -> Result<Self, Error>
{
// rustc will complain if the modifier is empty.
#[allow(unused_mut)]
let mut this = Self {
$($field: None),*
};
for modifier in modifiers {
$(#[expect(clippy::string_lit_as_bytes)]
if modifier.key.eq_ignore_ascii_case($parse_field.as_bytes()) {
this.$field = component_definition!(@if_from_str $($from_str)?
then {
parse_from_modifier_value::<$field_type>(&modifier.value)?
} else {
<$field_type>::from_modifier_value(&modifier.value)?
});
continue;
})*
return Err(Error {
_inner: unused(modifier.key.span.error("invalid modifier key")),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(*modifier.key).into_owned(),
index: modifier.key.span.start.byte as usize,
}
});
}
$(component_definition! { @if_required $($required)? then {
if this.$field.is_none() {
return Err(Error {
_inner: unused(_component_span.error("missing required modifier")),
public:
crate::error::InvalidFormatDescription::MissingRequiredModifier {
name: $parse_field,
index: _component_span.start.byte as usize,
}
});
}
}})*
Ok(this)
}
})*
impl From<$name> for crate::format_description::Component {
#[inline]
fn from(component: $name) -> Self {
match component {$(
$name::$variant($variant { $($field),* }) => {
$crate::format_description::component::Component::$variant(
$crate::format_description::modifier::$variant {$(
$target_field: component_definition! { @if_required $($required)?
then {
match $field {
Some(value) => value.into(),
None => bug!("required modifier was not set"),
}
} else {
$field.unwrap_or_default().into()
}
}
),*}
)
}
)*}
}
}
/// Parse a component from the AST, given its name and modifiers.
#[inline]
fn component_from_ast(
name: &Spanned<&[u8]>,
modifiers: &[ast::Modifier<'_>],
) -> Result<Component, Error> {
$(#[expect(clippy::string_lit_as_bytes)]
if name.eq_ignore_ascii_case($parse_variant.as_bytes()) {
return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?));
})*
Err(Error {
_inner: unused(name.span.error("invalid component")),
public: crate::error::InvalidFormatDescription::InvalidComponentName {
name: String::from_utf8_lossy(name).into_owned(),
index: name.span.start.byte as usize,
},
})
}
}
}
// Keep in alphabetical order.
component_definition! {
pub(super) enum Component {
Day = "day" {
padding = "padding": Option<Padding> => padding,
},
End = "end" {
trailing_input = "trailing_input": Option<TrailingInput> => trailing_input,
},
Hour = "hour" {
padding = "padding": Option<Padding> => padding,
base = "repr": Option<HourBase> => is_12_hour_clock,
},
Ignore = "ignore" {
#[required]
count = "count": Option<#[from_str] NonZero<u16>> => count,
},
Minute = "minute" {
padding = "padding": Option<Padding> => padding,
},
Month = "month" {
padding = "padding": Option<Padding> => padding,
repr = "repr": Option<MonthRepr> => repr,
case_sensitive = "case_sensitive": Option<MonthCaseSensitive> => case_sensitive,
},
OffsetHour = "offset_hour" {
sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
padding = "padding": Option<Padding> => padding,
},
OffsetMinute = "offset_minute" {
padding = "padding": Option<Padding> => padding,
},
OffsetSecond = "offset_second" {
padding = "padding": Option<Padding> => padding,
},
Ordinal = "ordinal" {
padding = "padding": Option<Padding> => padding,
},
Period = "period" {
case = "case": Option<PeriodCase> => is_uppercase,
case_sensitive = "case_sensitive": Option<PeriodCaseSensitive> => case_sensitive,
},
Second = "second" {
padding = "padding": Option<Padding> => padding,
},
Subsecond = "subsecond" {
digits = "digits": Option<SubsecondDigits> => digits,
},
UnixTimestamp = "unix_timestamp" {
precision = "precision": Option<UnixTimestampPrecision> => precision,
sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory,
},
Weekday = "weekday" {
repr = "repr": Option<WeekdayRepr> => repr,
one_indexed = "one_indexed": Option<WeekdayOneIndexed> => one_indexed,
case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive,
},
WeekNumber = "week_number" {
padding = "padding": Option<Padding> => padding,
repr = "repr": Option<WeekNumberRepr> => repr,
},
Year = "year" {
padding = "padding": Option<Padding> => padding,
repr = "repr": Option<YearRepr> => repr,
range = "range": Option<YearRange> => range,
base = "base": Option<YearBase> => iso_week_based,
sign_behavior = "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
};
}
/// 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
),* $(,)?
}
)+) => {$(
#[derive(Default)]
enum $name {
$($(#[$attr])? $variant),*
}
impl $name {
/// Parse the modifier from its string representation.
#[inline]
fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Option<Self>, Error> {
$(if value.eq_ignore_ascii_case($parse_variant) {
return Ok(Some(Self::$variant));
})*
Err(Error {
_inner: unused(value.span.error("invalid modifier value")),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(value).into_owned(),
index: value.span.start.byte as usize,
},
})
}
}
impl From<$name> for target_ty!($name $($target_ty)?) {
#[inline]
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 TrailingInput {
#[default]
Prohibit = b"prohibit",
Discard = b"discard",
}
enum UnixTimestampPrecision {
#[default]
Second = b"second",
Millisecond = b"millisecond",
Microsecond = b"microsecond",
Nanosecond = b"nanosecond",
}
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",
Century = b"century",
LastTwo = b"last_two",
}
enum YearRange {
Standard = b"standard",
#[default]
Extended = b"extended",
}
}
/// Parse a modifier value using `FromStr`. Requires the modifier value to be valid UTF-8.
#[inline]
fn parse_from_modifier_value<T>(value: &Spanned<&[u8]>) -> Result<Option<T>, Error>
where
T: FromStr,
{
str::from_utf8(value)
.ok()
.and_then(|val| val.parse::<T>().ok())
.map(|val| Some(val))
.ok_or_else(|| Error {
_inner: unused(value.span.error("invalid modifier value")),
public: crate::error::InvalidFormatDescription::InvalidModifier {
value: String::from_utf8_lossy(value).into_owned(),
index: value.span.start.byte as usize,
},
})
}

View File

@@ -0,0 +1,301 @@
//! Lexer for parsing format descriptions.
use core::iter;
use super::{Error, Location, Spanned, SpannedValue, attach_location, unused};
/// An iterator over the lexed tokens.
pub(super) struct Lexed<I>
where
I: Iterator,
{
/// The internal iterator.
iter: iter::Peekable<I>,
}
impl<I> Iterator for Lexed<I>
where
I: Iterator,
{
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<'iter, 'token, I> Lexed<I>
where
'token: 'iter,
I: Iterator<Item = Result<Token<'token>, Error>> + 'iter,
{
/// Peek at the next item in the iterator.
#[inline]
pub(super) fn peek(&mut self) -> Option<&I::Item> {
self.iter.peek()
}
/// Consume the next token if it is whitespace.
#[inline]
pub(super) fn next_if_whitespace(&mut self) -> Option<Spanned<&'token [u8]>> {
if let Some(&Ok(Token::ComponentPart {
kind: ComponentKind::Whitespace,
value,
})) = self.peek()
{
self.next(); // consume
Some(value)
} else {
None
}
}
/// Consume the next token if it is a component item that is not whitespace.
#[inline]
pub(super) fn next_if_not_whitespace(&mut self) -> Option<Spanned<&'token [u8]>> {
if let Some(&Ok(Token::ComponentPart {
kind: ComponentKind::NotWhitespace,
value,
})) = self.peek()
{
self.next(); // consume
Some(value)
} else {
None
}
}
/// Consume the next token if it is an opening bracket.
#[inline]
pub(super) fn next_if_opening_bracket(&mut self) -> Option<Location> {
if let Some(&Ok(Token::Bracket {
kind: BracketKind::Opening,
location,
})) = self.peek()
{
self.next(); // consume
Some(location)
} else {
None
}
}
/// Peek at the next token if it is a closing bracket.
#[inline]
pub(super) fn peek_closing_bracket(&'iter mut self) -> Option<&'iter Location> {
if let Some(Ok(Token::Bracket {
kind: BracketKind::Closing,
location,
})) = self.peek()
{
Some(location)
} else {
None
}
}
/// Consume the next token if it is a closing bracket.
#[inline]
pub(super) fn next_if_closing_bracket(&mut self) -> Option<Location> {
if let Some(&Ok(Token::Bracket {
kind: BracketKind::Closing,
location,
})) = self.peek()
{
self.next(); // consume
Some(location)
} else {
None
}
}
}
/// 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(Spanned<&'a [u8]>),
/// 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: Spanned<&'a [u8]>,
},
}
/// 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 {
Whitespace,
NotWhitespace,
}
/// Parse the string into a series of [`Token`]s.
///
/// `VERSION` controls the version of the format description that is being parsed. Currently, this
/// must be 1 or 2.
///
/// - When `VERSION` is 1, `[[` is the only escape sequence, resulting in a literal `[`.
/// - When `VERSION` is 2, all escape sequences begin with `\`. The only characters that may
/// currently follow are `\`, `[`, and `]`, all of which result in the literal character. All
/// other characters result in a lex error.
#[inline]
pub(super) fn lex<const VERSION: usize>(
mut input: &[u8],
) -> Lexed<impl Iterator<Item = Result<Token<'_>, Error>>> {
validate_version!(VERSION);
let mut depth: u32 = 0;
let mut iter = attach_location(input.iter()).peekable();
let mut second_bracket_location = None;
let iter = iter::from_fn(move || {
// The flag is only set when version is zero.
if version!(..=1) {
// 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(Ok(Token::Bracket {
kind: BracketKind::Opening,
location,
}));
}
}
Some(Ok(match iter.next()? {
// possible escape sequence
(b'\\', backslash_loc) if version!(2..) => {
match iter.next() {
Some((b'\\' | b'[' | b']', char_loc)) => {
// The escaped character is emitted as-is.
let char = &input[1..2];
input = &input[2..];
if depth == 0 {
Token::Literal(char.spanned(backslash_loc.to(char_loc)))
} else {
Token::ComponentPart {
kind: ComponentKind::NotWhitespace,
value: char.spanned(backslash_loc.to(char_loc)),
}
}
}
Some((_, loc)) => {
return Some(Err(Error {
_inner: unused(loc.error("invalid escape sequence")),
public: crate::error::InvalidFormatDescription::Expected {
what: "valid escape sequence",
index: loc.byte as usize,
},
}));
}
None => {
return Some(Err(Error {
_inner: unused(backslash_loc.error("unexpected end of input")),
public: crate::error::InvalidFormatDescription::Expected {
what: "valid escape sequence",
index: backslash_loc.byte as usize,
},
}));
}
}
}
// potentially escaped opening bracket
(b'[', location) if version!(..=1) => {
if let Some((_, second_location)) = iter.next_if(|&(&byte, _)| byte == b'[') {
// Escaped bracket. Store the location of the second so we can emit it later.
second_bracket_location = Some(second_location);
input = &input[2..];
} else {
// opening bracket
depth += 1;
input = &input[1..];
}
Token::Bracket {
kind: BracketKind::Opening,
location,
}
}
// opening bracket
(b'[', location) => {
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, _)| !((version!(2..) && byte == b'\\') || byte == b'['))
{
end_location = location;
bytes += 1;
}
let value = &input[..bytes];
input = &input[bytes..];
Token::Literal(value.spanned(start_location.to(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, _)| {
!matches!(byte, b'\\' | b'[' | 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: value.spanned(start_location.to(end_location)),
}
}
}))
});
Lexed {
iter: iter.peekable(),
}
}

View File

@@ -0,0 +1,272 @@
//! Parser for format descriptions.
use alloc::boxed::Box;
use alloc::vec::Vec;
pub use self::strftime::{parse_strftime_borrowed, parse_strftime_owned};
use crate::{error, format_description};
/// A helper macro to make version restrictions simpler to read and write.
macro_rules! version {
($range:expr) => {
$range.contains(&VERSION)
};
}
/// A helper macro to statically validate the version (when used as a const parameter).
macro_rules! validate_version {
($version:ident) => {
const {
assert!($version >= 1 && $version <= 2);
}
};
}
mod ast;
mod format_item;
mod lexer;
mod strftime;
/// 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).
///
/// This function exists for backward compatibility reasons. It is equivalent to calling
/// `parse_borrowed::<1>(s)`. In the future, this function will be deprecated in favor of
/// `parse_borrowed`.
#[inline]
pub fn parse(
s: &str,
) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
parse_borrowed::<1>(s)
}
/// 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). The version of the format
/// description is provided as the const parameter. **It is recommended to use version 2.**
#[inline]
pub fn parse_borrowed<const VERSION: usize>(
s: &str,
) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
Ok(format_items
.map(|res| res.and_then(TryInto::try_into))
.collect::<Result<_, _>>()?)
}
/// 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). The version of the format
/// description is provided as the const parameter.
///
/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means
/// that there is no lifetime that needs to be handled. **It is recommended to use version 2.**
///
/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem
#[inline]
pub fn parse_owned<const VERSION: usize>(
s: &str,
) -> Result<format_description::OwnedFormatItem, error::InvalidFormatDescription> {
validate_version!(VERSION);
let mut lexed = lexer::lex::<VERSION>(s.as_bytes());
let ast = ast::parse::<_, VERSION>(&mut lexed);
let format_items = format_item::parse(ast);
let items = format_items.collect::<Result<Box<_>, _>>()?;
Ok(items.into())
}
/// Attach [`Location`] information to each byte in the iterator.
#[inline]
fn attach_location<'item>(
iter: impl Iterator<Item = &'item u8>,
) -> impl Iterator<Item = (&'item u8, Location)> {
let mut byte_pos = 0;
iter.map(move |byte| {
let location = Location { byte: byte_pos };
byte_pos += 1;
(byte, location)
})
}
/// A location within a string.
#[derive(Clone, Copy)]
struct Location {
/// The zero-indexed byte of the string.
byte: u32,
}
impl Location {
/// Create a new [`Span`] from `self` to `other`.
#[inline]
const fn to(self, end: Self) -> Span {
Span { start: self, end }
}
/// Create a new [`Span`] consisting entirely of `self`.
#[inline]
const fn to_self(self) -> Span {
Span {
start: self,
end: self,
}
}
/// 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"]
#[inline]
const fn offset(&self, offset: u32) -> Self {
Self {
byte: self.byte + offset,
}
}
/// Create an error with the provided message at this location.
#[inline]
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 {
start: Location,
end: Location,
}
impl Span {
/// Obtain a `Span` pointing at the start of the pre-existing span.
#[must_use = "this does not modify the original value"]
#[inline]
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,
}
}
/// Obtain a `Span` that ends before the provided position of the pre-existing span.
#[must_use = "this does not modify the original value"]
#[inline]
const fn shrink_to_before(&self, pos: u32) -> Self {
Self {
start: self.start,
end: Location {
byte: self.start.byte + pos - 1,
},
}
}
/// Obtain a `Span` that starts after provided position to the end of the pre-existing span.
#[must_use = "this does not modify the original value"]
#[inline]
const fn shrink_to_after(&self, pos: u32) -> Self {
Self {
start: Location {
byte: self.start.byte + pos + 1,
},
end: self.end,
}
}
/// Create an error with the provided message at this span.
#[inline]
const fn error(self, message: &'static str) -> ErrorInner {
ErrorInner {
_message: message,
_span: self,
}
}
}
/// A value with an associated [`Span`].
#[derive(Clone, Copy)]
struct Spanned<T> {
/// The value.
value: T,
/// Where the value was in the format string.
span: Span,
}
impl<T> core::ops::Deref for Spanned<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.value
}
}
/// Helper trait to attach a [`Span`] to a value.
trait SpannedValue: Sized {
/// Attach a [`Span`] to a value.
fn spanned(self, span: Span) -> Spanned<Self>;
}
impl<T> SpannedValue for T {
#[inline]
fn spanned(self, span: Span) -> Spanned<Self> {
Spanned { value: self, span }
}
}
/// 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: Unused<ErrorInner>,
/// The error needed for interoperability with the rest of `time`.
public: error::InvalidFormatDescription,
}
impl From<Error> for error::InvalidFormatDescription {
#[inline]
fn from(error: Error) -> Self {
error.public
}
}
/// A value that may be used in the future, but currently is not.
///
/// This struct exists so that data can semantically be passed around without _actually_ passing it
/// around. This way the data still exists if it is needed in the future.
// `PhantomData` is not used directly because we don't want to introduce any trait implementations.
struct Unused<T>(core::marker::PhantomData<T>);
/// Indicate that a value is currently unused.
#[inline]
fn unused<T>(_: T) -> Unused<T> {
Unused(core::marker::PhantomData)
}

View File

@@ -0,0 +1,495 @@
use alloc::string::String;
use alloc::vec::Vec;
use core::iter;
use crate::error::InvalidFormatDescription;
use crate::format_description::parse::{
Error, ErrorInner, Location, Spanned, SpannedValue, Unused, attach_location, unused,
};
use crate::format_description::{self, BorrowedFormatItem, Component, modifier};
/// Parse a sequence of items from the [`strftime` format description][strftime docs].
///
/// The only heap allocation required is for the `Vec` itself. All components are bound to the
/// lifetime of the input.
///
/// [strftime docs]: https://man7.org/linux/man-pages/man3/strftime.3.html
#[doc(alias = "parse_strptime_borrowed")]
#[inline]
pub fn parse_strftime_borrowed(
s: &str,
) -> Result<Vec<BorrowedFormatItem<'_>>, InvalidFormatDescription> {
let tokens = lex(s.as_bytes());
let items = into_items(tokens).collect::<Result<_, _>>()?;
Ok(items)
}
/// Parse a sequence of items from the [`strftime` format description][strftime docs].
///
/// This requires heap allocation for some owned items.
///
/// [strftime docs]: https://man7.org/linux/man-pages/man3/strftime.3.html
#[doc(alias = "parse_strptime_owned")]
#[inline]
pub fn parse_strftime_owned(
s: &str,
) -> Result<format_description::OwnedFormatItem, InvalidFormatDescription> {
parse_strftime_borrowed(s).map(Into::into)
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Padding {
/// The default padding for a numeric component. Indicated by no character.
Default,
/// Pad a numeric component with spaces. Indicated by an underscore.
Spaces,
/// Do not pad a numeric component. Indicated by a hyphen.
None,
/// Pad a numeric component with zeroes. Indicated by a zero.
Zeroes,
}
enum Token<'a> {
Literal(Spanned<&'a [u8]>),
Component {
_percent: Unused<Location>,
padding: Spanned<Padding>,
component: Spanned<u8>,
},
}
#[inline]
fn lex(mut input: &[u8]) -> iter::Peekable<impl Iterator<Item = Result<Token<'_>, Error>>> {
let mut iter = attach_location(input.iter()).peekable();
iter::from_fn(move || {
Some(Ok(match iter.next()? {
(b'%', percent_loc) => match iter.next() {
Some((padding @ (b'_' | b'-' | b'0'), padding_loc)) => {
let padding = match padding {
b'_' => Padding::Spaces,
b'-' => Padding::None,
b'0' => Padding::Zeroes,
_ => unreachable!(),
};
let (&component, component_loc) = iter.next()?;
input = &input[3..];
Token::Component {
_percent: unused(percent_loc),
padding: padding.spanned(padding_loc.to_self()),
component: component.spanned(component_loc.to_self()),
}
}
Some((&component, component_loc)) => {
input = &input[2..];
let span = component_loc.to_self();
Token::Component {
_percent: unused(percent_loc),
padding: Padding::Default.spanned(span),
component: component.spanned(span),
}
}
None => {
return Some(Err(Error {
_inner: unused(percent_loc.error("unexpected end of input")),
public: InvalidFormatDescription::Expected {
what: "valid escape sequence",
index: percent_loc.byte as usize,
},
}));
}
},
(_, start_location) => {
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.spanned(start_location.to(end_location)))
}
}))
})
.peekable()
}
#[inline]
fn into_items<'iter, 'token, I>(
mut tokens: iter::Peekable<I>,
) -> impl Iterator<Item = Result<BorrowedFormatItem<'token>, Error>> + use<'token, I>
where
'token: 'iter,
I: Iterator<Item = Result<Token<'token>, Error>> + 'iter,
{
iter::from_fn(move || {
let next = match tokens.next()? {
Ok(token) => token,
Err(err) => return Some(Err(err)),
};
Some(match next {
Token::Literal(spanned) => Ok(BorrowedFormatItem::Literal(*spanned)),
Token::Component {
_percent,
padding,
component,
} => parse_component(padding, component),
})
})
}
fn parse_component(
padding: Spanned<Padding>,
component: Spanned<u8>,
) -> Result<BorrowedFormatItem<'static>, Error> {
let padding_or_default = |padding: Padding, default| match padding {
Padding::Default => default,
Padding::Spaces => modifier::Padding::Space,
Padding::None => modifier::Padding::None,
Padding::Zeroes => modifier::Padding::Zero,
};
/// Helper macro to create a component.
macro_rules! component {
($name:ident { $($inner:tt)* }) => {
BorrowedFormatItem::Component(Component::$name(modifier::$name {
$($inner)*
}))
}
}
Ok(match *component {
b'%' => BorrowedFormatItem::Literal(b"%"),
b'a' => component!(Weekday {
repr: modifier::WeekdayRepr::Short,
one_indexed: true,
case_sensitive: true,
}),
b'A' => component!(Weekday {
repr: modifier::WeekdayRepr::Long,
one_indexed: true,
case_sensitive: true,
}),
b'b' | b'h' => component!(Month {
repr: modifier::MonthRepr::Short,
padding: modifier::Padding::Zero,
case_sensitive: true,
}),
b'B' => component!(Month {
repr: modifier::MonthRepr::Long,
padding: modifier::Padding::Zero,
case_sensitive: true,
}),
b'c' => BorrowedFormatItem::Compound(&[
component!(Weekday {
repr: modifier::WeekdayRepr::Short,
one_indexed: true,
case_sensitive: true,
}),
BorrowedFormatItem::Literal(b" "),
component!(Month {
repr: modifier::MonthRepr::Short,
padding: modifier::Padding::Zero,
case_sensitive: true,
}),
BorrowedFormatItem::Literal(b" "),
component!(Day {
padding: modifier::Padding::Space
}),
BorrowedFormatItem::Literal(b" "),
component!(Hour {
padding: modifier::Padding::Zero,
is_12_hour_clock: false,
}),
BorrowedFormatItem::Literal(b":"),
component!(Minute {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b":"),
component!(Second {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b" "),
component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
]),
b'C' => component!(Year {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::YearRepr::Century,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
b'd' => component!(Day {
padding: padding_or_default(*padding, modifier::Padding::Zero),
}),
b'D' => BorrowedFormatItem::Compound(&[
component!(Month {
repr: modifier::MonthRepr::Numerical,
padding: modifier::Padding::Zero,
case_sensitive: true,
}),
BorrowedFormatItem::Literal(b"/"),
component!(Day {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b"/"),
component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::LastTwo,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
]),
b'e' => component!(Day {
padding: padding_or_default(*padding, modifier::Padding::Space),
}),
b'F' => BorrowedFormatItem::Compound(&[
component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
BorrowedFormatItem::Literal(b"-"),
component!(Month {
padding: modifier::Padding::Zero,
repr: modifier::MonthRepr::Numerical,
case_sensitive: true,
}),
BorrowedFormatItem::Literal(b"-"),
component!(Day {
padding: modifier::Padding::Zero,
}),
]),
b'g' => component!(Year {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::YearRepr::LastTwo,
range: modifier::YearRange::Extended,
iso_week_based: true,
sign_is_mandatory: false,
}),
b'G' => component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
range: modifier::YearRange::Extended,
iso_week_based: true,
sign_is_mandatory: false,
}),
b'H' => component!(Hour {
padding: padding_or_default(*padding, modifier::Padding::Zero),
is_12_hour_clock: false,
}),
b'I' => component!(Hour {
padding: padding_or_default(*padding, modifier::Padding::Zero),
is_12_hour_clock: true,
}),
b'j' => component!(Ordinal {
padding: padding_or_default(*padding, modifier::Padding::Zero),
}),
b'k' => component!(Hour {
padding: padding_or_default(*padding, modifier::Padding::Space),
is_12_hour_clock: false,
}),
b'l' => component!(Hour {
padding: padding_or_default(*padding, modifier::Padding::Space),
is_12_hour_clock: true,
}),
b'm' => component!(Month {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::MonthRepr::Numerical,
case_sensitive: true,
}),
b'M' => component!(Minute {
padding: padding_or_default(*padding, modifier::Padding::Zero),
}),
b'n' => BorrowedFormatItem::Literal(b"\n"),
b'O' => {
return Err(Error {
_inner: unused(ErrorInner {
_message: "unsupported modifier",
_span: component.span,
}),
public: InvalidFormatDescription::NotSupported {
what: "modifier",
context: "",
index: component.span.start.byte as usize,
},
});
}
b'p' => component!(Period {
is_uppercase: true,
case_sensitive: true
}),
b'P' => component!(Period {
is_uppercase: false,
case_sensitive: true
}),
b'r' => BorrowedFormatItem::Compound(&[
component!(Hour {
padding: modifier::Padding::Zero,
is_12_hour_clock: true,
}),
BorrowedFormatItem::Literal(b":"),
component!(Minute {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b":"),
component!(Second {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b" "),
component!(Period {
is_uppercase: true,
case_sensitive: true,
}),
]),
b'R' => BorrowedFormatItem::Compound(&[
component!(Hour {
padding: modifier::Padding::Zero,
is_12_hour_clock: false,
}),
BorrowedFormatItem::Literal(b":"),
component!(Minute {
padding: modifier::Padding::Zero,
}),
]),
b's' => component!(UnixTimestamp {
precision: modifier::UnixTimestampPrecision::Second,
sign_is_mandatory: false,
}),
b'S' => component!(Second {
padding: padding_or_default(*padding, modifier::Padding::Zero),
}),
b't' => BorrowedFormatItem::Literal(b"\t"),
b'T' => BorrowedFormatItem::Compound(&[
component!(Hour {
padding: modifier::Padding::Zero,
is_12_hour_clock: false,
}),
BorrowedFormatItem::Literal(b":"),
component!(Minute {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b":"),
component!(Second {
padding: modifier::Padding::Zero,
}),
]),
b'u' => component!(Weekday {
repr: modifier::WeekdayRepr::Monday,
one_indexed: true,
case_sensitive: true,
}),
b'U' => component!(WeekNumber {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::WeekNumberRepr::Sunday,
}),
b'V' => component!(WeekNumber {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::WeekNumberRepr::Iso,
}),
b'w' => component!(Weekday {
repr: modifier::WeekdayRepr::Sunday,
one_indexed: true,
case_sensitive: true,
}),
b'W' => component!(WeekNumber {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::WeekNumberRepr::Monday,
}),
b'x' => BorrowedFormatItem::Compound(&[
component!(Month {
repr: modifier::MonthRepr::Numerical,
padding: modifier::Padding::Zero,
case_sensitive: true,
}),
BorrowedFormatItem::Literal(b"/"),
component!(Day {
padding: modifier::Padding::Zero
}),
BorrowedFormatItem::Literal(b"/"),
component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::LastTwo,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
]),
b'X' => BorrowedFormatItem::Compound(&[
component!(Hour {
padding: modifier::Padding::Zero,
is_12_hour_clock: false,
}),
BorrowedFormatItem::Literal(b":"),
component!(Minute {
padding: modifier::Padding::Zero,
}),
BorrowedFormatItem::Literal(b":"),
component!(Second {
padding: modifier::Padding::Zero,
}),
]),
b'y' => component!(Year {
padding: padding_or_default(*padding, modifier::Padding::Zero),
repr: modifier::YearRepr::LastTwo,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
b'Y' => component!(Year {
padding: modifier::Padding::Zero,
repr: modifier::YearRepr::Full,
range: modifier::YearRange::Extended,
iso_week_based: false,
sign_is_mandatory: false,
}),
b'z' => BorrowedFormatItem::Compound(&[
component!(OffsetHour {
sign_is_mandatory: true,
padding: modifier::Padding::Zero,
}),
component!(OffsetMinute {
padding: modifier::Padding::Zero,
}),
]),
b'Z' => {
return Err(Error {
_inner: unused(ErrorInner {
_message: "unsupported component",
_span: component.span,
}),
public: InvalidFormatDescription::NotSupported {
what: "component",
context: "",
index: component.span.start.byte as usize,
},
});
}
_ => {
return Err(Error {
_inner: unused(ErrorInner {
_message: "invalid component",
_span: component.span,
}),
public: InvalidFormatDescription::InvalidComponentName {
name: String::from_utf8_lossy(&[*component]).into_owned(),
index: component.span.start.byte as usize,
},
});
}
})
}

View File

@@ -0,0 +1,264 @@
//! The format described in ISO 8601.
mod adt_hack;
use core::num::NonZero;
#[doc(hidden, no_inline)]
pub use self::adt_hack::DoNotRelyOnWhatThisIs;
pub use self::adt_hack::EncodedConfig;
/// 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 = { Config::DEFAULT.encode() }>;
impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Iso8601")
.field("config", &Config::decode(CONFIG))
.finish()
}
}
/// Define associated constants for `Iso8601`.
macro_rules! define_assoc_consts {
($($(#[$doc:meta])* $vis:vis const $const_name:ident = $format:expr;)*) => {$(
const $const_name: EncodedConfig = $format.encode();
impl Iso8601<$const_name> {
$(#[$doc])*
$vis const $const_name: Self = Self;
}
)*};
}
define_assoc_consts! {
/// 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 another associated constant. For full customization, use
/// [`Config::DEFAULT`] and [`Config`]'s methods to create a custom configuration.
pub const DEFAULT = Config::DEFAULT;
/// An [`Iso8601`] that can only be used for parsing. Using this to format a value is
/// unspecified behavior.
pub const PARSING = Config::PARSING;
/// An [`Iso8601`] that handles only the date, but is otherwise the same as [`Config::DEFAULT`].
pub const DATE = Config::DEFAULT.set_formatted_components(FormattedComponents::Date);
/// An [`Iso8601`] that handles only the time, but is otherwise the same as [`Config::DEFAULT`].
pub const TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::Time);
/// An [`Iso8601`] that handles only the UTC offset, but is otherwise the same as
/// [`Config::DEFAULT`].
pub const OFFSET = Config::DEFAULT.set_formatted_components(FormattedComponents::Offset);
/// An [`Iso8601`] that handles the date and time, but is otherwise the same as
/// [`Config::DEFAULT`].
pub const DATE_TIME = Config::DEFAULT.set_formatted_components(FormattedComponents::DateTime);
/// An [`Iso8601`] that handles the date, time, and UTC offset. This is the same as
/// [`Config::DEFAULT`].
pub const DATE_TIME_OFFSET = Config::DEFAULT;
/// An [`Iso8601`] that handles the time and UTC offset, but is otherwise the same as
/// [`Config::DEFAULT`].
pub const TIME_OFFSET = Config::DEFAULT
.set_formatted_components(FormattedComponents::TimeOffset);
}
/// 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 {
#[expect(missing_docs)]
decimal_digits: Option<NonZero<u8>>,
},
/// Format the hour and minute. Seconds and nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Minute {
#[expect(missing_docs)]
decimal_digits: Option<NonZero<u8>>,
},
/// Format the hour, minute, and second. Nanoseconds will be represented with the specified
/// number of decimal digits, if any.
Second {
#[expect(missing_docs)]
decimal_digits: Option<NonZero<u8>>,
},
}
/// 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.
#[expect(missing_copy_implementations, reason = "forwards compatibility")]
#[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: NonZero::new(9),
},
offset_precision: OffsetPrecision::Minute,
};
/// A configuration that can only be used for parsing. Using this to format a value is
/// unspecified behavior.
const PARSING: Self = Self {
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,
};
/// Set whether the format the date, time, and/or UTC offset.
#[inline]
pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self {
Self {
formatted_components,
..self
}
}
/// Set whether the format contains separators (such as `-` or `:`).
#[inline]
pub const fn set_use_separators(self, use_separators: bool) -> Self {
Self {
use_separators,
..self
}
}
/// Set whether the year is six digits.
#[inline]
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.
#[inline]
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.
#[inline]
pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self {
Self {
time_precision,
..self
}
}
/// Set the precision for the UTC offset.
#[inline]
pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self {
Self {
offset_precision,
..self
}
}
}

View File

@@ -0,0 +1,251 @@
//! Hackery to work around not being able to use ADTs in const generics on stable.
use core::num::NonZero;
#[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`](super::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 u8;
bytes[2] = self.year_is_six_digits as u8;
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: NonZero::new(bytes[5]),
},
1 => TimePrecision::Minute {
decimal_digits: NonZero::new(bytes[5]),
},
2 => TimePrecision::Second {
decimal_digits: NonZero::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 {
if bytes[idx] != 0 {
panic!("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: NonZero::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute {
decimal_digits: NonZero::new(1),
}));
assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second {
decimal_digits: NonZero::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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,443 @@
use core::mem::MaybeUninit;
use core::num::NonZero;
use num_conv::prelude::*;
use crate::format_description::Period;
use crate::{
Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
/// State used by date-providing types to cache computed values.
///
/// This is used to avoid redundant computations when multiple date components are almost certainly
/// going to be requested within the same formatting invocation.
#[derive(Debug)]
pub(crate) struct DateState {
day: Option<NonZero<u8>>,
month: Option<Month>,
iso_week: Option<NonZero<u8>>,
iso_year_is_initialized: bool,
iso_year: MaybeUninit<i32>,
}
impl Default for DateState {
fn default() -> Self {
Self {
day: Default::default(),
month: Default::default(),
iso_week: Default::default(),
iso_year_is_initialized: Default::default(),
iso_year: MaybeUninit::uninit(),
}
}
}
macro_rules! unimplemented_methods {
($(
$(#[$meta:meta])*
($component:literal) $name:ident => $ret:ty;
)*) => {
$(
$(#[$meta])*
#[track_caller]
#[expect(unused_variables, reason = "better for auto-generation of method stubs")]
fn $name(&self, state: &mut Self::State) -> $ret {
unimplemented!(concat!("type does not supply ", $component, " components"))
}
)*
};
}
macro_rules! delegate_providers {
(
$target:ident {
$($method:ident -> $return:ty)*
}
) => {$(
#[inline]
fn $method(&self, state: &mut Self::State) -> $return {
ComponentProvider::$method(&self.$target(), state)
}
)*};
(
$target:ident ($state:expr) {
$($method:ident -> $return:ty)*
}
) => {$(
#[inline]
fn $method(&self, _: &mut Self::State) -> $return {
ComponentProvider::$method(&self.$target(), $state)
}
)*};
}
/// A type with the ability to provide date, time, offset, and/or timestamp components on demand.
///
/// Note that while all methods have a default body, implementations are expected to override the
/// body for all components that they provide. The default implementation exists solely for
/// convenience, avoiding the need to specify unprovided components.
pub(crate) trait ComponentProvider {
/// The state type used by the provider, allowing for caching of computed values.
type State: Default;
/// Whether the type can provide date components, indicating that date-related methods can be
/// called.
const SUPPLIES_DATE: bool = false;
/// Whether the type can provide time components, indicating that time-related methods can be
/// called.
const SUPPLIES_TIME: bool = false;
/// Whether the type can provide offset components, indicating that offset-related methods can
/// be called.
const SUPPLIES_OFFSET: bool = false;
/// Whether the type can provide timestamp components, indicating that timestamp-related methods
/// can be called.
const SUPPLIES_TIMESTAMP: bool = false;
unimplemented_methods! {
/// Obtain the day of the month.
("date") day => u8;
/// Obtain the month of the year.
("date") month => Month;
/// Obtain the ordinal day of the year.
("date") ordinal => u16;
/// Obtain the day of the week.
("date") weekday => Weekday;
/// Obtain the ISO week number.
("date") iso_week_number => u8;
/// Obtain the Monday-based week number.
("date") monday_based_week => u8;
/// Obtain the Sunday-based week number.
("date") sunday_based_week => u8;
/// Obtain the calendar year.
("date") calendar_year => i32;
/// Obtain the ISO week-based year.
("date") iso_year => i32;
/// Obtain the hour within the day.
("time") hour => u8;
/// Obtain the minute within the hour.
("time") minute => u8;
/// Obtain the period of the day (AM/PM).
("time") period => Period;
/// Obtain the second within the minute.
("time") second => u8;
/// Obtain the nanosecond within the second.
("time") nanosecond => u32;
/// Obtain whether the offset is negative.
("offset") offset_is_negative => bool;
/// Obtain whether the offset is UTC.
("offset") offset_is_utc => bool;
/// Obtain the hour component of the UTC offset.
("offset") offset_hour => i8;
/// Obtain the minute component of the UTC offset.
("offset") offset_minute => i8;
/// Obtain the second component of the UTC offset.
("offset") offset_second => i8;
/// Obtain the Unix timestamp in seconds.
("timestamp") unix_timestamp_seconds => i64;
/// Obtain the Unix timestamp in milliseconds.
("timestamp") unix_timestamp_milliseconds => i64;
/// Obtain the Unix timestamp in microseconds.
("timestamp") unix_timestamp_microseconds => i128;
/// Obtain the Unix timestamp in nanoseconds.
("timestamp") unix_timestamp_nanoseconds => i128;
}
}
impl ComponentProvider for Time {
type State = ();
const SUPPLIES_TIME: bool = true;
#[inline]
fn hour(&self, _: &mut Self::State) -> u8 {
(*self).hour()
}
#[inline]
fn minute(&self, _: &mut Self::State) -> u8 {
(*self).minute()
}
#[inline]
fn period(&self, _: &mut Self::State) -> Period {
if (*self).hour() < 12 {
Period::Am
} else {
Period::Pm
}
}
#[inline]
fn second(&self, _: &mut Self::State) -> u8 {
(*self).second()
}
#[inline]
fn nanosecond(&self, _: &mut Self::State) -> u32 {
(*self).nanosecond()
}
}
impl ComponentProvider for Date {
type State = DateState;
const SUPPLIES_DATE: bool = true;
#[inline]
fn day(&self, state: &mut Self::State) -> u8 {
if let Some(day) = state.day {
return day.get();
}
let (_, month, day) = (*self).to_calendar_date();
state.month = Some(month);
// Safety: `day` is guaranteed to be non-zero.
state.day = Some(unsafe { NonZero::new_unchecked(day) });
day
}
#[inline]
fn month(&self, state: &mut Self::State) -> Month {
*state.month.get_or_insert_with(|| (*self).month())
}
#[inline]
fn ordinal(&self, _: &mut Self::State) -> u16 {
(*self).ordinal()
}
#[inline]
fn weekday(&self, _: &mut Self::State) -> Weekday {
(*self).weekday()
}
#[inline]
fn iso_week_number(&self, state: &mut Self::State) -> u8 {
if let Some(week) = state.iso_week {
return week.get();
}
let (iso_year, iso_week) = (*self).iso_year_week();
state.iso_year = MaybeUninit::new(iso_year);
state.iso_year_is_initialized = true;
// Safety: `iso_week` is guaranteed to be non-zero.
state.iso_week = Some(unsafe { NonZero::new_unchecked(iso_week) });
iso_week
}
#[inline]
fn monday_based_week(&self, _: &mut Self::State) -> u8 {
(*self).monday_based_week()
}
#[inline]
fn sunday_based_week(&self, _: &mut Self::State) -> u8 {
(*self).sunday_based_week()
}
#[inline]
fn calendar_year(&self, _: &mut Self::State) -> i32 {
(*self).year()
}
#[inline]
fn iso_year(&self, state: &mut Self::State) -> i32 {
if state.iso_year_is_initialized {
// Safety: `iso_year` was declared to be initialized.
return unsafe { state.iso_year.assume_init() };
}
let (iso_year, iso_week) = (*self).iso_year_week();
state.iso_year = MaybeUninit::new(iso_year);
state.iso_year_is_initialized = true;
// Safety: `iso_week` is guaranteed to be non-zero.
state.iso_week = Some(unsafe { NonZero::new_unchecked(iso_week) });
iso_year
}
}
impl ComponentProvider for PrimitiveDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
delegate_providers!(date {
day -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
}
impl ComponentProvider for UtcOffset {
type State = ();
const SUPPLIES_OFFSET: bool = true;
#[inline]
fn offset_is_negative(&self, _: &mut Self::State) -> bool {
(*self).is_negative()
}
#[inline]
fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
(*self).is_utc()
}
#[inline]
fn offset_hour(&self, _: &mut Self::State) -> i8 {
(*self).whole_hours()
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> i8 {
(*self).minutes_past_hour()
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> i8 {
(*self).seconds_past_minute()
}
}
impl ComponentProvider for UtcDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
const SUPPLIES_OFFSET: bool = true;
const SUPPLIES_TIMESTAMP: bool = true;
delegate_providers!(date {
day -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
#[inline]
fn offset_is_negative(&self, _: &mut Self::State) -> bool {
false
}
#[inline]
fn offset_is_utc(&self, _state: &mut Self::State) -> bool {
true
}
#[inline]
fn offset_hour(&self, _: &mut Self::State) -> i8 {
0
}
#[inline]
fn offset_minute(&self, _: &mut Self::State) -> i8 {
0
}
#[inline]
fn offset_second(&self, _: &mut Self::State) -> i8 {
0
}
#[inline]
fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
(*self).unix_timestamp()
}
#[inline]
fn unix_timestamp_milliseconds(&self, state: &mut Self::State) -> i64 {
(ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000_000).truncate()
}
#[inline]
fn unix_timestamp_microseconds(&self, state: &mut Self::State) -> i128 {
ComponentProvider::unix_timestamp_nanoseconds(self, state) / 1_000
}
#[inline]
fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos()
}
}
impl ComponentProvider for OffsetDateTime {
type State = DateState;
const SUPPLIES_DATE: bool = true;
const SUPPLIES_TIME: bool = true;
const SUPPLIES_OFFSET: bool = true;
const SUPPLIES_TIMESTAMP: bool = true;
delegate_providers!(date {
day -> u8
month -> Month
ordinal -> u16
weekday -> Weekday
iso_week_number -> u8
monday_based_week -> u8
sunday_based_week -> u8
calendar_year -> i32
iso_year -> i32
});
delegate_providers!(time (&mut ()) {
hour -> u8
minute -> u8
period -> Period
second -> u8
nanosecond -> u32
});
delegate_providers!(offset (&mut ()) {
offset_is_negative -> bool
offset_is_utc -> bool
offset_hour -> i8
offset_minute -> i8
offset_second -> i8
});
#[inline]
fn unix_timestamp_seconds(&self, _: &mut Self::State) -> i64 {
(*self).unix_timestamp()
}
#[inline]
fn unix_timestamp_milliseconds(&self, _: &mut Self::State) -> i64 {
((*self).unix_timestamp_nanos() / 1_000_000) as i64
}
#[inline]
fn unix_timestamp_microseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos() / 1_000
}
#[inline]
fn unix_timestamp_nanoseconds(&self, _: &mut Self::State) -> i128 {
(*self).unix_timestamp_nanos()
}
}

View File

@@ -0,0 +1,402 @@
//! A trait that can be used to format an item from its components.
use alloc::string::String;
use alloc::vec::Vec;
use core::ops::Deref;
use std::io;
use num_conv::prelude::*;
use crate::error;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
use crate::format_description::{BorrowedFormatItem, OwnedFormatItem};
use crate::formatting::{
ComponentProvider, MONTH_NAMES, WEEKDAY_NAMES, format_component, format_number_pad_zero,
iso8601, write, write_if_else,
};
/// A type that describes a format.
///
/// Implementors of [`Formattable`] are [format descriptions](crate::format_description).
///
/// [`Date::format`] and [`Time::format`] each use a format description to generate
/// a String from their data. See the respective methods for usage examples.
#[cfg_attr(docsrs, doc(notable_trait))]
pub trait Formattable: sealed::Sealed {}
impl Formattable for BorrowedFormatItem<'_> {}
impl Formattable for [BorrowedFormatItem<'_>] {}
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> Formattable for T where T: Deref<Target: Formattable> {}
/// Seal the trait to prevent downstream users from implementing it.
mod sealed {
use super::*;
use crate::formatting::ComponentProvider;
/// Format the item using a format description, the intended output, and the various components.
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
pub trait Sealed {
/// Format the item into the provided output, returning the number of bytes written.
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider;
/// Format the item directly to a `String`.
#[inline]
fn format<V>(&self, value: &V, state: &mut V::State) -> Result<String, error::Format>
where
V: ComponentProvider,
{
let mut buf = Vec::new();
self.format_into(&mut buf, value, state)?;
Ok(String::from_utf8_lossy(&buf).into_owned())
}
}
}
impl sealed::Sealed for BorrowedFormatItem<'_> {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
Ok(match *self {
Self::Literal(literal) => write(output, literal)?,
Self::Component(component) => format_component(output, component, value, state)?,
Self::Compound(items) => (*items).format_into(output, value, state)?,
Self::Optional(item) => (*item).format_into(output, value, state)?,
Self::First(items) => match items {
[] => 0,
[item, ..] => (*item).format_into(output, value, state)?,
},
})
}
}
impl sealed::Sealed for [BorrowedFormatItem<'_>] {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
let mut bytes = 0;
for item in self.iter() {
bytes += (*item).format_into(output, value, state)?;
}
Ok(bytes)
}
}
impl sealed::Sealed for OwnedFormatItem {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
match self {
Self::Literal(literal) => Ok(write(output, literal)?),
Self::Component(component) => format_component(output, *component, value, state),
Self::Compound(items) => (**items).format_into(output, value, state),
Self::Optional(item) => (**item).format_into(output, value, state),
Self::First(items) => match &**items {
[] => Ok(0),
[item, ..] => (*item).format_into(output, value, state),
},
}
}
}
impl sealed::Sealed for [OwnedFormatItem] {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
let mut bytes = 0;
for item in self.iter() {
bytes += item.format_into(output, value, state)?;
}
Ok(bytes)
}
}
impl<T> sealed::Sealed for T
where
T: Deref<Target: sealed::Sealed>,
{
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
self.deref().format_into(output, value, state)
}
}
impl sealed::Sealed for Rfc2822 {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
const {
assert!(
V::SUPPLIES_DATE && V::SUPPLIES_TIME && V::SUPPLIES_OFFSET,
"Rfc2822 requires date, time, and offset components, but not all can be provided \
by this type"
);
}
let mut bytes = 0;
if value.calendar_year(state) < 1900 {
return Err(error::Format::InvalidComponent("year"));
}
if value.offset_second(state) != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
// Safety: All weekday names are at least 3 bytes long.
bytes += write(output, unsafe {
WEEKDAY_NAMES[value
.weekday(state)
.number_days_from_monday()
.extend::<usize>()]
.get_unchecked(..3)
})?;
bytes += write(output, b", ")?;
bytes += format_number_pad_zero::<2>(output, value.day(state))?;
bytes += write(output, b" ")?;
// Safety: All month names are at least 3 bytes long.
bytes += write(output, unsafe {
MONTH_NAMES[u8::from(value.month(state)).extend::<usize>() - 1].get_unchecked(..3)
})?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<4>(output, value.calendar_year(state).cast_unsigned())?;
bytes += write(output, b" ")?;
bytes += format_number_pad_zero::<2>(output, value.hour(state))?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, value.minute(state))?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, value.second(state))?;
bytes += write(output, b" ")?;
bytes += write_if_else(output, value.offset_is_negative(state), b"-", b"+")?;
bytes += format_number_pad_zero::<2>(output, value.offset_hour(state).unsigned_abs())?;
bytes += format_number_pad_zero::<2>(output, value.offset_minute(state).unsigned_abs())?;
Ok(bytes)
}
}
impl sealed::Sealed for Rfc3339 {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
const {
assert!(
V::SUPPLIES_DATE && V::SUPPLIES_TIME && V::SUPPLIES_OFFSET,
"Rfc3339 requires date, time, and offset components, but not all can be provided \
by this type"
);
}
let offset_hour = value.offset_hour(state);
let mut bytes = 0;
if !(0..10_000).contains(&value.calendar_year(state)) {
return Err(error::Format::InvalidComponent("year"));
}
if offset_hour.unsigned_abs() > 23 {
return Err(error::Format::InvalidComponent("offset_hour"));
}
if value.offset_second(state) != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += format_number_pad_zero::<4>(output, value.calendar_year(state).cast_unsigned())?;
bytes += write(output, b"-")?;
bytes += format_number_pad_zero::<2>(output, u8::from(value.month(state)))?;
bytes += write(output, b"-")?;
bytes += format_number_pad_zero::<2>(output, value.day(state))?;
bytes += write(output, b"T")?;
bytes += format_number_pad_zero::<2>(output, value.hour(state))?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, value.minute(state))?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, value.second(state))?;
let nanos = value.nanosecond(state);
if nanos != 0 {
bytes += write(output, b".")?;
bytes += if nanos % 10 != 0 {
format_number_pad_zero::<9>(output, nanos)
} else if (nanos / 10) % 10 != 0 {
format_number_pad_zero::<8>(output, nanos / 10)
} else if (nanos / 100) % 10 != 0 {
format_number_pad_zero::<7>(output, nanos / 100)
} else if (nanos / 1_000) % 10 != 0 {
format_number_pad_zero::<6>(output, nanos / 1_000)
} else if (nanos / 10_000) % 10 != 0 {
format_number_pad_zero::<5>(output, nanos / 10_000)
} else if (nanos / 100_000) % 10 != 0 {
format_number_pad_zero::<4>(output, nanos / 100_000)
} else if (nanos / 1_000_000) % 10 != 0 {
format_number_pad_zero::<3>(output, nanos / 1_000_000)
} else if (nanos / 10_000_000) % 10 != 0 {
format_number_pad_zero::<2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<1>(output, nanos / 100_000_000)
}?;
}
if value.offset_is_utc(state) {
bytes += write(output, b"Z")?;
return Ok(bytes);
}
bytes += write_if_else(output, value.offset_is_negative(state), b"-", b"+")?;
bytes += format_number_pad_zero::<2>(output, offset_hour.unsigned_abs())?;
bytes += write(output, b":")?;
bytes += format_number_pad_zero::<2>(output, value.offset_minute(state).unsigned_abs())?;
Ok(bytes)
}
}
impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
#[expect(
private_bounds,
private_interfaces,
reason = "irrelevant due to being a sealed trait"
)]
#[inline]
fn format_into<V>(
&self,
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
let mut bytes = 0;
const {
assert!(
!Self::FORMAT_DATE || V::SUPPLIES_DATE,
"this Iso8601 configuration formats date components, but this type cannot provide \
them"
);
assert!(
!Self::FORMAT_TIME || V::SUPPLIES_TIME,
"this Iso8601 configuration formats time components, but this type cannot provide \
them"
);
assert!(
!Self::FORMAT_OFFSET || V::SUPPLIES_OFFSET,
"this Iso8601 configuration formats offset components, but this type cannot \
provide them"
);
assert!(
Self::FORMAT_DATE || Self::FORMAT_TIME || Self::FORMAT_OFFSET,
"this Iso8601 configuration does not format any components"
);
}
if Self::FORMAT_DATE {
bytes += iso8601::format_date::<_, CONFIG>(output, value, state)?;
}
if Self::FORMAT_TIME {
bytes += iso8601::format_time::<_, CONFIG>(output, value, state)?;
}
if Self::FORMAT_OFFSET {
bytes += iso8601::format_offset::<_, CONFIG>(output, value, state)?;
}
Ok(bytes)
}
}

160
vendor/time/src/formatting/iso8601.rs vendored Normal file
View File

@@ -0,0 +1,160 @@
//! Helpers for implementing formatting for ISO 8601.
use std::io;
use crate::convert::*;
use crate::error;
use crate::format_description::well_known::Iso8601;
use crate::format_description::well_known::iso8601::{
DateKind, EncodedConfig, OffsetPrecision, TimePrecision,
};
use crate::formatting::{
ComponentProvider, format_float, format_number_pad_zero, write, write_if, write_if_else,
};
/// Format the date portion of ISO 8601.
pub(super) fn format_date<V, const CONFIG: EncodedConfig>(
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
let mut bytes = 0;
match Iso8601::<CONFIG>::DATE_KIND {
DateKind::Calendar => {
let year = value.calendar_year(state);
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.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2>(output, u8::from(value.month(state)))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<2>(output, value.day(state))?;
}
DateKind::Week => {
let year = value.iso_year(state);
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.cast_unsigned())?;
}
bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?;
bytes += format_number_pad_zero::<2>(output, value.iso_week_number(state))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes +=
format_number_pad_zero::<1>(output, value.weekday(state).number_from_monday())?;
}
DateKind::Ordinal => {
let year = value.calendar_year(state);
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.cast_unsigned())?;
}
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?;
bytes += format_number_pad_zero::<3>(output, value.ordinal(state))?;
}
}
Ok(bytes)
}
/// Format the time portion of ISO 8601.
#[inline]
pub(super) fn format_time<V, const CONFIG: EncodedConfig>(
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
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",
)?;
match Iso8601::<CONFIG>::TIME_PRECISION {
TimePrecision::Hour { decimal_digits } => {
let hours = (value.hour(state) as f64)
+ (value.minute(state) as f64) / Minute::per_t::<f64>(Hour)
+ (value.second(state) as f64) / Second::per_t::<f64>(Hour)
+ (value.nanosecond(state) as f64) / Nanosecond::per_t::<f64>(Hour);
format_float(output, hours, 2, decimal_digits)?;
}
TimePrecision::Minute { decimal_digits } => {
bytes += format_number_pad_zero::<2>(output, value.hour(state))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let minutes = (value.minute(state) as f64)
+ (value.second(state) as f64) / Second::per_t::<f64>(Minute)
+ (value.nanosecond(state) as f64) / Nanosecond::per_t::<f64>(Minute);
bytes += format_float(output, minutes, 2, decimal_digits)?;
}
TimePrecision::Second { decimal_digits } => {
bytes += format_number_pad_zero::<2>(output, value.hour(state))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
bytes += format_number_pad_zero::<2>(output, value.minute(state))?;
bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?;
let seconds = (value.second(state) as f64)
+ (value.nanosecond(state) as f64) / Nanosecond::per_t::<f64>(Second);
bytes += format_float(output, seconds, 2, decimal_digits)?;
}
}
Ok(bytes)
}
/// Format the UTC offset portion of ISO 8601.
#[inline]
pub(super) fn format_offset<V, const CONFIG: EncodedConfig>(
output: &mut (impl io::Write + ?Sized),
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
if Iso8601::<CONFIG>::FORMAT_TIME && value.offset_is_utc(state) {
return Ok(write(output, b"Z")?);
}
let mut bytes = 0;
if value.offset_second(state) != 0 {
return Err(error::Format::InvalidComponent("offset_second"));
}
bytes += write_if_else(output, value.offset_is_negative(state), b"-", b"+")?;
bytes += format_number_pad_zero::<2>(output, value.offset_hour(state).unsigned_abs())?;
let minutes = value.offset_minute(state);
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)
}

664
vendor/time/src/formatting/mod.rs vendored Normal file
View File

@@ -0,0 +1,664 @@
//! Formatting for various types.
mod component_provider;
pub(crate) mod formattable;
mod iso8601;
use core::num::NonZero;
use std::io;
use num_conv::prelude::*;
use self::component_provider::ComponentProvider;
pub use self::formattable::Formattable;
use crate::ext::DigitCount;
use crate::format_description::{Component, Period, modifier};
use crate::internal_macros::try_likely_ok;
use crate::{Month, Weekday, error};
const MONTH_NAMES: [&[u8]; 12] = [
b"January",
b"February",
b"March",
b"April",
b"May",
b"June",
b"July",
b"August",
b"September",
b"October",
b"November",
b"December",
];
const WEEKDAY_NAMES: [&[u8]; 7] = [
b"Monday",
b"Tuesday",
b"Wednesday",
b"Thursday",
b"Friday",
b"Saturday",
b"Sunday",
];
/// Write all bytes to the output, returning the number of bytes written.
#[inline]
pub(crate) fn write(output: &mut (impl io::Write + ?Sized), 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.
#[inline]
pub(crate) fn write_if(
output: &mut (impl io::Write + ?Sized),
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`.
#[inline]
pub(crate) fn write_if_else(
output: &mut (impl io::Write + ?Sized),
pred: bool,
true_bytes: &[u8],
false_bytes: &[u8],
) -> io::Result<usize> {
write(output, if pred { true_bytes } else { false_bytes })
}
/// Helper function to obtain 10^x, guaranteeing determinism for x ≤ 9. For these cases, the
/// function optimizes to a lookup table. For x ≥ 10, it falls back to `10_f64.powi(x)`. The only
/// situation where this would occur is if the user explicitly requests such precision when
/// configuring the ISO 8601 well known format. All other possibilities max out at nine digits.
#[inline]
fn f64_10_pow_x(x: NonZero<u8>) -> f64 {
match x.get() {
1 => 10.,
2 => 100.,
3 => 1_000.,
4 => 10_000.,
5 => 100_000.,
6 => 1_000_000.,
7 => 10_000_000.,
8 => 100_000_000.,
9 => 1_000_000_000.,
x => 10_f64.powi(x.cast_signed().extend()),
}
}
/// 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.
#[inline]
pub(crate) fn format_float(
output: &mut (impl io::Write + ?Sized),
mut value: f64,
digits_before_decimal: u8,
digits_after_decimal: Option<NonZero<u8>>,
) -> io::Result<usize> {
match digits_after_decimal {
Some(digits_after_decimal) => {
// If the precision is less than nine digits after the decimal point, truncate the
// value. This avoids rounding up and causing the value to exceed the maximum permitted
// value (as in #678). If the precision is at least nine, then we don't truncate so as
// to avoid having an off-by-one error (as in #724). The latter is necessary
// because floating point values are inherently imprecise with decimal
// values, so a minuscule error can be amplified easily.
//
// Note that this is largely an issue for second values, as for minute and hour decimals
// the value is divided by 60 or 3,600, neither of which divide evenly into 10^x.
//
// While not a perfect approach, this addresses the bugs that have been reported so far
// without being overly complex.
if digits_after_decimal.get() < 9 {
let trunc_num = f64_10_pow_x(digits_after_decimal);
value = f64::trunc(value * trunc_num) / trunc_num;
}
let digits_after_decimal = digits_after_decimal.get().extend();
let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal;
write!(output, "{value:0>width$.digits_after_decimal$}")?;
Ok(width)
}
None => {
let value = value.trunc() as u64;
let width = digits_before_decimal.extend();
write!(output, "{value:0>width$}")?;
Ok(width)
}
}
}
/// Format a number with the provided padding and width.
///
/// The sign must be written by the caller.
#[inline]
pub(crate) fn format_number<const WIDTH: u8>(
output: &mut (impl io::Write + ?Sized),
value: impl itoa::Integer + DigitCount + Copy,
padding: modifier::Padding,
) -> Result<usize, io::Error> {
match padding {
modifier::Padding::Space => format_number_pad_space::<WIDTH>(output, value),
modifier::Padding::Zero => format_number_pad_zero::<WIDTH>(output, value),
modifier::Padding::None => format_number_pad_none(output, value),
}
}
/// Format a number with the provided width and spaces as padding.
///
/// The sign must be written by the caller.
#[inline]
pub(crate) fn format_number_pad_space<const WIDTH: u8>(
output: &mut (impl io::Write + ?Sized),
value: impl itoa::Integer + DigitCount + Copy,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, b" ")?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format a number with the provided width and zeros as padding.
///
/// The sign must be written by the caller.
#[inline]
pub(crate) fn format_number_pad_zero<const WIDTH: u8>(
output: &mut (impl io::Write + ?Sized),
value: impl itoa::Integer + DigitCount + Copy,
) -> Result<usize, io::Error> {
let mut bytes = 0;
for _ in 0..(WIDTH.saturating_sub(value.num_digits())) {
bytes += write(output, b"0")?;
}
bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?;
Ok(bytes)
}
/// Format a number with no padding.
///
/// If the sign is mandatory, the sign must be written by the caller.
#[inline]
pub(crate) fn format_number_pad_none(
output: &mut (impl io::Write + ?Sized),
value: impl itoa::Integer + Copy,
) -> Result<usize, io::Error> {
write(output, itoa::Buffer::new().format(value).as_bytes())
}
/// Format the provided component into the designated output. An `Err` will be returned if the
/// component requires information that it does not provide or if the value cannot be output to the
/// stream.
#[inline]
pub(crate) fn format_component<V>(
output: &mut (impl io::Write + ?Sized),
component: Component,
value: &V,
state: &mut V::State,
) -> Result<usize, error::Format>
where
V: ComponentProvider,
{
use Component::*;
Ok(match component {
Day(modifier) if V::SUPPLIES_DATE => {
try_likely_ok!(fmt_day(output, value.day(state), modifier))
}
Month(modifier) if V::SUPPLIES_DATE => {
try_likely_ok!(fmt_month(output, value.month(state), modifier))
}
Ordinal(modifier) if V::SUPPLIES_DATE => {
try_likely_ok!(fmt_ordinal(output, value.ordinal(state), modifier))
}
Weekday(modifier) if V::SUPPLIES_DATE => {
try_likely_ok!(fmt_weekday(output, value.weekday(state), modifier))
}
WeekNumber(modifier) if V::SUPPLIES_DATE => try_likely_ok!(fmt_week_number(
output,
match modifier.repr {
modifier::WeekNumberRepr::Iso => value.iso_week_number(state),
modifier::WeekNumberRepr::Sunday => value.sunday_based_week(state),
modifier::WeekNumberRepr::Monday => value.monday_based_week(state),
},
modifier,
)),
Year(modifier) if V::SUPPLIES_DATE => try_likely_ok!(fmt_year(
output,
if modifier.iso_week_based {
value.iso_year(state)
} else {
value.calendar_year(state)
},
modifier,
)),
Hour(modifier) if V::SUPPLIES_TIME => {
try_likely_ok!(fmt_hour(output, value.hour(state), modifier))
}
Minute(modifier) if V::SUPPLIES_TIME => {
try_likely_ok!(fmt_minute(output, value.minute(state), modifier))
}
Period(modifier) if V::SUPPLIES_TIME => {
try_likely_ok!(fmt_period(output, value.period(state), modifier))
}
Second(modifier) if V::SUPPLIES_TIME => {
try_likely_ok!(fmt_second(output, value.second(state), modifier))
}
Subsecond(modifier) if V::SUPPLIES_TIME => {
try_likely_ok!(fmt_subsecond(output, value.nanosecond(state), modifier))
}
OffsetHour(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_hour(
output,
value.offset_is_negative(state),
value.offset_hour(state),
modifier,
)),
OffsetMinute(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_minute(
output,
value.offset_minute(state),
modifier
)),
OffsetSecond(modifier) if V::SUPPLIES_OFFSET => try_likely_ok!(fmt_offset_second(
output,
value.offset_second(state),
modifier
)),
Ignore(_) => 0,
UnixTimestamp(modifier) if V::SUPPLIES_TIMESTAMP => match modifier.precision {
modifier::UnixTimestampPrecision::Second => try_likely_ok!(fmt_unix_timestamp_seconds(
output,
value.unix_timestamp_seconds(state),
modifier,
)),
modifier::UnixTimestampPrecision::Millisecond => {
try_likely_ok!(fmt_unix_timestamp_milliseconds(
output,
value.unix_timestamp_milliseconds(state),
modifier,
))
}
modifier::UnixTimestampPrecision::Microsecond => {
try_likely_ok!(fmt_unix_timestamp_microseconds(
output,
value.unix_timestamp_microseconds(state),
modifier,
))
}
modifier::UnixTimestampPrecision::Nanosecond => {
try_likely_ok!(fmt_unix_timestamp_nanoseconds(
output,
value.unix_timestamp_nanoseconds(state),
modifier,
))
}
},
End(modifier::End { trailing_input: _ }) => 0,
// This is functionally the same as a wildcard arm, but it will cause an error if a new
// component is added. This is to avoid a bug where a new component, the code compiles, and
// formatting fails.
// Allow unreachable patterns because some branches may be fully matched above.
#[allow(unreachable_patterns)]
Day(_) | Month(_) | Ordinal(_) | Weekday(_) | WeekNumber(_) | Year(_) | Hour(_)
| Minute(_) | Period(_) | Second(_) | Subsecond(_) | OffsetHour(_) | OffsetMinute(_)
| OffsetSecond(_) | Ignore(_) | UnixTimestamp(_) | End(_) => {
return Err(error::Format::InsufficientTypeInformation);
}
})
}
/// Format the day into the designated output.
#[inline]
fn fmt_day(
output: &mut (impl io::Write + ?Sized),
day: u8,
modifier::Day { padding }: modifier::Day,
) -> Result<usize, io::Error> {
format_number::<2>(output, day, padding)
}
/// Format the month into the designated output.
#[inline]
fn fmt_month(
output: &mut (impl io::Write + ?Sized),
month: Month,
modifier::Month {
padding,
repr,
case_sensitive: _, // no effect on formatting
}: modifier::Month,
) -> Result<usize, io::Error> {
match repr {
modifier::MonthRepr::Numerical => format_number::<2>(output, u8::from(month), padding),
modifier::MonthRepr::Long => {
write(output, MONTH_NAMES[u8::from(month).extend::<usize>() - 1])
}
// Safety: All month names are at least three bytes long.
modifier::MonthRepr::Short => write(output, unsafe {
MONTH_NAMES[u8::from(month).extend::<usize>() - 1].get_unchecked(..3)
}),
}
}
/// Format the ordinal into the designated output.
#[inline]
fn fmt_ordinal(
output: &mut (impl io::Write + ?Sized),
ordinal: u16,
modifier::Ordinal { padding }: modifier::Ordinal,
) -> Result<usize, io::Error> {
format_number::<3>(output, ordinal, padding)
}
/// Format the weekday into the designated output.
#[inline]
fn fmt_weekday(
output: &mut (impl io::Write + ?Sized),
weekday: Weekday,
modifier::Weekday {
repr,
one_indexed,
case_sensitive: _, // no effect on formatting
}: modifier::Weekday,
) -> Result<usize, io::Error> {
match repr {
// Safety: All weekday names are at least three bytes long.
modifier::WeekdayRepr::Short => write(output, unsafe {
WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()].get_unchecked(..3)
}),
modifier::WeekdayRepr::Long => write(
output,
WEEKDAY_NAMES[weekday.number_days_from_monday().extend::<usize>()],
),
modifier::WeekdayRepr::Sunday => format_number::<1>(
output,
weekday.number_days_from_sunday() + u8::from(one_indexed),
modifier::Padding::None,
),
modifier::WeekdayRepr::Monday => format_number::<1>(
output,
weekday.number_days_from_monday() + u8::from(one_indexed),
modifier::Padding::None,
),
}
}
/// Format the week number into the designated output.
#[inline]
fn fmt_week_number(
output: &mut (impl io::Write + ?Sized),
week_number: u8,
modifier::WeekNumber { padding, repr: _ }: modifier::WeekNumber,
) -> Result<usize, io::Error> {
format_number::<2>(output, week_number, padding)
}
/// Format the year into the designated output.
fn fmt_year(
output: &mut (impl io::Write + ?Sized),
full_year: i32,
modifier::Year {
padding,
repr,
range,
iso_week_based: _,
sign_is_mandatory,
}: modifier::Year,
) -> Result<usize, error::Format> {
let value = match repr {
modifier::YearRepr::Full => full_year,
modifier::YearRepr::Century => full_year / 100,
modifier::YearRepr::LastTwo => (full_year % 100).abs(),
};
let format_number = if cfg!(feature = "large-dates") && range == modifier::YearRange::Extended {
match repr {
modifier::YearRepr::Full if value.abs() >= 100_000 => format_number::<6>,
modifier::YearRepr::Full if value.abs() >= 10_000 => format_number::<5>,
modifier::YearRepr::Full => format_number::<4>,
modifier::YearRepr::Century if value.abs() >= 1_000 => format_number::<4>,
modifier::YearRepr::Century if value.abs() >= 100 => format_number::<3>,
modifier::YearRepr::Century => format_number::<2>,
modifier::YearRepr::LastTwo => format_number::<2>,
}
} else {
match repr {
modifier::YearRepr::Full | modifier::YearRepr::Century if full_year.abs() >= 10_000 => {
return Err(error::ComponentRange::conditional("year").into());
}
_ => {}
}
match repr {
modifier::YearRepr::Full => format_number::<4>,
modifier::YearRepr::Century => format_number::<2>,
modifier::YearRepr::LastTwo => format_number::<2>,
}
};
let mut bytes = 0;
if repr != modifier::YearRepr::LastTwo {
if full_year < 0 {
bytes += write(output, b"-")?;
} else if sign_is_mandatory || cfg!(feature = "large-dates") && full_year >= 10_000 {
bytes += write(output, b"+")?;
}
}
bytes += format_number(output, value.unsigned_abs(), padding)?;
Ok(bytes)
}
/// Format the hour into the designated output.
#[inline]
fn fmt_hour(
output: &mut (impl io::Write + ?Sized),
hour: u8,
modifier::Hour {
padding,
is_12_hour_clock,
}: modifier::Hour,
) -> Result<usize, io::Error> {
let value = match (hour, is_12_hour_clock) {
(hour, false) => hour,
(0 | 12, true) => 12,
(hour, true) if hour < 12 => hour,
(hour, true) => hour - 12,
};
format_number::<2>(output, value, padding)
}
/// Format the minute into the designated output.
#[inline]
fn fmt_minute(
output: &mut (impl io::Write + ?Sized),
minute: u8,
modifier::Minute { padding }: modifier::Minute,
) -> Result<usize, io::Error> {
format_number::<2>(output, minute, padding)
}
/// Format the period into the designated output.
#[inline]
fn fmt_period(
output: &mut (impl io::Write + ?Sized),
period: Period,
modifier::Period {
is_uppercase,
case_sensitive: _, // no effect on formatting
}: modifier::Period,
) -> Result<usize, io::Error> {
write(
output,
match (period, is_uppercase) {
(Period::Am, false) => b"am",
(Period::Am, true) => b"AM",
(Period::Pm, false) => b"pm",
(Period::Pm, true) => b"PM",
},
)
}
/// Format the second into the designated output.
#[inline]
fn fmt_second(
output: &mut (impl io::Write + ?Sized),
second: u8,
modifier::Second { padding }: modifier::Second,
) -> Result<usize, io::Error> {
format_number::<2>(output, second, padding)
}
/// Format the subsecond into the designated output.
#[inline]
fn fmt_subsecond(
output: &mut (impl io::Write + ?Sized),
nanos: u32,
modifier::Subsecond { digits }: modifier::Subsecond,
) -> Result<usize, io::Error> {
use modifier::SubsecondDigits::*;
if digits == Nine || (digits == OneOrMore && !nanos.is_multiple_of(10)) {
format_number_pad_zero::<9>(output, nanos)
} else if digits == Eight || (digits == OneOrMore && !(nanos / 10).is_multiple_of(10)) {
format_number_pad_zero::<8>(output, nanos / 10)
} else if digits == Seven || (digits == OneOrMore && !(nanos / 100).is_multiple_of(10)) {
format_number_pad_zero::<7>(output, nanos / 100)
} else if digits == Six || (digits == OneOrMore && !(nanos / 1_000).is_multiple_of(10)) {
format_number_pad_zero::<6>(output, nanos / 1_000)
} else if digits == Five || (digits == OneOrMore && !(nanos / 10_000).is_multiple_of(10)) {
format_number_pad_zero::<5>(output, nanos / 10_000)
} else if digits == Four || (digits == OneOrMore && !(nanos / 100_000).is_multiple_of(10)) {
format_number_pad_zero::<4>(output, nanos / 100_000)
} else if digits == Three || (digits == OneOrMore && !(nanos / 1_000_000).is_multiple_of(10)) {
format_number_pad_zero::<3>(output, nanos / 1_000_000)
} else if digits == Two || (digits == OneOrMore && !(nanos / 10_000_000).is_multiple_of(10)) {
format_number_pad_zero::<2>(output, nanos / 10_000_000)
} else {
format_number_pad_zero::<1>(output, nanos / 100_000_000)
}
}
#[inline]
fn fmt_offset_sign(
output: &mut (impl io::Write + ?Sized),
is_negative: bool,
sign_is_mandatory: bool,
) -> Result<usize, io::Error> {
if is_negative {
write(output, b"-")
} else if sign_is_mandatory {
write(output, b"+")
} else {
Ok(0)
}
}
/// Format the offset hour into the designated output.
#[inline]
fn fmt_offset_hour(
output: &mut (impl io::Write + ?Sized),
is_negative: bool,
hour: i8,
modifier::OffsetHour {
padding,
sign_is_mandatory,
}: modifier::OffsetHour,
) -> Result<usize, io::Error> {
let mut bytes = 0;
bytes += fmt_offset_sign(output, is_negative, sign_is_mandatory)?;
bytes += format_number::<2>(output, hour.unsigned_abs(), padding)?;
Ok(bytes)
}
/// Format the offset minute into the designated output.
#[inline]
fn fmt_offset_minute(
output: &mut (impl io::Write + ?Sized),
offset_minute: i8,
modifier::OffsetMinute { padding }: modifier::OffsetMinute,
) -> Result<usize, io::Error> {
format_number::<2>(output, offset_minute.unsigned_abs(), padding)
}
/// Format the offset second into the designated output.
#[inline]
fn fmt_offset_second(
output: &mut (impl io::Write + ?Sized),
offset_second: i8,
modifier::OffsetSecond { padding }: modifier::OffsetSecond,
) -> Result<usize, io::Error> {
format_number::<2>(output, offset_second.unsigned_abs(), padding)
}
/// Format the Unix timestamp (in seconds) into the designated output.
#[inline]
fn fmt_unix_timestamp_seconds(
output: &mut (impl io::Write + ?Sized),
timestamp: i64,
modifier::UnixTimestamp {
precision,
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Second);
let mut bytes = 0;
bytes += fmt_offset_sign(output, timestamp < 0, sign_is_mandatory)?;
bytes += format_number_pad_none(output, timestamp.unsigned_abs())?;
Ok(bytes)
}
/// Format the Unix timestamp (in milliseconds) into the designated output.
#[inline]
fn fmt_unix_timestamp_milliseconds(
output: &mut (impl io::Write + ?Sized),
timestamp_millis: i64,
modifier::UnixTimestamp {
precision,
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Millisecond);
let mut bytes = 0;
bytes += fmt_offset_sign(output, timestamp_millis < 0, sign_is_mandatory)?;
bytes += format_number_pad_none(output, timestamp_millis.unsigned_abs())?;
Ok(bytes)
}
/// Format the Unix timestamp into the designated output.
#[inline]
fn fmt_unix_timestamp_microseconds(
output: &mut (impl io::Write + ?Sized),
timestamp_micros: i128,
modifier::UnixTimestamp {
precision,
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Microsecond);
let mut bytes = 0;
bytes += fmt_offset_sign(output, timestamp_micros < 0, sign_is_mandatory)?;
bytes += format_number_pad_none(output, timestamp_micros.unsigned_abs())?;
Ok(bytes)
}
/// Format the Unix timestamp into the designated output.
#[inline]
fn fmt_unix_timestamp_nanoseconds(
output: &mut (impl io::Write + ?Sized),
timestamp_nanos: i128,
modifier::UnixTimestamp {
precision,
sign_is_mandatory,
}: modifier::UnixTimestamp,
) -> Result<usize, io::Error> {
debug_assert_eq!(precision, modifier::UnixTimestampPrecision::Nanosecond);
let mut bytes = 0;
bytes += fmt_offset_sign(output, timestamp_nanos < 0, sign_is_mandatory)?;
bytes += format_number_pad_none(output, timestamp_nanos.unsigned_abs())?;
Ok(bytes)
}

24
vendor/time/src/hint.rs vendored Normal file
View File

@@ -0,0 +1,24 @@
//! Hints to the compiler that affects how code should be emitted or optimized.
/// Indicate that a given branch is **not** likely to be taken, relatively speaking.
#[inline(always)]
#[cold]
pub(crate) const fn cold_path() {}
/// Indicate that a given condition is likely to be true.
#[inline(always)]
pub(crate) const fn likely(b: bool) -> bool {
if !b {
cold_path();
}
b
}
/// Indicate that a given condition is likely to be false.
#[inline(always)]
pub(crate) const fn unlikely(b: bool) -> bool {
if b {
cold_path();
}
b
}

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

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

226
vendor/time/src/internal_macros.rs vendored Normal file
View File

@@ -0,0 +1,226 @@
//! Macros for use within the library. They are not publicly available.
/// Division of integers, rounding the resulting value towards negative infinity.
macro_rules! div_floor {
($self:expr, $rhs:expr) => {
match ($self, $rhs) {
(this, rhs) => {
let d = this / rhs;
let r = this % rhs;
// If the remainder is non-zero, we need to subtract one if the
// signs of self and rhs differ, as this means we rounded upwards
// instead of downwards. We do this branchlessly by creating a mask
// which is all-ones iff the signs differ, and 0 otherwise. Then by
// adding this mask (which corresponds to the signed value -1), we
// get our correction.
let correction = (this ^ rhs) >> (size_of_val(&this) * 8 - 1);
if r != 0 { d + correction } else { d }
}
}
};
}
/// Similar to `overflowing_add`, but returning the number of times that it overflowed. Contained to
/// a certain range and only overflows a maximum number of times.
macro_rules! carry {
(@most_once $value:expr, $min:literal.. $max:expr) => {
match ($value, $min, $max) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else {
(value - (max - min), 1)
}
} else {
(value + (max - min), -1)
}
}
}
};
(@most_twice $value:expr, $min:literal.. $max:expr) => {
match ($value, $min, $max) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else {
(value - 2 * (max - min), 2)
}
} else {
if value >= min - max {
(value + (max - min), -1)
} else {
(value + 2 * (max - min), -2)
}
}
}
}
};
(@most_thrice $value:expr, $min:literal.. $max:expr) => {
match ($value, $min, $max) {
(value, min, max) => {
if crate::hint::likely(value >= min) {
if crate::hint::likely(value < max) {
(value, 0)
} else if value < 2 * max - min {
(value - (max - min), 1)
} else if value < 3 * max - 2 * min {
(value - 2 * (max - min), 2)
} else {
(value - 3 * (max - min), 3)
}
} else {
if value >= min - max {
(value + (max - min), -1)
} else if value >= 2 * (min - max) {
(value + 2 * (max - min), -2)
} else {
(value + 3 * (max - min), -3)
}
}
}
}
};
}
/// Cascade an out-of-bounds value.
macro_rules! cascade {
(@ordinal ordinal) => {};
(@year year) => {};
// Cascade an out-of-bounds value from "from" to "to".
($from:ident in $min:literal.. $max:expr => $to:tt) => {
#[allow(unused_comparisons, unused_assignments)]
let min = $min;
let max = $max;
if crate::hint::unlikely($from >= max) {
$from -= max - min;
$to += 1;
} else if crate::hint::unlikely($from < min) {
$from += max - min;
$to -= 1;
}
};
// Special case the ordinal-to-year cascade, as it has different behavior.
($ordinal:ident => $year:ident) => {
// We need to actually capture the idents. Without this, macro hygiene causes errors.
cascade!(@ordinal $ordinal);
cascade!(@year $year);
let days_in_year = crate::util::range_validated::days_in_year($year).cast_signed();
#[allow(unused_assignments)]
if crate::hint::unlikely($ordinal > days_in_year) {
$ordinal -= days_in_year;
$year += 1;
} else if crate::hint::unlikely($ordinal < 1) {
$year -= 1;
$ordinal += crate::util::range_validated::days_in_year($year).cast_signed();
}
};
}
/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
macro_rules! ensure_ranged {
($type:ty : $value:ident) => {
match <$type>::new($value) {
Some(val) => val,
None => {
$crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
}
}
};
($type:ty : $value:ident ($name:literal)) => {
match <$type>::new($value) {
Some(val) => val,
None => {
$crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional($name));
}
}
};
($type:ty : $value:ident $(as $as_type:ident)? * $factor:expr) => {
match ($value $(as $as_type)?).checked_mul($factor) {
Some(val) => match <$type>::new(val) {
Some(val) => val,
None => {
$crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
}
},
None => {
$crate::hint::cold_path();
return Err(crate::error::ComponentRange::unconditional(stringify!($value)));
}
}
};
}
/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
/// usable in `const` contexts.
macro_rules! const_try {
($e:expr) => {
match $e {
Ok(value) => value,
Err(error) => {
$crate::hint::cold_path();
return Err(error);
}
}
};
}
/// Try to unwrap an expression, returning if not possible.
///
/// This is identical to `?` in terms of behavior, but marks the error path as cold.
#[cfg(any(feature = "formatting", feature = "parsing"))]
macro_rules! try_likely_ok {
($e:expr) => {
match $e {
Ok(value) => value,
Err(error) => {
$crate::hint::cold_path();
return Err(error.into());
}
}
};
}
/// Try to unwrap an expression, returning if not possible.
///
/// This is similar to the `?` operator, but is usable in `const` contexts.
macro_rules! const_try_opt {
($e:expr) => {
match $e {
Some(value) => value,
None => {
$crate::hint::cold_path();
return None;
}
}
};
}
/// `unreachable!()`, but better.
#[cfg(any(feature = "formatting", feature = "parsing"))]
macro_rules! bug {
() => {
compile_error!("provide an error message to help fix a possible bug")
};
($descr:literal) => {
panic!(concat!("internal error: ", $descr))
};
}
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub(crate) use {bug, try_likely_ok};
pub(crate) use {carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged};

View File

@@ -0,0 +1,24 @@
use crate::OffsetDateTime;
use crate::convert::*;
impl From<js_sys::Date> for OffsetDateTime {
/// # Panics
///
/// This may panic if the timestamp can not be represented.
#[track_caller]
fn from(js_date: js_sys::Date) -> Self {
// get_time() returns milliseconds
let timestamp_nanos = js_date.get_time() as i128 * Nanosecond::per_t::<i128>(Millisecond);
Self::from_unix_timestamp_nanos(timestamp_nanos)
.expect("invalid timestamp: Timestamp cannot fit in range")
}
}
impl From<OffsetDateTime> for js_sys::Date {
fn from(datetime: OffsetDateTime) -> Self {
// new Date() takes milliseconds
let timestamp =
(datetime.unix_timestamp_nanos() / Nanosecond::per_t::<i128>(Millisecond)) as f64;
Self::new(&timestamp.into())
}
}

View File

@@ -0,0 +1,24 @@
use crate::UtcDateTime;
use crate::convert::*;
impl From<js_sys::Date> for UtcDateTime {
/// # Panics
///
/// This may panic if the timestamp can not be represented.
#[track_caller]
fn from(js_date: js_sys::Date) -> Self {
// get_time() returns milliseconds
let timestamp_nanos = (js_date.get_time() * Nanosecond::per_t::<f64>(Millisecond)) as i128;
Self::from_unix_timestamp_nanos(timestamp_nanos)
.expect("invalid timestamp: Timestamp cannot fit in range")
}
}
impl From<UtcDateTime> for js_sys::Date {
fn from(datetime: UtcDateTime) -> Self {
// new Date() takes milliseconds
let timestamp =
(datetime.unix_timestamp_nanos() / Nanosecond::per_t::<i128>(Millisecond)) as f64;
Self::new(&timestamp.into())
}
}

28
vendor/time/src/interop/mod.rs vendored Normal file
View File

@@ -0,0 +1,28 @@
//! Comparison, arithmetic, and conversion between various types in `time` and the standard library.
//!
//! Currently, full interoperability is present between [`OffsetDateTime`](crate::OffsetDateTime),
//! [`UtcDateTime`](crate::UtcDateTime), and [`SystemTime`](std::time::SystemTime). Partial
//! interoperability is present with [`js_sys::Date`]. Note that
//! [`PrimitiveDateTime`](crate::PrimitiveDateTime) is not interoperable with any of these types due
//! to the lack of an associated UTC offset.
// Module names should have the two types sorted in alphabetical order. This avoids any question
// of which type should be the "primary" type in the module name.
#[cfg(all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
))]
mod js_sys_date_offsetdatetime;
#[cfg(all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
))]
mod js_sys_date_utcdatetime;
#[cfg(feature = "std")]
mod offsetdatetime_systemtime;
mod offsetdatetime_utcdatetime;
#[cfg(feature = "std")]
mod utcdatetime_systemtime;

View File

@@ -0,0 +1,86 @@
use core::cmp::Ordering;
use core::ops::Sub;
use std::time::SystemTime;
use crate::{Duration, OffsetDateTime};
impl Sub<SystemTime> for OffsetDateTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
fn sub(self, rhs: SystemTime) -> Self::Output {
self - Self::from(rhs)
}
}
impl Sub<OffsetDateTime> for SystemTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
OffsetDateTime::from(self) - rhs
}
}
impl PartialEq<SystemTime> for OffsetDateTime {
#[inline]
fn eq(&self, rhs: &SystemTime) -> bool {
self == &Self::from(*rhs)
}
}
impl PartialEq<OffsetDateTime> for SystemTime {
#[inline]
fn eq(&self, rhs: &OffsetDateTime) -> bool {
&OffsetDateTime::from(*self) == rhs
}
}
impl PartialOrd<SystemTime> for OffsetDateTime {
#[inline]
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
}
impl PartialOrd<OffsetDateTime> for SystemTime {
#[inline]
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
OffsetDateTime::from(*self).partial_cmp(other)
}
}
impl From<SystemTime> for OffsetDateTime {
#[inline]
fn from(system_time: SystemTime) -> Self {
match system_time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(duration) => Self::UNIX_EPOCH + duration,
Err(err) => Self::UNIX_EPOCH - err.duration(),
}
}
}
impl From<OffsetDateTime> for SystemTime {
/// # Panics
///
/// This may panic if the resulting `SystemTime` cannot be represented.
#[inline]
fn from(datetime: OffsetDateTime) -> Self {
let duration = datetime - OffsetDateTime::UNIX_EPOCH;
if duration.is_zero() {
Self::UNIX_EPOCH
} else if duration.is_positive() {
Self::UNIX_EPOCH + duration.unsigned_abs()
} else {
debug_assert!(duration.is_negative());
Self::UNIX_EPOCH - duration.unsigned_abs()
}
}
}

View File

@@ -0,0 +1,80 @@
use core::cmp::Ordering;
use core::ops::Sub;
use crate::{Duration, OffsetDateTime, UtcDateTime};
impl Sub<OffsetDateTime> for UtcDateTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn sub(self, rhs: OffsetDateTime) -> Self::Output {
OffsetDateTime::from(self) - rhs
}
}
impl Sub<UtcDateTime> for OffsetDateTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn sub(self, rhs: UtcDateTime) -> Self::Output {
self - Self::from(rhs)
}
}
impl PartialEq<OffsetDateTime> for UtcDateTime {
#[inline]
fn eq(&self, other: &OffsetDateTime) -> bool {
OffsetDateTime::from(*self) == *other
}
}
impl PartialEq<UtcDateTime> for OffsetDateTime {
#[inline]
fn eq(&self, other: &UtcDateTime) -> bool {
*self == Self::from(*other)
}
}
impl PartialOrd<OffsetDateTime> for UtcDateTime {
#[inline]
fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> {
OffsetDateTime::from(*self).partial_cmp(other)
}
}
impl PartialOrd<UtcDateTime> for OffsetDateTime {
#[inline]
fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
}
impl From<OffsetDateTime> for UtcDateTime {
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn from(datetime: OffsetDateTime) -> Self {
datetime.to_utc()
}
}
impl From<UtcDateTime> for OffsetDateTime {
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn from(datetime: UtcDateTime) -> Self {
datetime.as_primitive().assume_utc()
}
}

View File

@@ -0,0 +1,88 @@
use core::cmp::Ordering;
use core::ops::Sub;
use std::time::SystemTime;
use crate::{Duration, UtcDateTime};
impl Sub<SystemTime> for UtcDateTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn sub(self, rhs: SystemTime) -> Self::Output {
self - Self::from(rhs)
}
}
impl Sub<UtcDateTime> for SystemTime {
type Output = Duration;
/// # Panics
///
/// This may panic if an overflow occurs.
#[inline]
#[track_caller]
fn sub(self, rhs: UtcDateTime) -> Self::Output {
UtcDateTime::from(self) - rhs
}
}
impl PartialEq<SystemTime> for UtcDateTime {
#[inline]
fn eq(&self, rhs: &SystemTime) -> bool {
self == &Self::from(*rhs)
}
}
impl PartialEq<UtcDateTime> for SystemTime {
#[inline]
fn eq(&self, rhs: &UtcDateTime) -> bool {
&UtcDateTime::from(*self) == rhs
}
}
impl PartialOrd<SystemTime> for UtcDateTime {
#[inline]
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
}
impl PartialOrd<UtcDateTime> for SystemTime {
#[inline]
fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> {
UtcDateTime::from(*self).partial_cmp(other)
}
}
impl From<SystemTime> for UtcDateTime {
#[inline]
fn from(system_time: SystemTime) -> Self {
match system_time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(duration) => Self::UNIX_EPOCH + duration,
Err(err) => Self::UNIX_EPOCH - err.duration(),
}
}
}
impl From<UtcDateTime> for SystemTime {
/// # Panics
///
/// This may panic if the resulting `SystemTime` cannot be represented.
#[inline]
fn from(datetime: UtcDateTime) -> Self {
let duration = datetime - UtcDateTime::UNIX_EPOCH;
if duration.is_zero() {
Self::UNIX_EPOCH
} else if duration.is_positive() {
Self::UNIX_EPOCH + duration.unsigned_abs()
} else {
debug_assert!(duration.is_negative());
Self::UNIX_EPOCH - duration.unsigned_abs()
}
}
}

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

@@ -0,0 +1,151 @@
//! # Feature flags
//!
//! This crate exposes a number of features. These can be enabled or disabled as shown
//! [in Cargo's documentation](https://doc.rust-lang.org/cargo/reference/features.html). Features
//! are _disabled_ by default unless otherwise noted.
//!
//! Reliance on a given feature is always indicated alongside the item definition.
//!
//! - `std` (_enabled by default, implicitly enables `alloc`_)
//!
//! This enables a number of features that depend on the standard library.
//!
//! - `alloc` (_enabled by default via `std`_)
//!
//! Enables a number of features that require the ability to dynamically allocate memory.
//!
//! - `macros`
//!
//! Enables macros that provide compile-time verification of values and intuitive syntax.
//!
//! - `formatting` (_implicitly enables `std`_)
//!
//! Enables formatting of most structs.
//!
//! - `parsing`
//!
//! Enables parsing of most structs.
//!
//! - `local-offset` (_implicitly enables `std`_)
//!
//! This feature enables a number of methods that allow obtaining the system's UTC offset.
//!
//! - `large-dates`
//!
//! By default, only years within the ±9999 range (inclusive) are supported. If you need support
//! for years outside this range, consider enabling this feature; the supported range will be
//! increased to ±999,999.
//!
//! Note that enabling this feature has some costs, as it means forgoing some optimizations.
//! Ambiguities may be introduced when parsing that would not otherwise exist.
//!
//! - `serde`
//!
//! Enables [`serde`](https://docs.rs/serde) support for all types.
//!
//! - `serde-human-readable` (_implicitly enables `serde`, `formatting`, and `parsing`_)
//!
//! Allows `serde` representations to use a human-readable format. This is determined by the
//! serializer, not the user. If this feature is not enabled or if the serializer requests a
//! non-human-readable format, a format optimized for binary representation will be used.
//!
//! Libraries should never enable this feature, as the decision of what format to use should be up
//! to the user.
//!
//! - `rand` (_implicitly enables `rand08` and `rand09`_)
//!
//! Previously, this would enable support for `rand` 0.8. Since the release of `rand` 0.9, the
//! feature has been split into `rand08` and `rand09` to allow support for both versions. For
//! backwards compatibility and simplicity, this feature enables support for _both_ series.
//!
//! It is strongly recommended to enable `rand08` or `rand09` directly, as enabling `rand` will
//! needlessly pull in both versions.
//!
//! - `rand08`
//!
//! Enables [`rand` 0.8](https://docs.rs/rand/0.8) support for all types.
//!
//! - `rand09`
//!
//! Enables [`rand` 0.9](https://docs.rs/rand/0.9) support for all types.
//!
//! - `quickcheck` (_implicitly enables `alloc`_)
//!
//! Enables [quickcheck](https://docs.rs/quickcheck) support for all types.
//!
//! - `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.
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![cfg_attr(docsrs, feature(doc_cfg, doc_notable_trait))]
#![no_std]
#![doc(html_favicon_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/55999857")]
#![doc(test(attr(deny(warnings))))]
#[allow(unused_extern_crates)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod date;
mod duration;
pub mod error;
pub mod ext;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod format_description;
#[cfg(feature = "formatting")]
pub mod formatting;
mod hint;
#[cfg(feature = "std")]
mod instant;
mod internal_macros;
mod interop;
#[cfg(feature = "macros")]
pub mod macros;
mod month;
mod offset_date_time;
#[cfg(feature = "parsing")]
pub mod parsing;
mod primitive_date_time;
#[cfg(feature = "quickcheck")]
mod quickcheck;
#[cfg(feature = "rand08")]
mod rand08;
#[cfg(feature = "rand09")]
mod rand09;
#[cfg(feature = "serde")]
pub mod serde;
mod sys;
#[cfg(test)]
mod tests;
mod time;
mod utc_date_time;
mod utc_offset;
pub mod util;
mod weekday;
pub use time_core::convert;
pub use crate::date::Date;
pub use crate::duration::Duration;
pub use crate::error::Error;
#[doc(hidden)]
#[cfg(feature = "std")]
#[expect(deprecated)]
pub use crate::instant::Instant;
pub use crate::month::Month;
pub use crate::offset_date_time::OffsetDateTime;
pub use crate::primitive_date_time::PrimitiveDateTime;
pub use crate::time::Time;
pub use crate::utc_date_time::UtcDateTime;
pub use crate::utc_offset::UtcOffset;
pub use crate::weekday::Weekday;
/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result<T> = core::result::Result<T, Error>;

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

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

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

@@ -0,0 +1,274 @@
//! The `Month` enum and its associated `impl`s.
use core::fmt;
use core::num::NonZero;
use core::str::FromStr;
use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
use self::Month::*;
use crate::{error, util};
/// Months of the year.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Month {
#[expect(missing_docs)]
January = 1,
#[expect(missing_docs)]
February = 2,
#[expect(missing_docs)]
March = 3,
#[expect(missing_docs)]
April = 4,
#[expect(missing_docs)]
May = 5,
#[expect(missing_docs)]
June = 6,
#[expect(missing_docs)]
July = 7,
#[expect(missing_docs)]
August = 8,
#[expect(missing_docs)]
September = 9,
#[expect(missing_docs)]
October = 10,
#[expect(missing_docs)]
November = 11,
#[expect(missing_docs)]
December = 12,
}
impl Month {
/// Create a `Month` from its numerical value.
#[inline]
pub(crate) const fn from_number(n: NonZero<u8>) -> Result<Self, error::ComponentRange> {
match n.get() {
1 => Ok(January),
2 => Ok(February),
3 => Ok(March),
4 => Ok(April),
5 => Ok(May),
6 => Ok(June),
7 => Ok(July),
8 => Ok(August),
9 => Ok(September),
10 => Ok(October),
11 => Ok(November),
12 => Ok(December),
_ => Err(error::ComponentRange::unconditional("month")),
}
}
/// Get the number of days in the month of a given year.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::February.length(2020), 29);
/// ```
#[inline]
pub const fn length(self, year: i32) -> u8 {
util::days_in_month(self, year)
}
/// Get the previous month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.previous(), Month::December);
/// ```
#[inline]
pub const fn previous(self) -> Self {
match self {
January => December,
February => January,
March => February,
April => March,
May => April,
June => May,
July => June,
August => July,
September => August,
October => September,
November => October,
December => November,
}
}
/// Get the next month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.next(), Month::February);
/// ```
#[inline]
pub const fn next(self) -> Self {
match self {
January => February,
February => March,
March => April,
April => May,
May => June,
June => July,
July => August,
August => September,
September => October,
October => November,
November => December,
December => January,
}
}
/// Get n-th next month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.nth_next(4), Month::May);
/// assert_eq!(Month::July.nth_next(9), Month::April);
/// ```
#[inline]
pub const fn nth_next(self, n: u8) -> Self {
match (self as u8 - 1 + n % 12) % 12 {
0 => January,
1 => February,
2 => March,
3 => April,
4 => May,
5 => June,
6 => July,
7 => August,
8 => September,
9 => October,
10 => November,
val => {
debug_assert!(val == 11);
December
}
}
}
/// Get n-th previous month.
///
/// ```rust
/// # use time::Month;
/// assert_eq!(Month::January.nth_prev(4), Month::September);
/// assert_eq!(Month::July.nth_prev(9), Month::October);
/// ```
#[inline]
pub const fn nth_prev(self, n: u8) -> Self {
match self as i8 - 1 - (n % 12).cast_signed() {
1 | -11 => February,
2 | -10 => March,
3 | -9 => April,
4 | -8 => May,
5 | -7 => June,
6 | -6 => July,
7 | -5 => August,
8 | -4 => September,
9 | -3 => October,
10 | -2 => November,
11 | -1 => December,
val => {
debug_assert!(val == 0);
January
}
}
}
}
mod private {
/// Metadata for `Month`.
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
pub struct MonthMetadata;
}
use private::MonthMetadata;
impl SmartDisplay for Month {
type Metadata = MonthMetadata;
#[inline]
fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
match self {
January => Metadata::new(7, self, MonthMetadata),
February => Metadata::new(8, self, MonthMetadata),
March => Metadata::new(5, self, MonthMetadata),
April => Metadata::new(5, self, MonthMetadata),
May => Metadata::new(3, self, MonthMetadata),
June => Metadata::new(4, self, MonthMetadata),
July => Metadata::new(4, self, MonthMetadata),
August => Metadata::new(6, self, MonthMetadata),
September => Metadata::new(9, self, MonthMetadata),
October => Metadata::new(7, self, MonthMetadata),
November => Metadata::new(8, self, MonthMetadata),
December => Metadata::new(8, self, MonthMetadata),
}
}
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(match self {
January => "January",
February => "February",
March => "March",
April => "April",
May => "May",
June => "June",
July => "July",
August => "August",
September => "September",
October => "October",
November => "November",
December => "December",
})
}
}
impl fmt::Display for Month {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
SmartDisplay::fmt(self, f)
}
}
impl FromStr for Month {
type Err = error::InvalidVariant;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"January" => Ok(January),
"February" => Ok(February),
"March" => Ok(March),
"April" => Ok(April),
"May" => Ok(May),
"June" => Ok(June),
"July" => Ok(July),
"August" => Ok(August),
"September" => Ok(September),
"October" => Ok(October),
"November" => Ok(November),
"December" => Ok(December),
_ => Err(error::InvalidVariant),
}
}
}
impl From<Month> for u8 {
#[inline]
fn from(month: Month) -> Self {
month as Self
}
}
impl TryFrom<u8> for Month {
type Error = error::ComponentRange;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
match NonZero::new(value) {
Some(value) => Self::from_number(value),
None => Err(error::ComponentRange::unconditional("month")),
}
}
}

1745
vendor/time/src/offset_date_time.rs vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,448 @@
//! Implementations of the low-level parser combinators.
pub(crate) mod rfc;
use crate::format_description::modifier::Padding;
use crate::parsing::ParsedItem;
use crate::parsing::shim::Integer;
/// The sign of a number.
#[allow(
clippy::missing_docs_in_private_items,
reason = "self-explanatory variants"
)]
#[derive(Debug)]
pub(crate) enum Sign {
Negative,
Positive,
}
/// Parse a "+" or "-" sign.
#[inline]
pub(crate) const fn sign(input: &[u8]) -> Option<ParsedItem<'_, Sign>> {
match input {
[b'-', remaining @ ..] => Some(ParsedItem(remaining, Sign::Negative)),
[b'+', remaining @ ..] => Some(ParsedItem(remaining, Sign::Positive)),
_ => None,
}
}
/// Consume zero or more instances of the provided parser. The parser must return the unit value.
#[inline]
pub(crate) fn zero_or_more<P>(parser: P) -> impl for<'a> FnMut(&'a [u8]) -> ParsedItem<'a, ()>
where
P: for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>,
{
move |mut input| {
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
ParsedItem(input, ())
}
}
/// Consume one of or more instances of the provided parser. The parser must produce the unit value.
#[inline]
pub(crate) fn one_or_more<P>(parser: P) -> impl for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>
where
P: for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>,
{
move |mut input| {
input = parser(input)?.into_inner();
while let Some(remaining) = parser(input) {
input = remaining.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume between `n` and `m` digits, returning the numerical value.
#[inline]
pub(crate) fn n_to_m_digits<const N: u8, const M: u8, T>(
mut input: &[u8],
) -> Option<ParsedItem<'_, T>>
where
T: Integer,
{
const {
assert!(N > 0);
assert!(M >= N);
}
let mut value = T::ZERO;
// Mandatory
for i in 0..N {
let digit;
ParsedItem(input, digit) = any_digit(input)?;
if i != T::MAX_NUM_DIGITS - 1 {
value = value.push_digit(digit - b'0');
} else {
value = value.checked_push_digit(digit - b'0')?;
}
}
// Optional
for i in N..M {
let Some(ParsedItem(new_input, digit)) = any_digit(input) else {
break;
};
input = new_input;
if i != T::MAX_NUM_DIGITS - 1 {
value = value.push_digit(digit - b'0');
} else {
value = value.checked_push_digit(digit - b'0')?;
}
}
Some(ParsedItem(input, value))
}
/// Consume one or two digits, returning the numerical value.
#[inline]
pub(crate) fn one_or_two_digits(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[a @ b'0'..=b'9', b @ b'0'..=b'9', remaining @ ..] => {
let a = *a - b'0';
let b = *b - b'0';
Some(ParsedItem(remaining, a * 10 + b))
}
[a @ b'0'..=b'9', remaining @ ..] => {
let a = *a - b'0';
Some(ParsedItem(remaining, a))
}
_ => None,
}
}
/// Parse an exact number of digits without padding.
#[derive(Debug)]
pub(crate) struct ExactlyNDigits<const N: u8>;
impl ExactlyNDigits<1> {
/// Consume exactly one digit.
#[inline]
pub(crate) const fn parse(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[a @ b'0'..=b'9', remaining @ ..] => Some(ParsedItem(remaining, *a - b'0')),
_ => None,
}
}
}
impl ExactlyNDigits<2> {
/// Consume exactly two digits.
#[inline]
pub(crate) const fn parse(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[a @ b'0'..=b'9', b @ b'0'..=b'9', remaining @ ..] => {
let a = *a - b'0';
let b = *b - b'0';
Some(ParsedItem(remaining, a * 10 + b))
}
_ => None,
}
}
}
impl ExactlyNDigits<3> {
/// Consume exactly three digits.
#[inline]
pub(crate) const fn parse(input: &[u8]) -> Option<ParsedItem<'_, u16>> {
match input {
[
a @ b'0'..=b'9',
b @ b'0'..=b'9',
c @ b'0'..=b'9',
remaining @ ..,
] => {
let a = (*a - b'0') as u16;
let b = (*b - b'0') as u16;
let c = (*c - b'0') as u16;
Some(ParsedItem(remaining, a * 100 + b * 10 + c))
}
_ => None,
}
}
}
impl ExactlyNDigits<4> {
/// Consume exactly four digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u16>> {
let [a, b, c, d, remaining @ ..] = input else {
return None;
};
let digits = [a, b, c, d].map(|d| (*d as u16).wrapping_sub(b'0' as u16));
if digits.iter().any(|&digit| digit > 9) {
return None;
}
let value = digits[0] * 1000 + digits[1] * 100 + digits[2] * 10 + digits[3];
Some(ParsedItem(remaining, value))
}
}
impl ExactlyNDigits<5> {
/// Consume exactly five digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u32>> {
let [a, b, c, d, e, remaining @ ..] = input else {
return None;
};
let digits = [a, b, c, d, e].map(|d| (*d as u32).wrapping_sub(b'0' as u32));
if digits.iter().any(|&digit| digit > 9) {
return None;
}
let value =
digits[0] * 10_000 + digits[1] * 1_000 + digits[2] * 100 + digits[3] * 10 + digits[4];
Some(ParsedItem(remaining, value))
}
}
impl ExactlyNDigits<6> {
/// Consume exactly six digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u32>> {
let [a, b, c, d, e, f, remaining @ ..] = input else {
return None;
};
// Calling `.map` successively results in slightly better codegen.
let digits = [a, b, c, d, e, f]
.map(|d| *d as u32)
.map(|d| d.wrapping_sub(b'0' as u32));
if digits.iter().any(|&digit| digit > 9) {
return None;
}
let value = digits[0] * 100_000
+ digits[1] * 10_000
+ digits[2] * 1_000
+ digits[3] * 100
+ digits[4] * 10
+ digits[5];
Some(ParsedItem(remaining, value))
}
}
impl ExactlyNDigits<7> {
/// Consume exactly seven digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u32>> {
let [a, b, c, d, e, f, g, remaining @ ..] = input else {
return None;
};
// For whatever reason, the compiler does *not* autovectorize if `.map` is applied directly.
let mut digits = [*a, *b, *c, *d, *e, *f, *g];
digits = digits.map(|d| d.wrapping_sub(b'0'));
if digits.iter().any(|&digit| digit > 9) {
return None;
}
let value = digits[0] as u32 * 1_000_000
+ digits[1] as u32 * 100_000
+ digits[2] as u32 * 10_000
+ digits[3] as u32 * 1_000
+ digits[4] as u32 * 100
+ digits[5] as u32 * 10
+ digits[6] as u32;
Some(ParsedItem(remaining, value))
}
}
impl ExactlyNDigits<8> {
/// Consume exactly eight digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u32>> {
let [a, b, c, d, e, f, g, h, remaining @ ..] = input else {
return None;
};
let mut digits = [*a, *b, *c, *d, *e, *f, *g, *h];
digits = [
digits[0].wrapping_sub(b'0'),
digits[1].wrapping_sub(b'0'),
digits[2].wrapping_sub(b'0'),
digits[3].wrapping_sub(b'0'),
digits[4].wrapping_sub(b'0'),
digits[5].wrapping_sub(b'0'),
digits[6].wrapping_sub(b'0'),
digits[7].wrapping_sub(b'0'),
];
if digits.iter().any(|&digit| digit > 9) {
return None;
}
let value = digits[0] as u32 * 10_000_000
+ digits[1] as u32 * 1_000_000
+ digits[2] as u32 * 100_000
+ digits[3] as u32 * 10_000
+ digits[4] as u32 * 1_000
+ digits[5] as u32 * 100
+ digits[6] as u32 * 10
+ digits[7] as u32;
Some(ParsedItem(remaining, value))
}
}
impl ExactlyNDigits<9> {
/// Consume exactly nine digits.
#[inline]
pub(crate) fn parse(input: &[u8]) -> Option<ParsedItem<'_, u32>> {
let [a, b, c, d, e, f, g, h, i, remaining @ ..] = input else {
return None;
};
let mut digits = [*a, *b, *c, *d, *e, *f, *g, *h];
digits = [
digits[0] - b'0',
digits[1] - b'0',
digits[2] - b'0',
digits[3] - b'0',
digits[4] - b'0',
digits[5] - b'0',
digits[6] - b'0',
digits[7] - b'0',
];
let ones_digit = (*i as u32).wrapping_sub(b'0' as u32);
if digits.iter().any(|&digit| digit > 9) || ones_digit > 9 {
return None;
}
let value = digits[0] as u32 * 100_000_000
+ digits[1] as u32 * 10_000_000
+ digits[2] as u32 * 1_000_000
+ digits[3] as u32 * 100_000
+ digits[4] as u32 * 10_000
+ digits[5] as u32 * 1_000
+ digits[6] as u32 * 100
+ digits[7] as u32 * 10
+ ones_digit;
Some(ParsedItem(remaining, value))
}
}
/// Consume exactly `n` digits, returning the numerical value.
pub(crate) fn exactly_n_digits_padded<const N: u8, T>(
padding: Padding,
) -> impl for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>
where
T: Integer,
{
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<const N: u8, const M: u8, T>(
padding: Padding,
) -> impl for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>
where
T: Integer,
{
const {
assert!(N > 0);
assert!(M >= N);
}
move |mut input| match padding {
Padding::None => n_to_m_digits::<1, M, _>(input),
Padding::Space => {
let mut value = T::ZERO;
// Consume the padding.
let mut pad_width = 0;
for _ in 0..(N - 1) {
match ascii_char::<b' '>(input) {
Some(parsed) => {
pad_width += 1;
input = parsed.0;
}
None => break,
}
}
// Mandatory
for i in 0..(N - pad_width) {
let digit;
ParsedItem(input, digit) = any_digit(input)?;
value = if i != T::MAX_NUM_DIGITS - 1 {
value.push_digit(digit - b'0')
} else {
value.checked_push_digit(digit - b'0')?
};
}
// Optional
for i in N..M {
let Some(ParsedItem(new_input, digit)) = any_digit(input) else {
break;
};
input = new_input;
value = if i - pad_width != T::MAX_NUM_DIGITS - 1 {
value.push_digit(digit - b'0')
} else {
value.checked_push_digit(digit - b'0')?
};
}
Some(ParsedItem(input, value))
}
Padding::Zero => n_to_m_digits::<N, M, _>(input),
}
}
/// Consume exactly one digit.
#[inline]
pub(crate) const fn any_digit(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
match input {
[c @ b'0'..=b'9', remaining @ ..] => Some(ParsedItem(remaining, *c)),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters.
#[inline]
pub(crate) fn ascii_char<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
const {
assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
}
match input {
[c, remaining @ ..] if *c == CHAR => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Consume exactly one of the provided ASCII characters, case-insensitive.
#[inline]
pub(crate) fn ascii_char_ignore_case<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
const {
assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace());
}
match input {
[c, remaining @ ..] if c.eq_ignore_ascii_case(&CHAR) => Some(ParsedItem(remaining, ())),
_ => None,
}
}
/// Optionally consume an input with a given parser.
#[inline]
pub(crate) fn opt<T>(
parser: impl for<'a> Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>,
) -> impl for<'a> Fn(&'a [u8]) -> ParsedItem<'a, Option<T>> {
move |input| match parser(input) {
Some(value) => value.map(Some),
None => ParsedItem(input, None),
}
}

View File

@@ -0,0 +1,191 @@
//! Rules defined in [ISO 8601].
//!
//! [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html
use core::num::NonZero;
use num_conv::prelude::*;
use crate::parsing::ParsedItem;
use crate::parsing::combinator::{ExactlyNDigits, Sign, any_digit, sign};
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?
#[inline]
pub(crate) const fn maybe_extended(self) -> bool {
matches!(self, Self::Extended | Self::Unknown)
}
/// Is the format known for certain to be extended?
#[inline]
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`.
#[inline]
pub(crate) const 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`.
#[inline]
pub(crate) const 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.
#[inline]
pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> {
Some(match sign(input) {
Some(ParsedItem(input, sign)) => ExactlyNDigits::<6>::parse(input)?.map(|val| {
let val = val.cast_signed();
match sign {
Sign::Negative => -val,
Sign::Positive => val,
}
}),
None => ExactlyNDigits::<4>::parse(input)?.map(|val| val.cast_signed().extend()),
})
}
/// Parse a month.
#[inline]
pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> {
match input {
[b'0', b'1', remaining @ ..] => Some(ParsedItem(remaining, Month::January)),
[b'0', b'2', remaining @ ..] => Some(ParsedItem(remaining, Month::February)),
[b'0', b'3', remaining @ ..] => Some(ParsedItem(remaining, Month::March)),
[b'0', b'4', remaining @ ..] => Some(ParsedItem(remaining, Month::April)),
[b'0', b'5', remaining @ ..] => Some(ParsedItem(remaining, Month::May)),
[b'0', b'6', remaining @ ..] => Some(ParsedItem(remaining, Month::June)),
[b'0', b'7', remaining @ ..] => Some(ParsedItem(remaining, Month::July)),
[b'0', b'8', remaining @ ..] => Some(ParsedItem(remaining, Month::August)),
[b'0', b'9', remaining @ ..] => Some(ParsedItem(remaining, Month::September)),
[b'1', b'0', remaining @ ..] => Some(ParsedItem(remaining, Month::October)),
[b'1', b'1', remaining @ ..] => Some(ParsedItem(remaining, Month::November)),
[b'1', b'2', remaining @ ..] => Some(ParsedItem(remaining, Month::December)),
_ => None,
}
}
/// Parse a week number.
#[inline]
pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
ExactlyNDigits::<2>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
}
/// Parse a day of the month.
#[inline]
pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u8>>> {
ExactlyNDigits::<2>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
}
/// Parse a day of the week.
#[inline]
pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> {
match input {
[b'1', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Monday)),
[b'2', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Tuesday)),
[b'3', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Wednesday)),
[b'4', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Thursday)),
[b'5', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Friday)),
[b'6', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Saturday)),
[b'7', remaining @ ..] => Some(ParsedItem(remaining, Weekday::Sunday)),
_ => None,
}
}
/// Parse a day of the year.
#[inline]
pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZero<u16>>> {
ExactlyNDigits::<3>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new))
}
/// Parse the hour.
#[inline]
pub(crate) const fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
ExactlyNDigits::<2>::parse(input)
}
/// Parse the minute.
#[inline]
pub(crate) const fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> {
ExactlyNDigits::<2>::parse(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.
#[inline]
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.
#[inline]
fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[b'.' | b',', remaining @ ..] => Some(ParsedItem(remaining, ())),
_ => None,
}
}

View File

@@ -0,0 +1,10 @@
//! Combinators for rules as defined in a standard.
//!
//! 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;

View File

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

View File

@@ -0,0 +1,199 @@
//! Rules defined in [RFC 2822].
//!
//! [RFC 2822]: https://datatracker.ietf.org/doc/html/rfc2822
use num_conv::prelude::*;
use crate::parsing::ParsedItem;
use crate::parsing::combinator::rfc::rfc2234::wsp;
use crate::parsing::combinator::{ascii_char, one_or_more, zero_or_more};
const DEPTH_LIMIT: u8 = 32;
/// Consume the `fws` rule.
// The full rule is equivalent to /\r\n[ \t]+|[ \t]+(?:\r\n[ \t]+)*/
#[inline]
pub(crate) fn fws(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
if let [b'\r', b'\n', rest @ ..] = input {
one_or_more(wsp)(rest)
} else {
input = one_or_more(wsp)(input)?.into_inner();
while let [b'\r', b'\n', rest @ ..] = input {
input = one_or_more(wsp)(rest)?.into_inner();
}
Some(ParsedItem(input, ()))
}
}
/// Consume the `cfws` rule.
// The full rule is equivalent to any combination of `fws` and `comment` so long as it is not empty.
#[inline]
pub(crate) fn cfws(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
one_or_more(|input| fws(input).or_else(|| comment(input, 1)))(input)
}
/// Consume the `comment` rule.
#[inline]
fn comment(mut input: &[u8], depth: u8) -> Option<ParsedItem<'_, ()>> {
// Avoid stack exhaustion DoS by limiting recursion depth. This will cause highly-nested
// comments to fail parsing, but comments *at all* are incredibly rare in practice.
//
// The error from this will not be descriptive, but the rarity and near-certain maliciousness of
// such inputs makes this an acceptable trade-off.
if depth == DEPTH_LIMIT {
return None;
}
input = ascii_char::<b'('>(input)?.into_inner();
input = zero_or_more(fws)(input).into_inner();
while let Some(rest) = ccontent(input, depth + 1) {
input = rest.into_inner();
input = zero_or_more(fws)(input).into_inner();
}
input = ascii_char::<b')'>(input)?.into_inner();
Some(ParsedItem(input, ()))
}
/// Consume the `ccontent` rule.
#[inline]
fn ccontent(input: &[u8], depth: u8) -> Option<ParsedItem<'_, ()>> {
ctext(input)
.or_else(|| quoted_pair(input))
.or_else(|| comment(input, depth))
}
/// Consume the `ctext` rule.
#[expect(
clippy::unnecessary_lazy_evaluations,
reason = "rust-lang/rust-clippy#8522"
)]
#[inline]
fn ctext(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
no_ws_ctl(input).or_else(|| match input {
[33..=39 | 42..=91 | 93..=126, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
})
}
/// Consume the `quoted_pair` rule.
#[inline]
fn quoted_pair(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> {
input = ascii_char::<b'\\'>(input)?.into_inner();
input = text(input).into_inner();
// If nothing is parsed by `text`, this means by hit the `obs-text` rule and nothing matched.
// This is technically a success, and we used to check the `obs-qp` rule to ensure everything
// possible was consumed. After further analysis, it was determined that this check was
// unnecessary due to `obs-text` wholly subsuming `obs-qp` in this context. For this reason, if
// `text` fails to parse anything, we consider it a success without further consideration.
Some(ParsedItem(input, ()))
}
/// Consume the `no_ws_ctl` rule.
#[inline]
const fn no_ws_ctl(input: &[u8]) -> Option<ParsedItem<'_, ()>> {
match input {
[1..=8 | 11..=12 | 14..=31 | 127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
}
}
/// Consume the `text` rule.
#[inline]
fn text<'a>(input: &'a [u8]) -> ParsedItem<'a, ()> {
let new_text = |input: &'a [u8]| match input {
[1..=9 | 11..=12 | 14..=127, rest @ ..] => Some(ParsedItem(rest, ())),
_ => None,
};
let obs_char = |input: &'a [u8]| match input {
// This is technically allowed, but consuming this would mean the rest of the string is
// eagerly consumed without consideration for where the comment actually ends.
[b')', ..] => None,
[0..=9 | 11..=12 | 14..=127, rest @ ..] => Some(rest),
_ => None,
};
let obs_text = |mut input| {
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
while let Some(rest) = obs_char(input) {
input = rest;
input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner();
input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner();
}
ParsedItem(input, ())
};
new_text(input).unwrap_or_else(|| obs_text(input))
}
/// Consume an old zone literal, returning the offset in hours.
#[inline]
pub(crate) fn zone_literal(input: &[u8]) -> Option<ParsedItem<'_, i8>> {
let [first, second, third, rest @ ..] = input else {
const UT_VARIANTS: [u16; 4] = [
u16::from_ne_bytes([b'u', b't']),
u16::from_ne_bytes([b'u', b'T']),
u16::from_ne_bytes([b'U', b't']),
u16::from_ne_bytes([b'U', b'T']),
];
let [first, rest @ ..] = input else {
return None;
};
if let [second, rest @ ..] = rest
&& UT_VARIANTS.contains(&u16::from_ne_bytes([*first, *second]))
{
return Some(ParsedItem(rest, 0));
}
return (*first != b'j' && *first != b'J' && first.is_ascii_alphabetic())
.then_some(ParsedItem(rest, 0));
};
let byte = u32::from_ne_bytes([
0,
first.to_ascii_lowercase(),
second.to_ascii_lowercase(),
third.to_ascii_lowercase(),
]);
const ZONES: [u32; 8] = [
u32::from_ne_bytes([0, b'e', b's', b't']),
u32::from_ne_bytes([0, b'e', b'd', b't']),
u32::from_ne_bytes([0, b'c', b's', b't']),
u32::from_ne_bytes([0, b'c', b'd', b't']),
u32::from_ne_bytes([0, b'm', b's', b't']),
u32::from_ne_bytes([0, b'm', b'd', b't']),
u32::from_ne_bytes([0, b'p', b's', b't']),
u32::from_ne_bytes([0, b'p', b'd', b't']),
];
let eq = [
if ZONES[0] == byte { i32::MAX } else { 0 },
if ZONES[1] == byte { i32::MAX } else { 0 },
if ZONES[2] == byte { i32::MAX } else { 0 },
if ZONES[3] == byte { i32::MAX } else { 0 },
if ZONES[4] == byte { i32::MAX } else { 0 },
if ZONES[5] == byte { i32::MAX } else { 0 },
if ZONES[6] == byte { i32::MAX } else { 0 },
if ZONES[7] == byte { i32::MAX } else { 0 },
];
if eq == [0; 8] && byte != const { u32::from_ne_bytes([0, b'g', b'm', b't']) } {
return None;
}
let nonzero_zones = [
eq[0] & -5,
eq[1] & -4,
eq[2] & -6,
eq[3] & -5,
eq[4] & -7,
eq[5] & -6,
eq[6] & -8,
eq[7] & -7,
];
let zone = nonzero_zones.iter().sum::<i32>().truncate();
Some(ParsedItem(rest, zone))
}

486
vendor/time/src/parsing/component.rs vendored Normal file
View File

@@ -0,0 +1,486 @@
//! Parsing implementations for all [`Component`](crate::format_description::Component)s.
use core::num::NonZero;
use num_conv::prelude::*;
use crate::convert::*;
use crate::format_description::{Period, modifier};
use crate::parsing::ParsedItem;
use crate::parsing::combinator::{
ExactlyNDigits, Sign, any_digit, exactly_n_digits_padded, n_to_m_digits, n_to_m_digits_padded,
opt, sign,
};
use crate::{Month, Weekday};
/// Parse the "year" component of a `Date`.
pub(crate) fn parse_year(
input: &[u8],
modifiers: modifier::Year,
) -> Option<ParsedItem<'_, (i32, bool)>> {
match modifiers.repr {
modifier::YearRepr::Full => {
let ParsedItem(input, sign) = opt(sign)(input);
if let Some(sign) = sign {
let ParsedItem(input, year) = if cfg!(feature = "large-dates")
&& modifiers.range == modifier::YearRange::Extended
{
n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)?
} else {
exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?
};
Some(ParsedItem(
input,
match sign {
Sign::Negative => (-year.cast_signed(), true),
Sign::Positive => (year.cast_signed(), false),
},
))
} else if modifiers.sign_is_mandatory {
None
} else {
let ParsedItem(input, year) =
exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)?;
Some(ParsedItem(input, (year.cast_signed(), false)))
}
}
modifier::YearRepr::Century => {
let ParsedItem(input, sign) = opt(sign)(input);
if let Some(sign) = sign {
let ParsedItem(input, year) = if cfg!(feature = "large-dates")
&& modifiers.range == modifier::YearRange::Extended
{
n_to_m_digits_padded::<2, 4, u32>(modifiers.padding)(input)?
} else {
exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?
};
Some(ParsedItem(
input,
match sign {
Sign::Negative => (-year.cast_signed(), true),
Sign::Positive => (year.cast_signed(), false),
},
))
} else if modifiers.sign_is_mandatory {
None
} else {
let ParsedItem(input, year) =
n_to_m_digits_padded::<1, 2, u32>(modifiers.padding)(input)?;
Some(ParsedItem(input, (year.cast_signed(), false)))
}
}
modifier::YearRepr::LastTwo => Some(
exactly_n_digits_padded::<2, u32>(modifiers.padding)(input)?
.map(|v| (v.cast_signed(), false)),
),
}
}
/// Parse the "month" component of a `Date`.
#[inline]
pub(crate) fn parse_month(
input: &[u8],
modifiers: modifier::Month,
) -> Option<ParsedItem<'_, Month>> {
use Month::*;
match modifiers.repr {
modifier::MonthRepr::Numerical => {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)?
.flat_map(|n| Month::from_number(NonZero::new(n)?).ok())
}
modifier::MonthRepr::Long | modifier::MonthRepr::Short => {
let [first, second, third, rest @ ..] = input else {
return None;
};
let byte = if modifiers.case_sensitive {
u32::from_ne_bytes([0, *first, *second, *third])
} else {
u32::from_ne_bytes([
0,
first.to_ascii_uppercase(),
second.to_ascii_lowercase(),
third.to_ascii_lowercase(),
])
};
const WEEKDAYS: [u32; 12] = [
u32::from_ne_bytes([0, b'J', b'a', b'n']),
u32::from_ne_bytes([0, b'F', b'e', b'b']),
u32::from_ne_bytes([0, b'M', b'a', b'r']),
u32::from_ne_bytes([0, b'A', b'p', b'r']),
u32::from_ne_bytes([0, b'M', b'a', b'y']),
u32::from_ne_bytes([0, b'J', b'u', b'n']),
u32::from_ne_bytes([0, b'J', b'u', b'l']),
u32::from_ne_bytes([0, b'A', b'u', b'g']),
u32::from_ne_bytes([0, b'S', b'e', b'p']),
u32::from_ne_bytes([0, b'O', b'c', b't']),
u32::from_ne_bytes([0, b'N', b'o', b'v']),
u32::from_ne_bytes([0, b'D', b'e', b'c']),
];
let bitmask = ((WEEKDAYS[0] == byte) as u32) << 1
| ((WEEKDAYS[1] == byte) as u32) << 2
| ((WEEKDAYS[2] == byte) as u32) << 3
| ((WEEKDAYS[3] == byte) as u32) << 4
| ((WEEKDAYS[4] == byte) as u32) << 5
| ((WEEKDAYS[5] == byte) as u32) << 6
| ((WEEKDAYS[6] == byte) as u32) << 7
| ((WEEKDAYS[7] == byte) as u32) << 8
| ((WEEKDAYS[8] == byte) as u32) << 9
| ((WEEKDAYS[9] == byte) as u32) << 10
| ((WEEKDAYS[10] == byte) as u32) << 11
| ((WEEKDAYS[11] == byte) as u32) << 12;
if bitmask == 0 {
return None;
}
let index = if cfg!(target_endian = "little") {
bitmask.trailing_zeros() as u8
} else {
31 - bitmask.leading_zeros() as u8
};
// Safety: `index` cannot be greater than 12 because there are only 12 elements in the
// array that is converted to a bitmask. We know at least one element matched because
// the bitmask is non-zero.
let month = unsafe { Month::from_number(NonZero::new(index)?).unwrap_unchecked() };
// For the "short" repr, we've already validated the full text expected. For the "long"
// repr, we need to validate the remaining characters.
if modifiers.repr == modifier::MonthRepr::Short {
return Some(ParsedItem(rest, month));
}
let expected_remaining = match month {
January => b"uary".as_slice(),
February => b"ruary".as_slice(),
March => b"ch".as_slice(),
April => b"il".as_slice(),
May => b"".as_slice(),
June => b"e".as_slice(),
July => b"y".as_slice(),
August => b"ust".as_slice(),
September => b"tember".as_slice(),
October => b"ober".as_slice(),
November | December => b"ember".as_slice(),
};
if modifiers.case_sensitive {
rest.strip_prefix(expected_remaining)
.map(|remaining| ParsedItem(remaining, month))
} else {
let (head, tail) = rest.split_at_checked(expected_remaining.len())?;
core::iter::zip(head, expected_remaining)
.all(|(a, b)| a.eq_ignore_ascii_case(b))
.then_some(ParsedItem(tail, month))
}
}
}
}
/// Parse the "week number" component of a `Date`.
pub(crate) fn parse_week_number(
input: &[u8],
modifiers: modifier::WeekNumber,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "weekday" component of a `Date`.
#[inline]
pub(crate) fn parse_weekday(
input: &[u8],
modifiers: modifier::Weekday,
) -> Option<ParsedItem<'_, Weekday>> {
match modifiers.repr {
modifier::WeekdayRepr::Long | modifier::WeekdayRepr::Short => {
let [first, second, third, rest @ ..] = input else {
return None;
};
let byte = if modifiers.case_sensitive {
u32::from_ne_bytes([0, *first, *second, *third])
} else {
u32::from_ne_bytes([
0,
first.to_ascii_uppercase(),
second.to_ascii_lowercase(),
third.to_ascii_lowercase(),
])
};
const WEEKDAYS: [u32; 7] = [
u32::from_ne_bytes([0, b'M', b'o', b'n']),
u32::from_ne_bytes([0, b'T', b'u', b'e']),
u32::from_ne_bytes([0, b'W', b'e', b'd']),
u32::from_ne_bytes([0, b'T', b'h', b'u']),
u32::from_ne_bytes([0, b'F', b'r', b'i']),
u32::from_ne_bytes([0, b'S', b'a', b't']),
u32::from_ne_bytes([0, b'S', b'u', b'n']),
];
let bitmask = ((WEEKDAYS[0] == byte) as u32)
| ((WEEKDAYS[1] == byte) as u32) << 1
| ((WEEKDAYS[2] == byte) as u32) << 2
| ((WEEKDAYS[3] == byte) as u32) << 3
| ((WEEKDAYS[4] == byte) as u32) << 4
| ((WEEKDAYS[5] == byte) as u32) << 5
| ((WEEKDAYS[6] == byte) as u32) << 6;
if bitmask == 0 {
return None;
}
let index = if cfg!(target_endian = "little") {
bitmask.trailing_zeros()
} else {
31 - bitmask.leading_zeros()
};
if index > 6 {
return None;
}
// Safety: Values zero thru six are valid variants, while values greater than six have
// already been excluded above. We know at least one element matched because the bitmask
// is non-zero.
let weekday = unsafe { core::mem::transmute::<u8, Weekday>(index.truncate()) };
// For the "short" repr, we've already validated the full text expected. For the "long"
// repr, we need to validate the remaining characters.
if modifiers.repr == modifier::WeekdayRepr::Short {
return Some(ParsedItem(rest, weekday));
}
let expected_remaining = match weekday {
Weekday::Monday | Weekday::Friday | Weekday::Sunday => b"day".as_slice(),
Weekday::Tuesday => b"sday".as_slice(),
Weekday::Wednesday => b"nesday".as_slice(),
Weekday::Thursday => b"rsday".as_slice(),
Weekday::Saturday => b"urday".as_slice(),
};
if modifiers.case_sensitive {
rest.strip_prefix(expected_remaining)
.map(|remaining| ParsedItem(remaining, weekday))
} else {
let (head, tail) = rest.split_at_checked(expected_remaining.len())?;
core::iter::zip(head, expected_remaining)
.all(|(a, b)| a.eq_ignore_ascii_case(b))
.then_some(ParsedItem(tail, weekday))
}
}
modifier::WeekdayRepr::Sunday | modifier::WeekdayRepr::Monday => {
let [digit, rest @ ..] = input else {
return None;
};
let mut digit = digit
.wrapping_sub(b'0')
.wrapping_sub(u8::from(modifiers.one_indexed));
if digit > 6 {
return None;
}
if modifiers.repr == modifier::WeekdayRepr::Sunday {
// Remap so that Sunday comes after Saturday, not before Monday.
digit = (digit + 6) % 7;
}
// Safety: Values zero thru six are valid variants.
let weekday = unsafe { core::mem::transmute::<u8, Weekday>(digit) };
Some(ParsedItem(rest, weekday))
}
}
}
/// Parse the "ordinal" component of a `Date`.
#[inline]
pub(crate) fn parse_ordinal(
input: &[u8],
modifiers: modifier::Ordinal,
) -> Option<ParsedItem<'_, NonZero<u16>>> {
exactly_n_digits_padded::<3, _>(modifiers.padding)(input)
.and_then(|parsed| parsed.flat_map(NonZero::new))
}
/// Parse the "day" component of a `Date`.
#[inline]
pub(crate) fn parse_day(
input: &[u8],
modifiers: modifier::Day,
) -> Option<ParsedItem<'_, NonZero<u8>>> {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
.and_then(|parsed| parsed.flat_map(NonZero::new))
}
/// Parse the "hour" component of a `Time`.
#[inline]
pub(crate) fn parse_hour(input: &[u8], modifiers: modifier::Hour) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "minute" component of a `Time`.
#[inline]
pub(crate) fn parse_minute(
input: &[u8],
modifiers: modifier::Minute,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "second" component of a `Time`.
#[inline]
pub(crate) fn parse_second(
input: &[u8],
modifiers: modifier::Second,
) -> Option<ParsedItem<'_, u8>> {
exactly_n_digits_padded::<2, _>(modifiers.padding)(input)
}
/// Parse the "period" component of a `Time`. Required if the hour is on a 12-hour clock.
#[inline]
pub(crate) fn parse_period(
input: &[u8],
modifiers: modifier::Period,
) -> Option<ParsedItem<'_, Period>> {
let [first, second, rest @ ..] = input else {
return None;
};
let mut first = *first;
let mut second = *second;
if modifiers.is_uppercase && modifiers.case_sensitive {
match [first, second].as_slice() {
b"AM" => Some(ParsedItem(rest, Period::Am)),
b"PM" => Some(ParsedItem(rest, Period::Pm)),
_ => None,
}
} else {
first = first.to_ascii_lowercase();
second = second.to_ascii_lowercase();
match &[first, second] {
b"am" => Some(ParsedItem(rest, Period::Am)),
b"pm" => Some(ParsedItem(rest, Period::Pm)),
_ => None,
}
}
}
/// Parse the "subsecond" component of a `Time`.
pub(crate) fn parse_subsecond(
input: &[u8],
modifiers: modifier::Subsecond,
) -> Option<ParsedItem<'_, u32>> {
use modifier::SubsecondDigits::*;
Some(match modifiers.digits {
One => ExactlyNDigits::<1>::parse(input)?.map(|v| v.extend::<u32>() * 100_000_000),
Two => ExactlyNDigits::<2>::parse(input)?.map(|v| v.extend::<u32>() * 10_000_000),
Three => ExactlyNDigits::<3>::parse(input)?.map(|v| v.extend::<u32>() * 1_000_000),
Four => ExactlyNDigits::<4>::parse(input)?.map(|v| v.extend::<u32>() * 100_000),
Five => ExactlyNDigits::<5>::parse(input)?.map(|v| v * 10_000),
Six => ExactlyNDigits::<6>::parse(input)?.map(|v| v * 1_000),
Seven => ExactlyNDigits::<7>::parse(input)?.map(|v| v * 100),
Eight => ExactlyNDigits::<8>::parse(input)?.map(|v| v * 10),
Nine => ExactlyNDigits::<9>::parse(input)?,
OneOrMore => {
let ParsedItem(mut input, mut value) =
any_digit(input)?.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
}
})
}
/// Parse the "hour" component of a `UtcOffset`.
///
/// Returns the value and whether the value is negative. This is used for when "-0" is parsed.
#[inline]
pub(crate) fn parse_offset_hour(
input: &[u8],
modifiers: modifier::OffsetHour,
) -> Option<ParsedItem<'_, (i8, bool)>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?;
match sign {
Some(Sign::Negative) => Some(ParsedItem(input, (-hour.cast_signed(), true))),
None if modifiers.sign_is_mandatory => None,
_ => Some(ParsedItem(input, (hour.cast_signed(), false))),
}
}
/// Parse the "minute" component of a `UtcOffset`.
#[inline]
pub(crate) fn parse_offset_minute(
input: &[u8],
modifiers: modifier::OffsetMinute,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
.map(|offset_minute| offset_minute.cast_signed()),
)
}
/// Parse the "second" component of a `UtcOffset`.
#[inline]
pub(crate) fn parse_offset_second(
input: &[u8],
modifiers: modifier::OffsetSecond,
) -> Option<ParsedItem<'_, i8>> {
Some(
exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?
.map(|offset_second| offset_second.cast_signed()),
)
}
/// Ignore the given number of bytes.
#[inline]
pub(crate) fn parse_ignore(
input: &[u8],
modifiers: modifier::Ignore,
) -> Option<ParsedItem<'_, ()>> {
let modifier::Ignore { count } = modifiers;
let input = input.get((count.get().extend())..)?;
Some(ParsedItem(input, ()))
}
/// Parse the Unix timestamp component.
pub(crate) fn parse_unix_timestamp(
input: &[u8],
modifiers: modifier::UnixTimestamp,
) -> Option<ParsedItem<'_, i128>> {
let ParsedItem(input, sign) = opt(sign)(input);
let ParsedItem(input, nano_timestamp) = match modifiers.precision {
modifier::UnixTimestampPrecision::Second => {
n_to_m_digits::<1, 14, u128>(input)?.map(|val| val * Nanosecond::per_t::<u128>(Second))
}
modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)?
.map(|val| val * Nanosecond::per_t::<u128>(Millisecond)),
modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)?
.map(|val| val * Nanosecond::per_t::<u128>(Microsecond)),
modifier::UnixTimestampPrecision::Nanosecond => n_to_m_digits::<1, 23, _>(input)?,
};
match sign {
Some(Sign::Negative) => Some(ParsedItem(input, -nano_timestamp.cast_signed())),
None if modifiers.sign_is_mandatory => None,
_ => Some(ParsedItem(input, nano_timestamp.cast_signed())),
}
}
/// Parse the `end` component, which represents the end of input. If any input is remaining _and_
/// trailing input is prohibited, `None` is returned. If trailing input is permitted, it is
/// discarded.
#[inline]
pub(crate) fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> {
let modifier::End { trailing_input } = end;
if trailing_input == modifier::TrailingInput::Discard || input.is_empty() {
Some(ParsedItem(b"", ()))
} else {
None
}
}

355
vendor/time/src/parsing/iso8601.rs vendored Normal file
View File

@@ -0,0 +1,355 @@
//! Parse parts of an ISO 8601-formatted value.
use crate::convert::*;
use crate::error;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::format_description::well_known::Iso8601;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::internal_macros::try_likely_ok;
use crate::parsing::combinator::rfc::iso8601::{
ExtendedKind, day, dayk, dayo, float, hour, min, month, week, year,
};
use crate::parsing::combinator::{Sign, 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> + use<'a, CONFIG> {
move |input| {
// Same for any acceptable format.
let ParsedItem(mut input, year) =
try_likely_ok!(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 parsed_month_day = (|| {
let ParsedItem(mut input, month) =
try_likely_ok!(month(input).ok_or(InvalidComponent("month")));
if extended_kind.is_extended() {
input = try_likely_ok!(ascii_char::<b'-'>(input).ok_or(InvalidLiteral))
.into_inner();
}
let ParsedItem(input, day) =
try_likely_ok!(day(input).ok_or(InvalidComponent("day")));
Ok(ParsedItem(input, (month, day)))
})();
let mut ret_error = match parsed_month_day {
Ok(ParsedItem(input, (month, day))) => {
*parsed = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(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 = try_likely_ok!(
try_likely_ok!(parsed.with_year(year).ok_or(InvalidComponent("year")))
.with_ordinal(ordinal)
.ok_or(InvalidComponent("ordinal"))
);
return Ok(input);
}
let parsed_week_weekday = (|| {
let input =
try_likely_ok!(ascii_char::<b'W'>(input).ok_or((false, InvalidLiteral)))
.into_inner();
let ParsedItem(mut input, week) =
try_likely_ok!(week(input).ok_or((true, InvalidComponent("week"))));
if extended_kind.is_extended() {
input = try_likely_ok!(ascii_char::<b'-'>(input).ok_or((true, InvalidLiteral)))
.into_inner();
}
let ParsedItem(input, weekday) =
try_likely_ok!(dayk(input).ok_or((true, InvalidComponent("weekday"))));
Ok(ParsedItem(input, (week, weekday)))
})();
match parsed_week_weekday {
Ok(ParsedItem(input, (week, weekday))) => {
*parsed = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(
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> + use<'a, CONFIG> {
move |mut input| {
if date_is_present {
input =
try_likely_ok!(ascii_char::<b'T'>(input).ok_or(InvalidLiteral)).into_inner();
}
let ParsedItem(mut input, hour) =
try_likely_ok!(float(input).ok_or(InvalidComponent("hour")));
match hour {
(hour, None) => {
try_likely_ok!(parsed.set_hour_24(hour).ok_or(InvalidComponent("hour")))
}
(hour, Some(fractional_part)) => {
*parsed = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(
try_likely_ok!(
parsed.with_hour_24(hour).ok_or(InvalidComponent("hour"))
)
.with_minute((fractional_part * Second::per_t::<f64>(Minute)) as u8)
.ok_or(InvalidComponent("minute"))
)
.with_second(
(fractional_part * Second::per_t::<f64>(Hour)
% Minute::per_t::<f64>(Hour))
as u8,
)
.ok_or(InvalidComponent("second"))
)
.with_subsecond(
(fractional_part * Nanosecond::per_t::<f64>(Hour)
% Nanosecond::per_t::<f64>(Second))
as u32,
)
.ok_or(InvalidComponent("subsecond"))
);
return Ok(input);
}
};
if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
try_likely_ok!(
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();
try_likely_ok!(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 = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(
parsed.with_minute(minute).ok_or(InvalidComponent("minute"))
)
.with_second((fractional_part * Second::per_t::<f64>(Minute)) as u8)
.ok_or(InvalidComponent("second"))
)
.with_subsecond(
(fractional_part * Nanosecond::per_t::<f64>(Minute)
% Nanosecond::per_t::<f64>(Second))
as u32,
)
.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 = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(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 = try_likely_ok!(
try_likely_ok!(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 * Nanosecond::per_t::<f64>(Second)) as u32,
),
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 = try_likely_ok!(
try_likely_ok!(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> + use<'a, CONFIG> {
move |input| {
if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
*parsed = try_likely_ok!(
try_likely_ok!(
try_likely_ok!(
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) =
try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
let mut input = try_likely_ok!(
hour(input)
.and_then(|parsed_item| {
parsed_item.consume_value(|hour| {
parsed.set_offset_hour(match sign {
Sign::Negative => -hour.cast_signed(),
Sign::Positive => hour.cast_signed(),
})
})
})
.ok_or(InvalidComponent("offset hour"))
);
if extended_kind.maybe_extended()
&& let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input)
{
try_likely_ok!(
extended_kind
.coerce_extended()
.ok_or(InvalidComponent("offset minute"))
);
input = new_input;
};
match min(input) {
Some(ParsedItem(new_input, min)) => {
input = new_input;
try_likely_ok!(
parsed
.set_offset_minute_signed(match sign {
Sign::Negative => -min.cast_signed(),
Sign::Positive => min.cast_signed(),
})
.ok_or(InvalidComponent("offset minute"))
);
}
None => {
// Omitted offset minute is assumed to be zero.
parsed.set_offset_minute_signed(0);
}
}
// 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`
#[inline]
fn round(value: f64) -> f64 {
#[cfg(feature = "std")]
{
value.round()
}
#[cfg(not(feature = "std"))]
{
debug_assert!(value.is_sign_positive() && !value.is_nan());
let f = value % 1.;
if f < 0.5 { value - f } else { value - f + 1. }
}
}

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

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

782
vendor/time/src/parsing/parsable.rs vendored Normal file
View File

@@ -0,0 +1,782 @@
//! A trait that can be used to parse an item from an input.
use core::num::NonZero;
use core::ops::Deref;
use num_conv::prelude::*;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::error::TryFromParsed;
#[cfg(feature = "alloc")]
use crate::format_description::OwnedFormatItem;
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339};
use crate::format_description::{BorrowedFormatItem, modifier};
use crate::internal_macros::{bug, try_likely_ok};
use crate::parsing::combinator::{
ExactlyNDigits, Sign, any_digit, ascii_char, ascii_char_ignore_case, one_or_two_digits, opt,
sign,
};
use crate::parsing::{Parsed, ParsedItem, component};
use crate::{Date, Month, OffsetDateTime, Time, UtcOffset, error};
/// A type that can be parsed.
#[cfg_attr(docsrs, doc(notable_trait))]
#[doc(alias = "Parseable")]
pub trait Parsable: sealed::Sealed {}
impl Parsable for BorrowedFormatItem<'_> {}
impl Parsable for [BorrowedFormatItem<'_>] {}
#[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> Parsable for T where T: Deref<Target: Parsable> {}
/// Seal the trait to prevent downstream users from implementing it, while still allowing it to
/// exist in generic bounds.
mod sealed {
use super::*;
use crate::{PrimitiveDateTime, UtcDateTime};
/// Parse the item using a format description and an input.
pub trait Sealed {
/// Parse the item into the provided [`Parsed`] struct.
///
/// This method can be used to parse a single component without parsing the full value.
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse>;
/// Parse the item into a new [`Parsed`] struct.
///
/// This method can only be used to parse a complete value of a type. If any characters
/// remain after parsing, an error will be returned.
#[inline]
fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> {
let mut parsed = Parsed::new();
if self.parse_into(input, &mut parsed)?.is_empty() {
Ok(parsed)
} else {
Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::UnexpectedTrailingCharacters,
))
}
}
/// Parse a [`Date`] from the format description.
#[inline]
fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`Time`] from the format description.
#[inline]
fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`UtcOffset`] from the format description.
#[inline]
fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`PrimitiveDateTime`] from the format description.
#[inline]
fn parse_primitive_date_time(
&self,
input: &[u8],
) -> Result<PrimitiveDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`UtcDateTime`] from the format description.
#[inline]
fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
/// Parse a [`OffsetDateTime`] from the format description.
#[inline]
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
Ok(self.parse(input)?.try_into()?)
}
}
}
impl sealed::Sealed for BorrowedFormatItem<'_> {
#[inline]
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_item(input, self)?)
}
}
impl sealed::Sealed for [BorrowedFormatItem<'_>] {
#[inline]
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_items(input, self)?)
}
}
#[cfg(feature = "alloc")]
impl sealed::Sealed for OwnedFormatItem {
#[inline]
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] {
#[inline]
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
Ok(parsed.parse_items(input, self)?)
}
}
impl<T> sealed::Sealed for T
where
T: Deref<Target: sealed::Sealed>,
{
#[inline]
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
self.deref().parse_into(input, parsed)
}
}
impl sealed::Sealed for Rfc2822 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws, zone_literal};
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
let input = opt(cfws)(input).into_inner();
let weekday = component::parse_weekday(
input,
modifier::Weekday {
repr: modifier::WeekdayRepr::Short,
one_indexed: false,
case_sensitive: false,
},
);
let input = if let Some(item) = weekday {
let input = try_likely_ok!(
item.consume_value(|value| parsed.set_weekday(value))
.ok_or(InvalidComponent("weekday"))
);
let input = try_likely_ok!(comma(input).ok_or(InvalidLiteral)).into_inner();
opt(cfws)(input).into_inner()
} else {
input
};
let input = try_likely_ok!(
one_or_two_digits(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(NonZero::new(value)?)))
.ok_or(InvalidComponent("day"))
);
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
component::parse_month(
input,
modifier::Month {
padding: modifier::Padding::None,
repr: modifier::MonthRepr::Short,
case_sensitive: false,
},
)
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))
);
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
let input = match ExactlyNDigits::<4>::parse(input) {
Some(item) => {
let input = try_likely_ok!(
item.flat_map(|year| if year >= 1900 { Some(year) } else { None })
.and_then(|item| {
item.consume_value(|value| {
parsed.set_year(value.cast_signed().extend())
})
})
.ok_or(InvalidComponent("year"))
);
try_likely_ok!(fws(input).ok_or(InvalidLiteral)).into_inner()
}
None => {
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| {
item.map(|year| year.extend::<u32>())
.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
.map(|year| year.cast_signed())
.consume_value(|value| parsed.set_year(value))
})
.ok_or(InvalidComponent("year"))
);
try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
}
};
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(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 = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let input = opt(cfws)(input).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(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 = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))
);
try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
} else {
try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner()
};
// The RFC explicitly allows leap seconds.
parsed.leap_second_allowed = true;
if let Some(zone_literal) = zone_literal(input) {
let input = try_likely_ok!(
zone_literal
.consume_value(|value| parsed.set_offset_hour(value))
.ok_or(InvalidComponent("offset hour"))
);
try_likely_ok!(
parsed
.set_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))
);
try_likely_ok!(
parsed
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))
);
return Ok(input);
}
let ParsedItem(input, offset_sign) =
try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| {
item.map(|offset_hour| match offset_sign {
Sign::Negative => -offset_hour.cast_signed(),
Sign::Positive => offset_hour.cast_signed(),
})
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))
);
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed()))
})
.ok_or(InvalidComponent("offset minute"))
);
let input = opt(cfws)(input).into_inner();
Ok(input)
}
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
use crate::parsing::combinator::rfc::rfc2822::{cfws, fws, zone_literal};
let colon = ascii_char::<b':'>;
let comma = ascii_char::<b','>;
let input = opt(cfws)(input).into_inner();
let weekday = component::parse_weekday(
input,
modifier::Weekday {
repr: modifier::WeekdayRepr::Short,
one_indexed: false,
case_sensitive: false,
},
);
let input = if let Some(item) = weekday {
let input = item.discard_value();
let input = try_likely_ok!(comma(input).ok_or(InvalidLiteral)).into_inner();
opt(cfws)(input).into_inner()
} else {
input
};
let ParsedItem(input, day) =
try_likely_ok!(one_or_two_digits(input).ok_or(InvalidComponent("day")));
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, month) = try_likely_ok!(
component::parse_month(
input,
modifier::Month {
padding: modifier::Padding::None,
repr: modifier::MonthRepr::Short,
case_sensitive: false,
},
)
.ok_or(InvalidComponent("month"))
);
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
let (input, year) = match ExactlyNDigits::<4>::parse(input) {
Some(item) => {
let ParsedItem(input, year) = try_likely_ok!(
item.flat_map(|year| if year >= 1900 { Some(year) } else { None })
.ok_or(InvalidComponent("year"))
);
let input = try_likely_ok!(fws(input).ok_or(InvalidLiteral)).into_inner();
(input, year)
}
None => {
let ParsedItem(input, year) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.map(|item| {
item.map(|year| year.extend::<u16>())
.map(|year| if year < 50 { year + 2000 } else { year + 1900 })
})
.ok_or(InvalidComponent("year"))
);
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
(input, year)
}
};
let ParsedItem(input, hour) =
try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour")));
let input = opt(cfws)(input).into_inner();
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let input = opt(cfws)(input).into_inner();
let ParsedItem(input, minute) =
try_likely_ok!(ExactlyNDigits::<2>::parse(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) =
try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("second")));
let input = try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner();
(input, second)
} else {
(
try_likely_ok!(cfws(input).ok_or(InvalidLiteral)).into_inner(),
0,
)
};
let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal(input) {
let ParsedItem(input, offset_hour) = zone_literal;
(input, offset_hour, 0)
} else {
let ParsedItem(input, offset_sign) =
try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
let ParsedItem(input, offset_hour) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.map(|item| {
item.map(|offset_hour| match offset_sign {
Sign::Negative => -offset_hour.cast_signed(),
Sign::Positive => offset_hour.cast_signed(),
})
})
.ok_or(InvalidComponent("offset hour"))
);
let ParsedItem(input, offset_minute) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))
);
(input, offset_hour, offset_minute.cast_signed())
};
let input = opt(cfws)(input).into_inner();
if !input.is_empty() {
return Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::UnexpectedTrailingCharacters,
));
}
let mut nanosecond = 0;
let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let dt = try_likely_ok!(
(|| {
let date = try_likely_ok!(Date::from_calendar_date(
year.cast_signed().extend(),
month,
day
));
let time = try_likely_ok!(Time::from_hms_nano(hour, minute, second, nanosecond));
let offset = try_likely_ok!(UtcOffset::from_hms(offset_hour, offset_minute, 0));
Ok(OffsetDateTime::new_in_offset(date, time, 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::conditional("second"),
)));
}
Ok(dt)
}
}
impl sealed::Sealed for Rfc3339 {
fn parse_into<'a>(
&self,
input: &'a [u8],
parsed: &mut Parsed,
) -> Result<&'a [u8], error::Parse> {
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let input = try_likely_ok!(
ExactlyNDigits::<4>::parse(input)
.and_then(|item| {
item.consume_value(|value| parsed.set_year(value.cast_signed().extend()))
})
.ok_or(InvalidComponent("year"))
);
let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(
|item| item.flat_map(|value| Month::from_number(NonZero::new(value)?).ok())
)
.and_then(|item| item.consume_value(|value| parsed.set_month(value)))
.ok_or(InvalidComponent("month"))
);
let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| item.consume_value(|value| parsed.set_day(NonZero::new(value)?)))
.ok_or(InvalidComponent("day"))
);
// RFC3339 allows any separator, not just `T`, not just `space`.
// cf. Section 5.6: Internet Date/Time Format:
// NOTE: ISO 8601 defines date and time separated by "T".
// Applications using this syntax may choose, for the sake of
// readability, to specify a full-date and full-time separated by
// (say) a space character.
// Specifically, rusqlite uses space separators.
let input = try_likely_ok!(input.get(1..).ok_or(InvalidComponent("separator")));
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| item.consume_value(|value| parsed.set_hour_24(value)))
.ok_or(InvalidComponent("hour"))
);
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| item.consume_value(|value| parsed.set_minute(value)))
.ok_or(InvalidComponent("minute"))
);
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| item.consume_value(|value| parsed.set_second(value)))
.ok_or(InvalidComponent("second"))
);
let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) {
let ParsedItem(mut input, mut value) =
try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond")))
.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
try_likely_ok!(
parsed
.set_subsecond(value)
.ok_or(InvalidComponent("subsecond"))
);
input
} else {
input
};
// The RFC explicitly allows leap seconds.
parsed.leap_second_allowed = true;
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
try_likely_ok!(
parsed
.set_offset_hour(0)
.ok_or(InvalidComponent("offset hour"))
);
try_likely_ok!(
parsed
.set_offset_minute_signed(0)
.ok_or(InvalidComponent("offset minute"))
);
try_likely_ok!(
parsed
.set_offset_second_signed(0)
.ok_or(InvalidComponent("offset second"))
);
return Ok(input);
}
let ParsedItem(input, offset_sign) =
try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| {
item.filter(|&offset_hour| offset_hour <= 23)?
.map(|offset_hour| match offset_sign {
Sign::Negative => -offset_hour.cast_signed(),
Sign::Positive => offset_hour.cast_signed(),
})
.consume_value(|value| parsed.set_offset_hour(value))
})
.ok_or(InvalidComponent("offset hour"))
);
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let input = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|item| {
item.map(|offset_minute| match offset_sign {
Sign::Negative => -offset_minute.cast_signed(),
Sign::Positive => offset_minute.cast_signed(),
})
.consume_value(|value| parsed.set_offset_minute_signed(value))
})
.ok_or(InvalidComponent("offset minute"))
);
Ok(input)
}
fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> {
let dash = ascii_char::<b'-'>;
let colon = ascii_char::<b':'>;
let ParsedItem(input, year) =
try_likely_ok!(ExactlyNDigits::<4>::parse(input).ok_or(InvalidComponent("year")));
let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, month) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|parsed| parsed.flat_map(NonZero::new))
.ok_or(InvalidComponent("month"))
);
let input = try_likely_ok!(dash(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, day) =
try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("day")));
// RFC3339 allows any separator, not just `T`, not just `space`.
// cf. Section 5.6: Internet Date/Time Format:
// NOTE: ISO 8601 defines date and time separated by "T".
// Applications using this syntax may choose, for the sake of
// readability, to specify a full-date and full-time separated by
// (say) a space character.
// Specifically, rusqlite uses space separators.
let input = try_likely_ok!(input.get(1..).ok_or(InvalidComponent("separator")));
let ParsedItem(input, hour) =
try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("hour")));
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, minute) =
try_likely_ok!(ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("minute")));
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, mut second) =
try_likely_ok!(ExactlyNDigits::<2>::parse(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) =
try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond")))
.map(|v| (v - b'0').extend::<u32>() * 100_000_000);
let mut multiplier = 10_000_000;
while let Some(ParsedItem(new_input, digit)) = any_digit(input) {
value += (digit - b'0').extend::<u32>() * multiplier;
input = new_input;
multiplier /= 10;
}
ParsedItem(input, value)
} else {
ParsedItem(input, 0)
};
let ParsedItem(input, offset) = {
if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) {
ParsedItem(input, UtcOffset::UTC)
} else {
let ParsedItem(input, offset_sign) =
try_likely_ok!(sign(input).ok_or(InvalidComponent("offset hour")));
let ParsedItem(input, offset_hour) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input)
.and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23))
.ok_or(InvalidComponent("offset hour"))
);
let input = try_likely_ok!(colon(input).ok_or(InvalidLiteral)).into_inner();
let ParsedItem(input, offset_minute) = try_likely_ok!(
ExactlyNDigits::<2>::parse(input).ok_or(InvalidComponent("offset minute"))
);
try_likely_ok!(
match offset_sign {
Sign::Negative => UtcOffset::from_hms(
-offset_hour.cast_signed(),
-offset_minute.cast_signed(),
0,
),
Sign::Positive => UtcOffset::from_hms(
offset_hour.cast_signed(),
offset_minute.cast_signed(),
0,
),
}
.map(|offset| ParsedItem(input, offset))
.map_err(TryFromParsed::ComponentRange)
)
}
};
if !input.is_empty() {
return Err(error::Parse::ParseFromDescription(
error::ParseFromDescription::UnexpectedTrailingCharacters,
));
}
// The RFC explicitly permits leap seconds. We don't currently support them, so treat it as
// the preceding nanosecond. However, leap seconds can only occur as the last second of the
// month UTC.
let leap_second_input = if second == 60 {
second = 59;
nanosecond = 999_999_999;
true
} else {
false
};
let date = try_likely_ok!(
Month::from_number(month)
.and_then(|month| Date::from_calendar_date(year.cast_signed().extend(), month, day))
.map_err(TryFromParsed::ComponentRange)
);
let time = try_likely_ok!(
Time::from_hms_nano(hour, minute, second, nanosecond)
.map_err(TryFromParsed::ComponentRange)
);
let dt = OffsetDateTime::new_in_offset(date, time, offset);
if leap_second_input && !dt.is_valid_leap_second_stand_in() {
return Err(error::Parse::TryFromParsed(TryFromParsed::ComponentRange(
error::ComponentRange::conditional("second"),
)));
}
Ok(dt)
}
}
impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> {
#[inline]
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;
parsed.leap_second_allowed = true;
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 => bug!("an error should be present if no components were parsed"),
}
}
Ok(input)
}
}

1173
vendor/time/src/parsing/parsed.rs vendored Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,46 @@
//! Extension traits for things either not implemented or not yet stable in the MSRV.
/// Marker trait for integer types.
pub(crate) trait Integer: Sized {
/// The maximum number of digits that this type can have.
const MAX_NUM_DIGITS: u8;
/// The zero value for this type.
const ZERO: Self;
/// Push a digit onto the end of this integer, assuming no overflow.
///
/// This is equivalent to `self * 10 + digit`.
fn push_digit(self, digit: u8) -> Self;
/// Push a digit onto the end of this integer, returning `None` on overflow.
///
/// This is equivalent to `self.checked_mul(10)?.checked_add(digit)`.
fn checked_push_digit(self, digit: u8) -> Option<Self>;
}
/// Parse the given types from bytes.
macro_rules! impl_parse_bytes {
($($t:ty)*) => ($(
impl Integer for $t {
const MAX_NUM_DIGITS: u8 = match Self::MAX.checked_ilog10() {
Some(digits) => digits as u8 + 1,
None => 1,
};
const ZERO: Self = 0;
#[allow(trivial_numeric_casts, reason = "macro-generated code")]
#[inline]
fn push_digit(self, digit: u8) -> Self {
self * 10 + digit as Self
}
#[allow(trivial_numeric_casts, reason = "macro-generated code")]
#[inline]
fn checked_push_digit(self, digit: u8) -> Option<Self> {
self.checked_mul(10)?.checked_add(digit as Self)
}
}
)*)
}
impl_parse_bytes! { u8 u16 u32 u128 }

1264
vendor/time/src/primitive_date_time.rs vendored Normal file

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,248 @@
//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait.
//!
//! This enables users to write tests such as this, and have test values provided automatically:
//!
//! ```ignore
//! # #![expect(dead_code)]
//! use quickcheck::quickcheck;
//! use time::Date;
//!
//! struct DateRange {
//! from: Date,
//! to: Date,
//! }
//!
//! impl DateRange {
//! fn new(from: Date, to: Date) -> Result<Self, ()> {
//! Ok(DateRange { from, to })
//! }
//! }
//!
//! quickcheck! {
//! fn date_range_is_well_defined(from: Date, to: Date) -> bool {
//! let r = DateRange::new(from, to);
//! if from <= to {
//! r.is_ok()
//! } else {
//! r.is_err()
//! }
//! }
//! }
//! ```
//!
//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in
//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple
//! anyway.
use alloc::boxed::Box;
use quickcheck::{Arbitrary, Gen, empty_shrinker, single_shrinker};
use crate::{
Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
/// Obtain an arbitrary value between the minimum and maximum inclusive.
macro_rules! arbitrary_between {
($type:ty; $gen:expr, $min:expr, $max:expr) => {{
let min = $min;
let max = $max;
let range = max - min;
<$type>::arbitrary($gen).rem_euclid(range + 1) + min
}};
}
impl Arbitrary for Date {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
// Safety: The Julian day number is in range.
unsafe {
Self::from_julian_day_unchecked(arbitrary_between!(
i32;
g,
Self::MIN.to_julian_day(),
Self::MAX.to_julian_day()
))
}
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.to_ordinal_date()
.shrink()
.flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
)
}
}
impl Arbitrary for Duration {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.subsec_nanoseconds_ranged(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
// Coerce the sign if necessary.
if (seconds > 0 && nanoseconds.get() < 0)
|| (seconds < 0 && nanoseconds.get() > 0)
{
nanoseconds = nanoseconds.neg();
}
Self::new_ranged_unchecked(seconds, nanoseconds)
}),
)
}
}
impl Arbitrary for Time {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::from_hms_nanos_ranged(
<_>::arbitrary(g),
<_>::arbitrary(g),
<_>::arbitrary(g),
<_>::arbitrary(g),
)
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_nano_ranged()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
}),
)
}
}
impl Arbitrary for PrimitiveDateTime {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date(), self.time())
.shrink()
.map(|(date, time)| Self::new(date, time)),
)
}
}
impl Arbitrary for UtcOffset {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_ranged()
.shrink()
.map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)),
)
}
}
impl Arbitrary for OffsetDateTime {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date(), self.time(), self.offset())
.shrink()
.map(|(date, time, offset)| Self::new_in_offset(date, time, offset)),
)
}
}
impl Arbitrary for UtcDateTime {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date(), self.time())
.shrink()
.map(|(date, time)| Self::new(date, time)),
)
}
}
impl Arbitrary for Weekday {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
use Weekday::*;
match arbitrary_between!(u8; g, 0, 6) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::Monday => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}
impl Arbitrary for Month {
#[inline]
fn arbitrary(g: &mut Gen) -> Self {
use Month::*;
match arbitrary_between!(u8; g, 1, 12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
val => {
debug_assert!(val == 12);
December
}
}
}
#[inline]
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::January => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}

134
vendor/time/src/rand08.rs vendored Normal file
View File

@@ -0,0 +1,134 @@
//! Implementation of [`Distribution`] for various structs.
use rand08::Rng;
use rand08::distributions::{Distribution, Standard};
use crate::{
Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
impl Distribution<Time> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Time
where
R: Rng + ?Sized,
{
Time::from_hms_nanos_ranged(rng.r#gen(), rng.r#gen(), rng.r#gen(), rng.r#gen())
}
}
impl Distribution<Date> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Date
where
R: Rng + ?Sized,
{
// Safety: The Julian day number is in range.
unsafe {
Date::from_julian_day_unchecked(
rng.gen_range(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()),
)
}
}
}
impl Distribution<UtcOffset> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> UtcOffset
where
R: Rng + ?Sized,
{
UtcOffset::from_hms_ranged(rng.r#gen(), rng.r#gen(), rng.r#gen())
}
}
impl Distribution<PrimitiveDateTime> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> PrimitiveDateTime
where
R: Rng + ?Sized,
{
PrimitiveDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<UtcDateTime> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> UtcDateTime
where
R: Rng + ?Sized,
{
UtcDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<OffsetDateTime> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> OffsetDateTime
where
R: Rng + ?Sized,
{
let date_time: PrimitiveDateTime = Self.sample(rng);
date_time.assume_offset(Self.sample(rng))
}
}
impl Distribution<Duration> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Duration
where
R: Rng + ?Sized,
{
Duration::new_ranged(rng.r#gen(), rng.r#gen())
}
}
impl Distribution<Weekday> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Weekday
where
R: Rng + ?Sized,
{
use Weekday::*;
match rng.gen_range(0u8..7) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
}
impl Distribution<Month> for Standard {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Month
where
R: Rng + ?Sized,
{
use Month::*;
match rng.gen_range(1u8..=12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
val => {
debug_assert!(val == 12);
December
}
}
}
}

134
vendor/time/src/rand09.rs vendored Normal file
View File

@@ -0,0 +1,134 @@
//! Implementation of [`Distribution`] for various structs.
use rand09::Rng;
use rand09::distr::{Distribution, StandardUniform};
use crate::{
Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
impl Distribution<Time> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Time
where
R: Rng + ?Sized,
{
Time::from_hms_nanos_ranged(rng.random(), rng.random(), rng.random(), rng.random())
}
}
impl Distribution<Date> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Date
where
R: Rng + ?Sized,
{
// Safety: The Julian day number is in range.
unsafe {
Date::from_julian_day_unchecked(
rng.random_range(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()),
)
}
}
}
impl Distribution<UtcOffset> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> UtcOffset
where
R: Rng + ?Sized,
{
UtcOffset::from_hms_ranged(rng.random(), rng.random(), rng.random())
}
}
impl Distribution<PrimitiveDateTime> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> PrimitiveDateTime
where
R: Rng + ?Sized,
{
PrimitiveDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<UtcDateTime> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> UtcDateTime
where
R: Rng + ?Sized,
{
UtcDateTime::new(Self.sample(rng), Self.sample(rng))
}
}
impl Distribution<OffsetDateTime> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> OffsetDateTime
where
R: Rng + ?Sized,
{
let date_time: PrimitiveDateTime = Self.sample(rng);
date_time.assume_offset(Self.sample(rng))
}
}
impl Distribution<Duration> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Duration
where
R: Rng + ?Sized,
{
Duration::new_ranged(rng.random(), rng.random())
}
}
impl Distribution<Weekday> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Weekday
where
R: Rng + ?Sized,
{
use Weekday::*;
match rng.random_range(0u8..7) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
}
impl Distribution<Month> for StandardUniform {
#[inline]
fn sample<R>(&self, rng: &mut R) -> Month
where
R: Rng + ?Sized,
{
use Month::*;
match rng.random_range(1u8..=12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
val => {
debug_assert!(val == 12);
December
}
}
}
}

88
vendor/time/src/serde/iso8601.rs vendored Normal file
View File

@@ -0,0 +1,88 @@
//! 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 = "parsing")]
use serde_core::Deserializer;
#[cfg(feature = "formatting")]
use serde_core::ser::Error as _;
#[cfg(feature = "formatting")]
use serde_core::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::OffsetDateTime;
use crate::format_description::well_known::Iso8601;
use crate::format_description::well_known::iso8601::{Config, EncodedConfig};
/// 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")]
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime
.format(&Iso8601::<SERDE_CONFIG>)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its ISO 8601 representation.
#[cfg(feature = "parsing")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_str(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.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known ISO 8601 format.
#[cfg(feature = "formatting")]
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
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")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_option(Visitor::<Option<Iso8601<SERDE_CONFIG>>>(PhantomData))
}
}

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

@@ -0,0 +1,609 @@
//! Differential formats for serde.
// This also includes the serde implementations for all types. This doesn't need to be externally
// documented, though.
// Types with guaranteed stable serde representations. Strings are avoided to allow for optimal
// representations in various binary forms.
/// Consume the next item in a sequence.
macro_rules! item {
($seq:expr, $name:literal) => {
$seq.next_element()?
.ok_or_else(|| <A::Error as serde_core::de::Error>::custom(concat!("expected ", $name)))
};
}
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod iso8601;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc2822;
#[cfg(any(feature = "formatting", feature = "parsing"))]
pub mod rfc3339;
pub mod timestamp;
mod visitor;
#[cfg(feature = "serde-human-readable")]
use alloc::string::ToString;
use core::marker::PhantomData;
#[cfg(feature = "serde-human-readable")]
use serde_core::ser::Error as _;
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
/// Generate a custom serializer and deserializer from a format string or an existing format.
///
/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can
/// be found in [the book](https://time-rs.github.io/book/api/format-description.html).
///
/// # Usage
///
/// Invoked as `serde::format_description!(mod_name, Date, FORMAT)` where `FORMAT` is either a
/// `"<format string>"` or something that implements
#[cfg_attr(
all(feature = "formatting", feature = "parsing"),
doc = "[`Formattable`](crate::formatting::Formattable) and \
[`Parsable`](crate::parsing::Parsable)."
)]
#[cfg_attr(
all(feature = "formatting", not(feature = "parsing")),
doc = "[`Formattable`](crate::formatting::Formattable)."
)]
#[cfg_attr(
all(not(feature = "formatting"), feature = "parsing"),
doc = "[`Parsable`](crate::parsing::Parsable)."
)]
/// This puts a module named `mod_name` in the current scope that can be used to format `Date`
/// structs. A submodule (`mod_name::option`) is also generated for `Option<Date>`. Both
/// modules are only visible in the current scope by default. To increase visibility, you can
/// specify `pub`, `pub(crate)`, or similar before the module name:
/// `serde::format_description!(pub mod_name, Date, FORMAT)`.
///
/// 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.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want
/// a missing field to deserialize as `None`.
///
/// # Examples
///
/// Using a format string:
///
/// ```rust,no_run
/// # use time::OffsetDateTime;
#[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]");
///
/// # #[allow(dead_code)]
#[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)]"
)]
/// struct SerializesWithCustom {
/// #[serde(with = "my_format")]
/// dt: OffsetDateTime,
/// #[serde(with = "my_format::option", default)]
/// maybe_dt: Option<OffsetDateTime>,
/// }
/// ```
///
/// Define the format separately to be used in multiple places:
/// ```rust,no_run
/// # use time::OffsetDateTime;
#[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;
/// use time::format_description::StaticFormatDescription;
///
/// const DATE_TIME_FORMAT: StaticFormatDescription = time::macros::format_description!(
/// "hour=[hour], minute=[minute]"
/// );
///
/// // Makes a module `mod my_format { ... }`.
/// serde::format_description!(my_format, OffsetDateTime, DATE_TIME_FORMAT);
///
/// # #[allow(dead_code)]
#[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)]"
)]
/// struct SerializesWithCustom {
/// #[serde(with = "my_format")]
/// dt: OffsetDateTime,
/// #[serde(with = "my_format::option", default)]
/// maybe_dt: Option<OffsetDateTime>,
/// }
///
/// fn main() {
/// # #[expect(unused_variables)]
/// let str_ts = OffsetDateTime::now_utc().format(DATE_TIME_FORMAT).unwrap();
/// }
/// ```
///
/// Customize the configuration of ISO 8601 formatting/parsing:
/// ```rust,no_run
/// # use time::OffsetDateTime;
#[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;
/// use time::format_description::well_known::{iso8601, Iso8601};
///
/// # #[allow(dead_code)]
/// const CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT
/// .set_year_is_six_digits(false)
/// .encode();
/// # #[allow(dead_code)]
/// const FORMAT: Iso8601<CONFIG> = Iso8601::<CONFIG>;
///
/// // Makes a module `mod my_format { ... }`.
/// serde::format_description!(my_format, OffsetDateTime, FORMAT);
///
/// # #[allow(dead_code)]
#[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)]"
)]
/// struct SerializesWithCustom {
/// #[serde(with = "my_format")]
/// dt: OffsetDateTime,
/// #[serde(with = "my_format::option", default)]
/// maybe_dt: Option<OffsetDateTime>,
/// }
/// # fn main() {}
/// ```
///
/// [`format_description::parse()`]: crate::format_description::parse()
#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing")))]
pub use time_macros::serde_format_description as format_description;
use self::visitor::Visitor;
#[cfg(feature = "parsing")]
use crate::format_description::{BorrowedFormatItem, Component, StaticFormatDescription, modifier};
use crate::{
Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
};
/// The format used when serializing and deserializing a human-readable `Date`.
#[cfg(feature = "parsing")]
const DATE_FORMAT: StaticFormatDescription = &[
BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
BorrowedFormatItem::Literal(b"-"),
BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
BorrowedFormatItem::Literal(b"-"),
BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
];
impl Serialize for Date {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&DATE_FORMAT) else {
return Err(S::Error::custom("failed formatting `Date`"));
};
return serializer.serialize_str(&s);
}
(self.year(), self.ordinal()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Date {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
}
}
}
impl Serialize for Duration {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
return serializer.collect_str(&format_args!(
"{}{}.{:>09}",
if self.is_negative() { "-" } else { "" },
self.whole_seconds().unsigned_abs(),
self.subsec_nanoseconds().abs(),
));
}
(self.whole_seconds(), self.subsec_nanoseconds()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Duration {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData))
}
}
}
/// The format used when serializing and deserializing a human-readable `OffsetDateTime`.
#[cfg(feature = "parsing")]
const OFFSET_DATE_TIME_FORMAT: StaticFormatDescription = &[
BorrowedFormatItem::Compound(DATE_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(TIME_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
];
impl Serialize for OffsetDateTime {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&OFFSET_DATE_TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `OffsetDateTime`"));
};
return serializer.serialize_str(&s);
}
(
self.year(),
self.ordinal(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
self.offset().whole_hours(),
self.offset().minutes_past_hour(),
self.offset().seconds_past_minute(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for OffsetDateTime {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(9, Visitor::<Self>(PhantomData))
}
}
}
/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`.
#[cfg(feature = "parsing")]
const PRIMITIVE_DATE_TIME_FORMAT: StaticFormatDescription = &[
BorrowedFormatItem::Compound(DATE_FORMAT),
BorrowedFormatItem::Literal(b" "),
BorrowedFormatItem::Compound(TIME_FORMAT),
];
impl Serialize for PrimitiveDateTime {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `PrimitiveDateTime`"));
};
return serializer.serialize_str(&s);
}
(
self.year(),
self.ordinal(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for PrimitiveDateTime {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
}
}
}
/// The format used when serializing and deserializing a human-readable `UtcDateTime`.
#[cfg(feature = "parsing")]
const UTC_DATE_TIME_FORMAT: StaticFormatDescription = PRIMITIVE_DATE_TIME_FORMAT;
impl Serialize for UtcDateTime {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `UtcDateTime`"));
};
return serializer.serialize_str(&s);
}
(
self.year(),
self.ordinal(),
self.hour(),
self.minute(),
self.second(),
self.nanosecond(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for UtcDateTime {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData))
}
}
}
/// The format used when serializing and deserializing a human-readable `Time`.
#[cfg(feature = "parsing")]
const TIME_FORMAT: StaticFormatDescription = &[
BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
BorrowedFormatItem::Literal(b"."),
BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
];
impl Serialize for Time {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&TIME_FORMAT) else {
return Err(S::Error::custom("failed formatting `Time`"));
};
return serializer.serialize_str(&s);
}
(self.hour(), self.minute(), self.second(), self.nanosecond()).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Time {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(4, Visitor::<Self>(PhantomData))
}
}
}
/// The format used when serializing and deserializing a human-readable `UtcOffset`.
#[cfg(feature = "parsing")]
const UTC_OFFSET_FORMAT: StaticFormatDescription = &[
BorrowedFormatItem::Component(Component::OffsetHour(
const {
let mut m = modifier::OffsetHour::default();
m.sign_is_mandatory = true;
m
},
)),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetMinute(
const { modifier::OffsetMinute::default() },
)),
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
BorrowedFormatItem::Literal(b":"),
BorrowedFormatItem::Component(Component::OffsetSecond(
const { modifier::OffsetSecond::default() },
)),
])),
])),
];
impl Serialize for UtcOffset {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
let Ok(s) = self.format(&UTC_OFFSET_FORMAT) else {
return Err(S::Error::custom("failed formatting `UtcOffset`"));
};
return serializer.serialize_str(&s);
}
(
self.whole_hours(),
self.minutes_past_hour(),
self.seconds_past_minute(),
)
.serialize(serializer)
}
}
impl<'a> Deserialize<'a> for UtcOffset {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_tuple(3, Visitor::<Self>(PhantomData))
}
}
}
impl Serialize for Weekday {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
return self.to_string().serialize(serializer);
}
self.number_from_monday().serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Weekday {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
}
}
}
impl Serialize for Month {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[cfg(feature = "serde-human-readable")]
if serializer.is_human_readable() {
#[cfg(not(feature = "std"))]
use alloc::string::String;
return self.to_string().serialize(serializer);
}
u8::from(*self).serialize(serializer)
}
}
impl<'a> Deserialize<'a> for Month {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'a>,
{
if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() {
deserializer.deserialize_any(Visitor::<Self>(PhantomData))
} else {
deserializer.deserialize_u8(Visitor::<Self>(PhantomData))
}
}
}

83
vendor/time/src/serde/rfc2822.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
//! Use the well-known [RFC2822 format] when serializing and deserializing an [`OffsetDateTime`].
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3
//! [with]: https://serde.rs/field-attrs.html#with
#[cfg(feature = "parsing")]
use core::marker::PhantomData;
#[cfg(feature = "parsing")]
use serde_core::Deserializer;
#[cfg(feature = "formatting")]
use serde_core::ser::Error as _;
#[cfg(feature = "formatting")]
use serde_core::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::OffsetDateTime;
use crate::format_description::well_known::Rfc2822;
/// Serialize an [`OffsetDateTime`] using the well-known RFC2822 format.
#[cfg(feature = "formatting")]
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime
.format(&Rfc2822)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its RFC2822 representation.
#[cfg(feature = "parsing")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_str(Visitor::<Rfc2822>(PhantomData))
}
/// Use the well-known [RFC2822 format] when serializing and deserializing an
/// [`Option<OffsetDateTime>`].
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC2822 format.
#[cfg(feature = "formatting")]
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(|odt| odt.format(&Rfc2822))
.transpose()
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC2822 representation.
#[cfg(feature = "parsing")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_option(Visitor::<Option<Rfc2822>>(PhantomData))
}
}

83
vendor/time/src/serde/rfc3339.rs vendored Normal file
View File

@@ -0,0 +1,83 @@
//! Use the well-known [RFC3339 format] when serializing and deserializing an [`OffsetDateTime`].
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6
//! [with]: https://serde.rs/field-attrs.html#with
#[cfg(feature = "parsing")]
use core::marker::PhantomData;
#[cfg(feature = "parsing")]
use serde_core::Deserializer;
#[cfg(feature = "formatting")]
use serde_core::ser::Error as _;
#[cfg(feature = "formatting")]
use serde_core::{Serialize, Serializer};
#[cfg(feature = "parsing")]
use super::Visitor;
use crate::OffsetDateTime;
use crate::format_description::well_known::Rfc3339;
/// Serialize an [`OffsetDateTime`] using the well-known RFC3339 format.
#[cfg(feature = "formatting")]
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime
.format(&Rfc3339)
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`OffsetDateTime`] from its RFC3339 representation.
#[cfg(feature = "parsing")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_str(Visitor::<Rfc3339>(PhantomData))
}
/// Use the well-known [RFC3339 format] when serializing and deserializing an
/// [`Option<OffsetDateTime>`].
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC3339 format.
#[cfg(feature = "formatting")]
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(|odt| odt.format(&Rfc3339))
.transpose()
.map_err(S::Error::custom)?
.serialize(serializer)
}
/// Deserialize an [`Option<OffsetDateTime>`] from its RFC3339 representation.
#[cfg(feature = "parsing")]
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
deserializer.deserialize_option(Visitor::<Option<Rfc3339>>(PhantomData))
}
}

View File

@@ -0,0 +1,74 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with microseconds for
//! the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
use crate::error::ComponentRange;
/// Serialize an `OffsetDateTime` as its Unix timestamp with microseconds
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let timestamp = datetime.unix_timestamp_nanos() / 1_000;
timestamp.serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp with microseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
let value: i128 = <_>::deserialize(deserializer)?;
OffsetDateTime::from_unix_timestamp_nanos(value * 1_000).map_err(ComponentRange::into_de_error)
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with microseconds
/// for the purposes of serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with microseconds
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000)
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with microseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer)?
.map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000))
.transpose()
.map_err(ComponentRange::into_de_error)
}
}

View File

@@ -0,0 +1,75 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with milliseconds for
//! the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
use crate::error::ComponentRange;
/// Serialize an `OffsetDateTime` as its Unix timestamp with milliseconds
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let timestamp = datetime.unix_timestamp_nanos() / 1_000_000;
timestamp.serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp with milliseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
let value: i128 = <_>::deserialize(deserializer)?;
OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000)
.map_err(ComponentRange::into_de_error)
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with milliseconds
/// for the purposes of serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with milliseconds
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000_000)
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with milliseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer)?
.map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000))
.transpose()
.map_err(ComponentRange::into_de_error)
}
}

View File

@@ -0,0 +1,78 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with milliseconds for
//! the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use num_conv::prelude::*;
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
use crate::error::ComponentRange;
/// Serialize an `OffsetDateTime` as its Unix timestamp with milliseconds
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let timestamp = (datetime.unix_timestamp_nanos() / 1_000_000).truncate::<i64>();
timestamp.serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp with milliseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
let value: i64 = <_>::deserialize(deserializer)?;
OffsetDateTime::from_unix_timestamp_nanos(value.extend::<i128>() * 1_000_000)
.map_err(ComponentRange::into_de_error)
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with milliseconds
/// for the purposes of serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with milliseconds
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(|timestamp| (timestamp.unix_timestamp_nanos() / 1_000_000).truncate::<i64>())
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with milliseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer)?
.map(|value: i64| {
OffsetDateTime::from_unix_timestamp_nanos(value.extend::<i128>() * 1_000_000)
})
.transpose()
.map_err(ComponentRange::into_de_error)
}
}

77
vendor/time/src/serde/timestamp/mod.rs vendored Normal file
View File

@@ -0,0 +1,77 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
pub mod microseconds;
pub mod milliseconds;
pub mod milliseconds_i64;
pub mod nanoseconds;
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
use crate::error::ComponentRange;
/// Serialize an `OffsetDateTime` as its Unix timestamp
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime.unix_timestamp().serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?)
.map_err(ComponentRange::into_de_error)
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] for the purposes of
/// serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(OffsetDateTime::unix_timestamp)
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer)?
.map(OffsetDateTime::from_unix_timestamp)
.transpose()
.map_err(ComponentRange::into_de_error)
}
}

View File

@@ -0,0 +1,73 @@
//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with nanoseconds for
//! the purposes of serde.
//!
//! Use this module in combination with serde's [`#[with]`][with] attribute.
//!
//! When deserializing, the offset is assumed to be UTC.
//!
//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
//! [with]: https://serde.rs/field-attrs.html#with
use serde_core::{Deserialize, Deserializer, Serialize, Serializer};
use crate::OffsetDateTime;
use crate::error::ComponentRange;
/// Serialize an `OffsetDateTime` as its Unix timestamp with nanoseconds
#[inline]
pub fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
datetime.unix_timestamp_nanos().serialize(serializer)
}
/// Deserialize an `OffsetDateTime` from its Unix timestamp with nanoseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
where
D: Deserializer<'a>,
{
OffsetDateTime::from_unix_timestamp_nanos(<_>::deserialize(deserializer)?)
.map_err(ComponentRange::into_de_error)
}
/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with nanoseconds
/// for the purposes of serde.
///
/// Use this module in combination with serde's [`#[with]`][with] attribute.
///
/// Note: Due to [serde-rs/serde#2878], you will need to apply `#[serde(default)]` if you want a
/// missing field to deserialize as `None`.
///
/// When deserializing, the offset is assumed to be UTC.
///
/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time
/// [with]: https://serde.rs/field-attrs.html#with
/// [serde-rs/serde#2878]: https://github.com/serde-rs/serde/issues/2878
pub mod option {
use super::*;
/// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with nanoseconds
#[inline]
pub fn serialize<S>(option: &Option<OffsetDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
option
.map(OffsetDateTime::unix_timestamp_nanos)
.serialize(serializer)
}
/// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with nanoseconds
#[inline]
pub fn deserialize<'a, D>(deserializer: D) -> Result<Option<OffsetDateTime>, D::Error>
where
D: Deserializer<'a>,
{
Option::deserialize(deserializer)?
.map(OffsetDateTime::from_unix_timestamp_nanos)
.transpose()
.map_err(ComponentRange::into_de_error)
}
}

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

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

View File

@@ -0,0 +1,9 @@
//! A fallback for any OS not covered.
use crate::{OffsetDateTime, UtcOffset};
#[expect(clippy::missing_docs_in_private_items)]
#[inline]
pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> {
None
}

View File

@@ -0,0 +1,27 @@
//! A method to obtain the local offset from UTC.
#![allow(
clippy::missing_const_for_fn,
reason = "system APIs are inherently not const, so this will only trigger on the fallback"
)]
#[cfg_attr(target_family = "windows", path = "windows.rs")]
#[cfg_attr(target_family = "unix", path = "unix.rs")]
#[cfg_attr(
all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm-bindgen"
),
path = "wasm_js.rs"
)]
mod imp;
use crate::{OffsetDateTime, UtcOffset};
/// Attempt to obtain the system's UTC offset. If the offset cannot be determined, `None` is
/// returned.
#[inline]
pub(crate) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
imp::local_offset_at(datetime)
}

Some files were not shown because too many files have changed in this diff Show More