time-0.3.47/.cargo_vcs_info.json0000644000000001421046102023000121040ustar { "git": { "sha1": "d5144cd2874862d46466c900910cd8577d066019" }, "path_in_vcs": "time" }time-0.3.47/Cargo.lock0000644000000531251046102023000100700ustar # 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" time-0.3.47/Cargo.toml0000644000000153301046102023000101070ustar # 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 ", "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" time-0.3.47/Cargo.toml.orig000064400000000000000000000056201046102023000135470ustar 00000000000000lints.workspace = true [package] name = "time" version = "0.3.47" categories = [ "date-and-time", "no-std", "parser-implementations", "value-formatting", ] description = "Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std]." include = ["{src,tests,benchmarks}/**/*", "LICENSE-*", "README.md"] readme = "../README.md" authors.workspace = true edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--generate-link-to-definition"] [lib] bench = false [features] default = ["std"] alloc = ["serde_core?/alloc"] 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"] # Deprecated in favor of using the relevant flags directly. serde-well-known = ["serde", "formatting", "parsing"] std = ["alloc"] wasm-bindgen = ["dep:js-sys"] # If adding an optional dependency, be sure to use the `dep:` prefix above to avoid an implicit # feature gate. [dependencies] deranged.workspace = true itoa = { workspace = true, optional = true } num-conv.workspace = true powerfmt.workspace = true quickcheck = { workspace = true, optional = true } rand08 = { workspace = true, optional = true } rand09 = { workspace = true, optional = true } serde_core = { workspace = true, optional = true } time-core.workspace = true time-macros = { workspace = true, optional = true } [target.'cfg(target_family = "unix")'.dependencies] libc = { workspace = true, optional = true } num_threads = { workspace = true, optional = true } [target.'cfg(all(target_family = "wasm", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies] js-sys = { workspace = true, optional = true } [dev-dependencies] num-conv.workspace = true rand08.workspace = true rand09 = { workspace = true, features = ["small_rng"] } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true serde_test.workspace = true quickcheck_macros.workspace = true time-macros.workspace = true rstest.workspace = true rstest_reuse.workspace = true [target.'cfg(__ui_tests)'.dev-dependencies] trybuild.workspace = true [target.'cfg(bench)'.dev-dependencies] criterion.workspace = true [[bench]] name = "benchmarks" harness = false path = "benchmarks/main.rs" [[test]] name = "tests" path = "tests/integration/main.rs" time-0.3.47/LICENSE-Apache000064400000000000000000000236761046102023000130570ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS time-0.3.47/LICENSE-MIT000064400000000000000000000020411046102023000123060ustar 00000000000000Copyright (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. time-0.3.47/README.md000064400000000000000000000050021046102023000121310ustar 00000000000000# 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. time-0.3.47/benchmarks/date.rs000064400000000000000000000225231046102023000142610ustar 00000000000000#![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))); } }); } } time-0.3.47/benchmarks/duration.rs000064400000000000000000000505451046102023000151760ustar 00000000000000use 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); } } time-0.3.47/benchmarks/formatting.rs000064400000000000000000000175021046102023000155170ustar 00000000000000use 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()); } } time-0.3.47/benchmarks/instant.rs000064400000000000000000000043551046102023000150270ustar 00000000000000#![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] ); } } time-0.3.47/benchmarks/main.rs000064400000000000000000000063751046102023000142770ustar 00000000000000//! 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(mut slice: [T; N]) -> [T; N] { use ::rand09::prelude::*; let mut seed = SmallRng::seed_from_u64(0); slice.shuffle(&mut seed); slice } time-0.3.47/benchmarks/month.rs000064400000000000000000000042161046102023000144700ustar 00000000000000use 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)); } } time-0.3.47/benchmarks/offset_date_time.rs000064400000000000000000000372131046102023000166470ustar 00000000000000use 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)); } } time-0.3.47/benchmarks/parsing.rs000064400000000000000000000203641046102023000150100ustar 00000000000000use 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)); } } time-0.3.47/benchmarks/primitive_date_time.rs000064400000000000000000000122521046102023000173650ustar 00000000000000use 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))); } } time-0.3.47/benchmarks/rand08.rs000064400000000000000000000013761046102023000144430ustar 00000000000000use 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, ]; time-0.3.47/benchmarks/rand09.rs000064400000000000000000000025441046102023000144420ustar 00000000000000use 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) } } time-0.3.47/benchmarks/time.rs000064400000000000000000000174161046102023000143070ustar 00000000000000use 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))); } } time-0.3.47/benchmarks/utc_date_time.rs000064400000000000000000000004311046102023000161440ustar 00000000000000use 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)))); } } time-0.3.47/benchmarks/utc_offset.rs000064400000000000000000000027231046102023000155050ustar 00000000000000use 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); } } time-0.3.47/benchmarks/util.rs000064400000000000000000000023461046102023000143220ustar 00000000000000use 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))); } }); } } time-0.3.47/benchmarks/weekday.rs000064400000000000000000000056361046102023000150030ustar 00000000000000use 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()); } } time-0.3.47/src/date.rs000064400000000000000000001520051046102023000127320ustar 00000000000000//! The [`Date`] struct and its associated `impl`s. #[cfg(feature = "formatting")] use alloc::string::String; use core::num::NonZero; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::time::Duration as StdDuration; use core::{cmp, fmt}; #[cfg(feature = "formatting")] use std::io; use deranged::RangedI32; use num_conv::prelude::*; use powerfmt::ext::FormatterExt; use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; use crate::convert::*; use crate::ext::DigitCount; #[cfg(feature = "formatting")] use crate::formatting::Formattable; use crate::internal_macros::{const_try, const_try_opt, div_floor, ensure_ranged}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::{days_in_month_leap, range_validated, weeks_in_year}; use crate::{Duration, Month, PrimitiveDateTime, Time, Weekday, error, hint}; type Year = RangedI32; /// The minimum valid year. pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") { -999_999 } else { -9999 }; /// The maximum valid year. pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") { 999_999 } else { 9999 }; /// Date in the proleptic Gregorian calendar. /// /// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999 /// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications /// and introduces some ambiguities when parsing. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Date { /// Bitpacked field containing the year, ordinal, and whether the year is a leap year. // | x | xxxxxxxxxxxxxxxxxxxxx | x | xxxxxxxxx | // | 1 bit | 21 bits | 1 bit | 9 bits | // | unassigned | year | is leap year? | ordinal | // The year is 15 bits when `large-dates` is not enabled. value: NonZero, } impl Date { /// Provide a representation of `Date` as a `i32`. This value can be used for equality, hashing, /// and ordering. /// /// **Note**: This value is explicitly signed, so do not cast this to or treat this as an /// unsigned integer. Doing so will lead to incorrect results for values with differing /// signs. #[inline] pub(crate) const fn as_i32(self) -> i32 { self.value.get() } /// The Unix epoch: 1970-01-01 // Safety: `ordinal` is not zero. pub(crate) const UNIX_EPOCH: Self = unsafe { Self::__from_ordinal_date_unchecked(1970, 1) }; /// The minimum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. // Safety: `ordinal` is not zero. pub const MIN: Self = unsafe { Self::__from_ordinal_date_unchecked(MIN_YEAR, 1) }; /// The maximum valid `Date`. /// /// The value of this may vary depending on the feature flags enabled. // Safety: `ordinal` is not zero. pub const MAX: Self = unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, range_validated::days_in_year(MAX_YEAR)) }; /// Construct a `Date` from its internal representation, the validity of which must be /// guaranteed by the caller. /// /// # Safety /// /// - `ordinal` must be non-zero and at most the number of days in `year` /// - `is_leap_year` must be `true` if and only if `year` is a leap year #[inline] #[track_caller] const unsafe fn from_parts(year: i32, is_leap_year: bool, ordinal: u16) -> Self { debug_assert!(year >= MIN_YEAR); debug_assert!(year <= MAX_YEAR); debug_assert!(ordinal != 0); debug_assert!(ordinal <= range_validated::days_in_year(year)); debug_assert!(range_validated::is_leap_year(year) == is_leap_year); Self { // Safety: `ordinal` is not zero. value: unsafe { NonZero::new_unchecked((year << 10) | ((is_leap_year as i32) << 9) | ordinal as i32) }, } } /// Construct a `Date` from the year and ordinal values, the validity of which must be /// guaranteed by the caller. /// /// # Safety /// /// - `year` must be in the range `MIN_YEAR..=MAX_YEAR`. /// - `ordinal` must be non-zero and at most the number of days in `year`. #[doc(hidden)] #[inline] #[track_caller] pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { // Safety: The caller must guarantee that `ordinal` is not zero and that the year is in // range. unsafe { Self::from_parts(year, range_validated::is_leap_year(year), ordinal) } } /// Attempt to create a `Date` from the year, month, and day. /// /// ```rust /// # use time::{Date, Month}; /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok()); /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok()); /// ``` /// /// ```rust /// # use time::{Date, Month}; /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year. /// ``` #[inline] pub const fn from_calendar_date( year: i32, month: Month, day: u8, ) -> Result { /// Cumulative days through the beginning of a month in both common and leap years. const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; ensure_ranged!(Year: year); let is_leap_year = range_validated::is_leap_year(year); match day { 1..=28 => {} 29..=31 if day <= days_in_month_leap(month as u8, is_leap_year) => hint::cold_path(), _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("day")); } } // Safety: `ordinal` is not zero and `is_leap_year` is correct. Ok(unsafe { Self::from_parts( year, is_leap_year, DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year as usize][month as usize - 1] + day as u16, ) }) } /// Attempt to create a `Date` from the year and ordinal day number. /// /// ```rust /// # use time::Date; /// assert!(Date::from_ordinal_date(2019, 1).is_ok()); /// assert!(Date::from_ordinal_date(2019, 365).is_ok()); /// ``` /// /// ```rust /// # use time::Date; /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year. /// ``` #[inline] pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result { ensure_ranged!(Year: year); let is_leap_year = range_validated::is_leap_year(year); match ordinal { 1..=365 => {} 366 if is_leap_year => hint::cold_path(), _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("ordinal")); } } // Safety: `ordinal` is not zero. Ok(unsafe { Self::from_parts(year, is_leap_year, ordinal) }) } /// Attempt to create a `Date` from the ISO year, week, and weekday. /// /// ```rust /// # use time::{Date, Weekday::*}; /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok()); /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok()); /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok()); /// ``` /// /// ```rust /// # use time::{Date, Weekday::*}; /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks. /// ``` pub const fn from_iso_week_date( year: i32, week: u8, weekday: Weekday, ) -> Result { ensure_ranged!(Year: year); match week { 1..=52 => {} 53 if week <= weeks_in_year(year) => hint::cold_path(), _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("week")); } } let adj_year = year - 1; let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + div_floor!(adj_year, 400); let jan_4 = match (raw % 7) as i8 { -6 | 1 => 8, -5 | 2 => 9, -4 | 3 => 10, -3 | 4 => 4, -2 | 5 => 5, -1 | 6 => 6, _ => 7, }; let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; if ordinal <= 0 { // Safety: `ordinal` is not zero. return Ok(unsafe { Self::__from_ordinal_date_unchecked( year - 1, ordinal .cast_unsigned() .wrapping_add(range_validated::days_in_year(year - 1)), ) }); } let is_leap_year = range_validated::is_leap_year(year); let days_in_year = if is_leap_year { 366 } else { 365 }; let ordinal = ordinal.cast_unsigned(); Ok(if ordinal > days_in_year { // Safety: `ordinal` is not zero. unsafe { Self::__from_ordinal_date_unchecked(year + 1, ordinal - days_in_year) } } else { // Safety: `ordinal` is not zero and `is_leap_year` is correct. unsafe { Self::from_parts(year, is_leap_year, ordinal) } }) } /// Create a `Date` from the Julian day. /// /// ```rust /// # use time::Date; /// # use time_macros::date; /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713-11-24))); /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000-01-01))); /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019-01-01))); /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019-12-31))); /// ``` #[doc(alias = "from_julian_date")] #[inline] pub const fn from_julian_day(julian_day: i32) -> Result { type JulianDay = RangedI32<{ Date::MIN.to_julian_day() }, { Date::MAX.to_julian_day() }>; ensure_ranged!(JulianDay: julian_day); // Safety: The Julian day number is in range. Ok(unsafe { Self::from_julian_day_unchecked(julian_day) }) } /// Create a `Date` from the Julian day. /// /// # Safety /// /// The provided Julian day number must be between `Date::MIN.to_julian_day()` and /// `Date::MAX.to_julian_day()` inclusive. #[inline] pub(crate) const unsafe fn from_julian_day_unchecked(julian_day: i32) -> Self { debug_assert!(julian_day >= Self::MIN.to_julian_day()); debug_assert!(julian_day <= Self::MAX.to_julian_day()); const ERAS: u32 = 5_949; // Rata Die shift: const D_SHIFT: u32 = 146097 * ERAS - 1_721_060; // Year shift: const Y_SHIFT: u32 = 400 * ERAS; const CEN_MUL: u32 = ((4u64 << 47) / 146_097) as u32; const JUL_MUL: u32 = ((4u64 << 40) / 1_461 + 1) as u32; const CEN_CUT: u32 = ((365u64 << 32) / 36_525) as u32; let day = julian_day.cast_unsigned().wrapping_add(D_SHIFT); let c_n = (day as u64 * CEN_MUL as u64) >> 15; let cen = (c_n >> 32) as u32; let cpt = c_n as u32; let ijy = cpt > CEN_CUT || cen.is_multiple_of(4); let jul = day - cen / 4 + cen; let y_n = (jul as u64 * JUL_MUL as u64) >> 8; let yrs = (y_n >> 32) as u32; let ypt = y_n as u32; let year = yrs.wrapping_sub(Y_SHIFT).cast_signed(); let ordinal = ((ypt as u64 * 1_461) >> 34) as u32 + ijy as u32; let leap = yrs.is_multiple_of(4) & ijy; // Safety: `ordinal` is not zero and `is_leap_year` is correct, so long as the Julian day // number is in range, which is guaranteed by the caller. unsafe { Self::from_parts(year, leap, ordinal as u16) } } /// Whether `is_leap_year(self.year())` is `true`. /// /// This method is optimized to take advantage of the fact that the value is pre-computed upon /// construction and stored in the bitpacked struct. #[inline] const fn is_in_leap_year(self) -> bool { (self.value.get() >> 9) & 1 == 1 } /// Get the year of the date. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).year(), 2019); /// assert_eq!(date!(2019-12-31).year(), 2019); /// assert_eq!(date!(2020-01-01).year(), 2020); /// ``` #[inline] pub const fn year(self) -> i32 { self.value.get() >> 10 } /// Get the month. /// /// ```rust /// # use time::Month; /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).month(), Month::January); /// assert_eq!(date!(2019-12-31).month(), Month::December); /// ``` #[inline] pub const fn month(self) -> Month { let ordinal = self.ordinal() as u32; let jan_feb_len = 59 + self.is_in_leap_year() as u32; let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len { (0, 0) } else { (2, jan_feb_len) }; let ordinal = ordinal - ordinal_adj; let month = ((ordinal * 268 + 8031) >> 13) + month_adj; // Safety: `month` is guaranteed to be between 1 and 12 inclusive. unsafe { match Month::from_number(NonZero::new_unchecked(month as u8)) { Ok(month) => month, Err(_) => core::hint::unreachable_unchecked(), } } } /// Get the day of the month. /// /// The returned value will always be in the range `1..=31`. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).day(), 1); /// assert_eq!(date!(2019-12-31).day(), 31); /// ``` #[inline] pub const fn day(self) -> u8 { let ordinal = self.ordinal() as u32; let jan_feb_len = 59 + self.is_in_leap_year() as u32; let ordinal_adj = if ordinal <= jan_feb_len { 0 } else { jan_feb_len }; let ordinal = ordinal - ordinal_adj; let month = (ordinal * 268 + 8031) >> 13; let days_in_preceding_months = (month * 3917 - 3866) >> 7; (ordinal - days_in_preceding_months) as u8 } /// Get the day of the year. /// /// The returned value will always be in the range `1..=366` (`1..=365` for common years). /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).ordinal(), 1); /// assert_eq!(date!(2019-12-31).ordinal(), 365); /// ``` #[inline] pub const fn ordinal(self) -> u16 { (self.value.get() & 0x1FF) as u16 } /// Get the ISO 8601 year and week number. #[inline] pub(crate) const fn iso_year_week(self) -> (i32, u8) { let (year, ordinal) = self.to_ordinal_date(); match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as u8 { 0 => (year - 1, weeks_in_year(year - 1)), 53 if weeks_in_year(year) == 52 => (year + 1, 1), week => (year, week), } } /// Get the ISO week number. /// /// The returned value will always be in the range `1..=53`. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).iso_week(), 1); /// assert_eq!(date!(2019-10-04).iso_week(), 40); /// assert_eq!(date!(2020-01-01).iso_week(), 1); /// assert_eq!(date!(2020-12-31).iso_week(), 53); /// assert_eq!(date!(2021-01-01).iso_week(), 53); /// ``` #[inline] pub const fn iso_week(self) -> u8 { self.iso_year_week().1 } /// Get the week number where week 1 begins on the first Sunday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).sunday_based_week(), 0); /// assert_eq!(date!(2020-01-01).sunday_based_week(), 0); /// assert_eq!(date!(2020-12-31).sunday_based_week(), 52); /// assert_eq!(date!(2021-01-01).sunday_based_week(), 0); /// ``` #[inline] pub const fn sunday_based_week(self) -> u8 { ((self.ordinal().cast_signed() - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as u8 } /// Get the week number where week 1 begins on the first Monday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).monday_based_week(), 0); /// assert_eq!(date!(2020-01-01).monday_based_week(), 0); /// assert_eq!(date!(2020-12-31).monday_based_week(), 52); /// assert_eq!(date!(2021-01-01).monday_based_week(), 0); /// ``` #[inline] pub const fn monday_based_week(self) -> u8 { ((self.ordinal().cast_signed() - self.weekday().number_days_from_monday() as i16 + 6) / 7) as u8 } /// Get the year, month, and day. /// /// ```rust /// # use time::Month; /// # use time_macros::date; /// assert_eq!( /// date!(2019-01-01).to_calendar_date(), /// (2019, Month::January, 1) /// ); /// ``` #[inline] pub const fn to_calendar_date(self) -> (i32, Month, u8) { let (year, ordinal) = self.to_ordinal_date(); let ordinal = ordinal as u32; let jan_feb_len = 59 + self.is_in_leap_year() as u32; let (month_adj, ordinal_adj) = if ordinal <= jan_feb_len { (0, 0) } else { (2, jan_feb_len) }; let ordinal = ordinal - ordinal_adj; let month = (ordinal * 268 + 8031) >> 13; let days_in_preceding_months = (month * 3917 - 3866) >> 7; let day = ordinal - days_in_preceding_months; let month = month + month_adj; ( year, // Safety: `month` is guaranteed to be between 1 and 12 inclusive. unsafe { match Month::from_number(NonZero::new_unchecked(month as u8)) { Ok(month) => month, Err(_) => core::hint::unreachable_unchecked(), } }, day as u8, ) } /// Get the year and ordinal day number. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).to_ordinal_date(), (2019, 1)); /// ``` #[inline] pub const fn to_ordinal_date(self) -> (i32, u16) { (self.year(), self.ordinal()) } /// Get the ISO 8601 year, week number, and weekday. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).to_iso_week_date(), (2019, 1, Tuesday)); /// assert_eq!(date!(2019-10-04).to_iso_week_date(), (2019, 40, Friday)); /// assert_eq!(date!(2020-01-01).to_iso_week_date(), (2020, 1, Wednesday)); /// assert_eq!(date!(2020-12-31).to_iso_week_date(), (2020, 53, Thursday)); /// assert_eq!(date!(2021-01-01).to_iso_week_date(), (2020, 53, Friday)); /// ``` #[inline] pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { let (year, ordinal) = self.to_ordinal_date(); let weekday = self.weekday(); match ((ordinal + 10 - weekday.number_from_monday() as u16) / 7) as u8 { 0 => (year - 1, weeks_in_year(year - 1), weekday), 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday), week => (year, week, weekday), } } /// Get the weekday. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).weekday(), Tuesday); /// assert_eq!(date!(2019-02-01).weekday(), Friday); /// assert_eq!(date!(2019-03-01).weekday(), Friday); /// assert_eq!(date!(2019-04-01).weekday(), Monday); /// assert_eq!(date!(2019-05-01).weekday(), Wednesday); /// assert_eq!(date!(2019-06-01).weekday(), Saturday); /// assert_eq!(date!(2019-07-01).weekday(), Monday); /// assert_eq!(date!(2019-08-01).weekday(), Thursday); /// assert_eq!(date!(2019-09-01).weekday(), Sunday); /// assert_eq!(date!(2019-10-01).weekday(), Tuesday); /// assert_eq!(date!(2019-11-01).weekday(), Friday); /// assert_eq!(date!(2019-12-01).weekday(), Sunday); /// ``` #[inline] pub const fn weekday(self) -> Weekday { match self.to_julian_day() % 7 { -6 | 1 => Weekday::Tuesday, -5 | 2 => Weekday::Wednesday, -4 | 3 => Weekday::Thursday, -3 | 4 => Weekday::Friday, -2 | 5 => Weekday::Saturday, -1 | 6 => Weekday::Sunday, val => { debug_assert!(val == 0); Weekday::Monday } } } /// Get the next calendar date. /// /// ```rust /// # use time::Date; /// # use time_macros::date; /// assert_eq!(date!(2019-01-01).next_day(), Some(date!(2019-01-02))); /// assert_eq!(date!(2019-01-31).next_day(), Some(date!(2019-02-01))); /// assert_eq!(date!(2019-12-31).next_day(), Some(date!(2020-01-01))); /// assert_eq!(Date::MAX.next_day(), None); /// ``` #[inline] pub const fn next_day(self) -> Option { let is_last_day_of_year = matches!(self.value.get() & 0x3FF, 365 | 878); if hint::unlikely(is_last_day_of_year) { if self.value.get() == Self::MAX.value.get() { None } else { // Safety: `ordinal` is not zero. unsafe { Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) } } } else { Some(Self { // Safety: `ordinal` is not zero. value: unsafe { NonZero::new_unchecked(self.value.get() + 1) }, }) } } /// Get the previous calendar date. /// /// ```rust /// # use time::Date; /// # use time_macros::date; /// assert_eq!(date!(2019-01-02).previous_day(), Some(date!(2019-01-01))); /// assert_eq!(date!(2019-02-01).previous_day(), Some(date!(2019-01-31))); /// assert_eq!(date!(2020-01-01).previous_day(), Some(date!(2019-12-31))); /// assert_eq!(Date::MIN.previous_day(), None); /// ``` #[inline] pub const fn previous_day(self) -> Option { if hint::likely(self.ordinal() != 1) { Some(Self { // Safety: `ordinal` is not zero. value: unsafe { NonZero::new_unchecked(self.value.get() - 1) }, }) } else if self.value.get() == Self::MIN.value.get() { None } else { let year = self.year() - 1; let is_leap_year = range_validated::is_leap_year(year); let ordinal = if is_leap_year { 366 } else { 365 }; // Safety: `ordinal` is not zero, `is_leap_year` is correct. Some(unsafe { Self::from_parts(year, is_leap_year, ordinal) }) } } /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`. /// /// # Panics /// Panics if an overflow occurred. /// /// # Examples /// ``` /// # use time::Weekday; /// # use time_macros::date; /// assert_eq!( /// date!(2023-06-28).next_occurrence(Weekday::Monday), /// date!(2023-07-03) /// ); /// assert_eq!( /// date!(2023-06-19).next_occurrence(Weekday::Monday), /// date!(2023-06-26) /// ); /// ``` #[inline] #[track_caller] pub const fn next_occurrence(self, weekday: Weekday) -> Self { self.checked_next_occurrence(weekday) .expect("overflow calculating the next occurrence of a weekday") } /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`. /// /// # Panics /// Panics if an overflow occurred. /// /// # Examples /// ``` /// # use time::Weekday; /// # use time_macros::date; /// assert_eq!( /// date!(2023-06-28).prev_occurrence(Weekday::Monday), /// date!(2023-06-26) /// ); /// assert_eq!( /// date!(2023-06-19).prev_occurrence(Weekday::Monday), /// date!(2023-06-12) /// ); /// ``` #[inline] #[track_caller] pub const fn prev_occurrence(self, weekday: Weekday) -> Self { self.checked_prev_occurrence(weekday) .expect("overflow calculating the previous occurrence of a weekday") } /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`. /// /// # Panics /// Panics if an overflow occurred or if `n == 0`. /// /// # Examples /// ``` /// # use time::Weekday; /// # use time_macros::date; /// assert_eq!( /// date!(2023-06-25).nth_next_occurrence(Weekday::Monday, 5), /// date!(2023-07-24) /// ); /// assert_eq!( /// date!(2023-06-26).nth_next_occurrence(Weekday::Monday, 5), /// date!(2023-07-31) /// ); /// ``` #[inline] #[track_caller] pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self { self.checked_nth_next_occurrence(weekday, n) .expect("overflow calculating the next occurrence of a weekday") } /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`. /// /// # Panics /// Panics if an overflow occurred or if `n == 0`. /// /// # Examples /// ``` /// # use time::Weekday; /// # use time_macros::date; /// assert_eq!( /// date!(2023-06-27).nth_prev_occurrence(Weekday::Monday, 3), /// date!(2023-06-12) /// ); /// assert_eq!( /// date!(2023-06-26).nth_prev_occurrence(Weekday::Monday, 3), /// date!(2023-06-05) /// ); /// ``` #[inline] #[track_caller] pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self { self.checked_nth_prev_occurrence(weekday, n) .expect("overflow calculating the previous occurrence of a weekday") } /// Get the Julian day for the date. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(-4713-11-24).to_julian_day(), 0); /// assert_eq!(date!(2000-01-01).to_julian_day(), 2_451_545); /// assert_eq!(date!(2019-01-01).to_julian_day(), 2_458_485); /// assert_eq!(date!(2019-12-31).to_julian_day(), 2_458_849); /// ``` #[inline] pub const fn to_julian_day(self) -> i32 { let (year, ordinal) = self.to_ordinal_date(); // The algorithm requires a non-negative year. Add the lowest value to make it so. This is // adjusted for at the end with the final subtraction. let adj_year = year + 999_999; let century = adj_year / 100; let days_before_year = (1461 * adj_year as i64 / 4) as i32 - century + century / 4; days_before_year + ordinal as i32 - 363_521_075 } /// Computes `self + duration`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_add(1.days()), None); /// assert_eq!(Date::MIN.checked_add((-2).days()), None); /// assert_eq!( /// date!(2020-12-31).checked_add(2.days()), /// Some(date!(2021-01-02)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ```rust /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX)); /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN)); /// assert_eq!( /// date!(2020-12-31).checked_add(23.hours()), /// Some(date!(2020-12-31)) /// ); /// assert_eq!( /// date!(2020-12-31).checked_add(47.hours()), /// Some(date!(2021-01-01)) /// ); /// ``` #[inline] pub const fn checked_add(self, duration: Duration) -> Option { let whole_days = duration.whole_days(); if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as i32)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } /// Computes `self + duration`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Date, ext::NumericalStdDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_add_std(1.std_days()), None); /// assert_eq!( /// date!(2020-12-31).checked_add_std(2.std_days()), /// Some(date!(2021-01-02)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ```rust /// # use time::{Date, ext::NumericalStdDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_add_std(23.std_hours()), Some(Date::MAX)); /// assert_eq!( /// date!(2020-12-31).checked_add_std(23.std_hours()), /// Some(date!(2020-12-31)) /// ); /// assert_eq!( /// date!(2020-12-31).checked_add_std(47.std_hours()), /// Some(date!(2021-01-01)) /// ); /// ``` #[inline] pub const fn checked_add_std(self, duration: StdDuration) -> Option { let whole_days = duration.as_secs() / Second::per_t::(Day); if whole_days > i32::MAX as u64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as i32)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_sub((-2).days()), None); /// assert_eq!(Date::MIN.checked_sub(1.days()), None); /// assert_eq!( /// date!(2020-12-31).checked_sub(2.days()), /// Some(date!(2020-12-29)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX)); /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN)); /// assert_eq!( /// date!(2020-12-31).checked_sub(23.hours()), /// Some(date!(2020-12-31)) /// ); /// assert_eq!( /// date!(2020-12-31).checked_sub(47.hours()), /// Some(date!(2020-12-30)) /// ); /// ``` #[inline] pub const fn checked_sub(self, duration: Duration) -> Option { let whole_days = duration.whole_days(); if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as i32)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalStdDuration}; /// # use time_macros::date; /// assert_eq!(Date::MIN.checked_sub_std(1.std_days()), None); /// assert_eq!( /// date!(2020-12-31).checked_sub_std(2.std_days()), /// Some(date!(2020-12-29)) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ``` /// # use time::{Date, ext::NumericalStdDuration}; /// # use time_macros::date; /// assert_eq!(Date::MIN.checked_sub_std(23.std_hours()), Some(Date::MIN)); /// assert_eq!( /// date!(2020-12-31).checked_sub_std(23.std_hours()), /// Some(date!(2020-12-31)) /// ); /// assert_eq!( /// date!(2020-12-31).checked_sub_std(47.std_hours()), /// Some(date!(2020-12-30)) /// ); /// ``` #[inline] pub const fn checked_sub_std(self, duration: StdDuration) -> Option { let whole_days = duration.as_secs() / Second::per_t::(Day); if whole_days > i32::MAX as u64 { return None; } let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as i32)); if let Ok(date) = Self::from_julian_day(julian_day) { Some(date) } else { None } } /// Calculates the first occurrence of a weekday that is strictly later than a given `Date`. /// Returns `None` if an overflow occurred. #[inline] pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option { let day_diff = match weekday as i8 - self.weekday() as i8 { 1 | -6 => 1, 2 | -5 => 2, 3 | -4 => 3, 4 | -3 => 4, 5 | -2 => 5, 6 | -1 => 6, val => { debug_assert!(val == 0); 7 } }; self.checked_add(Duration::days(day_diff)) } /// Calculates the first occurrence of a weekday that is strictly earlier than a given `Date`. /// Returns `None` if an overflow occurred. #[inline] pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option { let day_diff = match weekday as i8 - self.weekday() as i8 { 1 | -6 => 6, 2 | -5 => 5, 3 | -4 => 4, 4 | -3 => 3, 5 | -2 => 2, 6 | -1 => 1, val => { debug_assert!(val == 0); 7 } }; self.checked_sub(Duration::days(day_diff)) } /// Calculates the `n`th occurrence of a weekday that is strictly later than a given `Date`. /// Returns `None` if an overflow occurred or if `n == 0`. #[inline] pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option { if n == 0 { return None; } const_try_opt!(self.checked_next_occurrence(weekday)) .checked_add(Duration::weeks(n as i64 - 1)) } /// Calculates the `n`th occurrence of a weekday that is strictly earlier than a given `Date`. /// Returns `None` if an overflow occurred or if `n == 0`. #[inline] pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option { if n == 0 { return None; } const_try_opt!(self.checked_prev_occurrence(weekday)) .checked_sub(Duration::weeks(n as i64 - 1)) } /// Computes `self + duration`, saturating value on overflow. /// /// ```rust /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX); /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN); /// assert_eq!( /// date!(2020-12-31).saturating_add(2.days()), /// date!(2021-01-02) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ```rust /// # use time::ext::NumericalDuration; /// # use time_macros::date; /// assert_eq!( /// date!(2020-12-31).saturating_add(23.hours()), /// date!(2020-12-31) /// ); /// assert_eq!( /// date!(2020-12-31).saturating_add(47.hours()), /// date!(2021-01-01) /// ); /// ``` #[inline] pub const fn saturating_add(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_add(duration) { datetime } else if duration.is_negative() { Self::MIN } else { debug_assert!(duration.is_positive()); Self::MAX } } /// Computes `self - duration`, saturating value on overflow. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::date; /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX); /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN); /// assert_eq!( /// date!(2020-12-31).saturating_sub(2.days()), /// date!(2020-12-29) /// ); /// ``` /// /// # Note /// /// This function only takes whole days into account. /// /// ``` /// # use time::ext::NumericalDuration; /// # use time_macros::date; /// assert_eq!( /// date!(2020-12-31).saturating_sub(23.hours()), /// date!(2020-12-31) /// ); /// assert_eq!( /// date!(2020-12-31).saturating_sub(47.hours()), /// date!(2020-12-30) /// ); /// ``` #[inline] pub const fn saturating_sub(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_sub(duration) { datetime } else if duration.is_negative() { Self::MAX } else { debug_assert!(duration.is_positive()); Self::MIN } } /// Replace the year. The month and day will be unchanged. /// /// ```rust /// # use time_macros::date; /// assert_eq!( /// date!(2022-02-18).replace_year(2019), /// Ok(date!(2019-02-18)) /// ); /// assert!(date!(2022-02-18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year /// assert!(date!(2022-02-18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year /// ``` #[inline] #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_year(self, year: i32) -> Result { ensure_ranged!(Year: year); let new_is_leap_year = range_validated::is_leap_year(year); let ordinal = self.ordinal(); // Dates in January and February are unaffected by leap years. if ordinal <= 59 { // Safety: `ordinal` is not zero and `is_leap_year` is correct. return Ok(unsafe { Self::from_parts(year, new_is_leap_year, ordinal) }); } match (self.is_in_leap_year(), new_is_leap_year) { (false, false) | (true, true) => { Ok(Self { // Safety: Whether the year is leap or common, the ordinal are unchanged, with // only the year being replaced. value: unsafe { NonZero::new_unchecked((year << 10) | (self.value.get() & 0x3FF)) }, }) } // February 29 does not exist in common years. (true, false) if ordinal == 60 => Err(error::ComponentRange::conditional("day")), // We're going from a common year to a leap year. Shift dates in March and later by // one day. // Safety: `ordinal` is not zero and `is_leap_year` is correct. (false, true) => Ok(unsafe { Self::from_parts(year, true, ordinal + 1) }), // We're going from a leap year to a common year. Shift dates in January and // February by one day. // Safety: `ordinal` is not zero and `is_leap_year` is correct. (true, false) => Ok(unsafe { Self::from_parts(year, false, ordinal - 1) }), } } /// Replace the month of the year. /// /// ```rust /// # use time_macros::date; /// # use time::Month; /// assert_eq!( /// date!(2022-02-18).replace_month(Month::January), /// Ok(date!(2022-01-18)) /// ); /// assert!(date!(2022-01-30) /// .replace_month(Month::February) /// .is_err()); // 30 isn't a valid day in February /// ``` #[inline] #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_month(self, month: Month) -> Result { /// Cumulative days through the beginning of a month in both common and leap years. const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [ [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], ]; let (year, ordinal) = self.to_ordinal_date(); let mut ordinal = ordinal as u32; let is_leap_year = self.is_in_leap_year(); let jan_feb_len = 59 + is_leap_year as u32; if ordinal > jan_feb_len { ordinal -= jan_feb_len; } let current_month = (ordinal * 268 + 8031) >> 13; let days_in_preceding_months = (current_month * 3917 - 3866) >> 7; let day = (ordinal - days_in_preceding_months) as u8; match day { 1..=28 => {} 29..=31 if day <= days_in_month_leap(month as u8, is_leap_year) => hint::cold_path(), _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("day")); } } // Safety: `ordinal` is not zero and `is_leap_year` is correct. Ok(unsafe { Self::from_parts( year, is_leap_year, DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year as usize][month as usize - 1] + day as u16, ) }) } /// Replace the day of the month. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2022-02-18).replace_day(1), Ok(date!(2022-02-01))); /// assert!(date!(2022-02-18).replace_day(0).is_err()); // 0 isn't a valid day /// assert!(date!(2022-02-18).replace_day(30).is_err()); // 30 isn't a valid day in February /// ``` #[inline] #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_day(self, day: u8) -> Result { let is_leap_year = self.is_in_leap_year(); match day { 1..=28 => {} 29..=31 if day <= days_in_month_leap(self.month() as u8, is_leap_year) => { hint::cold_path() } _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("day")); } } // Safety: `ordinal` is not zero and `is_leap_year` is correct. Ok(unsafe { Self::from_parts( self.year(), is_leap_year, (self.ordinal().cast_signed() - self.day() as i16 + day as i16).cast_unsigned(), ) }) } /// Replace the day of the year. /// /// ```rust /// # use time_macros::date; /// assert_eq!(date!(2022-049).replace_ordinal(1), Ok(date!(2022-001))); /// assert!(date!(2022-049).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal /// assert!(date!(2022-049).replace_ordinal(366).is_err()); // 2022 isn't a leap year /// ``` #[inline] #[must_use = "This method does not mutate the original `Date`."] pub const fn replace_ordinal(self, ordinal: u16) -> Result { let is_leap_year = self.is_in_leap_year(); match ordinal { 1..=365 => {} 366 if is_leap_year => hint::cold_path(), _ => { hint::cold_path(); return Err(error::ComponentRange::conditional("ordinal")); } } // Safety: `ordinal` is in range and `is_leap_year` is correct. Ok(unsafe { Self::from_parts(self.year(), is_leap_year, ordinal) }) } } /// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`]. impl Date { /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set /// to midnight. /// /// ```rust /// # use time_macros::{date, datetime}; /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00)); /// ``` #[inline] pub const fn midnight(self) -> PrimitiveDateTime { PrimitiveDateTime::new(self, Time::MIDNIGHT) } /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`]. /// /// ```rust /// # use time_macros::{date, datetime, time}; /// assert_eq!( /// date!(1970-01-01).with_time(time!(0:00)), /// datetime!(1970-01-01 0:00), /// ); /// ``` #[inline] pub const fn with_time(self, time: Time) -> PrimitiveDateTime { PrimitiveDateTime::new(self, time) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time_macros::date; /// assert!(date!(1970-01-01).with_hms(0, 0, 0).is_ok()); /// assert!(date!(1970-01-01).with_hms(24, 0, 0).is_err()); /// ``` #[inline] pub const fn with_hms( self, hour: u8, minute: u8, second: u8, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms(hour, minute, second)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time_macros::date; /// assert!(date!(1970-01-01).with_hms_milli(0, 0, 0, 0).is_ok()); /// assert!(date!(1970-01-01).with_hms_milli(24, 0, 0, 0).is_err()); /// ``` #[inline] pub const fn with_hms_milli( self, hour: u8, minute: u8, second: u8, millisecond: u16, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_milli(hour, minute, second, millisecond)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time_macros::date; /// assert!(date!(1970-01-01).with_hms_micro(0, 0, 0, 0).is_ok()); /// assert!(date!(1970-01-01).with_hms_micro(24, 0, 0, 0).is_err()); /// ``` #[inline] pub const fn with_hms_micro( self, hour: u8, minute: u8, second: u8, microsecond: u32, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_micro(hour, minute, second, microsecond)), )) } /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. /// /// ```rust /// # use time_macros::date; /// assert!(date!(1970-01-01).with_hms_nano(0, 0, 0, 0).is_ok()); /// assert!(date!(1970-01-01).with_hms_nano(24, 0, 0, 0).is_err()); /// ``` #[inline] pub const fn with_hms_nano( self, hour: u8, minute: u8, second: u8, nanosecond: u32, ) -> Result { Ok(PrimitiveDateTime::new( self, const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)), )) } } #[cfg(feature = "formatting")] impl Date { /// Format the `Date` using the provided [format description](crate::format_description). #[inline] pub fn format_into( self, output: &mut (impl io::Write + ?Sized), format: &(impl Formattable + ?Sized), ) -> Result { format.format_into(output, &self, &mut Default::default()) } /// Format the `Date` using the provided [format description](crate::format_description). /// /// ```rust /// # use time::{format_description}; /// # use time_macros::date; /// let format = format_description::parse("[year]-[month]-[day]")?; /// assert_eq!(date!(2020-01-02).format(&format)?, "2020-01-02"); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { format.format(&self, &mut Default::default()) } } #[cfg(feature = "parsing")] impl Date { /// Parse a `Date` from the input using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::Date; /// # use time_macros::{date, format_description}; /// let format = format_description!("[year]-[month]-[day]"); /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020-01-02)); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn parse( input: &str, description: &(impl Parsable + ?Sized), ) -> Result { description.parse_date(input.as_bytes()) } } mod private { /// Metadata for `Date`. #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct DateMetadata { /// The width of the year component, including the sign. pub(super) year_width: u8, /// Whether the sign should be displayed. pub(super) display_sign: bool, pub(super) year: i32, pub(super) month: u8, pub(super) day: u8, } } use private::DateMetadata; impl SmartDisplay for Date { type Metadata = DateMetadata; #[inline] fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { let (year, month, day) = self.to_calendar_date(); // There is a minimum of four digits for any year. let mut year_width = cmp::max(year.unsigned_abs().num_digits(), 4); let display_sign = if !(0..10_000).contains(&year) { // An extra character is required for the sign. year_width += 1; true } else { false }; let formatted_width = year_width.extend::() + smart_display::padded_width_of!( "-", u8::from(month) => width(2), "-", day => width(2), ); Metadata::new( formatted_width, self, DateMetadata { year_width, display_sign, year, month: u8::from(month), day, }, ) } #[inline] fn fmt_with_metadata( &self, f: &mut fmt::Formatter<'_>, metadata: Metadata, ) -> fmt::Result { let DateMetadata { year_width, display_sign, year, month, day, } = *metadata; let year_width = year_width.extend(); if display_sign { f.pad_with_width( metadata.unpadded_width(), format_args!("{year:+0year_width$}-{month:02}-{day:02}"), ) } else { f.pad_with_width( metadata.unpadded_width(), format_args!("{year:0year_width$}-{month:02}-{day:02}"), ) } } } impl fmt::Display for Date { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { SmartDisplay::fmt(self, f) } } impl fmt::Debug for Date { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } } impl Add for Date { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: Duration) -> Self::Output { self.checked_add(duration) .expect("overflow adding duration to date") } } impl Add for Date { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: StdDuration) -> Self::Output { self.checked_add_std(duration) .expect("overflow adding duration to date") } } impl AddAssign for Date { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: Duration) { *self = *self + rhs; } } impl AddAssign for Date { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: StdDuration) { *self = *self + rhs; } } impl Sub for Date { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, duration: Duration) -> Self::Output { self.checked_sub(duration) .expect("overflow subtracting duration from date") } } impl Sub for Date { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, duration: StdDuration) -> Self::Output { self.checked_sub_std(duration) .expect("overflow subtracting duration from date") } } impl SubAssign for Date { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: Duration) { *self = *self - rhs; } } impl SubAssign for Date { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: StdDuration) { *self = *self - rhs; } } impl Sub for Date { type Output = Duration; #[inline] fn sub(self, other: Self) -> Self::Output { Duration::days((self.to_julian_day() - other.to_julian_day()).extend()) } } time-0.3.47/src/duration.rs000064400000000000000000001746341046102023000136560ustar 00000000000000//! The [`Duration`] struct and its associated `impl`s. use core::cmp::Ordering; use core::fmt; use core::iter::Sum; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use core::time::Duration as StdDuration; #[cfg(feature = "std")] use std::time::SystemTime; use deranged::RangedI32; use num_conv::prelude::*; #[cfg(feature = "std")] #[expect(deprecated)] use crate::Instant; use crate::convert::*; use crate::error; use crate::internal_macros::const_try_opt; #[derive(Debug)] enum FloatConstructorError { Nan, NegOverflow, PosOverflow, } /// By explicitly inserting this enum where padding is expected, the compiler is able to better /// perform niche value optimization. #[repr(u32)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) enum Padding { #[allow(clippy::missing_docs_in_private_items)] Optimize, } /// The type of the `nanosecond` field of `Duration`. type Nanoseconds = RangedI32<{ -Nanosecond::per_t::(Second) + 1 }, { Nanosecond::per_t::(Second) - 1 }>; /// A span of time with nanosecond precision. /// /// Each `Duration` is composed of a whole number of seconds and a fractional part represented in /// nanoseconds. /// /// This implementation allows for negative durations, unlike [`core::time::Duration`]. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Duration { /// Number of whole seconds. seconds: i64, /// Number of nanoseconds within the second. The sign always matches the `seconds` field. // Sign must match that of `seconds` (though this is not a safety requirement). nanoseconds: Nanoseconds, padding: Padding, } impl fmt::Debug for Duration { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Duration") .field("seconds", &self.seconds) .field("nanoseconds", &self.nanoseconds) .finish() } } impl Default for Duration { #[inline] fn default() -> Self { Self { seconds: 0, nanoseconds: Nanoseconds::new_static::<0>(), padding: Padding::Optimize, } } } /// This is adapted from the [`std` implementation][std], which uses mostly bit /// operations to ensure the highest precision: /// /// Changes from `std` are marked and explained below. /// /// [std]: https://github.com/rust-lang/rust/blob/3a37c2f0523c87147b64f1b8099fc9df22e8c53e/library/core/src/time.rs#L1262-L1340 #[rustfmt::skip] // Skip `rustfmt` because it reformats the arguments of the macro weirdly. macro_rules! try_from_secs { ( secs = $secs: expr, mantissa_bits = $mant_bits: literal, exponent_bits = $exp_bits: literal, offset = $offset: literal, bits_ty = $bits_ty:ty, bits_ty_signed = $bits_ty_signed:ty, double_ty = $double_ty:ty, float_ty = $float_ty:ty, ) => {{ 'value: { const MIN_EXP: i16 = 1 - (1i16 << $exp_bits) / 2; const MANT_MASK: $bits_ty = (1 << $mant_bits) - 1; const EXP_MASK: $bits_ty = (1 << $exp_bits) - 1; // Change from std: No error check for negative values necessary. let bits = $secs.to_bits(); let mant = (bits & MANT_MASK) | (MANT_MASK + 1); let exp = ((bits >> $mant_bits) & EXP_MASK) as i16 + MIN_EXP; let (secs, nanos) = if exp < -31 { // the input represents less than 1ns and can not be rounded to it (0u64, 0u32) } else if exp < 0 { // the input is less than 1 second let t = (mant as $double_ty) << ($offset + exp); let nanos_offset = $mant_bits + $offset; #[allow(trivial_numeric_casts)] let nanos_tmp = Nanosecond::per_t::(Second) * t as u128; let nanos = (nanos_tmp >> nanos_offset) as u32; let rem_mask = (1 << nanos_offset) - 1; let rem_msb_mask = 1 << (nanos_offset - 1); let rem = nanos_tmp & rem_mask; let is_tie = rem == rem_msb_mask; let is_even = (nanos & 1) == 0; let rem_msb = nanos_tmp & rem_msb_mask == 0; let add_ns = !(rem_msb || (is_even && is_tie)); // f32 does not have enough precision to trigger the second branch // since it can not represent numbers between 0.999_999_940_395 and 1.0. let nanos = nanos + add_ns as u32; if ($mant_bits == 23) || (nanos != Nanosecond::per_t::(Second)) { (0, nanos) } else { (1, 0) } } else if exp < $mant_bits { #[allow(trivial_numeric_casts)] let secs = (mant >> ($mant_bits - exp)) as u64; let t = ((mant << exp) & MANT_MASK) as $double_ty; let nanos_offset = $mant_bits; let nanos_tmp = Nanosecond::per_t::<$double_ty>(Second) * t; let nanos = (nanos_tmp >> nanos_offset) as u32; let rem_mask = (1 << nanos_offset) - 1; let rem_msb_mask = 1 << (nanos_offset - 1); let rem = nanos_tmp & rem_mask; let is_tie = rem == rem_msb_mask; let is_even = (nanos & 1) == 0; let rem_msb = nanos_tmp & rem_msb_mask == 0; let add_ns = !(rem_msb || (is_even && is_tie)); // f32 does not have enough precision to trigger the second branch. // For example, it can not represent numbers between 1.999_999_880... // and 2.0. Bigger values result in even smaller precision of the // fractional part. let nanos = nanos + add_ns as u32; if ($mant_bits == 23) || (nanos != Nanosecond::per_t::(Second)) { (secs, nanos) } else { (secs + 1, 0) } } else if exp < 63 { // Change from std: The exponent here is 63 instead of 64, // because i64::MAX + 1 is 2^63. // the input has no fractional part #[allow(trivial_numeric_casts)] let secs = (mant as u64) << (exp - $mant_bits); (secs, 0) } else if bits == (i64::MIN as $float_ty).to_bits() { // Change from std: Signed integers are asymmetrical in that // iN::MIN is -iN::MAX - 1. So for example i8 covers the // following numbers -128..=127. The check above (exp < 63) // doesn't cover i64::MIN as that is -2^63, so we have this // additional case to handle the asymmetry of iN::MIN. break 'value Ok(Self::new_ranged_unchecked(i64::MIN, Nanoseconds::new_static::<0>())); } else if $secs.is_nan() { // Change from std: std doesn't differentiate between the error // cases. break 'value Err(FloatConstructorError::Nan); } else if $secs.is_sign_negative() { break 'value Err(FloatConstructorError::NegOverflow); } else { break 'value Err(FloatConstructorError::PosOverflow); }; // Change from std: All the code is mostly unmodified in that it // simply calculates an unsigned integer. Here we extract the sign // bit and assign it to the number. We basically manually do two's // complement here, we could also use an if and just negate the // numbers based on the sign, but it turns out to be quite a bit // slower. let mask = (bits as $bits_ty_signed) >> ($mant_bits + $exp_bits); #[allow(trivial_numeric_casts)] let secs_signed = ((secs as i64) ^ (mask as i64)) - (mask as i64); #[allow(trivial_numeric_casts)] let nanos_signed = ((nanos as i32) ^ (mask as i32)) - (mask as i32); // Safety: `nanos_signed` is in range. Ok(unsafe { Self::new_unchecked(secs_signed, nanos_signed) }) } }}; } impl Duration { /// Equivalent to `0.seconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::ZERO, 0.seconds()); /// ``` pub const ZERO: Self = Self::seconds(0); /// Equivalent to `1.nanoseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::NANOSECOND, 1.nanoseconds()); /// ``` pub const NANOSECOND: Self = Self::nanoseconds(1); /// Equivalent to `1.microseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MICROSECOND, 1.microseconds()); /// ``` pub const MICROSECOND: Self = Self::microseconds(1); /// Equivalent to `1.milliseconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MILLISECOND, 1.milliseconds()); /// ``` pub const MILLISECOND: Self = Self::milliseconds(1); /// Equivalent to `1.seconds()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::SECOND, 1.seconds()); /// ``` pub const SECOND: Self = Self::seconds(1); /// Equivalent to `1.minutes()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::MINUTE, 1.minutes()); /// ``` pub const MINUTE: Self = Self::minutes(1); /// Equivalent to `1.hours()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::HOUR, 1.hours()); /// ``` pub const HOUR: Self = Self::hours(1); /// Equivalent to `1.days()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::DAY, 1.days()); /// ``` pub const DAY: Self = Self::days(1); /// Equivalent to `1.weeks()`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::WEEK, 1.weeks()); /// ``` pub const WEEK: Self = Self::weeks(1); /// The minimum possible duration. Adding any negative duration to this will cause an overflow. pub const MIN: Self = Self::new_ranged(i64::MIN, Nanoseconds::MIN); /// The maximum possible duration. Adding any positive duration to this will cause an overflow. pub const MAX: Self = Self::new_ranged(i64::MAX, Nanoseconds::MAX); /// Check if a duration is exactly zero. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!(0.seconds().is_zero()); /// assert!(!1.nanoseconds().is_zero()); /// ``` #[inline] pub const fn is_zero(self) -> bool { self.seconds == 0 && self.nanoseconds.get() == 0 } /// Check if a duration is negative. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!((-1).seconds().is_negative()); /// assert!(!0.seconds().is_negative()); /// assert!(!1.seconds().is_negative()); /// ``` #[inline] pub const fn is_negative(self) -> bool { self.seconds < 0 || self.nanoseconds.get() < 0 } /// Check if a duration is positive. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert!(1.seconds().is_positive()); /// assert!(!0.seconds().is_positive()); /// assert!(!(-1).seconds().is_positive()); /// ``` #[inline] pub const fn is_positive(self) -> bool { self.seconds > 0 || self.nanoseconds.get() > 0 } /// Get the absolute value of the duration. /// /// This method saturates the returned value if it would otherwise overflow. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().abs(), 1.seconds()); /// assert_eq!(0.seconds().abs(), 0.seconds()); /// assert_eq!((-1).seconds().abs(), 1.seconds()); /// ``` #[inline] pub const fn abs(self) -> Self { match self.seconds.checked_abs() { Some(seconds) => Self::new_ranged_unchecked(seconds, self.nanoseconds.abs()), None => Self::MAX, } } /// Convert the existing `Duration` to a `std::time::Duration` and its sign. This returns a /// [`std::time::Duration`] and does not saturate the returned value (unlike [`Duration::abs`]). /// /// ```rust /// # use time::ext::{NumericalDuration, NumericalStdDuration}; /// assert_eq!(1.seconds().unsigned_abs(), 1.std_seconds()); /// assert_eq!(0.seconds().unsigned_abs(), 0.std_seconds()); /// assert_eq!((-1).seconds().unsigned_abs(), 1.std_seconds()); /// ``` #[inline] pub const fn unsigned_abs(self) -> StdDuration { StdDuration::new( self.seconds.unsigned_abs(), self.nanoseconds.get().unsigned_abs(), ) } /// Create a new `Duration` without checking the validity of the components. /// /// # Safety /// /// - `nanoseconds` must be in the range `-999_999_999..=999_999_999`. /// /// While the sign of `nanoseconds` is required to be the same as the sign of `seconds`, this is /// not a safety invariant. #[inline] #[track_caller] pub(crate) const unsafe fn new_unchecked(seconds: i64, nanoseconds: i32) -> Self { Self::new_ranged_unchecked( seconds, // Safety: The caller must uphold the safety invariants. unsafe { Nanoseconds::new_unchecked(nanoseconds) }, ) } /// Create a new `Duration` without checking the validity of the components. #[inline] #[track_caller] pub(crate) const fn new_ranged_unchecked(seconds: i64, nanoseconds: Nanoseconds) -> Self { if seconds < 0 { debug_assert!(nanoseconds.get() <= 0); } else if seconds > 0 { debug_assert!(nanoseconds.get() >= 0); } Self { seconds, nanoseconds, padding: Padding::Optimize, } } /// Create a new `Duration` with the provided seconds and nanoseconds. If nanoseconds is at /// least ±109, it will wrap to the number of seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::new(1, 0), 1.seconds()); /// assert_eq!(Duration::new(-1, 0), (-1).seconds()); /// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds()); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { seconds = seconds .checked_add(nanoseconds as i64 / Nanosecond::per_t::(Second)) .expect("overflow constructing `time::Duration`"); nanoseconds %= Nanosecond::per_t::(Second); if seconds > 0 && nanoseconds < 0 { // `seconds` cannot overflow here because it is positive. seconds -= 1; nanoseconds += Nanosecond::per_t::(Second); } else if seconds < 0 && nanoseconds > 0 { // `seconds` cannot overflow here because it is negative. seconds += 1; nanoseconds -= Nanosecond::per_t::(Second); } // Safety: `nanoseconds` is in range due to the modulus above. unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Create a new `Duration` with the provided seconds and nanoseconds. #[inline] pub(crate) const fn new_ranged(mut seconds: i64, mut nanoseconds: Nanoseconds) -> Self { if seconds > 0 && nanoseconds.get() < 0 { // `seconds` cannot overflow here because it is positive. seconds -= 1; // Safety: `nanoseconds` is negative with a maximum of 999,999,999, so adding a billion // to it is guaranteed to result in an in-range value. nanoseconds = unsafe { Nanoseconds::new_unchecked(nanoseconds.get() + Nanosecond::per_t::(Second)) }; } else if seconds < 0 && nanoseconds.get() > 0 { // `seconds` cannot overflow here because it is negative. seconds += 1; // Safety: `nanoseconds` is positive with a minimum of -999,999,999, so subtracting a // billion from it is guaranteed to result in an in-range value. nanoseconds = unsafe { Nanoseconds::new_unchecked(nanoseconds.get() - Nanosecond::per_t::(Second)) }; } Self::new_ranged_unchecked(seconds, nanoseconds) } /// Create a new `Duration` with the given number of weeks. Equivalent to /// `Duration::seconds(weeks * 604_800)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::weeks(1), 604_800.seconds()); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] pub const fn weeks(weeks: i64) -> Self { Self::seconds( weeks .checked_mul(Second::per_t(Week)) .expect("overflow constructing `time::Duration`"), ) } /// Create a new `Duration` with the given number of days. Equivalent to /// `Duration::seconds(days * 86_400)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::days(1), 86_400.seconds()); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] pub const fn days(days: i64) -> Self { Self::seconds( days.checked_mul(Second::per_t(Day)) .expect("overflow constructing `time::Duration`"), ) } /// Create a new `Duration` with the given number of hours. Equivalent to /// `Duration::seconds(hours * 3_600)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::hours(1), 3_600.seconds()); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] pub const fn hours(hours: i64) -> Self { Self::seconds( hours .checked_mul(Second::per_t(Hour)) .expect("overflow constructing `time::Duration`"), ) } /// Create a new `Duration` with the given number of minutes. Equivalent to /// `Duration::seconds(minutes * 60)`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::minutes(1), 60.seconds()); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] pub const fn minutes(minutes: i64) -> Self { Self::seconds( minutes .checked_mul(Second::per_t(Minute)) .expect("overflow constructing `time::Duration`"), ) } /// Create a new `Duration` with the given number of seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds(1), 1_000.milliseconds()); /// ``` #[inline] pub const fn seconds(seconds: i64) -> Self { Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>()) } /// Create a new `Duration` from the specified number of seconds represented as `f64`. /// /// If the value is `NaN` or out of bounds, an error is returned that can be handled in the /// desired manner by the caller. #[inline] const fn try_seconds_f64(seconds: f64) -> Result { try_from_secs!( secs = seconds, mantissa_bits = 52, exponent_bits = 11, offset = 44, bits_ty = u64, bits_ty_signed = i64, double_ty = u128, float_ty = f64, ) } /// Create a new `Duration` from the specified number of seconds represented as `f32`. /// /// If the value is `NaN` or out of bounds, an error is returned that can be handled in the /// desired manner by the caller. #[inline] const fn try_seconds_f32(seconds: f32) -> Result { try_from_secs!( secs = seconds, mantissa_bits = 23, exponent_bits = 8, offset = 41, bits_ty = u32, bits_ty_signed = i32, double_ty = u64, float_ty = f32, ) } /// Creates a new `Duration` from the specified number of seconds represented as `f64`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds()); /// assert_eq!(Duration::seconds_f64(-0.5), (-0.5).seconds()); /// ``` #[inline] #[track_caller] pub const fn seconds_f64(seconds: f64) -> Self { match Self::try_seconds_f64(seconds) { Ok(duration) => duration, Err(FloatConstructorError::Nan) => { panic!("passed NaN to `time::Duration::seconds_f64`"); } Err(FloatConstructorError::NegOverflow | FloatConstructorError::PosOverflow) => { panic!("overflow constructing `time::Duration`"); } } } /// Creates a new `Duration` from the specified number of seconds represented as `f32`. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::seconds_f32(0.5), 0.5.seconds()); /// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds()); /// ``` #[inline] #[track_caller] pub const fn seconds_f32(seconds: f32) -> Self { match Self::try_seconds_f32(seconds) { Ok(duration) => duration, Err(FloatConstructorError::Nan) => { panic!("passed NaN to `time::Duration::seconds_f32`"); } Err(FloatConstructorError::NegOverflow | FloatConstructorError::PosOverflow) => { panic!("overflow constructing `time::Duration`"); } } } /// Creates a new `Duration` from the specified number of seconds /// represented as `f64`. Any values that are out of bounds are saturated at /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` /// of 0 seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::saturating_seconds_f64(0.5), 0.5.seconds()); /// assert_eq!(Duration::saturating_seconds_f64(-0.5), (-0.5).seconds()); /// assert_eq!( /// Duration::saturating_seconds_f64(f64::NAN), /// Duration::new(0, 0), /// ); /// assert_eq!( /// Duration::saturating_seconds_f64(f64::NEG_INFINITY), /// Duration::MIN, /// ); /// assert_eq!( /// Duration::saturating_seconds_f64(f64::INFINITY), /// Duration::MAX, /// ); /// ``` #[inline] pub const fn saturating_seconds_f64(seconds: f64) -> Self { match Self::try_seconds_f64(seconds) { Ok(duration) => duration, Err(FloatConstructorError::Nan) => Self::ZERO, Err(FloatConstructorError::NegOverflow) => Self::MIN, Err(FloatConstructorError::PosOverflow) => Self::MAX, } } /// Creates a new `Duration` from the specified number of seconds /// represented as `f32`. Any values that are out of bounds are saturated at /// the minimum or maximum respectively. `NaN` gets turned into a `Duration` /// of 0 seconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::saturating_seconds_f32(0.5), 0.5.seconds()); /// assert_eq!(Duration::saturating_seconds_f32(-0.5), (-0.5).seconds()); /// assert_eq!( /// Duration::saturating_seconds_f32(f32::NAN), /// Duration::new(0, 0), /// ); /// assert_eq!( /// Duration::saturating_seconds_f32(f32::NEG_INFINITY), /// Duration::MIN, /// ); /// assert_eq!( /// Duration::saturating_seconds_f32(f32::INFINITY), /// Duration::MAX, /// ); /// ``` #[inline] pub const fn saturating_seconds_f32(seconds: f32) -> Self { match Self::try_seconds_f32(seconds) { Ok(duration) => duration, Err(FloatConstructorError::Nan) => Self::ZERO, Err(FloatConstructorError::NegOverflow) => Self::MIN, Err(FloatConstructorError::PosOverflow) => Self::MAX, } } /// Creates a new `Duration` from the specified number of seconds /// represented as `f64`. Returns `None` if the `Duration` can't be /// represented. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::checked_seconds_f64(0.5), Some(0.5.seconds())); /// assert_eq!(Duration::checked_seconds_f64(-0.5), Some((-0.5).seconds())); /// assert_eq!(Duration::checked_seconds_f64(f64::NAN), None); /// assert_eq!(Duration::checked_seconds_f64(f64::NEG_INFINITY), None); /// assert_eq!(Duration::checked_seconds_f64(f64::INFINITY), None); /// ``` #[inline] pub const fn checked_seconds_f64(seconds: f64) -> Option { match Self::try_seconds_f64(seconds) { Ok(duration) => Some(duration), Err(_) => None, } } /// Creates a new `Duration` from the specified number of seconds /// represented as `f32`. Returns `None` if the `Duration` can't be /// represented. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::checked_seconds_f32(0.5), Some(0.5.seconds())); /// assert_eq!(Duration::checked_seconds_f32(-0.5), Some((-0.5).seconds())); /// assert_eq!(Duration::checked_seconds_f32(f32::NAN), None); /// assert_eq!(Duration::checked_seconds_f32(f32::NEG_INFINITY), None); /// assert_eq!(Duration::checked_seconds_f32(f32::INFINITY), None); /// ``` #[inline] pub const fn checked_seconds_f32(seconds: f32) -> Option { match Self::try_seconds_f32(seconds) { Ok(duration) => Some(duration), Err(_) => None, } } /// Create a new `Duration` with the given number of milliseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::milliseconds(1), 1_000.microseconds()); /// assert_eq!(Duration::milliseconds(-1), (-1_000).microseconds()); /// ``` #[inline] pub const fn milliseconds(milliseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked( milliseconds / Millisecond::per_t::(Second), (milliseconds % Millisecond::per_t::(Second) * Nanosecond::per_t::(Millisecond)) as i32, ) } } /// Create a new `Duration` with the given number of microseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::microseconds(1), 1_000.nanoseconds()); /// assert_eq!(Duration::microseconds(-1), (-1_000).nanoseconds()); /// ``` #[inline] pub const fn microseconds(microseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked( microseconds / Microsecond::per_t::(Second), (microseconds % Microsecond::per_t::(Second) * Nanosecond::per_t::(Microsecond)) as i32, ) } } /// Create a new `Duration` with the given number of nanoseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(Duration::nanoseconds(1), 1.microseconds() / 1_000); /// assert_eq!(Duration::nanoseconds(-1), (-1).microseconds() / 1_000); /// ``` #[inline] pub const fn nanoseconds(nanoseconds: i64) -> Self { // Safety: `nanoseconds` is guaranteed to be in range because of the modulus. unsafe { Self::new_unchecked( nanoseconds / Nanosecond::per_t::(Second), (nanoseconds % Nanosecond::per_t::(Second)) as i32, ) } } /// Create a new `Duration` with the given number of nanoseconds. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!( /// Duration::nanoseconds_i128(1_234_567_890), /// 1.seconds() + 234_567_890.nanoseconds() /// ); /// ``` /// /// # Panics /// /// This may panic if an overflow occurs. This may happen because the input range cannot be /// fully mapped to the output. #[inline] #[track_caller] pub const fn nanoseconds_i128(nanoseconds: i128) -> Self { let seconds = nanoseconds / Nanosecond::per_t::(Second); let nanoseconds = nanoseconds % Nanosecond::per_t::(Second); if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { panic!("overflow constructing `time::Duration`"); } // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. unsafe { Self::new_unchecked(seconds as i64, nanoseconds as i32) } } /// Get the number of whole weeks in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.weeks().whole_weeks(), 1); /// assert_eq!((-1).weeks().whole_weeks(), -1); /// assert_eq!(6.days().whole_weeks(), 0); /// assert_eq!((-6).days().whole_weeks(), 0); /// ``` #[inline] pub const fn whole_weeks(self) -> i64 { self.whole_seconds() / Second::per_t::(Week) } /// Get the number of whole days in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.days().whole_days(), 1); /// assert_eq!((-1).days().whole_days(), -1); /// assert_eq!(23.hours().whole_days(), 0); /// assert_eq!((-23).hours().whole_days(), 0); /// ``` #[inline] pub const fn whole_days(self) -> i64 { self.whole_seconds() / Second::per_t::(Day) } /// Get the number of whole hours in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.hours().whole_hours(), 1); /// assert_eq!((-1).hours().whole_hours(), -1); /// assert_eq!(59.minutes().whole_hours(), 0); /// assert_eq!((-59).minutes().whole_hours(), 0); /// ``` #[inline] pub const fn whole_hours(self) -> i64 { self.whole_seconds() / Second::per_t::(Hour) } /// Get the number of whole minutes in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.minutes().whole_minutes(), 1); /// assert_eq!((-1).minutes().whole_minutes(), -1); /// assert_eq!(59.seconds().whole_minutes(), 0); /// assert_eq!((-59).seconds().whole_minutes(), 0); /// ``` #[inline] pub const fn whole_minutes(self) -> i64 { self.whole_seconds() / Second::per_t::(Minute) } /// Get the number of whole seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().whole_seconds(), 1); /// assert_eq!((-1).seconds().whole_seconds(), -1); /// assert_eq!(1.minutes().whole_seconds(), 60); /// assert_eq!((-1).minutes().whole_seconds(), -60); /// ``` #[inline] pub const fn whole_seconds(self) -> i64 { self.seconds } /// Get the number of fractional seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5); /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); /// ``` #[inline] pub const fn as_seconds_f64(self) -> f64 { self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per_t::(Second) } /// Get the number of fractional seconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5); /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); /// ``` #[inline] pub const fn as_seconds_f32(self) -> f32 { self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per_t::(Second) } /// Get the number of whole milliseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.seconds().whole_milliseconds(), 1_000); /// assert_eq!((-1).seconds().whole_milliseconds(), -1_000); /// assert_eq!(1.milliseconds().whole_milliseconds(), 1); /// assert_eq!((-1).milliseconds().whole_milliseconds(), -1); /// ``` #[inline] pub const fn whole_milliseconds(self) -> i128 { self.seconds as i128 * Millisecond::per_t::(Second) + self.nanoseconds.get() as i128 / Nanosecond::per_t::(Millisecond) } /// Get the number of milliseconds past the number of whole seconds. /// /// Always in the range `-999..=999`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.4.seconds().subsec_milliseconds(), 400); /// assert_eq!((-1.4).seconds().subsec_milliseconds(), -400); /// ``` #[inline] pub const fn subsec_milliseconds(self) -> i16 { (self.nanoseconds.get() / Nanosecond::per_t::(Millisecond)) as i16 } /// Get the number of whole microseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.milliseconds().whole_microseconds(), 1_000); /// assert_eq!((-1).milliseconds().whole_microseconds(), -1_000); /// assert_eq!(1.microseconds().whole_microseconds(), 1); /// assert_eq!((-1).microseconds().whole_microseconds(), -1); /// ``` #[inline] pub const fn whole_microseconds(self) -> i128 { self.seconds as i128 * Microsecond::per_t::(Second) + self.nanoseconds.get() as i128 / Nanosecond::per_t::(Microsecond) } /// Get the number of microseconds past the number of whole seconds. /// /// Always in the range `-999_999..=999_999`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.0004.seconds().subsec_microseconds(), 400); /// assert_eq!((-1.0004).seconds().subsec_microseconds(), -400); /// ``` #[inline] pub const fn subsec_microseconds(self) -> i32 { self.nanoseconds.get() / Nanosecond::per_t::(Microsecond) } /// Get the number of nanoseconds in the duration. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000); /// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000); /// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1); /// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1); /// ``` #[inline] pub const fn whole_nanoseconds(self) -> i128 { self.seconds as i128 * Nanosecond::per_t::(Second) + self.nanoseconds.get() as i128 } /// Get the number of nanoseconds past the number of whole seconds. /// /// The returned value will always be in the range `-999_999_999..=999_999_999`. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(1.000_000_400.seconds().subsec_nanoseconds(), 400); /// assert_eq!((-1.000_000_400).seconds().subsec_nanoseconds(), -400); /// ``` #[inline] pub const fn subsec_nanoseconds(self) -> i32 { self.nanoseconds.get() } /// Get the number of nanoseconds past the number of whole seconds. #[cfg(feature = "quickcheck")] #[inline] pub(crate) const fn subsec_nanoseconds_ranged(self) -> Nanoseconds { self.nanoseconds } /// Computes `self + rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_add(5.seconds()), Some(10.seconds())); /// assert_eq!(Duration::MAX.checked_add(1.nanoseconds()), None); /// assert_eq!((-5).seconds().checked_add(5.seconds()), Some(0.seconds())); /// ``` #[inline] pub const fn checked_add(self, rhs: Self) -> Option { let mut seconds = const_try_opt!(self.seconds.checked_add(rhs.seconds)); let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); if nanoseconds >= Nanosecond::per_t(Second) || seconds < 0 && nanoseconds > 0 { nanoseconds -= Nanosecond::per_t::(Second); seconds = const_try_opt!(seconds.checked_add(1)); } else if nanoseconds <= -Nanosecond::per_t::(Second) || seconds > 0 && nanoseconds < 0 { nanoseconds += Nanosecond::per_t::(Second); seconds = const_try_opt!(seconds.checked_sub(1)); } // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self - rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_sub(5.seconds()), Some(Duration::ZERO)); /// assert_eq!(Duration::MIN.checked_sub(1.nanoseconds()), None); /// assert_eq!(5.seconds().checked_sub(10.seconds()), Some((-5).seconds())); /// ``` #[inline] pub const fn checked_sub(self, rhs: Self) -> Option { let mut seconds = const_try_opt!(self.seconds.checked_sub(rhs.seconds)); let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); if nanoseconds >= Nanosecond::per_t(Second) || seconds < 0 && nanoseconds > 0 { nanoseconds -= Nanosecond::per_t::(Second); seconds = const_try_opt!(seconds.checked_add(1)); } else if nanoseconds <= -Nanosecond::per_t::(Second) || seconds > 0 && nanoseconds < 0 { nanoseconds += Nanosecond::per_t::(Second); seconds = const_try_opt!(seconds.checked_sub(1)); } // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self * rhs`, returning `None` if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().checked_mul(2), Some(10.seconds())); /// assert_eq!(5.seconds().checked_mul(-2), Some((-10).seconds())); /// assert_eq!(5.seconds().checked_mul(0), Some(0.seconds())); /// assert_eq!(Duration::MAX.checked_mul(2), None); /// assert_eq!(Duration::MIN.checked_mul(2), None); /// ``` #[inline] pub const fn checked_mul(self, rhs: i32) -> Option { // Multiply nanoseconds as i64, because it cannot overflow that way. let total_nanos = self.nanoseconds.get() as i64 * rhs as i64; let extra_secs = total_nanos / Nanosecond::per_t::(Second); let nanoseconds = (total_nanos % Nanosecond::per_t::(Second)) as i32; let seconds = const_try_opt!( const_try_opt!(self.seconds.checked_mul(rhs as i64)).checked_add(extra_secs) ); // Safety: `nanoseconds` is guaranteed to be in range because of the modulus above. unsafe { Some(Self::new_unchecked(seconds, nanoseconds)) } } /// Computes `self / rhs`, returning `None` if `rhs == 0` or if the result would overflow. /// /// ```rust /// # use time::ext::NumericalDuration; /// assert_eq!(10.seconds().checked_div(2), Some(5.seconds())); /// assert_eq!(10.seconds().checked_div(-2), Some((-5).seconds())); /// assert_eq!(1.seconds().checked_div(0), None); /// ``` #[inline] pub const fn checked_div(self, rhs: i32) -> Option { let (secs, extra_secs) = ( const_try_opt!(self.seconds.checked_div(rhs as i64)), self.seconds % (rhs as i64), ); let (mut nanos, extra_nanos) = (self.nanoseconds.get() / rhs, self.nanoseconds.get() % rhs); nanos += ((extra_secs * (Nanosecond::per_t::(Second)) + extra_nanos as i64) / (rhs as i64)) as i32; // Safety: `nanoseconds` is in range. unsafe { Some(Self::new_unchecked(secs, nanos)) } } /// Computes `-self`, returning `None` if the result would overflow. /// /// ```rust /// # use time::ext::NumericalDuration; /// # use time::Duration; /// assert_eq!(5.seconds().checked_neg(), Some((-5).seconds())); /// assert_eq!(Duration::MIN.checked_neg(), None); /// ``` #[inline] pub const fn checked_neg(self) -> Option { if self.seconds == i64::MIN { None } else { Some(Self::new_ranged_unchecked( -self.seconds, self.nanoseconds.neg(), )) } } /// Computes `self + rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_add(5.seconds()), 10.seconds()); /// assert_eq!(Duration::MAX.saturating_add(1.nanoseconds()), Duration::MAX); /// assert_eq!( /// Duration::MIN.saturating_add((-1).nanoseconds()), /// Duration::MIN /// ); /// assert_eq!((-5).seconds().saturating_add(5.seconds()), Duration::ZERO); /// ``` #[inline] pub const fn saturating_add(self, rhs: Self) -> Self { let (mut seconds, overflow) = self.seconds.overflowing_add(rhs.seconds); if overflow { if self.seconds > 0 { return Self::MAX; } return Self::MIN; } let mut nanoseconds = self.nanoseconds.get() + rhs.nanoseconds.get(); if nanoseconds >= Nanosecond::per_t(Second) || seconds < 0 && nanoseconds > 0 { nanoseconds -= Nanosecond::per_t::(Second); seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; } else if nanoseconds <= -Nanosecond::per_t::(Second) || seconds > 0 && nanoseconds < 0 { nanoseconds += Nanosecond::per_t::(Second); seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Computes `self - rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_sub(5.seconds()), Duration::ZERO); /// assert_eq!(Duration::MIN.saturating_sub(1.nanoseconds()), Duration::MIN); /// assert_eq!( /// Duration::MAX.saturating_sub((-1).nanoseconds()), /// Duration::MAX /// ); /// assert_eq!(5.seconds().saturating_sub(10.seconds()), (-5).seconds()); /// ``` #[inline] pub const fn saturating_sub(self, rhs: Self) -> Self { let (mut seconds, overflow) = self.seconds.overflowing_sub(rhs.seconds); if overflow { if self.seconds > 0 { return Self::MAX; } return Self::MIN; } let mut nanoseconds = self.nanoseconds.get() - rhs.nanoseconds.get(); if nanoseconds >= Nanosecond::per_t(Second) || seconds < 0 && nanoseconds > 0 { nanoseconds -= Nanosecond::per_t::(Second); seconds = match seconds.checked_add(1) { Some(seconds) => seconds, None => return Self::MAX, }; } else if nanoseconds <= -Nanosecond::per_t::(Second) || seconds > 0 && nanoseconds < 0 { nanoseconds += Nanosecond::per_t::(Second); seconds = match seconds.checked_sub(1) { Some(seconds) => seconds, None => return Self::MIN, }; } // Safety: `nanoseconds` is guaranteed to be in range because of the overflow handling. unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Computes `self * rhs`, saturating if an overflow occurred. /// /// ```rust /// # use time::{Duration, ext::NumericalDuration}; /// assert_eq!(5.seconds().saturating_mul(2), 10.seconds()); /// assert_eq!(5.seconds().saturating_mul(-2), (-10).seconds()); /// assert_eq!(5.seconds().saturating_mul(0), Duration::ZERO); /// assert_eq!(Duration::MAX.saturating_mul(2), Duration::MAX); /// assert_eq!(Duration::MIN.saturating_mul(2), Duration::MIN); /// assert_eq!(Duration::MAX.saturating_mul(-2), Duration::MIN); /// assert_eq!(Duration::MIN.saturating_mul(-2), Duration::MAX); /// ``` #[inline] pub const fn saturating_mul(self, rhs: i32) -> Self { // Multiply nanoseconds as i64, because it cannot overflow that way. let total_nanos = self.nanoseconds.get() as i64 * rhs as i64; let extra_secs = total_nanos / Nanosecond::per_t::(Second); let nanoseconds = (total_nanos % Nanosecond::per_t::(Second)) as i32; let (seconds, overflow1) = self.seconds.overflowing_mul(rhs as i64); if overflow1 { if self.seconds > 0 && rhs > 0 || self.seconds < 0 && rhs < 0 { return Self::MAX; } return Self::MIN; } let (seconds, overflow2) = seconds.overflowing_add(extra_secs); if overflow2 { if self.seconds > 0 && rhs > 0 { return Self::MAX; } return Self::MIN; } // Safety: `nanoseconds` is guaranteed to be in range because of to the modulus above. unsafe { Self::new_unchecked(seconds, nanoseconds) } } /// Runs a closure, returning the duration of time it took to run. The return value of the /// closure is provided in the second part of the tuple. #[cfg(feature = "std")] #[doc(hidden)] #[inline] #[track_caller] #[deprecated( since = "0.3.32", note = "extremely limited use case, not intended for benchmarking" )] #[expect(deprecated)] pub fn time_fn(f: impl FnOnce() -> T) -> (Self, T) { let start = Instant::now(); let return_value = f(); let end = Instant::now(); (end - start, return_value) } } /// The format returned by this implementation is not stable and must not be relied upon. /// /// By default this produces an exact, full-precision printout of the duration. /// For a concise, rounded printout instead, you can use the `.N` format specifier: /// /// ``` /// # use time::Duration; /// # /// let duration = Duration::new(123456, 789011223); /// println!("{duration:.3}"); /// ``` /// /// For the purposes of this implementation, a day is exactly 24 hours and a minute is exactly 60 /// seconds. impl fmt::Display for Duration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_negative() { f.write_str("-")?; } if let Some(_precision) = f.precision() { // Concise, rounded representation. if self.is_zero() { // Write a zero value with the requested precision. return (0.).fmt(f).and_then(|_| f.write_str("s")); } /// Format the first item that produces a value greater than 1 and then break. macro_rules! item { ($name:literal, $value:expr) => { let value = $value; if value >= 1.0 { return value.fmt(f).and_then(|_| f.write_str($name)); } }; } // Even if this produces a de-normal float, because we're rounding we don't really care. let seconds = self.unsigned_abs().as_secs_f64(); item!("d", seconds / Second::per_t::(Day)); item!("h", seconds / Second::per_t::(Hour)); item!("m", seconds / Second::per_t::(Minute)); item!("s", seconds); item!("ms", seconds * Millisecond::per_t::(Second)); item!("µs", seconds * Microsecond::per_t::(Second)); item!("ns", seconds * Nanosecond::per_t::(Second)); } else { // Precise, but verbose representation. if self.is_zero() { return f.write_str("0s"); } /// Format a single item. macro_rules! item { ($name:literal, $value:expr) => { match $value { 0 => Ok(()), value => value.fmt(f).and_then(|_| f.write_str($name)), } }; } let seconds = self.seconds.unsigned_abs(); let nanoseconds = self.nanoseconds.get().unsigned_abs(); item!("d", seconds / Second::per_t::(Day))?; item!( "h", seconds / Second::per_t::(Hour) % Hour::per_t::(Day) )?; item!( "m", seconds / Second::per_t::(Minute) % Minute::per_t::(Hour) )?; item!("s", seconds % Second::per_t::(Minute))?; item!("ms", nanoseconds / Nanosecond::per_t::(Millisecond))?; item!( "µs", nanoseconds / Nanosecond::per_t::(Microsecond) % Microsecond::per_t::(Millisecond) )?; item!("ns", nanoseconds % Nanosecond::per_t::(Microsecond))?; } Ok(()) } } impl TryFrom for Duration { type Error = error::ConversionRange; #[inline] fn try_from(original: StdDuration) -> Result { Ok(Self::new( original .as_secs() .try_into() .map_err(|_| error::ConversionRange)?, original.subsec_nanos().cast_signed(), )) } } impl TryFrom for StdDuration { type Error = error::ConversionRange; #[inline] fn try_from(duration: Duration) -> Result { Ok(Self::new( duration .seconds .try_into() .map_err(|_| error::ConversionRange)?, duration .nanoseconds .get() .try_into() .map_err(|_| error::ConversionRange)?, )) } } impl Add for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, rhs: Self) -> Self::Output { self.checked_add(rhs) .expect("overflow when adding durations") } } impl Add for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, std_duration: StdDuration) -> Self::Output { self + Self::try_from(std_duration) .expect("overflow converting `std::time::Duration` to `time::Duration`") } } impl Add for StdDuration { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, rhs: Duration) -> Self::Output { rhs + self } } impl AddAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } impl AddAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: StdDuration) { *self = *self + rhs; } } impl AddAssign for StdDuration { /// # Panics /// /// This may panic if the resulting addition cannot be represented. #[inline] #[track_caller] fn add_assign(&mut self, rhs: Duration) { *self = (*self + rhs).try_into().expect( "Cannot represent a resulting duration in std. Try `let x = x + rhs;`, which will \ change the type.", ); } } impl Neg for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn neg(self) -> Self::Output { self.checked_neg().expect("overflow when negating duration") } } impl Sub for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: Self) -> Self::Output { self.checked_sub(rhs) .expect("overflow when subtracting durations") } } impl Sub for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: StdDuration) -> Self::Output { self - Self::try_from(rhs) .expect("overflow converting `std::time::Duration` to `time::Duration`") } } impl Sub for StdDuration { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: Duration) -> Self::Output { Duration::try_from(self) .expect("overflow converting `std::time::Duration` to `time::Duration`") - rhs } } impl SubAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } impl SubAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: StdDuration) { *self = *self - rhs; } } impl SubAssign for StdDuration { /// # Panics /// /// This may panic if the resulting subtraction can not be represented. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: Duration) { *self = (*self - rhs).try_into().expect( "Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \ change the type.", ); } } /// Given a value and whether it is signed, cast it to the signed version. macro_rules! cast_signed { (@signed $val:ident) => { $val }; (@unsigned $val:ident) => { $val.cast_signed() }; } /// Implement `Mul` (reflexively), `MulAssign`, `Div`, and `DivAssign` for `Duration` for various /// signed types. macro_rules! duration_mul_div_int { ($(@$signedness:ident $type:ty),+ $(,)?) => {$( impl Mul<$type> for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128( self.whole_nanoseconds() .checked_mul(cast_signed!(@$signedness rhs).extend::()) .expect("overflow when multiplying duration") ) } } impl Mul for $type { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl MulAssign<$type> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul_assign(&mut self, rhs: $type) { *self = *self * rhs; } } impl Div<$type> for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div(self, rhs: $type) -> Self::Output { Self::nanoseconds_i128( self.whole_nanoseconds() / cast_signed!(@$signedness rhs).extend::() ) } } impl DivAssign<$type> for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div_assign(&mut self, rhs: $type) { *self = *self / rhs; } } )+}; } duration_mul_div_int! { @signed i8, @signed i16, @signed i32, @unsigned u8, @unsigned u16, @unsigned u32, } impl Mul for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: f32) -> Self::Output { Self::seconds_f32(self.as_seconds_f32() * rhs) } } impl Mul for f32 { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl Mul for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: f64) -> Self::Output { Self::seconds_f64(self.as_seconds_f64() * rhs) } } impl Mul for f64 { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul(self, rhs: Duration) -> Self::Output { rhs * self } } impl MulAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul_assign(&mut self, rhs: f32) { *self = *self * rhs; } } impl MulAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn mul_assign(&mut self, rhs: f64) { *self = *self * rhs; } } impl Div for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div(self, rhs: f32) -> Self::Output { Self::seconds_f32(self.as_seconds_f32() / rhs) } } impl Div for Duration { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div(self, rhs: f64) -> Self::Output { Self::seconds_f64(self.as_seconds_f64() / rhs) } } impl DivAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div_assign(&mut self, rhs: f32) { *self = *self / rhs; } } impl DivAssign for Duration { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn div_assign(&mut self, rhs: f64) { *self = *self / rhs; } } impl Div for Duration { type Output = f64; #[inline] #[track_caller] fn div(self, rhs: Self) -> Self::Output { self.as_seconds_f64() / rhs.as_seconds_f64() } } impl Div for Duration { type Output = f64; #[inline] #[track_caller] fn div(self, rhs: StdDuration) -> Self::Output { self.as_seconds_f64() / rhs.as_secs_f64() } } impl Div for StdDuration { type Output = f64; #[inline] #[track_caller] fn div(self, rhs: Duration) -> Self::Output { self.as_secs_f64() / rhs.as_seconds_f64() } } impl PartialEq for Duration { #[inline] fn eq(&self, rhs: &StdDuration) -> bool { Ok(*self) == Self::try_from(*rhs) } } impl PartialEq for StdDuration { #[inline] fn eq(&self, rhs: &Duration) -> bool { rhs == self } } impl PartialOrd for Duration { #[inline] fn partial_cmp(&self, rhs: &StdDuration) -> Option { if rhs.as_secs() > i64::MAX.cast_unsigned() { return Some(Ordering::Less); } Some( self.seconds .cmp(&rhs.as_secs().cast_signed()) .then_with(|| { self.nanoseconds .get() .cmp(&rhs.subsec_nanos().cast_signed()) }), ) } } impl PartialOrd for StdDuration { #[inline] fn partial_cmp(&self, rhs: &Duration) -> Option { rhs.partial_cmp(self).map(Ordering::reverse) } } impl Sum for Duration { #[inline] fn sum(iter: I) -> Self where I: Iterator, { iter.reduce(|a, b| a + b).unwrap_or_default() } } impl<'a> Sum<&'a Self> for Duration { #[inline] fn sum(iter: I) -> Self where I: Iterator, { iter.copied().sum() } } #[cfg(feature = "std")] impl Add for SystemTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: Duration) -> Self::Output { if duration.is_zero() { self } else if duration.is_positive() { self + duration.unsigned_abs() } else { debug_assert!(duration.is_negative()); self - duration.unsigned_abs() } } } #[cfg(feature = "std")] impl AddAssign for SystemTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: Duration) { *self = *self + rhs; } } #[cfg(feature = "std")] impl Sub for SystemTime { type Output = Self; #[inline] #[track_caller] fn sub(self, duration: Duration) -> Self::Output { if duration.is_zero() { self } else if duration.is_positive() { self - duration.unsigned_abs() } else { debug_assert!(duration.is_negative()); self + duration.unsigned_abs() } } } #[cfg(feature = "std")] impl SubAssign for SystemTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: Duration) { *self = *self - rhs; } } time-0.3.47/src/error/component_range.rs000064400000000000000000000054441046102023000163300ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(original: ComponentRange) -> Self { Self::ComponentRange(original) } } impl TryFrom for ComponentRange { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { 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(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 {} time-0.3.47/src/error/conversion_range.rs000064400000000000000000000017041046102023000165060ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(err: ConversionRange) -> Self { Self::ConversionRange(err) } } impl TryFrom for ConversionRange { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::ConversionRange(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.47/src/error/different_variant.rs000064400000000000000000000016521046102023000166410ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(err: DifferentVariant) -> Self { Self::DifferentVariant(err) } } impl TryFrom for DifferentVariant { type Error = Self; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::DifferentVariant(err) => Ok(err), _ => Err(Self), } } } time-0.3.47/src/error/format.rs000064400000000000000000000064431046102023000144420ustar 00000000000000//! 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), /// 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 for Format { #[inline] fn from(err: error::ComponentRange) -> Self { Self::ComponentRange(Box::new(err)) } } impl From for Format { #[inline] fn from(err: io::Error) -> Self { Self::StdIo(err) } } impl TryFrom for error::ComponentRange { type Error = error::DifferentVariant; #[inline] fn try_from(err: Format) -> Result { match err { Format::ComponentRange(err) => Ok(*err), _ => Err(error::DifferentVariant), } } } impl TryFrom for io::Error { type Error = error::DifferentVariant; #[inline] fn try_from(err: Format) -> Result { 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 for crate::Error { #[inline] fn from(original: Format) -> Self { Self::Format(original) } } impl TryFrom for Format { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { 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(self) -> S::Error where S: serde_core::Serializer, { use serde_core::ser::Error; S::Error::custom(self) } } time-0.3.47/src/error/indeterminate_offset.rs000064400000000000000000000016671046102023000173530ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(err: IndeterminateOffset) -> Self { Self::IndeterminateOffset(err) } } impl TryFrom for IndeterminateOffset { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::IndeterminateOffset(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.47/src/error/invalid_format_description.rs000064400000000000000000000101721046102023000205450ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(original: InvalidFormatDescription) -> Self { Self::InvalidFormatDescription(original) } } impl TryFrom for InvalidFormatDescription { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { 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 {} time-0.3.47/src/error/invalid_variant.rs000064400000000000000000000016531046102023000163220ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(err: InvalidVariant) -> Self { Self::InvalidVariant(err) } } impl TryFrom for InvalidVariant { type Error = crate::error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::InvalidVariant(err) => Ok(err), _ => Err(crate::error::DifferentVariant), } } } time-0.3.47/src/error/mod.rs000064400000000000000000000112311046102023000137200ustar 00000000000000//! Various error types returned by methods in the time crate. mod component_range; mod conversion_range; mod different_variant; #[cfg(feature = "formatting")] mod format; #[cfg(feature = "local-offset")] mod indeterminate_offset; #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] mod invalid_format_description; 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), } } } time-0.3.47/src/error/parse.rs000064400000000000000000000064111046102023000142570ustar 00000000000000//! 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 for Parse { #[inline] fn from(err: TryFromParsed) -> Self { Self::TryFromParsed(err) } } impl TryFrom for TryFromParsed { type Error = error::DifferentVariant; #[inline] fn try_from(err: Parse) -> Result { match err { Parse::TryFromParsed(err) => Ok(err), _ => Err(error::DifferentVariant), } } } impl From for Parse { #[inline] fn from(err: ParseFromDescription) -> Self { Self::ParseFromDescription(err) } } impl TryFrom for ParseFromDescription { type Error = error::DifferentVariant; #[inline] fn try_from(err: Parse) -> Result { match err { Parse::ParseFromDescription(err) => Ok(err), _ => Err(error::DifferentVariant), } } } impl From 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 for Parse { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { 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), } } } time-0.3.47/src/error/parse_from_description.rs000064400000000000000000000032631046102023000177070ustar 00000000000000//! 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 for crate::Error { #[inline] fn from(original: ParseFromDescription) -> Self { Self::ParseFromDescription(original) } } impl TryFrom for ParseFromDescription { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::ParseFromDescription(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.47/src/error/try_from_parsed.rs000064400000000000000000000042621046102023000163460ustar 00000000000000//! 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 for TryFromParsed { #[inline] fn from(v: error::ComponentRange) -> Self { Self::ComponentRange(v) } } impl TryFrom for error::ComponentRange { type Error = error::DifferentVariant; #[inline] fn try_from(err: TryFromParsed) -> Result { 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 for crate::Error { #[inline] fn from(original: TryFromParsed) -> Self { Self::TryFromParsed(original) } } impl TryFrom for TryFromParsed { type Error = error::DifferentVariant; #[inline] fn try_from(err: crate::Error) -> Result { match err { crate::Error::TryFromParsed(err) => Ok(err), _ => Err(error::DifferentVariant), } } } time-0.3.47/src/ext/digit_count.rs000064400000000000000000000014531046102023000151250ustar 00000000000000use 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::() + 1, None => 1, } } })* }; } impl_digit_count!(u8, u16, u32); time-0.3.47/src/ext/instant.rs000064400000000000000000000072221046102023000142750ustar 00000000000000use 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; /// 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; /// 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 { 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 { 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) } } } time-0.3.47/src/ext/mod.rs000064400000000000000000000007051046102023000133730ustar 00000000000000//! 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; time-0.3.47/src/ext/numerical_duration.rs000064400000000000000000000114651046102023000165050ustar 00000000000000use 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::(Microsecond)) as i64) } #[inline] fn milliseconds(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Millisecond)) as i64) } #[inline] fn seconds(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Second)) as i64) } #[inline] #[track_caller] fn minutes(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Minute)) as i64) } #[inline] #[track_caller] fn hours(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Hour)) as i64) } #[inline] #[track_caller] fn days(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Day)) as i64) } #[inline] #[track_caller] fn weeks(self) -> Duration { Duration::nanoseconds((self * Nanosecond::per_t::(Week)) as i64) } } time-0.3.47/src/ext/numerical_std_duration.rs000064400000000000000000000146171046102023000173610ustar 00000000000000use 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::(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::(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::(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::(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::(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::(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::(Week)) as u64) } } time-0.3.47/src/ext/systemtime.rs000064400000000000000000000057531046102023000150270ustar 00000000000000use 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; /// 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; /// 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 { 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 { 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) } } } } } time-0.3.47/src/format_description/borrowed_format_item.rs000064400000000000000000000071131046102023000221200ustar 00000000000000//! 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 for BorrowedFormatItem<'_> { #[inline] fn from(component: Component) -> Self { Self::Component(component) } } impl TryFrom> for Component { type Error = error::DifferentVariant; #[inline] fn try_from(value: BorrowedFormatItem<'_>) -> Result { 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> for &[BorrowedFormatItem<'a>] { type Error = error::DifferentVariant; #[inline] fn try_from(value: BorrowedFormatItem<'a>) -> Result { match value { BorrowedFormatItem::Compound(items) => Ok(items), _ => Err(error::DifferentVariant), } } } impl PartialEq for BorrowedFormatItem<'_> { #[inline] fn eq(&self, rhs: &Component) -> bool { matches!(self, Self::Component(component) if component == rhs) } } impl PartialEq> 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> for &[BorrowedFormatItem<'_>] { #[inline] fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { rhs == self } } time-0.3.47/src/format_description/component.rs000064400000000000000000000033311046102023000177070ustar 00000000000000//! 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), } time-0.3.47/src/format_description/mod.rs000064400000000000000000000027401046102023000164670ustar 00000000000000//! Description of how types should be formatted and parsed. //! //! The formatted value will be output to the provided writer. Format descriptions can be //! [well-known](crate::format_description::well_known) or obtained by using the //! [`format_description!`](crate::macros::format_description) macro 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; } time-0.3.47/src/format_description/modifier.rs000064400000000000000000000570021046102023000175070ustar 00000000000000//! 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, } // 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) -> 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) -> 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 } } } time-0.3.47/src/format_description/owned_format_item.rs000064400000000000000000000121531046102023000214110ustar 00000000000000//! 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), /// 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> 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::>() .into_boxed_slice(), ), BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())), BorrowedFormatItem::First(items) => Self::First( items .iter() .cloned() .map(Into::into) .collect::>() .into_boxed_slice(), ), } } } impl From>> for OwnedFormatItem { #[inline] fn from(items: Vec>) -> 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::>() .into_boxed_slice(), ) } } impl From for OwnedFormatItem { #[inline] fn from(component: Component) -> Self { Self::Component(component) } } impl TryFrom for Component { type Error = error::DifferentVariant; #[inline] fn try_from(value: OwnedFormatItem) -> Result { match value { OwnedFormatItem::Component(component) => Ok(component), _ => Err(error::DifferentVariant), } } } impl From> for OwnedFormatItem { #[inline] fn from(items: Vec) -> Self { Self::Compound(items.into_boxed_slice()) } } impl TryFrom for Vec { type Error = error::DifferentVariant; #[inline] fn try_from(value: OwnedFormatItem) -> Result { match value { OwnedFormatItem::Compound(items) => Ok(items.into_vec()), _ => Err(error::DifferentVariant), } } } impl PartialEq for OwnedFormatItem { #[inline] fn eq(&self, rhs: &Component) -> bool { matches!(self, Self::Component(component) if component == rhs) } } impl PartialEq 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 for &[OwnedFormatItem] { #[inline] fn eq(&self, rhs: &OwnedFormatItem) -> bool { rhs == self } } time-0.3.47/src/format_description/parse/ast.rs000064400000000000000000000353261046102023000176170ustar 00000000000000//! 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, /// The second bracket. _second: Unused, }, /// Part of a type, along with its modifiers. Component { /// Where the opening bracket was in the format string. _opening_bracket: Unused, /// Whitespace between the opening bracket and name. _leading_whitespace: Unused>>, /// 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>>, /// Where the closing bracket was in the format string. _closing_bracket: Unused, }, /// 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>>, /// The "optional" keyword. _optional_kw: Unused>, /// Whitespace between the "optional" keyword and the opening bracket. _whitespace: Unused>, /// 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>>, /// The "first" keyword. _first_kw: Unused>, /// Whitespace between the "first" keyword and the opening bracket. _whitespace: Unused>, /// 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, /// 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, /// Whitespace between the closing bracket and the next item. pub(super) _trailing_whitespace: Unused>>, } /// A modifier for a component. pub(super) struct Modifier<'a> { /// Whitespace preceding the modifier. pub(super) _leading_whitespace: Unused>, /// 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, /// 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, ) -> impl Iterator, Error>> + use<'item, 'iter, I, VERSION> where 'item: 'iter, I: Iterator, 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, ) -> impl Iterator, Error>> + use<'_, 'item, I, NESTED, VERSION> where I: Iterator, 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, ) -> Result, Error> where I: Iterator, 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, ) -> Result, Error> where I: Iterator, 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::>()?; 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), }) } time-0.3.47/src/format_description/parse/format_item.rs000064400000000000000000000466051046102023000213400ustar 00000000000000//! 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, Error>>, ) -> impl Iterator, 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, 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::>()?; 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::>()?; Item::First { value: items, span: opening_bracket.to(closing_bracket), } } }) } } impl<'a> TryFrom> for crate::format_description::BorrowedFormatItem<'a> { type Error = Error; #[inline] fn try_from(item: Item<'a>) -> Result { 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> 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]>> 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 { // 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 { $(#[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, }, End = "end" { trailing_input = "trailing_input": Option => trailing_input, }, Hour = "hour" { padding = "padding": Option => padding, base = "repr": Option => is_12_hour_clock, }, Ignore = "ignore" { #[required] count = "count": Option<#[from_str] NonZero> => count, }, Minute = "minute" { padding = "padding": Option => padding, }, Month = "month" { padding = "padding": Option => padding, repr = "repr": Option => repr, case_sensitive = "case_sensitive": Option => case_sensitive, }, OffsetHour = "offset_hour" { sign_behavior = "sign": Option => sign_is_mandatory, padding = "padding": Option => padding, }, OffsetMinute = "offset_minute" { padding = "padding": Option => padding, }, OffsetSecond = "offset_second" { padding = "padding": Option => padding, }, Ordinal = "ordinal" { padding = "padding": Option => padding, }, Period = "period" { case = "case": Option => is_uppercase, case_sensitive = "case_sensitive": Option => case_sensitive, }, Second = "second" { padding = "padding": Option => padding, }, Subsecond = "subsecond" { digits = "digits": Option => digits, }, UnixTimestamp = "unix_timestamp" { precision = "precision": Option => precision, sign_behavior = "sign": Option => sign_is_mandatory, }, Weekday = "weekday" { repr = "repr": Option => repr, one_indexed = "one_indexed": Option => one_indexed, case_sensitive = "case_sensitive": Option => case_sensitive, }, WeekNumber = "week_number" { padding = "padding": Option => padding, repr = "repr": Option => repr, }, Year = "year" { padding = "padding": Option => padding, repr = "repr": Option => repr, range = "range": Option => range, base = "base": Option => iso_week_based, sign_behavior = "sign": Option => 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, 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(value: &Spanned<&[u8]>) -> Result, Error> where T: FromStr, { str::from_utf8(value) .ok() .and_then(|val| val.parse::().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, }, }) } time-0.3.47/src/format_description/parse/lexer.rs000064400000000000000000000230071046102023000201400ustar 00000000000000//! 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 where I: Iterator, { /// The internal iterator. iter: iter::Peekable, } impl Iterator for Lexed where I: Iterator, { type Item = I::Item; fn next(&mut self) -> Option { self.iter.next() } } impl<'iter, 'token, I> Lexed where 'token: 'iter, I: Iterator, 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> { 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> { 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 { 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 { 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( mut input: &[u8], ) -> Lexed, 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(), } } time-0.3.47/src/format_description/parse/mod.rs000064400000000000000000000172771046102023000176140ustar 00000000000000//! 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>, 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( s: &str, ) -> Result>, error::InvalidFormatDescription> { validate_version!(VERSION); let mut lexed = lexer::lex::(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::>()?) } /// 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( s: &str, ) -> Result { validate_version!(VERSION); let mut lexed = lexer::lex::(s.as_bytes()); let ast = ast::parse::<_, VERSION>(&mut lexed); let format_items = format_item::parse(ast); let items = format_items.collect::, _>>()?; Ok(items.into()) } /// Attach [`Location`] information to each byte in the iterator. #[inline] fn attach_location<'item>( iter: impl Iterator, ) -> impl Iterator { 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 { /// The value. value: T, /// Where the value was in the format string. span: Span, } impl core::ops::Deref for Spanned { 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; } impl SpannedValue for T { #[inline] fn spanned(self, span: Span) -> Spanned { 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, /// The error needed for interoperability with the rest of `time`. public: error::InvalidFormatDescription, } impl From 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(core::marker::PhantomData); /// Indicate that a value is currently unused. #[inline] fn unused(_: T) -> Unused { Unused(core::marker::PhantomData) } time-0.3.47/src/format_description/parse/strftime.rs000064400000000000000000000426601046102023000206640ustar 00000000000000use 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>, InvalidFormatDescription> { let tokens = lex(s.as_bytes()); let items = into_items(tokens).collect::>()?; 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 { 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, padding: Spanned, component: Spanned, }, } #[inline] fn lex(mut input: &[u8]) -> iter::Peekable, 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, ) -> impl Iterator, Error>> + use<'token, I> where 'token: 'iter, I: Iterator, 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, component: Spanned, ) -> Result, 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, }, }); } }) } time-0.3.47/src/format_description/well_known/iso8601/adt_hack.rs000064400000000000000000000231431046102023000227360ustar 00000000000000//! 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 Iso8601 { /// 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); } } time-0.3.47/src/format_description/well_known/iso8601.rs000064400000000000000000000227211046102023000212010ustar 00000000000000//! 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; impl core::fmt::Debug for Iso8601 { #[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>, }, /// 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>, }, /// 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>, }, } /// 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 } } } time-0.3.47/src/format_description/well_known/rfc2822.rs000064400000000000000000000020231046102023000211510ustar 00000000000000//! 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; time-0.3.47/src/format_description/well_known/rfc3339.rs000064400000000000000000000020141046102023000211550ustar 00000000000000//! 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; time-0.3.47/src/formatting/component_provider.rs000064400000000000000000000304231046102023000201020ustar 00000000000000use 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>, month: Option, iso_week: Option>, iso_year_is_initialized: bool, iso_year: MaybeUninit, } 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() } } time-0.3.47/src/formatting/formattable.rs000064400000000000000000000323531046102023000164720ustar 00000000000000//! 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 Formattable for Iso8601 {} impl Formattable for T where T: Deref {} /// 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result where V: ComponentProvider; /// Format the item directly to a `String`. #[inline] fn format(&self, value: &V, state: &mut V::State) -> Result 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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 T where T: Deref, { #[expect( private_bounds, private_interfaces, reason = "irrelevant due to being a sealed trait" )] #[inline] fn format_into( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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::()] .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::() - 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( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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 sealed::Sealed for Iso8601 { #[expect( private_bounds, private_interfaces, reason = "irrelevant due to being a sealed trait" )] #[inline] fn format_into( &self, output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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) } } time-0.3.47/src/formatting/iso8601.rs000064400000000000000000000145701046102023000153040ustar 00000000000000//! 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( output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result where V: ComponentProvider, { let mut bytes = 0; match Iso8601::::DATE_KIND { DateKind::Calendar => { let year = value.calendar_year(state); if Iso8601::::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::::USE_SEPARATORS, b"-")?; bytes += format_number_pad_zero::<2>(output, u8::from(value.month(state)))?; bytes += write_if(output, Iso8601::::USE_SEPARATORS, b"-")?; bytes += format_number_pad_zero::<2>(output, value.day(state))?; } DateKind::Week => { let year = value.iso_year(state); if Iso8601::::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::::USE_SEPARATORS, b"-W", b"W")?; bytes += format_number_pad_zero::<2>(output, value.iso_week_number(state))?; bytes += write_if(output, Iso8601::::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::::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::::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( output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result 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::::USE_SEPARATORS || Iso8601::::FORMAT_DATE, b"T", )?; match Iso8601::::TIME_PRECISION { TimePrecision::Hour { decimal_digits } => { let hours = (value.hour(state) as f64) + (value.minute(state) as f64) / Minute::per_t::(Hour) + (value.second(state) as f64) / Second::per_t::(Hour) + (value.nanosecond(state) as f64) / Nanosecond::per_t::(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::::USE_SEPARATORS, b":")?; let minutes = (value.minute(state) as f64) + (value.second(state) as f64) / Second::per_t::(Minute) + (value.nanosecond(state) as f64) / Nanosecond::per_t::(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::::USE_SEPARATORS, b":")?; bytes += format_number_pad_zero::<2>(output, value.minute(state))?; bytes += write_if(output, Iso8601::::USE_SEPARATORS, b":")?; let seconds = (value.second(state) as f64) + (value.nanosecond(state) as f64) / Nanosecond::per_t::(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( output: &mut (impl io::Write + ?Sized), value: &V, state: &mut V::State, ) -> Result where V: ComponentProvider, { if Iso8601::::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::::OFFSET_PRECISION == OffsetPrecision::Hour && minutes != 0 { return Err(error::Format::InvalidComponent("offset_minute")); } else if Iso8601::::OFFSET_PRECISION == OffsetPrecision::Minute { bytes += write_if(output, Iso8601::::USE_SEPARATORS, b":")?; bytes += format_number_pad_zero::<2>(output, minutes.unsigned_abs())?; } Ok(bytes) } time-0.3.47/src/formatting/mod.rs000064400000000000000000000551451046102023000147550ustar 00000000000000//! 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 { 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 { 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 { 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) -> 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>, ) -> io::Result { 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::() + 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( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, padding: modifier::Padding, ) -> Result { match padding { modifier::Padding::Space => format_number_pad_space::(output, value), modifier::Padding::Zero => format_number_pad_zero::(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( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, ) -> Result { 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( output: &mut (impl io::Write + ?Sized), value: impl itoa::Integer + DigitCount + Copy, ) -> Result { 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 { 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( output: &mut (impl io::Write + ?Sized), component: Component, value: &V, state: &mut V::State, ) -> Result 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 { 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 { match repr { modifier::MonthRepr::Numerical => format_number::<2>(output, u8::from(month), padding), modifier::MonthRepr::Long => { write(output, MONTH_NAMES[u8::from(month).extend::() - 1]) } // Safety: All month names are at least three bytes long. modifier::MonthRepr::Short => write(output, unsafe { MONTH_NAMES[u8::from(month).extend::() - 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 { 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 { 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::()].get_unchecked(..3) }), modifier::WeekdayRepr::Long => write( output, WEEKDAY_NAMES[weekday.number_days_from_monday().extend::()], ), 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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) } time-0.3.47/src/hint.rs000064400000000000000000000010711046102023000127530ustar 00000000000000//! 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 } time-0.3.47/src/instant.rs000064400000000000000000000253141046102023000134770ustar 00000000000000//! 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 { 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 { 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 for Instant { #[inline] fn from(instant: StdInstant) -> Self { Self(instant) } } impl From 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 for Instant { type Output = Duration; #[inline] fn sub(self, other: StdInstant) -> Self::Output { self - Self(other) } } impl Sub for StdInstant { type Output = Duration; #[inline] fn sub(self, other: Instant) -> Self::Output { Instant(self) - other } } impl Add 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 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 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 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 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 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 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 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 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 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 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 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 for Instant { #[inline] fn eq(&self, rhs: &StdInstant) -> bool { self.0.eq(rhs) } } impl PartialEq for StdInstant { #[inline] fn eq(&self, rhs: &Instant) -> bool { self.eq(&rhs.0) } } impl PartialOrd for Instant { #[inline] fn partial_cmp(&self, rhs: &StdInstant) -> Option { self.0.partial_cmp(rhs) } } impl PartialOrd for StdInstant { #[inline] fn partial_cmp(&self, rhs: &Instant) -> Option { self.partial_cmp(&rhs.0) } } impl AsRef for Instant { #[inline] fn as_ref(&self) -> &StdInstant { &self.0 } } impl Borrow for Instant { #[inline] fn borrow(&self) -> &StdInstant { &self.0 } } time-0.3.47/src/internal_macros.rs000064400000000000000000000171001046102023000151710ustar 00000000000000//! 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}; time-0.3.47/src/interop/js_sys_date_offsetdatetime.rs000064400000000000000000000014741046102023000210720ustar 00000000000000use crate::OffsetDateTime; use crate::convert::*; impl From 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::(Millisecond); Self::from_unix_timestamp_nanos(timestamp_nanos) .expect("invalid timestamp: Timestamp cannot fit in range") } } impl From for js_sys::Date { fn from(datetime: OffsetDateTime) -> Self { // new Date() takes milliseconds let timestamp = (datetime.unix_timestamp_nanos() / Nanosecond::per_t::(Millisecond)) as f64; Self::new(×tamp.into()) } } time-0.3.47/src/interop/js_sys_date_utcdatetime.rs000064400000000000000000000014611046102023000203730ustar 00000000000000use crate::UtcDateTime; use crate::convert::*; impl From 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::(Millisecond)) as i128; Self::from_unix_timestamp_nanos(timestamp_nanos) .expect("invalid timestamp: Timestamp cannot fit in range") } } impl From for js_sys::Date { fn from(datetime: UtcDateTime) -> Self { // new Date() takes milliseconds let timestamp = (datetime.unix_timestamp_nanos() / Nanosecond::per_t::(Millisecond)) as f64; Self::new(×tamp.into()) } } time-0.3.47/src/interop/mod.rs000064400000000000000000000021601046102023000142500ustar 00000000000000//! 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; time-0.3.47/src/interop/offsetdatetime_systemtime.rs000064400000000000000000000042251046102023000207630ustar 00000000000000use core::cmp::Ordering; use core::ops::Sub; use std::time::SystemTime; use crate::{Duration, OffsetDateTime}; impl Sub 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 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 for OffsetDateTime { #[inline] fn eq(&self, rhs: &SystemTime) -> bool { self == &Self::from(*rhs) } } impl PartialEq for SystemTime { #[inline] fn eq(&self, rhs: &OffsetDateTime) -> bool { &OffsetDateTime::from(*self) == rhs } } impl PartialOrd for OffsetDateTime { #[inline] fn partial_cmp(&self, other: &SystemTime) -> Option { self.partial_cmp(&Self::from(*other)) } } impl PartialOrd for SystemTime { #[inline] fn partial_cmp(&self, other: &OffsetDateTime) -> Option { OffsetDateTime::from(*self).partial_cmp(other) } } impl From 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 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() } } } time-0.3.47/src/interop/offsetdatetime_utcdatetime.rs000064400000000000000000000034641046102023000210740ustar 00000000000000use core::cmp::Ordering; use core::ops::Sub; use crate::{Duration, OffsetDateTime, UtcDateTime}; impl Sub 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 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 for UtcDateTime { #[inline] fn eq(&self, other: &OffsetDateTime) -> bool { OffsetDateTime::from(*self) == *other } } impl PartialEq for OffsetDateTime { #[inline] fn eq(&self, other: &UtcDateTime) -> bool { *self == Self::from(*other) } } impl PartialOrd for UtcDateTime { #[inline] fn partial_cmp(&self, other: &OffsetDateTime) -> Option { OffsetDateTime::from(*self).partial_cmp(other) } } impl PartialOrd for OffsetDateTime { #[inline] fn partial_cmp(&self, other: &UtcDateTime) -> Option { self.partial_cmp(&Self::from(*other)) } } impl From for UtcDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn from(datetime: OffsetDateTime) -> Self { datetime.to_utc() } } impl From for OffsetDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn from(datetime: UtcDateTime) -> Self { datetime.as_primitive().assume_utc() } } time-0.3.47/src/interop/utcdatetime_systemtime.rs000064400000000000000000000042121046102023000202640ustar 00000000000000use core::cmp::Ordering; use core::ops::Sub; use std::time::SystemTime; use crate::{Duration, UtcDateTime}; impl Sub 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 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 for UtcDateTime { #[inline] fn eq(&self, rhs: &SystemTime) -> bool { self == &Self::from(*rhs) } } impl PartialEq for SystemTime { #[inline] fn eq(&self, rhs: &UtcDateTime) -> bool { &UtcDateTime::from(*self) == rhs } } impl PartialOrd for UtcDateTime { #[inline] fn partial_cmp(&self, other: &SystemTime) -> Option { self.partial_cmp(&Self::from(*other)) } } impl PartialOrd for SystemTime { #[inline] fn partial_cmp(&self, other: &UtcDateTime) -> Option { UtcDateTime::from(*self).partial_cmp(other) } } impl From 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 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() } } } time-0.3.47/src/lib.rs000064400000000000000000000115461046102023000125670ustar 00000000000000//! # 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 = core::result::Result; time-0.3.47/src/macros.rs000064400000000000000000000141201046102023000132740ustar 00000000000000//! 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; time-0.3.47/src/month.rs000064400000000000000000000163121046102023000131420ustar 00000000000000//! 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) -> Result { 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 { 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 for u8 { #[inline] fn from(month: Month) -> Self { month as Self } } impl TryFrom for Month { type Error = error::ComponentRange; #[inline] fn try_from(value: u8) -> Result { match NonZero::new(value) { Some(value) => Self::from_number(value), None => Err(error::ComponentRange::unconditional("month")), } } } time-0.3.47/src/offset_date_time.rs000064400000000000000000001603621046102023000153230ustar 00000000000000//! The [`OffsetDateTime`] struct and its associated `impl`s. #[cfg(feature = "formatting")] use alloc::string::String; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::time::Duration as StdDuration; #[cfg(feature = "formatting")] use std::io; use deranged::RangedI64; use num_conv::prelude::*; use powerfmt::ext::FormatterExt as _; use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; use time_core::convert::*; use crate::date::{MAX_YEAR, MIN_YEAR}; #[cfg(feature = "formatting")] use crate::formatting::Formattable; use crate::internal_macros::{carry, cascade, const_try, const_try_opt, div_floor, ensure_ranged}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::days_in_year; use crate::{ Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, error, }; /// The Julian day of the Unix epoch. const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day(); /// A [`PrimitiveDateTime`] with a [`UtcOffset`]. #[derive(Clone, Copy, Eq)] pub struct OffsetDateTime { local_date_time: PrimitiveDateTime, offset: UtcOffset, } impl PartialEq for OffsetDateTime { #[inline] fn eq(&self, other: &Self) -> bool { raw_to_bits((self.year(), self.ordinal(), self.time())) == raw_to_bits(other.to_offset_raw(self.offset())) } } impl PartialOrd for OffsetDateTime { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for OffsetDateTime { #[inline] fn cmp(&self, other: &Self) -> Ordering { raw_to_bits((self.year(), self.ordinal(), self.time())) .cmp(&raw_to_bits(other.to_offset_raw(self.offset()))) } } impl Hash for OffsetDateTime { #[inline] fn hash(&self, state: &mut H) where H: Hasher, { raw_to_bits(self.to_utc_raw()).hash(state); } } /// **Note**: This value is explicitly signed, so do not cast this to or treat this as an /// unsigned integer. Doing so will lead to incorrect results for values with differing /// signs. #[inline] const fn raw_to_bits((year, ordinal, time): (i32, u16, Time)) -> i128 { ((year as i128) << 74) | ((ordinal as i128) << 64) | (time.as_u64() as i128) } impl OffsetDateTime { /// Midnight, 1 January, 1970 (UTC). /// /// ```rust /// # use time::OffsetDateTime; /// # use time_macros::datetime; /// assert_eq!(OffsetDateTime::UNIX_EPOCH, datetime!(1970-01-01 0:00 UTC)); /// ``` pub const UNIX_EPOCH: Self = Self::new_in_offset(Date::UNIX_EPOCH, Time::MIDNIGHT, UtcOffset::UTC); /// Create a new `OffsetDateTime` with the current date and time in UTC. /// /// ```rust /// # use time::OffsetDateTime; /// # use time_macros::offset; /// assert!(OffsetDateTime::now_utc().year() >= 2019); /// assert_eq!(OffsetDateTime::now_utc().offset(), offset!(UTC)); /// ``` #[cfg(feature = "std")] #[inline] pub fn now_utc() -> Self { #[cfg(all( target_family = "wasm", not(any(target_os = "emscripten", target_os = "wasi")), feature = "wasm-bindgen" ))] { js_sys::Date::new_0().into() } #[cfg(not(all( target_family = "wasm", not(any(target_os = "emscripten", target_os = "wasi")), feature = "wasm-bindgen" )))] std::time::SystemTime::now().into() } /// Attempt to create a new `OffsetDateTime` with the current date and time in the local offset. /// If the offset cannot be determined, an error is returned. /// /// ```rust /// # use time::OffsetDateTime; /// # if false { /// assert!(OffsetDateTime::now_local().is_ok()); /// # } /// ``` #[cfg(feature = "local-offset")] #[inline] pub fn now_local() -> Result { let t = Self::now_utc(); Ok(t.to_offset(UtcOffset::local_offset_at(t)?)) } /// Create a new `OffsetDateTime` with the given [`Date`], [`Time`], and [`UtcOffset`]. /// /// ``` /// # use time::{Date, Month, OffsetDateTime, Time, UtcOffset}; /// # use time_macros::datetime; /// let dt = OffsetDateTime::new_in_offset( /// Date::from_calendar_date(2024, Month::January, 1)?, /// Time::from_hms_nano(12, 59, 59, 500_000_000)?, /// UtcOffset::from_hms(-5, 0, 0)?, /// ); /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 -5)); /// # Ok::<_, time::error::Error>(()) /// ``` #[inline] pub const fn new_in_offset(date: Date, time: Time, offset: UtcOffset) -> Self { Self { local_date_time: date.with_time(time), offset, } } /// Create a new `OffsetDateTime` with the given [`Date`] and [`Time`] in the UTC timezone. /// /// ``` /// # use time::{Date, Month, OffsetDateTime, Time}; /// # use time_macros::datetime; /// let dt = OffsetDateTime::new_utc( /// Date::from_calendar_date(2024, Month::January, 1)?, /// Time::from_hms_nano(12, 59, 59, 500_000_000)?, /// ); /// assert_eq!(dt, datetime!(2024-01-01 12:59:59.5 UTC)); /// # Ok::<_, time::error::Error>(()) /// ``` #[inline] pub const fn new_utc(date: Date, time: Time) -> Self { PrimitiveDateTime::new(date, time).assume_utc() } /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`]. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!( /// datetime!(2000-01-01 0:00 UTC) /// .to_offset(offset!(-1)) /// .year(), /// 1999, /// ); /// /// // Let's see what time Sydney's new year's celebration is in New York and Los Angeles. /// /// // Construct midnight on new year's in Sydney. /// let sydney = datetime!(2000-01-01 0:00 +11); /// let new_york = sydney.to_offset(offset!(-5)); /// let los_angeles = sydney.to_offset(offset!(-8)); /// assert_eq!(sydney.hour(), 0); /// assert_eq!(new_york.hour(), 8); /// assert_eq!(los_angeles.hour(), 5); /// ``` /// /// # Panics /// /// This method panics if the local date-time in the new offset is outside the supported range. #[inline] #[track_caller] pub const fn to_offset(self, offset: UtcOffset) -> Self { self.checked_to_offset(offset) .expect("local datetime out of valid range") } /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to the provided [`UtcOffset`], /// returning `None` if the date-time in the resulting offset is invalid. /// /// ```rust /// # use time::PrimitiveDateTime; /// # use time_macros::{datetime, offset}; /// assert_eq!( /// datetime!(2000-01-01 0:00 UTC) /// .checked_to_offset(offset!(-1)) /// .unwrap() /// .year(), /// 1999, /// ); /// assert_eq!( /// PrimitiveDateTime::MAX /// .assume_utc() /// .checked_to_offset(offset!(+1)), /// None, /// ); /// ``` #[inline] pub const fn checked_to_offset(self, offset: UtcOffset) -> Option { if self.offset.as_u32_for_equality() == offset.as_u32_for_equality() { return Some(self); } let (year, ordinal, time) = self.to_offset_raw(offset); if year > MAX_YEAR || year < MIN_YEAR { return None; } Some(Self::new_in_offset( // Safety: `ordinal` is not zero. unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) }, time, offset, )) } /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to UTC, returning a /// [`UtcDateTime`]. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2000-01-01 0:00 +1) /// .to_utc() /// .year(), /// 1999, /// ); /// ``` /// /// # Panics /// /// This method panics if the UTC date-time is outside the supported range. #[inline] #[track_caller] pub const fn to_utc(self) -> UtcDateTime { self.checked_to_utc() .expect("local datetime out of valid range") } /// Convert the `OffsetDateTime` from the current [`UtcOffset`] to UTC, returning `None` if the /// UTC date-time is invalid. Returns a [`UtcDateTime`]. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2000-01-01 0:00 +1) /// .checked_to_utc() /// .unwrap() /// .year(), /// 1999, /// ); /// assert_eq!( #[cfg_attr( feature = "large-dates", doc = " datetime!(+999999-12-31 23:59:59 -1).checked_to_utc()," )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(9999-12-31 23:59:59 -1).checked_to_utc()," )] /// None, /// ); /// ``` #[inline] pub const fn checked_to_utc(self) -> Option { if self.offset.is_utc() { return Some(self.local_date_time.as_utc()); } let (year, ordinal, time) = self.to_utc_raw(); if year > MAX_YEAR || year < MIN_YEAR { return None; } Some(UtcDateTime::new( // Safety: `ordinal` is not zero. unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) }, time, )) } /// Equivalent to `.to_utc()`, but returning the year, ordinal, and time. This avoids /// constructing an invalid [`Date`] if the new value is out of range. #[inline] pub(crate) const fn to_utc_raw(self) -> (i32, u16, Time) { let from = self.offset; // Fast path for when no conversion is necessary. if from.is_utc() { return (self.year(), self.ordinal(), self.time()); } let (second, carry) = carry!(@most_once self.second().cast_signed() - from.seconds_past_minute(), 0..Second::per_t(Minute) ); let (minute, carry) = carry!(@most_once self.minute().cast_signed() - from.minutes_past_hour() + carry, 0..Minute::per_t(Hour) ); let (hour, carry) = carry!(@most_twice self.hour().cast_signed() - from.whole_hours() + carry, 0..Hour::per_t(Day) ); let (mut year, ordinal) = self.to_ordinal_date(); let mut ordinal = ordinal.cast_signed() + carry; cascade!(ordinal => year); debug_assert!(ordinal > 0); debug_assert!(ordinal <= days_in_year(year).cast_signed()); ( year, ordinal.cast_unsigned(), // Safety: The cascades above ensure the values are in range. unsafe { Time::__from_hms_nanos_unchecked( hour.cast_unsigned(), minute.cast_unsigned(), second.cast_unsigned(), self.nanosecond(), ) }, ) } /// Equivalent to `.to_offset(offset)`, but returning the year, ordinal, and time. This avoids /// constructing an invalid [`Date`] if the new value is out of range. #[inline] pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) { let from = self.offset; let to = offset; // Fast path for when no conversion is necessary. if from.as_u32_for_equality() == to.as_u32_for_equality() { return (self.year(), self.ordinal(), self.time()); } let (second, carry) = carry!(@most_twice self.second() as i16 - from.seconds_past_minute() as i16 + to.seconds_past_minute() as i16, 0..Second::per_t(Minute) ); let (minute, carry) = carry!(@most_twice self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16 + carry, 0..Minute::per_t(Hour) ); let (hour, carry) = carry!(@most_thrice self.hour().cast_signed() - from.whole_hours() + to.whole_hours() + carry, 0..Hour::per_t(Day) ); let (mut year, ordinal) = self.to_ordinal_date(); let mut ordinal = ordinal.cast_signed() + carry; cascade!(ordinal => year); debug_assert!(ordinal > 0); debug_assert!(ordinal <= days_in_year(year).cast_signed()); ( year, ordinal.cast_unsigned(), // Safety: The cascades above ensure the values are in range. unsafe { Time::__from_hms_nanos_unchecked( hour.cast_unsigned(), minute as u8, second as u8, self.nanosecond(), ) }, ) } /// Create an `OffsetDateTime` from the provided Unix timestamp. Calling `.offset()` on the /// resulting value is guaranteed to return UTC. /// /// ```rust /// # use time::OffsetDateTime; /// # use time_macros::datetime; /// assert_eq!( /// OffsetDateTime::from_unix_timestamp(0), /// Ok(OffsetDateTime::UNIX_EPOCH), /// ); /// assert_eq!( /// OffsetDateTime::from_unix_timestamp(1_546_300_800), /// Ok(datetime!(2019-01-01 0:00 UTC)), /// ); /// ``` /// /// If you have a timestamp-nanosecond pair, you can use something along the lines of the /// following: /// /// ```rust /// # use time::{Duration, OffsetDateTime, ext::NumericalDuration}; /// let (timestamp, nanos) = (1, 500_000_000); /// assert_eq!( /// OffsetDateTime::from_unix_timestamp(timestamp)? + Duration::nanoseconds(nanos), /// OffsetDateTime::UNIX_EPOCH + 1.5.seconds() /// ); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub const fn from_unix_timestamp(timestamp: i64) -> Result { type Timestamp = RangedI64< { OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC) .unix_timestamp() }, { OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC).unix_timestamp() }, >; ensure_ranged!(Timestamp: timestamp); // Use the unchecked method here, as the input validity has already been verified. // Safety: The Julian day number is in range. let date = unsafe { Date::from_julian_day_unchecked( UNIX_EPOCH_JULIAN_DAY + div_floor!(timestamp, Second::per_t::(Day)) as i32, ) }; let seconds_within_day = timestamp.rem_euclid(Second::per_t(Day)); // Safety: All values are in range. let time = unsafe { Time::__from_hms_nanos_unchecked( (seconds_within_day / Second::per_t::(Hour)) as u8, ((seconds_within_day % Second::per_t::(Hour)) / Minute::per_t::(Hour)) as u8, (seconds_within_day % Second::per_t::(Minute)) as u8, 0, ) }; Ok(Self::new_in_offset(date, time, UtcOffset::UTC)) } /// Construct an `OffsetDateTime` from the provided Unix timestamp (in nanoseconds). Calling /// `.offset()` on the resulting value is guaranteed to return UTC. /// /// ```rust /// # use time::OffsetDateTime; /// # use time_macros::datetime; /// assert_eq!( /// OffsetDateTime::from_unix_timestamp_nanos(0), /// Ok(OffsetDateTime::UNIX_EPOCH), /// ); /// assert_eq!( /// OffsetDateTime::from_unix_timestamp_nanos(1_546_300_800_000_000_000), /// Ok(datetime!(2019-01-01 0:00 UTC)), /// ); /// ``` #[inline] pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result { let datetime = const_try!(Self::from_unix_timestamp(div_floor!( timestamp, Nanosecond::per_t::(Second) ) as i64)); Ok(Self::new_in_offset( datetime.date(), // Safety: `nanosecond` is in range due to `rem_euclid`. unsafe { Time::__from_hms_nanos_unchecked( datetime.hour(), datetime.minute(), datetime.second(), timestamp.rem_euclid(Nanosecond::per_t(Second)) as u32, ) }, UtcOffset::UTC, )) } /// Get the [`UtcOffset`]. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).offset(), offset!(UTC)); /// assert_eq!(datetime!(2019-01-01 0:00 +1).offset(), offset!(+1)); /// ``` #[inline] pub const fn offset(self) -> UtcOffset { self.offset } /// Get the [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time). /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp(), 0); /// assert_eq!(datetime!(1970-01-01 0:00 -1).unix_timestamp(), 3_600); /// ``` #[inline] pub const fn unix_timestamp(self) -> i64 { let days = (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per_t::(Day); let hours = self.hour() as i64 * Second::per_t::(Hour); let minutes = self.minute() as i64 * Second::per_t::(Minute); let seconds = self.second() as i64; let offset_seconds = self.offset.whole_seconds() as i64; days + hours + minutes + seconds - offset_seconds } /// Get the Unix timestamp in nanoseconds. /// /// ```rust /// use time_macros::datetime; /// assert_eq!(datetime!(1970-01-01 0:00 UTC).unix_timestamp_nanos(), 0); /// assert_eq!( /// datetime!(1970-01-01 0:00 -1).unix_timestamp_nanos(), /// 3_600_000_000_000, /// ); /// ``` #[inline] pub const fn unix_timestamp_nanos(self) -> i128 { self.unix_timestamp() as i128 * Nanosecond::per_t::(Second) + self.nanosecond() as i128 } /// Get the [`PrimitiveDateTime`] in the stored offset. #[inline] pub(crate) const fn date_time(self) -> PrimitiveDateTime { self.local_date_time } /// Get the [`Date`] in the stored offset. /// /// ```rust /// # use time_macros::{date, datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).date(), date!(2019-01-01)); /// assert_eq!( /// datetime!(2019-01-01 0:00 UTC) /// .to_offset(offset!(-1)) /// .date(), /// date!(2018-12-31), /// ); /// ``` #[inline] pub const fn date(self) -> Date { self.date_time().date() } /// Get the [`Time`] in the stored offset. /// /// ```rust /// # use time_macros::{datetime, offset, time}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).time(), time!(0:00)); /// assert_eq!( /// datetime!(2019-01-01 0:00 UTC) /// .to_offset(offset!(-1)) /// .time(), /// time!(23:00) /// ); /// ``` #[inline] pub const fn time(self) -> Time { self.date_time().time() } /// Get the year of the date in the stored offset. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).year(), 2019); /// assert_eq!( /// datetime!(2019-12-31 23:00 UTC) /// .to_offset(offset!(+1)) /// .year(), /// 2020, /// ); /// assert_eq!(datetime!(2020-01-01 0:00 UTC).year(), 2020); /// ``` #[inline] pub const fn year(self) -> i32 { self.date().year() } /// Get the month of the date in the stored offset. /// /// ```rust /// # use time::Month; /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).month(), Month::January); /// assert_eq!( /// datetime!(2019-12-31 23:00 UTC) /// .to_offset(offset!(+1)) /// .month(), /// Month::January, /// ); /// ``` #[inline] pub const fn month(self) -> Month { self.date().month() } /// Get the day of the date in the stored offset. /// /// The returned value will always be in the range `1..=31`. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).day(), 1); /// assert_eq!( /// datetime!(2019-12-31 23:00 UTC) /// .to_offset(offset!(+1)) /// .day(), /// 1, /// ); /// ``` #[inline] pub const fn day(self) -> u8 { self.date().day() } /// Get the day of the year of the date in the stored offset. /// /// The returned value will always be in the range `1..=366`. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).ordinal(), 1); /// assert_eq!( /// datetime!(2019-12-31 23:00 UTC) /// .to_offset(offset!(+1)) /// .ordinal(), /// 1, /// ); /// ``` #[inline] pub const fn ordinal(self) -> u16 { self.date().ordinal() } /// Get the ISO week number of the date in the stored offset. /// /// The returned value will always be in the range `1..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).iso_week(), 1); /// assert_eq!(datetime!(2020-01-01 0:00 UTC).iso_week(), 1); /// assert_eq!(datetime!(2020-12-31 0:00 UTC).iso_week(), 53); /// assert_eq!(datetime!(2021-01-01 0:00 UTC).iso_week(), 53); /// ``` #[inline] pub const fn iso_week(self) -> u8 { self.date().iso_week() } /// Get the week number where week 1 begins on the first Sunday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).sunday_based_week(), 0); /// assert_eq!(datetime!(2020-01-01 0:00 UTC).sunday_based_week(), 0); /// assert_eq!(datetime!(2020-12-31 0:00 UTC).sunday_based_week(), 52); /// assert_eq!(datetime!(2021-01-01 0:00 UTC).sunday_based_week(), 0); /// ``` #[inline] pub const fn sunday_based_week(self) -> u8 { self.date().sunday_based_week() } /// Get the week number where week 1 begins on the first Monday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).monday_based_week(), 0); /// assert_eq!(datetime!(2020-01-01 0:00 UTC).monday_based_week(), 0); /// assert_eq!(datetime!(2020-12-31 0:00 UTC).monday_based_week(), 52); /// assert_eq!(datetime!(2021-01-01 0:00 UTC).monday_based_week(), 0); /// ``` #[inline] pub const fn monday_based_week(self) -> u8 { self.date().monday_based_week() } /// Get the year, month, and day. /// /// ```rust /// # use time::Month; /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00 UTC).to_calendar_date(), /// (2019, Month::January, 1) /// ); /// ``` #[inline] pub const fn to_calendar_date(self) -> (i32, Month, u8) { self.date().to_calendar_date() } /// Get the year and ordinal day number. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00 UTC).to_ordinal_date(), /// (2019, 1) /// ); /// ``` #[inline] pub const fn to_ordinal_date(self) -> (i32, u16) { self.date().to_ordinal_date() } /// Get the ISO 8601 year, week number, and weekday. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00 UTC).to_iso_week_date(), /// (2019, 1, Tuesday) /// ); /// assert_eq!( /// datetime!(2019-10-04 0:00 UTC).to_iso_week_date(), /// (2019, 40, Friday) /// ); /// assert_eq!( /// datetime!(2020-01-01 0:00 UTC).to_iso_week_date(), /// (2020, 1, Wednesday) /// ); /// assert_eq!( /// datetime!(2020-12-31 0:00 UTC).to_iso_week_date(), /// (2020, 53, Thursday) /// ); /// assert_eq!( /// datetime!(2021-01-01 0:00 UTC).to_iso_week_date(), /// (2020, 53, Friday) /// ); /// ``` #[inline] pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { self.date().to_iso_week_date() } /// Get the weekday of the date in the stored offset. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).weekday(), Tuesday); /// assert_eq!(datetime!(2019-02-01 0:00 UTC).weekday(), Friday); /// assert_eq!(datetime!(2019-03-01 0:00 UTC).weekday(), Friday); /// ``` #[inline] pub const fn weekday(self) -> Weekday { self.date().weekday() } /// Get the Julian day for the date. The time is not taken into account for this calculation. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(-4713-11-24 0:00 UTC).to_julian_day(), 0); /// assert_eq!(datetime!(2000-01-01 0:00 UTC).to_julian_day(), 2_451_545); /// assert_eq!(datetime!(2019-01-01 0:00 UTC).to_julian_day(), 2_458_485); /// assert_eq!(datetime!(2019-12-31 0:00 UTC).to_julian_day(), 2_458_849); /// ``` #[inline] pub const fn to_julian_day(self) -> i32 { self.date().to_julian_day() } /// Get the clock hour, minute, and second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2020-01-01 0:00:00 UTC).to_hms(), (0, 0, 0)); /// assert_eq!(datetime!(2020-01-01 23:59:59 UTC).to_hms(), (23, 59, 59)); /// ``` #[inline] pub const fn to_hms(self) -> (u8, u8, u8) { self.time().as_hms() } /// Get the clock hour, minute, second, and millisecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2020-01-01 0:00:00 UTC).to_hms_milli(), /// (0, 0, 0, 0) /// ); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999 UTC).to_hms_milli(), /// (23, 59, 59, 999) /// ); /// ``` #[inline] pub const fn to_hms_milli(self) -> (u8, u8, u8, u16) { self.time().as_hms_milli() } /// Get the clock hour, minute, second, and microsecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2020-01-01 0:00:00 UTC).to_hms_micro(), /// (0, 0, 0, 0) /// ); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999_999 UTC).to_hms_micro(), /// (23, 59, 59, 999_999) /// ); /// ``` #[inline] pub const fn to_hms_micro(self) -> (u8, u8, u8, u32) { self.time().as_hms_micro() } /// Get the clock hour, minute, second, and nanosecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2020-01-01 0:00:00 UTC).to_hms_nano(), /// (0, 0, 0, 0) /// ); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999_999_999 UTC).to_hms_nano(), /// (23, 59, 59, 999_999_999) /// ); /// ``` #[inline] pub const fn to_hms_nano(self) -> (u8, u8, u8, u32) { self.time().as_hms_nano() } /// Get the clock hour in the stored offset. /// /// The returned value will always be in the range `0..24`. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).hour(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59 UTC) /// .to_offset(offset!(-2)) /// .hour(), /// 21, /// ); /// ``` #[inline] pub const fn hour(self) -> u8 { self.time().hour() } /// Get the minute within the hour in the stored offset. /// /// The returned value will always be in the range `0..60`. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).minute(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59 UTC) /// .to_offset(offset!(+0:30)) /// .minute(), /// 29, /// ); /// ``` #[inline] pub const fn minute(self) -> u8 { self.time().minute() } /// Get the second within the minute in the stored offset. /// /// The returned value will always be in the range `0..60`. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).second(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59 UTC) /// .to_offset(offset!(+0:00:30)) /// .second(), /// 29, /// ); /// ``` #[inline] pub const fn second(self) -> u8 { self.time().second() } // Because a `UtcOffset` is limited in resolution to one second, any subsecond value will not // change when adjusting for the offset. /// Get the milliseconds within the second in the stored offset. /// /// The returned value will always be in the range `0..1_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).millisecond(), 0); /// assert_eq!(datetime!(2019-01-01 23:59:59.999 UTC).millisecond(), 999); /// ``` #[inline] pub const fn millisecond(self) -> u16 { self.time().millisecond() } /// Get the microseconds within the second in the stored offset. /// /// The returned value will always be in the range `0..1_000_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).microsecond(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59.999_999 UTC).microsecond(), /// 999_999, /// ); /// ``` #[inline] pub const fn microsecond(self) -> u32 { self.time().microsecond() } /// Get the nanoseconds within the second in the stored offset. /// /// The returned value will always be in the range `0..1_000_000_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00 UTC).nanosecond(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59.999_999_999 UTC).nanosecond(), /// 999_999_999, /// ); /// ``` #[inline] pub const fn nanosecond(self) -> u32 { self.time().nanosecond() } /// Computes `self + duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::{datetime, offset}; /// let datetime = Date::MIN.midnight().assume_offset(offset!(+10)); /// assert_eq!(datetime.checked_add((-2).days()), None); /// /// let datetime = Date::MAX.midnight().assume_offset(offset!(+10)); /// assert_eq!(datetime.checked_add(2.days()), None); /// /// assert_eq!( /// datetime!(2019-11-25 15:30 +10).checked_add(27.hours()), /// Some(datetime!(2019-11-26 18:30 +10)) /// ); /// ``` #[inline] pub const fn checked_add(self, duration: Duration) -> Option { Some(const_try_opt!(self.date_time().checked_add(duration)).assume_offset(self.offset())) } /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::{datetime, offset}; /// let datetime = Date::MIN.midnight().assume_offset(offset!(+10)); /// assert_eq!(datetime.checked_sub(2.days()), None); /// /// let datetime = Date::MAX.midnight().assume_offset(offset!(+10)); /// assert_eq!(datetime.checked_sub((-2).days()), None); /// /// assert_eq!( /// datetime!(2019-11-25 15:30 +10).checked_sub(27.hours()), /// Some(datetime!(2019-11-24 12:30 +10)) /// ); /// ``` #[inline] pub const fn checked_sub(self, duration: Duration) -> Option { Some(const_try_opt!(self.date_time().checked_sub(duration)).assume_offset(self.offset())) } /// Computes `self + duration`, saturating value on overflow. /// /// ``` /// # use time::ext::NumericalDuration; /// # use time_macros::datetime; /// assert_eq!( #[cfg_attr( feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10).saturating_add((-2).days())," )] #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(-9999-01-01 0:00 +10).saturating_add((-2).days())," )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(-9999-01-01 0:00 +10)" )] /// ); /// /// assert_eq!( #[cfg_attr( feature = "large-dates", doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days())," )] #[cfg_attr( feature = "large-dates", doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)" )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_add(2.days())," )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)" )] /// ); /// /// assert_eq!( /// datetime!(2019-11-25 15:30 +10).saturating_add(27.hours()), /// datetime!(2019-11-26 18:30 +10) /// ); /// ``` #[inline] pub const fn saturating_add(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_add(duration) { datetime } else if duration.is_negative() { PrimitiveDateTime::MIN.assume_offset(self.offset()) } else { PrimitiveDateTime::MAX.assume_offset(self.offset()) } } /// Computes `self - duration`, saturating value on overflow. /// /// ``` /// # use time::ext::NumericalDuration; /// # use time_macros::datetime; /// assert_eq!( #[cfg_attr( feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10).saturating_sub(2.days())," )] #[cfg_attr(feature = "large-dates", doc = " datetime!(-999999-01-01 0:00 +10)")] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(-9999-01-01 0:00 +10).saturating_sub(2.days())," )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(-9999-01-01 0:00 +10)" )] /// ); /// /// assert_eq!( #[cfg_attr( feature = "large-dates", doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days())," )] #[cfg_attr( feature = "large-dates", doc = " datetime!(+999999-12-31 23:59:59.999_999_999 +10)" )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10).saturating_sub((-2).days())," )] #[cfg_attr( not(feature = "large-dates"), doc = " datetime!(+9999-12-31 23:59:59.999_999_999 +10)" )] /// ); /// /// assert_eq!( /// datetime!(2019-11-25 15:30 +10).saturating_sub(27.hours()), /// datetime!(2019-11-24 12:30 +10) /// ); /// ``` #[inline] pub const fn saturating_sub(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_sub(duration) { datetime } else if duration.is_negative() { PrimitiveDateTime::MAX.assume_offset(self.offset()) } else { PrimitiveDateTime::MIN.assume_offset(self.offset()) } } } /// Methods that replace part of the `OffsetDateTime`. impl OffsetDateTime { /// Replace the time, which is assumed to be in the stored offset. The date and offset /// components are unchanged. /// /// ```rust /// # use time_macros::{datetime, time}; /// assert_eq!( /// datetime!(2020-01-01 5:00 UTC).replace_time(time!(12:00)), /// datetime!(2020-01-01 12:00 UTC) /// ); /// assert_eq!( /// datetime!(2020-01-01 12:00 -5).replace_time(time!(7:00)), /// datetime!(2020-01-01 7:00 -5) /// ); /// assert_eq!( /// datetime!(2020-01-01 0:00 +1).replace_time(time!(12:00)), /// datetime!(2020-01-01 12:00 +1) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_time(self, time: Time) -> Self { Self::new_in_offset(self.date(), time, self.offset()) } /// Replace the date, which is assumed to be in the stored offset. The time and offset /// components are unchanged. /// /// ```rust /// # use time_macros::{datetime, date}; /// assert_eq!( /// datetime!(2020-01-01 12:00 UTC).replace_date(date!(2020-01-30)), /// datetime!(2020-01-30 12:00 UTC) /// ); /// assert_eq!( /// datetime!(2020-01-01 0:00 +1).replace_date(date!(2020-01-30)), /// datetime!(2020-01-30 0:00 +1) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_date(self, date: Date) -> Self { Self::new_in_offset(date, self.time(), self.offset()) } /// Replace the date and time, which are assumed to be in the stored offset. The offset /// component remains unchanged. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2020-01-01 12:00 UTC).replace_date_time(datetime!(2020-01-30 16:00)), /// datetime!(2020-01-30 16:00 UTC) /// ); /// assert_eq!( /// datetime!(2020-01-01 12:00 +1).replace_date_time(datetime!(2020-01-30 0:00)), /// datetime!(2020-01-30 0:00 +1) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_date_time(self, date_time: PrimitiveDateTime) -> Self { date_time.assume_offset(self.offset()) } /// Replace the offset. The date and time components remain unchanged. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!( /// datetime!(2020-01-01 0:00 UTC).replace_offset(offset!(-5)), /// datetime!(2020-01-01 0:00 -5) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_offset(self, offset: UtcOffset) -> Self { self.date_time().assume_offset(offset) } /// Replace the year. The month and day will be unchanged. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 12:00 +01).replace_year(2019), /// Ok(datetime!(2019-02-18 12:00 +01)) /// ); /// assert!(datetime!(2022-02-18 12:00 +01).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year /// assert!(datetime!(2022-02-18 12:00 +01).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_year(self, year: i32) -> Result { Ok(const_try!(self.date_time().replace_year(year)).assume_offset(self.offset())) } /// Replace the month of the year. /// /// ```rust /// # use time_macros::datetime; /// # use time::Month; /// assert_eq!( /// datetime!(2022-02-18 12:00 +01).replace_month(Month::January), /// Ok(datetime!(2022-01-18 12:00 +01)) /// ); /// assert!(datetime!(2022-01-30 12:00 +01).replace_month(Month::February).is_err()); // 30 isn't a valid day in February /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_month(self, month: Month) -> Result { Ok(const_try!(self.date_time().replace_month(month)).assume_offset(self.offset())) } /// Replace the day of the month. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 12:00 +01).replace_day(1), /// Ok(datetime!(2022-02-01 12:00 +01)) /// ); /// assert!(datetime!(2022-02-18 12:00 +01).replace_day(0).is_err()); // 00 isn't a valid day /// assert!(datetime!(2022-02-18 12:00 +01).replace_day(30).is_err()); // 30 isn't a valid day in February /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_day(self, day: u8) -> Result { Ok(const_try!(self.date_time().replace_day(day)).assume_offset(self.offset())) } /// Replace the day of the year. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2022-049 12:00 +01).replace_ordinal(1), Ok(datetime!(2022-001 12:00 +01))); /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal /// assert!(datetime!(2022-049 12:00 +01).replace_ordinal(366).is_err()); // 2022 isn't a leap year /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_ordinal(self, ordinal: u16) -> Result { Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset())) } /// Truncate to the start of the day, setting the time to midnight. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123 +01).truncate_to_day(), /// datetime!(2022-02-18 0:00 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_day(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_day(); self } /// Replace the clock hour. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_hour(7), /// Ok(datetime!(2022-02-18 07:02:03.004_005_006 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_hour(24).is_err()); // 24 isn't a valid hour /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_hour(self, hour: u8) -> Result { Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset())) } /// Truncate to the hour, setting the minute, second, and subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123 +01).truncate_to_hour(), /// datetime!(2022-02-18 15:00 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_hour(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_hour(); self } /// Replace the minutes within the hour. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_minute(7), /// Ok(datetime!(2022-02-18 01:07:03.004_005_006 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_minute(60).is_err()); // 60 isn't a valid minute /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_minute(self, minute: u8) -> Result { Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset())) } /// Truncate to the minute, setting the second and subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123 +01).truncate_to_minute(), /// datetime!(2022-02-18 15:30 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_minute(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_minute(); self } /// Replace the seconds within the minute. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_second(7), /// Ok(datetime!(2022-02-18 01:02:07.004_005_006 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_second(60).is_err()); // 60 isn't a valid second /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_second(self, second: u8) -> Result { Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset())) } /// Truncate to the second, setting the subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123 +01).truncate_to_second(), /// datetime!(2022-02-18 15:30:45 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_second(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_second(); self } /// Replace the milliseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_millisecond(7), /// Ok(datetime!(2022-02-18 01:02:03.007 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_millisecond( self, millisecond: u16, ) -> Result { Ok( const_try!(self.date_time().replace_millisecond(millisecond)) .assume_offset(self.offset()), ) } /// Truncate to the millisecond, setting the microsecond and nanosecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789 +01).truncate_to_millisecond(), /// datetime!(2022-02-18 15:30:45.123 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_millisecond(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_millisecond(); self } /// Replace the microseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_microsecond(7_008), /// Ok(datetime!(2022-02-18 01:02:03.007_008 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_microsecond( self, microsecond: u32, ) -> Result { Ok( const_try!(self.date_time().replace_microsecond(microsecond)) .assume_offset(self.offset()), ) } /// Truncate to the microsecond, setting the nanosecond component to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789 +01).truncate_to_microsecond(), /// datetime!(2022-02-18 15:30:45.123_456 +01) /// ); /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn truncate_to_microsecond(mut self) -> Self { self.local_date_time = self.local_date_time.truncate_to_microsecond(); self } /// Replace the nanoseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_nanosecond(7_008_009), /// Ok(datetime!(2022-02-18 01:02:03.007_008_009 +01)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006 +01).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond /// ``` #[must_use = "This method does not mutate the original `OffsetDateTime`."] #[inline] pub const fn replace_nanosecond(self, nanosecond: u32) -> Result { Ok( const_try!(self.date_time().replace_nanosecond(nanosecond)) .assume_offset(self.offset()), ) } } #[cfg(feature = "formatting")] impl OffsetDateTime { /// Format the `OffsetDateTime` using the provided [format /// description](crate::format_description). #[inline] pub fn format_into( self, output: &mut (impl io::Write + ?Sized), format: &(impl Formattable + ?Sized), ) -> Result { format.format_into(output, &self, &mut Default::default()) } /// Format the `OffsetDateTime` using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::format_description; /// # use time_macros::datetime; /// let format = format_description::parse( /// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \ /// sign:mandatory]:[offset_minute]:[offset_second]", /// )?; /// assert_eq!( /// datetime!(2020-01-02 03:04:05 +06:07:08).format(&format)?, /// "2020-01-02 03:04:05 +06:07:08" /// ); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { format.format(&self, &mut Default::default()) } } #[cfg(feature = "parsing")] impl OffsetDateTime { /// Parse an `OffsetDateTime` from the input using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::OffsetDateTime; /// # use time_macros::{datetime, format_description}; /// let format = format_description!( /// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \ /// sign:mandatory]:[offset_minute]:[offset_second]" /// ); /// assert_eq!( /// OffsetDateTime::parse("2020-01-02 03:04:05 +06:07:08", &format)?, /// datetime!(2020-01-02 03:04:05 +06:07:08) /// ); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn parse( input: &str, description: &(impl Parsable + ?Sized), ) -> Result { description.parse_offset_date_time(input.as_bytes()) } /// A helper method to check if the `OffsetDateTime` is a valid representation of a leap second. /// Leap seconds, when parsed, are represented as the preceding nanosecond. However, leap /// seconds can only occur as the last second of a month UTC. #[cfg(feature = "parsing")] #[inline] pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool { // This comparison doesn't need to be adjusted for the stored offset, so check it first for // speed. if self.nanosecond() != 999_999_999 { return false; } let (year, ordinal, time) = self.to_utc_raw(); let Ok(date) = Date::from_ordinal_date(year, ordinal) else { return false; }; time.hour() == 23 && time.minute() == 59 && time.second() == 59 && date.day() == date.month().length(year) } } impl SmartDisplay for OffsetDateTime { type Metadata = (); #[inline] fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { let width = smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset()); Metadata::new(width, self, ()) } #[inline] fn fmt_with_metadata( &self, f: &mut fmt::Formatter<'_>, metadata: Metadata, ) -> fmt::Result { f.pad_with_width( metadata.unpadded_width(), format_args!("{} {} {}", self.date(), self.time(), self.offset()), ) } } impl fmt::Display for OffsetDateTime { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { SmartDisplay::fmt(self, f) } } impl fmt::Debug for OffsetDateTime { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl Add for OffsetDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: Duration) -> Self::Output { self.checked_add(duration) .expect("resulting value is out of range") } } impl Add for OffsetDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: StdDuration) -> Self::Output { let (is_next_day, time) = self.time().adjusting_add_std(duration); Self::new_in_offset( if is_next_day { (self.date() + duration) .next_day() .expect("resulting value is out of range") } else { self.date() + duration }, time, self.offset, ) } } impl AddAssign for OffsetDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: Duration) { *self = *self + rhs; } } impl AddAssign for OffsetDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, rhs: StdDuration) { *self = *self + rhs; } } impl Sub for OffsetDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: Duration) -> Self::Output { self.checked_sub(rhs) .expect("resulting value is out of range") } } impl Sub for OffsetDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, duration: StdDuration) -> Self::Output { let (is_previous_day, time) = self.time().adjusting_sub_std(duration); Self::new_in_offset( if is_previous_day { (self.date() - duration) .previous_day() .expect("resulting value is out of range") } else { self.date() - duration }, time, self.offset, ) } } impl SubAssign for OffsetDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: Duration) { *self = *self - rhs; } } impl SubAssign for OffsetDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, rhs: StdDuration) { *self = *self - rhs; } } impl Sub for OffsetDateTime { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: Self) -> Self::Output { let base = self.date_time() - rhs.date_time(); let adjustment = Duration::seconds( (self.offset.whole_seconds() - rhs.offset.whole_seconds()).extend::(), ); base - adjustment } } time-0.3.47/src/parsing/combinator/mod.rs000064400000000000000000000314651046102023000164020ustar 00000000000000//! 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> { 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

