diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-02 18:36:06 -0600 |
| commit | 8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch) | |
| tree | 22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/time | |
| parent | 4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff) | |
chore: add vendor directory
Diffstat (limited to 'vendor/time')
93 files changed, 22515 insertions, 0 deletions
diff --git a/vendor/time/.cargo-checksum.json b/vendor/time/.cargo-checksum.json new file mode 100644 index 00000000..96475fc6 --- /dev/null +++ b/vendor/time/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"54ec18fc70d29545aedf23b042a08cc9362636c69ec2a61c03d6c12b74d93a6b","Cargo.toml":"266015f9745494d625f3ebfd148d72c282a1ec2bb5a34c88012b3c2c31f87f04","LICENSE-Apache":"0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594","LICENSE-MIT":"2537228d9a1b44a5dc595241349cae7090b326c8de165aaf89bfddef4a00d0fc","README.md":"28ca0b1f1e88d8d2b2fa7ad2614daa06c301040d6d2980f90508e92283d47daf","src/date.rs":"8fa1c067f62882bf66c2f66b7a84d152f4cc0cd67d88551b37af19aeaf92faa4","src/duration.rs":"4af9d384d1a18653e2214bc79d94fa921725199e57b6241bc785196fc6affde1","src/error/component_range.rs":"f9ad9b0688088e16ec4299348f172ea769bbc85e8fa5c254c91b06c286d2c739","src/error/conversion_range.rs":"972abb765370070de01e2fc2e1bb1e80808a069e6213577d7beaca02e1d707c3","src/error/different_variant.rs":"107bef7b3addd7108b36a2da8389f611d4482f34a5b63429841141e05c8cb30c","src/error/format.rs":"086bfdd271e8b28d59eb07568bc6ad6a6296c2d74786a3e076d74c6140dc46bf","src/error/indeterminate_offset.rs":"1f52f9ea107847fa781399cfcc8046451d70155fb497486c80b2138f82782941","src/error/invalid_format_description.rs":"16eafff868a943877ef6d9f36ae2b32775e30c5a5803ef93731449c02f6f54f6","src/error/invalid_variant.rs":"b653a3e6e902f06cb9f2e0366c4da84b92e8bdb03164c2f8cb15fe66415706e4","src/error/mod.rs":"336265f0a88d01305f749e6382edfe6ebdf61002ff2cb2d6e7f575e03e977398","src/error/parse.rs":"170be4ed1a058342ac07f61330f594512aec30d36e32261766f93a5fa8852122","src/error/parse_from_description.rs":"2de1f5b5af3d9bb358cee1c66a2721a78ee99f0ee5e0c06f159e62de7a294a5f","src/error/try_from_parsed.rs":"8c227be52653a1d33af01a8024c0fc56f1f9803f08ef01487a7eaa5833adbb57","src/ext/digit_count.rs":"d32af3cac57077870f6b31c5bd6e0da3a530a7c713aaebdcd27125d9fd8857a3","src/ext/instant.rs":"96945bc0af1de8eddef110c2d5ec9d5459dde86ba4e3e361fce5927a2388b514","src/ext/mod.rs":"dc70d230a170ddd1bf7798703d9ff7cac419aaadc82b5f8dbf46d5320322e2c5","src/ext/numerical_duration.rs":"92fe416dd1c404d97f06ced3256e664b283b44ecc272aa4307f8a7d84e602cd4","src/ext/numerical_std_duration.rs":"0ada71ca29daa0a8a8d4d7fb477f0eec2114787a345d75a2d4d42534a2a20267","src/format_description/borrowed_format_item.rs":"ac61ff3b6f1e6fa1947669c7bea7ece6d65940731cced34a66db28fdc48ebc10","src/format_description/component.rs":"b65c0ca896ea6ec8dbfc7570c69849c88cbba6031a2847dcfdce06d721a59397","src/format_description/mod.rs":"d6ca12469c42910fd459d2cf5cebf93e694220cf51a6028cca6912fbe92b96ff","src/format_description/modifier.rs":"b7e6c637aa0f149aace9425285df43974b4c7539ee40bc52b777f4107b64dfef","src/format_description/owned_format_item.rs":"bfdf371ebef1125161e364fba745efc5ab3aebe1d0c9570e85cd9396b391f96d","src/format_description/parse/ast.rs":"645e7bce15323529902e9bd22c2a2e06beaa0af934e203e67031b5269a159c46","src/format_description/parse/format_item.rs":"c8ef68b6b0f882e4896033c9761b7f384f09626e8c438aea2e53d2966fb00318","src/format_description/parse/lexer.rs":"8f5bc750f83489e2a5fdc2e5e00ed8f0e5def0a376e9da541fc5fedad709a2c0","src/format_description/parse/mod.rs":"715d5bddfd98f1c1d6b15fe3797b61f2311561c29050fea4497af6f6efae3e99","src/format_description/parse/strftime.rs":"f125b20aefe5ee2eedb32d72cec535cbbce639fccd79e1348c9d0b76bb792377","src/format_description/well_known/iso8601.rs":"30b2f495044ab4e1f3ff6a317a8b4ca2ffb46b7cc23428eca8b685c0f16518cc","src/format_description/well_known/iso8601/adt_hack.rs":"fa099ffded092ce9d875c744891253045a065fa138adf4182c3b2b7e6ca466a4","src/format_description/well_known/rfc2822.rs":"36c23394724ae12250d4193cab26887a6ff8f82ca441ea6b0d03c4f1c928b3dd","src/format_description/well_known/rfc3339.rs":"1a6318dffd3ebb6ac7cf96eae3d9b1eb44b1089cf4284fa6a7e935c6fcf1b43c","src/formatting/formattable.rs":"6c32458f3bf34144ebe7405a568acb0ac85fc71cff6ff71ae72ceff2470ca77f","src/formatting/iso8601.rs":"f99f0d206d88f5360665c18e6119b0001e88fb1a6b7c62bcad06b70021effce9","src/formatting/mod.rs":"3c013d91e54eb4c9bd7661a8a8af92c2ecf6bdc99320c7db9ec588f240bc0e39","src/hint.rs":"7ec8b5320c8e133f0c02942d3e6be83643f84f73053597909ff6a2f06aa7b591","src/instant.rs":"5c0420795708954322b0da7410424ca5b93f5c91b3b799b18e419eb52977c9b0","src/internal_macros.rs":"c1c98ff11424b6f2f05c14bed88cfa146894a4607fd87fae490451f63257e500","src/interop/js_sys_date_offsetdatetime.rs":"f4d60aa1ac398a96a89533d063f9375a7e94021eb9ef8c2ccbcb7caa2c394abf","src/interop/js_sys_date_utcdatetime.rs":"4721b739d6ca06126b236dd9bcfde6b4f35872a7cc232e7026878a383ac63e09","src/interop/mod.rs":"d8c0905e9fea22e4dddab981a020f79a637f849e17f53e5102416dbce1f6dbea","src/interop/offsetdatetime_systemtime.rs":"b59e68dd35e3f47460355a71b2b0be2fb7d239afce2bf1b8ed933abb391b532f","src/interop/offsetdatetime_utcdatetime.rs":"07b1e2d25404fb2f38cf85eb7b27c0e503e6e70bc8107877edfc578eec6323b3","src/interop/utcdatetime_systemtime.rs":"dd0c5f21d77e1cc5909feec22eb20101c9ebdea4089e278e66855159d73daed8","src/lib.rs":"82226f3f9266794e4183759cc6310f0ee541c070eb8adedf57b408e29ad5e650","src/macros.rs":"c35b4f4fbb4781dadf70a952649cedf4f4e29b3b571c8bbfa58e9f6ad4071423","src/month.rs":"aab1a041bb5b69a0c21fa7ef37e3f8a9fe6f3c05bb8432339bbf67d59773fc5d","src/offset_date_time.rs":"f528ec252117159cdf69881de4fd6ec8914b70b3492a380ebbc4a188d5326645","src/parsing/combinator/mod.rs":"e49d14f7d0c38a847d9f5f2e63d466464641d365f1d39c48d1a455ebe6632ec5","src/parsing/combinator/rfc/iso8601.rs":"af40a366ebcdf577e5c16e75331d7e8d4ded26afa88ea9deba0615449fe10fb1","src/parsing/combinator/rfc/mod.rs":"f30b75d248f5ae92c27646d504703f5489185afb76c998cc4375437b3d15c822","src/parsing/combinator/rfc/rfc2234.rs":"08e2813c6d40c0dae881875fe0417ae06886c73679256587e33186e46b3c3bae","src/parsing/combinator/rfc/rfc2822.rs":"6c1f4d2c352aeb79c6d1b49bae6105c472859ce057a030a4cb0aa28b2853cf93","src/parsing/component.rs":"c4d24849e3f8fc14651056996c55f7e5a5ab342f64377c3bb223437c12acb620","src/parsing/iso8601.rs":"38ea74f81388e8beba1f94e01c3b372f566d0a06b9879b21c009aa818c81bbc9","src/parsing/mod.rs":"9f4e580bc88993c7ddefa184bbcb5b34723158d2f303d90b13def2d8f9d808a1","src/parsing/parsable.rs":"9227330b7341b5b76b9beab6aff23b6a1dcb0e91120065a5bec16b3f88d4d0ec","src/parsing/parsed.rs":"17ac85ca457f723ab28719e6a84dc49acc600a4a72cdf86410b187969a5f22db","src/parsing/shim.rs":"46efc374bc3129e28936a850143fff8e42aafe10c69ebbb904195aaeca26adc9","src/primitive_date_time.rs":"d0c4caf3ddb4eca2acd519bf188aab3f58d92249a412aa44ceb34542fc6b23d5","src/quickcheck.rs":"aa7e5417c70d7a99046b6af6974108ebed41c676b80b7f67cf7f3153d4cdd72f","src/rand.rs":"dd7228d1008246d33f2dee3f81e2a66ff12c257d4e89aa468903fe32bc7038c4","src/serde/iso8601.rs":"9c7a856213b95d2d7d7bd6a244aea36de03fca92ebfd2c32dea9a9bb11e1730b","src/serde/mod.rs":"7d8ab3dfb84b3e98f52a428dba0c2542e5b3e76082f551310db0006ef0a4fd64","src/serde/rfc2822.rs":"c3d577f4fcb9b70c5b7127d343bcd4d8449732f68fba333ef2c6013daa6e2ece","src/serde/rfc3339.rs":"4811b3e304ccce4e22803ee7e4a3466d0f3ecc3b4076ca74c9cca2506971eb0d","src/serde/timestamp/microseconds.rs":"7b8a0f6e455ddb6c50ed4a8473ea68cf9aacb31af36ccc058b68fa3bb5f3246d","src/serde/timestamp/milliseconds.rs":"df995f05f340f0429697269aa152a39b0beca45e1b4d8e60f4a6bb27f09a4d88","src/serde/timestamp/milliseconds_i64.rs":"9debcd18cc50bef9c935d25c6edb2827a0f6bb19b426c326349925ae38466e6f","src/serde/timestamp/mod.rs":"8c8349e0e9833480667a3fc7dc31c7b67610467ea36ce0fb24f5a47a2ebb1da5","src/serde/timestamp/nanoseconds.rs":"a46b1e60e8b19792a26ebdab33cba53db95cb13e3c4d9675a17b1491e9fb2940","src/serde/visitor.rs":"0b6bc76b88eb486b783d16c42a8f3af65c610fc8ffa07dd294dbfe0163f6e0b4","src/sys/local_offset_at/imp.rs":"4b6e57f02566364270ac9b7e1540290a5658a296f7e911f988264d103e420326","src/sys/local_offset_at/mod.rs":"0090b833161f0e69dd150bd8fdb599f2a4095475afbd5f7441ae6a363c6a4cea","src/sys/local_offset_at/unix.rs":"bb21dbe063889ceb409f54eee74d54690dc62b8ba840929581d6e827ca291f77","src/sys/local_offset_at/wasm_js.rs":"7cdacf5548a89a00265764ef07c1abb2ea1a3211f1ec720932b066f1d18b2f20","src/sys/local_offset_at/windows.rs":"90c16515d7ac29961bd0b8af92e38c7999f260bfd3438c9935fee65e8c0cc1e9","src/sys/mod.rs":"5e5649775aff43afa68d3ba0ca193273fd412e2de50e532828d7de65a1327f44","src/sys/refresh_tz/imp.rs":"c5265e17bdf1d0e676e7f75ca3a39e84c5d19d24de3d7f37ab0ce0e0f001d3b9","src/sys/refresh_tz/mod.rs":"0399000db309480debdaea9a61f0fad044f6acc4e22f8f7c49d7ea9e2e4877ab","src/sys/refresh_tz/unix.rs":"7a36ccb9efd23d264727fbb4cffcb5de5ed249b8216bbf6389fe10db22a70d30","src/tests.rs":"4cf40de5c6ffd670b9672ead5b3f25137f8214b1a435d298bd16ce467e72b040","src/time.rs":"da5a1bd6ce70547b8351297a0588c460894148b815f5c2008d61ab4102fb963a","src/utc_date_time.rs":"4bf3d0a07de1b5421bbc72cdfe21a297c311672a5618fec92e0a822295224599","src/utc_offset.rs":"3a9cd709e25837362778c550204486d4b4e6b4ff37b30cd87b4084163d06ed26","src/util.rs":"ccbfa06d000c83d8acef73a4576350305e77a547bbc2c67415a46b65f2c8c7da","src/weekday.rs":"daa82e07fa1d5291b7ab13b5dbcc1375f6ba4f8267abfbfea4fab4dae063d324"},"package":"8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"}
\ No newline at end of file diff --git a/vendor/time/Cargo.lock b/vendor/time/Cargo.lock new file mode 100644 index 00000000..27fa70c7 --- /dev/null +++ b/vendor/time/Cargo.lock @@ -0,0 +1,926 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "deranged" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +dependencies = [ + "powerfmt", + "quickcheck", + "rand 0.8.5", + "rand 0.9.0", + "serde", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "half" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "hermit-abi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +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.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_core 0.9.3", + "zerocopy", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[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.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +dependencies = [ + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.96", + "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 2.0.96", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[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.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "serde_json" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_test" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" +dependencies = [ + "serde", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + +[[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.41" +dependencies = [ + "criterion", + "deranged", + "itoa", + "js-sys", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "quickcheck", + "quickcheck_macros", + "rand 0.8.5", + "rstest", + "rstest_reuse", + "serde", + "serde_json", + "serde_test", + "time-core", + "time-macros", + "trybuild", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +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.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trybuild" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f14b5c02a137632f68194ec657ecb92304138948e8957c932127eb1b58c23be" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[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-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] diff --git a/vendor/time/Cargo.toml b/vendor/time/Cargo.toml new file mode 100644 index 00000000..fdffe750 --- /dev/null +++ b/vendor/time/Cargo.toml @@ -0,0 +1,298 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.67.1" +name = "time" +version = "0.3.41" +authors = [ + "Jacob Pratt <open-source@jhpratt.dev>", + "Time contributors", +] +build = false +include = [ + "src/**/*", + "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 +rustdoc-args = ["--generate-link-to-definition"] +targets = ["x86_64-unknown-linux-gnu"] + +[features] +alloc = ["serde?/alloc"] +default = ["std"] +formatting = [ + "dep:itoa", + "std", + "time-macros?/formatting", +] +large-dates = ["time-macros?/large-dates"] +local-offset = [ + "std", + "dep:libc", + "dep:num_threads", +] +macros = ["dep:time-macros"] +parsing = ["time-macros?/parsing"] +quickcheck = [ + "dep:quickcheck", + "alloc", + "deranged/quickcheck", +] +rand = [ + "dep:rand", + "deranged/rand", +] +serde = [ + "dep:serde", + "time-macros?/serde", + "deranged/serde", +] +serde-human-readable = [ + "serde", + "formatting", + "parsing", +] +serde-well-known = [ + "serde", + "formatting", + "parsing", +] +std = [ + "alloc", + "deranged/std", +] +wasm-bindgen = ["dep:js-sys"] + +[lib] +name = "time" +path = "src/lib.rs" +bench = false + +[dependencies.deranged] +version = "0.4.0" +features = ["powerfmt"] +default-features = false + +[dependencies.itoa] +version = "1.0.1" +optional = true + +[dependencies.num-conv] +version = "0.1.0" + +[dependencies.powerfmt] +version = "0.2.0" +default-features = false + +[dependencies.quickcheck] +version = "1.0.3" +optional = true +default-features = false + +[dependencies.rand] +version = "0.8.4" +optional = true +default-features = false + +[dependencies.serde] +version = "1.0.184" +optional = true +default-features = false + +[dependencies.time-core] +version = "=0.1.4" + +[dependencies.time-macros] +version = "=0.2.22" +optional = true + +[dev-dependencies.num-conv] +version = "0.1.0" + +[dev-dependencies.quickcheck_macros] +version = "1.0.0" + +[dev-dependencies.rand] +version = "0.8.4" +default-features = false + +[dev-dependencies.rstest] +version = "0.23.0" +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.22" + +[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.5.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.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" diff --git a/vendor/time/LICENSE-Apache b/vendor/time/LICENSE-Apache new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/vendor/time/LICENSE-Apache @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/time/LICENSE-MIT b/vendor/time/LICENSE-MIT new file mode 100644 index 00000000..663cb2a1 --- /dev/null +++ b/vendor/time/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Jacob Pratt et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/time/README.md b/vendor/time/README.md new file mode 100644 index 00000000..22192563 --- /dev/null +++ b/vendor/time/README.md @@ -0,0 +1,51 @@ +# time + +[](https://www.whatrustisit.com) +[](https://crates.io/crates/time) +[](https://github.com/time-rs/time/actions) +[](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. diff --git a/vendor/time/src/date.rs b/vendor/time/src/date.rs new file mode 100644 index 00000000..3772c2da --- /dev/null +++ b/vendor/time/src/date.rs @@ -0,0 +1,1490 @@ +//! The [`Date`] struct and its associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::num::{NonZeroI32, NonZeroU8}; +use core::ops::{Add, Sub}; +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, expect_opt, impl_add_assign, + impl_sub_assign, +}; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +use crate::util::{days_in_year, is_leap_year, weeks_in_year}; +use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday}; + +type Year = RangedI32<MIN_YEAR, MAX_YEAR>; + +/// 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: NonZeroI32, +} + +impl Date { + /// The minimum valid `Date`. + /// + /// The value of this may vary depending on the feature flags enabled. + // Safety: `ordinal` is not zero. + #[allow(clippy::undocumented_unsafe_blocks)] + 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. + #[allow(clippy::undocumented_unsafe_blocks)] + pub const MAX: Self = + unsafe { Self::__from_ordinal_date_unchecked(MAX_YEAR, 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 + 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 <= days_in_year(year)); + debug_assert!(crate::util::is_leap_year(year) == is_leap_year); + + Self { + // Safety: `ordinal` is not zero. + value: unsafe { + NonZeroI32::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 + /// + /// `ordinal` must be non-zero and at most the number of days in `year`. `year` should be in the + /// range `MIN_YEAR..=MAX_YEAR`, but this is not a safety invariant. + #[doc(hidden)] + pub const unsafe fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { + // Safety: The caller must guarantee that `ordinal` is not zero. + unsafe { Self::from_parts(year, 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. + /// ``` + pub const fn from_calendar_date( + year: i32, + month: Month, + day: u8, + ) -> Result<Self, error::ComponentRange> { + /// 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); + match day { + 1..=28 => {} + 29..=31 if day <= month.length(year) => {} + _ => { + return Err(error::ComponentRange { + name: "day", + minimum: 1, + maximum: month.length(year) as i64, + value: day as i64, + conditional_message: Some("for the given month and year"), + }); + } + } + + // Safety: `ordinal` is not zero. + Ok(unsafe { + Self::__from_ordinal_date_unchecked( + year, + DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] + + day as u16, + ) + }) + } + + /// Attempt to create a `Date` from the year and ordinal day number. + /// + /// ```rust + /// # use time::Date; + /// assert!(Date::from_ordinal_date(2019, 1).is_ok()); + /// assert!(Date::from_ordinal_date(2019, 365).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Date; + /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year. + /// ``` + pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> { + ensure_ranged!(Year: year); + match ordinal { + 1..=365 => {} + 366 if is_leap_year(year) => {} + _ => { + return Err(error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(year) as i64, + value: ordinal as i64, + conditional_message: Some("for the given year"), + }); + } + } + + // Safety: `ordinal` is not zero. + Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }) + } + + /// Attempt to create a `Date` from the ISO year, week, and weekday. + /// + /// ```rust + /// # use time::{Date, Weekday::*}; + /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok()); + /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok()); + /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::{Date, Weekday::*}; + /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks. + /// ``` + pub const fn from_iso_week_date( + year: i32, + week: u8, + weekday: Weekday, + ) -> Result<Self, error::ComponentRange> { + ensure_ranged!(Year: year); + match week { + 1..=52 => {} + 53 if week <= weeks_in_year(year) => {} + _ => { + return Err(error::ComponentRange { + name: "week", + minimum: 1, + maximum: weeks_in_year(year) as i64, + value: week as i64, + conditional_message: Some("for the given year"), + }); + } + } + + let adj_year = year - 1; + let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) + + div_floor!(adj_year, 400); + let jan_4 = match (raw % 7) as i8 { + -6 | 1 => 8, + -5 | 2 => 9, + -4 | 3 => 10, + -3 | 4 => 4, + -2 | 5 => 5, + -1 | 6 => 6, + _ => 7, + }; + let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; + + Ok(if ordinal <= 0 { + // Safety: `ordinal` is not zero. + unsafe { + Self::__from_ordinal_date_unchecked( + year - 1, + (ordinal as u16).wrapping_add(days_in_year(year - 1)), + ) + } + } else if ordinal > days_in_year(year) as i16 { + // Safety: `ordinal` is not zero. + unsafe { + Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) + } + } else { + // Safety: `ordinal` is not zero. + unsafe { Self::__from_ordinal_date_unchecked(year, ordinal as u16) } + }) + } + + /// Create a `Date` from the Julian day. + /// + /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is + /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). + /// + /// ```rust + /// # use time::Date; + /// # 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")] + pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> { + 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. + 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 S: i32 = 2_500; + const K: i32 = 719_468 + 146_097 * S; + const L: i32 = 400 * S; + + let julian_day = julian_day - 2_440_588; + let n = (julian_day + K) as u32; + + let n_1 = 4 * n + 3; + let c = n_1 / 146_097; + let n_c = n_1 % 146_097 / 4; + + let n_2 = 4 * n_c + 3; + let p_2 = 2_939_745 * n_2 as u64; + let z = (p_2 >> 32) as u32; + let n_y = p_2 as u32 / 2_939_745 / 4; + let y = 100 * c + z; + + let j = n_y >= 306; + let y_g = y as i32 - L + j as i32; + + let is_leap_year = is_leap_year(y_g); + let ordinal = if j { + n_y - 305 + } else { + n_y + 60 + is_leap_year as u32 + }; + + // Safety: `ordinal` is not zero and `is_leap_year` is correct, so long as the Julian day + // number is in range. + unsafe { Self::from_parts(y_g, is_leap_year, 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. + 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); + /// ``` + 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); + /// ``` + 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(NonZeroU8::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); + /// ``` + 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); + /// ``` + pub const fn ordinal(self) -> u16 { + (self.value.get() & 0x1FF) as u16 + } + + /// Get the ISO 8601 year and week number. + pub(crate) const fn iso_year_week(self) -> (i32, u8) { + let (year, ordinal) = self.to_ordinal_date(); + + match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as 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); + /// ``` + pub const fn iso_week(self) -> u8 { + self.iso_year_week().1 + } + + /// Get the week number where week 1 begins on the first Sunday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::date; + /// assert_eq!(date!(2019-01-01).sunday_based_week(), 0); + /// assert_eq!(date!(2020-01-01).sunday_based_week(), 0); + /// assert_eq!(date!(2020-12-31).sunday_based_week(), 52); + /// assert_eq!(date!(2021-01-01).sunday_based_week(), 0); + /// ``` + pub const fn sunday_based_week(self) -> u8 { + ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as 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); + /// ``` + pub const fn monday_based_week(self) -> u8 { + ((self.ordinal() as i16 - 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) + /// ); + /// ``` + 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(NonZeroU8::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)); + /// ``` + 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)); + /// ``` + 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); + /// ``` + 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); + /// ``` + pub const fn next_day(self) -> Option<Self> { + if self.ordinal() == 366 || (self.ordinal() == 365 && !self.is_in_leap_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 { NonZeroI32::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); + /// ``` + pub const fn previous_day(self) -> Option<Self> { + if self.ordinal() != 1 { + Some(Self { + // Safety: `ordinal` is not zero. + value: unsafe { NonZeroI32::new_unchecked(self.value.get() - 1) }, + }) + } else if self.value.get() == Self::MIN.value.get() { + None + } else { + // Safety: `ordinal` is not zero. + Some(unsafe { + Self::__from_ordinal_date_unchecked(self.year() - 1, days_in_year(self.year() - 1)) + }) + } + } + + /// 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) + /// ); + /// ``` + pub const fn next_occurrence(self, weekday: Weekday) -> Self { + expect_opt!( + self.checked_next_occurrence(weekday), + "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) + /// ); + /// ``` + pub const fn prev_occurrence(self, weekday: Weekday) -> Self { + expect_opt!( + self.checked_prev_occurrence(weekday), + "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) + /// ); + /// ``` + pub const fn nth_next_occurrence(self, weekday: Weekday, n: u8) -> Self { + expect_opt!( + self.checked_nth_next_occurrence(weekday, n), + "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) + /// ); + /// ``` + pub const fn nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Self { + expect_opt!( + self.checked_nth_prev_occurrence(weekday, n), + "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); + /// ``` + 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)) + /// ); + /// ``` + pub const fn checked_add(self, duration: Duration) -> Option<Self> { + 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)) + /// ); + /// ``` + pub const fn checked_add_std(self, duration: StdDuration) -> Option<Self> { + let whole_days = duration.as_secs() / Second::per(Day) as u64; + 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)) + /// ); + /// ``` + pub const fn checked_sub(self, duration: Duration) -> Option<Self> { + 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)) + /// ); + /// ``` + pub const fn checked_sub_std(self, duration: StdDuration) -> Option<Self> { + let whole_days = duration.as_secs() / Second::per(Day) as u64; + 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. + pub(crate) const fn checked_next_occurrence(self, weekday: Weekday) -> Option<Self> { + 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. + pub(crate) const fn checked_prev_occurrence(self, weekday: Weekday) -> Option<Self> { + 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`. + pub(crate) const fn checked_nth_next_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> { + 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`. + pub(crate) const fn checked_nth_prev_occurrence(self, weekday: Weekday, n: u8) -> Option<Self> { + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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 + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { + ensure_ranged!(Year: year); + + let ordinal = self.ordinal(); + + // Dates in January and February are unaffected by leap years. + if ordinal <= 59 { + // Safety: `ordinal` is not zero. + return Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }); + } + + match (self.is_in_leap_year(), is_leap_year(year)) { + (false, false) | (true, true) => { + // Safety: `ordinal` is not zero. + Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal) }) + } + // February 29 does not exist in common years. + (true, false) if ordinal == 60 => Err(error::ComponentRange { + name: "day", + value: 29, + minimum: 1, + maximum: 28, + conditional_message: Some("for the given month and year"), + }), + // 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. + (false, true) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, ordinal + 1) }), + // We're going from a leap year to a common year. Shift dates in January and + // February by one day. + // Safety: `ordinal` is not zero. + (true, false) => Ok(unsafe { Self::__from_ordinal_date_unchecked(year, 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 + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { + let (year, _, day) = self.to_calendar_date(); + Self::from_calendar_date(year, month, day) + } + + /// 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 + /// ``` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { + match day { + 1..=28 => {} + 29..=31 if day <= self.month().length(self.year()) => {} + _ => { + return Err(error::ComponentRange { + name: "day", + minimum: 1, + maximum: self.month().length(self.year()) as i64, + value: day as i64, + conditional_message: Some("for the given month and year"), + }); + } + } + + // Safety: `ordinal` is not zero. + Ok(unsafe { + Self::__from_ordinal_date_unchecked( + self.year(), + (self.ordinal() as i16 - self.day() as i16 + day as i16) as u16, + ) + }) + } + + /// 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 + /// ```` + #[must_use = "This method does not mutate the original `Date`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> { + match ordinal { + 1..=365 => {} + 366 if self.is_in_leap_year() => {} + _ => { + return Err(error::ComponentRange { + name: "ordinal", + minimum: 1, + maximum: days_in_year(self.year()) as i64, + value: ordinal as i64, + conditional_message: Some("for the given year"), + }); + } + } + + // Safety: `ordinal` is in range. + Ok(unsafe { Self::__from_ordinal_date_unchecked(self.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)); + /// ``` + pub const fn midnight(self) -> PrimitiveDateTime { + PrimitiveDateTime::new(self, Time::MIDNIGHT) + } + + /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`]. + /// + /// ```rust + /// # use time_macros::{date, datetime, time}; + /// assert_eq!( + /// date!(1970-01-01).with_time(time!(0:00)), + /// datetime!(1970-01-01 0:00), + /// ); + /// ``` + pub const fn with_time(self, time: Time) -> PrimitiveDateTime { + PrimitiveDateTime::new(self, time) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms(0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms(24, 0, 0).is_err()); + /// ``` + pub const fn with_hms( + self, + hour: u8, + minute: u8, + second: u8, + ) -> Result<PrimitiveDateTime, error::ComponentRange> { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms(hour, minute, second)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_milli(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_milli(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_milli( + self, + hour: u8, + minute: u8, + second: u8, + millisecond: u16, + ) -> Result<PrimitiveDateTime, error::ComponentRange> { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms_milli(hour, minute, second, millisecond)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_micro(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_micro(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_micro( + self, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + ) -> Result<PrimitiveDateTime, error::ComponentRange> { + Ok(PrimitiveDateTime::new( + self, + const_try!(Time::from_hms_micro(hour, minute, second, microsecond)), + )) + } + + /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. + /// + /// ```rust + /// # use time_macros::date; + /// assert!(date!(1970-01-01).with_hms_nano(0, 0, 0, 0).is_ok()); + /// assert!(date!(1970-01-01).with_hms_nano(24, 0, 0, 0).is_err()); + /// ``` + pub const fn with_hms_nano( + self, + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Result<PrimitiveDateTime, error::ComponentRange> { + 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). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into(output, Some(self), None, None) + } + + /// 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>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(Some(self), None, None) + } +} + +#[cfg(feature = "parsing")] +impl Date { + /// Parse a `Date` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::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>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_date(input.as_bytes()) + } +} + +mod private { + #[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; + + 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::<usize>() + + 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, + }, + ) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for Date { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + fmt::Display::fmt(self, f) + } +} + +impl Add<Duration> for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: Duration) -> Self::Output { + self.checked_add(duration) + .expect("overflow adding duration to date") + } +} + +impl Add<StdDuration> for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: StdDuration) -> Self::Output { + self.checked_add_std(duration) + .expect("overflow adding duration to date") + } +} + +impl_add_assign!(Date: Duration, StdDuration); + +impl Sub<Duration> for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: Duration) -> Self::Output { + self.checked_sub(duration) + .expect("overflow subtracting duration from date") + } +} + +impl Sub<StdDuration> for Date { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: StdDuration) -> Self::Output { + self.checked_sub_std(duration) + .expect("overflow subtracting duration from date") + } +} + +impl_sub_assign!(Date: Duration, StdDuration); + +impl Sub for Date { + type Output = Duration; + + fn sub(self, other: Self) -> Self::Output { + Duration::days((self.to_julian_day() - other.to_julian_day()).extend()) + } +} diff --git a/vendor/time/src/duration.rs b/vendor/time/src/duration.rs new file mode 100644 index 00000000..b86bfe7c --- /dev/null +++ b/vendor/time/src/duration.rs @@ -0,0 +1,1598 @@ +//! 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, Mul, Neg, Sub, SubAssign}; +use core::time::Duration as StdDuration; +#[cfg(feature = "std")] +use std::time::SystemTime; + +use deranged::RangedI32; +use num_conv::prelude::*; + +use crate::convert::*; +use crate::error; +use crate::internal_macros::{ + const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; +#[cfg(feature = "std")] +#[allow(deprecated)] +use crate::Instant; + +/// By explicitly inserting this enum where padding is expected, the compiler is able to better +/// perform niche value optimization. +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) enum Padding { + #[allow(clippy::missing_docs_in_private_items)] + Optimize, +} + +/// The type of the `nanosecond` field of `Duration`. +type Nanoseconds = + RangedI32<{ -(Nanosecond::per(Second) as i32 - 1) }, { Nanosecond::per(Second) as i32 - 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 { + 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 { + 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, + is_nan = $is_nan:expr, + is_overflow = $is_overflow:expr, + ) => {{ + '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 = <$double_ty>::from(mant) << ($offset + exp); + let nanos_offset = $mant_bits + $offset; + let nanos_tmp = u128::from(Nanosecond::per(Second)) * u128::from(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 + // 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(Second)) { + (0, nanos) + } else { + (1, 0) + } + } else if exp < $mant_bits { + let secs = u64::from(mant >> ($mant_bits - exp)); + let t = <$double_ty>::from((mant << exp) & MANT_MASK); + let nanos_offset = $mant_bits; + let nanos_tmp = <$double_ty>::from(Nanosecond::per(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(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 + let secs = u64::from(mant) << (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 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. + $is_nan + } else { + $is_overflow + }; + + // 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. + 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()); + /// ``` + 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()); + /// ``` + 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()); + /// ``` + 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()); + /// ``` + 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()); + /// ``` + 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. + 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. + 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 ±10<sup>9</sup>, 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. + pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self { + seconds = expect_opt!( + seconds.checked_add(nanoseconds as i64 / Nanosecond::per(Second) as i64), + "overflow constructing `time::Duration`" + ); + nanoseconds %= Nanosecond::per(Second) as i32; + + if seconds > 0 && nanoseconds < 0 { + // `seconds` cannot overflow here because it is positive. + seconds -= 1; + nanoseconds += Nanosecond::per(Second) as i32; + } else if seconds < 0 && nanoseconds > 0 { + // `seconds` cannot overflow here because it is negative. + seconds += 1; + nanoseconds -= Nanosecond::per(Second) as i32; + } + + // 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. + 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(Second) as i32) + }; + } 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(Second) as i32) + }; + } + + 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. + pub const fn weeks(weeks: i64) -> Self { + Self::seconds(expect_opt!( + weeks.checked_mul(Second::per(Week) as i64), + "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. + pub const fn days(days: i64) -> Self { + Self::seconds(expect_opt!( + days.checked_mul(Second::per(Day) as i64), + "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. + pub const fn hours(hours: i64) -> Self { + Self::seconds(expect_opt!( + hours.checked_mul(Second::per(Hour) as i64), + "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. + pub const fn minutes(minutes: i64) -> Self { + Self::seconds(expect_opt!( + minutes.checked_mul(Second::per(Minute) as i64), + "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()); + /// ``` + pub const fn seconds(seconds: i64) -> Self { + Self::new_ranged_unchecked(seconds, Nanoseconds::new_static::<0>()) + } + + /// Creates a new `Duration` from the specified number of seconds represented as `f64`. + /// + /// ```rust + /// # use time::{Duration, ext::NumericalDuration}; + /// assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds()); + /// assert_eq!(Duration::seconds_f64(-0.5), (-0.5).seconds()); + /// ``` + pub fn seconds_f64(seconds: f64) -> Self { + 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, + is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f64`"), + is_overflow = crate::expect_failed("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()); + /// ``` + pub fn seconds_f32(seconds: f32) -> Self { + 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, + is_nan = crate::expect_failed("passed NaN to `time::Duration::seconds_f32`"), + is_overflow = crate::expect_failed("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, + /// ); + /// ``` + pub fn saturating_seconds_f64(seconds: f64) -> Self { + 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, + is_nan = return Self::ZERO, + is_overflow = return if seconds < 0.0 { Self::MIN } else { 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, + /// ); + /// ``` + pub fn saturating_seconds_f32(seconds: f32) -> Self { + 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, + is_nan = return Self::ZERO, + is_overflow = return if seconds < 0.0 { Self::MIN } else { 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); + /// ``` + pub fn checked_seconds_f64(seconds: f64) -> Option<Self> { + Some(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, + is_nan = return None, + is_overflow = return 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); + /// ``` + pub fn checked_seconds_f32(seconds: f32) -> Option<Self> { + Some(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, + is_nan = return None, + is_overflow = return 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()); + /// ``` + 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(Second) as i64, + (milliseconds % Millisecond::per(Second) as i64 + * Nanosecond::per(Millisecond) as i64) 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()); + /// ``` + 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(Second) as i64, + (microseconds % Microsecond::per(Second) as i64 + * Nanosecond::per(Microsecond) as i64) 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); + /// ``` + 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(Second) as i64, + (nanoseconds % Nanosecond::per(Second) as i64) as i32, + ) + } + } + + /// Create a new `Duration` with the given number of nanoseconds. + /// + /// As the input range cannot be fully mapped to the output, this should only be used where it's + /// known to result in a valid value. + pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self { + let seconds = nanoseconds / Nanosecond::per(Second) as i128; + let nanoseconds = nanoseconds % Nanosecond::per(Second) as i128; + + if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 { + crate::expect_failed("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); + /// ``` + pub const fn whole_weeks(self) -> i64 { + self.whole_seconds() / Second::per(Week) as i64 + } + + /// Get the number of whole days in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.days().whole_days(), 1); + /// assert_eq!((-1).days().whole_days(), -1); + /// assert_eq!(23.hours().whole_days(), 0); + /// assert_eq!((-23).hours().whole_days(), 0); + /// ``` + pub const fn whole_days(self) -> i64 { + self.whole_seconds() / Second::per(Day) as i64 + } + + /// Get the number of whole hours in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.hours().whole_hours(), 1); + /// assert_eq!((-1).hours().whole_hours(), -1); + /// assert_eq!(59.minutes().whole_hours(), 0); + /// assert_eq!((-59).minutes().whole_hours(), 0); + /// ``` + pub const fn whole_hours(self) -> i64 { + self.whole_seconds() / Second::per(Hour) as i64 + } + + /// Get the number of whole minutes in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.minutes().whole_minutes(), 1); + /// assert_eq!((-1).minutes().whole_minutes(), -1); + /// assert_eq!(59.seconds().whole_minutes(), 0); + /// assert_eq!((-59).seconds().whole_minutes(), 0); + /// ``` + pub const fn whole_minutes(self) -> i64 { + self.whole_seconds() / Second::per(Minute) as i64 + } + + /// Get the number of whole seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_seconds(), 1); + /// assert_eq!((-1).seconds().whole_seconds(), -1); + /// assert_eq!(1.minutes().whole_seconds(), 60); + /// assert_eq!((-1).minutes().whole_seconds(), -60); + /// ``` + pub const fn whole_seconds(self) -> i64 { + self.seconds + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f64(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f64(), -1.5); + /// ``` + pub fn as_seconds_f64(self) -> f64 { + self.seconds as f64 + self.nanoseconds.get() as f64 / Nanosecond::per(Second) as f64 + } + + /// Get the number of fractional seconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.5.seconds().as_seconds_f32(), 1.5); + /// assert_eq!((-1.5).seconds().as_seconds_f32(), -1.5); + /// ``` + pub fn as_seconds_f32(self) -> f32 { + self.seconds as f32 + self.nanoseconds.get() as f32 / Nanosecond::per(Second) as f32 + } + + /// Get the number of whole milliseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.seconds().whole_milliseconds(), 1_000); + /// assert_eq!((-1).seconds().whole_milliseconds(), -1_000); + /// assert_eq!(1.milliseconds().whole_milliseconds(), 1); + /// assert_eq!((-1).milliseconds().whole_milliseconds(), -1); + /// ``` + pub const fn whole_milliseconds(self) -> i128 { + self.seconds as i128 * Millisecond::per(Second) as i128 + + self.nanoseconds.get() as i128 / Nanosecond::per(Millisecond) as i128 + } + + /// 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); + /// ``` + // Allow the lint, as the value is guaranteed to be less than 1000. + pub const fn subsec_milliseconds(self) -> i16 { + (self.nanoseconds.get() / Nanosecond::per(Millisecond) as i32) 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); + /// ``` + pub const fn whole_microseconds(self) -> i128 { + self.seconds as i128 * Microsecond::per(Second) as i128 + + self.nanoseconds.get() as i128 / Nanosecond::per(Microsecond) as i128 + } + + /// 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); + /// ``` + pub const fn subsec_microseconds(self) -> i32 { + self.nanoseconds.get() / Nanosecond::per(Microsecond) as i32 + } + + /// Get the number of nanoseconds in the duration. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// assert_eq!(1.microseconds().whole_nanoseconds(), 1_000); + /// assert_eq!((-1).microseconds().whole_nanoseconds(), -1_000); + /// assert_eq!(1.nanoseconds().whole_nanoseconds(), 1); + /// assert_eq!((-1).nanoseconds().whole_nanoseconds(), -1); + /// ``` + pub const fn whole_nanoseconds(self) -> i128 { + self.seconds as i128 * Nanosecond::per(Second) as i128 + 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); + /// ``` + pub const fn subsec_nanoseconds(self) -> i32 { + self.nanoseconds.get() + } + + /// Get the number of nanoseconds past the number of whole seconds. + #[cfg(feature = "quickcheck")] + 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())); + /// ``` + pub const fn checked_add(self, rhs: Self) -> Option<Self> { + 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(Second) as i32 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; + seconds = const_try_opt!(seconds.checked_add(1)); + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 + { + nanoseconds += Nanosecond::per(Second) as i32; + 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())); + /// ``` + pub const fn checked_sub(self, rhs: Self) -> Option<Self> { + 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(Second) as i32 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; + seconds = const_try_opt!(seconds.checked_add(1)); + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 + { + nanoseconds += Nanosecond::per(Second) as i32; + 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); + /// ``` + pub const fn checked_mul(self, rhs: i32) -> Option<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(Second) as i64; + let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) 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); + /// ``` + pub const fn checked_div(self, rhs: i32) -> Option<Self> { + 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(Second) as i64) + 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); + /// ``` + pub const fn checked_neg(self) -> Option<Self> { + 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); + /// ``` + 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(Second) as i32 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; + seconds = match seconds.checked_add(1) { + Some(seconds) => seconds, + None => return Self::MAX, + }; + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 + { + nanoseconds += Nanosecond::per(Second) as i32; + 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()); + /// ``` + 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(Second) as i32 || seconds < 0 && nanoseconds > 0 { + nanoseconds -= Nanosecond::per(Second) as i32; + seconds = match seconds.checked_add(1) { + Some(seconds) => seconds, + None => return Self::MAX, + }; + } else if nanoseconds <= -(Nanosecond::per(Second) as i32) || seconds > 0 && nanoseconds < 0 + { + nanoseconds += Nanosecond::per(Second) as i32; + 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); + /// ``` + 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(Second) as i64; + let nanoseconds = (total_nanos % Nanosecond::per(Second) as i64) 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. + #[doc(hidden)] + #[cfg(feature = "std")] + #[deprecated( + since = "0.3.32", + note = "extremely limited use case, not intended for benchmarking" + )] + #[allow(deprecated)] + pub fn time_fn<T>(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(Day) as f64); + item!("h", seconds / Second::per(Hour) as f64); + item!("m", seconds / Second::per(Minute) as f64); + item!("s", seconds); + item!("ms", seconds * Millisecond::per(Second) as f64); + item!("µs", seconds * Microsecond::per(Second) as f64); + item!("ns", seconds * Nanosecond::per(Second) as f64); + } 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(Day).extend::<u64>())?; + item!( + "h", + seconds / Second::per(Hour).extend::<u64>() % Hour::per(Day).extend::<u64>() + )?; + item!( + "m", + seconds / Second::per(Minute).extend::<u64>() % Minute::per(Hour).extend::<u64>() + )?; + item!("s", seconds % Second::per(Minute).extend::<u64>())?; + item!("ms", nanoseconds / Nanosecond::per(Millisecond))?; + item!( + "µs", + nanoseconds / Nanosecond::per(Microsecond).extend::<u32>() + % Microsecond::per(Millisecond).extend::<u32>() + )?; + item!( + "ns", + nanoseconds % Nanosecond::per(Microsecond).extend::<u32>() + )?; + } + + Ok(()) + } +} + +impl TryFrom<StdDuration> for Duration { + type Error = error::ConversionRange; + + fn try_from(original: StdDuration) -> Result<Self, error::ConversionRange> { + Ok(Self::new( + original + .as_secs() + .try_into() + .map_err(|_| error::ConversionRange)?, + original.subsec_nanos().cast_signed(), + )) + } +} + +impl TryFrom<Duration> for StdDuration { + type Error = error::ConversionRange; + + fn try_from(duration: Duration) -> Result<Self, error::ConversionRange> { + 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. + fn add(self, rhs: Self) -> Self::Output { + self.checked_add(rhs) + .expect("overflow when adding durations") + } +} + +impl Add<StdDuration> for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + 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<Duration> for StdDuration { + type Output = Duration; + + fn add(self, rhs: Duration) -> Self::Output { + rhs + self + } +} + +impl_add_assign!(Duration: Self, StdDuration); + +impl AddAssign<Duration> for StdDuration { + /// # Panics + /// + /// This may panic if the resulting addition cannot be represented. + 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; + + 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. + fn sub(self, rhs: Self) -> Self::Output { + self.checked_sub(rhs) + .expect("overflow when subtracting durations") + } +} + +impl Sub<StdDuration> for Duration { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: StdDuration) -> Self::Output { + self - Self::try_from(rhs) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + } +} + +impl Sub<Duration> for StdDuration { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Duration) -> Self::Output { + Duration::try_from(self) + .expect("overflow converting `std::time::Duration` to `time::Duration`") + - rhs + } +} + +impl_sub_assign!(Duration: Self, StdDuration); + +impl SubAssign<Duration> for StdDuration { + /// # Panics + /// + /// This may panic if the resulting subtraction can not be represented. + fn sub_assign(&mut self, rhs: Duration) { + *self = (*self - rhs).try_into().expect( + "Cannot represent a resulting duration in std. Try `let x = x - rhs;`, which will \ + change the type.", + ); + } +} + +/// Implement `Mul` (reflexively) and `Div` for `Duration` for various types. +macro_rules! duration_mul_div_int { + ($($type:ty),+) => {$( + impl Mul<$type> for Duration { + type Output = Self; + + fn mul(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128( + self.whole_nanoseconds() + .checked_mul(rhs.cast_signed().extend::<i128>()) + .expect("overflow when multiplying duration") + ) + } + } + + impl Mul<Duration> for $type { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } + } + + impl Div<$type> for Duration { + type Output = Self; + + fn div(self, rhs: $type) -> Self::Output { + Self::nanoseconds_i128( + self.whole_nanoseconds() / rhs.cast_signed().extend::<i128>() + ) + } + } + )+}; +} +duration_mul_div_int![i8, i16, i32, u8, u16, u32]; + +impl Mul<f32> for Duration { + type Output = Self; + + fn mul(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() * rhs) + } +} + +impl Mul<Duration> for f32 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } +} + +impl Mul<f64> for Duration { + type Output = Self; + + fn mul(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() * rhs) + } +} + +impl Mul<Duration> for f64 { + type Output = Duration; + + fn mul(self, rhs: Duration) -> Self::Output { + rhs * self + } +} + +impl_mul_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); + +impl Div<f32> for Duration { + type Output = Self; + + fn div(self, rhs: f32) -> Self::Output { + Self::seconds_f32(self.as_seconds_f32() / rhs) + } +} + +impl Div<f64> for Duration { + type Output = Self; + + fn div(self, rhs: f64) -> Self::Output { + Self::seconds_f64(self.as_seconds_f64() / rhs) + } +} + +impl_div_assign!(Duration: i8, i16, i32, u8, u16, u32, f32, f64); + +impl Div for Duration { + type Output = f64; + + fn div(self, rhs: Self) -> Self::Output { + self.as_seconds_f64() / rhs.as_seconds_f64() + } +} + +impl Div<StdDuration> for Duration { + type Output = f64; + + fn div(self, rhs: StdDuration) -> Self::Output { + self.as_seconds_f64() / rhs.as_secs_f64() + } +} + +impl Div<Duration> for StdDuration { + type Output = f64; + + fn div(self, rhs: Duration) -> Self::Output { + self.as_secs_f64() / rhs.as_seconds_f64() + } +} + +impl PartialEq<StdDuration> for Duration { + fn eq(&self, rhs: &StdDuration) -> bool { + Ok(*self) == Self::try_from(*rhs) + } +} + +impl PartialEq<Duration> for StdDuration { + fn eq(&self, rhs: &Duration) -> bool { + rhs == self + } +} + +impl PartialOrd<StdDuration> for Duration { + fn partial_cmp(&self, rhs: &StdDuration) -> Option<Ordering> { + 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<Duration> for StdDuration { + fn partial_cmp(&self, rhs: &Duration) -> Option<Ordering> { + rhs.partial_cmp(self).map(Ordering::reverse) + } +} + +impl Sum for Duration { + fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { + iter.reduce(|a, b| a + b).unwrap_or_default() + } +} + +impl<'a> Sum<&'a Self> for Duration { + fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self { + iter.copied().sum() + } +} + +#[cfg(feature = "std")] +impl Add<Duration> for SystemTime { + type Output = Self; + + 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() + } + } +} + +impl_add_assign!(SystemTime: #[cfg(feature = "std")] Duration); + +#[cfg(feature = "std")] +impl Sub<Duration> for SystemTime { + type Output = Self; + + 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() + } + } +} + +impl_sub_assign!(SystemTime: #[cfg(feature = "std")] Duration); diff --git a/vendor/time/src/error/component_range.rs b/vendor/time/src/error/component_range.rs new file mode 100644 index 00000000..dc980234 --- /dev/null +++ b/vendor/time/src/error/component_range.rs @@ -0,0 +1,114 @@ +//! Component range error + +use core::{fmt, hash}; + +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, Eq)] +pub struct ComponentRange { + /// Name of the component. + pub(crate) name: &'static str, + /// Minimum allowed value, inclusive. + pub(crate) minimum: i64, + /// Maximum allowed value, inclusive. + pub(crate) maximum: i64, + /// Value that was provided. + pub(crate) value: i64, + /// The minimum and/or maximum value is conditional on the value of other + /// parameters. + pub(crate) conditional_message: Option<&'static str>, +} + +impl ComponentRange { + /// Obtain the name of the component whose value was out of range. + pub const fn name(self) -> &'static str { + self.name + } + + /// Whether the value's permitted range is conditional, i.e. whether an input with this + /// value could have succeeded if the values of other components were different. + pub const fn is_conditional(self) -> bool { + self.conditional_message.is_some() + } +} + +impl PartialEq for ComponentRange { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + && self.minimum == other.minimum + && self.maximum == other.maximum + && self.value == other.value + // Skip the contents of the message when comparing for equality. + && self.conditional_message.is_some() == other.conditional_message.is_some() + } +} + +impl hash::Hash for ComponentRange { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.name.hash(state); + self.minimum.hash(state); + self.maximum.hash(state); + self.value.hash(state); + // Skip the contents of the message when comparing for equality. + self.conditional_message.is_some().hash(state); + } +} + +impl fmt::Display for ComponentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} must be in the range {}..={}", + self.name, self.minimum, self.maximum + )?; + + if let Some(message) = self.conditional_message { + write!(f, " {message}")?; + } + + Ok(()) + } +} + +impl From<ComponentRange> for crate::Error { + fn from(original: ComponentRange) -> Self { + Self::ComponentRange(original) + } +} + +impl TryFrom<crate::Error> for ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::ComponentRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +/// **This trait implementation is deprecated and will be removed in a future breaking release.** +#[cfg(feature = "serde")] +impl serde::de::Expected for ComponentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "a value in the range {}..={}", + self.minimum, self.maximum + ) + } +} + +#[cfg(feature = "serde")] +impl ComponentRange { + /// Convert the error to a deserialization error. + pub(crate) fn into_de_error<E: serde::de::Error>(self) -> E { + E::invalid_value(serde::de::Unexpected::Signed(self.value), &self) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ComponentRange {} diff --git a/vendor/time/src/error/conversion_range.rs b/vendor/time/src/error/conversion_range.rs new file mode 100644 index 00000000..d6d9243e --- /dev/null +++ b/vendor/time/src/error/conversion_range.rs @@ -0,0 +1,36 @@ +//! 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Source value is out of range for the target type") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConversionRange {} + +impl From<ConversionRange> for crate::Error { + fn from(err: ConversionRange) -> Self { + Self::ConversionRange(err) + } +} + +impl TryFrom<crate::Error> for ConversionRange { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::ConversionRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/different_variant.rs b/vendor/time/src/error/different_variant.rs new file mode 100644 index 00000000..22e21cb0 --- /dev/null +++ b/vendor/time/src/error/different_variant.rs @@ -0,0 +1,34 @@ +//! 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "value was of a different variant than required") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DifferentVariant {} + +impl From<DifferentVariant> for crate::Error { + fn from(err: DifferentVariant) -> Self { + Self::DifferentVariant(err) + } +} + +impl TryFrom<crate::Error> for DifferentVariant { + type Error = Self; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::DifferentVariant(err) => Ok(err), + _ => Err(Self), + } + } +} diff --git a/vendor/time/src/error/format.rs b/vendor/time/src/error/format.rs new file mode 100644 index 00000000..906a58c0 --- /dev/null +++ b/vendor/time/src/error/format.rs @@ -0,0 +1,113 @@ +//! Error formatting a struct + +use alloc::boxed::Box; +use core::fmt; +use std::io; + +use crate::error; + +/// An error occurred when formatting. +#[non_exhaustive] +#[derive(Debug)] +pub enum Format { + /// The type being formatted does not contain sufficient information to format a component. + #[non_exhaustive] + InsufficientTypeInformation, + /// The component named has a value that cannot be formatted into the requested format. + /// + /// This variant is only returned when using well-known formats. + InvalidComponent(&'static str), + /// A component provided was out of range. + ComponentRange(Box<error::ComponentRange>), + /// A value of `std::io::Error` was returned internally. + StdIo(io::Error), +} + +impl fmt::Display for Format { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientTypeInformation => f.write_str( + "The type being formatted does not contain sufficient information to format a \ + component.", + ), + Self::InvalidComponent(component) => write!( + f, + "The {component} component cannot be formatted into the requested format." + ), + Self::ComponentRange(err) => err.fmt(f), + Self::StdIo(err) => err.fmt(f), + } + } +} + +impl From<error::ComponentRange> for Format { + fn from(err: error::ComponentRange) -> Self { + Self::ComponentRange(Box::new(err)) + } +} + +impl From<io::Error> for Format { + fn from(err: io::Error) -> Self { + Self::StdIo(err) + } +} + +impl TryFrom<Format> for error::ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: Format) -> Result<Self, Self::Error> { + match err { + Format::ComponentRange(err) => Ok(*err), + _ => Err(error::DifferentVariant), + } + } +} + +impl TryFrom<Format> for io::Error { + type Error = error::DifferentVariant; + + fn try_from(err: Format) -> Result<Self, Self::Error> { + match err { + Format::StdIo(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Format { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InsufficientTypeInformation | Self::InvalidComponent(_) => None, + Self::ComponentRange(err) => Some(&**err), + Self::StdIo(err) => Some(err), + } + } +} + +impl From<Format> for crate::Error { + fn from(original: Format) -> Self { + Self::Format(original) + } +} + +impl TryFrom<crate::Error> for Format { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::Format(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "serde")] +impl Format { + /// Obtain an error type for the serializer. + #[doc(hidden)] // Exposed only for the `declare_format_string` macro + pub fn into_invalid_serde_value<S: serde::Serializer>(self) -> S::Error { + use serde::ser::Error; + S::Error::custom(self) + } +} diff --git a/vendor/time/src/error/indeterminate_offset.rs b/vendor/time/src/error/indeterminate_offset.rs new file mode 100644 index 00000000..d25d4164 --- /dev/null +++ b/vendor/time/src/error/indeterminate_offset.rs @@ -0,0 +1,35 @@ +//! 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("The system's UTC offset could not be determined") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for IndeterminateOffset {} + +impl From<IndeterminateOffset> for crate::Error { + fn from(err: IndeterminateOffset) -> Self { + Self::IndeterminateOffset(err) + } +} + +impl TryFrom<crate::Error> for IndeterminateOffset { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::IndeterminateOffset(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/invalid_format_description.rs b/vendor/time/src/error/invalid_format_description.rs new file mode 100644 index 00000000..635fee75 --- /dev/null +++ b/vendor/time/src/error/invalid_format_description.rs @@ -0,0 +1,132 @@ +//! Invalid format description + +use alloc::string::String; +use core::fmt; + +use crate::error; + +/// The format description provided was not valid. +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InvalidFormatDescription { + /// There was a bracket pair that was opened but not closed. + #[non_exhaustive] + UnclosedOpeningBracket { + /// The zero-based index of the opening bracket. + index: usize, + }, + /// A component name is not valid. + #[non_exhaustive] + InvalidComponentName { + /// The name of the invalid component name. + name: String, + /// The zero-based index the component name starts at. + index: usize, + }, + /// A modifier is not valid. + #[non_exhaustive] + InvalidModifier { + /// The value of the invalid modifier. + value: String, + /// The zero-based index the modifier starts at. + index: usize, + }, + /// A component name is missing. + #[non_exhaustive] + MissingComponentName { + /// The zero-based index where the component name should start. + index: usize, + }, + /// A required modifier is missing. + #[non_exhaustive] + MissingRequiredModifier { + /// The name of the modifier that is missing. + name: &'static str, + /// The zero-based index of the component. + index: usize, + }, + /// Something was expected, but not found. + #[non_exhaustive] + Expected { + /// What was expected to be present, but wasn't. + what: &'static str, + /// The zero-based index the item was expected to be found at. + index: usize, + }, + /// Certain behavior is not supported in the given context. + #[non_exhaustive] + NotSupported { + /// The behavior that is not supported. + what: &'static str, + /// The context in which the behavior is not supported. + context: &'static str, + /// The zero-based index the error occurred at. + index: usize, + }, +} + +impl From<InvalidFormatDescription> for crate::Error { + fn from(original: InvalidFormatDescription) -> Self { + Self::InvalidFormatDescription(original) + } +} + +impl TryFrom<crate::Error> for InvalidFormatDescription { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::InvalidFormatDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl fmt::Display for InvalidFormatDescription { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use InvalidFormatDescription::*; + match self { + UnclosedOpeningBracket { index } => { + write!(f, "unclosed opening bracket at byte index {index}") + } + InvalidComponentName { name, index } => { + write!(f, "invalid component name `{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}" + ) + } + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidFormatDescription {} diff --git a/vendor/time/src/error/invalid_variant.rs b/vendor/time/src/error/invalid_variant.rs new file mode 100644 index 00000000..396a749a --- /dev/null +++ b/vendor/time/src/error/invalid_variant.rs @@ -0,0 +1,34 @@ +//! 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "value was not a valid variant") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for InvalidVariant {} + +impl From<InvalidVariant> for crate::Error { + fn from(err: InvalidVariant) -> Self { + Self::InvalidVariant(err) + } +} + +impl TryFrom<crate::Error> for InvalidVariant { + type Error = crate::error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::InvalidVariant(err) => Ok(err), + _ => Err(crate::error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/mod.rs b/vendor/time/src/error/mod.rs new file mode 100644 index 00000000..78b15380 --- /dev/null +++ b/vendor/time/src/error/mod.rs @@ -0,0 +1,131 @@ +//! 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 { + #[allow(missing_docs)] + ConversionRange(ConversionRange), + #[allow(missing_docs)] + ComponentRange(ComponentRange), + #[cfg(feature = "local-offset")] + #[allow(missing_docs)] + IndeterminateOffset(IndeterminateOffset), + #[cfg(feature = "formatting")] + #[allow(missing_docs)] + Format(Format), + #[cfg(feature = "parsing")] + #[allow(missing_docs)] + ParseFromDescription(ParseFromDescription), + #[cfg(feature = "parsing")] + #[allow(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")] + #[allow(missing_docs)] + TryFromParsed(TryFromParsed), + #[cfg(all(any(feature = "formatting", feature = "parsing"), feature = "alloc"))] + #[allow(missing_docs)] + InvalidFormatDescription(InvalidFormatDescription), + #[allow(missing_docs)] + DifferentVariant(DifferentVariant), + #[allow(missing_docs)] + InvalidVariant(InvalidVariant), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ConversionRange(e) => e.fmt(f), + Self::ComponentRange(e) => e.fmt(f), + #[cfg(feature = "local-offset")] + Self::IndeterminateOffset(e) => e.fmt(f), + #[cfg(feature = "formatting")] + Self::Format(e) => e.fmt(f), + #[cfg(feature = "parsing")] + Self::ParseFromDescription(e) => e.fmt(f), + #[cfg(feature = "parsing")] + #[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), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::ConversionRange(err) => Some(err), + Self::ComponentRange(err) => Some(err), + #[cfg(feature = "local-offset")] + Self::IndeterminateOffset(err) => Some(err), + #[cfg(feature = "formatting")] + Self::Format(err) => Some(err), + #[cfg(feature = "parsing")] + Self::ParseFromDescription(err) => Some(err), + #[cfg(feature = "parsing")] + #[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), + } + } +} diff --git a/vendor/time/src/error/parse.rs b/vendor/time/src/error/parse.rs new file mode 100644 index 00000000..ee3f87b7 --- /dev/null +++ b/vendor/time/src/error/parse.rs @@ -0,0 +1,109 @@ +//! 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)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Parse { + #[allow(missing_docs)] + TryFromParsed(TryFromParsed), + #[allow(missing_docs)] + ParseFromDescription(ParseFromDescription), + #[allow(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 { + 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 {}, + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Parse { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::TryFromParsed(err) => Some(err), + Self::ParseFromDescription(err) => Some(err), + #[allow(deprecated)] + Self::UnexpectedTrailingCharacters { never } => match *never {}, + } + } +} + +impl From<TryFromParsed> for Parse { + fn from(err: TryFromParsed) -> Self { + Self::TryFromParsed(err) + } +} + +impl TryFrom<Parse> for TryFromParsed { + type Error = error::DifferentVariant; + + fn try_from(err: Parse) -> Result<Self, Self::Error> { + match err { + Parse::TryFromParsed(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl From<ParseFromDescription> for Parse { + fn from(err: ParseFromDescription) -> Self { + Self::ParseFromDescription(err) + } +} + +impl TryFrom<Parse> for ParseFromDescription { + type Error = error::DifferentVariant; + + fn try_from(err: Parse) -> Result<Self, Self::Error> { + match err { + Parse::ParseFromDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +impl From<Parse> for crate::Error { + fn from(err: Parse) -> Self { + match err { + Parse::TryFromParsed(err) => Self::TryFromParsed(err), + Parse::ParseFromDescription(err) => Self::ParseFromDescription(err), + #[allow(deprecated)] + Parse::UnexpectedTrailingCharacters { never } => match never {}, + } + } +} + +impl TryFrom<crate::Error> for Parse { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::ParseFromDescription(err) => Ok(Self::ParseFromDescription(err)), + #[allow(deprecated)] + crate::Error::UnexpectedTrailingCharacters { never } => match never {}, + crate::Error::TryFromParsed(err) => Ok(Self::TryFromParsed(err)), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/parse_from_description.rs b/vendor/time/src/error/parse_from_description.rs new file mode 100644 index 00000000..40124dce --- /dev/null +++ b/vendor/time/src/error/parse_from_description.rs @@ -0,0 +1,53 @@ +//! 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 { + 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") + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseFromDescription {} + +impl From<ParseFromDescription> for crate::Error { + fn from(original: ParseFromDescription) -> Self { + Self::ParseFromDescription(original) + } +} + +impl TryFrom<crate::Error> for ParseFromDescription { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::ParseFromDescription(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/error/try_from_parsed.rs b/vendor/time/src/error/try_from_parsed.rs new file mode 100644 index 00000000..da8e6947 --- /dev/null +++ b/vendor/time/src/error/try_from_parsed.rs @@ -0,0 +1,71 @@ +//! 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] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryFromParsed { + /// The [`Parsed`](crate::parsing::Parsed) did not include enough information to construct the + /// type. + InsufficientInformation, + /// Some component contained an invalid value for the type. + ComponentRange(error::ComponentRange), +} + +impl fmt::Display for TryFromParsed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientInformation => f.write_str( + "the `Parsed` struct did not include enough information to construct the type", + ), + Self::ComponentRange(err) => err.fmt(f), + } + } +} + +impl From<error::ComponentRange> for TryFromParsed { + fn from(v: error::ComponentRange) -> Self { + Self::ComponentRange(v) + } +} + +impl TryFrom<TryFromParsed> for error::ComponentRange { + type Error = error::DifferentVariant; + + fn try_from(err: TryFromParsed) -> Result<Self, Self::Error> { + match err { + TryFromParsed::ComponentRange(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryFromParsed { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InsufficientInformation => None, + Self::ComponentRange(err) => Some(err), + } + } +} + +impl From<TryFromParsed> for crate::Error { + fn from(original: TryFromParsed) -> Self { + Self::TryFromParsed(original) + } +} + +impl TryFrom<crate::Error> for TryFromParsed { + type Error = error::DifferentVariant; + + fn try_from(err: crate::Error) -> Result<Self, Self::Error> { + match err { + crate::Error::TryFromParsed(err) => Ok(err), + _ => Err(error::DifferentVariant), + } + } +} diff --git a/vendor/time/src/ext/digit_count.rs b/vendor/time/src/ext/digit_count.rs new file mode 100644 index 00000000..fb42ce9e --- /dev/null +++ b/vendor/time/src/ext/digit_count.rs @@ -0,0 +1,26 @@ +use num_conv::prelude::*; + +/// A trait that indicates the formatted width of the value can be determined. +/// +/// Note that this should not be implemented for any signed integers. This forces the caller to +/// write the sign if desired. +pub(crate) trait DigitCount { + /// The number of digits in the stringified value. + fn num_digits(self) -> u8; +} + +/// A macro to generate implementations of `DigitCount` for unsigned integers. +macro_rules! impl_digit_count { + ($($t:ty),* $(,)?) => { + $(impl DigitCount for $t { + fn num_digits(self) -> u8 { + match self.checked_ilog10() { + Some(n) => n.truncate::<u8>() + 1, + None => 1, + } + } + })* + }; +} + +impl_digit_count!(u8, u16, u32); diff --git a/vendor/time/src/ext/instant.rs b/vendor/time/src/ext/instant.rs new file mode 100644 index 00000000..313ad5b1 --- /dev/null +++ b/vendor/time/src/ext/instant.rs @@ -0,0 +1,98 @@ +use std::time::Instant as StdInstant; + +use crate::Duration; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed: Sized {} + impl Sealed for std::time::Instant {} +} + +/// An extension trait for [`std::time::Instant`] that adds methods for +/// [`time::Duration`](Duration)s. +pub trait InstantExt: sealed::Sealed { + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. See [`InstantExt::checked_add_signed`] for a non-panicking + /// version. + 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. + fn sub_signed(self, duration: Duration) -> Self { + self.checked_sub_signed(duration) + .expect("overflow when subtracting duration from instant") + } + + /// Returns `Some(t)` where `t` is the time `self.checked_add_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_add_signed(&self, duration: Duration) -> Option<Self>; + + /// Returns `Some(t)` where `t` is the time `self.checked_sub_signed(duration)` if `t` can be + /// represented as `Instant` (which means it's inside the bounds of the underlying data + /// structure), `None` otherwise. + fn checked_sub_signed(&self, duration: Duration) -> Option<Self>; + + /// Returns the amount of time elapsed from another instant to this one. This will be negative + /// if `earlier` is later than `self`. + /// + /// # Example + /// + /// ```rust + /// # use std::thread::sleep; + /// # use std::time::{Duration, Instant}; + /// # use time::ext::InstantExt; + /// let now = Instant::now(); + /// sleep(Duration::new(1, 0)); + /// let new_now = Instant::now(); + /// println!("{:?}", new_now.signed_duration_since(now)); // positive + /// println!("{:?}", now.signed_duration_since(new_now)); // negative + /// ``` + fn signed_duration_since(&self, earlier: Self) -> Duration; +} + +impl InstantExt for StdInstant { + fn checked_add_signed(&self, duration: Duration) -> Option<Self> { + if duration.is_positive() { + self.checked_add(duration.unsigned_abs()) + } else if duration.is_negative() { + self.checked_sub(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + fn checked_sub_signed(&self, duration: Duration) -> Option<Self> { + if duration.is_positive() { + self.checked_sub(duration.unsigned_abs()) + } else if duration.is_negative() { + self.checked_add(duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + Some(*self) + } + } + + 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) + } + } +} diff --git a/vendor/time/src/ext/mod.rs b/vendor/time/src/ext/mod.rs new file mode 100644 index 00000000..7cc2d0d9 --- /dev/null +++ b/vendor/time/src/ext/mod.rs @@ -0,0 +1,13 @@ +//! Extension traits. + +mod digit_count; +#[cfg(feature = "std")] +mod instant; +mod numerical_duration; +mod numerical_std_duration; + +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; diff --git a/vendor/time/src/ext/numerical_duration.rs b/vendor/time/src/ext/numerical_duration.rs new file mode 100644 index 00000000..a1e93cc4 --- /dev/null +++ b/vendor/time/src/ext/numerical_duration.rs @@ -0,0 +1,140 @@ +use crate::convert::*; +use crate::Duration; + +/// Sealed trait to prevent downstream implementations. +mod sealed { + /// A trait that cannot be implemented by downstream users. + pub trait Sealed {} + impl Sealed for i64 {} + impl Sealed for 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. +pub trait NumericalDuration: sealed::Sealed { + /// Create a [`Duration`] from the number of nanoseconds. + fn nanoseconds(self) -> Duration; + /// Create a [`Duration`] from the number of microseconds. + fn microseconds(self) -> Duration; + /// Create a [`Duration`] from the number of milliseconds. + fn milliseconds(self) -> Duration; + /// Create a [`Duration`] from the number of seconds. + fn seconds(self) -> Duration; + /// Create a [`Duration`] from the number of minutes. + fn minutes(self) -> Duration; + /// Create a [`Duration`] from the number of hours. + fn hours(self) -> Duration; + /// Create a [`Duration`] from the number of days. + fn days(self) -> Duration; + /// Create a [`Duration`] from the number of weeks. + fn weeks(self) -> Duration; +} + +impl NumericalDuration for i64 { + fn nanoseconds(self) -> Duration { + Duration::nanoseconds(self) + } + + fn microseconds(self) -> Duration { + Duration::microseconds(self) + } + + fn milliseconds(self) -> Duration { + Duration::milliseconds(self) + } + + fn seconds(self) -> Duration { + Duration::seconds(self) + } + + fn minutes(self) -> Duration { + Duration::minutes(self) + } + + fn hours(self) -> Duration { + Duration::hours(self) + } + + fn days(self) -> Duration { + Duration::days(self) + } + + fn weeks(self) -> Duration { + Duration::weeks(self) + } +} + +impl NumericalDuration for f64 { + fn nanoseconds(self) -> Duration { + Duration::nanoseconds(self as i64) + } + + fn microseconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Microsecond) as Self) as i64) + } + + fn milliseconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Millisecond) as Self) as i64) + } + + fn seconds(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Second) as Self) as i64) + } + + fn minutes(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Minute) as Self) as i64) + } + + fn hours(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Hour) as Self) as i64) + } + + fn days(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Day) as Self) as i64) + } + + fn weeks(self) -> Duration { + Duration::nanoseconds((self * Nanosecond::per(Week) as Self) as i64) + } +} diff --git a/vendor/time/src/ext/numerical_std_duration.rs b/vendor/time/src/ext/numerical_std_duration.rs new file mode 100644 index 00000000..d6bc7bab --- /dev/null +++ b/vendor/time/src/ext/numerical_std_duration.rs @@ -0,0 +1,192 @@ +use core::time::Duration as StdDuration; + +use num_conv::prelude::*; + +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. +pub trait NumericalStdDuration: sealed::Sealed { + /// Create a [`std::time::Duration`] from the number of nanoseconds. + fn std_nanoseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of microseconds. + fn std_microseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of milliseconds. + fn std_milliseconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of seconds. + fn std_seconds(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of minutes. + fn std_minutes(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of hours. + fn std_hours(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of days. + fn std_days(self) -> StdDuration; + /// Create a [`std::time::Duration`] from the number of weeks. + fn std_weeks(self) -> StdDuration; +} + +impl NumericalStdDuration for u64 { + fn std_nanoseconds(self) -> StdDuration { + StdDuration::from_nanos(self) + } + + fn std_microseconds(self) -> StdDuration { + StdDuration::from_micros(self) + } + + fn std_milliseconds(self) -> StdDuration { + StdDuration::from_millis(self) + } + + fn std_seconds(self) -> StdDuration { + StdDuration::from_secs(self) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_minutes(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Minute).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_hours(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Hour).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_days(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Day).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn std_weeks(self) -> StdDuration { + StdDuration::from_secs( + self.checked_mul(Second::per(Week).extend()) + .expect("overflow constructing `time::Duration`"), + ) + } +} + +impl NumericalStdDuration for f64 { + /// # Panics + /// + /// This will panic if self is negative. + fn std_nanoseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos(self as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_microseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Microsecond) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_milliseconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Millisecond) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_seconds(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Second) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_minutes(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Minute) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_hours(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Hour) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_days(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Day) as Self) as u64) + } + + /// # Panics + /// + /// This will panic if self is negative. + fn std_weeks(self) -> StdDuration { + assert!(self >= 0.); + StdDuration::from_nanos((self * Nanosecond::per(Week) as Self) as u64) + } +} diff --git a/vendor/time/src/format_description/borrowed_format_item.rs b/vendor/time/src/format_description/borrowed_format_item.rs new file mode 100644 index 00000000..2e97efd4 --- /dev/null +++ b/vendor/time/src/format_description/borrowed_format_item.rs @@ -0,0 +1,106 @@ +//! A format item with borrowed data. + +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use core::fmt; + +use crate::error; +use crate::format_description::Component; + +/// A complete description of how to format and parse a type. +#[non_exhaustive] +#[cfg_attr(not(feature = "alloc"), derive(Debug))] +#[derive(Clone, PartialEq, Eq)] +pub enum BorrowedFormatItem<'a> { + /// Bytes that are formatted as-is. + /// + /// **Note**: 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<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), + Self::Component(component) => component.fmt(f), + Self::Compound(compound) => compound.fmt(f), + Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(), + Self::First(items) => f.debug_tuple("First").field(items).finish(), + } + } +} + +impl From<Component> for BorrowedFormatItem<'_> { + fn from(component: Component) -> Self { + Self::Component(component) + } +} + +impl TryFrom<BorrowedFormatItem<'_>> for Component { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'_>) -> Result<Self, Self::Error> { + match value { + BorrowedFormatItem::Component(component) => Ok(component), + _ => Err(error::DifferentVariant), + } + } +} + +impl<'a> From<&'a [BorrowedFormatItem<'_>]> for BorrowedFormatItem<'a> { + fn from(items: &'a [BorrowedFormatItem<'_>]) -> Self { + Self::Compound(items) + } +} + +impl<'a> TryFrom<BorrowedFormatItem<'a>> for &[BorrowedFormatItem<'a>] { + type Error = error::DifferentVariant; + + fn try_from(value: BorrowedFormatItem<'a>) -> Result<Self, Self::Error> { + match value { + BorrowedFormatItem::Compound(items) => Ok(items), + _ => Err(error::DifferentVariant), + } + } +} + +impl PartialEq<Component> for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &Component) -> bool { + matches!(self, Self::Component(component) if component == rhs) + } +} + +impl PartialEq<BorrowedFormatItem<'_>> for Component { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} + +impl PartialEq<&[Self]> for BorrowedFormatItem<'_> { + fn eq(&self, rhs: &&[Self]) -> bool { + matches!(self, Self::Compound(compound) if compound == rhs) + } +} + +impl PartialEq<BorrowedFormatItem<'_>> for &[BorrowedFormatItem<'_>] { + fn eq(&self, rhs: &BorrowedFormatItem<'_>) -> bool { + rhs == self + } +} diff --git a/vendor/time/src/format_description/component.rs b/vendor/time/src/format_description/component.rs new file mode 100644 index 00000000..9119c090 --- /dev/null +++ b/vendor/time/src/format_description/component.rs @@ -0,0 +1,44 @@ +//! Part of a format description. + +use crate::format_description::modifier; + +/// 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), +} diff --git a/vendor/time/src/format_description/mod.rs b/vendor/time/src/format_description/mod.rs new file mode 100644 index 00000000..9e7d5202 --- /dev/null +++ b/vendor/time/src/format_description/mod.rs @@ -0,0 +1,41 @@ +//! 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; +#[cfg(feature = "alloc")] +pub use self::parse::{ + parse, parse_borrowed, parse_owned, parse_strftime_borrowed, parse_strftime_owned, +}; + +/// 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; +} diff --git a/vendor/time/src/format_description/modifier.rs b/vendor/time/src/format_description/modifier.rs new file mode 100644 index 00000000..82fca805 --- /dev/null +++ b/vendor/time/src/format_description/modifier.rs @@ -0,0 +1,434 @@ +//! Various modifiers for components. + +use core::num::NonZeroU16; + +/// Day of the month. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Day { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The representation of a month. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MonthRepr { + /// The number of the month (January is 1, December is 12). + Numerical, + /// The long form of the month name (e.g. "January"). + Long, + /// The short form of the month name (e.g. "Jan"). + Short, +} + +/// Month of the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Month { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// What form of representation should be used? + pub repr: MonthRepr, + /// Is the value case sensitive when parsing? + pub case_sensitive: bool, +} + +/// Ordinal day of the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Ordinal { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The representation used for the day of the week. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WeekdayRepr { + /// The short form of the weekday (e.g. "Mon"). + Short, + /// The long form of the weekday (e.g. "Monday"). + Long, + /// A numerical representation using Sunday as the first day of the week. + /// + /// Sunday is either 0 or 1, depending on the other modifier's value. + Sunday, + /// A numerical representation using Monday as the first day of the week. + /// + /// Monday is either 0 or 1, depending on the other modifier's value. + Monday, +} + +/// Day of the week. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Weekday { + /// What form of representation should be used? + pub repr: WeekdayRepr, + /// When using a numerical representation, should it be zero or one-indexed? + pub one_indexed: bool, + /// Is the value case sensitive when parsing? + pub case_sensitive: bool, +} + +/// The representation used for the week number. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WeekNumberRepr { + /// Week 1 is the week that contains January 4. + Iso, + /// Week 1 begins on the first Sunday of the calendar year. + Sunday, + /// Week 1 begins on the first Monday of the calendar year. + Monday, +} + +/// Week within the year. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct WeekNumber { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// What kind of representation should be used? + pub repr: WeekNumberRepr, +} + +/// The representation used for a year value. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum YearRepr { + /// The full value of the year. + Full, + /// 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, +} + +/// Hour of the day. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Hour { + /// The padding to obtain the minimum width. + pub padding: Padding, + /// Is the hour displayed using a 12 or 24-hour clock? + pub is_12_hour_clock: bool, +} + +/// Minute within the hour. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Minute { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// AM/PM part of the time. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Period { + /// Is the period uppercase or lowercase? + pub is_uppercase: bool, + /// Is the value case sensitive when parsing? + /// + /// Note that when `false`, the `is_uppercase` field has no effect on parsing behavior. + pub case_sensitive: bool, +} + +/// Second within the minute. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Second { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// The number of digits present in a subsecond representation. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SubsecondDigits { + /// Exactly one digit. + One, + /// Exactly two digits. + Two, + /// Exactly three digits. + Three, + /// Exactly four digits. + Four, + /// Exactly five digits. + Five, + /// Exactly six digits. + Six, + /// Exactly seven digits. + Seven, + /// Exactly eight digits. + Eight, + /// Exactly nine digits. + Nine, + /// Any number of digits (up to nine) that is at least one. When formatting, the minimum digits + /// necessary will be used. + OneOrMore, +} + +/// Subsecond within the second. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Subsecond { + /// How many digits are present in the component? + pub digits: SubsecondDigits, +} + +/// Hour of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetHour { + /// Whether the `+` sign is present on positive values. + pub sign_is_mandatory: bool, + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// Minute within the hour of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetMinute { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// Second within the minute of the UTC offset. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OffsetSecond { + /// The padding to obtain the minimum width. + pub padding: Padding, +} + +/// 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: NonZeroU16, +} + +// 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. + pub const fn count(count: NonZeroU16) -> 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, +} + +/// The end of input. +/// +/// There is currently not customization for this modifier. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct End; + +/// Generate the provided code if and only if `pub` is present. +macro_rules! if_pub { + (pub $(#[$attr:meta])*; $($x:tt)*) => { + $(#[$attr])* + /// + /// This function exists since [`Default::default()`] cannot be used in a `const` context. + /// It may be removed once that becomes possible. As the [`Default`] trait is in the + /// prelude, removing this function in the future will not cause any resolution failures for + /// the overwhelming majority of users; only users who use `#![no_implicit_prelude]` will be + /// affected. As such it will not be considered a breaking change. + $($x)* + }; + ($($_:tt)*) => {}; +} + +/// Implement `Default` for the given type. This also generates an inherent implementation of a +/// `default` method that is `const fn`, permitting the default value to be used in const contexts. +// Every modifier should use this macro rather than a derived `Default`. +macro_rules! impl_const_default { + ($($(#[$doc:meta])* $(@$pub:ident)? $type:ty => $default:expr;)*) => {$( + impl $type { + if_pub! { + $($pub)? + $(#[$doc])*; + pub const fn default() -> Self { + $default + } + } + } + + $(#[$doc])* + impl Default for $type { + fn default() -> Self { + $default + } + } + )*}; +} + +impl_const_default! { + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Day => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value uses the + /// [`Numerical`](Self::Numerical) representation. + MonthRepr => Self::Numerical; + /// Creates an instance of this type that indicates the value uses the + /// [`Numerical`](MonthRepr::Numerical) representation, is [padded with zeroes](Padding::Zero), + /// and is case-sensitive when parsing. + @pub Month => Self { + padding: Padding::Zero, + repr: MonthRepr::Numerical, + case_sensitive: true, + }; + /// Creates a modifier that indicates the value is [padded with zeroes](Padding::Zero). + @pub Ordinal => Self { padding: Padding::Zero }; + /// Creates a modifier that indicates the value uses the [`Long`](Self::Long) representation. + WeekdayRepr => Self::Long; + /// Creates a modifier that indicates the value uses the [`Long`](WeekdayRepr::Long) + /// representation and is case-sensitive when parsing. If the representation is changed to a + /// numerical one, the instance defaults to one-based indexing. + @pub Weekday => Self { + repr: WeekdayRepr::Long, + one_indexed: true, + case_sensitive: true, + }; + /// Creates a modifier that indicates that the value uses the [`Iso`](Self::Iso) representation. + WeekNumberRepr => Self::Iso; + /// Creates a modifier that indicates that the value is [padded with zeroes](Padding::Zero) + /// and uses the [`Iso`](WeekNumberRepr::Iso) representation. + @pub WeekNumber => Self { + padding: Padding::Zero, + repr: WeekNumberRepr::Iso, + }; + /// Creates a modifier that indicates the value uses the [`Full`](Self::Full) representation. + YearRepr => Self::Full; + /// Creates a modifier that indicates the value uses the [`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, + }; + /// Creates a modifier used to represent the end of input. + @pub End => End; +} diff --git a/vendor/time/src/format_description/owned_format_item.rs b/vendor/time/src/format_description/owned_format_item.rs new file mode 100644 index 00000000..391e76f0 --- /dev/null +++ b/vendor/time/src/format_description/owned_format_item.rs @@ -0,0 +1,158 @@ +//! A format item with owned data. + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +use crate::error; +use crate::format_description::{BorrowedFormatItem, Component}; + +/// A complete description of how to format and parse a type. +#[non_exhaustive] +#[derive(Clone, PartialEq, Eq)] +pub enum OwnedFormatItem { + /// Bytes that are formatted as-is. + /// + /// **Note**: These bytes **should** be UTF-8, but are not required to be. The value is passed + /// through `String::from_utf8_lossy` when necessary. + Literal(Box<[u8]>), + /// A minimal representation of a single non-literal item. + Component(Component), + /// A series of literals or components that collectively form a partial or complete + /// description. + Compound(Box<[Self]>), + /// A `FormatItem` that may or may not be present when parsing. If parsing fails, there + /// will be no effect on the resulting `struct`. + /// + /// This variant has no effect on formatting, as the value is guaranteed to be present. + Optional(Box<Self>), + /// A series of `FormatItem`s where, when parsing, the first successful parse is used. When + /// formatting, the first element of the [`Vec`] is used. An empty [`Vec`] is a no-op when + /// formatting or parsing. + First(Box<[Self]>), +} + +impl fmt::Debug for OwnedFormatItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Literal(literal) => f.write_str(&String::from_utf8_lossy(literal)), + Self::Component(component) => component.fmt(f), + Self::Compound(compound) => compound.fmt(f), + Self::Optional(item) => f.debug_tuple("Optional").field(item).finish(), + Self::First(items) => f.debug_tuple("First").field(items).finish(), + } + } +} + +impl From<BorrowedFormatItem<'_>> for OwnedFormatItem { + fn from(item: BorrowedFormatItem<'_>) -> Self { + (&item).into() + } +} + +impl From<&BorrowedFormatItem<'_>> for OwnedFormatItem { + fn from(item: &BorrowedFormatItem<'_>) -> Self { + match item { + BorrowedFormatItem::Literal(literal) => { + Self::Literal(literal.to_vec().into_boxed_slice()) + } + BorrowedFormatItem::Component(component) => Self::Component(*component), + BorrowedFormatItem::Compound(compound) => Self::Compound( + compound + .iter() + .cloned() + .map(Into::into) + .collect::<Vec<_>>() + .into_boxed_slice(), + ), + BorrowedFormatItem::Optional(item) => Self::Optional(Box::new((*item).into())), + BorrowedFormatItem::First(items) => Self::First( + items + .iter() + .cloned() + .map(Into::into) + .collect::<Vec<_>>() + .into_boxed_slice(), + ), + } + } +} + +impl From<Vec<BorrowedFormatItem<'_>>> for OwnedFormatItem { + fn from(items: Vec<BorrowedFormatItem<'_>>) -> Self { + items.as_slice().into() + } +} + +impl<'a, T: AsRef<[BorrowedFormatItem<'a>]> + ?Sized> From<&T> for OwnedFormatItem { + fn from(items: &T) -> Self { + Self::Compound( + items + .as_ref() + .iter() + .cloned() + .map(Into::into) + .collect::<Vec<_>>() + .into_boxed_slice(), + ) + } +} + +impl From<Component> for OwnedFormatItem { + fn from(component: Component) -> Self { + Self::Component(component) + } +} + +impl TryFrom<OwnedFormatItem> for Component { + type Error = error::DifferentVariant; + + fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> { + match value { + OwnedFormatItem::Component(component) => Ok(component), + _ => Err(error::DifferentVariant), + } + } +} + +impl From<Vec<Self>> for OwnedFormatItem { + fn from(items: Vec<Self>) -> Self { + Self::Compound(items.into_boxed_slice()) + } +} + +impl TryFrom<OwnedFormatItem> for Vec<OwnedFormatItem> { + type Error = error::DifferentVariant; + + fn try_from(value: OwnedFormatItem) -> Result<Self, Self::Error> { + match value { + OwnedFormatItem::Compound(items) => Ok(items.into_vec()), + _ => Err(error::DifferentVariant), + } + } +} + +impl PartialEq<Component> for OwnedFormatItem { + fn eq(&self, rhs: &Component) -> bool { + matches!(self, Self::Component(component) if component == rhs) + } +} + +impl PartialEq<OwnedFormatItem> for Component { + fn eq(&self, rhs: &OwnedFormatItem) -> bool { + rhs == self + } +} + +impl PartialEq<&[Self]> for OwnedFormatItem { + fn eq(&self, rhs: &&[Self]) -> bool { + matches!(self, Self::Compound(compound) if &&**compound == rhs) + } +} + +impl PartialEq<OwnedFormatItem> for &[OwnedFormatItem] { + fn eq(&self, rhs: &OwnedFormatItem) -> bool { + rhs == self + } +} diff --git a/vendor/time/src/format_description/parse/ast.rs b/vendor/time/src/format_description/parse/ast.rs new file mode 100644 index 00000000..cf13de89 --- /dev/null +++ b/vendor/time/src/format_description/parse/ast.rs @@ -0,0 +1,384 @@ +//! AST for parsing format descriptions. + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::vec::Vec; +use core::iter; + +use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; +use crate::internal_macros::bug; + +/// One part of a complete format description. +pub(super) enum Item<'a> { + /// A literal string, formatted and parsed as-is. + /// + /// This should never be present inside a nested format description. + Literal(Spanned<&'a [u8]>), + /// A sequence of brackets. The first acts as the escape character. + /// + /// This should never be present if the lexer has `BACKSLASH_ESCAPE` set to `true`. + EscapedBracket { + /// The first bracket. + _first: Unused<Location>, + /// The second bracket. + _second: Unused<Location>, + }, + /// Part of a type, along with its modifiers. + Component { + /// Where the opening bracket was in the format string. + _opening_bracket: Unused<Location>, + /// Whitespace between the opening bracket and name. + _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>, + /// The name of the component. + name: Spanned<&'a [u8]>, + /// The modifiers for the component. + modifiers: Box<[Modifier<'a>]>, + /// Whitespace between the modifiers and closing bracket. + _trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>, + /// Where the closing bracket was in the format string. + _closing_bracket: Unused<Location>, + }, + /// An optional sequence of items. + Optional { + /// Where the opening bracket was in the format string. + opening_bracket: Location, + /// Whitespace between the opening bracket and "optional". + _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>, + /// The "optional" keyword. + _optional_kw: Unused<Spanned<&'a [u8]>>, + /// Whitespace between the "optional" keyword and the opening bracket. + _whitespace: Unused<Spanned<&'a [u8]>>, + /// The items within the optional sequence. + nested_format_description: NestedFormatDescription<'a>, + /// Where the closing bracket was in the format string. + closing_bracket: Location, + }, + /// The first matching parse of a sequence of items. + First { + /// Where the opening bracket was in the format string. + opening_bracket: Location, + /// Whitespace between the opening bracket and "first". + _leading_whitespace: Unused<Option<Spanned<&'a [u8]>>>, + /// The "first" keyword. + _first_kw: Unused<Spanned<&'a [u8]>>, + /// Whitespace between the "first" keyword and the opening bracket. + _whitespace: Unused<Spanned<&'a [u8]>>, + /// The sequences of items to try. + nested_format_descriptions: Box<[NestedFormatDescription<'a>]>, + /// Where the closing bracket was in the format string. + closing_bracket: Location, + }, +} + +/// A format description that is nested within another format description. +pub(super) struct NestedFormatDescription<'a> { + /// Where the opening bracket was in the format string. + pub(super) _opening_bracket: Unused<Location>, + /// The items within the nested format description. + pub(super) items: Box<[Item<'a>]>, + /// Where the closing bracket was in the format string. + pub(super) _closing_bracket: Unused<Location>, + /// Whitespace between the closing bracket and the next item. + pub(super) _trailing_whitespace: Unused<Option<Spanned<&'a [u8]>>>, +} + +/// A modifier for a component. +pub(super) struct Modifier<'a> { + /// Whitespace preceding the modifier. + pub(super) _leading_whitespace: Unused<Spanned<&'a [u8]>>, + /// The key of the modifier. + pub(super) key: Spanned<&'a [u8]>, + /// Where the colon of the modifier was in the format string. + pub(super) _colon: Unused<Location>, + /// The value of the modifier. + pub(super) value: Spanned<&'a [u8]>, +} + +/// Parse the provided tokens into an AST. +pub(super) fn parse< + 'item: 'iter, + 'iter, + I: Iterator<Item = Result<lexer::Token<'item>, Error>>, + const VERSION: usize, +>( + tokens: &'iter mut lexer::Lexed<I>, +) -> impl Iterator<Item = Result<Item<'item>, Error>> + 'iter { + 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`]. +fn parse_inner< + 'item, + I: Iterator<Item = Result<lexer::Token<'item>, Error>>, + const NESTED: bool, + const VERSION: usize, +>( + tokens: &mut lexer::Lexed<I>, +) -> impl Iterator<Item = Result<Item<'item>, Error>> + '_ { + validate_version!(VERSION); + iter::from_fn(move || { + if NESTED && tokens.peek_closing_bracket().is_some() { + return None; + } + + let next = match tokens.next()? { + Ok(token) => token, + Err(err) => return Some(Err(err)), + }; + + Some(match next { + lexer::Token::Literal(Spanned { value: _, span: _ }) if NESTED => { + bug!("literal should not be present in nested description") + } + lexer::Token::Literal(value) => Ok(Item::Literal(value)), + lexer::Token::Bracket { + kind: lexer::BracketKind::Opening, + location, + } => { + if version!(..=1) { + if let Some(second_location) = tokens.next_if_opening_bracket() { + Ok(Item::EscapedBracket { + _first: unused(location), + _second: unused(second_location), + }) + } else { + parse_component::<_, VERSION>(location, tokens) + } + } else { + parse_component::<_, VERSION>(location, tokens) + } + } + lexer::Token::Bracket { + kind: lexer::BracketKind::Closing, + location: _, + } if NESTED => { + bug!("closing bracket should be caught by the `if` statement") + } + lexer::Token::Bracket { + kind: lexer::BracketKind::Closing, + location: _, + } => { + bug!("closing bracket should have been consumed by `parse_component`") + } + lexer::Token::ComponentPart { + kind: _, // whitespace is significant in nested components + value, + } if NESTED => Ok(Item::Literal(value)), + lexer::Token::ComponentPart { kind: _, value: _ } => { + bug!("component part should have been consumed by `parse_component`") + } + }) + }) +} + +/// Parse a component. This assumes that the opening bracket has already been consumed. +fn parse_component< + 'a, + I: Iterator<Item = Result<lexer::Token<'a>, Error>>, + const VERSION: usize, +>( + opening_bracket: Location, + tokens: &mut lexer::Lexed<I>, +) -> Result<Item<'a>, Error> { + validate_version!(VERSION); + let leading_whitespace = tokens.next_if_whitespace(); + + let Some(name) = tokens.next_if_not_whitespace() else { + let span = match leading_whitespace { + Some(Spanned { value: _, span }) => span, + None => opening_bracket.to_self(), + }; + return Err(Error { + _inner: unused(span.error("expected component name")), + public: crate::error::InvalidFormatDescription::MissingComponentName { + index: span.start.byte as usize, + }, + }); + }; + + if *name == b"optional" { + let Some(whitespace) = tokens.next_if_whitespace() else { + return Err(Error { + _inner: unused(name.span.error("expected whitespace after `optional`")), + public: crate::error::InvalidFormatDescription::Expected { + what: "whitespace after `optional`", + index: name.span.end.byte as usize, + }, + }); + }; + + let nested = parse_nested::<_, VERSION>(whitespace.span.end, tokens)?; + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + return Ok(Item::Optional { + opening_bracket, + _leading_whitespace: unused(leading_whitespace), + _optional_kw: unused(name), + _whitespace: unused(whitespace), + nested_format_description: nested, + closing_bracket, + }); + } + + if *name == b"first" { + let Some(whitespace) = tokens.next_if_whitespace() else { + return Err(Error { + _inner: unused(name.span.error("expected whitespace after `first`")), + public: crate::error::InvalidFormatDescription::Expected { + what: "whitespace after `first`", + index: name.span.end.byte as usize, + }, + }); + }; + + let mut nested_format_descriptions = Vec::new(); + while let Ok(description) = parse_nested::<_, VERSION>(whitespace.span.end, tokens) { + nested_format_descriptions.push(description); + } + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + return Ok(Item::First { + opening_bracket, + _leading_whitespace: unused(leading_whitespace), + _first_kw: unused(name), + _whitespace: unused(whitespace), + nested_format_descriptions: nested_format_descriptions.into_boxed_slice(), + closing_bracket, + }); + } + + let mut modifiers = Vec::new(); + let trailing_whitespace = loop { + let Some(whitespace) = tokens.next_if_whitespace() else { + break None; + }; + + // This is not necessary for proper parsing, but provides a much better error when a nested + // description is used where it's not allowed. + if let Some(location) = tokens.next_if_opening_bracket() { + return Err(Error { + _inner: unused( + location + .to_self() + .error("modifier must be of the form `key:value`"), + ), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from("["), + index: location.byte as usize, + }, + }); + } + + let Some(Spanned { value, span }) = tokens.next_if_not_whitespace() else { + break Some(whitespace); + }; + + let Some(colon_index) = value.iter().position(|&b| b == b':') else { + return Err(Error { + _inner: unused(span.error("modifier must be of the form `key:value`")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: span.start.byte as usize, + }, + }); + }; + let key = &value[..colon_index]; + let value = &value[colon_index + 1..]; + + if key.is_empty() { + return Err(Error { + _inner: unused(span.shrink_to_start().error("expected modifier key")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::new(), + index: span.start.byte as usize, + }, + }); + } + if value.is_empty() { + return Err(Error { + _inner: unused(span.shrink_to_end().error("expected modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::new(), + index: span.shrink_to_end().start.byte as usize, + }, + }); + } + + modifiers.push(Modifier { + _leading_whitespace: unused(whitespace), + key: key.spanned(span.shrink_to_before(colon_index as u32)), + _colon: unused(span.start.offset(colon_index as u32)), + value: value.spanned(span.shrink_to_after(colon_index as u32)), + }); + }; + + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + + Ok(Item::Component { + _opening_bracket: unused(opening_bracket), + _leading_whitespace: unused(leading_whitespace), + name, + modifiers: modifiers.into_boxed_slice(), + _trailing_whitespace: unused(trailing_whitespace), + _closing_bracket: unused(closing_bracket), + }) +} + +/// Parse a nested format description. The location provided is the the most recent one consumed. +fn parse_nested<'a, I: Iterator<Item = Result<lexer::Token<'a>, Error>>, const VERSION: usize>( + last_location: Location, + tokens: &mut lexer::Lexed<I>, +) -> Result<NestedFormatDescription<'a>, Error> { + validate_version!(VERSION); + let Some(opening_bracket) = tokens.next_if_opening_bracket() else { + return Err(Error { + _inner: unused(last_location.error("expected opening bracket")), + public: crate::error::InvalidFormatDescription::Expected { + what: "opening bracket", + index: last_location.byte as usize, + }, + }); + }; + let items = parse_inner::<_, true, VERSION>(tokens).collect::<Result<_, _>>()?; + let Some(closing_bracket) = tokens.next_if_closing_bracket() else { + return Err(Error { + _inner: unused(opening_bracket.error("unclosed bracket")), + public: crate::error::InvalidFormatDescription::UnclosedOpeningBracket { + index: opening_bracket.byte as usize, + }, + }); + }; + let trailing_whitespace = tokens.next_if_whitespace(); + + Ok(NestedFormatDescription { + _opening_bracket: unused(opening_bracket), + items, + _closing_bracket: unused(closing_bracket), + _trailing_whitespace: unused(trailing_whitespace), + }) +} diff --git a/vendor/time/src/format_description/parse/format_item.rs b/vendor/time/src/format_description/parse/format_item.rs new file mode 100644 index 00000000..d401daa5 --- /dev/null +++ b/vendor/time/src/format_description/parse/format_item.rs @@ -0,0 +1,549 @@ +//! Typed, validated representation of a parsed format description. + +use alloc::boxed::Box; +use alloc::string::String; +use core::num::NonZeroU16; +use core::str::{self, FromStr}; + +use super::{ast, unused, Error, Span, Spanned}; +use crate::internal_macros::bug; + +/// Parse an AST iterator into a sequence of format items. +pub(super) fn parse<'a>( + ast_items: impl Iterator<Item = Result<ast::Item<'a>, Error>>, +) -> impl Iterator<Item = Result<Item<'a>, Error>> { + ast_items.map(|ast_item| ast_item.and_then(Item::from_ast)) +} + +/// A description of how to format and parse one part of a type. +pub(super) enum Item<'a> { + /// A literal string. + Literal(&'a [u8]), + /// Part of a type, along with its modifiers. + Component(Component), + /// A sequence of optional items. + Optional { + /// The items themselves. + value: Box<[Self]>, + /// The span of the full sequence. + span: Span, + }, + /// The first matching parse of a sequence of format descriptions. + First { + /// The sequence of format descriptions. + value: Box<[Box<[Self]>]>, + /// The span of the full sequence. + span: Span, + }, +} + +impl Item<'_> { + /// Parse an AST item into a format item. + pub(super) fn from_ast(ast_item: ast::Item<'_>) -> Result<Item<'_>, Error> { + Ok(match ast_item { + ast::Item::Component { + _opening_bracket: _, + _leading_whitespace: _, + name, + modifiers, + _trailing_whitespace: _, + _closing_bracket: _, + } => Item::Component(component_from_ast(&name, &modifiers)?), + ast::Item::Literal(Spanned { value, span: _ }) => Item::Literal(value), + ast::Item::EscapedBracket { + _first: _, + _second: _, + } => Item::Literal(b"["), + ast::Item::Optional { + opening_bracket, + _leading_whitespace: _, + _optional_kw: _, + _whitespace: _, + nested_format_description, + closing_bracket, + } => { + let items = nested_format_description + .items + .into_vec() + .into_iter() + .map(Item::from_ast) + .collect::<Result<_, _>>()?; + Item::Optional { + value: items, + span: opening_bracket.to(closing_bracket), + } + } + ast::Item::First { + opening_bracket, + _leading_whitespace: _, + _first_kw: _, + _whitespace: _, + nested_format_descriptions, + closing_bracket, + } => { + let items = nested_format_descriptions + .into_vec() + .into_iter() + .map(|nested_format_description| { + nested_format_description + .items + .into_vec() + .into_iter() + .map(Item::from_ast) + .collect() + }) + .collect::<Result<_, _>>()?; + Item::First { + value: items, + span: opening_bracket.to(closing_bracket), + } + } + }) + } +} + +impl<'a> TryFrom<Item<'a>> for crate::format_description::BorrowedFormatItem<'a> { + type Error = Error; + + fn try_from(item: Item<'a>) -> Result<Self, Self::Error> { + match item { + Item::Literal(literal) => Ok(Self::Literal(literal)), + Item::Component(component) => Ok(Self::Component(component.into())), + Item::Optional { value: _, span } => Err(Error { + _inner: unused(span.error( + "optional items are not supported in runtime-parsed format descriptions", + )), + public: crate::error::InvalidFormatDescription::NotSupported { + what: "optional item", + context: "runtime-parsed format descriptions", + index: span.start.byte as usize, + }, + }), + Item::First { value: _, span } => Err(Error { + _inner: unused(span.error( + "'first' items are not supported in runtime-parsed format descriptions", + )), + public: crate::error::InvalidFormatDescription::NotSupported { + what: "'first' item", + context: "runtime-parsed format descriptions", + index: span.start.byte as usize, + }, + }), + } + } +} + +impl From<Item<'_>> for crate::format_description::OwnedFormatItem { + fn from(item: Item<'_>) -> Self { + match item { + Item::Literal(literal) => Self::Literal(literal.to_vec().into_boxed_slice()), + Item::Component(component) => Self::Component(component.into()), + Item::Optional { value, span: _ } => Self::Optional(Box::new(value.into())), + Item::First { value, span: _ } => { + Self::First(value.into_vec().into_iter().map(Into::into).collect()) + } + } + } +} + +impl<'a> From<Box<[Item<'a>]>> for crate::format_description::OwnedFormatItem { + 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. + fn with_modifiers( + modifiers: &[ast::Modifier<'_>], + _component_span: Span, + ) -> Result<Self, Error> + { + // rustc will complain if the modifier is empty. + #[allow(unused_mut)] + let mut this = Self { + $($field: None),* + }; + + for modifier in modifiers { + $(#[allow(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 { + 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. + fn component_from_ast( + name: &Spanned<&[u8]>, + modifiers: &[ast::Modifier<'_>], + ) -> Result<Component, Error> { + $(#[allow(clippy::string_lit_as_bytes)] + if name.eq_ignore_ascii_case($parse_variant.as_bytes()) { + return Ok(Component::$variant($variant::with_modifiers(&modifiers, name.span)?)); + })* + Err(Error { + _inner: unused(name.span.error("invalid component")), + public: crate::error::InvalidFormatDescription::InvalidComponentName { + name: String::from_utf8_lossy(name).into_owned(), + index: name.span.start.byte as usize, + }, + }) + } + } +} + +// Keep in alphabetical order. +component_definition! { + pub(super) enum Component { + Day = "day" { + padding = "padding": Option<Padding> => padding, + }, + End = "end" {}, + Hour = "hour" { + padding = "padding": Option<Padding> => padding, + base = "repr": Option<HourBase> => is_12_hour_clock, + }, + Ignore = "ignore" { + #[required] + count = "count": Option<#[from_str] NonZeroU16> => count, + }, + Minute = "minute" { + padding = "padding": Option<Padding> => padding, + }, + Month = "month" { + padding = "padding": Option<Padding> => padding, + repr = "repr": Option<MonthRepr> => repr, + case_sensitive = "case_sensitive": Option<MonthCaseSensitive> => case_sensitive, + }, + OffsetHour = "offset_hour" { + sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory, + padding = "padding": Option<Padding> => padding, + }, + OffsetMinute = "offset_minute" { + padding = "padding": Option<Padding> => padding, + }, + OffsetSecond = "offset_second" { + padding = "padding": Option<Padding> => padding, + }, + Ordinal = "ordinal" { + padding = "padding": Option<Padding> => padding, + }, + Period = "period" { + case = "case": Option<PeriodCase> => is_uppercase, + case_sensitive = "case_sensitive": Option<PeriodCaseSensitive> => case_sensitive, + }, + Second = "second" { + padding = "padding": Option<Padding> => padding, + }, + Subsecond = "subsecond" { + digits = "digits": Option<SubsecondDigits> => digits, + }, + UnixTimestamp = "unix_timestamp" { + precision = "precision": Option<UnixTimestampPrecision> => precision, + sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory, + }, + Weekday = "weekday" { + repr = "repr": Option<WeekdayRepr> => repr, + one_indexed = "one_indexed": Option<WeekdayOneIndexed> => one_indexed, + case_sensitive = "case_sensitive": Option<WeekdayCaseSensitive> => case_sensitive, + }, + WeekNumber = "week_number" { + padding = "padding": Option<Padding> => padding, + repr = "repr": Option<WeekNumberRepr> => repr, + }, + Year = "year" { + padding = "padding": Option<Padding> => padding, + repr = "repr": Option<YearRepr> => repr, + range = "range": Option<YearRange> => range, + base = "base": Option<YearBase> => iso_week_based, + sign_behavior = "sign": Option<SignBehavior> => sign_is_mandatory, + }, + } +} + +/// Get the target type for a given enum. +macro_rules! target_ty { + ($name:ident $type:ty) => { + $type + }; + ($name:ident) => { + $crate::format_description::modifier::$name + }; +} + +/// Get the target value for a given enum. +macro_rules! target_value { + ($name:ident $variant:ident $value:expr) => { + $value + }; + ($name:ident $variant:ident) => { + $crate::format_description::modifier::$name::$variant + }; +} + +/// Declare the various modifiers. +/// +/// For the general case, ordinary syntax can be used. Note that you _must_ declare a default +/// variant. The only significant change is that the string representation of the variant must be +/// provided after the variant name. For example, `Numerical = b"numerical"` declares a variant +/// named `Numerical` with the string representation `b"numerical"`. This is the value that will be +/// used when parsing the modifier. The value is not case sensitive. +/// +/// If the type in the public API does not have the same name as the type in the internal +/// representation, then the former must be specified in parenthesis after the internal name. For +/// example, `HourBase(bool)` has an internal name "HourBase", but is represented as a boolean in +/// the public API. +/// +/// By default, the internal variant name is assumed to be the same as the public variant name. If +/// this is not the case, the qualified path to the variant must be specified in parenthesis after +/// the internal variant name. For example, `Twelve(true)` has an internal variant name "Twelve", +/// but is represented as `true` in the public API. +macro_rules! modifier { + ($( + enum $name:ident $(($target_ty:ty))? { + $( + $(#[$attr:meta])? + $variant:ident $(($target_value:expr))? = $parse_variant:literal + ),* $(,)? + } + )+) => {$( + #[derive(Default)] + enum $name { + $($(#[$attr])? $variant),* + } + + impl $name { + /// Parse the modifier from its string representation. + fn from_modifier_value(value: &Spanned<&[u8]>) -> Result<Option<Self>, Error> { + $(if value.eq_ignore_ascii_case($parse_variant) { + return Ok(Some(Self::$variant)); + })* + Err(Error { + _inner: unused(value.span.error("invalid modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: value.span.start.byte as usize, + }, + }) + } + } + + impl From<$name> for target_ty!($name $($target_ty)?) { + 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 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. +fn parse_from_modifier_value<T: FromStr>(value: &Spanned<&[u8]>) -> Result<Option<T>, Error> { + str::from_utf8(value) + .ok() + .and_then(|val| val.parse::<T>().ok()) + .map(|val| Some(val)) + .ok_or_else(|| Error { + _inner: unused(value.span.error("invalid modifier value")), + public: crate::error::InvalidFormatDescription::InvalidModifier { + value: String::from_utf8_lossy(value).into_owned(), + index: value.span.start.byte as usize, + }, + }) +} diff --git a/vendor/time/src/format_description/parse/lexer.rs b/vendor/time/src/format_description/parse/lexer.rs new file mode 100644 index 00000000..a63722e1 --- /dev/null +++ b/vendor/time/src/format_description/parse/lexer.rs @@ -0,0 +1,284 @@ +//! Lexer for parsing format descriptions. + +use core::iter; + +use super::{attach_location, unused, Error, Location, Spanned, SpannedValue}; + +/// An iterator over the lexed tokens. +pub(super) struct Lexed<I: Iterator> { + /// The internal iterator. + iter: iter::Peekable<I>, +} + +impl<I: Iterator> Iterator for Lexed<I> { + type Item = I::Item; + + fn next(&mut self) -> Option<Self::Item> { + self.iter.next() + } +} + +impl<'iter, 'token: 'iter, I: Iterator<Item = Result<Token<'token>, Error>> + 'iter> Lexed<I> { + /// Peek at the next item in the iterator. + pub(super) fn peek(&mut self) -> Option<&I::Item> { + self.iter.peek() + } + + /// Consume the next token if it is whitespace. + pub(super) fn next_if_whitespace(&mut self) -> Option<Spanned<&'token [u8]>> { + if let Some(&Ok(Token::ComponentPart { + kind: ComponentKind::Whitespace, + value, + })) = self.peek() + { + self.next(); // consume + Some(value) + } else { + None + } + } + + /// Consume the next token if it is a component item that is not whitespace. + pub(super) fn next_if_not_whitespace(&mut self) -> Option<Spanned<&'token [u8]>> { + if let Some(&Ok(Token::ComponentPart { + kind: ComponentKind::NotWhitespace, + value, + })) = self.peek() + { + self.next(); // consume + Some(value) + } else { + None + } + } + + /// Consume the next token if it is an opening bracket. + pub(super) fn next_if_opening_bracket(&mut self) -> Option<Location> { + if let Some(&Ok(Token::Bracket { + kind: BracketKind::Opening, + location, + })) = self.peek() + { + self.next(); // consume + Some(location) + } else { + None + } + } + + /// Peek at the next token if it is a closing bracket. + 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. + pub(super) fn next_if_closing_bracket(&mut self) -> Option<Location> { + if let Some(&Ok(Token::Bracket { + kind: BracketKind::Closing, + location, + })) = self.peek() + { + self.next(); // consume + Some(location) + } else { + None + } + } +} + +/// A token emitted by the lexer. There is no semantic meaning at this stage. +pub(super) enum Token<'a> { + /// A literal string, formatted and parsed as-is. + Literal(Spanned<&'a [u8]>), + /// An opening or closing bracket. May or may not be the start or end of a component. + Bracket { + /// Whether the bracket is opening or closing. + kind: BracketKind, + /// Where the bracket was in the format string. + location: Location, + }, + /// One part of a component. This could be its name, a modifier, or whitespace. + ComponentPart { + /// Whether the part is whitespace or not. + kind: ComponentKind, + /// The part itself. + value: Spanned<&'a [u8]>, + }, +} + +/// What type of bracket is present. +pub(super) enum BracketKind { + /// An opening bracket: `[` + Opening, + /// A closing bracket: `]` + Closing, +} + +/// Indicates whether the component is whitespace or not. +pub(super) enum ComponentKind { + Whitespace, + NotWhitespace, +} + +/// Parse the string into a series of [`Token`]s. +/// +/// `VERSION` controls the version of the format description that is being parsed. Currently, this +/// must be 1 or 2. +/// +/// - When `VERSION` is 1, `[[` is the only escape sequence, resulting in a literal `[`. +/// - When `VERSION` is 2, all escape sequences begin with `\`. The only characters that may +/// currently follow are `\`, `[`, and `]`, all of which result in the literal character. All +/// other characters result in a lex error. +pub(super) fn lex<const VERSION: usize>( + mut input: &[u8], +) -> Lexed<impl Iterator<Item = Result<Token<'_>, Error>>> { + validate_version!(VERSION); + + let mut depth: u8 = 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(), + } +} diff --git a/vendor/time/src/format_description/parse/mod.rs b/vendor/time/src/format_description/parse/mod.rs new file mode 100644 index 00000000..d058594d --- /dev/null +++ b/vendor/time/src/format_description/parse/mod.rs @@ -0,0 +1,262 @@ +//! 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) => { + let _ = $crate::format_description::parse::Version::<$version>::IS_VALID; + }; +} + +mod ast; +mod format_item; +mod lexer; +mod strftime; + +/// A struct that is used to ensure that the version is valid. +struct Version<const N: usize>; +impl<const N: usize> Version<N> { + /// A constant that panics if the version is not valid. This results in a post-monomorphization + /// error. + const IS_VALID: () = assert!(N >= 1 && N <= 2); +} + +/// 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`. +pub fn parse( + s: &str, +) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> { + parse_borrowed::<1>(s) +} + +/// Parse a sequence of items from the format description. +/// +/// The syntax for the format description can be found in [the +/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format +/// description is provided as the const parameter. **It is recommended to use version 2.** +pub fn parse_borrowed<const VERSION: usize>( + s: &str, +) -> Result<Vec<format_description::BorrowedFormatItem<'_>>, error::InvalidFormatDescription> { + validate_version!(VERSION); + let mut lexed = lexer::lex::<VERSION>(s.as_bytes()); + let ast = ast::parse::<_, VERSION>(&mut lexed); + let format_items = format_item::parse(ast); + Ok(format_items + .map(|res| res.and_then(TryInto::try_into)) + .collect::<Result<_, _>>()?) +} + +/// Parse a sequence of items from the format description. +/// +/// The syntax for the format description can be found in [the +/// book](https://time-rs.github.io/book/api/format-description.html). The version of the format +/// description is provided as the const parameter. +/// +/// Unlike [`parse`], this function returns [`OwnedFormatItem`], which owns its contents. This means +/// that there is no lifetime that needs to be handled. **It is recommended to use version 2.** +/// +/// [`OwnedFormatItem`]: crate::format_description::OwnedFormatItem +pub fn parse_owned<const VERSION: usize>( + s: &str, +) -> Result<format_description::OwnedFormatItem, error::InvalidFormatDescription> { + validate_version!(VERSION); + let mut lexed = lexer::lex::<VERSION>(s.as_bytes()); + let ast = ast::parse::<_, VERSION>(&mut lexed); + let format_items = format_item::parse(ast); + let items = format_items.collect::<Result<Box<_>, _>>()?; + Ok(items.into()) +} + +/// Attach [`Location`] information to each byte in the iterator. +fn attach_location<'item>( + iter: impl Iterator<Item = &'item u8>, +) -> impl Iterator<Item = (&'item u8, Location)> { + let mut byte_pos = 0; + + iter.map(move |byte| { + let location = Location { byte: byte_pos }; + byte_pos += 1; + (byte, location) + }) +} + +/// A location within a string. +#[derive(Clone, Copy)] +struct Location { + /// The zero-indexed byte of the string. + byte: u32, +} + +impl Location { + /// Create a new [`Span`] from `self` to `other`. + const fn to(self, end: Self) -> Span { + Span { start: self, end } + } + + /// Create a new [`Span`] consisting entirely of `self`. + 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"] + const fn offset(&self, offset: u32) -> Self { + Self { + byte: self.byte + offset, + } + } + + /// Create an error with the provided message at this location. + const fn error(self, message: &'static str) -> ErrorInner { + ErrorInner { + _message: message, + _span: Span { + start: self, + end: self, + }, + } + } +} + +/// A start and end point within a string. +#[derive(Clone, Copy)] +struct Span { + 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"] + 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"] + 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"] + 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. + const fn error(self, message: &'static str) -> ErrorInner { + ErrorInner { + _message: message, + _span: self, + } + } +} + +/// A value with an associated [`Span`]. +#[derive(Clone, Copy)] +struct Spanned<T> { + /// The value. + value: T, + /// Where the value was in the format string. + span: Span, +} + +impl<T> core::ops::Deref for Spanned<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +/// Helper trait to attach a [`Span`] to a value. +trait SpannedValue: Sized { + /// Attach a [`Span`] to a value. + fn spanned(self, span: Span) -> Spanned<Self>; +} + +impl<T> SpannedValue for T { + fn spanned(self, span: Span) -> Spanned<Self> { + Spanned { value: self, span } + } +} + +/// The internal error type. +struct ErrorInner { + /// The message displayed to the user. + _message: &'static str, + /// Where the error originated. + _span: Span, +} + +/// A complete error description. +struct Error { + /// The internal error. + _inner: Unused<ErrorInner>, + /// The error needed for interoperability with the rest of `time`. + public: error::InvalidFormatDescription, +} + +impl From<Error> for error::InvalidFormatDescription { + fn from(error: Error) -> Self { + error.public + } +} + +/// A value that may be used in the future, but currently is not. +/// +/// This struct exists so that data can semantically be passed around without _actually_ passing it +/// around. This way the data still exists if it is needed in the future. +// `PhantomData` is not used directly because we don't want to introduce any trait implementations. +struct Unused<T>(core::marker::PhantomData<T>); + +/// Indicate that a value is currently unused. +fn unused<T>(_: T) -> Unused<T> { + Unused(core::marker::PhantomData) +} diff --git a/vendor/time/src/format_description/parse/strftime.rs b/vendor/time/src/format_description/parse/strftime.rs new file mode 100644 index 00000000..5fcaf184 --- /dev/null +++ b/vendor/time/src/format_description/parse/strftime.rs @@ -0,0 +1,487 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::iter; + +use crate::error::InvalidFormatDescription; +use crate::format_description::parse::{ + attach_location, unused, Error, ErrorInner, Location, Spanned, SpannedValue, Unused, +}; +use crate::format_description::{self, modifier, BorrowedFormatItem, Component}; + +/// 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")] +pub fn parse_strftime_borrowed( + s: &str, +) -> Result<Vec<BorrowedFormatItem<'_>>, InvalidFormatDescription> { + let tokens = lex(s.as_bytes()); + let items = into_items(tokens).collect::<Result<_, _>>()?; + Ok(items) +} + +/// Parse a sequence of items from the [`strftime` format description][strftime docs]. +/// +/// This requires heap allocation for some owned items. +/// +/// [strftime docs]: https://man7.org/linux/man-pages/man3/strftime.3.html +#[doc(alias = "parse_strptime_owned")] +pub fn parse_strftime_owned( + s: &str, +) -> Result<format_description::OwnedFormatItem, InvalidFormatDescription> { + parse_strftime_borrowed(s).map(Into::into) +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Padding { + /// The default padding for a numeric component. Indicated by no character. + Default, + /// Pad a numeric component with spaces. Indicated by an underscore. + Spaces, + /// Do not pad a numeric component. Indicated by a hyphen. + None, + /// Pad a numeric component with zeroes. Indicated by a zero. + Zeroes, +} + +enum Token<'a> { + Literal(Spanned<&'a [u8]>), + Component { + _percent: Unused<Location>, + padding: Spanned<Padding>, + component: Spanned<u8>, + }, +} + +fn lex(mut input: &[u8]) -> iter::Peekable<impl Iterator<Item = Result<Token<'_>, Error>>> { + let mut iter = attach_location(input.iter()).peekable(); + + iter::from_fn(move || { + Some(Ok(match iter.next()? { + (b'%', percent_loc) => match iter.next() { + Some((padding @ (b'_' | b'-' | b'0'), padding_loc)) => { + let padding = match padding { + b'_' => Padding::Spaces, + b'-' => Padding::None, + b'0' => Padding::Zeroes, + _ => unreachable!(), + }; + let (&component, component_loc) = iter.next()?; + input = &input[3..]; + Token::Component { + _percent: unused(percent_loc), + padding: padding.spanned(padding_loc.to_self()), + component: component.spanned(component_loc.to_self()), + } + } + Some((&component, component_loc)) => { + input = &input[2..]; + let span = component_loc.to_self(); + Token::Component { + _percent: unused(percent_loc), + padding: Padding::Default.spanned(span), + component: component.spanned(span), + } + } + None => { + return Some(Err(Error { + _inner: unused(percent_loc.error("unexpected end of input")), + public: InvalidFormatDescription::Expected { + what: "valid escape sequence", + index: percent_loc.byte as usize, + }, + })); + } + }, + (_, start_location) => { + let mut bytes = 1; + let mut end_location = start_location; + + while let Some((_, location)) = iter.next_if(|&(&byte, _)| byte != b'%') { + end_location = location; + bytes += 1; + } + + let value = &input[..bytes]; + input = &input[bytes..]; + + Token::Literal(value.spanned(start_location.to(end_location))) + } + })) + }) + .peekable() +} + +fn into_items<'iter, 'token: 'iter>( + mut tokens: iter::Peekable<impl Iterator<Item = Result<Token<'token>, Error>> + 'iter>, +) -> impl Iterator<Item = Result<BorrowedFormatItem<'token>, Error>> + 'iter { + iter::from_fn(move || { + let next = match tokens.next()? { + Ok(token) => token, + Err(err) => return Some(Err(err)), + }; + + Some(match next { + Token::Literal(spanned) => Ok(BorrowedFormatItem::Literal(*spanned)), + Token::Component { + _percent, + padding, + component, + } => parse_component(padding, component), + }) + }) +} + +fn parse_component( + padding: Spanned<Padding>, + component: Spanned<u8>, +) -> Result<BorrowedFormatItem<'static>, Error> { + let padding_or_default = |padding: Padding, default| match padding { + Padding::Default => default, + Padding::Spaces => modifier::Padding::Space, + Padding::None => modifier::Padding::None, + Padding::Zeroes => modifier::Padding::Zero, + }; + + /// Helper macro to create a component. + macro_rules! component { + ($name:ident { $($inner:tt)* }) => { + BorrowedFormatItem::Component(Component::$name(modifier::$name { + $($inner)* + })) + } + } + + Ok(match *component { + b'%' => BorrowedFormatItem::Literal(b"%"), + b'a' => component!(Weekday { + repr: modifier::WeekdayRepr::Short, + one_indexed: true, + case_sensitive: true, + }), + b'A' => component!(Weekday { + repr: modifier::WeekdayRepr::Long, + one_indexed: true, + case_sensitive: true, + }), + b'b' | b'h' => component!(Month { + repr: modifier::MonthRepr::Short, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + b'B' => component!(Month { + repr: modifier::MonthRepr::Long, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + b'c' => BorrowedFormatItem::Compound(&[ + component!(Weekday { + repr: modifier::WeekdayRepr::Short, + one_indexed: true, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b" "), + component!(Month { + repr: modifier::MonthRepr::Short, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b" "), + component!(Day { + padding: modifier::Padding::Space + }), + BorrowedFormatItem::Literal(b" "), + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b" "), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'C' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::Century, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'd' => component!(Day { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'D' => BorrowedFormatItem::Compound(&[ + component!(Month { + repr: modifier::MonthRepr::Numerical, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Day { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'e' => component!(Day { + padding: padding_or_default(*padding, modifier::Padding::Space), + }), + b'F' => BorrowedFormatItem::Compound(&[ + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + BorrowedFormatItem::Literal(b"-"), + component!(Month { + padding: modifier::Padding::Zero, + repr: modifier::MonthRepr::Numerical, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"-"), + component!(Day { + padding: modifier::Padding::Zero, + }), + ]), + b'g' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: true, + sign_is_mandatory: false, + }), + b'G' => component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: true, + sign_is_mandatory: false, + }), + b'H' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Zero), + is_12_hour_clock: false, + }), + b'I' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Zero), + is_12_hour_clock: true, + }), + b'j' => component!(Ordinal { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'k' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Space), + is_12_hour_clock: false, + }), + b'l' => component!(Hour { + padding: padding_or_default(*padding, modifier::Padding::Space), + is_12_hour_clock: true, + }), + b'm' => component!(Month { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::MonthRepr::Numerical, + case_sensitive: true, + }), + b'M' => component!(Minute { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b'n' => BorrowedFormatItem::Literal(b"\n"), + b'O' => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "unsupported modifier", + _span: component.span, + }), + public: InvalidFormatDescription::NotSupported { + what: "modifier", + context: "", + index: component.span.start.byte as usize, + }, + }) + } + b'p' => component!(Period { + is_uppercase: true, + case_sensitive: true + }), + b'P' => component!(Period { + is_uppercase: false, + case_sensitive: true + }), + b'r' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: true, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b" "), + component!(Period { + is_uppercase: true, + case_sensitive: true, + }), + ]), + b'R' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + ]), + b's' => component!(UnixTimestamp { + precision: modifier::UnixTimestampPrecision::Second, + sign_is_mandatory: false, + }), + b'S' => component!(Second { + padding: padding_or_default(*padding, modifier::Padding::Zero), + }), + b't' => BorrowedFormatItem::Literal(b"\t"), + b'T' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + ]), + b'u' => component!(Weekday { + repr: modifier::WeekdayRepr::Monday, + one_indexed: true, + case_sensitive: true, + }), + b'U' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Sunday, + }), + b'V' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Iso, + }), + b'w' => component!(Weekday { + repr: modifier::WeekdayRepr::Sunday, + one_indexed: true, + case_sensitive: true, + }), + b'W' => component!(WeekNumber { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::WeekNumberRepr::Monday, + }), + b'x' => BorrowedFormatItem::Compound(&[ + component!(Month { + repr: modifier::MonthRepr::Numerical, + padding: modifier::Padding::Zero, + case_sensitive: true, + }), + BorrowedFormatItem::Literal(b"/"), + component!(Day { + padding: modifier::Padding::Zero + }), + BorrowedFormatItem::Literal(b"/"), + component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + ]), + b'X' => BorrowedFormatItem::Compound(&[ + component!(Hour { + padding: modifier::Padding::Zero, + is_12_hour_clock: false, + }), + BorrowedFormatItem::Literal(b":"), + component!(Minute { + padding: modifier::Padding::Zero, + }), + BorrowedFormatItem::Literal(b":"), + component!(Second { + padding: modifier::Padding::Zero, + }), + ]), + b'y' => component!(Year { + padding: padding_or_default(*padding, modifier::Padding::Zero), + repr: modifier::YearRepr::LastTwo, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'Y' => component!(Year { + padding: modifier::Padding::Zero, + repr: modifier::YearRepr::Full, + range: modifier::YearRange::Extended, + iso_week_based: false, + sign_is_mandatory: false, + }), + b'z' => BorrowedFormatItem::Compound(&[ + component!(OffsetHour { + sign_is_mandatory: true, + padding: modifier::Padding::Zero, + }), + component!(OffsetMinute { + padding: modifier::Padding::Zero, + }), + ]), + b'Z' => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "unsupported component", + _span: component.span, + }), + public: InvalidFormatDescription::NotSupported { + what: "component", + context: "", + index: component.span.start.byte as usize, + }, + }) + } + _ => { + return Err(Error { + _inner: unused(ErrorInner { + _message: "invalid component", + _span: component.span, + }), + public: InvalidFormatDescription::InvalidComponentName { + name: String::from_utf8_lossy(&[*component]).into_owned(), + index: component.span.start.byte as usize, + }, + }) + } + }) +} diff --git a/vendor/time/src/format_description/well_known/iso8601.rs b/vendor/time/src/format_description/well_known/iso8601.rs new file mode 100644 index 00000000..6b8ff2ab --- /dev/null +++ b/vendor/time/src/format_description/well_known/iso8601.rs @@ -0,0 +1,257 @@ +//! The format described in ISO 8601. + +mod adt_hack; + +use core::num::NonZeroU8; + +#[doc(hidden, no_inline)] +pub use self::adt_hack::DoNotRelyOnWhatThisIs; +pub use self::adt_hack::EncodedConfig; + +/// The format described in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html). +/// +/// This implementation is of ISO 8601-1:2019. It may not be compatible with other versions. +/// +/// The const parameter `CONFIG` **must** be a value that was returned by [`Config::encode`]. +/// Passing any other value is **unspecified behavior**. +/// +/// Example: 1997-11-21T09:55:06.000000000-06:00 +/// +/// # Examples +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Iso8601; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1997-11-12 9:55:06 -6:00).format(&Iso8601::DEFAULT)?, +/// "1997-11-12T09:55:06.000000000-06:00" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Iso8601<const CONFIG: EncodedConfig = { Config::DEFAULT.encode() }>; + +impl<const CONFIG: EncodedConfig> core::fmt::Debug for Iso8601<CONFIG> { + 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 { + #[allow(missing_docs)] + decimal_digits: Option<NonZeroU8>, + }, + /// Format the hour and minute. Seconds and nanoseconds will be represented with the specified + /// number of decimal digits, if any. + Minute { + #[allow(missing_docs)] + decimal_digits: Option<NonZeroU8>, + }, + /// Format the hour, minute, and second. Nanoseconds will be represented with the specified + /// number of decimal digits, if any. + Second { + #[allow(missing_docs)] + decimal_digits: Option<NonZeroU8>, + }, +} + +/// The precision for the UTC offset. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OffsetPrecision { + /// Format only the offset hour. Requires the offset minute to be zero. + Hour, + /// Format both the offset hour and minute. + Minute, +} + +/// Configuration for [`Iso8601`]. +// This is only used as a const generic, so there's no need to have a number of implementations on +// it. +#[allow(missing_copy_implementations)] +#[doc(alias = "EncodedConfig")] // People will likely search for `EncodedConfig`, so show them this. +#[derive(Debug)] +pub struct Config { + /// Which components, if any, will be formatted. + pub(crate) formatted_components: FormattedComponents, + /// Whether the format contains separators (such as `-` or `:`). + pub(crate) use_separators: bool, + /// Whether the year is six digits. + pub(crate) year_is_six_digits: bool, + /// The format used for the date. + pub(crate) date_kind: DateKind, + /// The precision and number of decimal digits present for the time. + pub(crate) time_precision: TimePrecision, + /// The precision for the UTC offset. + pub(crate) offset_precision: OffsetPrecision, +} + +impl Config { + /// A configuration for the [`Iso8601`] format. + /// + /// The following is the default behavior: + /// + /// - The configuration can be used for both formatting and parsing. + /// - The date, time, and UTC offset are all formatted. + /// - Separators (such as `-` and `:`) are included. + /// - The year contains four digits, such that the year must be between 0 and 9999. + /// - The date uses the calendar format. + /// - The time has precision to the second and nine decimal digits. + /// - The UTC offset has precision to the minute. + /// + /// If you need different behavior, use the setter methods on this struct. + pub const DEFAULT: Self = Self { + formatted_components: FormattedComponents::DateTimeOffset, + use_separators: true, + year_is_six_digits: false, + date_kind: DateKind::Calendar, + time_precision: TimePrecision::Second { + decimal_digits: NonZeroU8::new(9), + }, + offset_precision: OffsetPrecision::Minute, + }; + + /// 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. + pub const fn set_formatted_components(self, formatted_components: FormattedComponents) -> Self { + Self { + formatted_components, + ..self + } + } + + /// Set whether the format contains separators (such as `-` or `:`). + pub const fn set_use_separators(self, use_separators: bool) -> Self { + Self { + use_separators, + ..self + } + } + + /// Set whether the year is six digits. + pub const fn set_year_is_six_digits(self, year_is_six_digits: bool) -> Self { + Self { + year_is_six_digits, + ..self + } + } + + /// Set the format used for the date. + pub const fn set_date_kind(self, date_kind: DateKind) -> Self { + Self { date_kind, ..self } + } + + /// Set the precision and number of decimal digits present for the time. + pub const fn set_time_precision(self, time_precision: TimePrecision) -> Self { + Self { + time_precision, + ..self + } + } + + /// Set the precision for the UTC offset. + pub const fn set_offset_precision(self, offset_precision: OffsetPrecision) -> Self { + Self { + offset_precision, + ..self + } + } +} diff --git a/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs new file mode 100644 index 00000000..6d3daebb --- /dev/null +++ b/vendor/time/src/format_description/well_known/iso8601/adt_hack.rs @@ -0,0 +1,247 @@ +//! Hackery to work around not being able to use ADTs in const generics on stable. + +use core::num::NonZeroU8; + +#[cfg(feature = "formatting")] +use super::Iso8601; +use super::{Config, DateKind, FormattedComponents as FC, OffsetPrecision, TimePrecision}; + +// This provides a way to include `EncodedConfig` in documentation without displaying the type it is +// aliased to. +#[doc(hidden)] +pub type DoNotRelyOnWhatThisIs = u128; + +/// An encoded [`Config`] that can be used as a const parameter to [`Iso8601`](super::Iso8601). +/// +/// The type this is aliased to must not be relied upon. It can change in any release without +/// notice. +pub type EncodedConfig = DoNotRelyOnWhatThisIs; + +#[cfg(feature = "formatting")] +impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { + /// The user-provided configuration for the ISO 8601 format. + const CONFIG: Config = Config::decode(CONFIG); + /// Whether the date should be formatted. + pub(crate) const FORMAT_DATE: bool = matches!( + Self::CONFIG.formatted_components, + FC::Date | FC::DateTime | FC::DateTimeOffset + ); + /// Whether the time should be formatted. + pub(crate) const FORMAT_TIME: bool = matches!( + Self::CONFIG.formatted_components, + FC::Time | FC::DateTime | FC::DateTimeOffset | FC::TimeOffset + ); + /// Whether the UTC offset should be formatted. + pub(crate) const FORMAT_OFFSET: bool = matches!( + Self::CONFIG.formatted_components, + FC::Offset | FC::DateTimeOffset | FC::TimeOffset + ); + /// Whether the year is six digits. + pub(crate) const YEAR_IS_SIX_DIGITS: bool = Self::CONFIG.year_is_six_digits; + /// Whether the format contains separators (such as `-` or `:`). + pub(crate) const USE_SEPARATORS: bool = Self::CONFIG.use_separators; + /// Which format to use for the date. + pub(crate) const DATE_KIND: DateKind = Self::CONFIG.date_kind; + /// The precision and number of decimal digits to use for the time. + pub(crate) const TIME_PRECISION: TimePrecision = Self::CONFIG.time_precision; + /// The precision for the UTC offset. + pub(crate) const OFFSET_PRECISION: OffsetPrecision = Self::CONFIG.offset_precision; +} + +impl Config { + /// Encode the configuration, permitting it to be used as a const parameter of [`Iso8601`]. + /// + /// The value returned by this method must only be used as a const parameter to [`Iso8601`]. Any + /// other usage is unspecified behavior. + pub const fn encode(&self) -> EncodedConfig { + let mut bytes = [0; EncodedConfig::BITS as usize / 8]; + + bytes[0] = match self.formatted_components { + FC::None => 0, + FC::Date => 1, + FC::Time => 2, + FC::Offset => 3, + FC::DateTime => 4, + FC::DateTimeOffset => 5, + FC::TimeOffset => 6, + }; + bytes[1] = self.use_separators as u8; + bytes[2] = self.year_is_six_digits as u8; + bytes[3] = match self.date_kind { + DateKind::Calendar => 0, + DateKind::Week => 1, + DateKind::Ordinal => 2, + }; + bytes[4] = match self.time_precision { + TimePrecision::Hour { .. } => 0, + TimePrecision::Minute { .. } => 1, + TimePrecision::Second { .. } => 2, + }; + bytes[5] = match self.time_precision { + TimePrecision::Hour { decimal_digits } + | TimePrecision::Minute { decimal_digits } + | TimePrecision::Second { decimal_digits } => match decimal_digits { + None => 0, + Some(decimal_digits) => decimal_digits.get(), + }, + }; + bytes[6] = match self.offset_precision { + OffsetPrecision::Hour => 0, + OffsetPrecision::Minute => 1, + }; + + EncodedConfig::from_be_bytes(bytes) + } + + /// Decode the configuration. The configuration must have been generated from + /// [`Config::encode`]. + pub(super) const fn decode(encoded: EncodedConfig) -> Self { + let bytes = encoded.to_be_bytes(); + + let formatted_components = match bytes[0] { + 0 => FC::None, + 1 => FC::Date, + 2 => FC::Time, + 3 => FC::Offset, + 4 => FC::DateTime, + 5 => FC::DateTimeOffset, + 6 => FC::TimeOffset, + _ => panic!("invalid configuration"), + }; + let use_separators = match bytes[1] { + 0 => false, + 1 => true, + _ => panic!("invalid configuration"), + }; + let year_is_six_digits = match bytes[2] { + 0 => false, + 1 => true, + _ => panic!("invalid configuration"), + }; + let date_kind = match bytes[3] { + 0 => DateKind::Calendar, + 1 => DateKind::Week, + 2 => DateKind::Ordinal, + _ => panic!("invalid configuration"), + }; + let time_precision = match bytes[4] { + 0 => TimePrecision::Hour { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + 1 => TimePrecision::Minute { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + 2 => TimePrecision::Second { + decimal_digits: NonZeroU8::new(bytes[5]), + }, + _ => panic!("invalid configuration"), + }; + let offset_precision = match bytes[6] { + 0 => OffsetPrecision::Hour, + 1 => OffsetPrecision::Minute, + _ => panic!("invalid configuration"), + }; + + // No `for` loops in `const fn`. + let mut idx = 7; // first unused byte + while idx < EncodedConfig::BITS as usize / 8 { + assert!(bytes[idx] == 0, "invalid configuration"); + idx += 1; + } + + Self { + formatted_components, + use_separators, + year_is_six_digits, + date_kind, + time_precision, + offset_precision, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! eq { + ($a:expr, $b:expr) => {{ + let a = $a; + let b = $b; + a.formatted_components == b.formatted_components + && a.use_separators == b.use_separators + && a.year_is_six_digits == b.year_is_six_digits + && a.date_kind == b.date_kind + && a.time_precision == b.time_precision + && a.offset_precision == b.offset_precision + }}; + } + + #[test] + fn encoding_roundtrip() { + macro_rules! assert_roundtrip { + ($config:expr) => { + let config = $config; + let encoded = config.encode(); + let decoded = Config::decode(encoded); + assert!(eq!(config, decoded)); + }; + } + + assert_roundtrip!(Config::DEFAULT); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::None)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Date)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Time)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::Offset)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTime)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::DateTimeOffset)); + assert_roundtrip!(Config::DEFAULT.set_formatted_components(FC::TimeOffset)); + assert_roundtrip!(Config::DEFAULT.set_use_separators(false)); + assert_roundtrip!(Config::DEFAULT.set_use_separators(true)); + assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(false)); + assert_roundtrip!(Config::DEFAULT.set_year_is_six_digits(true)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Calendar)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Week)); + assert_roundtrip!(Config::DEFAULT.set_date_kind(DateKind::Ordinal)); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second { + decimal_digits: None, + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Hour { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Minute { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_time_precision(TimePrecision::Second { + decimal_digits: NonZeroU8::new(1), + })); + assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Hour)); + assert_roundtrip!(Config::DEFAULT.set_offset_precision(OffsetPrecision::Minute)); + } + + macro_rules! assert_decode_fail { + ($encoding:expr) => { + assert!(std::panic::catch_unwind(|| { + Config::decode($encoding); + }) + .is_err()); + }; + } + + #[test] + fn decode_fail() { + assert_decode_fail!(0x07_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_02_00_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_02_00_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_03_00_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_03_00_00_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_00_00_02_00_00_00_00_00_00_00_00_00); + assert_decode_fail!(0x00_00_00_00_00_00_00_01_00_00_00_00_00_00_00_00); + } +} diff --git a/vendor/time/src/format_description/well_known/rfc2822.rs b/vendor/time/src/format_description/well_known/rfc2822.rs new file mode 100644 index 00000000..3c890ab1 --- /dev/null +++ b/vendor/time/src/format_description/well_known/rfc2822.rs @@ -0,0 +1,30 @@ +//! The format described in RFC 2822. + +/// The format described in [RFC 2822](https://tools.ietf.org/html/rfc2822#section-3.3). +/// +/// Example: Fri, 21 Nov 1997 09:55:06 -0600 +/// +/// # Examples +#[cfg_attr(feature = "parsing", doc = "```rust")] +#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")] +/// # use time::{format_description::well_known::Rfc2822, OffsetDateTime}; +/// use time_macros::datetime; +/// assert_eq!( +/// OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?, +/// datetime!(1993-06-12 13:25:19 +00:00) +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +/// +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Rfc2822; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1997-11-21 09:55:06 -06:00).format(&Rfc2822)?, +/// "Fri, 21 Nov 1997 09:55:06 -0600" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rfc2822; diff --git a/vendor/time/src/format_description/well_known/rfc3339.rs b/vendor/time/src/format_description/well_known/rfc3339.rs new file mode 100644 index 00000000..f0873cba --- /dev/null +++ b/vendor/time/src/format_description/well_known/rfc3339.rs @@ -0,0 +1,30 @@ +//! The format described in RFC 3339. + +/// The format described in [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6). +/// +/// Format example: 1985-04-12T23:20:50.52Z +/// +/// # Examples +#[cfg_attr(feature = "parsing", doc = "```rust")] +#[cfg_attr(not(feature = "parsing"), doc = "```rust,ignore")] +/// # use time::{format_description::well_known::Rfc3339, OffsetDateTime}; +/// # use time_macros::datetime; +/// assert_eq!( +/// OffsetDateTime::parse("1985-04-12T23:20:50.52Z", &Rfc3339)?, +/// datetime!(1985-04-12 23:20:50.52 +00:00) +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +/// +#[cfg_attr(feature = "formatting", doc = "```rust")] +#[cfg_attr(not(feature = "formatting"), doc = "```rust,ignore")] +/// # use time::format_description::well_known::Rfc3339; +/// # use time_macros::datetime; +/// assert_eq!( +/// datetime!(1985-04-12 23:20:50.52 +00:00).format(&Rfc3339)?, +/// "1985-04-12T23:20:50.52Z" +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rfc3339; diff --git a/vendor/time/src/formatting/formattable.rs b/vendor/time/src/formatting/formattable.rs new file mode 100644 index 00000000..7d1ce031 --- /dev/null +++ b/vendor/time/src/formatting/formattable.rs @@ -0,0 +1,312 @@ +//! 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::format_description::well_known::iso8601::EncodedConfig; +use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339}; +use crate::format_description::{BorrowedFormatItem, OwnedFormatItem}; +use crate::formatting::{ + format_component, format_number_pad_zero, iso8601, write, MONTH_NAMES, WEEKDAY_NAMES, +}; +use crate::{error, Date, Time, UtcOffset}; + +/// A type that describes a format. +/// +/// Implementors of [`Formattable`] are [format descriptions](crate::format_description). +/// +/// [`Date::format`] and [`Time::format`] each use a format description to generate +/// a String from their data. See the respective methods for usage examples. +#[cfg_attr(docsrs, doc(notable_trait))] +pub trait Formattable: sealed::Sealed {} +impl Formattable for BorrowedFormatItem<'_> {} +impl Formattable for [BorrowedFormatItem<'_>] {} +impl Formattable for OwnedFormatItem {} +impl Formattable for [OwnedFormatItem] {} +impl Formattable for Rfc3339 {} +impl Formattable for Rfc2822 {} +impl<const CONFIG: EncodedConfig> Formattable for Iso8601<CONFIG> {} +impl<T: Deref> Formattable for T where T::Target: Formattable {} + +/// Seal the trait to prevent downstream users from implementing it. +mod sealed { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Format the item using a format description, the intended output, and the various components. + 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), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format>; + + /// Format the item directly to a `String`. + fn format( + &self, + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<String, error::Format> { + let mut buf = Vec::new(); + self.format_into(&mut buf, date, time, offset)?; + Ok(String::from_utf8_lossy(&buf).into_owned()) + } + } +} + +impl sealed::Sealed for BorrowedFormatItem<'_> { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + Ok(match *self { + Self::Literal(literal) => write(output, literal)?, + Self::Component(component) => format_component(output, component, date, time, offset)?, + Self::Compound(items) => items.format_into(output, date, time, offset)?, + Self::Optional(item) => item.format_into(output, date, time, offset)?, + Self::First(items) => match items { + [] => 0, + [item, ..] => item.format_into(output, date, time, offset)?, + }, + }) + } +} + +impl sealed::Sealed for [BorrowedFormatItem<'_>] { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + let mut bytes = 0; + for item in self.iter() { + bytes += item.format_into(output, date, time, offset)?; + } + Ok(bytes) + } +} + +impl sealed::Sealed for OwnedFormatItem { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + match self { + Self::Literal(literal) => Ok(write(output, literal)?), + Self::Component(component) => format_component(output, *component, date, time, offset), + Self::Compound(items) => items.format_into(output, date, time, offset), + Self::Optional(item) => item.format_into(output, date, time, offset), + Self::First(items) => match &**items { + [] => Ok(0), + [item, ..] => item.format_into(output, date, time, offset), + }, + } + } +} + +impl sealed::Sealed for [OwnedFormatItem] { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + let mut bytes = 0; + for item in self.iter() { + bytes += item.format_into(output, date, time, offset)?; + } + Ok(bytes) + } +} + +impl<T: Deref> sealed::Sealed for T +where + T::Target: sealed::Sealed, +{ + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + self.deref().format_into(output, date, time, offset) + } +} + +impl sealed::Sealed for Rfc2822 { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + let date = date.ok_or(error::Format::InsufficientTypeInformation)?; + let time = time.ok_or(error::Format::InsufficientTypeInformation)?; + let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?; + + let mut bytes = 0; + + let (year, month, day) = date.to_calendar_date(); + + if year < 1900 { + return Err(error::Format::InvalidComponent("year")); + } + if offset.seconds_past_minute() != 0 { + return Err(error::Format::InvalidComponent("offset_second")); + } + + bytes += write( + output, + &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3], + )?; + bytes += write(output, b", ")?; + bytes += format_number_pad_zero::<2>(output, day)?; + bytes += write(output, b" ")?; + bytes += write( + output, + &MONTH_NAMES[u8::from(month).extend::<usize>() - 1][..3], + )?; + bytes += write(output, b" ")?; + bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?; + bytes += write(output, b" ")?; + bytes += format_number_pad_zero::<2>(output, time.hour())?; + bytes += write(output, b":")?; + bytes += format_number_pad_zero::<2>(output, time.minute())?; + bytes += write(output, b":")?; + bytes += format_number_pad_zero::<2>(output, time.second())?; + bytes += write(output, b" ")?; + bytes += write(output, if offset.is_negative() { b"-" } else { b"+" })?; + bytes += format_number_pad_zero::<2>(output, offset.whole_hours().unsigned_abs())?; + bytes += format_number_pad_zero::<2>(output, offset.minutes_past_hour().unsigned_abs())?; + + Ok(bytes) + } +} + +impl sealed::Sealed for Rfc3339 { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + let date = date.ok_or(error::Format::InsufficientTypeInformation)?; + let time = time.ok_or(error::Format::InsufficientTypeInformation)?; + let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?; + + let mut bytes = 0; + + let year = date.year(); + + if !(0..10_000).contains(&year) { + return Err(error::Format::InvalidComponent("year")); + } + if offset.whole_hours().unsigned_abs() > 23 { + return Err(error::Format::InvalidComponent("offset_hour")); + } + if offset.seconds_past_minute() != 0 { + return Err(error::Format::InvalidComponent("offset_second")); + } + + bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?; + bytes += write(output, b"-")?; + bytes += format_number_pad_zero::<2>(output, u8::from(date.month()))?; + bytes += write(output, b"-")?; + bytes += format_number_pad_zero::<2>(output, date.day())?; + bytes += write(output, b"T")?; + bytes += format_number_pad_zero::<2>(output, time.hour())?; + bytes += write(output, b":")?; + bytes += format_number_pad_zero::<2>(output, time.minute())?; + bytes += write(output, b":")?; + bytes += format_number_pad_zero::<2>(output, time.second())?; + + if time.nanosecond() != 0 { + let nanos = time.nanosecond(); + bytes += write(output, b".")?; + bytes += if nanos % 10 != 0 { + format_number_pad_zero::<9>(output, nanos) + } else if (nanos / 10) % 10 != 0 { + format_number_pad_zero::<8>(output, nanos / 10) + } else if (nanos / 100) % 10 != 0 { + format_number_pad_zero::<7>(output, nanos / 100) + } else if (nanos / 1_000) % 10 != 0 { + format_number_pad_zero::<6>(output, nanos / 1_000) + } else if (nanos / 10_000) % 10 != 0 { + format_number_pad_zero::<5>(output, nanos / 10_000) + } else if (nanos / 100_000) % 10 != 0 { + format_number_pad_zero::<4>(output, nanos / 100_000) + } else if (nanos / 1_000_000) % 10 != 0 { + format_number_pad_zero::<3>(output, nanos / 1_000_000) + } else if (nanos / 10_000_000) % 10 != 0 { + format_number_pad_zero::<2>(output, nanos / 10_000_000) + } else { + format_number_pad_zero::<1>(output, nanos / 100_000_000) + }?; + } + + if offset == UtcOffset::UTC { + bytes += write(output, b"Z")?; + return Ok(bytes); + } + + bytes += write(output, if offset.is_negative() { b"-" } else { b"+" })?; + bytes += format_number_pad_zero::<2>(output, offset.whole_hours().unsigned_abs())?; + bytes += write(output, b":")?; + bytes += format_number_pad_zero::<2>(output, offset.minutes_past_hour().unsigned_abs())?; + + Ok(bytes) + } +} + +impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> { + fn format_into( + &self, + output: &mut (impl io::Write + ?Sized), + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, + ) -> Result<usize, error::Format> { + let mut bytes = 0; + + if Self::FORMAT_DATE { + let date = date.ok_or(error::Format::InsufficientTypeInformation)?; + bytes += iso8601::format_date::<CONFIG>(output, date)?; + } + if Self::FORMAT_TIME { + let time = time.ok_or(error::Format::InsufficientTypeInformation)?; + bytes += iso8601::format_time::<CONFIG>(output, time)?; + } + if Self::FORMAT_OFFSET { + let offset = offset.ok_or(error::Format::InsufficientTypeInformation)?; + bytes += iso8601::format_offset::<CONFIG>(output, offset)?; + } + + if bytes == 0 { + // The only reason there would be no bytes written is if the format was only for + // parsing. + panic!("attempted to format a parsing-only format description"); + } + + Ok(bytes) + } +} diff --git a/vendor/time/src/formatting/iso8601.rs b/vendor/time/src/formatting/iso8601.rs new file mode 100644 index 00000000..a2d2affa --- /dev/null +++ b/vendor/time/src/formatting/iso8601.rs @@ -0,0 +1,142 @@ +//! Helpers for implementing formatting for ISO 8601. + +use std::io; + +use num_conv::prelude::*; + +use crate::convert::*; +use crate::format_description::well_known::iso8601::{ + DateKind, EncodedConfig, OffsetPrecision, TimePrecision, +}; +use crate::format_description::well_known::Iso8601; +use crate::formatting::{format_float, format_number_pad_zero, write, write_if, write_if_else}; +use crate::{error, Date, Time, UtcOffset}; + +/// Format the date portion of ISO 8601. +pub(super) fn format_date<const CONFIG: EncodedConfig>( + output: &mut (impl io::Write + ?Sized), + date: Date, +) -> Result<usize, error::Format> { + let mut bytes = 0; + + match Iso8601::<CONFIG>::DATE_KIND { + DateKind::Calendar => { + let (year, month, day) = date.to_calendar_date(); + if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS { + bytes += write_if_else(output, year < 0, b"-", b"+")?; + bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?; + } else if !(0..=9999).contains(&year) { + return Err(error::Format::InvalidComponent("year")); + } else { + bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?; + } + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?; + bytes += format_number_pad_zero::<2>(output, u8::from(month))?; + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?; + bytes += format_number_pad_zero::<2>(output, day)?; + } + DateKind::Week => { + let (year, week, day) = date.to_iso_week_date(); + if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS { + bytes += write_if_else(output, year < 0, b"-", b"+")?; + bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?; + } else if !(0..=9999).contains(&year) { + return Err(error::Format::InvalidComponent("year")); + } else { + bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?; + } + bytes += write_if_else(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-W", b"W")?; + bytes += format_number_pad_zero::<2>(output, week)?; + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?; + bytes += format_number_pad_zero::<1>(output, day.number_from_monday())?; + } + DateKind::Ordinal => { + let (year, day) = date.to_ordinal_date(); + if Iso8601::<CONFIG>::YEAR_IS_SIX_DIGITS { + bytes += write_if_else(output, year < 0, b"-", b"+")?; + bytes += format_number_pad_zero::<6>(output, year.unsigned_abs())?; + } else if !(0..=9999).contains(&year) { + return Err(error::Format::InvalidComponent("year")); + } else { + bytes += format_number_pad_zero::<4>(output, year.cast_unsigned())?; + } + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b"-")?; + bytes += format_number_pad_zero::<3>(output, day)?; + } + } + + Ok(bytes) +} + +/// Format the time portion of ISO 8601. +pub(super) fn format_time<const CONFIG: EncodedConfig>( + output: &mut (impl io::Write + ?Sized), + time: Time, +) -> Result<usize, error::Format> { + let mut bytes = 0; + + // The "T" can only be omitted in extended format where there is no date being formatted. + bytes += write_if( + output, + Iso8601::<CONFIG>::USE_SEPARATORS || Iso8601::<CONFIG>::FORMAT_DATE, + b"T", + )?; + + let (hours, minutes, seconds, nanoseconds) = time.as_hms_nano(); + + match Iso8601::<CONFIG>::TIME_PRECISION { + TimePrecision::Hour { decimal_digits } => { + let hours = (hours as f64) + + (minutes as f64) / Minute::per(Hour) as f64 + + (seconds as f64) / Second::per(Hour) as f64 + + (nanoseconds as f64) / Nanosecond::per(Hour) as f64; + format_float(output, hours, 2, decimal_digits)?; + } + TimePrecision::Minute { decimal_digits } => { + bytes += format_number_pad_zero::<2>(output, hours)?; + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; + let minutes = (minutes as f64) + + (seconds as f64) / Second::per(Minute) as f64 + + (nanoseconds as f64) / Nanosecond::per(Minute) as f64; + bytes += format_float(output, minutes, 2, decimal_digits)?; + } + TimePrecision::Second { decimal_digits } => { + bytes += format_number_pad_zero::<2>(output, hours)?; + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; + bytes += format_number_pad_zero::<2>(output, minutes)?; + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; + let seconds = (seconds as f64) + (nanoseconds as f64) / Nanosecond::per(Second) as f64; + bytes += format_float(output, seconds, 2, decimal_digits)?; + } + } + + Ok(bytes) +} + +/// Format the UTC offset portion of ISO 8601. +pub(super) fn format_offset<const CONFIG: EncodedConfig>( + output: &mut (impl io::Write + ?Sized), + offset: UtcOffset, +) -> Result<usize, error::Format> { + if Iso8601::<CONFIG>::FORMAT_TIME && offset.is_utc() { + return Ok(write(output, b"Z")?); + } + + let mut bytes = 0; + + let (hours, minutes, seconds) = offset.as_hms(); + if seconds != 0 { + return Err(error::Format::InvalidComponent("offset_second")); + } + bytes += write_if_else(output, offset.is_negative(), b"-", b"+")?; + bytes += format_number_pad_zero::<2>(output, hours.unsigned_abs())?; + + if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Hour && minutes != 0 { + return Err(error::Format::InvalidComponent("offset_minute")); + } else if Iso8601::<CONFIG>::OFFSET_PRECISION == OffsetPrecision::Minute { + bytes += write_if(output, Iso8601::<CONFIG>::USE_SEPARATORS, b":")?; + bytes += format_number_pad_zero::<2>(output, minutes.unsigned_abs())?; + } + + Ok(bytes) +} diff --git a/vendor/time/src/formatting/mod.rs b/vendor/time/src/formatting/mod.rs new file mode 100644 index 00000000..c3837107 --- /dev/null +++ b/vendor/time/src/formatting/mod.rs @@ -0,0 +1,517 @@ +//! Formatting for various types. + +pub(crate) mod formattable; +mod iso8601; + +use core::num::NonZeroU8; +use std::io; + +use num_conv::prelude::*; + +pub use self::formattable::Formattable; +use crate::convert::*; +use crate::ext::DigitCount; +use crate::format_description::{modifier, Component}; +use crate::{error, Date, OffsetDateTime, Time, UtcOffset}; + +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. +pub(crate) fn write(output: &mut (impl io::Write + ?Sized), bytes: &[u8]) -> io::Result<usize> { + output.write_all(bytes)?; + Ok(bytes.len()) +} + +/// If `pred` is true, write all bytes to the output, returning the number of bytes written. +pub(crate) fn write_if( + output: &mut (impl io::Write + ?Sized), + pred: bool, + bytes: &[u8], +) -> io::Result<usize> { + if pred { + write(output, bytes) + } else { + Ok(0) + } +} + +/// If `pred` is true, write `true_bytes` to the output. Otherwise, write `false_bytes`. +pub(crate) fn write_if_else( + output: &mut (impl io::Write + ?Sized), + pred: bool, + true_bytes: &[u8], + false_bytes: &[u8], +) -> io::Result<usize> { + write(output, if pred { true_bytes } else { false_bytes }) +} + +/// Write the floating point number to the output, returning the number of bytes written. +/// +/// This method accepts the number of digits before and after the decimal. The value will be padded +/// with zeroes to the left if necessary. +pub(crate) fn format_float( + output: &mut (impl io::Write + ?Sized), + value: f64, + digits_before_decimal: u8, + digits_after_decimal: Option<NonZeroU8>, +) -> io::Result<usize> { + match digits_after_decimal { + Some(digits_after_decimal) => { + // Truncate the decimal points up to the precision + let trunc_num = 10_f64.powi(digits_after_decimal.get().cast_signed().extend()); + let value = f64::trunc(value * trunc_num) / trunc_num; + + let digits_after_decimal = digits_after_decimal.get().extend(); + let width = digits_before_decimal.extend::<usize>() + 1 + digits_after_decimal; + write!(output, "{value:0>width$.digits_after_decimal$}")?; + Ok(width) + } + None => { + let value = value.trunc() as u64; + let width = digits_before_decimal.extend(); + write!(output, "{value:0>width$}")?; + Ok(width) + } + } +} + +/// Format a number with the provided padding and width. +/// +/// The sign must be written by the caller. +pub(crate) fn format_number<const WIDTH: u8>( + output: &mut (impl io::Write + ?Sized), + value: impl itoa::Integer + DigitCount + Copy, + padding: modifier::Padding, +) -> Result<usize, io::Error> { + match padding { + modifier::Padding::Space => format_number_pad_space::<WIDTH>(output, value), + modifier::Padding::Zero => format_number_pad_zero::<WIDTH>(output, value), + modifier::Padding::None => format_number_pad_none(output, value), + } +} + +/// Format a number with the provided width and spaces as padding. +/// +/// The sign must be written by the caller. +pub(crate) fn format_number_pad_space<const WIDTH: u8>( + output: &mut (impl io::Write + ?Sized), + value: impl itoa::Integer + DigitCount + Copy, +) -> Result<usize, io::Error> { + let mut bytes = 0; + for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { + bytes += write(output, b" ")?; + } + bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; + Ok(bytes) +} + +/// Format a number with the provided width and zeros as padding. +/// +/// The sign must be written by the caller. +pub(crate) fn format_number_pad_zero<const WIDTH: u8>( + output: &mut (impl io::Write + ?Sized), + value: impl itoa::Integer + DigitCount + Copy, +) -> Result<usize, io::Error> { + let mut bytes = 0; + for _ in 0..(WIDTH.saturating_sub(value.num_digits())) { + bytes += write(output, b"0")?; + } + bytes += write(output, itoa::Buffer::new().format(value).as_bytes())?; + Ok(bytes) +} + +/// Format a number with no padding. +/// +/// If the sign is mandatory, the sign must be written by the caller. +pub(crate) fn format_number_pad_none( + output: &mut (impl io::Write + ?Sized), + value: impl itoa::Integer + Copy, +) -> Result<usize, io::Error> { + write(output, itoa::Buffer::new().format(value).as_bytes()) +} + +/// Format the provided component into the designated output. An `Err` will be returned if the +/// component requires information that it does not provide or if the value cannot be output to the +/// stream. +pub(crate) fn format_component( + output: &mut (impl io::Write + ?Sized), + component: Component, + date: Option<Date>, + time: Option<Time>, + offset: Option<UtcOffset>, +) -> Result<usize, error::Format> { + use Component::*; + Ok(match (component, date, time, offset) { + (Day(modifier), Some(date), ..) => fmt_day(output, date, modifier)?, + (Month(modifier), Some(date), ..) => fmt_month(output, date, modifier)?, + (Ordinal(modifier), Some(date), ..) => fmt_ordinal(output, date, modifier)?, + (Weekday(modifier), Some(date), ..) => fmt_weekday(output, date, modifier)?, + (WeekNumber(modifier), Some(date), ..) => fmt_week_number(output, date, modifier)?, + (Year(modifier), Some(date), ..) => fmt_year(output, date, modifier)?, + (Hour(modifier), _, Some(time), _) => fmt_hour(output, time, modifier)?, + (Minute(modifier), _, Some(time), _) => fmt_minute(output, time, modifier)?, + (Period(modifier), _, Some(time), _) => fmt_period(output, time, modifier)?, + (Second(modifier), _, Some(time), _) => fmt_second(output, time, modifier)?, + (Subsecond(modifier), _, Some(time), _) => fmt_subsecond(output, time, modifier)?, + (OffsetHour(modifier), .., Some(offset)) => fmt_offset_hour(output, offset, modifier)?, + (OffsetMinute(modifier), .., Some(offset)) => fmt_offset_minute(output, offset, modifier)?, + (OffsetSecond(modifier), .., Some(offset)) => fmt_offset_second(output, offset, modifier)?, + (Ignore(_), ..) => 0, + (UnixTimestamp(modifier), Some(date), Some(time), Some(offset)) => { + fmt_unix_timestamp(output, date, time, offset, modifier)? + } + (End(modifier::End {}), ..) => 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. +fn fmt_day( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::Day { padding }: modifier::Day, +) -> Result<usize, io::Error> { + format_number::<2>(output, date.day(), padding) +} + +/// Format the month into the designated output. +fn fmt_month( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::Month { + padding, + repr, + case_sensitive: _, // no effect on formatting + }: modifier::Month, +) -> Result<usize, io::Error> { + match repr { + modifier::MonthRepr::Numerical => { + format_number::<2>(output, u8::from(date.month()), padding) + } + modifier::MonthRepr::Long => write( + output, + MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1], + ), + modifier::MonthRepr::Short => write( + output, + &MONTH_NAMES[u8::from(date.month()).extend::<usize>() - 1][..3], + ), + } +} + +/// Format the ordinal into the designated output. +fn fmt_ordinal( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::Ordinal { padding }: modifier::Ordinal, +) -> Result<usize, io::Error> { + format_number::<3>(output, date.ordinal(), padding) +} + +/// Format the weekday into the designated output. +fn fmt_weekday( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::Weekday { + repr, + one_indexed, + case_sensitive: _, // no effect on formatting + }: modifier::Weekday, +) -> Result<usize, io::Error> { + match repr { + modifier::WeekdayRepr::Short => write( + output, + &WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()][..3], + ), + modifier::WeekdayRepr::Long => write( + output, + WEEKDAY_NAMES[date.weekday().number_days_from_monday().extend::<usize>()], + ), + modifier::WeekdayRepr::Sunday => format_number::<1>( + output, + date.weekday().number_days_from_sunday() + u8::from(one_indexed), + modifier::Padding::None, + ), + modifier::WeekdayRepr::Monday => format_number::<1>( + output, + date.weekday().number_days_from_monday() + u8::from(one_indexed), + modifier::Padding::None, + ), + } +} + +/// Format the week number into the designated output. +fn fmt_week_number( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::WeekNumber { padding, repr }: modifier::WeekNumber, +) -> Result<usize, io::Error> { + format_number::<2>( + output, + match repr { + modifier::WeekNumberRepr::Iso => date.iso_week(), + modifier::WeekNumberRepr::Sunday => date.sunday_based_week(), + modifier::WeekNumberRepr::Monday => date.monday_based_week(), + }, + padding, + ) +} + +/// Format the year into the designated output. +fn fmt_year( + output: &mut (impl io::Write + ?Sized), + date: Date, + modifier::Year { + padding, + repr, + range, + iso_week_based, + sign_is_mandatory, + }: modifier::Year, +) -> Result<usize, error::Format> { + let full_year = if iso_week_based { + date.iso_year_week().0 + } else { + date.year() + }; + let value = match repr { + modifier::YearRepr::Full => full_year, + modifier::YearRepr::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 { + name: "year", + minimum: -9999, + maximum: 9999, + value: full_year.extend(), + conditional_message: Some("when `range:standard` is used"), + } + .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. +fn fmt_hour( + output: &mut (impl io::Write + ?Sized), + time: Time, + modifier::Hour { + padding, + is_12_hour_clock, + }: modifier::Hour, +) -> Result<usize, io::Error> { + let value = match (time.hour(), is_12_hour_clock) { + (hour, false) => hour, + (0 | 12, true) => 12, + (hour, true) if hour < 12 => hour, + (hour, true) => hour - 12, + }; + format_number::<2>(output, value, padding) +} + +/// Format the minute into the designated output. +fn fmt_minute( + output: &mut (impl io::Write + ?Sized), + time: Time, + modifier::Minute { padding }: modifier::Minute, +) -> Result<usize, io::Error> { + format_number::<2>(output, time.minute(), padding) +} + +/// Format the period into the designated output. +fn fmt_period( + output: &mut (impl io::Write + ?Sized), + time: Time, + modifier::Period { + is_uppercase, + case_sensitive: _, // no effect on formatting + }: modifier::Period, +) -> Result<usize, io::Error> { + match (time.hour() >= 12, is_uppercase) { + (false, false) => write(output, b"am"), + (false, true) => write(output, b"AM"), + (true, false) => write(output, b"pm"), + (true, true) => write(output, b"PM"), + } +} + +/// Format the second into the designated output. +fn fmt_second( + output: &mut (impl io::Write + ?Sized), + time: Time, + modifier::Second { padding }: modifier::Second, +) -> Result<usize, io::Error> { + format_number::<2>(output, time.second(), padding) +} + +/// Format the subsecond into the designated output. +fn fmt_subsecond( + output: &mut (impl io::Write + ?Sized), + time: Time, + modifier::Subsecond { digits }: modifier::Subsecond, +) -> Result<usize, io::Error> { + use modifier::SubsecondDigits::*; + let nanos = time.nanosecond(); + + if digits == Nine || (digits == OneOrMore && nanos % 10 != 0) { + format_number_pad_zero::<9>(output, nanos) + } else if digits == Eight || (digits == OneOrMore && (nanos / 10) % 10 != 0) { + format_number_pad_zero::<8>(output, nanos / 10) + } else if digits == Seven || (digits == OneOrMore && (nanos / 100) % 10 != 0) { + format_number_pad_zero::<7>(output, nanos / 100) + } else if digits == Six || (digits == OneOrMore && (nanos / 1_000) % 10 != 0) { + format_number_pad_zero::<6>(output, nanos / 1_000) + } else if digits == Five || (digits == OneOrMore && (nanos / 10_000) % 10 != 0) { + format_number_pad_zero::<5>(output, nanos / 10_000) + } else if digits == Four || (digits == OneOrMore && (nanos / 100_000) % 10 != 0) { + format_number_pad_zero::<4>(output, nanos / 100_000) + } else if digits == Three || (digits == OneOrMore && (nanos / 1_000_000) % 10 != 0) { + format_number_pad_zero::<3>(output, nanos / 1_000_000) + } else if digits == Two || (digits == OneOrMore && (nanos / 10_000_000) % 10 != 0) { + format_number_pad_zero::<2>(output, nanos / 10_000_000) + } else { + format_number_pad_zero::<1>(output, nanos / 100_000_000) + } +} + +/// Format the offset hour into the designated output. +fn fmt_offset_hour( + output: &mut (impl io::Write + ?Sized), + offset: UtcOffset, + modifier::OffsetHour { + padding, + sign_is_mandatory, + }: modifier::OffsetHour, +) -> Result<usize, io::Error> { + let mut bytes = 0; + if offset.is_negative() { + bytes += write(output, b"-")?; + } else if sign_is_mandatory { + bytes += write(output, b"+")?; + } + bytes += format_number::<2>(output, offset.whole_hours().unsigned_abs(), padding)?; + Ok(bytes) +} + +/// Format the offset minute into the designated output. +fn fmt_offset_minute( + output: &mut (impl io::Write + ?Sized), + offset: UtcOffset, + modifier::OffsetMinute { padding }: modifier::OffsetMinute, +) -> Result<usize, io::Error> { + format_number::<2>(output, offset.minutes_past_hour().unsigned_abs(), padding) +} + +/// Format the offset second into the designated output. +fn fmt_offset_second( + output: &mut (impl io::Write + ?Sized), + offset: UtcOffset, + modifier::OffsetSecond { padding }: modifier::OffsetSecond, +) -> Result<usize, io::Error> { + format_number::<2>(output, offset.seconds_past_minute().unsigned_abs(), padding) +} + +/// Format the Unix timestamp into the designated output. +fn fmt_unix_timestamp( + output: &mut (impl io::Write + ?Sized), + date: Date, + time: Time, + offset: UtcOffset, + modifier::UnixTimestamp { + precision, + sign_is_mandatory, + }: modifier::UnixTimestamp, +) -> Result<usize, io::Error> { + let date_time = OffsetDateTime::new_in_offset(date, time, offset).to_offset(UtcOffset::UTC); + + if date_time < OffsetDateTime::UNIX_EPOCH { + write(output, b"-")?; + } else if sign_is_mandatory { + write(output, b"+")?; + } + + match precision { + modifier::UnixTimestampPrecision::Second => { + format_number_pad_none(output, date_time.unix_timestamp().unsigned_abs()) + } + modifier::UnixTimestampPrecision::Millisecond => format_number_pad_none( + output, + (date_time.unix_timestamp_nanos() + / Nanosecond::per(Millisecond).cast_signed().extend::<i128>()) + .unsigned_abs(), + ), + modifier::UnixTimestampPrecision::Microsecond => format_number_pad_none( + output, + (date_time.unix_timestamp_nanos() + / Nanosecond::per(Microsecond).cast_signed().extend::<i128>()) + .unsigned_abs(), + ), + modifier::UnixTimestampPrecision::Nanosecond => { + format_number_pad_none(output, date_time.unix_timestamp_nanos().unsigned_abs()) + } + } +} diff --git a/vendor/time/src/hint.rs b/vendor/time/src/hint.rs new file mode 100644 index 00000000..ded72d4c --- /dev/null +++ b/vendor/time/src/hint.rs @@ -0,0 +1,26 @@ +//! Hints to the compiler that affects how code should be emitted or optimized. + +#![allow(dead_code)] // may be used in the future and has minimal overhead + +/// 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 +} diff --git a/vendor/time/src/instant.rs b/vendor/time/src/instant.rs new file mode 100644 index 00000000..66264007 --- /dev/null +++ b/vendor/time/src/instant.rs @@ -0,0 +1,287 @@ +//! The [`Instant`] struct and its associated `impl`s. + +#![allow(deprecated)] + +use core::borrow::Borrow; +use core::cmp::{Ord, Ordering, PartialEq, PartialOrd}; +use core::ops::{Add, Sub}; +use core::time::Duration as StdDuration; +use std::time::Instant as StdInstant; + +use crate::internal_macros::{impl_add_assign, impl_sub_assign}; +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 + /// # #![allow(deprecated)] + /// # use time::Instant; + /// println!("{:?}", Instant::now()); + /// ``` + pub fn now() -> Self { + Self(StdInstant::now()) + } + + /// Returns the amount of time elapsed since this instant was created. The duration will always + /// be nonnegative if the instant is not synthetically created. + /// + /// ```rust + /// # #![allow(deprecated)] + /// # use time::{Instant, ext::{NumericalStdDuration, NumericalDuration}}; + /// # use std::thread; + /// let instant = Instant::now(); + /// thread::sleep(1.std_milliseconds()); + /// assert!(instant.elapsed() >= 1.milliseconds()); + /// ``` + pub fn elapsed(self) -> Duration { + Self::now() - self + } + + /// 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 + /// # #![allow(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())); + /// ``` + pub fn checked_add(self, duration: Duration) -> Option<Self> { + if duration.is_zero() { + Some(self) + } else if duration.is_positive() { + self.0.checked_add(duration.unsigned_abs()).map(Self) + } else { + debug_assert!(duration.is_negative()); + self.0.checked_sub(duration.unsigned_abs()).map(Self) + } + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + /// + /// ```rust + /// # #![allow(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())); + /// ``` + pub fn checked_sub(self, duration: Duration) -> Option<Self> { + if duration.is_zero() { + Some(self) + } else if duration.is_positive() { + self.0.checked_sub(duration.unsigned_abs()).map(Self) + } else { + debug_assert!(duration.is_negative()); + self.0.checked_add(duration.unsigned_abs()).map(Self) + } + } + + /// Obtain the inner [`std::time::Instant`]. + /// + /// ```rust + /// # #![allow(deprecated)] + /// # use time::Instant; + /// let now = Instant::now(); + /// assert_eq!(now.into_inner(), now.0); + /// ``` + pub const fn into_inner(self) -> StdInstant { + self.0 + } +} + +impl From<StdInstant> for Instant { + fn from(instant: StdInstant) -> Self { + Self(instant) + } +} + +impl From<Instant> for StdInstant { + fn from(instant: Instant) -> Self { + instant.0 + } +} + +impl Sub for Instant { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, other: Self) -> Self::Output { + match self.0.cmp(&other.0) { + Ordering::Equal => Duration::ZERO, + Ordering::Greater => (self.0 - other.0) + .try_into() + .expect("overflow converting `std::time::Duration` to `time::Duration`"), + Ordering::Less => -Duration::try_from(other.0 - self.0) + .expect("overflow converting `std::time::Duration` to `time::Duration`"), + } + } +} + +impl Sub<StdInstant> for Instant { + type Output = Duration; + + fn sub(self, other: StdInstant) -> Self::Output { + self - Self(other) + } +} + +impl Sub<Instant> for StdInstant { + type Output = Duration; + + fn sub(self, other: Instant) -> Self::Output { + Instant(self) - other + } +} + +impl Add<Duration> for Instant { + type Output = Self; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. + fn add(self, duration: Duration) -> Self::Output { + if duration.is_positive() { + Self(self.0 + duration.unsigned_abs()) + } else if duration.is_negative() { + #[allow(clippy::unchecked_duration_subtraction)] + Self(self.0 - duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + self + } + } +} + +impl Add<Duration> for StdInstant { + type Output = Self; + + fn add(self, duration: Duration) -> Self::Output { + (Instant(self) + duration).0 + } +} + +impl Add<StdDuration> for Instant { + type Output = Self; + + fn add(self, duration: StdDuration) -> Self::Output { + Self(self.0 + duration) + } +} + +impl_add_assign!(Instant: Duration, StdDuration); +impl_add_assign!(StdInstant: Duration); + +impl Sub<Duration> for Instant { + type Output = Self; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. + fn sub(self, duration: Duration) -> Self::Output { + if duration.is_positive() { + #[allow(clippy::unchecked_duration_subtraction)] + Self(self.0 - duration.unsigned_abs()) + } else if duration.is_negative() { + Self(self.0 + duration.unsigned_abs()) + } else { + debug_assert!(duration.is_zero()); + self + } + } +} + +impl Sub<Duration> for StdInstant { + type Output = Self; + + fn sub(self, duration: Duration) -> Self::Output { + (Instant(self) - duration).0 + } +} + +impl Sub<StdDuration> for Instant { + type Output = Self; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. + fn sub(self, duration: StdDuration) -> Self::Output { + #[allow(clippy::unchecked_duration_subtraction)] + Self(self.0 - duration) + } +} + +impl_sub_assign!(Instant: Duration, StdDuration); +impl_sub_assign!(StdInstant: Duration); + +impl PartialEq<StdInstant> for Instant { + fn eq(&self, rhs: &StdInstant) -> bool { + self.0.eq(rhs) + } +} + +impl PartialEq<Instant> for StdInstant { + fn eq(&self, rhs: &Instant) -> bool { + self.eq(&rhs.0) + } +} + +impl PartialOrd<StdInstant> for Instant { + fn partial_cmp(&self, rhs: &StdInstant) -> Option<Ordering> { + self.0.partial_cmp(rhs) + } +} + +impl PartialOrd<Instant> for StdInstant { + fn partial_cmp(&self, rhs: &Instant) -> Option<Ordering> { + self.partial_cmp(&rhs.0) + } +} + +impl AsRef<StdInstant> for Instant { + fn as_ref(&self) -> &StdInstant { + &self.0 + } +} + +impl Borrow<StdInstant> for Instant { + fn borrow(&self) -> &StdInstant { + &self.0 + } +} diff --git a/vendor/time/src/internal_macros.rs b/vendor/time/src/internal_macros.rs new file mode 100644 index 00000000..74205ac4 --- /dev/null +++ b/vendor/time/src/internal_macros.rs @@ -0,0 +1,209 @@ +//! Macros for use within the library. They are not publicly available. + +/// Helper macro for easily implementing `OpAssign`. +macro_rules! __impl_assign { + ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$( + #[allow(unused_qualifications)] + $(#[$attr])* + impl core::ops::$op<$t> for $target { + fn $fn(&mut self, rhs: $t) { + *self = *self $sym rhs; + } + } + )+}; +} + +/// Implement `AddAssign` for the provided types. +macro_rules! impl_add_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + + AddAssign add_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `SubAssign` for the provided types. +macro_rules! impl_sub_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + - SubAssign sub_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `MulAssign` for the provided types. +macro_rules! impl_mul_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + * MulAssign mul_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `DivAssign` for the provided types. +macro_rules! impl_div_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + / DivAssign div_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// 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) >> ($crate::size_of_val(&this) * 8 - 1); + if r != 0 { + d + correction + } else { + d + } + } + } + }; +} + +/// 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 $from >= max { + $from -= max - min; + $to += 1; + } else if $from < min { + $from += max - min; + $to -= 1; + } + }; + + // Special case the ordinal-to-year cascade, as it has different behavior. + ($ordinal:ident => $year:ident) => { + // We need to actually capture the idents. Without this, macro hygiene causes errors. + cascade!(@ordinal $ordinal); + cascade!(@year $year); + #[allow(unused_assignments)] + if $ordinal > crate::util::days_in_year($year) as i16 { + $ordinal -= crate::util::days_in_year($year) as i16; + $year += 1; + } else if $ordinal < 1 { + $year -= 1; + $ordinal += crate::util::days_in_year($year) as i16; + } + }; +} + +/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range. +macro_rules! ensure_ranged { + ($type:ident : $value:ident) => { + match $type::new($value) { + Some(val) => val, + None => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64, + maximum: $type::MAX.get() as i64, + value: $value as i64, + conditional_message: None, + }); + } + } + }; + + ($type:ident : $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 => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as i64, + conditional_message: None, + }); + } + }, + None => { + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as i64, + conditional_message: None, + }); + } + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is +/// usable in `const` contexts. +macro_rules! const_try { + ($e:expr) => { + match $e { + Ok(value) => value, + Err(error) => return Err(error), + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but is usable in `const` contexts. +macro_rules! const_try_opt { + ($e:expr) => { + match $e { + Some(value) => value, + None => return None, + } + }; +} + +/// Try to unwrap an expression, panicking if not possible. +/// +/// This is similar to `$e.expect($message)`, but is usable in `const` contexts. +macro_rules! expect_opt { + ($e:expr, $message:literal) => { + match $e { + Some(value) => value, + None => crate::expect_failed($message), + } + }; +} + +/// `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 $($rest:tt)?) => { + panic!(concat!("internal error: ", $descr) $($rest)?) + } +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub(crate) use bug; +pub(crate) use { + __impl_assign, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, + impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; diff --git a/vendor/time/src/interop/js_sys_date_offsetdatetime.rs b/vendor/time/src/interop/js_sys_date_offsetdatetime.rs new file mode 100644 index 00000000..ecbd601e --- /dev/null +++ b/vendor/time/src/interop/js_sys_date_offsetdatetime.rs @@ -0,0 +1,27 @@ +use num_conv::prelude::*; + +use crate::convert::*; +use crate::OffsetDateTime; + +impl From<js_sys::Date> for OffsetDateTime { + /// # Panics + /// + /// This may panic if the timestamp can not be represented. + fn from(js_date: js_sys::Date) -> Self { + // get_time() returns milliseconds + let timestamp_nanos = js_date.get_time() as i128 + * Nanosecond::per(Millisecond).cast_signed().extend::<i128>(); + Self::from_unix_timestamp_nanos(timestamp_nanos) + .expect("invalid timestamp: Timestamp cannot fit in range") + } +} + +impl From<OffsetDateTime> for js_sys::Date { + fn from(datetime: OffsetDateTime) -> Self { + // new Date() takes milliseconds + let timestamp = (datetime.unix_timestamp_nanos() + / Nanosecond::per(Millisecond).cast_signed().extend::<i128>()) + as f64; + Self::new(×tamp.into()) + } +} diff --git a/vendor/time/src/interop/js_sys_date_utcdatetime.rs b/vendor/time/src/interop/js_sys_date_utcdatetime.rs new file mode 100644 index 00000000..e387c684 --- /dev/null +++ b/vendor/time/src/interop/js_sys_date_utcdatetime.rs @@ -0,0 +1,26 @@ +use num_conv::prelude::*; + +use crate::convert::*; +use crate::UtcDateTime; + +impl From<js_sys::Date> for UtcDateTime { + /// # Panics + /// + /// This may panic if the timestamp can not be represented. + fn from(js_date: js_sys::Date) -> Self { + // get_time() returns milliseconds + let timestamp_nanos = (js_date.get_time() * Nanosecond::per(Millisecond) as f64) as i128; + Self::from_unix_timestamp_nanos(timestamp_nanos) + .expect("invalid timestamp: Timestamp cannot fit in range") + } +} + +impl From<UtcDateTime> for js_sys::Date { + fn from(datetime: UtcDateTime) -> Self { + // new Date() takes milliseconds + let timestamp = (datetime.unix_timestamp_nanos() + / Nanosecond::per(Millisecond).cast_signed().extend::<i128>()) + as f64; + Self::new(×tamp.into()) + } +} diff --git a/vendor/time/src/interop/mod.rs b/vendor/time/src/interop/mod.rs new file mode 100644 index 00000000..d562663c --- /dev/null +++ b/vendor/time/src/interop/mod.rs @@ -0,0 +1,28 @@ +//! Comparison, arithmetic, and conversion between various types in `time` and the standard library. +//! +//! Currently, full interoperability is present between [`OffsetDateTime`](crate::OffsetDateTime), +//! [`UtcDateTime`](crate::UtcDateTime), and [`SystemTime`](std::time::SystemTime). Partial +//! interoperability is present with [`js_sys::Date`]. Note that +//! [`PrimitiveDateTime`](crate::PrimitiveDateTime) is not interoperable with any of these types due +//! to the lack of an associated UTC offset. + +// Module names should have the two types sorted in alphabetical order. This avoids any question +// of which type should be the "primary" type in the module name. + +#[cfg(all( + target_family = "wasm", + not(any(target_os = "emscripten", target_os = "wasi")), + feature = "wasm-bindgen" +))] +mod js_sys_date_offsetdatetime; +#[cfg(all( + target_family = "wasm", + not(any(target_os = "emscripten", target_os = "wasi")), + feature = "wasm-bindgen" +))] +mod js_sys_date_utcdatetime; +#[cfg(feature = "std")] +mod offsetdatetime_systemtime; +mod offsetdatetime_utcdatetime; +#[cfg(feature = "std")] +mod utcdatetime_systemtime; diff --git a/vendor/time/src/interop/offsetdatetime_systemtime.rs b/vendor/time/src/interop/offsetdatetime_systemtime.rs new file mode 100644 index 00000000..21a5d7f1 --- /dev/null +++ b/vendor/time/src/interop/offsetdatetime_systemtime.rs @@ -0,0 +1,75 @@ +use core::cmp::Ordering; +use core::ops::Sub; +use std::time::SystemTime; + +use crate::{Duration, OffsetDateTime}; + +impl Sub<SystemTime> for OffsetDateTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: SystemTime) -> Self::Output { + self - Self::from(rhs) + } +} + +impl Sub<OffsetDateTime> for SystemTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: OffsetDateTime) -> Self::Output { + OffsetDateTime::from(self) - rhs + } +} + +impl PartialEq<SystemTime> for OffsetDateTime { + fn eq(&self, rhs: &SystemTime) -> bool { + self == &Self::from(*rhs) + } +} + +impl PartialEq<OffsetDateTime> for SystemTime { + fn eq(&self, rhs: &OffsetDateTime) -> bool { + &OffsetDateTime::from(*self) == rhs + } +} + +impl PartialOrd<SystemTime> for OffsetDateTime { + fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> { + self.partial_cmp(&Self::from(*other)) + } +} + +impl PartialOrd<OffsetDateTime> for SystemTime { + fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> { + OffsetDateTime::from(*self).partial_cmp(other) + } +} + +impl From<SystemTime> for OffsetDateTime { + fn from(system_time: SystemTime) -> Self { + match system_time.duration_since(SystemTime::UNIX_EPOCH) { + Ok(duration) => Self::UNIX_EPOCH + duration, + Err(err) => Self::UNIX_EPOCH - err.duration(), + } + } +} + +impl From<OffsetDateTime> for SystemTime { + 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() + } + } +} diff --git a/vendor/time/src/interop/offsetdatetime_utcdatetime.rs b/vendor/time/src/interop/offsetdatetime_utcdatetime.rs new file mode 100644 index 00000000..34a4887d --- /dev/null +++ b/vendor/time/src/interop/offsetdatetime_utcdatetime.rs @@ -0,0 +1,68 @@ +use core::cmp::Ordering; +use core::ops::Sub; + +use crate::{Duration, OffsetDateTime, UtcDateTime}; + +impl Sub<OffsetDateTime> for UtcDateTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: OffsetDateTime) -> Self::Output { + OffsetDateTime::from(self) - rhs + } +} + +impl Sub<UtcDateTime> for OffsetDateTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: UtcDateTime) -> Self::Output { + self - Self::from(rhs) + } +} + +impl PartialEq<OffsetDateTime> for UtcDateTime { + fn eq(&self, other: &OffsetDateTime) -> bool { + OffsetDateTime::from(*self) == *other + } +} + +impl PartialEq<UtcDateTime> for OffsetDateTime { + fn eq(&self, other: &UtcDateTime) -> bool { + *self == Self::from(*other) + } +} + +impl PartialOrd<OffsetDateTime> for UtcDateTime { + fn partial_cmp(&self, other: &OffsetDateTime) -> Option<Ordering> { + OffsetDateTime::from(*self).partial_cmp(other) + } +} + +impl PartialOrd<UtcDateTime> for OffsetDateTime { + fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> { + self.partial_cmp(&Self::from(*other)) + } +} + +impl From<OffsetDateTime> for UtcDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn from(datetime: OffsetDateTime) -> Self { + datetime.to_utc() + } +} + +impl From<UtcDateTime> for OffsetDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn from(datetime: UtcDateTime) -> Self { + datetime.as_primitive().assume_utc() + } +} diff --git a/vendor/time/src/interop/utcdatetime_systemtime.rs b/vendor/time/src/interop/utcdatetime_systemtime.rs new file mode 100644 index 00000000..7cfb44e1 --- /dev/null +++ b/vendor/time/src/interop/utcdatetime_systemtime.rs @@ -0,0 +1,75 @@ +use core::cmp::Ordering; +use core::ops::Sub; +use std::time::SystemTime; + +use crate::{Duration, UtcDateTime}; + +impl Sub<SystemTime> for UtcDateTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: SystemTime) -> Self::Output { + self - Self::from(rhs) + } +} + +impl Sub<UtcDateTime> for SystemTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: UtcDateTime) -> Self::Output { + UtcDateTime::from(self) - rhs + } +} + +impl PartialEq<SystemTime> for UtcDateTime { + fn eq(&self, rhs: &SystemTime) -> bool { + self == &Self::from(*rhs) + } +} + +impl PartialEq<UtcDateTime> for SystemTime { + fn eq(&self, rhs: &UtcDateTime) -> bool { + &UtcDateTime::from(*self) == rhs + } +} + +impl PartialOrd<SystemTime> for UtcDateTime { + fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> { + self.partial_cmp(&Self::from(*other)) + } +} + +impl PartialOrd<UtcDateTime> for SystemTime { + fn partial_cmp(&self, other: &UtcDateTime) -> Option<Ordering> { + UtcDateTime::from(*self).partial_cmp(other) + } +} + +impl From<SystemTime> for UtcDateTime { + fn from(system_time: SystemTime) -> Self { + match system_time.duration_since(SystemTime::UNIX_EPOCH) { + Ok(duration) => Self::UNIX_EPOCH + duration, + Err(err) => Self::UNIX_EPOCH - err.duration(), + } + } +} + +impl From<UtcDateTime> for SystemTime { + 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() + } + } +} diff --git a/vendor/time/src/lib.rs b/vendor/time/src/lib.rs new file mode 100644 index 00000000..d8765d17 --- /dev/null +++ b/vendor/time/src/lib.rs @@ -0,0 +1,153 @@ +//! # 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` +//! +//! Enables [rand](https://docs.rs/rand) 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_auto_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 = "rand")] +mod rand; +#[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")] +#[allow(deprecated)] +pub use crate::instant::Instant; +pub use crate::month::Month; +pub use crate::offset_date_time::OffsetDateTime; +pub use crate::primitive_date_time::PrimitiveDateTime; +pub use crate::time::Time; +pub use crate::utc_date_time::UtcDateTime; +pub use crate::utc_offset::UtcOffset; +pub use crate::weekday::Weekday; + +/// An alias for [`std::result::Result`] with a generic error from the time crate. +pub type Result<T> = core::result::Result<T, Error>; + +/// This is a separate function to reduce the code size of `expect_opt!`. +#[inline(never)] +#[cold] +#[track_caller] +const fn expect_failed(message: &str) -> ! { + panic!("{}", message) +} + +/// Returns the size of the pointed-to value in bytes. +/// +/// This is a `const fn` in the standard library starting in Rust 1.85. When MSRV is at least that, +/// this can be removed. +#[allow(unused_qualifications)] // added to prelude after MSRV +const fn size_of_val<T>(_: &T) -> usize { + core::mem::size_of::<T>() +} diff --git a/vendor/time/src/macros.rs b/vendor/time/src/macros.rs new file mode 100644 index 00000000..e9ca572e --- /dev/null +++ b/vendor/time/src/macros.rs @@ -0,0 +1,149 @@ +//! Macros to construct statically known values. + +/// Construct a [`Date`](crate::Date) with a statically known value. +/// +/// The resulting expression can be used in `const` or `static` declarations. +/// +/// Three formats are supported: year-week-weekday, year-ordinal, and year-month-day. +/// +/// ```rust +/// # use time::{Date, Weekday::*, Month, macros::date}; +/// assert_eq!( +/// date!(2020 - W 01 - 3), +/// Date::from_iso_week_date(2020, 1, Wednesday)? +/// ); +/// assert_eq!(date!(2020-001), Date::from_ordinal_date(2020, 1)?); +/// assert_eq!( +/// date!(2020-01-01), +/// Date::from_calendar_date(2020, Month::January, 1)? +/// ); +/// # Ok::<_, time::Error>(()) +/// ``` +pub use time_macros::date; +/// Construct a [`PrimitiveDateTime`] or [`OffsetDateTime`] with a statically known value. +/// +/// The resulting expression can be used in `const` or `static` declarations. +/// +/// The syntax accepted by this macro is the same as [`date!`] and [`time!`], with an optional +/// [`offset!`], all space-separated. If an [`offset!`] is provided, the resulting value will +/// be an [`OffsetDateTime`]; otherwise it will be a [`PrimitiveDateTime`]. +/// +/// [`OffsetDateTime`]: crate::OffsetDateTime +/// [`PrimitiveDateTime`]: crate::PrimitiveDateTime +/// +/// ```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. +/// +/// 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; diff --git a/vendor/time/src/month.rs b/vendor/time/src/month.rs new file mode 100644 index 00000000..18bb091c --- /dev/null +++ b/vendor/time/src/month.rs @@ -0,0 +1,273 @@ +//! The `Month` enum and its associated `impl`s. + +use core::fmt; +use core::num::NonZeroU8; +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, Hash)] +pub enum Month { + #[allow(missing_docs)] + January = 1, + #[allow(missing_docs)] + February = 2, + #[allow(missing_docs)] + March = 3, + #[allow(missing_docs)] + April = 4, + #[allow(missing_docs)] + May = 5, + #[allow(missing_docs)] + June = 6, + #[allow(missing_docs)] + July = 7, + #[allow(missing_docs)] + August = 8, + #[allow(missing_docs)] + September = 9, + #[allow(missing_docs)] + October = 10, + #[allow(missing_docs)] + November = 11, + #[allow(missing_docs)] + December = 12, +} + +impl Month { + /// Create a `Month` from its numerical value. + pub(crate) const fn from_number(n: NonZeroU8) -> Result<Self, error::ComponentRange> { + match n.get() { + 1 => Ok(January), + 2 => Ok(February), + 3 => Ok(March), + 4 => Ok(April), + 5 => Ok(May), + 6 => Ok(June), + 7 => Ok(July), + 8 => Ok(August), + 9 => Ok(September), + 10 => Ok(October), + 11 => Ok(November), + 12 => Ok(December), + n => Err(error::ComponentRange { + name: "month", + minimum: 1, + maximum: 12, + value: n as i64, + conditional_message: None, + }), + } + } + + /// Get the number of days in the month of a given year. + /// + /// ```rust + /// # use time::Month; + /// assert_eq!(Month::February.length(2020), 29); + /// ``` + 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); + /// ``` + pub const fn previous(self) -> Self { + match self { + January => December, + February => January, + March => February, + April => March, + May => April, + June => May, + July => June, + August => July, + September => August, + October => September, + November => October, + December => November, + } + } + + /// Get the next month. + /// + /// ```rust + /// # use time::Month; + /// assert_eq!(Month::January.next(), Month::February); + /// ``` + pub const fn next(self) -> Self { + match self { + January => February, + February => March, + March => April, + April => May, + May => June, + June => July, + July => August, + August => September, + September => October, + October => November, + November => December, + December => January, + } + } + + /// 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); + /// ``` + 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); + /// ``` + pub const fn nth_prev(self, n: u8) -> Self { + match self as i8 - 1 - (n % 12) as i8 { + 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 { + #[non_exhaustive] + #[derive(Debug, Clone, Copy)] + pub struct MonthMetadata; +} +use private::MonthMetadata; + +impl SmartDisplay for Month { + type Metadata = MonthMetadata; + + 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), + } + } + + 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 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl FromStr for Month { + type Err = error::InvalidVariant; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "January" => Ok(January), + "February" => Ok(February), + "March" => Ok(March), + "April" => Ok(April), + "May" => Ok(May), + "June" => Ok(June), + "July" => Ok(July), + "August" => Ok(August), + "September" => Ok(September), + "October" => Ok(October), + "November" => Ok(November), + "December" => Ok(December), + _ => Err(error::InvalidVariant), + } + } +} + +impl From<Month> for u8 { + fn from(month: Month) -> Self { + month as Self + } +} + +impl TryFrom<u8> for Month { + type Error = error::ComponentRange; + + fn try_from(value: u8) -> Result<Self, Self::Error> { + match NonZeroU8::new(value) { + Some(value) => Self::from_number(value), + None => Err(error::ComponentRange { + name: "month", + minimum: 1, + maximum: 12, + value: 0, + conditional_message: None, + }), + } + } +} diff --git a/vendor/time/src/offset_date_time.rs b/vendor/time/src/offset_date_time.rs new file mode 100644 index 00000000..274fad2b --- /dev/null +++ b/vendor/time/src/offset_date_time.rs @@ -0,0 +1,1511 @@ +//! 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; +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::{ + cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, +}; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +use crate::{ + error, util, Date, Duration, Month, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, +}; + +/// The Julian day of the Unix epoch. +const UNIX_EPOCH_JULIAN_DAY: i32 = OffsetDateTime::UNIX_EPOCH.to_julian_day(); + +/// A [`PrimitiveDateTime`] with a [`UtcOffset`]. +/// +/// All comparisons are performed using the UTC time. +#[derive(Clone, Copy, Eq)] +pub struct OffsetDateTime { + local_date_time: PrimitiveDateTime, + offset: UtcOffset, +} + +impl PartialEq for OffsetDateTime { + fn eq(&self, other: &Self) -> bool { + self.to_offset_raw(UtcOffset::UTC) == other.to_offset_raw(UtcOffset::UTC) + } +} + +impl PartialOrd for OffsetDateTime { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for OffsetDateTime { + fn cmp(&self, other: &Self) -> Ordering { + self.to_offset_raw(UtcOffset::UTC) + .cmp(&other.to_offset_raw(UtcOffset::UTC)) + } +} + +impl Hash for OffsetDateTime { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.to_offset_raw(UtcOffset::UTC).hash(state); + } +} + +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( + // Safety: `ordinal` is not zero. + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }, + 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")] + 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")] + pub fn now_local() -> Result<Self, error::IndeterminateOffset> { + 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>(()) + /// ``` + 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>(()) + /// ``` + 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. + pub const fn to_offset(self, offset: UtcOffset) -> Self { + expect_opt!( + self.checked_to_offset(offset), + "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, + /// ); + /// ``` + pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<Self> { + if self.offset.whole_hours() == offset.whole_hours() + && self.offset.minutes_past_hour() == offset.minutes_past_hour() + && self.offset.seconds_past_minute() == offset.seconds_past_minute() + { + return Some(self.replace_offset(offset)); + } + + 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. + pub const fn to_utc(self) -> UtcDateTime { + self.to_offset(UtcOffset::UTC).local_date_time.as_utc() + } + + /// 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, + /// ); + /// ``` + pub const fn checked_to_utc(self) -> Option<UtcDateTime> { + Some( + const_try_opt!(self.checked_to_offset(UtcOffset::UTC)) + .local_date_time + .as_utc(), + ) + } + + /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This + /// avoids constructing an invalid [`Date`] if the new value is out of range. + 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.whole_hours() == to.whole_hours() + && from.minutes_past_hour() == to.minutes_past_hour() + && from.seconds_past_minute() == to.seconds_past_minute() + { + return (self.year(), self.ordinal(), self.time()); + } + + let mut second = self.second() as i16 - from.seconds_past_minute() as i16 + + to.seconds_past_minute() as i16; + let mut minute = + self.minute() as i16 - from.minutes_past_hour() as i16 + to.minutes_past_hour() as i16; + let mut hour = self.hour() as i8 - from.whole_hours() + to.whole_hours(); + let (mut year, ordinal) = self.to_ordinal_date(); + let mut ordinal = ordinal as i16; + + // Cascade the values twice. This is needed because the values are adjusted twice above. + cascade!(second in 0..Second::per(Minute) as i16 => minute); + cascade!(second in 0..Second::per(Minute) as i16 => minute); + cascade!(minute in 0..Minute::per(Hour) as i16 => hour); + cascade!(minute in 0..Minute::per(Hour) as i16 => hour); + cascade!(hour in 0..Hour::per(Day) as i8 => ordinal); + cascade!(hour in 0..Hour::per(Day) as i8 => ordinal); + cascade!(ordinal => year); + + debug_assert!(ordinal > 0); + debug_assert!(ordinal <= util::days_in_year(year) as i16); + + ( + year, + ordinal as u16, + // Safety: The cascades above ensure the values are in range. + unsafe { + Time::__from_hms_nanos_unchecked( + hour as u8, + 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>(()) + /// ``` + pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> { + 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(Day) as i64) as i32, + ) + }; + + let seconds_within_day = timestamp.rem_euclid(Second::per(Day) as i64); + // Safety: All values are in range. + let time = unsafe { + Time::__from_hms_nanos_unchecked( + (seconds_within_day / Second::per(Hour) as i64) as u8, + ((seconds_within_day % Second::per(Hour) as i64) / Minute::per(Hour) as i64) as u8, + (seconds_within_day % Second::per(Minute) as i64) 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)), + /// ); + /// ``` + pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> { + let datetime = const_try!(Self::from_unix_timestamp(div_floor!( + timestamp, + Nanosecond::per(Second) as i128 + ) 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(Second) as i128) 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)); + /// ``` + 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); + /// ``` + pub const fn unix_timestamp(self) -> i64 { + let days = + (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per(Day) as i64; + let hours = self.hour() as i64 * Second::per(Hour) as i64; + let minutes = self.minute() as i64 * Second::per(Minute) as i64; + 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, + /// ); + /// ``` + pub const fn unix_timestamp_nanos(self) -> i128 { + self.unix_timestamp() as i128 * Nanosecond::per(Second) as i128 + self.nanosecond() as i128 + } + + /// Get the [`PrimitiveDateTime`] in the stored offset. + 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), + /// ); + /// ``` + 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) + /// ); + /// ``` + 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); + /// ``` + 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, + /// ); + /// ``` + 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, + /// ); + /// ``` + 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, + /// ); + /// ``` + 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); + /// ``` + 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); + /// ``` + 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); + /// ``` + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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); + /// ``` + pub const fn weekday(self) -> Weekday { + self.date().weekday() + } + + /// Get the Julian day for the date. The time is not taken into account for this calculation. + /// + /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is + /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(-4713-11-24 0:00 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); + /// ``` + 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)); + /// ``` + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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, + /// ); + /// ``` + 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, + /// ); + /// ``` + 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, + /// ); + /// ``` + 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); + /// ``` + 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, + /// ); + /// ``` + 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, + /// ); + /// ``` + 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)) + /// ); + /// ``` + pub const fn checked_add(self, duration: Duration) -> Option<Self> { + 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)) + /// ); + /// ``` + pub const fn checked_sub(self, duration: Duration) -> Option<Self> { + 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) + /// ); + /// ``` + 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) + /// ); + /// ``` + 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`."] + 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`."] + 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`."] + 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`."] + 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`."] + pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> { + Ok(const_try!(self.date_time().replace_ordinal(ordinal)).assume_offset(self.offset())) + } + + /// 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`."] + pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { + Ok(const_try!(self.date_time().replace_hour(hour)).assume_offset(self.offset())) + } + + /// 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`."] + pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { + Ok(const_try!(self.date_time().replace_minute(minute)).assume_offset(self.offset())) + } + + /// 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`."] + pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { + Ok(const_try!(self.date_time().replace_second(second)).assume_offset(self.offset())) + } + + /// 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`."] + pub const fn replace_millisecond( + self, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + Ok( + const_try!(self.date_time().replace_millisecond(millisecond)) + .assume_offset(self.offset()), + ) + } + + /// 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`."] + pub const fn replace_microsecond( + self, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + Ok( + const_try!(self.date_time().replace_microsecond(microsecond)) + .assume_offset(self.offset()), + ) + } + + /// 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`."] + pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { + 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). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into( + output, + Some(self.date()), + Some(self.time()), + Some(self.offset()), + ) + } + + /// 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>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(Some(self.date()), Some(self.time()), Some(self.offset())) + } +} + +#[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>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + 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")] + 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_offset_raw(UtcOffset::UTC); + 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 = (); + + fn metadata(&self, _: FormatterOptions) -> Metadata<Self> { + let width = + smart_display::padded_width_of!(self.date(), " ", self.time(), " ", self.offset()); + Metadata::new(width, self, ()) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> fmt::Result { + f.pad_with_width( + metadata.unpadded_width(), + format_args!("{} {} {}", self.date(), self.time(), self.offset()), + ) + } +} + +impl fmt::Display for OffsetDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for OffsetDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Add<Duration> for OffsetDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: Duration) -> Self::Output { + self.checked_add(duration) + .expect("resulting value is out of range") + } +} + +impl Add<StdDuration> for OffsetDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + 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<Duration> for OffsetDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, rhs: Duration) { + *self = *self + rhs; + } +} + +impl AddAssign<StdDuration> for OffsetDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, rhs: StdDuration) { + *self = *self + rhs; + } +} + +impl Sub<Duration> for OffsetDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Duration) -> Self::Output { + self.checked_sub(rhs) + .expect("resulting value is out of range") + } +} + +impl Sub<StdDuration> for OffsetDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + 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<Duration> for OffsetDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub_assign(&mut self, rhs: Duration) { + *self = *self - rhs; + } +} + +impl SubAssign<StdDuration> for OffsetDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + 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. + 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::<i64>(), + ); + base - adjustment + } +} diff --git a/vendor/time/src/parsing/combinator/mod.rs b/vendor/time/src/parsing/combinator/mod.rs new file mode 100644 index 00000000..e3e1df76 --- /dev/null +++ b/vendor/time/src/parsing/combinator/mod.rs @@ -0,0 +1,194 @@ +//! Implementations of the low-level parser combinators. + +pub(crate) mod rfc; + +use num_conv::prelude::*; + +use crate::format_description::modifier::Padding; +use crate::parsing::shim::{Integer, IntegerParseBytes}; +use crate::parsing::ParsedItem; + +/// Parse a "+" or "-" sign. Returns the ASCII byte representing the sign, if present. +pub(crate) const fn sign(input: &[u8]) -> Option<ParsedItem<'_, u8>> { + match input { + [sign @ (b'-' | b'+'), remaining @ ..] => Some(ParsedItem(remaining, *sign)), + _ => None, + } +} + +/// Consume the first matching item, returning its associated value. +pub(crate) fn first_match<'a, T>( + options: impl IntoIterator<Item = (&'a [u8], T)>, + case_sensitive: bool, +) -> impl FnMut(&'a [u8]) -> Option<ParsedItem<'a, T>> { + let mut options = options.into_iter(); + move |input| { + options.find_map(|(expected, t)| { + if case_sensitive { + Some(ParsedItem(input.strip_prefix(expected)?, t)) + } else { + let n = expected.len(); + if n <= input.len() { + let (head, tail) = input.split_at(n); + if head.eq_ignore_ascii_case(expected) { + return Some(ParsedItem(tail, t)); + } + } + None + } + }) + } +} + +/// Consume zero or more instances of the provided parser. The parser must return the unit value. +pub(crate) fn zero_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>( + parser: P, +) -> impl FnMut(&'a [u8]) -> ParsedItem<'a, ()> { + move |mut input| { + while let Some(remaining) = parser(input) { + input = remaining.into_inner(); + } + ParsedItem(input, ()) + } +} + +/// Consume one of or more instances of the provided parser. The parser must produce the unit value. +pub(crate) fn one_or_more<'a, P: Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>>>( + parser: P, +) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, ()>> { + move |mut input| { + input = parser(input)?.into_inner(); + while let Some(remaining) = parser(input) { + input = remaining.into_inner(); + } + Some(ParsedItem(input, ())) + } +} + +/// Consume between `n` and `m` instances of the provided parser. +pub(crate) fn n_to_m< + 'a, + const N: u8, + const M: u8, + T, + P: Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>, +>( + parser: P, +) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, &'a [u8]>> { + debug_assert!(M >= N); + move |mut input| { + // We need to keep this to determine the total length eventually consumed. + let orig_input = input; + + // Mandatory + for _ in 0..N { + input = parser(input)?.0; + } + + // Optional + for _ in N..M { + match parser(input) { + Some(parsed) => input = parsed.0, + None => break, + } + } + + Some(ParsedItem( + input, + &orig_input[..(orig_input.len() - input.len())], + )) + } +} + +/// Consume between `n` and `m` digits, returning the numerical value. +pub(crate) fn n_to_m_digits<const N: u8, const M: u8, T: Integer>( + input: &[u8], +) -> Option<ParsedItem<'_, T>> { + debug_assert!(M >= N); + n_to_m::<N, M, _, _>(any_digit)(input)?.flat_map(|value| value.parse_bytes()) +} + +/// Consume exactly `n` digits, returning the numerical value. +pub(crate) fn exactly_n_digits<const N: u8, T: Integer>(input: &[u8]) -> Option<ParsedItem<'_, T>> { + n_to_m_digits::<N, N, _>(input) +} + +/// Consume exactly `n` digits, returning the numerical value. +pub(crate) fn exactly_n_digits_padded<'a, const N: u8, T: Integer>( + padding: Padding, +) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> { + n_to_m_digits_padded::<N, N, _>(padding) +} + +/// Consume between `n` and `m` digits, returning the numerical value. +pub(crate) fn n_to_m_digits_padded<'a, const N: u8, const M: u8, T: Integer>( + padding: Padding, +) -> impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>> { + debug_assert!(M >= N); + move |mut input| match padding { + Padding::None => n_to_m_digits::<1, M, _>(input), + Padding::Space => { + debug_assert!(N > 0); + + let mut orig_input = input; + for _ in 0..(N - 1) { + match ascii_char::<b' '>(input) { + Some(parsed) => input = parsed.0, + None => break, + } + } + let pad_width = (orig_input.len() - input.len()).truncate::<u8>(); + + orig_input = input; + for _ in 0..(N - pad_width) { + input = any_digit(input)?.0; + } + for _ in N..M { + match any_digit(input) { + Some(parsed) => input = parsed.0, + None => break, + } + } + + ParsedItem(input, &orig_input[..(orig_input.len() - input.len())]) + .flat_map(|value| value.parse_bytes()) + } + Padding::Zero => n_to_m_digits::<N, M, _>(input), + } +} + +/// Consume exactly one digit. +pub(crate) const fn any_digit(input: &[u8]) -> Option<ParsedItem<'_, u8>> { + match input { + [c, remaining @ ..] if c.is_ascii_digit() => Some(ParsedItem(remaining, *c)), + _ => None, + } +} + +/// Consume exactly one of the provided ASCII characters. +pub(crate) fn ascii_char<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace()); + match input { + [c, remaining @ ..] if *c == CHAR => Some(ParsedItem(remaining, ())), + _ => None, + } +} + +/// Consume exactly one of the provided ASCII characters, case-insensitive. +pub(crate) fn ascii_char_ignore_case<const CHAR: u8>(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + debug_assert!(CHAR.is_ascii_graphic() || CHAR.is_ascii_whitespace()); + match input { + [c, remaining @ ..] if c.eq_ignore_ascii_case(&CHAR) => Some(ParsedItem(remaining, ())), + _ => None, + } +} + +/// Optionally consume an input with a given parser. +pub(crate) fn opt<'a, T>( + parser: impl Fn(&'a [u8]) -> Option<ParsedItem<'a, T>>, +) -> impl Fn(&'a [u8]) -> ParsedItem<'a, Option<T>> { + move |input| match parser(input) { + Some(value) => value.map(Some), + None => ParsedItem(input, None), + } +} diff --git a/vendor/time/src/parsing/combinator/rfc/iso8601.rs b/vendor/time/src/parsing/combinator/rfc/iso8601.rs new file mode 100644 index 00000000..4a1ebc46 --- /dev/null +++ b/vendor/time/src/parsing/combinator/rfc/iso8601.rs @@ -0,0 +1,177 @@ +//! Rules defined in [ISO 8601]. +//! +//! [ISO 8601]: https://www.iso.org/iso-8601-date-and-time-format.html + +use core::num::{NonZeroU16, NonZeroU8}; + +use num_conv::prelude::*; + +use crate::parsing::combinator::{any_digit, ascii_char, exactly_n_digits, first_match, sign}; +use crate::parsing::ParsedItem; +use crate::{Month, Weekday}; + +/// What kind of format is being parsed. This is used to ensure each part of the format (date, time, +/// offset) is the same kind. +#[derive(Debug, Clone, Copy)] +pub(crate) enum ExtendedKind { + /// The basic format. + Basic, + /// The extended format. + Extended, + /// ¯\_(ツ)_/¯ + Unknown, +} + +impl ExtendedKind { + /// Is it possible that the format is extended? + pub(crate) const fn maybe_extended(self) -> bool { + matches!(self, Self::Extended | Self::Unknown) + } + + /// Is the format known for certain to be extended? + pub(crate) const fn is_extended(self) -> bool { + matches!(self, Self::Extended) + } + + /// If the kind is `Unknown`, make it `Basic`. Otherwise, do nothing. Returns `Some` if and only + /// if the kind is now `Basic`. + pub(crate) fn coerce_basic(&mut self) -> Option<()> { + match self { + Self::Basic => Some(()), + Self::Extended => None, + Self::Unknown => { + *self = Self::Basic; + Some(()) + } + } + } + + /// If the kind is `Unknown`, make it `Extended`. Otherwise, do nothing. Returns `Some` if and + /// only if the kind is now `Extended`. + pub(crate) fn coerce_extended(&mut self) -> Option<()> { + match self { + Self::Basic => None, + Self::Extended => Some(()), + Self::Unknown => { + *self = Self::Extended; + Some(()) + } + } + } +} + +/// Parse a possibly expanded year. +pub(crate) fn year(input: &[u8]) -> Option<ParsedItem<'_, i32>> { + Some(match sign(input) { + Some(ParsedItem(input, sign)) => exactly_n_digits::<6, u32>(input)?.map(|val| { + let val = val.cast_signed(); + if sign == b'-' { + -val + } else { + val + } + }), + None => exactly_n_digits::<4, u32>(input)?.map(|val| val.cast_signed()), + }) +} + +/// Parse a month. +pub(crate) fn month(input: &[u8]) -> Option<ParsedItem<'_, Month>> { + first_match( + [ + (b"01".as_slice(), Month::January), + (b"02".as_slice(), Month::February), + (b"03".as_slice(), Month::March), + (b"04".as_slice(), Month::April), + (b"05".as_slice(), Month::May), + (b"06".as_slice(), Month::June), + (b"07".as_slice(), Month::July), + (b"08".as_slice(), Month::August), + (b"09".as_slice(), Month::September), + (b"10".as_slice(), Month::October), + (b"11".as_slice(), Month::November), + (b"12".as_slice(), Month::December), + ], + true, + )(input) +} + +/// Parse a week number. +pub(crate) fn week(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a day of the month. +pub(crate) fn day(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU8>> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a day of the week. +pub(crate) fn dayk(input: &[u8]) -> Option<ParsedItem<'_, Weekday>> { + first_match( + [ + (b"1".as_slice(), Weekday::Monday), + (b"2".as_slice(), Weekday::Tuesday), + (b"3".as_slice(), Weekday::Wednesday), + (b"4".as_slice(), Weekday::Thursday), + (b"5".as_slice(), Weekday::Friday), + (b"6".as_slice(), Weekday::Saturday), + (b"7".as_slice(), Weekday::Sunday), + ], + true, + )(input) +} + +/// Parse a day of the year. +pub(crate) fn dayo(input: &[u8]) -> Option<ParsedItem<'_, NonZeroU16>> { + exactly_n_digits::<3, _>(input) +} + +/// Parse the hour. +pub(crate) fn hour(input: &[u8]) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits::<2, _>(input) +} + +/// Parse the minute. +pub(crate) fn min(input: &[u8]) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits::<2, _>(input) +} + +/// Parse a floating point number as its integer and optional fractional parts. +/// +/// The number must have two digits before the decimal point. If a decimal point is present, at +/// least one digit must follow. +/// +/// The return type is a tuple of the integer part and optional fraction part. +pub(crate) fn float(input: &[u8]) -> Option<ParsedItem<'_, (u8, Option<f64>)>> { + // Two digits before the decimal. + let ParsedItem(input, integer_part) = match input { + [first_digit @ b'0'..=b'9', second_digit @ b'0'..=b'9', input @ ..] => { + ParsedItem(input, (first_digit - b'0') * 10 + (second_digit - b'0')) + } + _ => return None, + }; + + if let Some(ParsedItem(input, ())) = decimal_sign(input) { + // Mandatory post-decimal digit. + let ParsedItem(mut input, mut fractional_part) = + any_digit(input)?.map(|digit| ((digit - b'0') as f64) / 10.); + + let mut divisor = 10.; + // Any number of subsequent digits. + while let Some(ParsedItem(new_input, digit)) = any_digit(input) { + input = new_input; + divisor *= 10.; + fractional_part += (digit - b'0') as f64 / divisor; + } + + Some(ParsedItem(input, (integer_part, Some(fractional_part)))) + } else { + Some(ParsedItem(input, (integer_part, None))) + } +} + +/// Parse a "decimal sign", which is either a comma or a period. +fn decimal_sign(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + ascii_char::<b'.'>(input).or_else(|| ascii_char::<b','>(input)) +} diff --git a/vendor/time/src/parsing/combinator/rfc/mod.rs b/vendor/time/src/parsing/combinator/rfc/mod.rs new file mode 100644 index 00000000..2974a4d5 --- /dev/null +++ b/vendor/time/src/parsing/combinator/rfc/mod.rs @@ -0,0 +1,10 @@ +//! Combinators for rules as defined in a standard. +//! +//! When applicable, these rules have been converted strictly following the ABNF syntax as specified +//! in [RFC 2234]. +//! +//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234 + +pub(crate) mod iso8601; +pub(crate) mod rfc2234; +pub(crate) mod rfc2822; diff --git a/vendor/time/src/parsing/combinator/rfc/rfc2234.rs b/vendor/time/src/parsing/combinator/rfc/rfc2234.rs new file mode 100644 index 00000000..67534443 --- /dev/null +++ b/vendor/time/src/parsing/combinator/rfc/rfc2234.rs @@ -0,0 +1,13 @@ +//! Rules defined in [RFC 2234]. +//! +//! [RFC 2234]: https://datatracker.ietf.org/doc/html/rfc2234 + +use crate::parsing::ParsedItem; + +/// Consume exactly one space or tab. +pub(crate) const fn wsp(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + match input { + [b' ' | b'\t', rest @ ..] => Some(ParsedItem(rest, ())), + _ => None, + } +} diff --git a/vendor/time/src/parsing/combinator/rfc/rfc2822.rs b/vendor/time/src/parsing/combinator/rfc/rfc2822.rs new file mode 100644 index 00000000..0e886ee4 --- /dev/null +++ b/vendor/time/src/parsing/combinator/rfc/rfc2822.rs @@ -0,0 +1,108 @@ +//! Rules defined in [RFC 2822]. +//! +//! [RFC 2822]: https://datatracker.ietf.org/doc/html/rfc2822 + +use crate::parsing::combinator::rfc::rfc2234::wsp; +use crate::parsing::combinator::{ascii_char, one_or_more, zero_or_more}; +use crate::parsing::ParsedItem; + +/// Consume the `fws` rule. +// The full rule is equivalent to /\r\n[ \t]+|[ \t]+(?:\r\n[ \t]+)*/ +pub(crate) fn fws(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> { + if let [b'\r', b'\n', rest @ ..] = input { + one_or_more(wsp)(rest) + } else { + input = one_or_more(wsp)(input)?.into_inner(); + while let [b'\r', b'\n', rest @ ..] = input { + input = one_or_more(wsp)(rest)?.into_inner(); + } + Some(ParsedItem(input, ())) + } +} + +/// Consume the `cfws` rule. +// The full rule is equivalent to any combination of `fws` and `comment` so long as it is not empty. +pub(crate) fn cfws(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + one_or_more(|input| fws(input).or_else(|| comment(input)))(input) +} + +/// Consume the `comment` rule. +fn comment(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> { + input = ascii_char::<b'('>(input)?.into_inner(); + input = zero_or_more(fws)(input).into_inner(); + while let Some(rest) = ccontent(input) { + input = rest.into_inner(); + input = zero_or_more(fws)(input).into_inner(); + } + input = ascii_char::<b')'>(input)?.into_inner(); + + Some(ParsedItem(input, ())) +} + +/// Consume the `ccontent` rule. +fn ccontent(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + ctext(input) + .or_else(|| quoted_pair(input)) + .or_else(|| comment(input)) +} + +/// Consume the `ctext` rule. +#[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522 +fn ctext(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + no_ws_ctl(input).or_else(|| match input { + [33..=39 | 42..=91 | 93..=126, rest @ ..] => Some(ParsedItem(rest, ())), + _ => None, + }) +} + +/// Consume the `quoted_pair` rule. +fn quoted_pair(mut input: &[u8]) -> Option<ParsedItem<'_, ()>> { + input = ascii_char::<b'\\'>(input)?.into_inner(); + 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. +const fn no_ws_ctl(input: &[u8]) -> Option<ParsedItem<'_, ()>> { + match input { + [1..=8 | 11..=12 | 14..=31 | 127, rest @ ..] => Some(ParsedItem(rest, ())), + _ => None, + } +} + +/// Consume the `text` rule. +fn text<'a>(input: &'a [u8]) -> ParsedItem<'a, ()> { + let new_text = |input: &'a [u8]| match input { + [1..=9 | 11..=12 | 14..=127, rest @ ..] => Some(ParsedItem(rest, ())), + _ => None, + }; + + let obs_char = |input: &'a [u8]| match input { + // This is technically allowed, but consuming this would mean the rest of the string is + // eagerly consumed without consideration for where the comment actually ends. + [b')', ..] => None, + [0..=9 | 11..=12 | 14..=127, rest @ ..] => Some(rest), + _ => None, + }; + + let obs_text = |mut input| { + input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner(); + input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner(); + while let Some(rest) = obs_char(input) { + input = rest; + input = zero_or_more(ascii_char::<b'\n'>)(input).into_inner(); + input = zero_or_more(ascii_char::<b'\r'>)(input).into_inner(); + } + + ParsedItem(input, ()) + }; + + new_text(input).unwrap_or_else(|| obs_text(input)) +} diff --git a/vendor/time/src/parsing/component.rs b/vendor/time/src/parsing/component.rs new file mode 100644 index 00000000..79645704 --- /dev/null +++ b/vendor/time/src/parsing/component.rs @@ -0,0 +1,379 @@ +//! Parsing implementations for all [`Component`](crate::format_description::Component)s. + +use core::num::{NonZeroU16, NonZeroU8}; + +use num_conv::prelude::*; + +use crate::convert::*; +use crate::format_description::modifier; +use crate::parsing::combinator::{ + any_digit, exactly_n_digits, exactly_n_digits_padded, first_match, n_to_m_digits, + n_to_m_digits_padded, opt, sign, +}; +use crate::parsing::ParsedItem; +use crate::{Month, Weekday}; + +/// Parse the "year" component of a `Date`. +pub(crate) fn parse_year( + input: &[u8], + modifiers: modifier::Year, +) -> Option<ParsedItem<'_, (i32, bool)>> { + match modifiers.repr { + modifier::YearRepr::Full => { + let ParsedItem(input, sign) = opt(sign)(input); + + if let Some(sign) = sign { + let ParsedItem(input, year) = if cfg!(feature = "large-dates") + && modifiers.range == modifier::YearRange::Extended + { + n_to_m_digits_padded::<4, 6, u32>(modifiers.padding)(input)? + } else { + exactly_n_digits_padded::<4, u32>(modifiers.padding)(input)? + }; + + Some(if sign == b'-' { + ParsedItem(input, (-year.cast_signed(), true)) + } else { + ParsedItem(input, (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(if sign == b'-' { + ParsedItem(input, (-year.cast_signed(), true)) + } else { + ParsedItem(input, (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`. +pub(crate) fn parse_month( + input: &[u8], + modifiers: modifier::Month, +) -> Option<ParsedItem<'_, Month>> { + use Month::*; + let ParsedItem(remaining, value) = first_match( + match modifiers.repr { + modifier::MonthRepr::Numerical => { + return exactly_n_digits_padded::<2, _>(modifiers.padding)(input)? + .flat_map(|n| Month::from_number(n).ok()); + } + modifier::MonthRepr::Long => [ + (b"January".as_slice(), January), + (b"February".as_slice(), February), + (b"March".as_slice(), March), + (b"April".as_slice(), April), + (b"May".as_slice(), May), + (b"June".as_slice(), June), + (b"July".as_slice(), July), + (b"August".as_slice(), August), + (b"September".as_slice(), September), + (b"October".as_slice(), October), + (b"November".as_slice(), November), + (b"December".as_slice(), December), + ], + modifier::MonthRepr::Short => [ + (b"Jan".as_slice(), January), + (b"Feb".as_slice(), February), + (b"Mar".as_slice(), March), + (b"Apr".as_slice(), April), + (b"May".as_slice(), May), + (b"Jun".as_slice(), June), + (b"Jul".as_slice(), July), + (b"Aug".as_slice(), August), + (b"Sep".as_slice(), September), + (b"Oct".as_slice(), October), + (b"Nov".as_slice(), November), + (b"Dec".as_slice(), December), + ], + }, + modifiers.case_sensitive, + )(input)?; + Some(ParsedItem(remaining, value)) +} + +/// Parse the "week number" component of a `Date`. +pub(crate) fn parse_week_number( + input: &[u8], + modifiers: modifier::WeekNumber, +) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits_padded::<2, _>(modifiers.padding)(input) +} + +/// Parse the "weekday" component of a `Date`. +pub(crate) fn parse_weekday( + input: &[u8], + modifiers: modifier::Weekday, +) -> Option<ParsedItem<'_, Weekday>> { + first_match( + match (modifiers.repr, modifiers.one_indexed) { + (modifier::WeekdayRepr::Short, _) => [ + (b"Mon".as_slice(), Weekday::Monday), + (b"Tue".as_slice(), Weekday::Tuesday), + (b"Wed".as_slice(), Weekday::Wednesday), + (b"Thu".as_slice(), Weekday::Thursday), + (b"Fri".as_slice(), Weekday::Friday), + (b"Sat".as_slice(), Weekday::Saturday), + (b"Sun".as_slice(), Weekday::Sunday), + ], + (modifier::WeekdayRepr::Long, _) => [ + (b"Monday".as_slice(), Weekday::Monday), + (b"Tuesday".as_slice(), Weekday::Tuesday), + (b"Wednesday".as_slice(), Weekday::Wednesday), + (b"Thursday".as_slice(), Weekday::Thursday), + (b"Friday".as_slice(), Weekday::Friday), + (b"Saturday".as_slice(), Weekday::Saturday), + (b"Sunday".as_slice(), Weekday::Sunday), + ], + (modifier::WeekdayRepr::Sunday, false) => [ + (b"1".as_slice(), Weekday::Monday), + (b"2".as_slice(), Weekday::Tuesday), + (b"3".as_slice(), Weekday::Wednesday), + (b"4".as_slice(), Weekday::Thursday), + (b"5".as_slice(), Weekday::Friday), + (b"6".as_slice(), Weekday::Saturday), + (b"0".as_slice(), Weekday::Sunday), + ], + (modifier::WeekdayRepr::Sunday, true) => [ + (b"2".as_slice(), Weekday::Monday), + (b"3".as_slice(), Weekday::Tuesday), + (b"4".as_slice(), Weekday::Wednesday), + (b"5".as_slice(), Weekday::Thursday), + (b"6".as_slice(), Weekday::Friday), + (b"7".as_slice(), Weekday::Saturday), + (b"1".as_slice(), Weekday::Sunday), + ], + (modifier::WeekdayRepr::Monday, false) => [ + (b"0".as_slice(), Weekday::Monday), + (b"1".as_slice(), Weekday::Tuesday), + (b"2".as_slice(), Weekday::Wednesday), + (b"3".as_slice(), Weekday::Thursday), + (b"4".as_slice(), Weekday::Friday), + (b"5".as_slice(), Weekday::Saturday), + (b"6".as_slice(), Weekday::Sunday), + ], + (modifier::WeekdayRepr::Monday, true) => [ + (b"1".as_slice(), Weekday::Monday), + (b"2".as_slice(), Weekday::Tuesday), + (b"3".as_slice(), Weekday::Wednesday), + (b"4".as_slice(), Weekday::Thursday), + (b"5".as_slice(), Weekday::Friday), + (b"6".as_slice(), Weekday::Saturday), + (b"7".as_slice(), Weekday::Sunday), + ], + }, + modifiers.case_sensitive, + )(input) +} + +/// Parse the "ordinal" component of a `Date`. +pub(crate) fn parse_ordinal( + input: &[u8], + modifiers: modifier::Ordinal, +) -> Option<ParsedItem<'_, NonZeroU16>> { + exactly_n_digits_padded::<3, _>(modifiers.padding)(input) +} + +/// Parse the "day" component of a `Date`. +pub(crate) fn parse_day( + input: &[u8], + modifiers: modifier::Day, +) -> Option<ParsedItem<'_, NonZeroU8>> { + exactly_n_digits_padded::<2, _>(modifiers.padding)(input) +} + +/// Indicate whether the hour is "am" or "pm". +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Period { + #[allow(clippy::missing_docs_in_private_items)] + Am, + #[allow(clippy::missing_docs_in_private_items)] + Pm, +} + +/// Parse the "hour" component of a `Time`. +pub(crate) fn parse_hour(input: &[u8], modifiers: modifier::Hour) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits_padded::<2, _>(modifiers.padding)(input) +} + +/// Parse the "minute" component of a `Time`. +pub(crate) fn parse_minute( + input: &[u8], + modifiers: modifier::Minute, +) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits_padded::<2, _>(modifiers.padding)(input) +} + +/// Parse the "second" component of a `Time`. +pub(crate) fn parse_second( + input: &[u8], + modifiers: modifier::Second, +) -> Option<ParsedItem<'_, u8>> { + exactly_n_digits_padded::<2, _>(modifiers.padding)(input) +} + +/// Parse the "period" component of a `Time`. Required if the hour is on a 12-hour clock. +pub(crate) fn parse_period( + input: &[u8], + modifiers: modifier::Period, +) -> Option<ParsedItem<'_, Period>> { + first_match( + if modifiers.is_uppercase { + [ + (b"AM".as_slice(), Period::Am), + (b"PM".as_slice(), Period::Pm), + ] + } else { + [ + (b"am".as_slice(), Period::Am), + (b"pm".as_slice(), Period::Pm), + ] + }, + modifiers.case_sensitive, + )(input) +} + +/// Parse the "subsecond" component of a `Time`. +pub(crate) fn parse_subsecond( + input: &[u8], + modifiers: modifier::Subsecond, +) -> Option<ParsedItem<'_, u32>> { + use modifier::SubsecondDigits::*; + Some(match modifiers.digits { + One => exactly_n_digits::<1, u32>(input)?.map(|v| v * 100_000_000), + Two => exactly_n_digits::<2, u32>(input)?.map(|v| v * 10_000_000), + Three => exactly_n_digits::<3, u32>(input)?.map(|v| v * 1_000_000), + Four => exactly_n_digits::<4, u32>(input)?.map(|v| v * 100_000), + Five => exactly_n_digits::<5, u32>(input)?.map(|v| v * 10_000), + Six => exactly_n_digits::<6, u32>(input)?.map(|v| v * 1_000), + Seven => exactly_n_digits::<7, u32>(input)?.map(|v| v * 100), + Eight => exactly_n_digits::<8, u32>(input)?.map(|v| v * 10), + Nine => exactly_n_digits::<9, _>(input)?, + OneOrMore => { + let ParsedItem(mut input, mut value) = + any_digit(input)?.map(|v| (v - b'0').extend::<u32>() * 100_000_000); + + let mut multiplier = 10_000_000; + while let Some(ParsedItem(new_input, digit)) = any_digit(input) { + value += (digit - b'0').extend::<u32>() * multiplier; + input = new_input; + multiplier /= 10; + } + + ParsedItem(input, value) + } + }) +} + +/// Parse the "hour" component of a `UtcOffset`. +/// +/// Returns the value and whether the value is negative. This is used for when "-0" is parsed. +pub(crate) fn parse_offset_hour( + input: &[u8], + modifiers: modifier::OffsetHour, +) -> Option<ParsedItem<'_, (i8, bool)>> { + let ParsedItem(input, sign) = opt(sign)(input); + let ParsedItem(input, hour) = exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)?; + match sign { + Some(b'-') => 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`. +pub(crate) fn parse_offset_minute( + input: &[u8], + modifiers: modifier::OffsetMinute, +) -> Option<ParsedItem<'_, i8>> { + Some( + exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)? + .map(|offset_minute| offset_minute.cast_signed()), + ) +} + +/// Parse the "second" component of a `UtcOffset`. +pub(crate) fn parse_offset_second( + input: &[u8], + modifiers: modifier::OffsetSecond, +) -> Option<ParsedItem<'_, i8>> { + Some( + exactly_n_digits_padded::<2, u8>(modifiers.padding)(input)? + .map(|offset_second| offset_second.cast_signed()), + ) +} + +/// Ignore the given number of bytes. +pub(crate) fn parse_ignore( + input: &[u8], + modifiers: modifier::Ignore, +) -> Option<ParsedItem<'_, ()>> { + let modifier::Ignore { count } = modifiers; + let input = input.get((count.get().extend())..)?; + Some(ParsedItem(input, ())) +} + +/// Parse the Unix timestamp component. +pub(crate) fn parse_unix_timestamp( + input: &[u8], + modifiers: modifier::UnixTimestamp, +) -> Option<ParsedItem<'_, i128>> { + let ParsedItem(input, sign) = opt(sign)(input); + let ParsedItem(input, nano_timestamp) = match modifiers.precision { + modifier::UnixTimestampPrecision::Second => n_to_m_digits::<1, 14, u128>(input)? + .map(|val| val * Nanosecond::per(Second).extend::<u128>()), + modifier::UnixTimestampPrecision::Millisecond => n_to_m_digits::<1, 17, u128>(input)? + .map(|val| val * Nanosecond::per(Millisecond).extend::<u128>()), + modifier::UnixTimestampPrecision::Microsecond => n_to_m_digits::<1, 20, u128>(input)? + .map(|val| val * Nanosecond::per(Microsecond).extend::<u128>()), + modifier::UnixTimestampPrecision::Nanosecond => n_to_m_digits::<1, 23, _>(input)?, + }; + + match sign { + Some(b'-') => 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, `None` +/// is returned. +pub(crate) const fn parse_end(input: &[u8], end: modifier::End) -> Option<ParsedItem<'_, ()>> { + let modifier::End {} = end; + + if input.is_empty() { + Some(ParsedItem(input, ())) + } else { + None + } +} diff --git a/vendor/time/src/parsing/iso8601.rs b/vendor/time/src/parsing/iso8601.rs new file mode 100644 index 00000000..a57d6274 --- /dev/null +++ b/vendor/time/src/parsing/iso8601.rs @@ -0,0 +1,332 @@ +//! Parse parts of an ISO 8601-formatted value. + +use num_conv::prelude::*; + +use crate::convert::*; +use crate::error; +use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral}; +use crate::format_description::well_known::iso8601::EncodedConfig; +use crate::format_description::well_known::Iso8601; +use crate::parsing::combinator::rfc::iso8601::{ + day, dayk, dayo, float, hour, min, month, week, year, ExtendedKind, +}; +use crate::parsing::combinator::{ascii_char, sign}; +use crate::parsing::{Parsed, ParsedItem}; + +impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> { + // Basic: [year][month][day] + // Extended: [year]["-"][month]["-"][day] + // Basic: [year][dayo] + // Extended: [year]["-"][dayo] + // Basic: [year]["W"][week][dayk] + // Extended: [year]["-"]["W"][week]["-"][dayk] + /// Parse a date in the basic or extended format. Reduced precision is permitted. + pub(crate) fn parse_date<'a>( + parsed: &'a mut Parsed, + extended_kind: &'a mut ExtendedKind, + ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a { + move |input| { + // Same for any acceptable format. + let ParsedItem(mut input, year) = year(input).ok_or(InvalidComponent("year"))?; + *extended_kind = match ascii_char::<b'-'>(input) { + Some(ParsedItem(new_input, ())) => { + input = new_input; + ExtendedKind::Extended + } + None => ExtendedKind::Basic, // no separator before mandatory month/ordinal/week + }; + + let parsed_month_day = (|| { + let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?; + if extended_kind.is_extended() { + input = ascii_char::<b'-'>(input) + .ok_or(InvalidLiteral)? + .into_inner(); + } + let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?; + Ok(ParsedItem(input, (month, day))) + })(); + let mut ret_error = match parsed_month_day { + Ok(ParsedItem(input, (month, day))) => { + *parsed = parsed + .with_year(year) + .ok_or(InvalidComponent("year"))? + .with_month(month) + .ok_or(InvalidComponent("month"))? + .with_day(day) + .ok_or(InvalidComponent("day"))?; + return Ok(input); + } + Err(err) => err, + }; + + // Don't check for `None`, as the error from year-month-day will always take priority. + if let Some(ParsedItem(input, ordinal)) = dayo(input) { + *parsed = parsed + .with_year(year) + .ok_or(InvalidComponent("year"))? + .with_ordinal(ordinal) + .ok_or(InvalidComponent("ordinal"))?; + return Ok(input); + } + + let parsed_week_weekday = (|| { + let input = ascii_char::<b'W'>(input) + .ok_or((false, InvalidLiteral))? + .into_inner(); + let ParsedItem(mut input, week) = + week(input).ok_or((true, InvalidComponent("week")))?; + if extended_kind.is_extended() { + input = ascii_char::<b'-'>(input) + .ok_or((true, InvalidLiteral))? + .into_inner(); + } + let ParsedItem(input, weekday) = + dayk(input).ok_or((true, InvalidComponent("weekday")))?; + Ok(ParsedItem(input, (week, weekday))) + })(); + match parsed_week_weekday { + Ok(ParsedItem(input, (week, weekday))) => { + *parsed = parsed + .with_iso_year(year) + .ok_or(InvalidComponent("year"))? + .with_iso_week_number(week) + .ok_or(InvalidComponent("week"))? + .with_weekday(weekday) + .ok_or(InvalidComponent("weekday"))?; + return Ok(input); + } + Err((false, _err)) => {} + // This error is more accurate than the one from year-month-day. + Err((true, err)) => ret_error = err, + } + + Err(ret_error.into()) + } + } + + // Basic: ["T"][hour][min][sec] + // Extended: ["T"][hour][":"][min][":"][sec] + // Reduced precision: components after [hour] (including their preceding separator) can be + // omitted. ["T"] can be omitted if there is no date present. + /// Parse a time in the basic or extended format. Reduced precision is permitted. + pub(crate) fn parse_time<'a>( + parsed: &'a mut Parsed, + extended_kind: &'a mut ExtendedKind, + date_is_present: bool, + ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a { + move |mut input| { + if date_is_present { + input = ascii_char::<b'T'>(input) + .ok_or(InvalidLiteral)? + .into_inner(); + } + + let ParsedItem(mut input, hour) = float(input).ok_or(InvalidComponent("hour"))?; + match hour { + (hour, None) => parsed.set_hour_24(hour).ok_or(InvalidComponent("hour"))?, + (hour, Some(fractional_part)) => { + *parsed = parsed + .with_hour_24(hour) + .ok_or(InvalidComponent("hour"))? + .with_minute((fractional_part * Second::per(Minute) as f64) as u8) + .ok_or(InvalidComponent("minute"))? + .with_second( + (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64) + as u8, + ) + .ok_or(InvalidComponent("second"))? + .with_subsecond( + (fractional_part * Nanosecond::per(Hour) as f64 + % Nanosecond::per(Second) as f64) + as u32, + ) + .ok_or(InvalidComponent("subsecond"))?; + return Ok(input); + } + }; + + if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) { + extended_kind + .coerce_extended() + .ok_or(InvalidComponent("minute"))?; + input = new_input; + }; + + let mut input = match float(input) { + Some(ParsedItem(input, (minute, None))) => { + extended_kind.coerce_basic(); + parsed + .set_minute(minute) + .ok_or(InvalidComponent("minute"))?; + input + } + Some(ParsedItem(input, (minute, Some(fractional_part)))) => { + // `None` is valid behavior, so don't error if this fails. + extended_kind.coerce_basic(); + *parsed = parsed + .with_minute(minute) + .ok_or(InvalidComponent("minute"))? + .with_second((fractional_part * Second::per(Minute) as f64) as u8) + .ok_or(InvalidComponent("second"))? + .with_subsecond( + (fractional_part * Nanosecond::per(Minute) as f64 + % Nanosecond::per(Second) as f64) + 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 = parsed + .with_minute(0) + .ok_or(InvalidComponent("minute"))? + .with_second(0) + .ok_or(InvalidComponent("second"))? + .with_subsecond(0) + .ok_or(InvalidComponent("subsecond"))?; + return Ok(input); + } + }; + + if extended_kind.is_extended() { + match ascii_char::<b':'>(input) { + Some(ParsedItem(new_input, ())) => input = new_input, + None => { + *parsed = parsed + .with_second(0) + .ok_or(InvalidComponent("second"))? + .with_subsecond(0) + .ok_or(InvalidComponent("subsecond"))?; + return Ok(input); + } + } + } + + let (input, second, subsecond) = match float(input) { + Some(ParsedItem(input, (second, None))) => (input, second, 0), + Some(ParsedItem(input, (second, Some(fractional_part)))) => ( + input, + second, + round(fractional_part * Nanosecond::per(Second) as f64) 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 = parsed + .with_second(second) + .ok_or(InvalidComponent("second"))? + .with_subsecond(subsecond) + .ok_or(InvalidComponent("subsecond"))?; + + Ok(input) + } + } + + // Basic: [±][hour][min] or ["Z"] + // Extended: [±][hour][":"][min] or ["Z"] + // Reduced precision: [±][hour] or ["Z"] + /// Parse a UTC offset in the basic or extended format. Reduced precision is supported. + pub(crate) fn parse_offset<'a>( + parsed: &'a mut Parsed, + extended_kind: &'a mut ExtendedKind, + ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a { + move |input| { + if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) { + *parsed = parsed + .with_offset_hour(0) + .ok_or(InvalidComponent("offset hour"))? + .with_offset_minute_signed(0) + .ok_or(InvalidComponent("offset minute"))? + .with_offset_second_signed(0) + .ok_or(InvalidComponent("offset second"))?; + return Ok(input); + } + + let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?; + let mut input = hour(input) + .and_then(|parsed_item| { + parsed_item.consume_value(|hour| { + parsed.set_offset_hour(if sign == b'-' { + -hour.cast_signed() + } else { + hour.cast_signed() + }) + }) + }) + .ok_or(InvalidComponent("offset hour"))?; + + if extended_kind.maybe_extended() { + if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) { + extended_kind + .coerce_extended() + .ok_or(InvalidComponent("offset minute"))?; + input = new_input; + }; + } + + match min(input) { + Some(ParsedItem(new_input, min)) => { + input = new_input; + parsed + .set_offset_minute_signed(if sign == b'-' { + -min.cast_signed() + } else { + 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` +fn round(value: f64) -> f64 { + #[cfg(feature = "std")] + { + value.round() + } + #[cfg(not(feature = "std"))] + { + round_impl(value) + } +} + +#[cfg(not(feature = "std"))] +#[allow(clippy::missing_docs_in_private_items)] +fn round_impl(value: f64) -> f64 { + debug_assert!(value.is_sign_positive() && !value.is_nan()); + + let f = value % 1.; + if f < 0.5 { + value - f + } else { + value - f + 1. + } +} diff --git a/vendor/time/src/parsing/mod.rs b/vendor/time/src/parsing/mod.rs new file mode 100644 index 00000000..34e92736 --- /dev/null +++ b/vendor/time/src/parsing/mod.rs @@ -0,0 +1,57 @@ +//! 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. + pub(crate) fn map<U>(self, f: impl FnOnce(T) -> U) -> ParsedItem<'a, U> { + ParsedItem(self.0, f(self.1)) + } + + /// Map the value to a new, optional value, preserving the remaining input. + pub(crate) fn flat_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<ParsedItem<'a, U>> { + Some(ParsedItem(self.0, f(self.1)?)) + } + + /// Consume the stored value with the provided function. The remaining input is returned. + #[must_use = "this returns the remaining input"] + pub(crate) fn consume_value(self, f: impl FnOnce(T) -> Option<()>) -> Option<&'a [u8]> { + f(self.1)?; + Some(self.0) + } + + /// 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. + pub(crate) fn filter(self, f: impl FnOnce(&T) -> bool) -> Option<Self> { + f(&self.1).then_some(self) + } +} + +impl<'a> ParsedItem<'a, ()> { + /// Discard the unit value, returning the remaining input. + #[must_use = "this returns the remaining input"] + pub(crate) const fn into_inner(self) -> &'a [u8] { + self.0 + } +} + +impl<'a> ParsedItem<'a, Option<()>> { + /// Discard the potential unit value, returning the remaining input. + #[must_use = "this returns the remaining input"] + pub(crate) const fn into_inner(self) -> &'a [u8] { + self.0 + } +} diff --git a/vendor/time/src/parsing/parsable.rs b/vendor/time/src/parsing/parsable.rs new file mode 100644 index 00000000..c631c5fe --- /dev/null +++ b/vendor/time/src/parsing/parsable.rs @@ -0,0 +1,800 @@ +//! A trait that can be used to parse an item from an input. + +use core::ops::Deref; + +use num_conv::prelude::*; + +use crate::error::TryFromParsed; +use crate::format_description::well_known::iso8601::EncodedConfig; +use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339}; +use crate::format_description::BorrowedFormatItem; +#[cfg(feature = "alloc")] +use crate::format_description::OwnedFormatItem; +use crate::internal_macros::bug; +use crate::parsing::{Parsed, ParsedItem}; +use crate::{error, Date, Month, OffsetDateTime, Time, UtcOffset, Weekday}; + +/// A type that can be parsed. +#[cfg_attr(docsrs, doc(notable_trait))] +#[doc(alias = "Parseable")] +pub trait Parsable: sealed::Sealed {} +impl Parsable for BorrowedFormatItem<'_> {} +impl Parsable for [BorrowedFormatItem<'_>] {} +#[cfg(feature = "alloc")] +impl Parsable for OwnedFormatItem {} +#[cfg(feature = "alloc")] +impl Parsable for [OwnedFormatItem] {} +impl Parsable for Rfc2822 {} +impl Parsable for Rfc3339 {} +impl<const CONFIG: EncodedConfig> Parsable for Iso8601<CONFIG> {} +impl<T: Deref> Parsable for T where T::Target: Parsable {} + +/// Seal the trait to prevent downstream users from implementing it, while still allowing it to +/// exist in generic bounds. +mod sealed { + #[allow(clippy::wildcard_imports)] + use super::*; + 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. + fn parse(&self, input: &[u8]) -> Result<Parsed, error::Parse> { + let mut parsed = Parsed::new(); + if self.parse_into(input, &mut parsed)?.is_empty() { + Ok(parsed) + } else { + Err(error::Parse::ParseFromDescription( + error::ParseFromDescription::UnexpectedTrailingCharacters, + )) + } + } + + /// Parse a [`Date`] from the format description. + fn parse_date(&self, input: &[u8]) -> Result<Date, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + + /// Parse a [`Time`] from the format description. + fn parse_time(&self, input: &[u8]) -> Result<Time, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + + /// Parse a [`UtcOffset`] from the format description. + fn parse_offset(&self, input: &[u8]) -> Result<UtcOffset, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + + /// Parse a [`PrimitiveDateTime`] from the format description. + fn parse_primitive_date_time( + &self, + input: &[u8], + ) -> Result<PrimitiveDateTime, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + + /// Parse a [`UtcDateTime`] from the format description. + fn parse_utc_date_time(&self, input: &[u8]) -> Result<UtcDateTime, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + + /// Parse a [`OffsetDateTime`] from the format description. + fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> { + Ok(self.parse(input)?.try_into()?) + } + } +} + +impl sealed::Sealed for BorrowedFormatItem<'_> { + 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<'_>] { + 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 { + fn parse_into<'a>( + &self, + input: &'a [u8], + parsed: &mut Parsed, + ) -> Result<&'a [u8], error::Parse> { + Ok(parsed.parse_item(input, self)?) + } +} + +#[cfg(feature = "alloc")] +impl sealed::Sealed for [OwnedFormatItem] { + fn parse_into<'a>( + &self, + input: &'a [u8], + parsed: &mut Parsed, + ) -> Result<&'a [u8], error::Parse> { + Ok(parsed.parse_items(input, self)?) + } +} + +impl<T: Deref> sealed::Sealed for T +where + T::Target: sealed::Sealed, +{ + 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::error::ParseFromDescription::{InvalidComponent, InvalidLiteral}; + use crate::parsing::combinator::rfc::rfc2822::{cfws, fws}; + use crate::parsing::combinator::{ + ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign, + }; + + let colon = ascii_char::<b':'>; + let comma = ascii_char::<b','>; + + let input = opt(cfws)(input).into_inner(); + let weekday = first_match( + [ + (b"Mon".as_slice(), Weekday::Monday), + (b"Tue".as_slice(), Weekday::Tuesday), + (b"Wed".as_slice(), Weekday::Wednesday), + (b"Thu".as_slice(), Weekday::Thursday), + (b"Fri".as_slice(), Weekday::Friday), + (b"Sat".as_slice(), Weekday::Saturday), + (b"Sun".as_slice(), Weekday::Sunday), + ], + false, + )(input); + let input = if let Some(item) = weekday { + let input = item + .consume_value(|value| parsed.set_weekday(value)) + .ok_or(InvalidComponent("weekday"))?; + let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); + opt(cfws)(input).into_inner() + } else { + input + }; + let input = n_to_m_digits::<1, 2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_day(value))) + .ok_or(InvalidComponent("day"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + let input = first_match( + [ + (b"Jan".as_slice(), Month::January), + (b"Feb".as_slice(), Month::February), + (b"Mar".as_slice(), Month::March), + (b"Apr".as_slice(), Month::April), + (b"May".as_slice(), Month::May), + (b"Jun".as_slice(), Month::June), + (b"Jul".as_slice(), Month::July), + (b"Aug".as_slice(), Month::August), + (b"Sep".as_slice(), Month::September), + (b"Oct".as_slice(), Month::October), + (b"Nov".as_slice(), Month::November), + (b"Dec".as_slice(), Month::December), + ], + false, + )(input) + .and_then(|item| item.consume_value(|value| parsed.set_month(value))) + .ok_or(InvalidComponent("month"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + let input = match exactly_n_digits::<4, u32>(input) { + Some(item) => { + let input = item + .flat_map(|year| if year >= 1900 { Some(year) } else { None }) + .and_then(|item| { + item.consume_value(|value| parsed.set_year(value.cast_signed())) + }) + .ok_or(InvalidComponent("year"))?; + fws(input).ok_or(InvalidLiteral)?.into_inner() + } + None => { + let input = exactly_n_digits::<2, u32>(input) + .and_then(|item| { + item.map(|year| if year < 50 { year + 2000 } else { year + 1900 }) + .map(|year| year.cast_signed()) + .consume_value(|value| parsed.set_year(value)) + }) + .ok_or(InvalidComponent("year"))?; + cfws(input).ok_or(InvalidLiteral)?.into_inner() + } + }; + + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value))) + .ok_or(InvalidComponent("hour"))?; + let input = opt(cfws)(input).into_inner(); + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let input = opt(cfws)(input).into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_minute(value))) + .ok_or(InvalidComponent("minute"))?; + + let input = if let Some(input) = colon(opt(cfws)(input).into_inner()) { + let input = input.into_inner(); // discard the colon + let input = opt(cfws)(input).into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_second(value))) + .ok_or(InvalidComponent("second"))?; + cfws(input).ok_or(InvalidLiteral)?.into_inner() + } else { + cfws(input).ok_or(InvalidLiteral)?.into_inner() + }; + + // The RFC explicitly allows leap seconds. + parsed.leap_second_allowed = true; + + #[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522 + let zone_literal = first_match( + [ + (b"UT".as_slice(), 0), + (b"GMT".as_slice(), 0), + (b"EST".as_slice(), -5), + (b"EDT".as_slice(), -4), + (b"CST".as_slice(), -6), + (b"CDT".as_slice(), -5), + (b"MST".as_slice(), -7), + (b"MDT".as_slice(), -6), + (b"PST".as_slice(), -8), + (b"PDT".as_slice(), -7), + ], + false, + )(input) + .or_else(|| match input { + [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => { + Some(ParsedItem(rest, 0)) + } + _ => None, + }); + if let Some(zone_literal) = zone_literal { + let input = zone_literal + .consume_value(|value| parsed.set_offset_hour(value)) + .ok_or(InvalidComponent("offset hour"))?; + parsed + .set_offset_minute_signed(0) + .ok_or(InvalidComponent("offset minute"))?; + parsed + .set_offset_second_signed(0) + .ok_or(InvalidComponent("offset second"))?; + return Ok(input); + } + + let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?; + let input = exactly_n_digits::<2, u8>(input) + .and_then(|item| { + item.map(|offset_hour| { + if offset_sign == b'-' { + -offset_hour.cast_signed() + } else { + offset_hour.cast_signed() + } + }) + .consume_value(|value| parsed.set_offset_hour(value)) + }) + .ok_or(InvalidComponent("offset hour"))?; + let input = exactly_n_digits::<2, u8>(input) + .and_then(|item| { + item.consume_value(|value| parsed.set_offset_minute_signed(value.cast_signed())) + }) + .ok_or(InvalidComponent("offset minute"))?; + + let input = opt(cfws)(input).into_inner(); + + Ok(input) + } + + fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> { + use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral}; + use crate::parsing::combinator::rfc::rfc2822::{cfws, fws}; + use crate::parsing::combinator::{ + ascii_char, exactly_n_digits, first_match, n_to_m_digits, opt, sign, + }; + + let colon = ascii_char::<b':'>; + let comma = ascii_char::<b','>; + + let input = opt(cfws)(input).into_inner(); + // This parses the weekday, but we don't actually use the value anywhere. Because of this, + // just return `()` to avoid unnecessary generated code. + let weekday = first_match( + [ + (b"Mon".as_slice(), ()), + (b"Tue".as_slice(), ()), + (b"Wed".as_slice(), ()), + (b"Thu".as_slice(), ()), + (b"Fri".as_slice(), ()), + (b"Sat".as_slice(), ()), + (b"Sun".as_slice(), ()), + ], + false, + )(input); + let input = if let Some(item) = weekday { + let input = item.into_inner(); + let input = comma(input).ok_or(InvalidLiteral)?.into_inner(); + opt(cfws)(input).into_inner() + } else { + input + }; + let ParsedItem(input, day) = + n_to_m_digits::<1, 2, _>(input).ok_or(InvalidComponent("day"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, month) = first_match( + [ + (b"Jan".as_slice(), Month::January), + (b"Feb".as_slice(), Month::February), + (b"Mar".as_slice(), Month::March), + (b"Apr".as_slice(), Month::April), + (b"May".as_slice(), Month::May), + (b"Jun".as_slice(), Month::June), + (b"Jul".as_slice(), Month::July), + (b"Aug".as_slice(), Month::August), + (b"Sep".as_slice(), Month::September), + (b"Oct".as_slice(), Month::October), + (b"Nov".as_slice(), Month::November), + (b"Dec".as_slice(), Month::December), + ], + false, + )(input) + .ok_or(InvalidComponent("month"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + let (input, year) = match exactly_n_digits::<4, u32>(input) { + Some(item) => { + let ParsedItem(input, year) = item + .flat_map(|year| if year >= 1900 { Some(year) } else { None }) + .ok_or(InvalidComponent("year"))?; + let input = fws(input).ok_or(InvalidLiteral)?.into_inner(); + (input, year) + } + None => { + let ParsedItem(input, year) = exactly_n_digits::<2, u32>(input) + .map(|item| item.map(|year| if year < 50 { year + 2000 } else { year + 1900 })) + .ok_or(InvalidComponent("year"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + (input, year) + } + }; + + let ParsedItem(input, hour) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?; + let input = opt(cfws)(input).into_inner(); + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let input = opt(cfws)(input).into_inner(); + let ParsedItem(input, minute) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?; + + let (input, mut second) = if let Some(input) = colon(opt(cfws)(input).into_inner()) { + let input = input.into_inner(); // discard the colon + let input = opt(cfws)(input).into_inner(); + let ParsedItem(input, second) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?; + let input = cfws(input).ok_or(InvalidLiteral)?.into_inner(); + (input, second) + } else { + (cfws(input).ok_or(InvalidLiteral)?.into_inner(), 0) + }; + + #[allow(clippy::unnecessary_lazy_evaluations)] // rust-lang/rust-clippy#8522 + let zone_literal = first_match( + [ + (b"UT".as_slice(), 0), + (b"GMT".as_slice(), 0), + (b"EST".as_slice(), -5), + (b"EDT".as_slice(), -4), + (b"CST".as_slice(), -6), + (b"CDT".as_slice(), -5), + (b"MST".as_slice(), -7), + (b"MDT".as_slice(), -6), + (b"PST".as_slice(), -8), + (b"PDT".as_slice(), -7), + ], + false, + )(input) + .or_else(|| match input { + [b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z', rest @ ..] => { + Some(ParsedItem(rest, 0)) + } + _ => None, + }); + + let (input, offset_hour, offset_minute) = if let Some(zone_literal) = zone_literal { + let ParsedItem(input, offset_hour) = zone_literal; + (input, offset_hour, 0) + } else { + let ParsedItem(input, offset_sign) = + sign(input).ok_or(InvalidComponent("offset hour"))?; + let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input) + .map(|item| { + item.map(|offset_hour| { + if offset_sign == b'-' { + -offset_hour.cast_signed() + } else { + offset_hour.cast_signed() + } + }) + }) + .ok_or(InvalidComponent("offset hour"))?; + let ParsedItem(input, offset_minute) = + exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?; + (input, offset_hour, offset_minute.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 = (|| { + let date = Date::from_calendar_date(year.cast_signed(), month, day)?; + let time = Time::from_hms_nano(hour, minute, second, nanosecond)?; + let offset = 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 { + name: "second", + minimum: 0, + maximum: 59, + value: 60, + conditional_message: Some("because leap seconds are not supported"), + }, + ))); + } + + Ok(dt) + } +} + +impl sealed::Sealed for Rfc3339 { + fn parse_into<'a>( + &self, + input: &'a [u8], + parsed: &mut Parsed, + ) -> Result<&'a [u8], error::Parse> { + use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral}; + use crate::parsing::combinator::{ + any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign, + }; + + let dash = ascii_char::<b'-'>; + let colon = ascii_char::<b':'>; + + let input = exactly_n_digits::<4, u32>(input) + .and_then(|item| item.consume_value(|value| parsed.set_year(value.cast_signed()))) + .ok_or(InvalidComponent("year"))?; + let input = dash(input).ok_or(InvalidLiteral)?.into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.flat_map(|value| Month::from_number(value).ok())) + .and_then(|item| item.consume_value(|value| parsed.set_month(value))) + .ok_or(InvalidComponent("month"))?; + let input = dash(input).ok_or(InvalidLiteral)?.into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_day(value))) + .ok_or(InvalidComponent("day"))?; + + // 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 = input.get(1..).ok_or(InvalidComponent("separator"))?; + + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_hour_24(value))) + .ok_or(InvalidComponent("hour"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_minute(value))) + .ok_or(InvalidComponent("minute"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let input = exactly_n_digits::<2, _>(input) + .and_then(|item| item.consume_value(|value| parsed.set_second(value))) + .ok_or(InvalidComponent("second"))?; + let input = if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) { + let ParsedItem(mut input, mut value) = any_digit(input) + .ok_or(InvalidComponent("subsecond"))? + .map(|v| (v - b'0').extend::<u32>() * 100_000_000); + + let mut multiplier = 10_000_000; + while let Some(ParsedItem(new_input, digit)) = any_digit(input) { + value += (digit - b'0').extend::<u32>() * multiplier; + input = new_input; + multiplier /= 10; + } + + parsed + .set_subsecond(value) + .ok_or(InvalidComponent("subsecond"))?; + input + } else { + input + }; + + // The RFC explicitly allows leap seconds. + parsed.leap_second_allowed = true; + + if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) { + parsed + .set_offset_hour(0) + .ok_or(InvalidComponent("offset hour"))?; + parsed + .set_offset_minute_signed(0) + .ok_or(InvalidComponent("offset minute"))?; + parsed + .set_offset_second_signed(0) + .ok_or(InvalidComponent("offset second"))?; + return Ok(input); + } + + let ParsedItem(input, offset_sign) = sign(input).ok_or(InvalidComponent("offset hour"))?; + let input = exactly_n_digits::<2, u8>(input) + .and_then(|item| { + item.filter(|&offset_hour| offset_hour <= 23)? + .map(|offset_hour| { + if offset_sign == b'-' { + -offset_hour.cast_signed() + } else { + offset_hour.cast_signed() + } + }) + .consume_value(|value| parsed.set_offset_hour(value)) + }) + .ok_or(InvalidComponent("offset hour"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let input = exactly_n_digits::<2, u8>(input) + .and_then(|item| { + item.map(|offset_minute| { + if offset_sign == b'-' { + -offset_minute.cast_signed() + } else { + offset_minute.cast_signed() + } + }) + .consume_value(|value| parsed.set_offset_minute_signed(value)) + }) + .ok_or(InvalidComponent("offset minute"))?; + + Ok(input) + } + + fn parse_offset_date_time(&self, input: &[u8]) -> Result<OffsetDateTime, error::Parse> { + use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral}; + use crate::parsing::combinator::{ + any_digit, ascii_char, ascii_char_ignore_case, exactly_n_digits, sign, + }; + + let dash = ascii_char::<b'-'>; + let colon = ascii_char::<b':'>; + + let ParsedItem(input, year) = + exactly_n_digits::<4, u32>(input).ok_or(InvalidComponent("year"))?; + let input = dash(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, month) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("month"))?; + let input = dash(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, day) = + exactly_n_digits::<2, _>(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 = input.get(1..).ok_or(InvalidComponent("separator"))?; + + let ParsedItem(input, hour) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("hour"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, minute) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("minute"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, mut second) = + exactly_n_digits::<2, _>(input).ok_or(InvalidComponent("second"))?; + let ParsedItem(input, mut nanosecond) = + if let Some(ParsedItem(input, ())) = ascii_char::<b'.'>(input) { + let ParsedItem(mut input, mut value) = any_digit(input) + .ok_or(InvalidComponent("subsecond"))? + .map(|v| (v - b'0').extend::<u32>() * 100_000_000); + + let mut multiplier = 10_000_000; + while let Some(ParsedItem(new_input, digit)) = any_digit(input) { + value += (digit - b'0').extend::<u32>() * multiplier; + input = new_input; + multiplier /= 10; + } + + ParsedItem(input, value) + } else { + ParsedItem(input, 0) + }; + let ParsedItem(input, offset) = { + if let Some(ParsedItem(input, ())) = ascii_char_ignore_case::<b'Z'>(input) { + ParsedItem(input, UtcOffset::UTC) + } else { + let ParsedItem(input, offset_sign) = + sign(input).ok_or(InvalidComponent("offset hour"))?; + let ParsedItem(input, offset_hour) = exactly_n_digits::<2, u8>(input) + .and_then(|parsed| parsed.filter(|&offset_hour| offset_hour <= 23)) + .ok_or(InvalidComponent("offset hour"))?; + let input = colon(input).ok_or(InvalidLiteral)?.into_inner(); + let ParsedItem(input, offset_minute) = + exactly_n_digits::<2, u8>(input).ok_or(InvalidComponent("offset minute"))?; + UtcOffset::from_hms( + if offset_sign == b'-' { + -offset_hour.cast_signed() + } else { + offset_hour.cast_signed() + }, + if offset_sign == b'-' { + -offset_minute.cast_signed() + } else { + offset_minute.cast_signed() + }, + 0, + ) + .map(|offset| ParsedItem(input, offset)) + .map_err(|mut err| { + // Provide the user a more accurate error. + if err.name == "hours" { + err.name = "offset hour"; + } else if err.name == "minutes" { + err.name = "offset minute"; + } + err + }) + .map_err(TryFromParsed::ComponentRange)? + } + }; + + if !input.is_empty() { + return Err(error::Parse::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 = Month::from_number(month) + .and_then(|month| Date::from_calendar_date(year.cast_signed(), month, day)) + .map_err(TryFromParsed::ComponentRange)?; + let time = 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 { + name: "second", + minimum: 0, + maximum: 59, + value: 60, + conditional_message: Some("because leap seconds are not supported"), + }, + ))); + } + + Ok(dt) + } +} + +impl<const CONFIG: EncodedConfig> sealed::Sealed for Iso8601<CONFIG> { + fn parse_into<'a>( + &self, + mut input: &'a [u8], + parsed: &mut Parsed, + ) -> Result<&'a [u8], error::Parse> { + use crate::parsing::combinator::rfc::iso8601::ExtendedKind; + + let mut extended_kind = ExtendedKind::Unknown; + let mut date_is_present = false; + let mut time_is_present = false; + let mut offset_is_present = false; + let mut first_error = None; + + 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) + } +} diff --git a/vendor/time/src/parsing/parsed.rs b/vendor/time/src/parsing/parsed.rs new file mode 100644 index 00000000..a6f00d9b --- /dev/null +++ b/vendor/time/src/parsing/parsed.rs @@ -0,0 +1,1118 @@ +//! Information parsed from an input and format description. + +use core::num::{NonZeroU16, NonZeroU8}; + +use deranged::{ + OptionRangedI128, OptionRangedI16, OptionRangedI32, OptionRangedI8, OptionRangedU16, + OptionRangedU32, OptionRangedU8, RangedI128, RangedI16, RangedI32, RangedI8, RangedU16, + RangedU32, RangedU8, +}; +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::{modifier, BorrowedFormatItem, Component}; +use crate::internal_macros::{bug, const_try_opt}; +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, Period, +}; +use crate::parsing::ParsedItem; +use crate::{ + error, Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, +}; + +/// 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<'_> { + 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 { + 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<Month>, + /// 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<Weekday>, + /// 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(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<bool>, + /// Minute within the hour. + minute: OptionRangedU8<0, { Minute::per(Hour) - 1 }>, + /// Second within the minute. + // do not subtract one, as leap seconds may be allowed + second: OptionRangedU8<0, { Second::per(Minute) }>, + /// Nanosecond within the second. + subsecond: OptionRangedU32<0, { Nanosecond::per(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(Hour) - 1) as i8) }, { (Minute::per(Hour) - 1) as i8 }>, + /// Seconds within the minute of the UTC offset. + offset_second: + OptionRangedI8<{ -((Second::per(Minute) - 1) as i8) }, { (Second::per(Minute) - 1) as i8 }>, + /// 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 { + fn default() -> Self { + Self::new() + } +} + +impl Parsed { + /// Create a new instance of `Parsed` with no information known. + 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. + 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. + 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. + 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 => { + NonZeroU8::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 { + NonZeroU8::new(value).and_then(|value| self.set_hour_12(value)) + } else { + self.set_hour_24(value) + } + .ok_or(InvalidComponent("hour"))?; + Ok(remaining) + } + Component::Minute(modifiers) => parse_minute(input, modifiers) + .and_then(|parsed| parsed.consume_value(|value| self.set_minute(value))) + .ok_or(InvalidComponent("minute")), + Component::Period(modifiers) => parse_period(input, modifiers) + .and_then(|parsed| { + parsed.consume_value(|value| self.set_hour_12_is_pm(value == Period::Pm)) + }) + .ok_or(InvalidComponent("period")), + Component::Second(modifiers) => parse_second(input, modifiers) + .and_then(|parsed| parsed.consume_value(|value| self.set_second(value))) + .ok_or(InvalidComponent("second")), + Component::Subsecond(modifiers) => parse_subsecond(input, modifiers) + .and_then(|parsed| parsed.consume_value(|value| self.set_subsecond(value))) + .ok_or(InvalidComponent("subsecond")), + Component::OffsetHour(modifiers) => parse_offset_hour(input, modifiers) + .and_then(|parsed| { + parsed.consume_value(|(value, 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. + pub const fn year(&self) -> Option<i32> { + 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`. + pub const fn year_century(&self) -> Option<i16> { + 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. + pub const fn year_century_is_negative(&self) -> Option<bool> { + match self.year_century() { + Some(_) => Some(self.year_century_is_negative), + None => None, + } + } + + /// Obtain the `year_last_two` component. + pub const fn year_last_two(&self) -> Option<u8> { + self.year_last_two.get_primitive() + } + + /// Obtain the `iso_year` component. + pub const fn iso_year(&self) -> Option<i32> { + 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`. + pub const fn iso_year_century(&self) -> Option<i16> { + 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. + pub const fn iso_year_century_is_negative(&self) -> Option<bool> { + match self.iso_year_century() { + Some(_) => Some(self.iso_year_century_is_negative), + None => None, + } + } + + /// Obtain the `iso_year_last_two` component. + pub const fn iso_year_last_two(&self) -> Option<u8> { + self.iso_year_last_two.get_primitive() + } + + /// Obtain the `month` component. + pub const fn month(&self) -> Option<Month> { + self.month + } + + /// Obtain the `sunday_week_number` component. + pub const fn sunday_week_number(&self) -> Option<u8> { + self.sunday_week_number.get_primitive() + } + + /// Obtain the `monday_week_number` component. + pub const fn monday_week_number(&self) -> Option<u8> { + self.monday_week_number.get_primitive() + } + + /// Obtain the `iso_week_number` component. + pub const fn iso_week_number(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.iso_week_number.get_primitive())) + } + + /// Obtain the `weekday` component. + pub const fn weekday(&self) -> Option<Weekday> { + self.weekday + } + + /// Obtain the `ordinal` component. + pub const fn ordinal(&self) -> Option<NonZeroU16> { + NonZeroU16::new(const_try_opt!(self.ordinal.get_primitive())) + } + + /// Obtain the `day` component. + pub const fn day(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.day.get_primitive())) + } + + /// Obtain the `hour_24` component. + pub const fn hour_24(&self) -> Option<u8> { + self.hour_24.get_primitive() + } + + /// Obtain the `hour_12` component. + pub const fn hour_12(&self) -> Option<NonZeroU8> { + NonZeroU8::new(const_try_opt!(self.hour_12.get_primitive())) + } + + /// Obtain the `hour_12_is_pm` component. + pub const fn hour_12_is_pm(&self) -> Option<bool> { + self.hour_12_is_pm + } + + /// Obtain the `minute` component. + pub const fn minute(&self) -> Option<u8> { + self.minute.get_primitive() + } + + /// Obtain the `second` component. + pub const fn second(&self) -> Option<u8> { + self.second.get_primitive() + } + + /// Obtain the `subsecond` component. + pub const fn subsecond(&self) -> Option<u32> { + self.subsecond.get_primitive() + } + + /// Obtain the `offset_hour` component. + pub const fn offset_hour(&self) -> Option<i8> { + 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")] + pub const fn offset_minute(&self) -> Option<u8> { + Some(const_try_opt!(self.offset_minute_signed()).unsigned_abs()) + } + + /// Obtain the `offset_minute` component. + pub const fn offset_minute_signed(&self) -> Option<i8> { + 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")] + pub const fn offset_second(&self) -> Option<u8> { + Some(const_try_opt!(self.offset_second_signed()).unsigned_abs()) + } + + /// Obtain the `offset_second` component. + pub const fn offset_second_signed(&self) -> Option<i8> { + 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. + pub const fn unix_timestamp_nanos(&self) -> Option<i128> { + 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.")] + pub fn $setter(&mut self, value: $type) -> Option<()> { + *self = self.$builder(value)?; + Some(()) + } + )*}; +} + +/// Setter methods +/// +/// All setters return `Option<()>`, which is `Some` if the value was set, and `None` if not. The +/// setters _may_ fail if the value is invalid, though behavior is not guaranteed. +impl Parsed { + setters! { + 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. + pub 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. + pub 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 NonZeroU8; + weekday set_weekday with_weekday Weekday; + ordinal set_ordinal with_ordinal NonZeroU16; + day set_day with_day NonZeroU8; + hour_24 set_hour_24 with_hour_24 u8; + hour_12 set_hour_12 with_hour_12 NonZeroU8; + 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" + )] + pub 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" + )] + pub 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<Self>`, which is `Some` if the value was set, and `None` if +/// not. The builder methods _may_ fail if the value is invalid, though behavior is not guaranteed. +impl Parsed { + /// Set the `year` component and return `self`. + pub const fn with_year(mut self, value: i32) -> Option<Self> { + 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. + pub const fn with_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> { + 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`. + pub const fn with_year_last_two(mut self, value: u8) -> Option<Self> { + self.year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `iso_year` component and return `self`. + pub const fn with_iso_year(mut self, value: i32) -> Option<Self> { + 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. + pub const fn with_iso_year_century(mut self, value: i16, is_negative: bool) -> Option<Self> { + 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`. + pub const fn with_iso_year_last_two(mut self, value: u8) -> Option<Self> { + self.iso_year_last_two = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `month` component and return `self`. + pub const fn with_month(mut self, value: Month) -> Option<Self> { + self.month = Some(value); + Some(self) + } + + /// Set the `sunday_week_number` component and return `self`. + pub const fn with_sunday_week_number(mut self, value: u8) -> Option<Self> { + self.sunday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `monday_week_number` component and return `self`. + pub const fn with_monday_week_number(mut self, value: u8) -> Option<Self> { + self.monday_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `iso_week_number` component and return `self`. + pub const fn with_iso_week_number(mut self, value: NonZeroU8) -> Option<Self> { + self.iso_week_number = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `weekday` component and return `self`. + pub const fn with_weekday(mut self, value: Weekday) -> Option<Self> { + self.weekday = Some(value); + Some(self) + } + + /// Set the `ordinal` component and return `self`. + pub const fn with_ordinal(mut self, value: NonZeroU16) -> Option<Self> { + self.ordinal = OptionRangedU16::Some(const_try_opt!(RangedU16::new(value.get()))); + Some(self) + } + + /// Set the `day` component and return `self`. + pub const fn with_day(mut self, value: NonZeroU8) -> Option<Self> { + self.day = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `hour_24` component and return `self`. + pub const fn with_hour_24(mut self, value: u8) -> Option<Self> { + self.hour_24 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `hour_12` component and return `self`. + pub const fn with_hour_12(mut self, value: NonZeroU8) -> Option<Self> { + self.hour_12 = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value.get()))); + Some(self) + } + + /// Set the `hour_12_is_pm` component and return `self`. + pub const fn with_hour_12_is_pm(mut self, value: bool) -> Option<Self> { + self.hour_12_is_pm = Some(value); + Some(self) + } + + /// Set the `minute` component and return `self`. + pub const fn with_minute(mut self, value: u8) -> Option<Self> { + self.minute = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `second` component and return `self`. + pub const fn with_second(mut self, value: u8) -> Option<Self> { + self.second = OptionRangedU8::Some(const_try_opt!(RangedU8::new(value))); + Some(self) + } + + /// Set the `subsecond` component and return `self`. + pub const fn with_subsecond(mut self, value: u32) -> Option<Self> { + self.subsecond = OptionRangedU32::Some(const_try_opt!(RangedU32::new(value))); + Some(self) + } + + /// Set the `offset_hour` component and return `self`. + pub const fn with_offset_hour(mut self, value: i8) -> Option<Self> { + 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" + )] + pub const fn with_offset_minute(self, value: u8) -> Option<Self> { + if value > i8::MAX as u8 { + None + } else { + self.with_offset_minute_signed(value as i8) + } + } + + /// Set the `offset_minute` component and return `self`. + pub const fn with_offset_minute_signed(mut self, value: i8) -> Option<Self> { + 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" + )] + pub const fn with_offset_second(self, value: u8) -> Option<Self> { + if value > i8::MAX as u8 { + None + } else { + self.with_offset_second_signed(value as i8) + } + } + + /// Set the `offset_second` component and return `self`. + pub const fn with_offset_second_signed(mut self, value: i8) -> Option<Self> { + self.offset_second = OptionRangedI8::Some(const_try_opt!(RangedI8::new(value))); + Some(self) + } + + /// Set the `unix_timestamp_nanos` component and return `self`. + pub const fn with_unix_timestamp_nanos(mut self, value: i128) -> Option<Self> { + self.unix_timestamp_nanos = OptionRangedI128::Some(const_try_opt!(RangedI128::new(value))); + Some(self) + } +} + +impl TryFrom<Parsed> for Date { + type Error = error::TryFromParsed; + + fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> { + /// Match on the components that need to be present. + macro_rules! match_ { + (_ => $catch_all:expr $(,)?) => { + $catch_all + }; + (($($name:ident),* $(,)?) => $arm:expr, $($rest:tt)*) => { + if let ($(Some($name)),*) = ($(parsed.$name()),*) { + $arm + } else { + match_!($($rest)*) + } + }; + } + + /// Get the value needed to adjust the ordinal day for Sunday and Monday-based week + /// numbering. + const fn adjustment(year: i32) -> i16 { + // 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::<i32>() - last_two.cast_signed().extend::<i32>() + } else { + 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>() + }; + 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::<i32>() - last_two.cast_signed().extend::<i32>() + } else { + 100 * century.extend::<i32>() + last_two.cast_signed().extend::<i32>() + }; + 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::<i16>() * 7 + + weekday.number_days_from_sunday().cast_signed().extend::<i16>() + - adjustment(year) + + 1).cast_unsigned(), + )?), + (year, monday_week_number, weekday) => Ok(Self::from_ordinal_date( + year, + (monday_week_number.cast_signed().extend::<i16>() * 7 + + weekday.number_days_from_monday().cast_signed().extend::<i16>() + - adjustment(year) + + 1).cast_unsigned(), + )?), + _ => Err(InsufficientInformation), + } + } +} + +impl TryFrom<Parsed> for Time { + type Error = error::TryFromParsed; + + fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { + let hour = match (parsed.hour_24(), parsed.hour_12(), parsed.hour_12_is_pm()) { + (Some(hour), _, _) => hour, + (_, Some(hour), Some(false)) if hour.get() == 12 => 0, + (_, Some(hour), Some(true)) if hour.get() == 12 => 12, + (_, Some(hour), Some(false)) => hour.get(), + (_, Some(hour), Some(true)) => hour.get() + 12, + _ => return Err(InsufficientInformation), + }; + + if parsed.hour_24().is_none() + && parsed.hour_12().is_some() + && parsed.hour_12_is_pm().is_some() + && parsed.minute().is_none() + && parsed.second().is_none() + && parsed.subsecond().is_none() + { + return Ok(Self::from_hms_nano(hour, 0, 0, 0)?); + } + + // 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), + } + } +} + +fn utc_offset_try_from_parsed<const REQUIRED: bool>( + parsed: Parsed, +) -> Result<UtcOffset, error::TryFromParsed> { + 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(|mut err| { + // Provide the user a more accurate error. + if err.name == "hours" { + err.name = "offset hour"; + } else if err.name == "minutes" { + err.name = "offset minute"; + } else if err.name == "seconds" { + err.name = "offset second"; + } + err.into() + }) +} + +impl TryFrom<Parsed> for UtcOffset { + type Error = error::TryFromParsed; + + fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { + utc_offset_try_from_parsed::<true>(parsed) + } +} + +impl TryFrom<Parsed> for PrimitiveDateTime { + type Error = error::TryFromParsed; + + fn try_from(parsed: Parsed) -> Result<Self, Self::Error> { + Ok(Self::new(parsed.try_into()?, parsed.try_into()?)) + } +} + +impl TryFrom<Parsed> for UtcDateTime { + type Error = error::TryFromParsed; + + fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> { + 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::<false>(parsed)?, + ) + .to_utc(); + + if leap_second_input && !dt.is_valid_leap_second_stand_in() { + return Err(error::TryFromParsed::ComponentRange( + error::ComponentRange { + name: "second", + minimum: 0, + maximum: 59, + value: 60, + conditional_message: Some("because leap seconds are not supported"), + }, + )); + } + Ok(dt) + } +} + +impl TryFrom<Parsed> for OffsetDateTime { + type Error = error::TryFromParsed; + + fn try_from(mut parsed: Parsed) -> Result<Self, Self::Error> { + 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 { + name: "second", + minimum: 0, + maximum: 59, + value: 60, + conditional_message: Some("because leap seconds are not supported"), + }, + )); + } + Ok(dt) + } +} diff --git a/vendor/time/src/parsing/shim.rs b/vendor/time/src/parsing/shim.rs new file mode 100644 index 00000000..ced7feb0 --- /dev/null +++ b/vendor/time/src/parsing/shim.rs @@ -0,0 +1,50 @@ +//! Extension traits for things either not implemented or not yet stable in the MSRV. + +/// Equivalent of `foo.parse()` for slices. +pub(crate) trait IntegerParseBytes<T> { + #[allow(clippy::missing_docs_in_private_items)] + fn parse_bytes(&self) -> Option<T>; +} + +impl<T: Integer> IntegerParseBytes<T> for [u8] { + fn parse_bytes(&self) -> Option<T> { + T::parse_bytes(self) + } +} + +/// Marker trait for all integer types, including `NonZero*` +pub(crate) trait Integer: Sized { + #[allow(clippy::missing_docs_in_private_items)] + fn parse_bytes(src: &[u8]) -> Option<Self>; +} + +/// Parse the given types from bytes. +macro_rules! impl_parse_bytes { + ($($t:ty)*) => ($( + impl Integer for $t { + #[allow(trivial_numeric_casts)] + fn parse_bytes(src: &[u8]) -> Option<Self> { + src.iter().try_fold::<Self, _, _>(0, |result, c| { + result.checked_mul(10)?.checked_add((c - b'0') as Self) + }) + } + } + )*) +} +impl_parse_bytes! { u8 u16 u32 u128 } + +/// Parse the given types from bytes. +macro_rules! impl_parse_bytes_nonzero { + ($($t:ty)*) => {$( + impl Integer for $t { + fn parse_bytes(src: &[u8]) -> Option<Self> { + Self::new(src.parse_bytes()?) + } + } + )*} +} + +impl_parse_bytes_nonzero! { + core::num::NonZeroU8 + core::num::NonZeroU16 +} diff --git a/vendor/time/src/primitive_date_time.rs b/vendor/time/src/primitive_date_time.rs new file mode 100644 index 00000000..07c11cba --- /dev/null +++ b/vendor/time/src/primitive_date_time.rs @@ -0,0 +1,1050 @@ +//! The [`PrimitiveDateTime`] struct and its associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::fmt; +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::{ + error, util, Date, Duration, Month, OffsetDateTime, Time, UtcDateTime, UtcOffset, Weekday, +}; + +/// Combined date and time. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct PrimitiveDateTime { + date: Date, + time: Time, +} + +impl PrimitiveDateTime { + /// The smallest value that can be represented by `PrimitiveDateTime`. + /// + /// Depending on `large-dates` feature flag, value of this constant may vary. + /// + /// 1. With `large-dates` disabled it is equal to `-9999-01-01 00:00:00.0` + /// 2. With `large-dates` enabled it is equal to `-999999-01-01 00:00:00.0` + /// + /// ```rust + /// # use time::PrimitiveDateTime; + /// # 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), + /// ); + /// ``` + 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)); + /// ``` + pub const fn date(self) -> Date { + self.date + } + + /// Get the [`Time`] component of the `PrimitiveDateTime`. + /// + /// ```rust + /// # use time_macros::{datetime, time}; + /// assert_eq!(datetime!(2019-01-01 0:00).time(), time!(0:00)); + /// ``` + pub const fn time(self) -> Time { + self.time + } + + /// Get the year of the date. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).year(), 2019); + /// assert_eq!(datetime!(2019-12-31 0:00).year(), 2019); + /// assert_eq!(datetime!(2020-01-01 0:00).year(), 2020); + /// ``` + pub const fn year(self) -> i32 { + self.date().year() + } + + /// Get the month of the date. + /// + /// ```rust + /// # use time::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); + /// ``` + pub const fn month(self) -> Month { + self.date().month() + } + + /// Get the day of the date. + /// + /// The returned value will always be in the range `1..=31`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).day(), 1); + /// assert_eq!(datetime!(2019-12-31 0:00).day(), 31); + /// ``` + pub const fn day(self) -> u8 { + self.date().day() + } + + /// Get the day of the year. + /// + /// The returned value will always be in the range `1..=366` (`1..=365` for common years). + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).ordinal(), 1); + /// assert_eq!(datetime!(2019-12-31 0:00).ordinal(), 365); + /// ``` + pub const fn ordinal(self) -> u16 { + self.date().ordinal() + } + + /// Get the ISO week number. + /// + /// The returned value will always be in the range `1..=53`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).iso_week(), 1); + /// assert_eq!(datetime!(2019-10-04 0:00).iso_week(), 40); + /// assert_eq!(datetime!(2020-01-01 0:00).iso_week(), 1); + /// assert_eq!(datetime!(2020-12-31 0:00).iso_week(), 53); + /// assert_eq!(datetime!(2021-01-01 0:00).iso_week(), 53); + /// ``` + pub const fn iso_week(self) -> u8 { + self.date().iso_week() + } + + /// Get the week number where week 1 begins on the first Sunday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).sunday_based_week(), 0); + /// assert_eq!(datetime!(2020-01-01 0:00).sunday_based_week(), 0); + /// assert_eq!(datetime!(2020-12-31 0:00).sunday_based_week(), 52); + /// assert_eq!(datetime!(2021-01-01 0:00).sunday_based_week(), 0); + /// ``` + pub const fn sunday_based_week(self) -> u8 { + self.date().sunday_based_week() + } + + /// Get the week number where week 1 begins on the first Monday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).monday_based_week(), 0); + /// assert_eq!(datetime!(2020-01-01 0:00).monday_based_week(), 0); + /// assert_eq!(datetime!(2020-12-31 0:00).monday_based_week(), 52); + /// assert_eq!(datetime!(2021-01-01 0:00).monday_based_week(), 0); + /// ``` + pub const fn monday_based_week(self) -> u8 { + self.date().monday_based_week() + } + + /// Get the year, month, and day. + /// + /// ```rust + /// # use time::Month; + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2019-01-01 0:00).to_calendar_date(), + /// (2019, Month::January, 1) + /// ); + /// ``` + pub const fn to_calendar_date(self) -> (i32, Month, u8) { + self.date().to_calendar_date() + } + + /// Get the year and ordinal day number. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1)); + /// ``` + pub const fn to_ordinal_date(self) -> (i32, u16) { + self.date().to_ordinal_date() + } + + /// Get the ISO 8601 year, week number, and weekday. + /// + /// ```rust + /// # use time::Weekday::*; + /// # 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) + /// ); + /// ``` + 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); + /// ``` + pub const fn weekday(self) -> Weekday { + self.date().weekday() + } + + /// Get the Julian day for the date. The time is not taken into account for this calculation. + /// + /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is + /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(-4713-11-24 0:00).to_julian_day(), 0); + /// assert_eq!(datetime!(2000-01-01 0:00).to_julian_day(), 2_451_545); + /// assert_eq!(datetime!(2019-01-01 0:00).to_julian_day(), 2_458_485); + /// assert_eq!(datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849); + /// ``` + pub const fn to_julian_day(self) -> i32 { + self.date().to_julian_day() + } + + /// Get the clock hour, minute, and second. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms(), (0, 0, 0)); + /// assert_eq!(datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59)); + /// ``` + pub const fn as_hms(self) -> (u8, u8, u8) { + self.time().as_hms() + } + + /// Get the clock hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_milli(), (0, 0, 0, 0)); + /// assert_eq!( + /// datetime!(2020-01-01 23:59:59.999).as_hms_milli(), + /// (23, 59, 59, 999) + /// ); + /// ``` + pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { + self.time().as_hms_milli() + } + + /// Get the clock hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_micro(), (0, 0, 0, 0)); + /// assert_eq!( + /// datetime!(2020-01-01 23:59:59.999_999).as_hms_micro(), + /// (23, 59, 59, 999_999) + /// ); + /// ``` + pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { + self.time().as_hms_micro() + } + + /// Get the clock hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2020-01-01 0:00:00).as_hms_nano(), (0, 0, 0, 0)); + /// assert_eq!( + /// datetime!(2020-01-01 23:59:59.999_999_999).as_hms_nano(), + /// (23, 59, 59, 999_999_999) + /// ); + /// ``` + pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { + self.time().as_hms_nano() + } + + /// Get the clock hour. + /// + /// The returned value will always be in the range `0..24`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).hour(), 0); + /// assert_eq!(datetime!(2019-01-01 23:59:59).hour(), 23); + /// ``` + pub const fn hour(self) -> u8 { + self.time().hour() + } + + /// Get the minute within the hour. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).minute(), 0); + /// assert_eq!(datetime!(2019-01-01 23:59:59).minute(), 59); + /// ``` + pub const fn minute(self) -> u8 { + self.time().minute() + } + + /// Get the second within the minute. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).second(), 0); + /// assert_eq!(datetime!(2019-01-01 23:59:59).second(), 59); + /// ``` + pub const fn second(self) -> u8 { + self.time().second() + } + + /// Get the milliseconds within the second. + /// + /// The returned value will always be in the range `0..1_000`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).millisecond(), 0); + /// assert_eq!(datetime!(2019-01-01 23:59:59.999).millisecond(), 999); + /// ``` + pub const fn millisecond(self) -> u16 { + self.time().millisecond() + } + + /// Get the microseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).microsecond(), 0); + /// assert_eq!( + /// datetime!(2019-01-01 23:59:59.999_999).microsecond(), + /// 999_999 + /// ); + /// ``` + pub const fn microsecond(self) -> u32 { + self.time().microsecond() + } + + /// Get the nanoseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000_000`. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!(datetime!(2019-01-01 0:00).nanosecond(), 0); + /// assert_eq!( + /// datetime!(2019-01-01 23:59:59.999_999_999).nanosecond(), + /// 999_999_999, + /// ); + /// ``` + pub const fn nanosecond(self) -> u32 { + self.time().nanosecond() + } + + /// Assuming that the existing `PrimitiveDateTime` represents a moment in the provided + /// [`UtcOffset`], return an [`OffsetDateTime`]. + /// + /// ```rust + /// # use time_macros::{datetime, offset}; + /// assert_eq!( + /// datetime!(2019-01-01 0:00) + /// .assume_offset(offset!(UTC)) + /// .unix_timestamp(), + /// 1_546_300_800, + /// ); + /// assert_eq!( + /// datetime!(2019-01-01 0:00) + /// .assume_offset(offset!(-1)) + /// .unix_timestamp(), + /// 1_546_304_400, + /// ); + /// ``` + pub const fn assume_offset(self, offset: UtcOffset) -> OffsetDateTime { + OffsetDateTime::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. + 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, + /// ); + /// ``` + 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)) + /// ); + /// ``` + pub const fn checked_add(self, duration: Duration) -> Option<Self> { + let (date_adjustment, time) = self.time.adjusting_add(duration); + let date = const_try_opt!(self.date.checked_add(duration)); + + Some(Self { + date: match date_adjustment { + util::DateAdjustment::Previous => const_try_opt!(date.previous_day()), + util::DateAdjustment::Next => const_try_opt!(date.next_day()), + util::DateAdjustment::None => date, + }, + time, + }) + } + + /// Computes `self - duration`, returning `None` if an overflow occurred. + /// + /// ``` + /// # use time::{Date, ext::NumericalDuration}; + /// # use time_macros::datetime; + /// let datetime = Date::MIN.midnight(); + /// assert_eq!(datetime.checked_sub(2.days()), None); + /// + /// let datetime = Date::MAX.midnight(); + /// assert_eq!(datetime.checked_sub((-1).days()), None); + /// + /// assert_eq!( + /// datetime!(2019-11-25 15:30).checked_sub(27.hours()), + /// Some(datetime!(2019-11-24 12:30)) + /// ); + /// ``` + pub const fn checked_sub(self, duration: Duration) -> Option<Self> { + let (date_adjustment, time) = self.time.adjusting_sub(duration); + let date = const_try_opt!(self.date.checked_sub(duration)); + + Some(Self { + date: match date_adjustment { + util::DateAdjustment::Previous => const_try_opt!(date.previous_day()), + util::DateAdjustment::Next => const_try_opt!(date.next_day()), + util::DateAdjustment::None => date, + }, + time, + }) + } + + /// Computes `self + duration`, saturating value on overflow. + /// + /// ``` + /// # use time::{PrimitiveDateTime, ext::NumericalDuration}; + /// # use time_macros::datetime; + /// assert_eq!( + /// PrimitiveDateTime::MIN.saturating_add((-2).days()), + /// PrimitiveDateTime::MIN + /// ); + /// + /// assert_eq!( + /// PrimitiveDateTime::MAX.saturating_add(2.days()), + /// PrimitiveDateTime::MAX + /// ); + /// + /// assert_eq!( + /// datetime!(2019-11-25 15:30).saturating_add(27.hours()), + /// datetime!(2019-11-26 18:30) + /// ); + /// ``` + pub const fn saturating_add(self, duration: Duration) -> Self { + if let Some(datetime) = self.checked_add(duration) { + datetime + } else if duration.is_negative() { + Self::MIN + } else { + Self::MAX + } + } + + /// Computes `self - duration`, saturating value on overflow. + /// + /// ``` + /// # use time::{PrimitiveDateTime, ext::NumericalDuration}; + /// # use time_macros::datetime; + /// assert_eq!( + /// PrimitiveDateTime::MIN.saturating_sub(2.days()), + /// PrimitiveDateTime::MIN + /// ); + /// + /// assert_eq!( + /// PrimitiveDateTime::MAX.saturating_sub((-2).days()), + /// PrimitiveDateTime::MAX + /// ); + /// + /// assert_eq!( + /// datetime!(2019-11-25 15:30).saturating_sub(27.hours()), + /// datetime!(2019-11-24 12:30) + /// ); + /// ``` + pub const fn saturating_sub(self, duration: Duration) -> Self { + if let Some(datetime) = self.checked_sub(duration) { + datetime + } else if duration.is_negative() { + Self::MAX + } else { + Self::MIN + } + } +} + +/// Methods that replace part of the `PrimitiveDateTime`. +impl PrimitiveDateTime { + /// Replace the time, preserving the date. + /// + /// ```rust + /// # use time_macros::{datetime, time}; + /// assert_eq!( + /// datetime!(2020-01-01 17:00).replace_time(time!(5:00)), + /// datetime!(2020-01-01 5:00) + /// ); + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_time(self, time: Time) -> Self { + Self { + date: 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`."] + 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`."] + pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { + 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`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: const_try!(self.date.replace_ordinal(ordinal)), + time: self.time, + }) + } + + /// Replace the clock hour. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_hour(7), + /// Ok(datetime!(2022-02-18 07:02:03.004_005_006)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: self.date, + time: const_try!(self.time.replace_hour(hour)), + }) + } + + /// Replace the minutes within the hour. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_minute(7), + /// Ok(datetime!(2022-02-18 01:07:03.004_005_006)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_minute(self, minute: u8) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: self.date, + time: const_try!(self.time.replace_minute(minute)), + }) + } + + /// Replace the seconds within the minute. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_second(7), + /// Ok(datetime!(2022-02-18 01:02:07.004_005_006)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_second(self, second: u8) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: self.date, + time: const_try!(self.time.replace_second(second)), + }) + } + + /// Replace the milliseconds within the second. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_millisecond(7), + /// Ok(datetime!(2022-02-18 01:02:03.007)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_millisecond(1_000).is_err()); // 1_000 isn't a valid millisecond + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_millisecond( + self, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: self.date, + time: const_try!(self.time.replace_millisecond(millisecond)), + }) + } + + /// Replace the microseconds within the second. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_microsecond(7_008), + /// Ok(datetime!(2022-02-18 01:02:03.007_008)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_microsecond(1_000_000).is_err()); // 1_000_000 isn't a valid microsecond + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_microsecond( + self, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: self.date, + time: const_try!(self.time.replace_microsecond(microsecond)), + }) + } + + /// Replace the nanoseconds within the second. + /// + /// ```rust + /// # use time_macros::datetime; + /// assert_eq!( + /// datetime!(2022-02-18 01:02:03.004_005_006).replace_nanosecond(7_008_009), + /// Ok(datetime!(2022-02-18 01:02:03.007_008_009)) + /// ); + /// assert!(datetime!(2022-02-18 01:02:03.004_005_006).replace_nanosecond(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond + /// ``` + #[must_use = "This method does not mutate the original `PrimitiveDateTime`."] + pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { + Ok(Self { + date: 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). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into(output, Some(self.date), Some(self.time), None) + } + + /// Format the `PrimitiveDateTime` using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::format_description; + /// # 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>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(Some(self.date), Some(self.time), None) + } +} + +#[cfg(feature = "parsing")] +impl PrimitiveDateTime { + /// Parse a `PrimitiveDateTime` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::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>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_primitive_date_time(input.as_bytes()) + } +} + +impl SmartDisplay for PrimitiveDateTime { + type Metadata = (); + + fn metadata(&self, _: FormatterOptions) -> Metadata<Self> { + let width = smart_display::padded_width_of!(self.date, " ", self.time); + Metadata::new(width, self, ()) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> fmt::Result { + f.pad_with_width( + metadata.unpadded_width(), + format_args!("{} {}", self.date, self.time), + ) + } +} + +impl fmt::Display for PrimitiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for PrimitiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Add<Duration> for PrimitiveDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: Duration) -> Self::Output { + self.checked_add(duration) + .expect("resulting value is out of range") + } +} + +impl Add<StdDuration> for PrimitiveDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + 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<Duration> for PrimitiveDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, duration: Duration) { + *self = *self + duration; + } +} + +impl AddAssign<StdDuration> for PrimitiveDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, duration: StdDuration) { + *self = *self + duration; + } +} + +impl Sub<Duration> for PrimitiveDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: Duration) -> Self::Output { + self.checked_sub(duration) + .expect("resulting value is out of range") + } +} + +impl Sub<StdDuration> for PrimitiveDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + 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<Duration> for PrimitiveDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub_assign(&mut self, duration: Duration) { + *self = *self - duration; + } +} + +impl SubAssign<StdDuration> for PrimitiveDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + 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. + fn sub(self, rhs: Self) -> Self::Output { + (self.date - rhs.date) + (self.time - rhs.time) + } +} diff --git a/vendor/time/src/quickcheck.rs b/vendor/time/src/quickcheck.rs new file mode 100644 index 00000000..6336ec25 --- /dev/null +++ b/vendor/time/src/quickcheck.rs @@ -0,0 +1,230 @@ +//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait. +//! +//! This enables users to write tests such as this, and have test values provided automatically: +//! +//! ```ignore +//! # #![allow(dead_code)] +//! use quickcheck::quickcheck; +//! use time::Date; +//! +//! struct DateRange { +//! from: Date, +//! to: Date, +//! } +//! +//! impl DateRange { +//! fn new(from: Date, to: Date) -> Result<Self, ()> { +//! Ok(DateRange { from, to }) +//! } +//! } +//! +//! quickcheck! { +//! fn date_range_is_well_defined(from: Date, to: Date) -> bool { +//! let r = DateRange::new(from, to); +//! if from <= to { +//! r.is_ok() +//! } else { +//! r.is_err() +//! } +//! } +//! } +//! ``` +//! +//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in +//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple +//! anyway. + +use alloc::boxed::Box; + +use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen}; + +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 { + 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() + )) + } + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.to_ordinal_date() + .shrink() + .flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)), + ) + } +} + +impl Arbitrary for Duration { + fn arbitrary(g: &mut Gen) -> Self { + Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.subsec_nanoseconds_ranged(), self.whole_seconds()) + .shrink() + .map(|(mut nanoseconds, seconds)| { + // Coerce the sign if necessary. + if (seconds > 0 && nanoseconds.get() < 0) + || (seconds < 0 && nanoseconds.get() > 0) + { + nanoseconds = nanoseconds.neg(); + } + + Self::new_ranged_unchecked(seconds, nanoseconds) + }), + ) + } +} + +impl Arbitrary for Time { + fn arbitrary(g: &mut Gen) -> Self { + Self::from_hms_nanos_ranged( + <_>::arbitrary(g), + <_>::arbitrary(g), + <_>::arbitrary(g), + <_>::arbitrary(g), + ) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.as_hms_nano_ranged() + .shrink() + .map(|(hour, minute, second, nanosecond)| { + Self::from_hms_nanos_ranged(hour, minute, second, nanosecond) + }), + ) + } +} + +impl Arbitrary for PrimitiveDateTime { + fn arbitrary(g: &mut Gen) -> Self { + Self::new(<_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.date(), self.time()) + .shrink() + .map(|(date, time)| Self::new(date, time)), + ) + } +} + +impl Arbitrary for UtcOffset { + fn arbitrary(g: &mut Gen) -> Self { + Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.as_hms_ranged() + .shrink() + .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)), + ) + } +} + +impl Arbitrary for OffsetDateTime { + fn arbitrary(g: &mut Gen) -> Self { + Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.date(), self.time(), self.offset()) + .shrink() + .map(|(date, time, offset)| Self::new_in_offset(date, time, offset)), + ) + } +} + +impl Arbitrary for UtcDateTime { + fn arbitrary(g: &mut Gen) -> Self { + Self::new(<_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.date(), self.time()) + .shrink() + .map(|(date, time)| Self::new(date, time)), + ) + } +} + +impl Arbitrary for Weekday { + fn arbitrary(g: &mut Gen) -> Self { + use Weekday::*; + match arbitrary_between!(u8; g, 0, 6) { + 0 => Monday, + 1 => Tuesday, + 2 => Wednesday, + 3 => Thursday, + 4 => Friday, + 5 => Saturday, + val => { + debug_assert!(val == 6); + Sunday + } + } + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + match self { + Self::Monday => empty_shrinker(), + _ => single_shrinker(self.previous()), + } + } +} + +impl Arbitrary for Month { + fn arbitrary(g: &mut Gen) -> Self { + use Month::*; + match arbitrary_between!(u8; g, 1, 12) { + 1 => January, + 2 => February, + 3 => March, + 4 => April, + 5 => May, + 6 => June, + 7 => July, + 8 => August, + 9 => September, + 10 => October, + 11 => November, + val => { + debug_assert!(val == 12); + December + } + } + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + match self { + Self::January => empty_shrinker(), + _ => single_shrinker(self.previous()), + } + } +} diff --git a/vendor/time/src/rand.rs b/vendor/time/src/rand.rs new file mode 100644 index 00000000..8e4098cb --- /dev/null +++ b/vendor/time/src/rand.rs @@ -0,0 +1,90 @@ +//! Implementation of [`Distribution`] for various structs. + +use rand::distributions::{Distribution, Standard}; +use rand::Rng; + +use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; + +impl Distribution<Time> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Time { + Time::from_hms_nanos_ranged(rng.r#gen(), rng.r#gen(), rng.r#gen(), rng.r#gen()) + } +} + +impl Distribution<Date> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Date { + // Safety: The Julian day number is in range. + unsafe { + Date::from_julian_day_unchecked( + rng.gen_range(Date::MIN.to_julian_day()..=Date::MAX.to_julian_day()), + ) + } + } +} + +impl Distribution<UtcOffset> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UtcOffset { + UtcOffset::from_hms_ranged(rng.r#gen(), rng.r#gen(), rng.r#gen()) + } +} + +impl Distribution<PrimitiveDateTime> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> PrimitiveDateTime { + PrimitiveDateTime::new(Self.sample(rng), Self.sample(rng)) + } +} + +impl Distribution<OffsetDateTime> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> OffsetDateTime { + let date_time: PrimitiveDateTime = Self.sample(rng); + date_time.assume_offset(Self.sample(rng)) + } +} + +impl Distribution<Duration> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration { + Duration::new_ranged(rng.r#gen(), rng.r#gen()) + } +} + +impl Distribution<Weekday> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Weekday { + use Weekday::*; + + match rng.gen_range(0u8..7) { + 0 => Monday, + 1 => Tuesday, + 2 => Wednesday, + 3 => Thursday, + 4 => Friday, + 5 => Saturday, + val => { + debug_assert!(val == 6); + Sunday + } + } + } +} + +impl Distribution<Month> for Standard { + fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Month { + use Month::*; + match rng.gen_range(1u8..=12) { + 1 => January, + 2 => February, + 3 => March, + 4 => April, + 5 => May, + 6 => June, + 7 => July, + 8 => August, + 9 => September, + 10 => October, + 11 => November, + val => { + debug_assert!(val == 12); + December + } + } + } +} diff --git a/vendor/time/src/serde/iso8601.rs b/vendor/time/src/serde/iso8601.rs new file mode 100644 index 00000000..f08cf511 --- /dev/null +++ b/vendor/time/src/serde/iso8601.rs @@ -0,0 +1,76 @@ +//! Use the well-known [ISO 8601 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::iso8601::{Config, EncodedConfig}; +use crate::format_description::well_known::Iso8601; +use crate::OffsetDateTime; + +/// The configuration of ISO 8601 used for serde implementations. +pub(crate) const SERDE_CONFIG: EncodedConfig = + Config::DEFAULT.set_year_is_six_digits(true).encode(); + +/// Serialize an [`OffsetDateTime`] using the well-known ISO 8601 format. +#[cfg(feature = "formatting")] +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime + .format(&Iso8601::<SERDE_CONFIG>) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its ISO 8601 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + deserializer.deserialize_str(Visitor::<Iso8601<SERDE_CONFIG>>(PhantomData)) +} + +/// Use the well-known ISO 8601 format when serializing and deserializing an +/// [`Option<OffsetDateTime>`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [ISO 8601 format]: https://www.iso.org/iso-8601-date-and-time-format.html +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known ISO 8601 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Iso8601::<SERDE_CONFIG>)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its ISO 8601 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Iso8601<SERDE_CONFIG>>>(PhantomData)) + } +} diff --git a/vendor/time/src/serde/mod.rs b/vendor/time/src/serde/mod.rs new file mode 100644 index 00000000..72b43721 --- /dev/null +++ b/vendor/time/src/serde/mod.rs @@ -0,0 +1,535 @@ +//! Differential formats for serde. +// This also includes the serde implementations for all types. This doesn't need to be externally +// documented, though. + +// Types with guaranteed stable serde representations. Strings are avoided to allow for optimal +// representations in various binary forms. + +/// Consume the next item in a sequence. +macro_rules! item { + ($seq:expr, $name:literal) => { + $seq.next_element()? + .ok_or_else(|| <A::Error as serde::de::Error>::custom(concat!("expected ", $name))) + }; +} + +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod iso8601; +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod rfc2822; +#[cfg(any(feature = "formatting", feature = "parsing"))] +pub mod rfc3339; +pub mod timestamp; +mod visitor; + +#[cfg(feature = "serde-human-readable")] +use alloc::string::ToString; +use core::marker::PhantomData; + +#[cfg(feature = "serde-human-readable")] +use serde::ser::Error as _; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +/// Generate a custom serializer and deserializer from a format string or an existing format. +/// +/// The syntax accepted by this macro is the same as [`format_description::parse()`], which can +/// be found in [the book](https://time-rs.github.io/book/api/format-description.html). +/// +/// # Usage +/// +/// Invoked as `serde::format_description!(mod_name, Date, FORMAT)` where `FORMAT` is either a +/// `"<format string>"` or something that implements +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "[`Formattable`](crate::formatting::Formattable) and \ + [`Parsable`](crate::parsing::Parsable)." +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "[`Formattable`](crate::formatting::Formattable)." +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "[`Parsable`](crate::parsing::Parsable)." +)] +/// This puts a module named `mod_name` in the current scope that can be used to format `Date` +/// structs. A submodule (`mod_name::option`) is also generated for `Option<Date>`. Both +/// modules are only visible in the current scope by default. To increase visibility, you can +/// specify `pub`, `pub(crate)`, or similar before the module name: +/// `serde::format_description!(pub mod_name, Date, FORMAT)`. +/// +/// The returned `Option` will contain a deserialized value if present and `None` if the field +/// is present but the value is `null` (or the equivalent in other formats). To return `None` +/// when the field is not present, you should use `#[serde(default)]` on the field. +/// +/// # Examples +/// +/// Using a format string: +/// +/// ```rust,no_run +/// # use time::OffsetDateTime; +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "use ::serde::{Serialize, Deserialize};" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "use ::serde::Serialize;" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "use ::serde::Deserialize;" +)] +/// use time::serde; +/// +/// // Makes a module `mod my_format { ... }`. +/// serde::format_description!(my_format, OffsetDateTime, "hour=[hour], minute=[minute]"); +/// +/// # #[allow(dead_code)] +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "#[derive(Serialize, Deserialize)]" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "#[derive(Serialize)]" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "#[derive(Deserialize)]" +)] +/// struct SerializesWithCustom { +/// #[serde(with = "my_format")] +/// dt: OffsetDateTime, +/// #[serde(with = "my_format::option")] +/// maybe_dt: Option<OffsetDateTime>, +/// } +/// ``` +/// +/// Define the format separately to be used in multiple places: +/// ```rust,no_run +/// # use time::OffsetDateTime; +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "use ::serde::{Serialize, Deserialize};" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "use ::serde::Serialize;" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "use ::serde::Deserialize;" +)] +/// use time::serde; +/// use time::format_description::BorrowedFormatItem; +/// +/// const DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = time::macros::format_description!( +/// "hour=[hour], minute=[minute]" +/// ); +/// +/// // Makes a module `mod my_format { ... }`. +/// serde::format_description!(my_format, OffsetDateTime, DATE_TIME_FORMAT); +/// +/// # #[allow(dead_code)] +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "#[derive(Serialize, Deserialize)]" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "#[derive(Serialize)]" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "#[derive(Deserialize)]" +)] +/// struct SerializesWithCustom { +/// #[serde(with = "my_format")] +/// dt: OffsetDateTime, +/// #[serde(with = "my_format::option")] +/// maybe_dt: Option<OffsetDateTime>, +/// } +/// +/// fn main() { +/// # #[allow(unused_variables)] +/// let str_ts = OffsetDateTime::now_utc().format(DATE_TIME_FORMAT).unwrap(); +/// } +/// ``` +/// +/// Customize the configuration of ISO 8601 formatting/parsing: +/// ```rust,no_run +/// # use time::OffsetDateTime; +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "use ::serde::{Serialize, Deserialize};" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "use ::serde::Serialize;" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "use ::serde::Deserialize;" +)] +/// use time::serde; +/// use time::format_description::well_known::{iso8601, Iso8601}; +/// +/// # #[allow(dead_code)] +/// const CONFIG: iso8601::EncodedConfig = iso8601::Config::DEFAULT +/// .set_year_is_six_digits(false) +/// .encode(); +/// # #[allow(dead_code)] +/// const FORMAT: Iso8601<CONFIG> = Iso8601::<CONFIG>; +/// +/// // Makes a module `mod my_format { ... }`. +/// serde::format_description!(my_format, OffsetDateTime, FORMAT); +/// +/// # #[allow(dead_code)] +#[cfg_attr( + all(feature = "formatting", feature = "parsing"), + doc = "#[derive(Serialize, Deserialize)]" +)] +#[cfg_attr( + all(feature = "formatting", not(feature = "parsing")), + doc = "#[derive(Serialize)]" +)] +#[cfg_attr( + all(not(feature = "formatting"), feature = "parsing"), + doc = "#[derive(Deserialize)]" +)] +/// struct SerializesWithCustom { +/// #[serde(with = "my_format")] +/// dt: OffsetDateTime, +/// #[serde(with = "my_format::option")] +/// maybe_dt: Option<OffsetDateTime>, +/// } +/// # fn main() {} +/// ``` +/// +/// [`format_description::parse()`]: crate::format_description::parse() +#[cfg(all(feature = "macros", any(feature = "formatting", feature = "parsing")))] +pub use time_macros::serde_format_description as format_description; + +use self::visitor::Visitor; +#[cfg(feature = "parsing")] +use crate::format_description::{modifier, BorrowedFormatItem, Component}; +use crate::{ + Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, +}; + +/// The format used when serializing and deserializing a human-readable `Date`. +#[cfg(feature = "parsing")] +const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Component(Component::Year(modifier::Year::default())), + BorrowedFormatItem::Literal(b"-"), + BorrowedFormatItem::Component(Component::Month(modifier::Month::default())), + BorrowedFormatItem::Literal(b"-"), + BorrowedFormatItem::Component(Component::Day(modifier::Day::default())), +]; + +impl Serialize for Date { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&DATE_FORMAT) else { + return Err(S::Error::custom("failed formatting `Date`")); + }; + return serializer.serialize_str(&s); + } + + (self.year(), self.ordinal()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Date { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData)) + } + } +} + +impl Serialize for Duration { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + return serializer.collect_str(&format_args!( + "{}{}.{:>09}", + if self.is_negative() { "-" } else { "" }, + self.whole_seconds().unsigned_abs(), + self.subsec_nanoseconds().abs(), + )); + } + + (self.whole_seconds(), self.subsec_nanoseconds()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Duration { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(2, Visitor::<Self>(PhantomData)) + } + } +} + +/// The format used when serializing and deserializing a human-readable `OffsetDateTime`. +#[cfg(feature = "parsing")] +const OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Compound(DATE_FORMAT), + BorrowedFormatItem::Literal(b" "), + BorrowedFormatItem::Compound(TIME_FORMAT), + BorrowedFormatItem::Literal(b" "), + BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT), +]; + +impl Serialize for OffsetDateTime { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&OFFSET_DATE_TIME_FORMAT) else { + return Err(S::Error::custom("failed formatting `OffsetDateTime`")); + }; + return serializer.serialize_str(&s); + } + + ( + self.year(), + self.ordinal(), + self.hour(), + self.minute(), + self.second(), + self.nanosecond(), + self.offset().whole_hours(), + self.offset().minutes_past_hour(), + self.offset().seconds_past_minute(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for OffsetDateTime { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(9, Visitor::<Self>(PhantomData)) + } + } +} + +/// The format used when serializing and deserializing a human-readable `PrimitiveDateTime`. +#[cfg(feature = "parsing")] +const PRIMITIVE_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Compound(DATE_FORMAT), + BorrowedFormatItem::Literal(b" "), + BorrowedFormatItem::Compound(TIME_FORMAT), +]; + +impl Serialize for PrimitiveDateTime { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else { + return Err(S::Error::custom("failed formatting `PrimitiveDateTime`")); + }; + return serializer.serialize_str(&s); + } + + ( + self.year(), + self.ordinal(), + self.hour(), + self.minute(), + self.second(), + self.nanosecond(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for PrimitiveDateTime { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData)) + } + } +} + +/// The format used when serializing and deserializing a human-readable `UtcDateTime`. +#[cfg(feature = "parsing")] +const UTC_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = PRIMITIVE_DATE_TIME_FORMAT; + +impl Serialize for UtcDateTime { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&PRIMITIVE_DATE_TIME_FORMAT) else { + return Err(S::Error::custom("failed formatting `UtcDateTime`")); + }; + return serializer.serialize_str(&s); + } + + ( + self.year(), + self.ordinal(), + self.hour(), + self.minute(), + self.second(), + self.nanosecond(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for UtcDateTime { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(6, Visitor::<Self>(PhantomData)) + } + } +} + +/// The format used when serializing and deserializing a human-readable `Time`. +#[cfg(feature = "parsing")] +const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())), + BorrowedFormatItem::Literal(b":"), + BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())), + BorrowedFormatItem::Literal(b":"), + BorrowedFormatItem::Component(Component::Second(modifier::Second::default())), + BorrowedFormatItem::Literal(b"."), + BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())), +]; + +impl Serialize for Time { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&TIME_FORMAT) else { + return Err(S::Error::custom("failed formatting `Time`")); + }; + return serializer.serialize_str(&s); + } + + (self.hour(), self.minute(), self.second(), self.nanosecond()).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Time { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(4, Visitor::<Self>(PhantomData)) + } + } +} + +// FIXME: turn these constants into `const { ... }` blocks once we can depend on Rust 1.79. +#[cfg(feature = "parsing")] +const UTC_OFFSET_HOUR: modifier::OffsetHour = { + let mut m = modifier::OffsetHour::default(); + m.sign_is_mandatory = true; + m +}; +#[cfg(feature = "parsing")] +const UTC_OFFSET_MINUTE: modifier::OffsetMinute = modifier::OffsetMinute::default(); +#[cfg(feature = "parsing")] +const UTC_OFFSET_SECOND: modifier::OffsetSecond = modifier::OffsetSecond::default(); +/// The format used when serializing and deserializing a human-readable `UtcOffset`. +#[cfg(feature = "parsing")] +const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[ + BorrowedFormatItem::Component(Component::OffsetHour(UTC_OFFSET_HOUR)), + BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[ + BorrowedFormatItem::Literal(b":"), + BorrowedFormatItem::Component(Component::OffsetMinute(UTC_OFFSET_MINUTE)), + BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[ + BorrowedFormatItem::Literal(b":"), + BorrowedFormatItem::Component(Component::OffsetSecond(UTC_OFFSET_SECOND)), + ])), + ])), +]; + +impl Serialize for UtcOffset { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + let Ok(s) = self.format(&UTC_OFFSET_FORMAT) else { + return Err(S::Error::custom("failed formatting `UtcOffset`")); + }; + return serializer.serialize_str(&s); + } + + ( + self.whole_hours(), + self.minutes_past_hour(), + self.seconds_past_minute(), + ) + .serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for UtcOffset { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_tuple(3, Visitor::<Self>(PhantomData)) + } + } +} + +impl Serialize for Weekday { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + #[cfg(not(feature = "std"))] + use alloc::string::ToString; + return self.to_string().serialize(serializer); + } + + self.number_from_monday().serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Weekday { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::<Self>(PhantomData)) + } + } +} + +impl Serialize for Month { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + #[cfg(feature = "serde-human-readable")] + if serializer.is_human_readable() { + #[cfg(not(feature = "std"))] + use alloc::string::String; + return self.to_string().serialize(serializer); + } + + u8::from(*self).serialize(serializer) + } +} + +impl<'a> Deserialize<'a> for Month { + fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> { + if cfg!(feature = "serde-human-readable") && deserializer.is_human_readable() { + deserializer.deserialize_any(Visitor::<Self>(PhantomData)) + } else { + deserializer.deserialize_u8(Visitor::<Self>(PhantomData)) + } + } +} diff --git a/vendor/time/src/serde/rfc2822.rs b/vendor/time/src/serde/rfc2822.rs new file mode 100644 index 00000000..c6716d58 --- /dev/null +++ b/vendor/time/src/serde/rfc2822.rs @@ -0,0 +1,71 @@ +//! Use the well-known [RFC2822 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3 +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::Rfc2822; +use crate::OffsetDateTime; + +/// Serialize an [`OffsetDateTime`] using the well-known RFC2822 format. +#[cfg(feature = "formatting")] +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime + .format(&Rfc2822) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its RFC2822 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + deserializer.deserialize_str(Visitor::<Rfc2822>(PhantomData)) +} + +/// Use the well-known [RFC2822 format] when serializing and deserializing an +/// [`Option<OffsetDateTime>`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [RFC2822 format]: https://tools.ietf.org/html/rfc2822#section-3.3 +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC2822 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Rfc2822)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its RFC2822 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Rfc2822>>(PhantomData)) + } +} diff --git a/vendor/time/src/serde/rfc3339.rs b/vendor/time/src/serde/rfc3339.rs new file mode 100644 index 00000000..4cccd635 --- /dev/null +++ b/vendor/time/src/serde/rfc3339.rs @@ -0,0 +1,71 @@ +//! Use the well-known [RFC3339 format] when serializing and deserializing an [`OffsetDateTime`]. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6 +//! [with]: https://serde.rs/field-attrs.html#with + +#[cfg(feature = "parsing")] +use core::marker::PhantomData; + +#[cfg(feature = "formatting")] +use serde::ser::Error as _; +#[cfg(feature = "parsing")] +use serde::Deserializer; +#[cfg(feature = "formatting")] +use serde::{Serialize, Serializer}; + +#[cfg(feature = "parsing")] +use super::Visitor; +use crate::format_description::well_known::Rfc3339; +use crate::OffsetDateTime; + +/// Serialize an [`OffsetDateTime`] using the well-known RFC3339 format. +#[cfg(feature = "formatting")] +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime + .format(&Rfc3339) + .map_err(S::Error::custom)? + .serialize(serializer) +} + +/// Deserialize an [`OffsetDateTime`] from its RFC3339 representation. +#[cfg(feature = "parsing")] +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + deserializer.deserialize_str(Visitor::<Rfc3339>(PhantomData)) +} + +/// Use the well-known [RFC3339 format] when serializing and deserializing an +/// [`Option<OffsetDateTime>`]. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// [RFC3339 format]: https://tools.ietf.org/html/rfc3339#section-5.6 +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + use super::*; + + /// Serialize an [`Option<OffsetDateTime>`] using the well-known RFC3339 format. + #[cfg(feature = "formatting")] + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|odt| odt.format(&Rfc3339)) + .transpose() + .map_err(S::Error::custom)? + .serialize(serializer) + } + + /// Deserialize an [`Option<OffsetDateTime>`] from its RFC3339 representation. + #[cfg(feature = "parsing")] + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer.deserialize_option(Visitor::<Option<Rfc3339>>(PhantomData)) + } +} diff --git a/vendor/time/src/serde/timestamp/microseconds.rs b/vendor/time/src/serde/timestamp/microseconds.rs new file mode 100644 index 00000000..65c603ec --- /dev/null +++ b/vendor/time/src/serde/timestamp/microseconds.rs @@ -0,0 +1,63 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with microseconds for +//! the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp with microseconds +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + let timestamp = datetime.unix_timestamp_nanos() / 1_000; + timestamp.serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp with microseconds +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + let value: i128 = <_>::deserialize(deserializer)?; + OffsetDateTime::from_unix_timestamp_nanos(value * 1_000) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with microseconds +/// for the purposes of serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with microseconds + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with microseconds + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + Option::deserialize(deserializer)? + .map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000)) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/vendor/time/src/serde/timestamp/milliseconds.rs b/vendor/time/src/serde/timestamp/milliseconds.rs new file mode 100644 index 00000000..e571b6c9 --- /dev/null +++ b/vendor/time/src/serde/timestamp/milliseconds.rs @@ -0,0 +1,63 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with milliseconds for +//! the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp with milliseconds +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + let timestamp = datetime.unix_timestamp_nanos() / 1_000_000; + timestamp.serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp with milliseconds +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + let value: i128 = <_>::deserialize(deserializer)?; + OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with milliseconds +/// for the purposes of serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with milliseconds + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|timestamp| timestamp.unix_timestamp_nanos() / 1_000_000) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with milliseconds + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + Option::deserialize(deserializer)? + .map(|value: i128| OffsetDateTime::from_unix_timestamp_nanos(value * 1_000_000)) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/vendor/time/src/serde/timestamp/milliseconds_i64.rs b/vendor/time/src/serde/timestamp/milliseconds_i64.rs new file mode 100644 index 00000000..59f44644 --- /dev/null +++ b/vendor/time/src/serde/timestamp/milliseconds_i64.rs @@ -0,0 +1,66 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with milliseconds for +//! the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +use num_conv::prelude::*; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp with milliseconds +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + let timestamp = (datetime.unix_timestamp_nanos() / 1_000_000).truncate::<i64>(); + timestamp.serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp with milliseconds +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + let value: i64 = <_>::deserialize(deserializer)?; + OffsetDateTime::from_unix_timestamp_nanos(value.extend::<i128>() * 1_000_000) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with milliseconds +/// for the purposes of serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with milliseconds + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(|timestamp| (timestamp.unix_timestamp_nanos() / 1_000_000).truncate::<i64>()) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with milliseconds + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + Option::deserialize(deserializer)? + .map(|value: i64| { + OffsetDateTime::from_unix_timestamp_nanos(value.extend::<i128>() * 1_000_000) + }) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/vendor/time/src/serde/timestamp/mod.rs b/vendor/time/src/serde/timestamp/mod.rs new file mode 100644 index 00000000..d27836b6 --- /dev/null +++ b/vendor/time/src/serde/timestamp/mod.rs @@ -0,0 +1,65 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] for the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +pub mod microseconds; +pub mod milliseconds; +pub mod milliseconds_i64; +pub mod nanoseconds; + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime.unix_timestamp().serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + OffsetDateTime::from_unix_timestamp(<_>::deserialize(deserializer)?) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] for the purposes of +/// serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(OffsetDateTime::unix_timestamp) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + Option::deserialize(deserializer)? + .map(OffsetDateTime::from_unix_timestamp) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/vendor/time/src/serde/timestamp/nanoseconds.rs b/vendor/time/src/serde/timestamp/nanoseconds.rs new file mode 100644 index 00000000..c71d1e7c --- /dev/null +++ b/vendor/time/src/serde/timestamp/nanoseconds.rs @@ -0,0 +1,61 @@ +//! Treat an [`OffsetDateTime`] as a [Unix timestamp] with nanoseconds for +//! the purposes of serde. +//! +//! Use this module in combination with serde's [`#[with]`][with] attribute. +//! +//! When deserializing, the offset is assumed to be UTC. +//! +//! [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +//! [with]: https://serde.rs/field-attrs.html#with + +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use crate::OffsetDateTime; + +/// Serialize an `OffsetDateTime` as its Unix timestamp with nanoseconds +pub fn serialize<S: Serializer>( + datetime: &OffsetDateTime, + serializer: S, +) -> Result<S::Ok, S::Error> { + datetime.unix_timestamp_nanos().serialize(serializer) +} + +/// Deserialize an `OffsetDateTime` from its Unix timestamp with nanoseconds +pub fn deserialize<'a, D: Deserializer<'a>>(deserializer: D) -> Result<OffsetDateTime, D::Error> { + OffsetDateTime::from_unix_timestamp_nanos(<_>::deserialize(deserializer)?) + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) +} + +/// Treat an `Option<OffsetDateTime>` as a [Unix timestamp] with nanoseconds +/// for the purposes of serde. +/// +/// Use this module in combination with serde's [`#[with]`][with] attribute. +/// +/// When deserializing, the offset is assumed to be UTC. +/// +/// [Unix timestamp]: https://en.wikipedia.org/wiki/Unix_time +/// [with]: https://serde.rs/field-attrs.html#with +pub mod option { + #[allow(clippy::wildcard_imports)] + use super::*; + + /// Serialize an `Option<OffsetDateTime>` as its Unix timestamp with nanoseconds + pub fn serialize<S: Serializer>( + option: &Option<OffsetDateTime>, + serializer: S, + ) -> Result<S::Ok, S::Error> { + option + .map(OffsetDateTime::unix_timestamp_nanos) + .serialize(serializer) + } + + /// Deserialize an `Option<OffsetDateTime>` from its Unix timestamp with nanoseconds + pub fn deserialize<'a, D: Deserializer<'a>>( + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + Option::deserialize(deserializer)? + .map(OffsetDateTime::from_unix_timestamp_nanos) + .transpose() + .map_err(|err| de::Error::invalid_value(de::Unexpected::Signed(err.value), &err)) + } +} diff --git a/vendor/time/src/serde/visitor.rs b/vendor/time/src/serde/visitor.rs new file mode 100644 index 00000000..b7034020 --- /dev/null +++ b/vendor/time/src/serde/visitor.rs @@ -0,0 +1,355 @@ +//! Serde visitor for various types. + +use core::fmt; +use core::marker::PhantomData; + +use serde::de; +#[cfg(feature = "parsing")] +use serde::Deserializer; + +#[cfg(feature = "parsing")] +use super::{ + DATE_FORMAT, OFFSET_DATE_TIME_FORMAT, PRIMITIVE_DATE_TIME_FORMAT, TIME_FORMAT, + UTC_DATE_TIME_FORMAT, UTC_OFFSET_FORMAT, +}; +use crate::error::ComponentRange; +#[cfg(feature = "parsing")] +use crate::format_description::well_known::*; +use crate::{ + Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday, +}; + +/// A serde visitor for various types. +pub(super) struct Visitor<T: ?Sized>(pub(super) PhantomData<T>); + +impl<'a> de::Visitor<'a> for Visitor<Date> { + type Value = Date; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Date`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<Date, E> { + Date::parse(value, &DATE_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Date, A::Error> { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + Date::from_ordinal_date(year, ordinal).map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<Duration> { + type Value = Duration; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Duration`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Duration, E> { + let (seconds, nanoseconds) = value.split_once('.').ok_or_else(|| { + de::Error::invalid_value(de::Unexpected::Str(value), &"a decimal point") + })?; + + let seconds = seconds + .parse() + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(seconds), &"seconds"))?; + let mut nanoseconds = nanoseconds.parse().map_err(|_| { + de::Error::invalid_value(de::Unexpected::Str(nanoseconds), &"nanoseconds") + })?; + + if seconds < 0 + // make sure sign does not disappear when seconds == 0 + || (seconds == 0 && value.starts_with("-")) + { + nanoseconds *= -1; + } + + Ok(Duration::new(seconds, nanoseconds)) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Duration, A::Error> { + let seconds = item!(seq, "seconds")?; + let nanoseconds = item!(seq, "nanoseconds")?; + Ok(Duration::new(seconds, nanoseconds)) + } +} + +impl<'a> de::Visitor<'a> for Visitor<OffsetDateTime> { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an `OffsetDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> { + OffsetDateTime::parse(value, &OFFSET_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<OffsetDateTime, A::Error> { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + let offset_hours = item!(seq, "offset hours")?; + let offset_minutes = item!(seq, "offset minutes")?; + let offset_seconds = item!(seq, "offset seconds")?; + + Date::from_ordinal_date(year, ordinal) + .and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond)) + .and_then(|datetime| { + UtcOffset::from_hms(offset_hours, offset_minutes, offset_seconds) + .map(|offset| datetime.assume_offset(offset)) + }) + .map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<PrimitiveDateTime> { + type Value = PrimitiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `PrimitiveDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<PrimitiveDateTime, E> { + PrimitiveDateTime::parse(value, &PRIMITIVE_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<PrimitiveDateTime, A::Error> { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + + Date::from_ordinal_date(year, ordinal) + .and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond)) + .map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<UtcDateTime> { + type Value = UtcDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `PrimitiveDateTime`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<UtcDateTime, E> { + UtcDateTime::parse(value, &UTC_DATE_TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<UtcDateTime, A::Error> { + let year = item!(seq, "year")?; + let ordinal = item!(seq, "day of year")?; + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + + Date::from_ordinal_date(year, ordinal) + .and_then(|date| date.with_hms_nano(hour, minute, second, nanosecond)) + .map(UtcDateTime::from_primitive) + .map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<Time> { + type Value = Time; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Time`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<Time, E> { + Time::parse(value, &TIME_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<Time, A::Error> { + let hour = item!(seq, "hour")?; + let minute = item!(seq, "minute")?; + let second = item!(seq, "second")?; + let nanosecond = item!(seq, "nanosecond")?; + + Time::from_hms_nano(hour, minute, second, nanosecond).map_err(ComponentRange::into_de_error) + } +} + +impl<'a> de::Visitor<'a> for Visitor<UtcOffset> { + type Value = UtcOffset; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `UtcOffset`") + } + + #[cfg(feature = "parsing")] + fn visit_str<E: de::Error>(self, value: &str) -> Result<UtcOffset, E> { + UtcOffset::parse(value, &UTC_OFFSET_FORMAT).map_err(E::custom) + } + + fn visit_seq<A: de::SeqAccess<'a>>(self, mut seq: A) -> Result<UtcOffset, A::Error> { + let hours = item!(seq, "offset hours")?; + let mut minutes = 0; + let mut seconds = 0; + + if let Ok(Some(min)) = seq.next_element() { + minutes = min; + if let Ok(Some(sec)) = seq.next_element() { + seconds = sec; + } + }; + + UtcOffset::from_hms(hours, minutes, seconds).map_err(ComponentRange::into_de_error) + } +} + +impl de::Visitor<'_> for Visitor<Weekday> { + type Value = Weekday; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Weekday`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Weekday, E> { + match value { + "Monday" => Ok(Weekday::Monday), + "Tuesday" => Ok(Weekday::Tuesday), + "Wednesday" => Ok(Weekday::Wednesday), + "Thursday" => Ok(Weekday::Thursday), + "Friday" => Ok(Weekday::Friday), + "Saturday" => Ok(Weekday::Saturday), + "Sunday" => Ok(Weekday::Sunday), + _ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Weekday`")), + } + } + + fn visit_u64<E: de::Error>(self, value: u64) -> Result<Weekday, E> { + match value { + 1 => Ok(Weekday::Monday), + 2 => Ok(Weekday::Tuesday), + 3 => Ok(Weekday::Wednesday), + 4 => Ok(Weekday::Thursday), + 5 => Ok(Weekday::Friday), + 6 => Ok(Weekday::Saturday), + 7 => Ok(Weekday::Sunday), + _ => Err(E::invalid_value( + de::Unexpected::Unsigned(value), + &"a value in the range 1..=7", + )), + } + } +} + +impl de::Visitor<'_> for Visitor<Month> { + type Value = Month; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a `Month`") + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<Month, E> { + match value { + "January" => Ok(Month::January), + "February" => Ok(Month::February), + "March" => Ok(Month::March), + "April" => Ok(Month::April), + "May" => Ok(Month::May), + "June" => Ok(Month::June), + "July" => Ok(Month::July), + "August" => Ok(Month::August), + "September" => Ok(Month::September), + "October" => Ok(Month::October), + "November" => Ok(Month::November), + "December" => Ok(Month::December), + _ => Err(E::invalid_value(de::Unexpected::Str(value), &"a `Month`")), + } + } + + fn visit_u64<E: de::Error>(self, value: u64) -> Result<Month, E> { + match value { + 1 => Ok(Month::January), + 2 => Ok(Month::February), + 3 => Ok(Month::March), + 4 => Ok(Month::April), + 5 => Ok(Month::May), + 6 => Ok(Month::June), + 7 => Ok(Month::July), + 8 => Ok(Month::August), + 9 => Ok(Month::September), + 10 => Ok(Month::October), + 11 => Ok(Month::November), + 12 => Ok(Month::December), + _ => Err(E::invalid_value( + de::Unexpected::Unsigned(value), + &"a value in the range 1..=12", + )), + } + } +} + +/// Implement a visitor for a well-known format. +macro_rules! well_known { + ($article:literal, $name:literal, $($ty:tt)+) => { + #[cfg(feature = "parsing")] + impl de::Visitor<'_> for Visitor<$($ty)+> { + type Value = OffsetDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(concat!($article, " ", $name, "-formatted `OffsetDateTime`")) + } + + fn visit_str<E: de::Error>(self, value: &str) -> Result<OffsetDateTime, E> { + OffsetDateTime::parse(value, &$($ty)+).map_err(E::custom) + } + } + + #[cfg(feature = "parsing")] + impl<'a> de::Visitor<'a> for Visitor<Option<$($ty)+>> { + type Value = Option<OffsetDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(concat!( + $article, + " ", + $name, + "-formatted `Option<OffsetDateTime>`" + )) + } + + fn visit_some<D: Deserializer<'a>>( + self, + deserializer: D, + ) -> Result<Option<OffsetDateTime>, D::Error> { + deserializer + .deserialize_any(Visitor::<$($ty)+>(PhantomData)) + .map(Some) + } + + fn visit_none<E: de::Error>(self) -> Result<Option<OffsetDateTime>, E> { + Ok(None) + } + + fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> { + Ok(None) + } + } + }; +} + +well_known!("an", "RFC2822", Rfc2822); +well_known!("an", "RFC3339", Rfc3339); +well_known!( + "an", + "ISO 8601", + Iso8601::<{ super::iso8601::SERDE_CONFIG }> +); diff --git a/vendor/time/src/sys/local_offset_at/imp.rs b/vendor/time/src/sys/local_offset_at/imp.rs new file mode 100644 index 00000000..251fa667 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/imp.rs @@ -0,0 +1,8 @@ +//! A fallback for any OS not covered. + +use crate::{OffsetDateTime, UtcOffset}; + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) fn local_offset_at(_datetime: OffsetDateTime) -> Option<UtcOffset> { + None +} diff --git a/vendor/time/src/sys/local_offset_at/mod.rs b/vendor/time/src/sys/local_offset_at/mod.rs new file mode 100644 index 00000000..04cfffd5 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/mod.rs @@ -0,0 +1,23 @@ +//! A method to obtain the local offset from UTC. + +#![allow(clippy::missing_const_for_fn)] + +#[cfg_attr(target_family = "windows", path = "windows.rs")] +#[cfg_attr(target_family = "unix", path = "unix.rs")] +#[cfg_attr( + all( + target_family = "wasm", + not(any(target_os = "emscripten", target_os = "wasi")), + feature = "wasm-bindgen" + ), + path = "wasm_js.rs" +)] +mod imp; + +use crate::{OffsetDateTime, UtcOffset}; + +/// Attempt to obtain the system's UTC offset. If the offset cannot be determined, `None` is +/// returned. +pub(crate) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + imp::local_offset_at(datetime) +} diff --git a/vendor/time/src/sys/local_offset_at/unix.rs b/vendor/time/src/sys/local_offset_at/unix.rs new file mode 100644 index 00000000..d036a0e0 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/unix.rs @@ -0,0 +1,101 @@ +//! Get the system's UTC offset on Unix. + +use core::mem::MaybeUninit; + +use crate::{OffsetDateTime, UtcOffset}; + +/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error. +fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> { + // The exact type of `timestamp` beforehand can vary, so this conversion is necessary. + #[allow(clippy::useless_conversion)] + let timestamp = timestamp.try_into().ok()?; + + let mut tm = MaybeUninit::uninit(); + + // Safety: We are calling a system API, which mutates the `tm` variable. If a null + // pointer is returned, an error occurred. + let tm_ptr = unsafe { libc::localtime_r(×tamp, tm.as_mut_ptr()) }; + + if tm_ptr.is_null() { + None + } else { + // Safety: The value was initialized, as we no longer have a null pointer. + Some(unsafe { tm.assume_init() }) + } +} + +/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error. +// This is available to any target known to have the `tm_gmtoff` extension. +#[cfg(any( + target_os = "redox", + target_os = "linux", + target_os = "l4re", + target_os = "android", + target_os = "emscripten", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku", +))] +fn tm_to_offset(_unix_timestamp: i64, tm: libc::tm) -> Option<UtcOffset> { + let seconds = tm.tm_gmtoff.try_into().ok()?; + UtcOffset::from_whole_seconds(seconds).ok() +} + +/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error. +/// +/// This method can return an incorrect value, as it only approximates the `tm_gmtoff` field. The +/// reason for this is that daylight saving time does not start on the same date every year, nor are +/// the rules for daylight saving time the same for every year. This implementation assumes 1970 is +/// equivalent to every other year, which is not always the case. +#[cfg(not(any( + target_os = "redox", + target_os = "linux", + target_os = "l4re", + target_os = "android", + target_os = "emscripten", + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + target_os = "haiku", +)))] +fn tm_to_offset(unix_timestamp: i64, tm: libc::tm) -> Option<UtcOffset> { + use crate::Date; + + let mut tm = tm; + if tm.tm_sec == 60 { + // Leap seconds are not currently supported. + tm.tm_sec = 59; + } + + let local_timestamp = + Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1) + .ok()? + .with_hms( + tm.tm_hour.try_into().ok()?, + tm.tm_min.try_into().ok()?, + tm.tm_sec.try_into().ok()?, + ) + .ok()? + .assume_utc() + .unix_timestamp(); + + let diff_secs = (local_timestamp - unix_timestamp).try_into().ok()?; + + UtcOffset::from_whole_seconds(diff_secs).ok() +} + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + let unix_timestamp = datetime.unix_timestamp(); + let tm = timestamp_to_tm(unix_timestamp)?; + tm_to_offset(unix_timestamp, tm) +} diff --git a/vendor/time/src/sys/local_offset_at/wasm_js.rs b/vendor/time/src/sys/local_offset_at/wasm_js.rs new file mode 100644 index 00000000..a985e7c4 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/wasm_js.rs @@ -0,0 +1,16 @@ +use num_conv::prelude::*; + +use crate::convert::*; +use crate::{OffsetDateTime, UtcOffset}; + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + let js_date: js_sys::Date = datetime.into(); + // The number of minutes returned by getTimezoneOffset() is positive if the local time zone + // is behind UTC, and negative if the local time zone is ahead of UTC. For example, + // for UTC+10, -600 will be returned. + let timezone_offset = + (js_date.get_timezone_offset() as i32) * -Minute::per(Hour).cast_signed().extend::<i32>(); + + UtcOffset::from_whole_seconds(timezone_offset).ok() +} diff --git a/vendor/time/src/sys/local_offset_at/windows.rs b/vendor/time/src/sys/local_offset_at/windows.rs new file mode 100644 index 00000000..d1442920 --- /dev/null +++ b/vendor/time/src/sys/local_offset_at/windows.rs @@ -0,0 +1,113 @@ +//! Get the system's UTC offset on Windows. + +use core::mem::MaybeUninit; + +use num_conv::prelude::*; + +use crate::convert::*; +use crate::{OffsetDateTime, UtcOffset}; + +// ffi: WINAPI FILETIME struct +#[repr(C)] +#[allow(non_snake_case, clippy::missing_docs_in_private_items)] +struct FileTime { + dwLowDateTime: u32, + dwHighDateTime: u32, +} + +// ffi: WINAPI SYSTEMTIME struct +#[repr(C)] +#[allow(non_snake_case, clippy::missing_docs_in_private_items)] +struct SystemTime { + wYear: u16, + wMonth: u16, + wDayOfWeek: u16, + wDay: u16, + wHour: u16, + wMinute: u16, + wSecond: u16, + wMilliseconds: u16, +} + +#[link(name = "kernel32")] +extern "system" { + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime + fn SystemTimeToFileTime(lpSystemTime: *const SystemTime, lpFileTime: *mut FileTime) -> i32; + + // https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime + fn SystemTimeToTzSpecificLocalTime( + lpTimeZoneInformation: *const core::ffi::c_void, // We only pass `nullptr` here + lpUniversalTime: *const SystemTime, + lpLocalTime: *mut SystemTime, + ) -> i32; +} + +/// Convert a `SYSTEMTIME` to a `FILETIME`. Returns `None` if any error occurred. +fn systemtime_to_filetime(systime: &SystemTime) -> Option<FileTime> { + let mut ft = MaybeUninit::uninit(); + + // Safety: `SystemTimeToFileTime` is thread-safe. + if 0 == unsafe { SystemTimeToFileTime(systime, ft.as_mut_ptr()) } { + // failed + None + } else { + // Safety: The call succeeded. + Some(unsafe { ft.assume_init() }) + } +} + +/// Convert a `FILETIME` to an `i64`, representing a number of seconds. +fn filetime_to_secs(filetime: &FileTime) -> i64 { + /// FILETIME represents 100-nanosecond intervals + const FT_TO_SECS: u64 = Nanosecond::per(Second) as u64 / 100; + ((filetime.dwHighDateTime.extend::<u64>() << 32 | filetime.dwLowDateTime.extend::<u64>()) + / FT_TO_SECS) as i64 +} + +/// Convert an [`OffsetDateTime`] to a `SYSTEMTIME`. +fn offset_to_systemtime(datetime: OffsetDateTime) -> SystemTime { + let (_, month, day_of_month) = datetime.to_offset(UtcOffset::UTC).date().to_calendar_date(); + SystemTime { + wYear: datetime.year().cast_unsigned().truncate(), + wMonth: u8::from(month).extend(), + wDay: day_of_month.extend(), + wDayOfWeek: 0, // ignored + wHour: datetime.hour().extend(), + wMinute: datetime.minute().extend(), + wSecond: datetime.second().extend(), + wMilliseconds: datetime.millisecond(), + } +} + +/// Obtain the system's UTC offset. +pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> { + // This function falls back to UTC if any system call fails. + let systime_utc = offset_to_systemtime(datetime.to_offset(UtcOffset::UTC)); + + // Safety: `local_time` is only read if it is properly initialized, and + // `SystemTimeToTzSpecificLocalTime` is thread-safe. + let systime_local = unsafe { + let mut local_time = MaybeUninit::uninit(); + + if 0 == SystemTimeToTzSpecificLocalTime( + core::ptr::null(), // use system's current timezone + &systime_utc, + local_time.as_mut_ptr(), + ) { + // call failed + return None; + } else { + local_time.assume_init() + } + }; + + // Convert SYSTEMTIMEs to FILETIMEs so we can perform arithmetic on them. + let ft_system = systemtime_to_filetime(&systime_utc)?; + let ft_local = systemtime_to_filetime(&systime_local)?; + + let diff_secs = (filetime_to_secs(&ft_local) - filetime_to_secs(&ft_system)) + .try_into() + .ok()?; + + UtcOffset::from_whole_seconds(diff_secs).ok() +} diff --git a/vendor/time/src/sys/mod.rs b/vendor/time/src/sys/mod.rs new file mode 100644 index 00000000..76ca93b5 --- /dev/null +++ b/vendor/time/src/sys/mod.rs @@ -0,0 +1,11 @@ +//! Functions with a common interface that rely on system calls. + +#[cfg(feature = "local-offset")] +mod local_offset_at; +#[cfg(feature = "local-offset")] +mod refresh_tz; + +#[cfg(feature = "local-offset")] +pub(crate) use self::local_offset_at::local_offset_at; +#[cfg(feature = "local-offset")] +pub(crate) use self::refresh_tz::{refresh_tz, refresh_tz_unchecked}; diff --git a/vendor/time/src/sys/refresh_tz/imp.rs b/vendor/time/src/sys/refresh_tz/imp.rs new file mode 100644 index 00000000..6f8b1328 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/imp.rs @@ -0,0 +1,9 @@ +//! A fallback for any OS not covered. + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) unsafe fn refresh_tz_unchecked() {} + +#[allow(clippy::missing_docs_in_private_items)] +pub(super) fn refresh_tz() -> Option<()> { + Some(()) +} diff --git a/vendor/time/src/sys/refresh_tz/mod.rs b/vendor/time/src/sys/refresh_tz/mod.rs new file mode 100644 index 00000000..90ddaa99 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/mod.rs @@ -0,0 +1,17 @@ +#[cfg_attr(target_family = "unix", path = "unix.rs")] +mod imp; + +/// Update time zone information from the system. +/// +/// For safety documentation, see [`time::util::refresh_tz`]. +pub(crate) unsafe fn refresh_tz_unchecked() { + // Safety: The caller must uphold the safety requirements. + unsafe { imp::refresh_tz_unchecked() } +} + +/// Attempt to update time zone information from the system. +/// +/// Returns `None` if the call is not known to be sound. +pub(crate) fn refresh_tz() -> Option<()> { + imp::refresh_tz() +} diff --git a/vendor/time/src/sys/refresh_tz/unix.rs b/vendor/time/src/sys/refresh_tz/unix.rs new file mode 100644 index 00000000..6c9813e2 --- /dev/null +++ b/vendor/time/src/sys/refresh_tz/unix.rs @@ -0,0 +1,48 @@ +/// Whether the operating system has a thread-safe environment. This allows bypassing the check for +/// if the process is multi-threaded. +// This is the same value as `cfg!(target_os = "x")`. +// Use byte-strings to work around current limitations of const eval. +const OS_HAS_THREAD_SAFE_ENVIRONMENT: bool = match std::env::consts::OS.as_bytes() { + // https://github.com/illumos/illumos-gate/blob/0fb96ba1f1ce26ff8b286f8f928769a6afcb00a6/usr/src/lib/libc/port/gen/getenv.c + b"illumos" + // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/getenv.c + // https://github.com/NetBSD/src/blob/f45028636a44111bc4af44d460924958a4460844/lib/libc/stdlib/setenv.c + | b"netbsd" + => true, + _ => false, +}; + +/// Update time zone information from the system. +/// +/// For safety documentation, see [`time::util::refresh_tz`]. +pub(super) unsafe fn refresh_tz_unchecked() { + extern "C" { + #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")] + fn tzset(); + } + + // Safety: The caller must uphold the safety requirements. + unsafe { tzset() }; +} + +/// Attempt to update time zone information from the system. Returns `None` if the call is not known +/// to be sound. +pub(super) fn refresh_tz() -> Option<()> { + // Refresh $TZ if and only if the call is known to be sound. + // + // Soundness can be guaranteed either by knowledge of the operating system or knowledge that the + // process is single-threaded. If the process is single-threaded, then the environment cannot + // be mutated by a different thread in the process while execution of this function is taking + // place, which can cause a segmentation fault by dereferencing a dangling pointer. + // + // If the `num_threads` crate is incapable of determining the number of running threads, then + // we conservatively return `None` to avoid a soundness bug. + + if OS_HAS_THREAD_SAFE_ENVIRONMENT || num_threads::is_single_threaded() == Some(true) { + // Safety: The caller must uphold the safety requirements. + unsafe { refresh_tz_unchecked() }; + Some(()) + } else { + None + } +} diff --git a/vendor/time/src/tests.rs b/vendor/time/src/tests.rs new file mode 100644 index 00000000..1a404aa9 --- /dev/null +++ b/vendor/time/src/tests.rs @@ -0,0 +1,104 @@ +#![cfg(all( // require `--all-features` to be passed + feature = "default", + feature = "alloc", + feature = "formatting", + feature = "large-dates", + feature = "local-offset", + feature = "macros", + feature = "parsing", + feature = "quickcheck", + feature = "serde-human-readable", + feature = "serde-well-known", + feature = "std", + feature = "rand", + feature = "serde", +))] +#![allow( + let_underscore_drop, + clippy::clone_on_copy, + clippy::cognitive_complexity, + clippy::std_instead_of_core +)] + +//! Tests for internal details. +//! +//! This module should only be used when it is not possible to test the implementation in a +//! reasonable manner externally. + +use std::format; +use std::num::NonZeroU8; + +use crate::ext::DigitCount; +use crate::parsing::combinator::rfc::iso8601; +use crate::parsing::shim::Integer; +use crate::{duration, parsing}; + +#[test] +fn digit_count() { + assert_eq!(1_u8.num_digits(), 1); + assert_eq!(9_u8.num_digits(), 1); + assert_eq!(10_u8.num_digits(), 2); + assert_eq!(99_u8.num_digits(), 2); + assert_eq!(100_u8.num_digits(), 3); + + assert_eq!(1_u16.num_digits(), 1); + assert_eq!(9_u16.num_digits(), 1); + assert_eq!(10_u16.num_digits(), 2); + assert_eq!(99_u16.num_digits(), 2); + assert_eq!(100_u16.num_digits(), 3); + assert_eq!(999_u16.num_digits(), 3); + assert_eq!(1_000_u16.num_digits(), 4); + assert_eq!(9_999_u16.num_digits(), 4); + assert_eq!(10_000_u16.num_digits(), 5); + + assert_eq!(1_u32.num_digits(), 1); + assert_eq!(9_u32.num_digits(), 1); + assert_eq!(10_u32.num_digits(), 2); + assert_eq!(99_u32.num_digits(), 2); + assert_eq!(100_u32.num_digits(), 3); + assert_eq!(999_u32.num_digits(), 3); + assert_eq!(1_000_u32.num_digits(), 4); + assert_eq!(9_999_u32.num_digits(), 4); + assert_eq!(10_000_u32.num_digits(), 5); + assert_eq!(99_999_u32.num_digits(), 5); + assert_eq!(100_000_u32.num_digits(), 6); + assert_eq!(999_999_u32.num_digits(), 6); + assert_eq!(1_000_000_u32.num_digits(), 7); + assert_eq!(9_999_999_u32.num_digits(), 7); + assert_eq!(10_000_000_u32.num_digits(), 8); + assert_eq!(99_999_999_u32.num_digits(), 8); + assert_eq!(100_000_000_u32.num_digits(), 9); + assert_eq!(999_999_999_u32.num_digits(), 9); + assert_eq!(1_000_000_000_u32.num_digits(), 10); +} + +#[test] +fn debug() { + let _ = format!("{:?}", duration::Padding::Optimize); + let _ = format!("{:?}", parsing::ParsedItem(b"", 0)); + let _ = format!("{:?}", parsing::component::Period::Am); + let _ = format!("{:?}", iso8601::ExtendedKind::Basic); +} + +#[test] +fn clone() { + assert_eq!( + parsing::component::Period::Am.clone(), + parsing::component::Period::Am + ); + // does not impl Debug + assert!(crate::time::Padding::Optimize.clone() == crate::time::Padding::Optimize); + // does not impl PartialEq + assert!(matches!( + iso8601::ExtendedKind::Basic.clone(), + iso8601::ExtendedKind::Basic + )); +} + +#[test] +fn parsing_internals() { + assert!(parsing::ParsedItem(b"", ()) + .flat_map(|_| None::<()>) + .is_none()); + assert!(<NonZeroU8 as Integer>::parse_bytes(b"256").is_none()); +} diff --git a/vendor/time/src/time.rs b/vendor/time/src/time.rs new file mode 100644 index 00000000..627a2504 --- /dev/null +++ b/vendor/time/src/time.rs @@ -0,0 +1,937 @@ +//! The [`Time`] struct and its associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::fmt; +use core::ops::{Add, Sub}; +use core::time::Duration as StdDuration; +#[cfg(feature = "formatting")] +use std::io; + +use deranged::{RangedU32, RangedU8}; +use num_conv::prelude::*; +use powerfmt::ext::FormatterExt; +use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; + +use crate::convert::*; +#[cfg(feature = "formatting")] +use crate::formatting::Formattable; +use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign}; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +use crate::util::DateAdjustment; +use crate::{error, Duration}; + +/// By explicitly inserting this enum where padding is expected, the compiler is able to better +/// perform niche value optimization. +#[repr(u8)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) enum Padding { + #[allow(clippy::missing_docs_in_private_items)] + Optimize, +} + +/// The type of the `hour` field of `Time`. +type Hours = RangedU8<0, { Hour::per(Day) - 1 }>; +/// The type of the `minute` field of `Time`. +type Minutes = RangedU8<0, { Minute::per(Hour) - 1 }>; +/// The type of the `second` field of `Time`. +type Seconds = RangedU8<0, { Second::per(Minute) - 1 }>; +/// The type of the `nanosecond` field of `Time`. +type Nanoseconds = RangedU32<0, { Nanosecond::per(Second) - 1 }>; + +/// The clock time within a given date. Nanosecond precision. +/// +/// All minutes are assumed to have exactly 60 seconds; no attempt is made to handle leap seconds +/// (either positive or negative). +/// +/// When comparing two `Time`s, they are assumed to be in the same calendar date. +#[derive(Clone, Copy, Eq)] +#[repr(C)] +pub struct Time { + // The order of this struct's fields matter! + // Do not change them. + + // Little endian version + #[cfg(target_endian = "little")] + nanosecond: Nanoseconds, + #[cfg(target_endian = "little")] + second: Seconds, + #[cfg(target_endian = "little")] + minute: Minutes, + #[cfg(target_endian = "little")] + hour: Hours, + #[cfg(target_endian = "little")] + padding: Padding, + + // Big endian version + #[cfg(target_endian = "big")] + padding: Padding, + #[cfg(target_endian = "big")] + hour: Hours, + #[cfg(target_endian = "big")] + minute: Minutes, + #[cfg(target_endian = "big")] + second: Seconds, + #[cfg(target_endian = "big")] + nanosecond: Nanoseconds, +} + +impl core::hash::Hash for Time { + fn hash<H: core::hash::Hasher>(&self, state: &mut H) { + self.as_u64().hash(state) + } +} + +impl PartialEq for Time { + fn eq(&self, other: &Self) -> bool { + self.as_u64().eq(&other.as_u64()) + } +} + +impl PartialOrd for Time { + fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Time { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.as_u64().cmp(&other.as_u64()) + } +} + +impl Time { + /// Provides an u64 based representation **of the correct endianness** + /// + /// This representation can be used to do comparisons equality testing or hashing. + const fn as_u64(self) -> u64 { + let nano_bytes = self.nanosecond.get().to_ne_bytes(); + + #[cfg(target_endian = "big")] + return u64::from_be_bytes([ + self.padding as u8, + self.hour.get(), + self.minute.get(), + self.second.get(), + nano_bytes[0], + nano_bytes[1], + nano_bytes[2], + nano_bytes[3], + ]); + + #[cfg(target_endian = "little")] + return u64::from_le_bytes([ + nano_bytes[0], + nano_bytes[1], + nano_bytes[2], + nano_bytes[3], + self.second.get(), + self.minute.get(), + self.hour.get(), + self.padding as u8, + ]); + } + + /// A `Time` that is exactly midnight. This is the smallest possible value for a `Time`. + /// + /// ```rust + /// # use time::Time; + /// # use time_macros::time; + /// assert_eq!(Time::MIDNIGHT, time!(0:00)); + /// ``` + #[doc(alias = "MIN")] + pub const MIDNIGHT: Self = + Self::from_hms_nanos_ranged(Hours::MIN, Minutes::MIN, Seconds::MIN, Nanoseconds::MIN); + + /// A `Time` that is one nanosecond before midnight. This is the largest possible value for a + /// `Time`. + /// + /// ```rust + /// # use time::Time; + /// # use time_macros::time; + /// assert_eq!(Time::MAX, time!(23:59:59.999_999_999)); + /// ``` + pub const MAX: Self = + Self::from_hms_nanos_ranged(Hours::MAX, Minutes::MAX, Seconds::MAX, Nanoseconds::MAX); + + /// Create a `Time` from its components. + /// + /// # Safety + /// + /// - `hours` must be in the range `0..=23`. + /// - `minutes` must be in the range `0..=59`. + /// - `seconds` must be in the range `0..=59`. + /// - `nanoseconds` must be in the range `0..=999_999_999`. + #[doc(hidden)] + pub const unsafe fn __from_hms_nanos_unchecked( + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Self { + // Safety: The caller must uphold the safety invariants. + unsafe { + Self::from_hms_nanos_ranged( + Hours::new_unchecked(hour), + Minutes::new_unchecked(minute), + Seconds::new_unchecked(second), + Nanoseconds::new_unchecked(nanosecond), + ) + } + } + + /// Attempt to create a `Time` from the hour, minute, and second. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms(1, 2, 3).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms(24, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms(0, 60, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms(0, 0, 60).is_err()); // 60 isn't a valid second. + /// ``` + pub const fn from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, error::ComponentRange> { + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + Nanoseconds::MIN, + )) + } + + /// Create a `Time` from the hour, minute, second, and nanosecond. + pub(crate) const fn from_hms_nanos_ranged( + hour: Hours, + minute: Minutes, + second: Seconds, + nanosecond: Nanoseconds, + ) -> Self { + Self { + hour, + minute, + second, + nanosecond, + padding: Padding::Optimize, + } + } + + /// Attempt to create a `Time` from the hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_milli(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_milli(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_milli(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_milli(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_milli(0, 0, 0, 1_000).is_err()); // 1_000 isn't a valid millisecond. + /// ``` + pub const fn from_hms_milli( + hour: u8, + minute: u8, + second: u8, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)), + )) + } + + /// Attempt to create a `Time` from the hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_micro(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_micro(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_micro(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_micro(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_micro(0, 0, 0, 1_000_000).is_err()); // 1_000_000 isn't a valid microsecond. + /// ``` + pub const fn from_hms_micro( + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32), + )) + } + + /// Attempt to create a `Time` from the hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_nano(1, 2, 3, 4).is_ok()); + /// ``` + /// + /// ```rust + /// # use time::Time; + /// assert!(Time::from_hms_nano(24, 0, 0, 0).is_err()); // 24 isn't a valid hour. + /// assert!(Time::from_hms_nano(0, 60, 0, 0).is_err()); // 60 isn't a valid minute. + /// assert!(Time::from_hms_nano(0, 0, 60, 0).is_err()); // 60 isn't a valid second. + /// assert!(Time::from_hms_nano(0, 0, 0, 1_000_000_000).is_err()); // 1_000_000_000 isn't a valid nanosecond. + /// ``` + pub const fn from_hms_nano( + hour: u8, + minute: u8, + second: u8, + nanosecond: u32, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_hms_nanos_ranged( + ensure_ranged!(Hours: hour), + ensure_ranged!(Minutes: minute), + ensure_ranged!(Seconds: second), + ensure_ranged!(Nanoseconds: nanosecond), + )) + } + + /// Get the clock hour, minute, and second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms(), (0, 0, 0)); + /// assert_eq!(time!(23:59:59).as_hms(), (23, 59, 59)); + /// ``` + pub const fn as_hms(self) -> (u8, u8, u8) { + (self.hour.get(), self.minute.get(), self.second.get()) + } + + /// Get the clock hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_milli(), (0, 0, 0, 0)); + /// assert_eq!(time!(23:59:59.999).as_hms_milli(), (23, 59, 59, 999)); + /// ``` + pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { + ( + self.hour.get(), + self.minute.get(), + self.second.get(), + (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16, + ) + } + + /// Get the clock hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_micro(), (0, 0, 0, 0)); + /// assert_eq!( + /// time!(23:59:59.999_999).as_hms_micro(), + /// (23, 59, 59, 999_999) + /// ); + /// ``` + pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { + ( + self.hour.get(), + self.minute.get(), + self.second.get(), + self.nanosecond.get() / Nanosecond::per(Microsecond) as u32, + ) + } + + /// Get the clock hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).as_hms_nano(), (0, 0, 0, 0)); + /// assert_eq!( + /// time!(23:59:59.999_999_999).as_hms_nano(), + /// (23, 59, 59, 999_999_999) + /// ); + /// ``` + pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { + ( + self.hour.get(), + self.minute.get(), + self.second.get(), + self.nanosecond.get(), + ) + } + + /// Get the clock hour, minute, second, and nanosecond. + #[cfg(feature = "quickcheck")] + pub(crate) const fn as_hms_nano_ranged(self) -> (Hours, Minutes, Seconds, Nanoseconds) { + (self.hour, self.minute, self.second, self.nanosecond) + } + + /// Get the clock hour. + /// + /// The returned value will always be in the range `0..24`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).hour(), 0); + /// assert_eq!(time!(23:59:59).hour(), 23); + /// ``` + pub const fn hour(self) -> u8 { + self.hour.get() + } + + /// Get the minute within the hour. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).minute(), 0); + /// assert_eq!(time!(23:59:59).minute(), 59); + /// ``` + pub const fn minute(self) -> u8 { + self.minute.get() + } + + /// Get the second within the minute. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00:00).second(), 0); + /// assert_eq!(time!(23:59:59).second(), 59); + /// ``` + pub const fn second(self) -> u8 { + self.second.get() + } + + /// Get the milliseconds within the second. + /// + /// The returned value will always be in the range `0..1_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).millisecond(), 0); + /// assert_eq!(time!(23:59:59.999).millisecond(), 999); + /// ``` + pub const fn millisecond(self) -> u16 { + (self.nanosecond.get() / Nanosecond::per(Millisecond)) as u16 + } + + /// Get the microseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).microsecond(), 0); + /// assert_eq!(time!(23:59:59.999_999).microsecond(), 999_999); + /// ``` + pub const fn microsecond(self) -> u32 { + self.nanosecond.get() / Nanosecond::per(Microsecond) as u32 + } + + /// Get the nanoseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000_000`. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!(time!(0:00).nanosecond(), 0); + /// assert_eq!(time!(23:59:59.999_999_999).nanosecond(), 999_999_999); + /// ``` + pub const fn nanosecond(self) -> u32 { + self.nanosecond.get() + } + + /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning whether + /// the date is different. + pub(crate) const fn adjusting_add(self, duration: Duration) -> (DateAdjustment, Self) { + let mut nanoseconds = self.nanosecond.get() as i32 + duration.subsec_nanoseconds(); + let mut seconds = + self.second.get() as i8 + (duration.whole_seconds() % Second::per(Minute) as i64) as i8; + let mut minutes = + self.minute.get() as i8 + (duration.whole_minutes() % Minute::per(Hour) as i64) as i8; + let mut hours = + self.hour.get() as i8 + (duration.whole_hours() % Hour::per(Day) as i64) as i8; + let mut date_adjustment = DateAdjustment::None; + + cascade!(nanoseconds in 0..Nanosecond::per(Second) as i32 => seconds); + cascade!(seconds in 0..Second::per(Minute) as i8 => minutes); + cascade!(minutes in 0..Minute::per(Hour) as i8 => hours); + if hours >= Hour::per(Day) as i8 { + hours -= Hour::per(Day) as i8; + date_adjustment = DateAdjustment::Next; + } else if hours < 0 { + hours += Hour::per(Day) as i8; + date_adjustment = DateAdjustment::Previous; + } + + ( + date_adjustment, + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hours as u8, + minutes as u8, + seconds as u8, + nanoseconds as u32, + ) + }, + ) + } + + /// Subtract the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow, returning + /// whether the date is different. + pub(crate) const fn adjusting_sub(self, duration: Duration) -> (DateAdjustment, Self) { + let mut nanoseconds = self.nanosecond.get() as i32 - duration.subsec_nanoseconds(); + let mut seconds = + self.second.get() as i8 - (duration.whole_seconds() % Second::per(Minute) as i64) as i8; + let mut minutes = + self.minute.get() as i8 - (duration.whole_minutes() % Minute::per(Hour) as i64) as i8; + let mut hours = + self.hour.get() as i8 - (duration.whole_hours() % Hour::per(Day) as i64) as i8; + let mut date_adjustment = DateAdjustment::None; + + cascade!(nanoseconds in 0..Nanosecond::per(Second) as i32 => seconds); + cascade!(seconds in 0..Second::per(Minute) as i8 => minutes); + cascade!(minutes in 0..Minute::per(Hour) as i8 => hours); + if hours >= Hour::per(Day) as i8 { + hours -= Hour::per(Day) as i8; + date_adjustment = DateAdjustment::Next; + } else if hours < 0 { + hours += Hour::per(Day) as i8; + date_adjustment = DateAdjustment::Previous; + } + + ( + date_adjustment, + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hours as u8, + minutes as u8, + seconds as u8, + nanoseconds as u32, + ) + }, + ) + } + + /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, + /// returning whether the date is the previous date as the first element of the tuple. + pub(crate) const fn adjusting_add_std(self, duration: StdDuration) -> (bool, Self) { + let mut nanosecond = self.nanosecond.get() + duration.subsec_nanos(); + let mut second = + self.second.get() + (duration.as_secs() % Second::per(Minute) as u64) as u8; + let mut minute = self.minute.get() + + ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as u8; + let mut hour = self.hour.get() + + ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as u8; + let mut is_next_day = false; + + cascade!(nanosecond in 0..Nanosecond::per(Second) => second); + cascade!(second in 0..Second::per(Minute) => minute); + cascade!(minute in 0..Minute::per(Hour) => hour); + if hour >= Hour::per(Day) { + hour -= Hour::per(Day); + is_next_day = true; + } + + ( + is_next_day, + // Safety: The cascades above ensure the values are in range. + unsafe { Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) }, + ) + } + + /// Subtract the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow, + /// returning whether the date is the previous date as the first element of the tuple. + pub(crate) const fn adjusting_sub_std(self, duration: StdDuration) -> (bool, Self) { + let mut nanosecond = self.nanosecond.get() as i32 - duration.subsec_nanos() as i32; + let mut second = + self.second.get() as i8 - (duration.as_secs() % Second::per(Minute) as u64) as i8; + let mut minute = self.minute.get() as i8 + - ((duration.as_secs() / Second::per(Minute) as u64) % Minute::per(Hour) as u64) as i8; + let mut hour = self.hour.get() as i8 + - ((duration.as_secs() / Second::per(Hour) as u64) % Hour::per(Day) as u64) as i8; + let mut is_previous_day = false; + + cascade!(nanosecond in 0..Nanosecond::per(Second) as i32 => second); + cascade!(second in 0..Second::per(Minute) as i8 => minute); + cascade!(minute in 0..Minute::per(Hour) as i8 => hour); + if hour < 0 { + hour += Hour::per(Day) as i8; + is_previous_day = true; + } + + ( + is_previous_day, + // Safety: The cascades above ensure the values are in range. + unsafe { + Self::__from_hms_nanos_unchecked( + hour as u8, + minute as u8, + second as u8, + nanosecond as u32, + ) + }, + ) + } + + /// Replace the clock hour. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_hour(7), + /// Ok(time!(07:02:03.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_hour(24).is_err()); // 24 isn't a valid hour + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_hour(mut self, hour: u8) -> Result<Self, error::ComponentRange> { + self.hour = ensure_ranged!(Hours: hour); + Ok(self) + } + + /// Replace the minutes within the hour. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_minute(7), + /// Ok(time!(01:07:03.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_minute(60).is_err()); // 60 isn't a valid minute + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_minute(mut self, minute: u8) -> Result<Self, error::ComponentRange> { + self.minute = ensure_ranged!(Minutes: minute); + Ok(self) + } + + /// Replace the seconds within the minute. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_second(7), + /// Ok(time!(01:02:07.004_005_006)) + /// ); + /// assert!(time!(01:02:03.004_005_006).replace_second(60).is_err()); // 60 isn't a valid second + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_second(mut self, second: u8) -> Result<Self, error::ComponentRange> { + self.second = ensure_ranged!(Seconds: second); + Ok(self) + } + + /// Replace the milliseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_millisecond(7), + /// Ok(time!(01:02:03.007)) + /// ); + /// assert!(time!(01:02:03.004_005_006) + /// .replace_millisecond(1_000) + /// .is_err()); // 1_000 isn't a valid millisecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_millisecond( + mut self, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + self.nanosecond = + ensure_ranged!(Nanoseconds: millisecond as u32 * Nanosecond::per(Millisecond)); + Ok(self) + } + + /// Replace the microseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_microsecond(7_008), + /// Ok(time!(01:02:03.007_008)) + /// ); + /// assert!(time!(01:02:03.004_005_006) + /// .replace_microsecond(1_000_000) + /// .is_err()); // 1_000_000 isn't a valid microsecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_microsecond( + mut self, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + self.nanosecond = + ensure_ranged!(Nanoseconds: microsecond * Nanosecond::per(Microsecond) as u32); + Ok(self) + } + + /// Replace the nanoseconds within the second. + /// + /// ```rust + /// # use time_macros::time; + /// assert_eq!( + /// time!(01:02:03.004_005_006).replace_nanosecond(7_008_009), + /// Ok(time!(01:02:03.007_008_009)) + /// ); + /// assert!(time!(01:02:03.004_005_006) + /// .replace_nanosecond(1_000_000_000) + /// .is_err()); // 1_000_000_000 isn't a valid nanosecond + /// ``` + #[must_use = "This method does not mutate the original `Time`."] + pub const fn replace_nanosecond( + mut self, + nanosecond: u32, + ) -> Result<Self, error::ComponentRange> { + self.nanosecond = ensure_ranged!(Nanoseconds: nanosecond); + Ok(self) + } +} + +#[cfg(feature = "formatting")] +impl Time { + /// Format the `Time` using the provided [format description](crate::format_description). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into(output, None, Some(self), None) + } + + /// Format the `Time` using the provided [format description](crate::format_description). + /// + /// ```rust + /// # use time::format_description; + /// # use time_macros::time; + /// let format = format_description::parse("[hour]:[minute]:[second]")?; + /// assert_eq!(time!(12:00).format(&format)?, "12:00:00"); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(None, Some(self), None) + } +} + +#[cfg(feature = "parsing")] +impl Time { + /// Parse a `Time` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::Time; + /// # use time_macros::{time, format_description}; + /// let format = format_description!("[hour]:[minute]:[second]"); + /// assert_eq!(Time::parse("12:00:00", &format)?, time!(12:00)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_time(input.as_bytes()) + } +} + +mod private { + #[non_exhaustive] + #[derive(Debug, Clone, Copy)] + pub struct TimeMetadata { + /// How many characters wide the formatted subsecond is. + pub(super) subsecond_width: u8, + /// The value to use when formatting the subsecond. Leading zeroes will be added as + /// necessary. + pub(super) subsecond_value: u32, + } +} +use private::TimeMetadata; + +impl SmartDisplay for Time { + type Metadata = TimeMetadata; + + fn metadata(&self, _: FormatterOptions) -> Metadata<Self> { + let (subsecond_value, subsecond_width) = match self.nanosecond() { + nanos if nanos % 10 != 0 => (nanos, 9), + nanos if (nanos / 10) % 10 != 0 => (nanos / 10, 8), + nanos if (nanos / 100) % 10 != 0 => (nanos / 100, 7), + nanos if (nanos / 1_000) % 10 != 0 => (nanos / 1_000, 6), + nanos if (nanos / 10_000) % 10 != 0 => (nanos / 10_000, 5), + nanos if (nanos / 100_000) % 10 != 0 => (nanos / 100_000, 4), + nanos if (nanos / 1_000_000) % 10 != 0 => (nanos / 1_000_000, 3), + nanos if (nanos / 10_000_000) % 10 != 0 => (nanos / 10_000_000, 2), + nanos => (nanos / 100_000_000, 1), + }; + + let formatted_width = smart_display::padded_width_of!( + self.hour.get(), + ":", + self.minute.get() => width(2) fill('0'), + ":", + self.second.get() => width(2) fill('0'), + ".", + ) + subsecond_width; + + Metadata::new( + formatted_width, + self, + TimeMetadata { + subsecond_width: subsecond_width.truncate(), + subsecond_value, + }, + ) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> fmt::Result { + let subsecond_width = metadata.subsecond_width.extend(); + let subsecond_value = metadata.subsecond_value; + + f.pad_with_width( + metadata.unpadded_width(), + format_args!( + "{}:{:02}:{:02}.{subsecond_value:0subsecond_width$}", + self.hour, self.minute, self.second + ), + ) + } +} + +impl fmt::Display for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Add<Duration> for Time { + type Output = Self; + + /// Add the sub-day time of the [`Duration`] to the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(12:00) + 2.hours(), time!(14:00)); + /// assert_eq!(time!(0:00:01) + (-2).seconds(), time!(23:59:59)); + /// ``` + fn add(self, duration: Duration) -> Self::Output { + self.adjusting_add(duration).1 + } +} + +impl Add<StdDuration> for Time { + type Output = Self; + + /// Add the sub-day time of the [`std::time::Duration`] to the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalStdDuration; + /// # use time_macros::time; + /// assert_eq!(time!(12:00) + 2.std_hours(), time!(14:00)); + /// assert_eq!(time!(23:59:59) + 2.std_seconds(), time!(0:00:01)); + /// ``` + fn add(self, duration: StdDuration) -> Self::Output { + self.adjusting_add_std(duration).1 + } +} + +impl_add_assign!(Time: Duration, StdDuration); + +impl Sub<Duration> for Time { + type Output = Self; + + /// Subtract the sub-day time of the [`Duration`] from the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(14:00) - 2.hours(), time!(12:00)); + /// assert_eq!(time!(23:59:59) - (-2).seconds(), time!(0:00:01)); + /// ``` + fn sub(self, duration: Duration) -> Self::Output { + self.adjusting_sub(duration).1 + } +} + +impl Sub<StdDuration> for Time { + type Output = Self; + + /// Subtract the sub-day time of the [`std::time::Duration`] from the `Time`. Wraps on overflow. + /// + /// ```rust + /// # use time::ext::NumericalStdDuration; + /// # use time_macros::time; + /// assert_eq!(time!(14:00) - 2.std_hours(), time!(12:00)); + /// assert_eq!(time!(0:00:01) - 2.std_seconds(), time!(23:59:59)); + /// ``` + fn sub(self, duration: StdDuration) -> Self::Output { + self.adjusting_sub_std(duration).1 + } +} + +impl_sub_assign!(Time: Duration, StdDuration); + +impl Sub for Time { + type Output = Duration; + + /// Subtract two `Time`s, returning the [`Duration`] between. This assumes both `Time`s are in + /// the same calendar day. + /// + /// ```rust + /// # use time::ext::NumericalDuration; + /// # use time_macros::time; + /// assert_eq!(time!(0:00) - time!(0:00), 0.seconds()); + /// assert_eq!(time!(1:00) - time!(0:00), 1.hours()); + /// assert_eq!(time!(0:00) - time!(1:00), (-1).hours()); + /// assert_eq!(time!(0:00) - time!(23:00), (-23).hours()); + /// ``` + fn sub(self, rhs: Self) -> Self::Output { + let hour_diff = self.hour.get().cast_signed() - rhs.hour.get().cast_signed(); + let minute_diff = self.minute.get().cast_signed() - rhs.minute.get().cast_signed(); + let second_diff = self.second.get().cast_signed() - rhs.second.get().cast_signed(); + let nanosecond_diff = + self.nanosecond.get().cast_signed() - rhs.nanosecond.get().cast_signed(); + + let seconds = hour_diff.extend::<i64>() * Second::per(Hour).cast_signed().extend::<i64>() + + minute_diff.extend::<i64>() * Second::per(Minute).cast_signed().extend::<i64>() + + second_diff.extend::<i64>(); + + let (seconds, nanoseconds) = if seconds > 0 && nanosecond_diff < 0 { + ( + seconds - 1, + nanosecond_diff + Nanosecond::per(Second).cast_signed(), + ) + } else if seconds < 0 && nanosecond_diff > 0 { + ( + seconds + 1, + nanosecond_diff - Nanosecond::per(Second).cast_signed(), + ) + } else { + (seconds, nanosecond_diff) + }; + + // Safety: `nanoseconds` is in range due to the overflow handling. + unsafe { Duration::new_unchecked(seconds, nanoseconds) } + } +} diff --git a/vendor/time/src/utc_date_time.rs b/vendor/time/src/utc_date_time.rs new file mode 100644 index 00000000..e7bb8026 --- /dev/null +++ b/vendor/time/src/utc_date_time.rs @@ -0,0 +1,1223 @@ +//! The [`UtcDateTime`] struct and associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::fmt; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::time::Duration as StdDuration; +#[cfg(feature = "formatting")] +use std::io; + +use deranged::RangedI64; +use powerfmt::ext::FormatterExt as _; +use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; + +use crate::convert::*; +use crate::date::{MAX_YEAR, MIN_YEAR}; +#[cfg(feature = "formatting")] +use crate::formatting::Formattable; +use crate::internal_macros::{ + cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, +}; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +use crate::{ + error, util, Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday, +}; + +/// The Julian day of the Unix epoch. +const UNIX_EPOCH_JULIAN_DAY: i32 = UtcDateTime::UNIX_EPOCH.to_julian_day(); + +/// A [`PrimitiveDateTime`] that is known to be UTC. +/// +/// `UtcDateTime` is guaranteed to be ABI-compatible with [`PrimitiveDateTime`], meaning that +/// transmuting from one to the other will not result in undefined behavior. +#[repr(transparent)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct UtcDateTime { + inner: PrimitiveDateTime, +} + +impl UtcDateTime { + /// Midnight, 1 January, 1970. + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::utc_datetime; + /// assert_eq!(UtcDateTime::UNIX_EPOCH, utc_datetime!(1970-01-01 0:00)); + /// ``` + pub const UNIX_EPOCH: Self = Self::new( + // Safety: `ordinal` is not zero. + unsafe { Date::__from_ordinal_date_unchecked(1970, 1) }, + Time::MIDNIGHT, + ); + + /// The smallest value that can be represented by `UtcDateTime`. + /// + /// 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::UtcDateTime; + /// # use time_macros::utc_datetime; + #[cfg_attr( + feature = "large-dates", + doc = "// Assuming `large-dates` feature is enabled." + )] + #[cfg_attr( + feature = "large-dates", + doc = "assert_eq!(UtcDateTime::MIN, utc_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!(UtcDateTime::MIN, utc_datetime!(-9999-01-01 0:00));" + )] + /// ``` + pub const MIN: Self = Self::new(Date::MIN, Time::MIDNIGHT); + + /// The largest value that can be represented by `UtcDateTime`. + /// + /// 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::UtcDateTime; + /// # use time_macros::utc_datetime; + #[cfg_attr( + feature = "large-dates", + doc = "// Assuming `large-dates` feature is enabled." + )] + #[cfg_attr( + feature = "large-dates", + doc = "assert_eq!(UtcDateTime::MAX, utc_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!(UtcDateTime::MAX, utc_datetime!(+9999-12-31 23:59:59.999_999_999));" + )] + /// ``` + pub const MAX: Self = Self::new(Date::MAX, Time::MAX); + + /// Create a new `UtcDateTime` with the current date and time. + /// + /// ```rust + /// # use time::UtcDateTime; + /// assert!(UtcDateTime::now().year() >= 2019); + /// ``` + #[cfg(feature = "std")] + pub fn now() -> 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() + } + + /// Create a new `UtcDateTime` from the provided [`Date`] and [`Time`]. + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::{date, utc_datetime, time}; + /// assert_eq!( + /// UtcDateTime::new(date!(2019-01-01), time!(0:00)), + /// utc_datetime!(2019-01-01 0:00), + /// ); + /// ``` + pub const fn new(date: Date, time: Time) -> Self { + Self { + inner: PrimitiveDateTime::new(date, time), + } + } + + /// Create a new `UtcDateTime` from the [`PrimitiveDateTime`], assuming that the latter is UTC. + pub(crate) const fn from_primitive(date_time: PrimitiveDateTime) -> Self { + Self { inner: date_time } + } + + /// Obtain the [`PrimitiveDateTime`] that this `UtcDateTime` represents. The no-longer-attached + /// [`UtcOffset`] is assumed to be UTC. + pub(crate) const fn as_primitive(self) -> PrimitiveDateTime { + self.inner + } + + /// Create a `UtcDateTime` from the provided Unix timestamp. + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// UtcDateTime::from_unix_timestamp(0), + /// Ok(UtcDateTime::UNIX_EPOCH), + /// ); + /// assert_eq!( + /// UtcDateTime::from_unix_timestamp(1_546_300_800), + /// Ok(utc_datetime!(2019-01-01 0:00)), + /// ); + /// ``` + /// + /// If you have a timestamp-nanosecond pair, you can use something along the lines of the + /// following: + /// + /// ```rust + /// # use time::{Duration, UtcDateTime, ext::NumericalDuration}; + /// let (timestamp, nanos) = (1, 500_000_000); + /// assert_eq!( + /// UtcDateTime::from_unix_timestamp(timestamp)? + Duration::nanoseconds(nanos), + /// UtcDateTime::UNIX_EPOCH + 1.5.seconds() + /// ); + /// # Ok::<_, time::Error>(()) + /// ``` + pub const fn from_unix_timestamp(timestamp: i64) -> Result<Self, error::ComponentRange> { + type Timestamp = + RangedI64<{ UtcDateTime::MIN.unix_timestamp() }, { UtcDateTime::MAX.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(Day) as i64) as i32, + ) + }; + + let seconds_within_day = timestamp.rem_euclid(Second::per(Day) as i64); + // Safety: All values are in range. + let time = unsafe { + Time::__from_hms_nanos_unchecked( + (seconds_within_day / Second::per(Hour) as i64) as u8, + ((seconds_within_day % Second::per(Hour) as i64) / Minute::per(Hour) as i64) as u8, + (seconds_within_day % Second::per(Minute) as i64) as u8, + 0, + ) + }; + + Ok(Self::new(date, time)) + } + + /// Construct an `UtcDateTime` from the provided Unix timestamp (in nanoseconds). + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// UtcDateTime::from_unix_timestamp_nanos(0), + /// Ok(UtcDateTime::UNIX_EPOCH), + /// ); + /// assert_eq!( + /// UtcDateTime::from_unix_timestamp_nanos(1_546_300_800_000_000_000), + /// Ok(utc_datetime!(2019-01-01 0:00)), + /// ); + /// ``` + pub const fn from_unix_timestamp_nanos(timestamp: i128) -> Result<Self, error::ComponentRange> { + let datetime = const_try!(Self::from_unix_timestamp(div_floor!( + timestamp, + Nanosecond::per(Second) as i128 + ) as i64)); + + Ok(Self::new( + 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(Second) as i128) as u32, + ) + }, + )) + } + + /// Convert the `UtcDateTime` from UTC to the provided [`UtcOffset`], returning an + /// [`OffsetDateTime`]. + /// + /// ```rust + /// # use time_macros::{utc_datetime, offset}; + /// assert_eq!( + /// utc_datetime!(2000-01-01 0:00) + /// .to_offset(offset!(-1)) + /// .year(), + /// 1999, + /// ); + /// + /// // Construct midnight on new year's, UTC. + /// let utc = utc_datetime!(2000-01-01 0:00); + /// let new_york = utc.to_offset(offset!(-5)); + /// let los_angeles = utc.to_offset(offset!(-8)); + /// assert_eq!(utc.hour(), 0); + /// assert_eq!(new_york.hour(), 19); + /// assert_eq!(los_angeles.hour(), 16); + /// ``` + /// + /// # Panics + /// + /// This method panics if the local date-time in the new offset is outside the supported range. + pub const fn to_offset(self, offset: UtcOffset) -> OffsetDateTime { + expect_opt!( + self.checked_to_offset(offset), + "local datetime out of valid range" + ) + } + + /// Convert the `UtcDateTime` from UTC to the provided [`UtcOffset`], returning an + /// [`OffsetDateTime`]. `None` is returned if the date-time in the resulting offset is + /// invalid. + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::{utc_datetime, offset}; + /// assert_eq!( + /// utc_datetime!(2000-01-01 0:00) + /// .checked_to_offset(offset!(-1)) + /// .unwrap() + /// .year(), + /// 1999, + /// ); + /// assert_eq!( + /// UtcDateTime::MAX.checked_to_offset(offset!(+1)), + /// None, + /// ); + /// ``` + pub const fn checked_to_offset(self, offset: UtcOffset) -> Option<OffsetDateTime> { + // Fast path for when no conversion is necessary. + if offset.is_utc() { + return Some(self.inner.assume_utc()); + } + + let (year, ordinal, time) = self.to_offset_raw(offset); + + if year > MAX_YEAR || year < MIN_YEAR { + return None; + } + + Some(OffsetDateTime::new_in_offset( + // Safety: `ordinal` is not zero. + unsafe { Date::__from_ordinal_date_unchecked(year, ordinal) }, + time, + offset, + )) + } + + /// Equivalent to `.to_offset(UtcOffset::UTC)`, but returning the year, ordinal, and time. This + /// avoids constructing an invalid [`Date`] if the new value is out of range. + pub(crate) const fn to_offset_raw(self, offset: UtcOffset) -> (i32, u16, Time) { + let mut second = self.second() as i16 + offset.seconds_past_minute() as i16; + let mut minute = self.minute() as i16 + offset.minutes_past_hour() as i16; + let mut hour = self.hour() as i8 + offset.whole_hours(); + let (mut year, ordinal) = self.to_ordinal_date(); + let mut ordinal = ordinal as i16; + + cascade!(second in 0..Second::per(Minute) as i16 => minute); + cascade!(minute in 0..Minute::per(Hour) as i16 => hour); + cascade!(hour in 0..Hour::per(Day) as i8 => ordinal); + cascade!(ordinal => year); + + debug_assert!(ordinal > 0); + debug_assert!(ordinal <= util::days_in_year(year) as i16); + + ( + year, + ordinal as u16, + // Safety: The cascades above ensure the values are in range. + unsafe { + Time::__from_hms_nanos_unchecked( + hour as u8, + minute as u8, + second as u8, + self.nanosecond(), + ) + }, + ) + } + + /// Get the [Unix timestamp](https://en.wikipedia.org/wiki/Unix_time). + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(1970-01-01 0:00).unix_timestamp(), 0); + /// assert_eq!(utc_datetime!(1970-01-01 1:00).unix_timestamp(), 3_600); + /// ``` + pub const fn unix_timestamp(self) -> i64 { + let days = + (self.to_julian_day() as i64 - UNIX_EPOCH_JULIAN_DAY as i64) * Second::per(Day) as i64; + let hours = self.hour() as i64 * Second::per(Hour) as i64; + let minutes = self.minute() as i64 * Second::per(Minute) as i64; + let seconds = self.second() as i64; + days + hours + minutes + seconds + } + + /// Get the Unix timestamp in nanoseconds. + /// + /// ```rust + /// use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(1970-01-01 0:00).unix_timestamp_nanos(), 0); + /// assert_eq!( + /// utc_datetime!(1970-01-01 1:00).unix_timestamp_nanos(), + /// 3_600_000_000_000, + /// ); + /// ``` + pub const fn unix_timestamp_nanos(self) -> i128 { + self.unix_timestamp() as i128 * Nanosecond::per(Second) as i128 + self.nanosecond() as i128 + } + + /// Get the [`Date`] component of the `UtcDateTime`. + /// + /// ```rust + /// # use time_macros::{date, utc_datetime}; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).date(), date!(2019-01-01)); + /// ``` + pub const fn date(self) -> Date { + self.inner.date() + } + + /// Get the [`Time`] component of the `UtcDateTime`. + /// + /// ```rust + /// # use time_macros::{utc_datetime, time}; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).time(), time!(0:00)); + /// ``` + pub const fn time(self) -> Time { + self.inner.time() + } + + /// Get the year of the date. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).year(), 2019); + /// assert_eq!(utc_datetime!(2019-12-31 0:00).year(), 2019); + /// assert_eq!(utc_datetime!(2020-01-01 0:00).year(), 2020); + /// ``` + pub const fn year(self) -> i32 { + self.date().year() + } + + /// Get the month of the date. + /// + /// ```rust + /// # use time::Month; + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).month(), Month::January); + /// assert_eq!(utc_datetime!(2019-12-31 0:00).month(), Month::December); + /// ``` + pub const fn month(self) -> Month { + self.date().month() + } + + /// Get the day of the date. + /// + /// The returned value will always be in the range `1..=31`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).day(), 1); + /// assert_eq!(utc_datetime!(2019-12-31 0:00).day(), 31); + /// ``` + pub const fn day(self) -> u8 { + self.date().day() + } + + /// Get the day of the year. + /// + /// The returned value will always be in the range `1..=366` (`1..=365` for common years). + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).ordinal(), 1); + /// assert_eq!(utc_datetime!(2019-12-31 0:00).ordinal(), 365); + /// ``` + pub const fn ordinal(self) -> u16 { + self.date().ordinal() + } + + /// Get the ISO week number. + /// + /// The returned value will always be in the range `1..=53`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).iso_week(), 1); + /// assert_eq!(utc_datetime!(2019-10-04 0:00).iso_week(), 40); + /// assert_eq!(utc_datetime!(2020-01-01 0:00).iso_week(), 1); + /// assert_eq!(utc_datetime!(2020-12-31 0:00).iso_week(), 53); + /// assert_eq!(utc_datetime!(2021-01-01 0:00).iso_week(), 53); + /// ``` + pub const fn iso_week(self) -> u8 { + self.date().iso_week() + } + + /// Get the week number where week 1 begins on the first Sunday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).sunday_based_week(), 0); + /// assert_eq!(utc_datetime!(2020-01-01 0:00).sunday_based_week(), 0); + /// assert_eq!(utc_datetime!(2020-12-31 0:00).sunday_based_week(), 52); + /// assert_eq!(utc_datetime!(2021-01-01 0:00).sunday_based_week(), 0); + /// ``` + pub const fn sunday_based_week(self) -> u8 { + self.date().sunday_based_week() + } + + /// Get the week number where week 1 begins on the first Monday. + /// + /// The returned value will always be in the range `0..=53`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).monday_based_week(), 0); + /// assert_eq!(utc_datetime!(2020-01-01 0:00).monday_based_week(), 0); + /// assert_eq!(utc_datetime!(2020-12-31 0:00).monday_based_week(), 52); + /// assert_eq!(utc_datetime!(2021-01-01 0:00).monday_based_week(), 0); + /// ``` + pub const fn monday_based_week(self) -> u8 { + self.date().monday_based_week() + } + + /// Get the year, month, and day. + /// + /// ```rust + /// # use time::Month; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2019-01-01 0:00).to_calendar_date(), + /// (2019, Month::January, 1) + /// ); + /// ``` + pub const fn to_calendar_date(self) -> (i32, Month, u8) { + self.date().to_calendar_date() + } + + /// Get the year and ordinal day number. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).to_ordinal_date(), (2019, 1)); + /// ``` + pub const fn to_ordinal_date(self) -> (i32, u16) { + self.date().to_ordinal_date() + } + + /// Get the ISO 8601 year, week number, and weekday. + /// + /// ```rust + /// # use time::Weekday::*; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2019-01-01 0:00).to_iso_week_date(), + /// (2019, 1, Tuesday) + /// ); + /// assert_eq!( + /// utc_datetime!(2019-10-04 0:00).to_iso_week_date(), + /// (2019, 40, Friday) + /// ); + /// assert_eq!( + /// utc_datetime!(2020-01-01 0:00).to_iso_week_date(), + /// (2020, 1, Wednesday) + /// ); + /// assert_eq!( + /// utc_datetime!(2020-12-31 0:00).to_iso_week_date(), + /// (2020, 53, Thursday) + /// ); + /// assert_eq!( + /// utc_datetime!(2021-01-01 0:00).to_iso_week_date(), + /// (2020, 53, Friday) + /// ); + /// ``` + pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { + self.date().to_iso_week_date() + } + + /// Get the weekday. + /// + /// ```rust + /// # use time::Weekday::*; + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).weekday(), Tuesday); + /// assert_eq!(utc_datetime!(2019-02-01 0:00).weekday(), Friday); + /// assert_eq!(utc_datetime!(2019-03-01 0:00).weekday(), Friday); + /// assert_eq!(utc_datetime!(2019-04-01 0:00).weekday(), Monday); + /// assert_eq!(utc_datetime!(2019-05-01 0:00).weekday(), Wednesday); + /// assert_eq!(utc_datetime!(2019-06-01 0:00).weekday(), Saturday); + /// assert_eq!(utc_datetime!(2019-07-01 0:00).weekday(), Monday); + /// assert_eq!(utc_datetime!(2019-08-01 0:00).weekday(), Thursday); + /// assert_eq!(utc_datetime!(2019-09-01 0:00).weekday(), Sunday); + /// assert_eq!(utc_datetime!(2019-10-01 0:00).weekday(), Tuesday); + /// assert_eq!(utc_datetime!(2019-11-01 0:00).weekday(), Friday); + /// assert_eq!(utc_datetime!(2019-12-01 0:00).weekday(), Sunday); + /// ``` + pub const fn weekday(self) -> Weekday { + self.date().weekday() + } + + /// Get the Julian day for the date. The time is not taken into account for this calculation. + /// + /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is + /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(-4713-11-24 0:00).to_julian_day(), 0); + /// assert_eq!(utc_datetime!(2000-01-01 0:00).to_julian_day(), 2_451_545); + /// assert_eq!(utc_datetime!(2019-01-01 0:00).to_julian_day(), 2_458_485); + /// assert_eq!(utc_datetime!(2019-12-31 0:00).to_julian_day(), 2_458_849); + /// ``` + pub const fn to_julian_day(self) -> i32 { + self.date().to_julian_day() + } + + /// Get the clock hour, minute, and second. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2020-01-01 0:00:00).as_hms(), (0, 0, 0)); + /// assert_eq!(utc_datetime!(2020-01-01 23:59:59).as_hms(), (23, 59, 59)); + /// ``` + pub const fn as_hms(self) -> (u8, u8, u8) { + self.time().as_hms() + } + + /// Get the clock hour, minute, second, and millisecond. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2020-01-01 0:00:00).as_hms_milli(), (0, 0, 0, 0)); + /// assert_eq!( + /// utc_datetime!(2020-01-01 23:59:59.999).as_hms_milli(), + /// (23, 59, 59, 999) + /// ); + /// ``` + pub const fn as_hms_milli(self) -> (u8, u8, u8, u16) { + self.time().as_hms_milli() + } + + /// Get the clock hour, minute, second, and microsecond. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2020-01-01 0:00:00).as_hms_micro(), (0, 0, 0, 0)); + /// assert_eq!( + /// utc_datetime!(2020-01-01 23:59:59.999_999).as_hms_micro(), + /// (23, 59, 59, 999_999) + /// ); + /// ``` + pub const fn as_hms_micro(self) -> (u8, u8, u8, u32) { + self.time().as_hms_micro() + } + + /// Get the clock hour, minute, second, and nanosecond. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2020-01-01 0:00:00).as_hms_nano(), (0, 0, 0, 0)); + /// assert_eq!( + /// utc_datetime!(2020-01-01 23:59:59.999_999_999).as_hms_nano(), + /// (23, 59, 59, 999_999_999) + /// ); + /// ``` + pub const fn as_hms_nano(self) -> (u8, u8, u8, u32) { + self.time().as_hms_nano() + } + + /// Get the clock hour. + /// + /// The returned value will always be in the range `0..24`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).hour(), 0); + /// assert_eq!(utc_datetime!(2019-01-01 23:59:59).hour(), 23); + /// ``` + pub const fn hour(self) -> u8 { + self.time().hour() + } + + /// Get the minute within the hour. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).minute(), 0); + /// assert_eq!(utc_datetime!(2019-01-01 23:59:59).minute(), 59); + /// ``` + pub const fn minute(self) -> u8 { + self.time().minute() + } + + /// Get the second within the minute. + /// + /// The returned value will always be in the range `0..60`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).second(), 0); + /// assert_eq!(utc_datetime!(2019-01-01 23:59:59).second(), 59); + /// ``` + pub const fn second(self) -> u8 { + self.time().second() + } + + /// Get the milliseconds within the second. + /// + /// The returned value will always be in the range `0..1_000`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).millisecond(), 0); + /// assert_eq!(utc_datetime!(2019-01-01 23:59:59.999).millisecond(), 999); + /// ``` + pub const fn millisecond(self) -> u16 { + self.time().millisecond() + } + + /// Get the microseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).microsecond(), 0); + /// assert_eq!( + /// utc_datetime!(2019-01-01 23:59:59.999_999).microsecond(), + /// 999_999 + /// ); + /// ``` + pub const fn microsecond(self) -> u32 { + self.time().microsecond() + } + + /// Get the nanoseconds within the second. + /// + /// The returned value will always be in the range `0..1_000_000_000`. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2019-01-01 0:00).nanosecond(), 0); + /// assert_eq!( + /// utc_datetime!(2019-01-01 23:59:59.999_999_999).nanosecond(), + /// 999_999_999, + /// ); + /// ``` + pub const fn nanosecond(self) -> u32 { + self.time().nanosecond() + } + + /// Computes `self + duration`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{UtcDateTime, ext::NumericalDuration}; + /// # use time_macros::utc_datetime; + /// assert_eq!(UtcDateTime::MIN.checked_add((-2).days()), None); + /// assert_eq!(UtcDateTime::MAX.checked_add(1.days()), None); + /// assert_eq!( + /// utc_datetime!(2019 - 11 - 25 15:30).checked_add(27.hours()), + /// Some(utc_datetime!(2019 - 11 - 26 18:30)) + /// ); + /// ``` + pub const fn checked_add(self, duration: Duration) -> Option<Self> { + Some(Self::from_primitive(const_try_opt!(self + .inner + .checked_add(duration)))) + } + + /// Computes `self - duration`, returning `None` if an overflow occurred. + /// + /// ```rust + /// # use time::{UtcDateTime, ext::NumericalDuration}; + /// # use time_macros::utc_datetime; + /// assert_eq!(UtcDateTime::MIN.checked_sub(2.days()), None); + /// assert_eq!(UtcDateTime::MAX.checked_sub((-1).days()), None); + /// assert_eq!( + /// utc_datetime!(2019 - 11 - 25 15:30).checked_sub(27.hours()), + /// Some(utc_datetime!(2019 - 11 - 24 12:30)) + /// ); + /// ``` + pub const fn checked_sub(self, duration: Duration) -> Option<Self> { + Some(Self::from_primitive(const_try_opt!(self + .inner + .checked_sub(duration)))) + } + + /// Computes `self + duration`, saturating value on overflow. + /// + /// ```rust + /// # use time::{UtcDateTime, ext::NumericalDuration}; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// UtcDateTime::MIN.saturating_add((-2).days()), + /// UtcDateTime::MIN + /// ); + /// assert_eq!( + /// UtcDateTime::MAX.saturating_add(2.days()), + /// UtcDateTime::MAX + /// ); + /// assert_eq!( + /// utc_datetime!(2019 - 11 - 25 15:30).saturating_add(27.hours()), + /// utc_datetime!(2019 - 11 - 26 18:30) + /// ); + /// ``` + pub const fn saturating_add(self, duration: Duration) -> Self { + Self::from_primitive(self.inner.saturating_add(duration)) + } + + /// Computes `self - duration`, saturating value on overflow. + /// + /// ```rust + /// # use time::{UtcDateTime, ext::NumericalDuration}; + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// UtcDateTime::MIN.saturating_sub(2.days()), + /// UtcDateTime::MIN + /// ); + /// assert_eq!( + /// UtcDateTime::MAX.saturating_sub((-2).days()), + /// UtcDateTime::MAX + /// ); + /// assert_eq!( + /// utc_datetime!(2019 - 11 - 25 15:30).saturating_sub(27.hours()), + /// utc_datetime!(2019 - 11 - 24 12:30) + /// ); + /// ``` + pub const fn saturating_sub(self, duration: Duration) -> Self { + Self::from_primitive(self.inner.saturating_sub(duration)) + } +} + +/// Methods that replace part of the `UtcDateTime`. +impl UtcDateTime { + /// Replace the time, preserving the date. + /// + /// ```rust + /// # use time_macros::{utc_datetime, time}; + /// assert_eq!( + /// utc_datetime!(2020-01-01 17:00).replace_time(time!(5:00)), + /// utc_datetime!(2020-01-01 5:00) + /// ); + /// ``` + #[must_use = "This method does not mutate the original `UtcDateTime`."] + pub const fn replace_time(self, time: Time) -> Self { + Self::from_primitive(self.inner.replace_time(time)) + } + + /// Replace the date, preserving the time. + /// + /// ```rust + /// # use time_macros::{utc_datetime, date}; + /// assert_eq!( + /// utc_datetime!(2020-01-01 12:00).replace_date(date!(2020-01-30)), + /// utc_datetime!(2020-01-30 12:00) + /// ); + /// ``` + #[must_use = "This method does not mutate the original `UtcDateTime`."] + pub const fn replace_date(self, date: Date) -> Self { + Self::from_primitive(self.inner.replace_date(date)) + } + + /// Replace the year. The month and day will be unchanged. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 12:00).replace_year(2019), + /// Ok(utc_datetime!(2019 - 02 - 18 12:00)) + /// ); + /// assert!(utc_datetime!(2022 - 02 - 18 12:00).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_year(year)))) + } + + /// Replace the month of the year. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// # use time::Month; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 12:00).replace_month(Month::January), + /// Ok(utc_datetime!(2022 - 01 - 18 12:00)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_month(month)))) + } + + /// Replace the day of the month. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 12:00).replace_day(1), + /// Ok(utc_datetime!(2022 - 02 - 01 12:00)) + /// ); + /// assert!(utc_datetime!(2022 - 02 - 18 12:00).replace_day(0).is_err()); // 00 isn't a valid day + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_day(day)))) + } + + /// Replace the day of the year. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!(utc_datetime!(2022-049 12:00).replace_ordinal(1), Ok(utc_datetime!(2022-001 12:00))); + /// assert!(utc_datetime!(2022-049 12:00).replace_ordinal(0).is_err()); // 0 isn't a valid ordinal + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_ordinal(self, ordinal: u16) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_ordinal(ordinal)))) + } + + /// Replace the clock hour. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_hour(7), + /// Ok(utc_datetime!(2022 - 02 - 18 07:02:03.004_005_006)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_hour(self, hour: u8) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_hour(hour)))) + } + + /// Replace the minutes within the hour. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_minute(7), + /// Ok(utc_datetime!(2022 - 02 - 18 01:07:03.004_005_006)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_minute( + self, + sunday_based_week: u8, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_minute(sunday_based_week)))) + } + + /// Replace the seconds within the minute. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_second(7), + /// Ok(utc_datetime!(2022 - 02 - 18 01:02:07.004_005_006)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_second( + self, + monday_based_week: u8, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_second(monday_based_week)))) + } + + /// Replace the milliseconds within the second. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_millisecond(7), + /// Ok(utc_datetime!(2022 - 02 - 18 01:02:03.007)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_millisecond( + self, + millisecond: u16, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_millisecond(millisecond)))) + } + + /// Replace the microseconds within the second. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_microsecond(7_008), + /// Ok(utc_datetime!(2022 - 02 - 18 01:02:03.007_008)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_microsecond( + self, + microsecond: u32, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_microsecond(microsecond)))) + } + + /// Replace the nanoseconds within the second. + /// + /// ```rust + /// # use time_macros::utc_datetime; + /// assert_eq!( + /// utc_datetime!(2022 - 02 - 18 01:02:03.004_005_006).replace_nanosecond(7_008_009), + /// Ok(utc_datetime!(2022 - 02 - 18 01:02:03.007_008_009)) + /// ); + /// assert!(utc_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 `UtcDateTime`."] + pub const fn replace_nanosecond(self, nanosecond: u32) -> Result<Self, error::ComponentRange> { + Ok(Self::from_primitive(const_try!(self + .inner + .replace_nanosecond(nanosecond)))) + } +} + +#[cfg(feature = "formatting")] +impl UtcDateTime { + /// Format the `UtcDateTime` using the provided [format + /// description](crate::format_description). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into( + output, + Some(self.date()), + Some(self.time()), + Some(UtcOffset::UTC), + ) + } + + /// Format the `UtcDateTime` using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::format_description; + /// # use time_macros::utc_datetime; + /// let format = format_description::parse( + /// "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \ + /// sign:mandatory]:[offset_minute]:[offset_second]", + /// )?; + /// assert_eq!( + /// utc_datetime!(2020-01-02 03:04:05).format(&format)?, + /// "2020-01-02 03:04:05 +00:00:00" + /// ); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(Some(self.date()), Some(self.time()), Some(UtcOffset::UTC)) + } +} + +#[cfg(feature = "parsing")] +impl UtcDateTime { + /// Parse an `UtcDateTime` from the input using the provided [format + /// description](crate::format_description). A [`UtcOffset`] is permitted, but not required to + /// be present. If present, the value will be converted to UTC. + /// + /// ```rust + /// # use time::UtcDateTime; + /// # use time_macros::{utc_datetime, format_description}; + /// let format = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); + /// assert_eq!( + /// UtcDateTime::parse("2020-01-02 03:04:05", &format)?, + /// utc_datetime!(2020-01-02 03:04:05) + /// ); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_utc_date_time(input.as_bytes()) + } + + /// A helper method to check if the `UtcDateTime` 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")] + pub(crate) const fn is_valid_leap_second_stand_in(self) -> bool { + let dt = self.inner; + + dt.hour() == 23 + && dt.minute() == 59 + && dt.second() == 59 + && dt.nanosecond() == 999_999_999 + && dt.day() == dt.month().length(dt.year()) + } +} + +impl SmartDisplay for UtcDateTime { + type Metadata = (); + + fn metadata(&self, _: FormatterOptions) -> Metadata<Self> { + let width = smart_display::padded_width_of!(self.date(), " ", self.time(), " +00"); + Metadata::new(width, self, ()) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> fmt::Result { + f.pad_with_width( + metadata.unpadded_width(), + format_args!("{} {} +00", self.date(), self.time()), + ) + } +} + +impl fmt::Display for UtcDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for UtcDateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Add<Duration> for UtcDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: Duration) -> Self::Output { + self.inner.add(duration).as_utc() + } +} + +impl Add<StdDuration> for UtcDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add(self, duration: StdDuration) -> Self::Output { + self.inner.add(duration).as_utc() + } +} + +impl AddAssign<Duration> for UtcDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, rhs: Duration) { + self.inner.add_assign(rhs); + } +} + +impl AddAssign<StdDuration> for UtcDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn add_assign(&mut self, rhs: StdDuration) { + self.inner.add_assign(rhs); + } +} + +impl Sub<Duration> for UtcDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Duration) -> Self::Output { + self.checked_sub(rhs) + .expect("resulting value is out of range") + } +} + +impl Sub<StdDuration> for UtcDateTime { + type Output = Self; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, duration: StdDuration) -> Self::Output { + Self::from_primitive(self.inner.sub(duration)) + } +} + +impl SubAssign<Duration> for UtcDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub_assign(&mut self, rhs: Duration) { + self.inner.sub_assign(rhs); + } +} + +impl SubAssign<StdDuration> for UtcDateTime { + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub_assign(&mut self, rhs: StdDuration) { + self.inner.sub_assign(rhs); + } +} + +impl Sub for UtcDateTime { + type Output = Duration; + + /// # Panics + /// + /// This may panic if an overflow occurs. + fn sub(self, rhs: Self) -> Self::Output { + self.inner.sub(rhs.inner) + } +} diff --git a/vendor/time/src/utc_offset.rs b/vendor/time/src/utc_offset.rs new file mode 100644 index 00000000..73bcf2d1 --- /dev/null +++ b/vendor/time/src/utc_offset.rs @@ -0,0 +1,464 @@ +//! The [`UtcOffset`] struct and its associated `impl`s. + +#[cfg(feature = "formatting")] +use alloc::string::String; +use core::fmt; +use core::ops::Neg; +#[cfg(feature = "formatting")] +use std::io; + +use deranged::{RangedI32, RangedI8}; +use powerfmt::ext::FormatterExt; +use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; + +use crate::convert::*; +use crate::error; +#[cfg(feature = "formatting")] +use crate::formatting::Formattable; +use crate::internal_macros::ensure_ranged; +#[cfg(feature = "parsing")] +use crate::parsing::Parsable; +#[cfg(feature = "local-offset")] +use crate::sys::local_offset_at; +#[cfg(feature = "local-offset")] +use crate::OffsetDateTime; + +/// The type of the `hours` field of `UtcOffset`. +type Hours = RangedI8<-25, 25>; +/// The type of the `minutes` field of `UtcOffset`. +type Minutes = RangedI8<{ -(Minute::per(Hour) as i8 - 1) }, { Minute::per(Hour) as i8 - 1 }>; +/// The type of the `seconds` field of `UtcOffset`. +type Seconds = RangedI8<{ -(Second::per(Minute) as i8 - 1) }, { Second::per(Minute) as i8 - 1 }>; +/// The type capable of storing the range of whole seconds that a `UtcOffset` can encompass. +type WholeSeconds = RangedI32< + { + Hours::MIN.get() as i32 * Second::per(Hour) as i32 + + Minutes::MIN.get() as i32 * Second::per(Minute) as i32 + + Seconds::MIN.get() as i32 + }, + { + Hours::MAX.get() as i32 * Second::per(Hour) as i32 + + Minutes::MAX.get() as i32 * Second::per(Minute) as i32 + + Seconds::MAX.get() as i32 + }, +>; + +/// An offset from UTC. +/// +/// This struct can store values up to ±25:59:59. If you need support outside this range, please +/// file an issue with your use case. +// All three components _must_ have the same sign. +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct UtcOffset { + hours: Hours, + minutes: Minutes, + seconds: Seconds, +} + +impl UtcOffset { + /// A `UtcOffset` that is UTC. + /// + /// ```rust + /// # use time::UtcOffset; + /// # use time_macros::offset; + /// assert_eq!(UtcOffset::UTC, offset!(UTC)); + /// ``` + pub const UTC: Self = Self::from_whole_seconds_ranged(WholeSeconds::new_static::<0>()); + + /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided, the + /// validity of which must be guaranteed by the caller. All three parameters must have the same + /// sign. + /// + /// # Safety + /// + /// - Hours must be in the range `-25..=25`. + /// - Minutes must be in the range `-59..=59`. + /// - Seconds must be in the range `-59..=59`. + /// + /// While the signs of the parameters are required to match to avoid bugs, this is not a safety + /// invariant. + #[doc(hidden)] + pub const unsafe fn __from_hms_unchecked(hours: i8, minutes: i8, seconds: i8) -> Self { + // Safety: The caller must uphold the safety invariants. + unsafe { + Self::from_hms_ranged_unchecked( + Hours::new_unchecked(hours), + Minutes::new_unchecked(minutes), + Seconds::new_unchecked(seconds), + ) + } + } + + /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds + /// provided. + /// + /// The sign of all three components should match. If they do not, all smaller components will + /// have their signs flipped. + /// + /// ```rust + /// # use time::UtcOffset; + /// assert_eq!(UtcOffset::from_hms(1, 2, 3)?.as_hms(), (1, 2, 3)); + /// assert_eq!(UtcOffset::from_hms(1, -2, -3)?.as_hms(), (1, 2, 3)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub const fn from_hms( + hours: i8, + minutes: i8, + seconds: i8, + ) -> Result<Self, error::ComponentRange> { + Ok(Self::from_hms_ranged( + ensure_ranged!(Hours: hours), + ensure_ranged!(Minutes: minutes), + ensure_ranged!(Seconds: seconds), + )) + } + + /// Create a `UtcOffset` representing an offset of the hours, minutes, and seconds provided. All + /// three parameters must have the same sign. + /// + /// While the signs of the parameters are required to match, this is not a safety invariant. + pub(crate) const fn from_hms_ranged_unchecked( + hours: Hours, + minutes: Minutes, + seconds: Seconds, + ) -> Self { + if hours.get() < 0 { + debug_assert!(minutes.get() <= 0); + debug_assert!(seconds.get() <= 0); + } else if hours.get() > 0 { + debug_assert!(minutes.get() >= 0); + debug_assert!(seconds.get() >= 0); + } + if minutes.get() < 0 { + debug_assert!(seconds.get() <= 0); + } else if minutes.get() > 0 { + debug_assert!(seconds.get() >= 0); + } + + Self { + hours, + minutes, + seconds, + } + } + + /// Create a `UtcOffset` representing an offset by the number of hours, minutes, and seconds + /// provided. + /// + /// The sign of all three components should match. If they do not, all smaller components will + /// have their signs flipped. + pub(crate) const fn from_hms_ranged( + hours: Hours, + mut minutes: Minutes, + mut seconds: Seconds, + ) -> Self { + if (hours.get() > 0 && minutes.get() < 0) || (hours.get() < 0 && minutes.get() > 0) { + minutes = minutes.neg(); + } + if (hours.get() > 0 && seconds.get() < 0) + || (hours.get() < 0 && seconds.get() > 0) + || (minutes.get() > 0 && seconds.get() < 0) + || (minutes.get() < 0 && seconds.get() > 0) + { + seconds = seconds.neg(); + } + + Self { + hours, + minutes, + seconds, + } + } + + /// Create a `UtcOffset` representing an offset by the number of seconds provided. + /// + /// ```rust + /// # use time::UtcOffset; + /// assert_eq!(UtcOffset::from_whole_seconds(3_723)?.as_hms(), (1, 2, 3)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub const fn from_whole_seconds(seconds: i32) -> Result<Self, error::ComponentRange> { + Ok(Self::from_whole_seconds_ranged( + ensure_ranged!(WholeSeconds: seconds), + )) + } + + /// Create a `UtcOffset` representing an offset by the number of seconds provided. + // ignore because the function is crate-private + /// ```rust,ignore + /// # use time::UtcOffset; + /// # use deranged::RangedI32; + /// assert_eq!( + /// UtcOffset::from_whole_seconds_ranged(RangedI32::new_static::<3_723>()).as_hms(), + /// (1, 2, 3) + /// ); + /// # Ok::<_, time::Error>(()) + /// ``` + pub(crate) const fn from_whole_seconds_ranged(seconds: WholeSeconds) -> Self { + // Safety: The type of `seconds` guarantees that all values are in range. + unsafe { + Self::__from_hms_unchecked( + (seconds.get() / Second::per(Hour) as i32) as i8, + ((seconds.get() % Second::per(Hour) as i32) / Minute::per(Hour) as i32) as i8, + (seconds.get() % Second::per(Minute) as i32) as i8, + ) + } + } + + /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components + /// will always match. A positive value indicates an offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).as_hms(), (1, 2, 3)); + /// assert_eq!(offset!(-1:02:03).as_hms(), (-1, -2, -3)); + /// ``` + pub const fn as_hms(self) -> (i8, i8, i8) { + (self.hours.get(), self.minutes.get(), self.seconds.get()) + } + + /// Obtain the UTC offset as its hours, minutes, and seconds. The sign of all three components + /// will always match. A positive value indicates an offset to the east; a negative to the west. + #[cfg(feature = "quickcheck")] + pub(crate) const fn as_hms_ranged(self) -> (Hours, Minutes, Seconds) { + (self.hours, self.minutes, self.seconds) + } + + /// Obtain the number of whole hours the offset is from UTC. A positive value indicates an + /// offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).whole_hours(), 1); + /// assert_eq!(offset!(-1:02:03).whole_hours(), -1); + /// ``` + pub const fn whole_hours(self) -> i8 { + self.hours.get() + } + + /// Obtain the number of whole minutes the offset is from UTC. A positive value indicates an + /// offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).whole_minutes(), 62); + /// assert_eq!(offset!(-1:02:03).whole_minutes(), -62); + /// ``` + pub const fn whole_minutes(self) -> i16 { + self.hours.get() as i16 * Minute::per(Hour) as i16 + self.minutes.get() as i16 + } + + /// Obtain the number of minutes past the hour the offset is from UTC. A positive value + /// indicates an offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).minutes_past_hour(), 2); + /// assert_eq!(offset!(-1:02:03).minutes_past_hour(), -2); + /// ``` + pub const fn minutes_past_hour(self) -> i8 { + self.minutes.get() + } + + /// Obtain the number of whole seconds the offset is from UTC. A positive value indicates an + /// offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).whole_seconds(), 3723); + /// assert_eq!(offset!(-1:02:03).whole_seconds(), -3723); + /// ``` + // This may be useful for anyone manually implementing arithmetic, as it + // would let them construct a `Duration` directly. + pub const fn whole_seconds(self) -> i32 { + self.hours.get() as i32 * Second::per(Hour) as i32 + + self.minutes.get() as i32 * Second::per(Minute) as i32 + + self.seconds.get() as i32 + } + + /// Obtain the number of seconds past the minute the offset is from UTC. A positive value + /// indicates an offset to the east; a negative to the west. + /// + /// ```rust + /// # use time_macros::offset; + /// assert_eq!(offset!(+1:02:03).seconds_past_minute(), 3); + /// assert_eq!(offset!(-1:02:03).seconds_past_minute(), -3); + /// ``` + pub const fn seconds_past_minute(self) -> i8 { + self.seconds.get() + } + + /// Check if the offset is exactly UTC. + /// + /// + /// ```rust + /// # use time_macros::offset; + /// assert!(!offset!(+1:02:03).is_utc()); + /// assert!(!offset!(-1:02:03).is_utc()); + /// assert!(offset!(UTC).is_utc()); + /// ``` + pub const fn is_utc(self) -> bool { + self.hours.get() == 0 && self.minutes.get() == 0 && self.seconds.get() == 0 + } + + /// Check if the offset is positive, or east of UTC. + /// + /// ```rust + /// # use time_macros::offset; + /// assert!(offset!(+1:02:03).is_positive()); + /// assert!(!offset!(-1:02:03).is_positive()); + /// assert!(!offset!(UTC).is_positive()); + /// ``` + pub const fn is_positive(self) -> bool { + self.hours.get() > 0 || self.minutes.get() > 0 || self.seconds.get() > 0 + } + + /// Check if the offset is negative, or west of UTC. + /// + /// ```rust + /// # use time_macros::offset; + /// assert!(!offset!(+1:02:03).is_negative()); + /// assert!(offset!(-1:02:03).is_negative()); + /// assert!(!offset!(UTC).is_negative()); + /// ``` + pub const fn is_negative(self) -> bool { + self.hours.get() < 0 || self.minutes.get() < 0 || self.seconds.get() < 0 + } + + /// Attempt to obtain the system's UTC offset at a known moment in time. If the offset cannot be + /// determined, an error is returned. + /// + /// ```rust + /// # use time::{UtcOffset, OffsetDateTime}; + /// let local_offset = UtcOffset::local_offset_at(OffsetDateTime::UNIX_EPOCH); + /// # if false { + /// assert!(local_offset.is_ok()); + /// # } + /// ``` + #[cfg(feature = "local-offset")] + pub fn local_offset_at(datetime: OffsetDateTime) -> Result<Self, error::IndeterminateOffset> { + local_offset_at(datetime).ok_or(error::IndeterminateOffset) + } + + /// Attempt to obtain the system's current UTC offset. If the offset cannot be determined, an + /// error is returned. + /// + /// ```rust + /// # use time::UtcOffset; + /// let local_offset = UtcOffset::current_local_offset(); + /// # if false { + /// assert!(local_offset.is_ok()); + /// # } + /// ``` + #[cfg(feature = "local-offset")] + pub fn current_local_offset() -> Result<Self, error::IndeterminateOffset> { + let now = OffsetDateTime::now_utc(); + local_offset_at(now).ok_or(error::IndeterminateOffset) + } +} + +#[cfg(feature = "formatting")] +impl UtcOffset { + /// Format the `UtcOffset` using the provided [format description](crate::format_description). + pub fn format_into( + self, + output: &mut (impl io::Write + ?Sized), + format: &(impl Formattable + ?Sized), + ) -> Result<usize, error::Format> { + format.format_into(output, None, None, Some(self)) + } + + /// Format the `UtcOffset` using the provided [format description](crate::format_description). + /// + /// ```rust + /// # use time::format_description; + /// # use time_macros::offset; + /// let format = format_description::parse("[offset_hour sign:mandatory]:[offset_minute]")?; + /// assert_eq!(offset!(+1).format(&format)?, "+01:00"); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { + format.format(None, None, Some(self)) + } +} + +#[cfg(feature = "parsing")] +impl UtcOffset { + /// Parse a `UtcOffset` from the input using the provided [format + /// description](crate::format_description). + /// + /// ```rust + /// # use time::UtcOffset; + /// # use time_macros::{offset, format_description}; + /// let format = format_description!("[offset_hour]:[offset_minute]"); + /// assert_eq!(UtcOffset::parse("-03:42", &format)?, offset!(-3:42)); + /// # Ok::<_, time::Error>(()) + /// ``` + pub fn parse( + input: &str, + description: &(impl Parsable + ?Sized), + ) -> Result<Self, error::Parse> { + description.parse_offset(input.as_bytes()) + } +} + +mod private { + #[non_exhaustive] + #[derive(Debug, Clone, Copy)] + pub struct UtcOffsetMetadata; +} +use private::UtcOffsetMetadata; + +impl SmartDisplay for UtcOffset { + type Metadata = UtcOffsetMetadata; + + fn metadata(&self, _: FormatterOptions) -> Metadata<Self> { + let sign = if self.is_negative() { '-' } else { '+' }; + let width = smart_display::padded_width_of!( + sign, + self.hours.abs() => width(2), + ":", + self.minutes.abs() => width(2), + ":", + self.seconds.abs() => width(2), + ); + Metadata::new(width, self, UtcOffsetMetadata) + } + + fn fmt_with_metadata( + &self, + f: &mut fmt::Formatter<'_>, + metadata: Metadata<Self>, + ) -> fmt::Result { + f.pad_with_width( + metadata.unpadded_width(), + format_args!( + "{}{:02}:{:02}:{:02}", + if self.is_negative() { '-' } else { '+' }, + self.hours.abs(), + self.minutes.abs(), + self.seconds.abs(), + ), + ) + } +} + +impl fmt::Display for UtcOffset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl fmt::Debug for UtcOffset { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Neg for UtcOffset { + type Output = Self; + + fn neg(self) -> Self::Output { + Self::from_hms_ranged(self.hours.neg(), self.minutes.neg(), self.seconds.neg()) + } +} diff --git a/vendor/time/src/util.rs b/vendor/time/src/util.rs new file mode 100644 index 00000000..c5166a70 --- /dev/null +++ b/vendor/time/src/util.rs @@ -0,0 +1,105 @@ +//! Utility functions, including updating time zone information. + +pub use time_core::util::{days_in_year, is_leap_year, weeks_in_year}; + +use crate::Month; + +/// Whether to adjust the date, and in which direction. Useful when implementing arithmetic. +pub(crate) enum DateAdjustment { + /// The previous day should be used. + Previous, + /// The next day should be used. + Next, + /// The date should be used as-is. + None, +} + +/// Get the number of days in the month of a given year. +/// +/// ```rust +/// # use time::{Month, util}; +/// assert_eq!(util::days_in_month(Month::February, 2020), 29); +/// ``` +pub const fn days_in_month(month: Month, year: i32) -> u8 { + time_core::util::days_in_month(month as u8, year) +} + +/// Get the number of days in the month of a given year. +/// +/// ```rust +/// # #![allow(deprecated)] +/// # use time::{Month, util}; +/// assert_eq!(util::days_in_year_month(2020, Month::February), 29); +/// ``` +#[deprecated( + since = "0.3.37", + note = "use `days_in_month` or `Month::length` instead" +)] +pub const fn days_in_year_month(year: i32, month: Month) -> u8 { + days_in_month(month, year) +} + +/// Update time zone information from the system. +/// +/// For a version of this function that is guaranteed to be sound, see [`refresh_tz`]. +/// +/// # Safety +/// +/// This is a system call with specific requirements. The following is from POSIX's description of +/// `tzset`: +/// +/// > If a thread accesses `tzname`, `daylight`, or `timezone` directly while another thread is in a +/// > call to `tzset()`, or to any function that is required or allowed to set timezone information +/// > as if by calling `tzset()`, the behavior is undefined. +/// +/// Effectively, this translates to the requirement that at least one of the following must be true: +/// +/// - The operating system provides a thread-safe environment. +/// - The process is single-threaded. +/// - The process is multi-threaded **and** no other thread is mutating the environment in any way +/// at the same time a call to a method that obtains the local UTC offset. This includes adding, +/// removing, or modifying an environment variable. +/// +/// ## Soundness is global +/// +/// You must not only verify this safety conditions for your code, but for **all** code that will be +/// included in the final binary. Notably, it applies to both direct and transitive dependencies and +/// to both Rust and non-Rust code. **For this reason it is not possible for a library to soundly +/// call this method.** +/// +/// ## Forward compatibility +/// +/// This currently only does anything on Unix-like systems. On other systems, it is a no-op. This +/// may change in the future if necessary, expanding the safety requirements. It is expected that, +/// at a minimum, calling this method when the process is single-threaded will remain sound. +#[cfg(feature = "local-offset")] +pub unsafe fn refresh_tz_unchecked() { + // Safety: The caller must uphold the safety requirements. + unsafe { crate::sys::refresh_tz_unchecked() }; +} + +/// Attempt to update time zone information from the system. +/// +/// Returns `None` if the call is not known to be sound. +#[cfg(feature = "local-offset")] +pub fn refresh_tz() -> Option<()> { + crate::sys::refresh_tz() +} + +#[doc(hidden)] +#[cfg(feature = "local-offset")] +#[allow(clippy::missing_const_for_fn)] +#[deprecated(since = "0.3.37", note = "no longer needed; TZ is refreshed manually")] +pub mod local_offset { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum Soundness { + Sound, + Unsound, + } + + pub unsafe fn set_soundness(_: Soundness) {} + + pub fn get_soundness() -> Soundness { + Soundness::Sound + } +} diff --git a/vendor/time/src/weekday.rs b/vendor/time/src/weekday.rs new file mode 100644 index 00000000..4331da83 --- /dev/null +++ b/vendor/time/src/weekday.rs @@ -0,0 +1,220 @@ +//! Days of the week. + +use core::fmt; +use core::str::FromStr; + +use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay}; + +use self::Weekday::*; +use crate::error; + +/// Days of the week. +/// +/// As order is dependent on context (Sunday could be either two days after or five days before +/// Friday), this type does not implement `PartialOrd` or `Ord`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Weekday { + #[allow(missing_docs)] + Monday, + #[allow(missing_docs)] + Tuesday, + #[allow(missing_docs)] + Wednesday, + #[allow(missing_docs)] + Thursday, + #[allow(missing_docs)] + Friday, + #[allow(missing_docs)] + Saturday, + #[allow(missing_docs)] + Sunday, +} + +impl Weekday { + /// Get the previous weekday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Tuesday.previous(), Weekday::Monday); + /// ``` + pub const fn previous(self) -> Self { + match self { + Monday => Sunday, + Tuesday => Monday, + Wednesday => Tuesday, + Thursday => Wednesday, + Friday => Thursday, + Saturday => Friday, + Sunday => Saturday, + } + } + + /// Get the next weekday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.next(), Weekday::Tuesday); + /// ``` + pub const fn next(self) -> Self { + match self { + Monday => Tuesday, + Tuesday => Wednesday, + Wednesday => Thursday, + Thursday => Friday, + Friday => Saturday, + Saturday => Sunday, + Sunday => Monday, + } + } + + /// Get n-th next day. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.nth_next(1), Weekday::Tuesday); + /// assert_eq!(Weekday::Sunday.nth_next(10), Weekday::Wednesday); + /// ``` + pub const fn nth_next(self, n: u8) -> Self { + match (self.number_days_from_monday() + n % 7) % 7 { + 0 => Monday, + 1 => Tuesday, + 2 => Wednesday, + 3 => Thursday, + 4 => Friday, + 5 => Saturday, + val => { + debug_assert!(val == 6); + Sunday + } + } + } + + /// Get n-th previous day. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.nth_prev(1), Weekday::Sunday); + /// assert_eq!(Weekday::Sunday.nth_prev(10), Weekday::Thursday); + /// ``` + pub const fn nth_prev(self, n: u8) -> Self { + match self.number_days_from_monday() as i8 - (n % 7) as i8 { + 1 | -6 => Tuesday, + 2 | -5 => Wednesday, + 3 | -4 => Thursday, + 4 | -3 => Friday, + 5 | -2 => Saturday, + 6 | -1 => Sunday, + val => { + debug_assert!(val == 0); + Monday + } + } + } + + /// Get the one-indexed number of days from Monday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.number_from_monday(), 1); + /// ``` + #[doc(alias = "iso_weekday_number")] + pub const fn number_from_monday(self) -> u8 { + self.number_days_from_monday() + 1 + } + + /// Get the one-indexed number of days from Sunday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.number_from_sunday(), 2); + /// ``` + pub const fn number_from_sunday(self) -> u8 { + self.number_days_from_sunday() + 1 + } + + /// Get the zero-indexed number of days from Monday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.number_days_from_monday(), 0); + /// ``` + pub const fn number_days_from_monday(self) -> u8 { + self as u8 + } + + /// Get the zero-indexed number of days from Sunday. + /// + /// ```rust + /// # use time::Weekday; + /// assert_eq!(Weekday::Monday.number_days_from_sunday(), 1); + /// ``` + pub const fn number_days_from_sunday(self) -> u8 { + match self { + Monday => 1, + Tuesday => 2, + Wednesday => 3, + Thursday => 4, + Friday => 5, + Saturday => 6, + Sunday => 0, + } + } +} + +mod private { + #[non_exhaustive] + #[derive(Debug, Clone, Copy)] + pub struct WeekdayMetadata; +} +use private::WeekdayMetadata; + +impl SmartDisplay for Weekday { + type Metadata = WeekdayMetadata; + + fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { + match self { + Monday => Metadata::new(6, self, WeekdayMetadata), + Tuesday => Metadata::new(7, self, WeekdayMetadata), + Wednesday => Metadata::new(9, self, WeekdayMetadata), + Thursday => Metadata::new(8, self, WeekdayMetadata), + Friday => Metadata::new(6, self, WeekdayMetadata), + Saturday => Metadata::new(8, self, WeekdayMetadata), + Sunday => Metadata::new(6, self, WeekdayMetadata), + } + } + + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(match self { + Monday => "Monday", + Tuesday => "Tuesday", + Wednesday => "Wednesday", + Thursday => "Thursday", + Friday => "Friday", + Saturday => "Saturday", + Sunday => "Sunday", + }) + } +} + +impl fmt::Display for Weekday { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + SmartDisplay::fmt(self, f) + } +} + +impl FromStr for Weekday { + type Err = error::InvalidVariant; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "Monday" => Ok(Monday), + "Tuesday" => Ok(Tuesday), + "Wednesday" => Ok(Wednesday), + "Thursday" => Ok(Thursday), + "Friday" => Ok(Friday), + "Saturday" => Ok(Saturday), + "Sunday" => Ok(Sunday), + _ => Err(error::InvalidVariant), + } + } +} |