(parser: P) -> impl for<'a> FnMut(&'a [u8]) -> ParsedItem<'a, ()> where P: for<'a> Fn(&'a [u8]) -> Option>, { 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

(parser: P) -> impl for<'a> Fn(&'a [u8]) -> Option> where P: for<'a> Fn(&'a [u8]) -> Option>, { 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( mut input: &[u8], ) -> Option> 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> { 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; impl ExactlyNDigits<1> { /// Consume exactly one digit. #[inline] pub(crate) const fn parse(input: &[u8]) -> Option> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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( padding: Padding, ) -> impl for<'a> Fn(&'a [u8]) -> Option> where T: Integer, { n_to_m_digits_padded::(padding) } /// Consume between `n` and `m` digits, returning the numerical value. pub(crate) fn n_to_m_digits_padded( padding: Padding, ) -> impl for<'a> Fn(&'a [u8]) -> Option> 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::(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::(input), } } /// Consume exactly one digit. #[inline] pub(crate) const fn any_digit(input: &[u8]) -> Option> { 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(input: &[u8]) -> Option> { 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(input: &[u8]) -> Option> { 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( parser: impl for<'a> Fn(&'a [u8]) -> Option>, ) -> impl for<'a> Fn(&'a [u8]) -> ParsedItem<'a, Option> { move |input| match parser(input) { Some(value) => value.map(Some), None => ParsedItem(input, None), } } time-0.3.47/src/parsing/combinator/rfc/iso8601.rs000064400000000000000000000151461046102023000175040ustar 00000000000000//! 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> { 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> { 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>> { 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>> { 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> { 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>> { ExactlyNDigits::<3>::parse(input).and_then(|parsed| parsed.flat_map(NonZero::new)) } /// Parse the hour. #[inline] pub(crate) const fn hour(input: &[u8]) -> Option> { ExactlyNDigits::<2>::parse(input) } /// Parse the minute. #[inline] pub(crate) const fn min(input: &[u8]) -> Option> { 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)>> { // 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> { match input { [b'.' | b',', remaining @ ..] => Some(ParsedItem(remaining, ())), _ => None, } } time-0.3.47/src/parsing/combinator/rfc/mod.rs000064400000000000000000000004731046102023000171470ustar 00000000000000//! 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; time-0.3.47/src/parsing/combinator/rfc/rfc2234.rs000064400000000000000000000005511046102023000174520ustar 00000000000000//! 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> { match input { [b' ' | b'\t', rest @ ..] => Some(ParsedItem(rest, ())), _ => None, } } time-0.3.47/src/parsing/combinator/rfc/rfc2822.rs000064400000000000000000000153311046102023000174570ustar 00000000000000//! 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> { 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> { 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> { // 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::(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::(input)?.into_inner(); Some(ParsedItem(input, ())) } /// Consume the `ccontent` rule. #[inline] fn ccontent(input: &[u8], depth: u8) -> Option> { 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> { 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> { input = ascii_char::(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> { 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::)(input).into_inner(); input = zero_or_more(ascii_char::)(input).into_inner(); while let Some(rest) = obs_char(input) { input = rest; input = zero_or_more(ascii_char::)(input).into_inner(); input = zero_or_more(ascii_char::)(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> { 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::().truncate(); Some(ParsedItem(rest, zone)) } time-0.3.47/src/parsing/component.rs000064400000000000000000000444011046102023000154620ustar 00000000000000//! 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> { 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> { 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> { 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> { 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::(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::(digit) }; Some(ParsedItem(rest, weekday)) } } } /// Parse the "ordinal" component of a `Date`. #[inline] pub(crate) fn parse_ordinal( input: &[u8], modifiers: modifier::Ordinal, ) -> Option>> { 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>> { 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> { 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> { 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> { 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> { 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> { use modifier::SubsecondDigits::*; Some(match modifiers.digits { One => ExactlyNDigits::<1>::parse(input)?.map(|v| v.extend::() * 100_000_000), Two => ExactlyNDigits::<2>::parse(input)?.map(|v| v.extend::() * 10_000_000), Three => ExactlyNDigits::<3>::parse(input)?.map(|v| v.extend::() * 1_000_000), Four => ExactlyNDigits::<4>::parse(input)?.map(|v| v.extend::() * 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::() * 100_000_000); let mut multiplier = 10_000_000; while let Some(ParsedItem(new_input, digit)) = any_digit(input) { value += (digit - b'0').extend::() * 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> { 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> { 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> { 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> { 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> { 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::(Second)) } modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)? .map(|val| val * Nanosecond::per_t::(Millisecond)), modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)? .map(|val| val * Nanosecond::per_t::(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> { let modifier::End { trailing_input } = end; if trailing_input == modifier::TrailingInput::Discard || input.is_empty() { Some(ParsedItem(b"", ())) } else { None } } time-0.3.47/src/parsing/iso8601.rs000064400000000000000000000356741046102023000146050ustar 00000000000000//! 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 Iso8601 { // 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::(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::(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::(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::(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::(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::(Minute)) as u8) .ok_or(InvalidComponent("minute")) ) .with_second( (fractional_part * Second::per_t::(Hour) % Minute::per_t::(Hour)) as u8, ) .ok_or(InvalidComponent("second")) ) .with_subsecond( (fractional_part * Nanosecond::per_t::(Hour) % Nanosecond::per_t::(Second)) as u32, ) .ok_or(InvalidComponent("subsecond")) ); return Ok(input); } }; if let Some(ParsedItem(new_input, ())) = ascii_char::(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::(Minute)) as u8) .ok_or(InvalidComponent("second")) ) .with_subsecond( (fractional_part * Nanosecond::per_t::(Minute) % Nanosecond::per_t::(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::(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::(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::(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::(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. } } } time-0.3.47/src/parsing/mod.rs000064400000000000000000000042611046102023000142370ustar 00000000000000//! 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(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(self, f: impl FnOnce(T) -> Option) -> Option> { 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 { 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 } } time-0.3.47/src/parsing/parsable.rs000064400000000000000000000741501046102023000152550ustar 00000000000000//! 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 Parsable for Iso8601 {} impl Parsable for T where T: Deref {} /// 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 { 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 { Ok(self.parse(input)?.try_into()?) } /// Parse a [`Time`] from the format description. #[inline] fn parse_time(&self, input: &[u8]) -> Result { Ok(self.parse(input)?.try_into()?) } /// Parse a [`UtcOffset`] from the format description. #[inline] fn parse_offset(&self, input: &[u8]) -> Result { Ok(self.parse(input)?.try_into()?) } /// Parse a [`PrimitiveDateTime`] from the format description. #[inline] fn parse_primitive_date_time( &self, input: &[u8], ) -> Result { Ok(self.parse(input)?.try_into()?) } /// Parse a [`UtcDateTime`] from the format description. #[inline] fn parse_utc_date_time(&self, input: &[u8]) -> Result { Ok(self.parse(input)?.try_into()?) } /// Parse a [`OffsetDateTime`] from the format description. #[inline] fn parse_offset_date_time(&self, input: &[u8]) -> Result { 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 sealed::Sealed for T where T: Deref, { #[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::; let comma = ascii_char::; 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::()) .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 { use crate::parsing::combinator::rfc::rfc2822::{cfws, fws, zone_literal}; let colon = ascii_char::; let comma = ascii_char::; 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::()) .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::; let colon = ascii_char::; 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::(input) { let ParsedItem(mut input, mut value) = try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond"))) .map(|v| (v - b'0').extend::() * 100_000_000); let mut multiplier = 10_000_000; while let Some(ParsedItem(new_input, digit)) = any_digit(input) { value += (digit - b'0').extend::() * 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::(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 { let dash = ascii_char::; let colon = ascii_char::; 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::(input) { let ParsedItem(mut input, mut value) = try_likely_ok!(any_digit(input).ok_or(InvalidComponent("subsecond"))) .map(|v| (v - b'0').extend::() * 100_000_000); let mut multiplier = 10_000_000; while let Some(ParsedItem(new_input, digit)) = any_digit(input) { value += (digit - b'0').extend::() * 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::(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 sealed::Sealed for Iso8601 { #[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) } } time-0.3.47/src/parsing/parsed.rs000064400000000000000000001310671046102023000147430ustar 00000000000000//! Information parsed from an input and format description. use core::num::NonZero; use deranged::{ OptionRangedI8, OptionRangedI16, OptionRangedI32, OptionRangedI128, OptionRangedU8, OptionRangedU16, OptionRangedU32, RangedI8, RangedI16, RangedI32, RangedI128, RangedU8, RangedU16, RangedU32, }; use num_conv::prelude::*; use crate::convert::{Day, Hour, Minute, Nanosecond, Second}; use crate::date::{MAX_YEAR, MIN_YEAR}; use crate::error::TryFromParsed::InsufficientInformation; #[cfg(feature = "alloc")] use crate::format_description::OwnedFormatItem; use crate::format_description::{BorrowedFormatItem, Component, Period, modifier}; use crate::internal_macros::{bug, const_try_opt}; use crate::parsing::ParsedItem; use crate::parsing::component::{ parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour, parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second, parse_subsecond, parse_unix_timestamp, parse_week_number, parse_weekday, parse_year, }; use crate::{ Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, error, }; /// Sealed to prevent downstream implementations. mod sealed { use super::*; /// A trait to allow `parse_item` to be generic. pub trait AnyFormatItem { /// Parse a single item, returning the remaining input on success. fn parse_item<'a>( &self, parsed: &mut Parsed, input: &'a [u8], ) -> Result<&'a [u8], error::ParseFromDescription>; } } impl sealed::AnyFormatItem for BorrowedFormatItem<'_> { #[inline] fn parse_item<'a>( &self, parsed: &mut Parsed, input: &'a [u8], ) -> Result<&'a [u8], error::ParseFromDescription> { match self { Self::Literal(literal) => Parsed::parse_literal(input, literal), Self::Component(component) => parsed.parse_component(input, *component), Self::Compound(compound) => parsed.parse_items(input, compound), Self::Optional(item) => parsed.parse_item(input, *item).or(Ok(input)), Self::First(items) => { let mut first_err = None; for item in items.iter() { match parsed.parse_item(input, item) { Ok(remaining_input) => return Ok(remaining_input), Err(err) if first_err.is_none() => first_err = Some(err), Err(_) => {} } } match first_err { Some(err) => Err(err), // This location will be reached if the slice is empty, skipping the `for` loop. // As this case is expected to be uncommon, there's no need to check up front. None => Ok(input), } } } } } #[cfg(feature = "alloc")] impl sealed::AnyFormatItem for OwnedFormatItem { #[inline] fn parse_item<'a>( &self, parsed: &mut Parsed, input: &'a [u8], ) -> Result<&'a [u8], error::ParseFromDescription> { match self { Self::Literal(literal) => Parsed::parse_literal(input, literal), Self::Component(component) => parsed.parse_component(input, *component), Self::Compound(compound) => parsed.parse_items(input, compound), Self::Optional(item) => parsed.parse_item(input, item.as_ref()).or(Ok(input)), Self::First(items) => { let mut first_err = None; for item in items.iter() { match parsed.parse_item(input, item) { Ok(remaining_input) => return Ok(remaining_input), Err(err) if first_err.is_none() => first_err = Some(err), Err(_) => {} } } match first_err { Some(err) => Err(err), // This location will be reached if the slice is empty, skipping the `for` loop. // As this case is expected to be uncommon, there's no need to check up front. None => Ok(input), } } } } } /// All information parsed. /// /// This information is directly used to construct the final values. /// /// Most users will not need think about this struct in any way. It is public to allow for manual /// control over values, in the instance that the default parser is insufficient. #[derive(Debug, Clone, Copy)] pub struct Parsed { /// Calendar year. year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, /// All digits except the last two of the calendar year. year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>, /// The last two digits of the calendar year. year_last_two: OptionRangedU8<0, 99>, /// Year of the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date). iso_year: OptionRangedI32<{ MIN_YEAR }, { MAX_YEAR }>, /// All digits except the last two of the ISO week year. iso_year_century: OptionRangedI16<{ (MIN_YEAR / 100) as i16 }, { (MAX_YEAR / 100) as i16 }>, /// The last two digits of the ISO week year. iso_year_last_two: OptionRangedU8<0, 99>, /// Month of the year. month: Option, /// Week of the year, where week one begins on the first Sunday of the calendar year. sunday_week_number: OptionRangedU8<0, 53>, /// Week of the year, where week one begins on the first Monday of the calendar year. monday_week_number: OptionRangedU8<0, 53>, /// Week of the year, where week one is the Monday-to-Sunday period containing January 4. iso_week_number: OptionRangedU8<1, 53>, /// Day of the week. weekday: Option, /// Day of the year. ordinal: OptionRangedU16<1, 366>, /// Day of the month. day: OptionRangedU8<1, 31>, /// Hour within the day. hour_24: OptionRangedU8<0, { Hour::per_t::(Day) - 1 }>, /// Hour within the 12-hour period (midnight to noon or vice versa). This is typically used in /// conjunction with AM/PM, which is indicated by the `hour_12_is_pm` field. hour_12: OptionRangedU8<1, 12>, /// Whether the `hour_12` field indicates a time that "PM". hour_12_is_pm: Option, /// Minute within the hour. minute: OptionRangedU8<0, { Minute::per_t::(Hour) - 1 }>, /// Second within the minute. // do not subtract one, as leap seconds may be allowed second: OptionRangedU8<0, { Second::per_t::(Minute) }>, /// Nanosecond within the second. subsecond: OptionRangedU32<0, { Nanosecond::per_t::(Second) - 1 }>, /// Whole hours of the UTC offset. offset_hour: OptionRangedI8<-23, 23>, /// Minutes within the hour of the UTC offset. offset_minute: OptionRangedI8<{ -Minute::per_t::(Hour) + 1 }, { Minute::per_t::(Hour) - 1 }>, /// Seconds within the minute of the UTC offset. offset_second: OptionRangedI8<{ -Second::per_t::(Minute) + 1 }, { Second::per_t::(Minute) - 1 }>, /// The Unix timestamp in nanoseconds. unix_timestamp_nanos: OptionRangedI128< { OffsetDateTime::new_in_offset(Date::MIN, Time::MIDNIGHT, UtcOffset::UTC) .unix_timestamp_nanos() }, { OffsetDateTime::new_in_offset(Date::MAX, Time::MAX, UtcOffset::UTC) .unix_timestamp_nanos() }, >, /// Indicates whether the [`UtcOffset`] is negative. This information is obtained when parsing /// the offset hour, but may not otherwise be stored due to "-0" being equivalent to "0". offset_is_negative: bool, /// Indicates whether the `year_century` component is negative. This information is obtained /// when parsing, but may not otherwise be stored due to "-0" being equivalent to "0". year_century_is_negative: bool, /// Indicates whether the `iso_year_century` component is negative. This information is /// obtained when parsing, but may not otherwise be stored due to "-0" being equivalent to "0". iso_year_century_is_negative: bool, /// Indicates whether a leap second is permitted to be parsed. This is required by some /// well-known formats. pub(super) leap_second_allowed: bool, } impl Default for Parsed { #[inline] fn default() -> Self { Self::new() } } impl Parsed { /// Create a new instance of `Parsed` with no information known. #[inline] pub const fn new() -> Self { Self { year: OptionRangedI32::None, year_century: OptionRangedI16::None, year_last_two: OptionRangedU8::None, iso_year: OptionRangedI32::None, iso_year_century: OptionRangedI16::None, iso_year_last_two: OptionRangedU8::None, month: None, sunday_week_number: OptionRangedU8::None, monday_week_number: OptionRangedU8::None, iso_week_number: OptionRangedU8::None, weekday: None, ordinal: OptionRangedU16::None, day: OptionRangedU8::None, hour_24: OptionRangedU8::None, hour_12: OptionRangedU8::None, hour_12_is_pm: None, minute: OptionRangedU8::None, second: OptionRangedU8::None, subsecond: OptionRangedU32::None, offset_hour: OptionRangedI8::None, offset_minute: OptionRangedI8::None, offset_second: OptionRangedI8::None, unix_timestamp_nanos: OptionRangedI128::None, offset_is_negative: false, year_century_is_negative: false, iso_year_century_is_negative: false, leap_second_allowed: false, } } /// Parse a single [`BorrowedFormatItem`] or [`OwnedFormatItem`], mutating the struct. The /// remaining input is returned as the `Ok` value. /// /// If a [`BorrowedFormatItem::Optional`] or [`OwnedFormatItem::Optional`] is passed, parsing /// will not fail; the input will be returned as-is if the expected format is not present. #[inline] pub fn parse_item<'a>( &mut self, input: &'a [u8], item: &impl sealed::AnyFormatItem, ) -> Result<&'a [u8], error::ParseFromDescription> { item.parse_item(self, input) } /// Parse a sequence of [`BorrowedFormatItem`]s or [`OwnedFormatItem`]s, mutating the struct. /// The remaining input is returned as the `Ok` value. /// /// This method will fail if any of the contained [`BorrowedFormatItem`]s or /// [`OwnedFormatItem`]s fail to parse. `self` will not be mutated in this instance. #[inline] pub fn parse_items<'a>( &mut self, mut input: &'a [u8], items: &[impl sealed::AnyFormatItem], ) -> Result<&'a [u8], error::ParseFromDescription> { // Make a copy that we can mutate. It will only be set to the user's copy if everything // succeeds. let mut this = *self; for item in items { input = this.parse_item(input, item)?; } *self = this; Ok(input) } /// Parse a literal byte sequence. The remaining input is returned as the `Ok` value. #[inline] pub fn parse_literal<'a>( input: &'a [u8], literal: &[u8], ) -> Result<&'a [u8], error::ParseFromDescription> { input .strip_prefix(literal) .ok_or(error::ParseFromDescription::InvalidLiteral) } /// Parse a single component, mutating the struct. The remaining input is returned as the `Ok` /// value. pub fn parse_component<'a>( &mut self, input: &'a [u8], component: Component, ) -> Result<&'a [u8], error::ParseFromDescription> { use error::ParseFromDescription::InvalidComponent; match component { Component::Day(modifiers) => parse_day(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_day(value))) .ok_or(InvalidComponent("day")), Component::Month(modifiers) => parse_month(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_month(value))) .ok_or(InvalidComponent("month")), Component::Ordinal(modifiers) => parse_ordinal(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_ordinal(value))) .ok_or(InvalidComponent("ordinal")), Component::Weekday(modifiers) => parse_weekday(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_weekday(value))) .ok_or(InvalidComponent("weekday")), Component::WeekNumber(modifiers) => { let ParsedItem(remaining, value) = parse_week_number(input, modifiers).ok_or(InvalidComponent("week number"))?; match modifiers.repr { modifier::WeekNumberRepr::Iso => { NonZero::new(value).and_then(|value| self.set_iso_week_number(value)) } modifier::WeekNumberRepr::Sunday => self.set_sunday_week_number(value), modifier::WeekNumberRepr::Monday => self.set_monday_week_number(value), } .ok_or(InvalidComponent("week number"))?; Ok(remaining) } Component::Year(modifiers) => { let ParsedItem(remaining, (value, is_negative)) = parse_year(input, modifiers).ok_or(InvalidComponent("year"))?; match (modifiers.iso_week_based, modifiers.repr) { (false, modifier::YearRepr::Full) => self.set_year(value), (false, modifier::YearRepr::Century) => { self.set_year_century(value.truncate(), is_negative) } (false, modifier::YearRepr::LastTwo) => { self.set_year_last_two(value.cast_unsigned().truncate()) } (true, modifier::YearRepr::Full) => self.set_iso_year(value), (true, modifier::YearRepr::Century) => { self.set_iso_year_century(value.truncate(), is_negative) } (true, modifier::YearRepr::LastTwo) => { self.set_iso_year_last_two(value.cast_unsigned().truncate()) } } .ok_or(InvalidComponent("year"))?; Ok(remaining) } Component::Hour(modifiers) => { let ParsedItem(remaining, value) = parse_hour(input, modifiers).ok_or(InvalidComponent("hour"))?; if modifiers.is_12_hour_clock { NonZero::new(value).and_then(|value| self.set_hour_12(value)) } else { self.set_hour_24(value) } .ok_or(InvalidComponent("hour"))?; Ok(remaining) } Component::Minute(modifiers) => parse_minute(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value))) .ok_or(InvalidComponent("minute")), Component::Period(modifiers) => parse_period(input, modifiers) .and_then(|parsed| { parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm)) }) .ok_or(InvalidComponent("period")), Component::Second(modifiers) => parse_second(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_second(value))) .ok_or(InvalidComponent("second")), Component::Subsecond(modifiers) => parse_subsecond(input, modifiers) .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value))) .ok_or(InvalidComponent("subsecond")), Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers) .and_then(|parsed| { parsed.consume_value(|(value, is_negative)| { self.set_offset_hour(value)?; self.offset_is_negative = is_negative; Some(()) }) }) .ok_or(InvalidComponent("offset hour")), Component::OffsetMinute(modifiers) => parse_offset_minute(input, modifiers) .and_then(|parsed| { parsed.consume_value(|value| self.set_offset_minute_signed(value)) }) .ok_or(InvalidComponent("offset minute")), Component::OffsetSecond(modifiers) => parse_offset_second(input, modifiers) .and_then(|parsed| { parsed.consume_value(|value| self.set_offset_second_signed(value)) }) .ok_or(InvalidComponent("offset second")), Component::Ignore(modifiers) => parse_ignore(input, modifiers) .map(ParsedItem::<()>::into_inner) .ok_or(InvalidComponent("ignore")), Component::UnixTimestamp(modifiers) => parse_unix_timestamp(input, modifiers) .and_then(|parsed| { parsed.consume_value(|value| self.set_unix_timestamp_nanos(value)) }) .ok_or(InvalidComponent("unix_timestamp")), Component::End(modifiers) => parse_end(input, modifiers) .map(ParsedItem::<()>::into_inner) .ok_or(error::ParseFromDescription::UnexpectedTrailingCharacters), } } } /// Getter methods impl Parsed { /// Obtain the `year` component. #[inline] pub const fn year(&self) -> Option { self.year.get_primitive() } /// Obtain the `year_century` component. /// /// If the year is zero, the sign of the century is not stored. To differentiate between /// positive and negative zero, use `year_century_is_negative`. #[inline] pub const fn year_century(&self) -> Option { self.year_century.get_primitive() } /// Obtain the `year_century_is_negative` component. /// /// This indicates whether the value returned from `year_century` is negative. If the year is /// zero, it is necessary to call this method for disambiguation. #[inline] pub const fn year_century_is_negative(&self) -> Option { match self.year_century() { Some(_) => Some(self.year_century_is_negative), None => None, } } /// Obtain the `year_last_two` component. #[inline] pub const fn year_last_two(&self) -> Option { self.year_last_two.get_primitive() } /// Obtain the `iso_year` component. #[inline] pub const fn iso_year(&self) -> Option { self.iso_year.get_primitive() } /// Obtain the `iso_year_century` component. /// /// If the year is zero, the sign of the century is not stored. To differentiate between /// positive and negative zero, use `iso_year_century_is_negative`. #[inline] pub const fn iso_year_century(&self) -> Option { self.iso_year_century.get_primitive() } /// Obtain the `iso_year_century_is_negative` component. /// /// This indicates whether the value returned from `iso_year_century` is negative. If the year /// is zero, it is necessary to call this method for disambiguation. #[inline] pub const fn iso_year_century_is_negative(&self) -> Option { match self.iso_year_century() { Some(_) => Some(self.iso_year_century_is_negative), None => None, } } /// Obtain the `iso_year_last_two` component. #[inline] pub const fn iso_year_last_two(&self) -> Option { self.iso_year_last_two.get_primitive() } /// Obtain the `month` component. #[inline] pub const fn month(&self) -> Option { self.month } /// Obtain the `sunday_week_number` component. #[inline] pub const fn sunday_week_number(&self) -> Option { self.sunday_week_number.get_primitive() } /// Obtain the `monday_week_number` component. #[inline] pub const fn monday_week_number(&self) -> Option { self.monday_week_number.get_primitive() } /// Obtain the `iso_week_number` component. #[inline] pub const fn iso_week_number(&self) -> Option> { NonZero::new(const_try_opt!(self.iso_week_number.get_primitive())) } /// Obtain the `weekday` component. #[inline] pub const fn weekday(&self) -> Option { self.weekday } /// Obtain the `ordinal` component. #[inline] pub const fn ordinal(&self) -> Option> { NonZero::new(const_try_opt!(self.ordinal.get_primitive())) } /// Obtain the `day` component. #[inline] pub const fn day(&self) -> Option> { NonZero::new(const_try_opt!(self.day.get_primitive())) } /// Obtain the `hour_24` component. #[inline] pub const fn hour_24(&self) -> Option { self.hour_24.get_primitive() } /// Obtain the `hour_12` component. #[inline] pub const fn hour_12(&self) -> Option> { NonZero::new(const_try_opt!(self.hour_12.get_primitive())) } /// Obtain the `hour_12_is_pm` component. #[inline] pub const fn hour_12_is_pm(&self) -> Option { self.hour_12_is_pm } /// Obtain the `minute` component. #[inline] pub const fn minute(&self) -> Option { self.minute.get_primitive() } /// Obtain the `second` component. #[inline] pub const fn second(&self) -> Option { self.second.get_primitive() } /// Obtain the `subsecond` component. #[inline] pub const fn subsecond(&self) -> Option { self.subsecond.get_primitive() } /// Obtain the `offset_hour` component. #[inline] pub const fn offset_hour(&self) -> Option { self.offset_hour.get_primitive() } /// Obtain the absolute value of the `offset_minute` component. #[doc(hidden)] #[deprecated(since = "0.3.8", note = "use `parsed.offset_minute_signed()` instead")] #[inline] pub const fn offset_minute(&self) -> Option { Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs()) } /// Obtain the `offset_minute` component. #[inline] pub const fn offset_minute_signed(&self) -> Option { match (self.offset_minute.get_primitive(), self.offset_is_negative) { (Some(offset_minute), true) => Some(-offset_minute), (Some(offset_minute), _) => Some(offset_minute), (None, _) => None, } } /// Obtain the absolute value of the `offset_second` component. #[doc(hidden)] #[deprecated(since = "0.3.8", note = "use `parsed.offset_second_signed()` instead")] #[inline] pub const fn offset_second(&self) -> Option { Some(const_try_opt!(self.offset_second_signed()).unsigned_abs()) } /// Obtain the `offset_second` component. #[inline] pub const fn offset_second_signed(&self) -> Option { match (self.offset_second.get_primitive(), self.offset_is_negative) { (Some(offset_second), true) => Some(-offset_second), (Some(offset_second), _) => Some(offset_second), (None, _) => None, } } /// Obtain the `unix_timestamp_nanos` component. #[inline] pub const fn unix_timestamp_nanos(&self) -> Option { self.unix_timestamp_nanos.get_primitive() } } /// Generate setters based on the builders. macro_rules! setters { ($($name:ident $setter:ident $builder:ident $type:ty;)*) => {$( #[doc = concat!("Set the `", stringify!($name), "` component.")] #[inline] pub const fn $setter(&mut self, value: $type) -> Option<()> { match self.$builder(value) { Some(value) => { *self = value; Some(()) }, None => None, } } )*}; } /// Setter methods /// /// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The /// setters _may_ fail if the value is invalid, though behavior is not guaranteed. impl Parsed { setters! { year set_year with_year i32; } /// Set the `year_century` component. /// /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise /// the sign is inferred from the value. #[inline] pub const fn set_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> { self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); if value != 0 { self.year_century_is_negative = value.is_negative(); } else { self.year_century_is_negative = is_negative; } Some(()) } setters! { year_last_two set_year_last_two with_year_last_two u8; iso_year set_iso_year with_iso_year i32; iso_year_last_two set_iso_year_last_two with_iso_year_last_two u8; } /// Set the `iso_year_century` component. /// /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise /// the sign is inferred from the value. #[inline] pub const fn set_iso_year_century(&mut self, value: i16, is_negative: bool) -> Option<()> { self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); if value != 0 { self.iso_year_century_is_negative = value.is_negative(); } else { self.iso_year_century_is_negative = is_negative; } Some(()) } setters! { month set_month with_month Month; sunday_week_number set_sunday_week_number with_sunday_week_number u8; monday_week_number set_monday_week_number with_monday_week_number u8; iso_week_number set_iso_week_number with_iso_week_number NonZero; weekday set_weekday with_weekday Weekday; ordinal set_ordinal with_ordinal NonZero; day set_day with_day NonZero; hour_24 set_hour_24 with_hour_24 u8; hour_12 set_hour_12 with_hour_12 NonZero; hour_12_is_pm set_hour_12_is_pm with_hour_12_is_pm bool; minute set_minute with_minute u8; second set_second with_second u8; subsecond set_subsecond with_subsecond u32; offset_hour set_offset_hour with_offset_hour i8; offset_minute set_offset_minute_signed with_offset_minute_signed i8; offset_second set_offset_second_signed with_offset_second_signed i8; unix_timestamp_nanos set_unix_timestamp_nanos with_unix_timestamp_nanos i128; } /// Set the `offset_minute` component. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.set_offset_minute_signed()` instead" )] #[inline] pub const fn set_offset_minute(&mut self, value: u8) -> Option<()> { if value > i8::MAX.cast_unsigned() { None } else { self.set_offset_minute_signed(value.cast_signed()) } } /// Set the `offset_minute` component. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.set_offset_second_signed()` instead" )] #[inline] pub const fn set_offset_second(&mut self, value: u8) -> Option<()> { if value > i8::MAX.cast_unsigned() { None } else { self.set_offset_second_signed(value.cast_signed()) } } } /// Builder methods /// /// All builder methods return `Option`, which is `Some` if the value was set, and `None` if /// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed. impl Parsed { /// Set the `year` component and return `self`. #[inline] pub const fn with_year(mut self, value: i32) -> Option { self.year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); Some(self) } /// Set the `year_century` component and return `self`. /// /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise /// the sign is inferred from the value. #[inline] pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option { self.year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); if value != 0 { self.year_century_is_negative = value.is_negative(); } else { self.year_century_is_negative = is_negative; } Some(self) } /// Set the `year_last_two` component and return `self`. #[inline] pub const fn with_year_last_two(mut self, value: u8) -> Option { self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `iso_year` component and return `self`. #[inline] pub const fn with_iso_year(mut self, value: i32) -> Option { self.iso_year = OptionRangedI32::Some(const_try_opt!(RangedI32::new(value))); Some(self) } /// Set the `iso_year_century` component and return `self`. /// /// If the value is zero, the sign of the century is taken from the second parameter. Otherwise /// the sign is inferred from the value. #[inline] pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option { self.iso_year_century = OptionRangedI16::Some(const_try_opt!(RangedI16::new(value))); if value != 0 { self.iso_year_century_is_negative = value.is_negative(); } else { self.iso_year_century_is_negative = is_negative; } Some(self) } /// Set the `iso_year_last_two` component and return `self`. #[inline] pub const fn with_iso_year_last_two(mut self, value: u8) -> Option { self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `month` component and return `self`. #[inline] pub const fn with_month(mut self, value: Month) -> Option { self.month = Some(value); Some(self) } /// Set the `sunday_week_number` component and return `self`. #[inline] pub const fn with_sunday_week_number(mut self, value: u8) -> Option { self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `monday_week_number` component and return `self`. #[inline] pub const fn with_monday_week_number(mut self, value: u8) -> Option { self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `iso_week_number` component and return `self`. #[inline] pub const fn with_iso_week_number(mut self, value: NonZero) -> Option { self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); Some(self) } /// Set the `weekday` component and return `self`. #[inline] pub const fn with_weekday(mut self, value: Weekday) -> Option { self.weekday = Some(value); Some(self) } /// Set the `ordinal` component and return `self`. #[inline] pub const fn with_ordinal(mut self, value: NonZero) -> Option { self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get()))); Some(self) } /// Set the `day` component and return `self`. #[inline] pub const fn with_day(mut self, value: NonZero) -> Option { self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); Some(self) } /// Set the `hour_24` component and return `self`. #[inline] pub const fn with_hour_24(mut self, value: u8) -> Option { self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `hour_12` component and return `self`. #[inline] pub const fn with_hour_12(mut self, value: NonZero) -> Option { self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); Some(self) } /// Set the `hour_12_is_pm` component and return `self`. #[inline] pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option { self.hour_12_is_pm = Some(value); Some(self) } /// Set the `minute` component and return `self`. #[inline] pub const fn with_minute(mut self, value: u8) -> Option { self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `second` component and return `self`. #[inline] pub const fn with_second(mut self, value: u8) -> Option { self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); Some(self) } /// Set the `subsecond` component and return `self`. #[inline] pub const fn with_subsecond(mut self, value: u32) -> Option { self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value))); Some(self) } /// Set the `offset_hour` component and return `self`. #[inline] pub const fn with_offset_hour(mut self, value: i8) -> Option { self.offset_hour = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); Some(self) } /// Set the `offset_minute` component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.with_offset_minute_signed()` instead" )] #[inline] pub const fn with_offset_minute(self, value: u8) -> Option { if value > i8::MAX.cast_unsigned() { None } else { self.with_offset_minute_signed(value.cast_signed()) } } /// Set the `offset_minute` component and return `self`. #[inline] pub const fn with_offset_minute_signed(mut self, value: i8) -> Option { self.offset_minute = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); Some(self) } /// Set the `offset_minute` component and return `self`. #[doc(hidden)] #[deprecated( since = "0.3.8", note = "use `parsed.with_offset_second_signed()` instead" )] #[inline] pub const fn with_offset_second(self, value: u8) -> Option { if value > i8::MAX.cast_unsigned() { None } else { self.with_offset_second_signed(value.cast_signed()) } } /// Set the `offset_second` component and return `self`. #[inline] pub const fn with_offset_second_signed(mut self, value: i8) -> Option { self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); Some(self) } /// Set the `unix_timestamp_nanos` component and return `self`. #[inline] pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option { self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value))); Some(self) } } impl TryFrom for Date { type Error = error::TryFromParsed; #[inline] fn try_from(mut parsed: Parsed) -> Result { /// Match on the components that need to be present. macro_rules! match_ { (_ => $catch_all:expr $(,)?) => { $catch_all }; (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => { if let ($(Some($name)),*) = ($(parsed.$name()),*) { $arm } else { match_!($($rest)*) } }; } /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week /// numbering. #[inline] const fn adjustment(year: i32) -> i16 { // Safety: `ordinal` is not zero. match unsafe { Date::__from_ordinal_date_unchecked(year, 1) }.weekday() { Weekday::Monday => 7, Weekday::Tuesday => 1, Weekday::Wednesday => 2, Weekday::Thursday => 3, Weekday::Friday => 4, Weekday::Saturday => 5, Weekday::Sunday => 6, } } // If we do not have the year but we have *both* the century and the last two digits, we can // construct the year. Likewise for the ISO year. if let (None, Some(century), Some(is_negative), Some(last_two)) = ( parsed.year(), parsed.year_century(), parsed.year_century_is_negative(), parsed.year_last_two(), ) { let year = if is_negative { 100 * century.extend::() - last_two.cast_signed().extend::() } else { 100 * century.extend::() + last_two.cast_signed().extend::() }; parsed.year = OptionRangedI32::from(RangedI32::new(year)); } if let (None, Some(century), Some(is_negative), Some(last_two)) = ( parsed.iso_year(), parsed.iso_year_century(), parsed.iso_year_century_is_negative(), parsed.iso_year_last_two(), ) { let iso_year = if is_negative { 100 * century.extend::() - last_two.cast_signed().extend::() } else { 100 * century.extend::() + last_two.cast_signed().extend::() }; parsed.iso_year = OptionRangedI32::from(RangedI32::new(iso_year)); } match_! { (year, ordinal) => Ok(Self::from_ordinal_date(year, ordinal.get())?), (year, month, day) => Ok(Self::from_calendar_date(year, month, day.get())?), (iso_year, iso_week_number, weekday) => Ok(Self::from_iso_week_date( iso_year, iso_week_number.get(), weekday, )?), (year, sunday_week_number, weekday) => Ok(Self::from_ordinal_date( year, (sunday_week_number.cast_signed().extend::() * 7 + weekday.number_days_from_sunday().cast_signed().extend::() - adjustment(year) + 1).cast_unsigned(), )?), (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date( year, (monday_week_number.cast_signed().extend::() * 7 + weekday.number_days_from_monday().cast_signed().extend::() - adjustment(year) + 1).cast_unsigned(), )?), _ => Err(InsufficientInformation), } } } impl TryFrom for Time { type Error = error::TryFromParsed; #[inline] fn try_from(parsed: Parsed) -> Result { let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) { (Some(hour), _, _) => hour, (_, Some(hour), Some(false)) if hour.get() == 12 => 0, (_, Some(hour), Some(true)) if hour.get() == 12 => 12, (_, Some(hour), Some(false)) => hour.get(), (_, Some(hour), Some(true)) => hour.get() + 12, _ => return Err(InsufficientInformation), }; if parsed.hour_24().is_none() && parsed.hour_12().is_some() && parsed.hour_12_is_pm().is_some() && parsed.minute().is_none() && parsed.second().is_none() && parsed.subsecond().is_none() { return Ok(Self::from_hms_nano(hour, 0, 0, 0)?); } // Reject combinations such as hour-second with minute omitted. match (parsed.minute(), parsed.second(), parsed.subsecond()) { (None, None, None) => Ok(Self::from_hms_nano(hour, 0, 0, 0)?), (Some(minute), None, None) => Ok(Self::from_hms_nano(hour, minute, 0, 0)?), (Some(minute), Some(second), None) => Ok(Self::from_hms_nano(hour, minute, second, 0)?), (Some(minute), Some(second), Some(subsecond)) => { Ok(Self::from_hms_nano(hour, minute, second, subsecond)?) } _ => Err(InsufficientInformation), } } } #[inline] fn utc_offset_try_from_parsed( parsed: Parsed, ) -> Result { let hour = match (REQUIRED, parsed.offset_hour()) { // An offset is required, but the hour is missing. Return an error. (true, None) => return Err(InsufficientInformation), // An offset is not required (e.g. for `UtcDateTime`). As the hour is missing, minutes and // seconds are not parsed. This is UTC. (false, None) => return Ok(UtcOffset::UTC), // Any other situation has an hour present. (_, Some(hour)) => hour, }; let minute = parsed.offset_minute_signed(); // Force `second` to be `None` if `minute` is `None`. let second = minute.and_then(|_| parsed.offset_second_signed()); let minute = minute.unwrap_or(0); let second = second.unwrap_or(0); UtcOffset::from_hms(hour, minute, second).map_err(Into::into) } impl TryFrom for UtcOffset { type Error = error::TryFromParsed; #[inline] fn try_from(parsed: Parsed) -> Result { utc_offset_try_from_parsed::(parsed) } } impl TryFrom for PrimitiveDateTime { type Error = error::TryFromParsed; #[inline] fn try_from(parsed: Parsed) -> Result { Ok(Self::new(parsed.try_into()?, parsed.try_into()?)) } } impl TryFrom for UtcDateTime { type Error = error::TryFromParsed; #[inline] fn try_from(mut parsed: Parsed) -> Result { if let Some(timestamp) = parsed.unix_timestamp_nanos() { let mut value = Self::from_unix_timestamp_nanos(timestamp)?; if let Some(subsecond) = parsed.subsecond() { value = value.replace_nanosecond(subsecond)?; } return Ok(value); } // Some well-known formats explicitly allow leap seconds. We don't currently support them, // so treat it as the nearest preceding moment that can be represented. Because leap seconds // always fall at the end of a month UTC, reject any that are at other times. let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) { if parsed.set_second(59).is_none() { bug!("59 is a valid second"); } if parsed.set_subsecond(999_999_999).is_none() { bug!("999_999_999 is a valid subsecond"); } true } else { false }; let dt = OffsetDateTime::new_in_offset( Date::try_from(parsed)?, Time::try_from(parsed)?, utc_offset_try_from_parsed::(parsed)?, ) .to_utc(); if leap_second_input && !dt.is_valid_leap_second_stand_in() { return Err(error::TryFromParsed::ComponentRange( error::ComponentRange::conditional("second"), )); } Ok(dt) } } impl TryFrom for OffsetDateTime { type Error = error::TryFromParsed; #[inline] fn try_from(mut parsed: Parsed) -> Result { if let Some(timestamp) = parsed.unix_timestamp_nanos() { let mut value = Self::from_unix_timestamp_nanos(timestamp)?; if let Some(subsecond) = parsed.subsecond() { value = value.replace_nanosecond(subsecond)?; } return Ok(value); } // Some well-known formats explicitly allow leap seconds. We don't currently support them, // so treat it as the nearest preceding moment that can be represented. Because leap seconds // always fall at the end of a month UTC, reject any that are at other times. let leap_second_input = if parsed.leap_second_allowed && parsed.second() == Some(60) { if parsed.set_second(59).is_none() { bug!("59 is a valid second"); } if parsed.set_subsecond(999_999_999).is_none() { bug!("999_999_999 is a valid subsecond"); } true } else { false }; let dt = Self::new_in_offset( Date::try_from(parsed)?, Time::try_from(parsed)?, UtcOffset::try_from(parsed)?, ); if leap_second_input && !dt.is_valid_leap_second_stand_in() { return Err(error::TryFromParsed::ComponentRange( error::ComponentRange::conditional("second"), )); } Ok(dt) } } time-0.3.47/src/parsing/shim.rs000064400000000000000000000030201046102023000144100ustar 00000000000000//! 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; } /// 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.checked_mul(10)?.checked_add(digit as Self) } } )*) } impl_parse_bytes! { u8 u16 u32 u128 } time-0.3.47/src/primitive_date_time.rs000064400000000000000000001156511046102023000160460ustar 00000000000000//! The [`PrimitiveDateTime`] struct and its associated `impl`s. #[cfg(feature = "formatting")] use alloc::string::String; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::time::Duration as StdDuration; #[cfg(feature = "formatting")] use std::io; use powerfmt::ext::FormatterExt as _; use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; #[cfg(feature = "formatting")] use crate::formatting::Formattable; use crate::internal_macros::{const_try, const_try_opt}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::{ Date, Duration, Month, OffsetDateTime, Time, UtcDateTime, UtcOffset, Weekday, error, util, }; /// Combined date and time. #[derive(Clone, Copy, Eq)] #[cfg_attr(not(docsrs), repr(C))] pub struct PrimitiveDateTime { // The order of this struct's fields matter! Do not reorder them. // Little endian version #[cfg(target_endian = "little")] time: Time, #[cfg(target_endian = "little")] date: Date, // Big endian version #[cfg(target_endian = "big")] date: Date, #[cfg(target_endian = "big")] time: Time, } impl Hash for PrimitiveDateTime { #[inline] fn hash(&self, state: &mut H) where H: Hasher, { self.as_i128().hash(state); } } impl PartialEq for PrimitiveDateTime { #[inline] fn eq(&self, other: &Self) -> bool { self.as_i128().eq(&other.as_i128()) } } impl PartialOrd for PrimitiveDateTime { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for PrimitiveDateTime { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.as_i128().cmp(&other.as_i128()) } } impl PrimitiveDateTime { /// Provide a representation of `PrimitiveDateTime` as a `i128`. This value can be used for /// equality, hashing, and ordering. /// /// **Note**: This value is explicitly signed, so do not cast this to or treat this as an /// unsigned integer. Doing so will lead to incorrect results for values with differing /// signs. #[inline] const fn as_i128(self) -> i128 { let time = self.time.as_u64() as i128; let date = self.date.as_i32() as i128; (date << 64) | time } /// The smallest value that can be represented by `PrimitiveDateTime`. /// /// Depending on `large-dates` feature flag, value of this constant may vary. /// /// 1. With `large-dates` disabled it is equal to `-9999-01-01 00:00:00.0` /// 2. With `large-dates` enabled it is equal to `-999999-01-01 00:00:00.0` /// /// ```rust /// # use time::PrimitiveDateTime; /// # use time_macros::datetime; #[cfg_attr( feature = "large-dates", doc = "// Assuming `large-dates` feature is enabled." )] #[cfg_attr( feature = "large-dates", doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-999999-01-01 0:00));" )] #[cfg_attr( not(feature = "large-dates"), doc = "// Assuming `large-dates` feature is disabled." )] #[cfg_attr( not(feature = "large-dates"), doc = "assert_eq!(PrimitiveDateTime::MIN, datetime!(-9999-01-01 0:00));" )] /// ``` pub const MIN: Self = Self { date: Date::MIN, time: Time::MIDNIGHT, }; /// The largest value that can be represented by `PrimitiveDateTime`. /// /// Depending on `large-dates` feature flag, value of this constant may vary. /// /// 1. With `large-dates` disabled it is equal to `9999-12-31 23:59:59.999_999_999` /// 2. With `large-dates` enabled it is equal to `999999-12-31 23:59:59.999_999_999` /// /// ```rust /// # use time::PrimitiveDateTime; /// # use time_macros::datetime; #[cfg_attr( feature = "large-dates", doc = "// Assuming `large-dates` feature is enabled." )] #[cfg_attr( feature = "large-dates", doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+999999-12-31 23:59:59.999_999_999));" )] #[cfg_attr( not(feature = "large-dates"), doc = "// Assuming `large-dates` feature is disabled." )] #[cfg_attr( not(feature = "large-dates"), doc = "assert_eq!(PrimitiveDateTime::MAX, datetime!(+9999-12-31 23:59:59.999_999_999));" )] /// ``` pub const MAX: Self = Self { date: Date::MAX, time: Time::MAX, }; /// Create a new `PrimitiveDateTime` from the provided [`Date`] and [`Time`]. /// /// ```rust /// # use time::PrimitiveDateTime; /// # use time_macros::{date, datetime, time}; /// assert_eq!( /// PrimitiveDateTime::new(date!(2019-01-01), time!(0:00)), /// datetime!(2019-01-01 0:00), /// ); /// ``` #[inline] pub const fn new(date: Date, time: Time) -> Self { Self { date, time } } /// Get the [`Date`] component of the `PrimitiveDateTime`. /// /// ```rust /// # use time_macros::{date, datetime}; /// assert_eq!(datetime!(2019-01-01 0:00).date(), date!(2019-01-01)); /// ``` #[inline] pub const fn date(self) -> Date { self.date } /// Get the [`Time`] component of the `PrimitiveDateTime`. /// /// ```rust /// # use time_macros::{datetime, time}; /// assert_eq!(datetime!(2019-01-01 0:00).time(), time!(0:00)); /// ``` #[inline] pub const fn time(self) -> Time { self.time } /// Get the year of the date. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).year(), 2019); /// assert_eq!(datetime!(2019-12-31 0:00).year(), 2019); /// assert_eq!(datetime!(2020-01-01 0:00).year(), 2020); /// ``` #[inline] pub const fn year(self) -> i32 { self.date().year() } /// Get the month of the date. /// /// ```rust /// # use time::Month; /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).month(), Month::January); /// assert_eq!(datetime!(2019-12-31 0:00).month(), Month::December); /// ``` #[inline] pub const fn month(self) -> Month { self.date().month() } /// Get the day of the date. /// /// The returned value will always be in the range `1..=31`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).day(), 1); /// assert_eq!(datetime!(2019-12-31 0:00).day(), 31); /// ``` #[inline] pub const fn day(self) -> u8 { self.date().day() } /// Get the day of the year. /// /// The returned value will always be in the range `1..=366` (`1..=365` for common years). /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).ordinal(), 1); /// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365); /// ``` #[inline] pub const fn ordinal(self) -> u16 { self.date().ordinal() } /// Get the ISO week number. /// /// The returned value will always be in the range `1..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).iso_week(), 1); /// assert_eq!(datetime!(2019-10-04 0:00).iso_week(), 40); /// assert_eq!(datetime!(2020-01-01 0:00).iso_week(), 1); /// assert_eq!(datetime!(2020-12-31 0:00).iso_week(), 53); /// assert_eq!(datetime!(2021-01-01 0:00).iso_week(), 53); /// ``` #[inline] pub const fn iso_week(self) -> u8 { self.date().iso_week() } /// Get the week number where week 1 begins on the first Sunday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).sunday_based_week(), 0); /// assert_eq!(datetime!(2020-01-01 0:00).sunday_based_week(), 0); /// assert_eq!(datetime!(2020-12-31 0:00).sunday_based_week(), 52); /// assert_eq!(datetime!(2021-01-01 0:00).sunday_based_week(), 0); /// ``` #[inline] pub const fn sunday_based_week(self) -> u8 { self.date().sunday_based_week() } /// Get the week number where week 1 begins on the first Monday. /// /// The returned value will always be in the range `0..=53`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).monday_based_week(), 0); /// assert_eq!(datetime!(2020-01-01 0:00).monday_based_week(), 0); /// assert_eq!(datetime!(2020-12-31 0:00).monday_based_week(), 52); /// assert_eq!(datetime!(2021-01-01 0:00).monday_based_week(), 0); /// ``` #[inline] pub const fn monday_based_week(self) -> u8 { self.date().monday_based_week() } /// Get the year, month, and day. /// /// ```rust /// # use time::Month; /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00).to_calendar_date(), /// (2019, Month::January, 1) /// ); /// ``` #[inline] pub const fn to_calendar_date(self) -> (i32, Month, u8) { self.date().to_calendar_date() } /// Get the year and ordinal day number. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1)); /// ``` #[inline] pub const fn to_ordinal_date(self) -> (i32, u16) { self.date().to_ordinal_date() } /// Get the ISO 8601 year, week number, and weekday. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00).to_iso_week_date(), /// (2019, 1, Tuesday) /// ); /// assert_eq!( /// datetime!(2019-10-04 0:00).to_iso_week_date(), /// (2019, 40, Friday) /// ); /// assert_eq!( /// datetime!(2020-01-01 0:00).to_iso_week_date(), /// (2020, 1, Wednesday) /// ); /// assert_eq!( /// datetime!(2020-12-31 0:00).to_iso_week_date(), /// (2020, 53, Thursday) /// ); /// assert_eq!( /// datetime!(2021-01-01 0:00).to_iso_week_date(), /// (2020, 53, Friday) /// ); /// ``` #[inline] pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { self.date().to_iso_week_date() } /// Get the weekday. /// /// ```rust /// # use time::Weekday::*; /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).weekday(), Tuesday); /// assert_eq!(datetime!(2019-02-01 0:00).weekday(), Friday); /// assert_eq!(datetime!(2019-03-01 0:00).weekday(), Friday); /// assert_eq!(datetime!(2019-04-01 0:00).weekday(), Monday); /// assert_eq!(datetime!(2019-05-01 0:00).weekday(), Wednesday); /// assert_eq!(datetime!(2019-06-01 0:00).weekday(), Saturday); /// assert_eq!(datetime!(2019-07-01 0:00).weekday(), Monday); /// assert_eq!(datetime!(2019-08-01 0:00).weekday(), Thursday); /// assert_eq!(datetime!(2019-09-01 0:00).weekday(), Sunday); /// assert_eq!(datetime!(2019-10-01 0:00).weekday(), Tuesday); /// assert_eq!(datetime!(2019-11-01 0:00).weekday(), Friday); /// assert_eq!(datetime!(2019-12-01 0:00).weekday(), Sunday); /// ``` #[inline] pub const fn weekday(self) -> Weekday { self.date().weekday() } /// Get the Julian day for the date. The time is not taken into account for this calculation. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(-4713-11-24 0:00).to_julian_day(), 0); /// assert_eq!(datetime!(2000-01-01 0:00).to_julian_day(), 2_451_545); /// assert_eq!(datetime!(2019-01-01 0:00).to_julian_day(), 2_458_485); /// assert_eq!(datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849); /// ``` #[inline] pub const fn to_julian_day(self) -> i32 { self.date().to_julian_day() } /// Get the clock hour, minute, and second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms(), (0, 0, 0)); /// assert_eq!(datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59)); /// ``` #[inline] pub const fn as_hms(self) -> (u8, u8, u8) { self.time().as_hms() } /// Get the clock hour, minute, second, and millisecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_milli(), (0, 0, 0, 0)); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999).as_hms_milli(), /// (23, 59, 59, 999) /// ); /// ``` #[inline] pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { self.time().as_hms_milli() } /// Get the clock hour, minute, second, and microsecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_micro(), (0, 0, 0, 0)); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999_999).as_hms_micro(), /// (23, 59, 59, 999_999) /// ); /// ``` #[inline] pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { self.time().as_hms_micro() } /// Get the clock hour, minute, second, and nanosecond. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_nano(), (0, 0, 0, 0)); /// assert_eq!( /// datetime!(2020-01-01 23:59:59.999_999_999).as_hms_nano(), /// (23, 59, 59, 999_999_999) /// ); /// ``` #[inline] pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { self.time().as_hms_nano() } /// Get the clock hour. /// /// The returned value will always be in the range `0..24`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).hour(), 0); /// assert_eq!(datetime!(2019-01-01 23:59:59).hour(), 23); /// ``` #[inline] pub const fn hour(self) -> u8 { self.time().hour() } /// Get the minute within the hour. /// /// The returned value will always be in the range `0..60`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).minute(), 0); /// assert_eq!(datetime!(2019-01-01 23:59:59).minute(), 59); /// ``` #[inline] pub const fn minute(self) -> u8 { self.time().minute() } /// Get the second within the minute. /// /// The returned value will always be in the range `0..60`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).second(), 0); /// assert_eq!(datetime!(2019-01-01 23:59:59).second(), 59); /// ``` #[inline] pub const fn second(self) -> u8 { self.time().second() } /// Get the milliseconds within the second. /// /// The returned value will always be in the range `0..1_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).millisecond(), 0); /// assert_eq!(datetime!(2019-01-01 23:59:59.999).millisecond(), 999); /// ``` #[inline] pub const fn millisecond(self) -> u16 { self.time().millisecond() } /// Get the microseconds within the second. /// /// The returned value will always be in the range `0..1_000_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).microsecond(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59.999_999).microsecond(), /// 999_999 /// ); /// ``` #[inline] pub const fn microsecond(self) -> u32 { self.time().microsecond() } /// Get the nanoseconds within the second. /// /// The returned value will always be in the range `0..1_000_000_000`. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2019-01-01 0:00).nanosecond(), 0); /// assert_eq!( /// datetime!(2019-01-01 23:59:59.999_999_999).nanosecond(), /// 999_999_999, /// ); /// ``` #[inline] pub const fn nanosecond(self) -> u32 { self.time().nanosecond() } /// Assuming that the existing `PrimitiveDateTime` represents a moment in the provided /// [`UtcOffset`], return an [`OffsetDateTime`]. /// /// ```rust /// # use time_macros::{datetime, offset}; /// assert_eq!( /// datetime!(2019-01-01 0:00) /// .assume_offset(offset!(UTC)) /// .unix_timestamp(), /// 1_546_300_800, /// ); /// assert_eq!( /// datetime!(2019-01-01 0:00) /// .assume_offset(offset!(-1)) /// .unix_timestamp(), /// 1_546_304_400, /// ); /// ``` #[inline] pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime { OffsetDateTime::new_in_offset(self.date, self.time, offset) } /// Assuming that the existing `PrimitiveDateTime` represents a moment in UTC, return an /// [`OffsetDateTime`]. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00).assume_utc().unix_timestamp(), /// 1_546_300_800, /// ); /// ``` /// /// **Note**: You may want a [`UtcDateTime`] instead, which can be obtained with the /// [`PrimitiveDateTime::as_utc`] method. #[inline] pub const fn assume_utc(self) -> OffsetDateTime { self.assume_offset(UtcOffset::UTC) } /// Assuming that the existing `PrimitiveDateTime` represents a moment in UTC, return a /// [`UtcDateTime`]. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2019-01-01 0:00).as_utc().unix_timestamp(), /// 1_546_300_800, /// ); /// ``` #[inline] pub const fn as_utc(self) -> UtcDateTime { UtcDateTime::from_primitive(self) } /// Computes `self + duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::datetime; /// let datetime = Date::MIN.midnight(); /// assert_eq!(datetime.checked_add((-2).days()), None); /// /// let datetime = Date::MAX.midnight(); /// assert_eq!(datetime.checked_add(1.days()), None); /// /// assert_eq!( /// datetime!(2019-11-25 15:30).checked_add(27.hours()), /// Some(datetime!(2019-11-26 18:30)) /// ); /// ``` #[inline] pub const fn checked_add(self, duration: Duration) -> Option { let (date_adjustment, time) = self.time.adjusting_add(duration); let date = const_try_opt!(self.date.checked_add(duration)); Some(Self { date: match date_adjustment { util::DateAdjustment::Previous => const_try_opt!(date.previous_day()), util::DateAdjustment::Next => const_try_opt!(date.next_day()), util::DateAdjustment::None => date, }, time, }) } /// Computes `self - duration`, returning `None` if an overflow occurred. /// /// ``` /// # use time::{Date, ext::NumericalDuration}; /// # use time_macros::datetime; /// let datetime = Date::MIN.midnight(); /// assert_eq!(datetime.checked_sub(2.days()), None); /// /// let datetime = Date::MAX.midnight(); /// assert_eq!(datetime.checked_sub((-1).days()), None); /// /// assert_eq!( /// datetime!(2019-11-25 15:30).checked_sub(27.hours()), /// Some(datetime!(2019-11-24 12:30)) /// ); /// ``` #[inline] pub const fn checked_sub(self, duration: Duration) -> Option { let (date_adjustment, time) = self.time.adjusting_sub(duration); let date = const_try_opt!(self.date.checked_sub(duration)); Some(Self { date: match date_adjustment { util::DateAdjustment::Previous => const_try_opt!(date.previous_day()), util::DateAdjustment::Next => const_try_opt!(date.next_day()), util::DateAdjustment::None => date, }, time, }) } /// Computes `self + duration`, saturating value on overflow. /// /// ``` /// # use time::{PrimitiveDateTime, ext::NumericalDuration}; /// # use time_macros::datetime; /// assert_eq!( /// PrimitiveDateTime::MIN.saturating_add((-2).days()), /// PrimitiveDateTime::MIN /// ); /// /// assert_eq!( /// PrimitiveDateTime::MAX.saturating_add(2.days()), /// PrimitiveDateTime::MAX /// ); /// /// assert_eq!( /// datetime!(2019-11-25 15:30).saturating_add(27.hours()), /// datetime!(2019-11-26 18:30) /// ); /// ``` #[inline] pub const fn saturating_add(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_add(duration) { datetime } else if duration.is_negative() { Self::MIN } else { Self::MAX } } /// Computes `self - duration`, saturating value on overflow. /// /// ``` /// # use time::{PrimitiveDateTime, ext::NumericalDuration}; /// # use time_macros::datetime; /// assert_eq!( /// PrimitiveDateTime::MIN.saturating_sub(2.days()), /// PrimitiveDateTime::MIN /// ); /// /// assert_eq!( /// PrimitiveDateTime::MAX.saturating_sub((-2).days()), /// PrimitiveDateTime::MAX /// ); /// /// assert_eq!( /// datetime!(2019-11-25 15:30).saturating_sub(27.hours()), /// datetime!(2019-11-24 12:30) /// ); /// ``` #[inline] pub const fn saturating_sub(self, duration: Duration) -> Self { if let Some(datetime) = self.checked_sub(duration) { datetime } else if duration.is_negative() { Self::MAX } else { Self::MIN } } } /// Methods that replace part of the `PrimitiveDateTime`. impl PrimitiveDateTime { /// Replace the time, preserving the date. /// /// ```rust /// # use time_macros::{datetime, time}; /// assert_eq!( /// datetime!(2020-01-01 17:00).replace_time(time!(5:00)), /// datetime!(2020-01-01 5:00) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_time(self, time: Time) -> Self { Self { date: self.date, time, } } /// Replace the date, preserving the time. /// /// ```rust /// # use time_macros::{datetime, date}; /// assert_eq!( /// datetime!(2020-01-01 12:00).replace_date(date!(2020-01-30)), /// datetime!(2020-01-30 12:00) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_date(self, date: Date) -> Self { Self { date, time: self.time, } } /// Replace the year. The month and day will be unchanged. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 12:00).replace_year(2019), /// Ok(datetime!(2019-02-18 12:00)) /// ); /// assert!(datetime!(2022-02-18 12:00).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year /// assert!(datetime!(2022-02-18 12:00).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_year(self, year: i32) -> Result { Ok(Self { date: const_try!(self.date.replace_year(year)), time: self.time, }) } /// Replace the month of the year. /// /// ```rust /// # use time_macros::datetime; /// # use time::Month; /// assert_eq!( /// datetime!(2022-02-18 12:00).replace_month(Month::January), /// Ok(datetime!(2022-01-18 12:00)) /// ); /// assert!(datetime!(2022-01-30 12:00).replace_month(Month::February).is_err()); // 30 isn't a valid day in February /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_month(self, month: Month) -> Result { Ok(Self { date: const_try!(self.date.replace_month(month)), time: self.time, }) } /// Replace the day of the month. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 12:00).replace_day(1), /// Ok(datetime!(2022-02-01 12:00)) /// ); /// assert!(datetime!(2022-02-18 12:00).replace_day(0).is_err()); // 00 isn't a valid day /// assert!(datetime!(2022-02-18 12:00).replace_day(30).is_err()); // 30 isn't a valid day in February /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_day(self, day: u8) -> Result { Ok(Self { date: const_try!(self.date.replace_day(day)), time: self.time, }) } /// Replace the day of the year. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!(datetime!(2022-049 12:00).replace_ordinal(1), Ok(datetime!(2022-001 12:00))); /// assert!(datetime!(2022-049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal /// assert!(datetime!(2022-049 12:00).replace_ordinal(366).is_err()); // 2022 isn't a leap year /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_ordinal(self, ordinal: u16) -> Result { Ok(Self { date: const_try!(self.date.replace_ordinal(ordinal)), time: self.time, }) } /// Truncate to the start of the day, setting the time to midnight. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_day(), /// datetime!(2022-02-18 0:00) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_day(self) -> Self { self.replace_time(Time::MIDNIGHT) } /// Replace the clock hour. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_hour(7), /// Ok(datetime!(2022-02-18 07:02:03.004_005_006)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_hour(self, hour: u8) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_hour(hour)), }) } /// Truncate to the hour, setting the minute, second, and subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_hour(), /// datetime!(2022-02-18 15:00) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_hour(self) -> Self { self.replace_time(self.time.truncate_to_hour()) } /// Replace the minutes within the hour. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_minute(7), /// Ok(datetime!(2022-02-18 01:07:03.004_005_006)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_minute(self, minute: u8) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_minute(minute)), }) } /// Truncate to the minute, setting the second and subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_minute(), /// datetime!(2022-02-18 15:30) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_minute(self) -> Self { self.replace_time(self.time.truncate_to_minute()) } /// Replace the seconds within the minute. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_second(7), /// Ok(datetime!(2022-02-18 01:02:07.004_005_006)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_second(self, second: u8) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_second(second)), }) } /// Truncate to the second, setting the subsecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_second(), /// datetime!(2022-02-18 15:30:45) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_second(self) -> Self { self.replace_time(self.time.truncate_to_second()) } /// Replace the milliseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_millisecond(7), /// Ok(datetime!(2022-02-18 01:02:03.007)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_millisecond( self, millisecond: u16, ) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_millisecond(millisecond)), }) } /// Truncate to the millisecond, setting the microsecond and nanosecond components to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_millisecond(), /// datetime!(2022-02-18 15:30:45.123) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_millisecond(self) -> Self { self.replace_time(self.time.truncate_to_millisecond()) } /// Replace the microseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_microsecond(7_008), /// Ok(datetime!(2022-02-18 01:02:03.007_008)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_microsecond( self, microsecond: u32, ) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_microsecond(microsecond)), }) } /// Truncate to the microsecond, setting the nanosecond component to zero. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 15:30:45.123_456_789).truncate_to_microsecond(), /// datetime!(2022-02-18 15:30:45.123_456) /// ); /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn truncate_to_microsecond(self) -> Self { self.replace_time(self.time.truncate_to_microsecond()) } /// Replace the nanoseconds within the second. /// /// ```rust /// # use time_macros::datetime; /// assert_eq!( /// datetime!(2022-02-18 01:02:03.004_005_006).replace_nanosecond(7_008_009), /// Ok(datetime!(2022-02-18 01:02:03.007_008_009)) /// ); /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond /// ``` #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] #[inline] pub const fn replace_nanosecond(self, nanosecond: u32) -> Result { Ok(Self { date: self.date, time: const_try!(self.time.replace_nanosecond(nanosecond)), }) } } #[cfg(feature = "formatting")] impl PrimitiveDateTime { /// Format the `PrimitiveDateTime` using the provided [format /// description](crate::format_description). #[inline] pub fn format_into( self, output: &mut (impl io::Write + ?Sized), format: &(impl Formattable + ?Sized), ) -> Result { format.format_into(output, &self, &mut Default::default()) } /// Format the `PrimitiveDateTime` using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::format_description; /// # use time_macros::datetime; /// let format = format_description::parse("[year]-[month]-[day] [hour]:[minute]:[second]")?; /// assert_eq!( /// datetime!(2020-01-02 03:04:05).format(&format)?, /// "2020-01-02 03:04:05" /// ); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result { format.format(&self, &mut Default::default()) } } #[cfg(feature = "parsing")] impl PrimitiveDateTime { /// Parse a `PrimitiveDateTime` from the input using the provided [format /// description](crate::format_description). /// /// ```rust /// # use time::PrimitiveDateTime; /// # use time_macros::{datetime, format_description}; /// let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); /// assert_eq!( /// PrimitiveDateTime::parse("2020-01-02 03:04:05", &format)?, /// datetime!(2020-01-02 03:04:05) /// ); /// # Ok::<_, time::Error>(()) /// ``` #[inline] pub fn parse( input: &str, description: &(impl Parsable + ?Sized), ) -> Result { description.parse_primitive_date_time(input.as_bytes()) } } impl SmartDisplay for PrimitiveDateTime { type Metadata = (); #[inline] fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { let width = smart_display::padded_width_of!(self.date, " ", self.time); Metadata::new(width, self, ()) } #[inline] fn fmt_with_metadata( &self, f: &mut fmt::Formatter<'_>, metadata: Metadata, ) -> fmt::Result { f.pad_with_width( metadata.unpadded_width(), format_args!("{} {}", self.date, self.time), ) } } impl fmt::Display for PrimitiveDateTime { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { SmartDisplay::fmt(self, f) } } impl fmt::Debug for PrimitiveDateTime { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) } } impl Add for PrimitiveDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: Duration) -> Self::Output { self.checked_add(duration) .expect("resulting value is out of range") } } impl Add for PrimitiveDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add(self, duration: StdDuration) -> Self::Output { let (is_next_day, time) = self.time.adjusting_add_std(duration); Self { date: if is_next_day { (self.date + duration) .next_day() .expect("resulting value is out of range") } else { self.date + duration }, time, } } } impl AddAssign for PrimitiveDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, duration: Duration) { *self = *self + duration; } } impl AddAssign for PrimitiveDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn add_assign(&mut self, duration: StdDuration) { *self = *self + duration; } } impl Sub for PrimitiveDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, duration: Duration) -> Self::Output { self.checked_sub(duration) .expect("resulting value is out of range") } } impl Sub for PrimitiveDateTime { type Output = Self; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, duration: StdDuration) -> Self::Output { let (is_previous_day, time) = self.time.adjusting_sub_std(duration); Self { date: if is_previous_day { (self.date - duration) .previous_day() .expect("resulting value is out of range") } else { self.date - duration }, time, } } } impl SubAssign for PrimitiveDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, duration: Duration) { *self = *self - duration; } } impl SubAssign for PrimitiveDateTime { /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub_assign(&mut self, duration: StdDuration) { *self = *self - duration; } } impl Sub for PrimitiveDateTime { type Output = Duration; /// # Panics /// /// This may panic if an overflow occurs. #[inline] #[track_caller] fn sub(self, rhs: Self) -> Self::Output { (self.date - rhs.date) + (self.time - rhs.time) } } time-0.3.47/src/quickcheck.rs000064400000000000000000000146471046102023000141400ustar 00000000000000//! 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 { //! 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { match self { Self::January => empty_shrinker(), _ => single_shrinker(self.previous()), } } } time-0.3.47/src/rand08.rs000064400000000000000000000060721046102023000131130ustar 00000000000000//! 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