diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 02:49:50 +0000 |
commit | 9835e2ae736235810b4ea1c162ca5e65c547e770 (patch) | |
tree | 3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/chrono | |
parent | Releasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff) | |
download | rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip |
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/chrono')
60 files changed, 15471 insertions, 9482 deletions
diff --git a/vendor/chrono/.cargo-checksum.json b/vendor/chrono/.cargo-checksum.json index 278002f2a..5d66a6fc4 100644 --- a/vendor/chrono/.cargo-checksum.json +++ b/vendor/chrono/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"AUTHORS.txt":"bcca43c486176e81edcb64d065d418986cb5ca71af7ee4a648236a644305960d","CHANGELOG.md":"a925f71022f1c9e34e5df7c016ab2a32e9be22fc503929aa87d567900ebe064b","Cargo.toml":"13fab9c53580e2754c4fa075768ee9591e8d4b4a437ac9dbae503c4d4b6daf09","LICENSE.txt":"46610329ff0b38effb9cb05979ff1ef761e465fed96b2eaca39e439d00129fd7","README.md":"39985917c9bc71ee87a24808369ddfab22f3c6343da7c87c2274c965e1eec9db","benches/chrono.rs":"4de07b4c7bc907926e5a6ed20fc00a30e4e0491604f3d78eece81f1a8b45870a","benches/serde.rs":"e1a9624bcad4892c4cc7b76d5ad14607a016305a27b2231c2c77814b1d4448a8","rustfmt.toml":"f74204a6f92aa7422a16ecb2ffe2d5bae0f123b778d08b5db1a398a3c9ca4306","src/date.rs":"ec234e777efa9d8cd2f0c7f87db9296eda04bd4d56c994232a3329eb863acd34","src/datetime.rs":"63e582cd17f3070cbcb586bea15b5102ead1a898252e14b1817a498750043102","src/div.rs":"6c0a08648bb688b418b42f1c7be8387057a9db9774d3f09e97df2a183b79cd9f","src/format/locales.rs":"8c14cb93c68b747947b74ab2933aae53c8a0edd009ff16815e756015fbea6e9f","src/format/mod.rs":"09c66dee24e69325ce9a7be5b6c830317515ee37f7c2a26f48835484ed155c89","src/format/parse.rs":"c98217756370c6186bdab117373912208c018a2c6411f9be0fd1aecab54fb10c","src/format/parsed.rs":"6d4453a5fedc753fae268e0f545fcc817d48f9ce6803231d1c83aeb1f626c138","src/format/scan.rs":"1846554a45c776d164239ec6874881c6c97468631a1c00c8c956630420ea408f","src/format/strftime.rs":"8bbe43ca06c8a5e71187431890a54ff1faeb3be990c0d9d8c2fd8ee8b5c1361f","src/lib.rs":"522163d278acefa80fd1af4afeec2fdd2648e13e3fbdde8e6d31214253b013f9","src/naive/date.rs":"8be6146b3c15c395a71d30449a799b6e4387d1cd515e7e849509b824161fa6e3","src/naive/datetime.rs":"fd0de90d13793e5a8ecf99f63fa27592a6add57ba39a116495f9b2b253323fce","src/naive/internals.rs":"8c1aa5ab3373e04b51d36ed1591d667e1450055208b8c44d3fb720a496722c57","src/naive/isoweek.rs":"a8c5ae43ee1b916a2f79d42230aea448bf85691d615c4d271fcf1809e763df01","src/naive/time.rs":"b83e4ae0a809badce9131a19e3c5c75dbb823db4ef2f79d50bd522126ba0b48f","src/offset/fixed.rs":"19b97271300b821407756e14f64a42d48eb25a71c7011b2967885e4946e089ef","src/offset/local.rs":"9bc3af0ebf35a49858647302ac7e44abe344cbb76819d273311e50d234d1e6b2","src/offset/mod.rs":"a1036f75fc686603b216f9bb45b1689c8b34198b617b204800ceafb87b66ec45","src/offset/utc.rs":"2940ade0e834a9e1247600e92d38ee7bb11653f2d267862f99f292f617e25ca4","src/oldtime.rs":"780bc4ae5652affa8f7020580bb5977e9f32b304b0905c742124fd87a82ae50e","src/round.rs":"f7ae453ec0caacffc23bc0bad38b2c59b616c097ccaa0a15c0e7bcb8d1e1aed3","src/sys.rs":"4a3e8a96a2060e7df82c57405a5de4fffa54132a77fdd919970d51f3c43442cb","src/sys/stub.rs":"78babcdbe867ce5978bd69935e141ee15313ee7d90edce52355a19ab906a019b","src/sys/unix.rs":"c838ba088423c2b26643bd7191fe1914f099c14d632c9874f236c9c2c9dbd2d6","src/sys/windows.rs":"5c19383e9ffe11c16102008b5984dbd3455fd4003df15ac1565720c0a5edfac8","tests/wasm.rs":"d152681d5a79d9bbb69684433388bb8d7ca90e181387d4690cf5bda8cf25d17f"},"package":"670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"}
\ No newline at end of file +{"files":{"AUTHORS.txt":"8bfaf8f8599111cd6eef4408a55b98ba188611d1bcab0295e5b6a35def1343df","CHANGELOG.md":"1cc518c80946bc676a8c691428abe1cdf5a2e4e210b3cacc264b2efc5489d566","Cargo.toml":"d43fb7f28cbbd4c4e5410fdca9b5d22f8fb6ff8ae880f09038a0fb20c7916a13","LICENSE.txt":"46610329ff0b38effb9cb05979ff1ef761e465fed96b2eaca39e439d00129fd7","Makefile":"1a85008275ee7bf7cc0f0c054b1b393f34a4e614a28ebf3418c9d6b6e53b1b2c","README.md":"9260b662c38f931780e5bf89c837e351dada72affc29cacc9cfea6c558003845","appveyor.yml":"bcfcff73097a37be373e91934294cfea31fbdfe7a4b01a83bcbc64a8a56c3971","benches/chrono.rs":"a18e8f87e80d7e32cc4a1103d7c075b868ff3018eec2a09cc3b4c80e773fa987","benches/serde.rs":"0cebd95c96f0ddf0f7b6ca2d4a4fa307c7ccc691ee5eb090556f89807120eb9f","clippy.toml":"0ee2428866ddc7519d178a46d9073399e28b3c879a9d14658a1e7c5c023c3a7c","deny.toml":"4189fcb1125499ac851c0bd29f3f27a614b28573fa3299f712ee4bdace897dd3","rustfmt.toml":"f74204a6f92aa7422a16ecb2ffe2d5bae0f123b778d08b5db1a398a3c9ca4306","src/date.rs":"4929ac7b68a3d26ae7f94e1b375017a08a3f80581432f69c6e068832377a0ff6","src/datetime/mod.rs":"8eee73e7fe6e903e05b8d39acdb1e835d9163f6e1638d8f0ba74de7ea8ec1fd1","src/datetime/rustc_serialize.rs":"8cef7bba6aec9e75679c78b5544f22be41989c15e253fb739b76fcc4c9ee9fbe","src/datetime/serde.rs":"85d8bd79ded20fe68d7b8b68889c19a9f9dad5af6620885554190365d58967f2","src/datetime/tests.rs":"80c6e770c8dabebfee45ee67ede5a7418bdae24b5de9957bb15ecaca630e7c33","src/format/locales.rs":"8c14cb93c68b747947b74ab2933aae53c8a0edd009ff16815e756015fbea6e9f","src/format/mod.rs":"abf92d8f13683d13167a094add4a2908cb1a2cba0ec6867018e93ce4dbc5df8d","src/format/parse.rs":"74aeac3d34385497901a889f0ed55faea9a489391e9db1055e6bd1af1819d6e8","src/format/parsed.rs":"999fadd3e787b4c69b2f2471247fbfce32951156705bac20170da5aed76c2b98","src/format/scan.rs":"392c12418cd0cc3b570ea68542db9281d01f1ec595e95005d743d9287752e0ff","src/format/strftime.rs":"a10f904e4a86748657f2a8f4fe5fc0f44bf99275311ac8ac50b821208e41e891","src/lib.rs":"935de4f470a6189b92ca4a78ea14b149d3d418fbd4abdfb1b90f84c4bfcfe480","src/month.rs":"198802cc8a6dfd6614842e81729eef2fa9e6f5b2d418bad64e4bb1a49fcd3c21","src/naive/date.rs":"803664c44eef75e2f0bbc8a8174679208146d01fc3da18b1036d6fabd7ca0f46","src/naive/datetime/mod.rs":"ab619388eaa0659934c71fdc06b51fbd0f0250fe0ad6bfd88d6e76b090ab4d1e","src/naive/datetime/rustc_serialize.rs":"50491c855583694f01aed7d577c43dee9adf9bd548377b6707b4d596d887e0b2","src/naive/datetime/serde.rs":"8f677be5ece94f799219b66d018bd9dc82471c2192e2374c9d904ceabf982686","src/naive/datetime/tests.rs":"9bfbe494525fa1c8943c22c985bb9ce358459a97b8939fbdb8540b45fc6c2c8a","src/naive/internals.rs":"c73325b4f63ed221436643ebd58935004f8f76b4aa25a20a0703a2ebfc2a6c12","src/naive/isoweek.rs":"6c34cda15d21bdc095393c1de5bfaefa9611e6c35417aa21af82c4b44b4d6765","src/naive/mod.rs":"743e0ba276a235baa536005108e11597c1ca4ee100ed86e09cbad2216a91939d","src/naive/time/mod.rs":"c8cff488deb75943f02fe73e905fe16cc63cea52c84a6d8a1c5f2c7592338a51","src/naive/time/rustc_serialize.rs":"ce93c81341f35e29ec32a43644fd892a27823643b8ac2f010194824f434180ca","src/naive/time/serde.rs":"a89c1f0d127e89793d8bc1b786996babf02d5aa0a2d859d53a6d3ad02399d9a1","src/naive/time/tests.rs":"b8586903b65c1b050253c04bc9770f7889bfd4db87d02143a2b7fddbecf56dea","src/offset/fixed.rs":"9ac52822a66eac1f7eab92c8c77235573a40a5ca0b58c17e7894df51ae8edaa4","src/offset/local/mod.rs":"7160b6db6efcf06b6604cba363323f741c9dd8b235b2bd062b70eeb19f5d07b6","src/offset/local/stub.rs":"c753e9f45b85834e834191a38802c1105098ae17c56c31c2d99be42c50e6247c","src/offset/local/tz_info/mod.rs":"3488ac6811ea2a39418ff22f0de282ebddba4620dc916595d8b250d50d0bfb20","src/offset/local/tz_info/parser.rs":"47a569bf39eec6121ab9cd69d8fafb679be463d1e919e9d81a7d523667868136","src/offset/local/tz_info/rule.rs":"a88ad07f638ead425ae2dcc1aa76842b44a9f939c9f736cfa6fe2189e2b9f2a5","src/offset/local/tz_info/timezone.rs":"7dee5dd24c5bec63b2e94e3355d3f7e1ed5055771ffb6b70a449bd8f6b22fba8","src/offset/local/unix.rs":"e854d1cb52e9d18c4161ed660b03c62bc8d05418f85c746e2799dd1bdf7ed105","src/offset/local/windows.rs":"edda38f1f78f2392e0e89d01f6f06454ed8f1f269df0505d533107eeed9e713f","src/offset/mod.rs":"f0d486ab04c803a887b6b775e7f50a0b341b050497d3b4df67abc211daaa41f7","src/offset/utc.rs":"e15afd6a7aba6d2706d262f75b6e6b38e4536af0611b077446da04b06f40be6e","src/oldtime.rs":"8a4a0e1c932a04a30aa306bafdaf2457ffdbcd7f735c9b5cc365e56fdcba2ddc","src/round.rs":"3b70e5a88ea00ab2b70307b4b19c9cd1b42a8bf3356f0f63e4bd4696f46f3a84","src/traits.rs":"48d78381164232c13449dbd229b653ad05215ab44c7901fb026da94938d2ec9d","src/weekday.rs":"61891524fb27ad43d751b47dd4c0115b7f6d4ce509801953d03b8e44afd3e31a","tests/dateutils.rs":"eeff9ce9bf705818690af811f8611990eb8c580756b10f4d8c1691517a8ff969","tests/wasm.rs":"026c36013894a2a55794b4510f893eabeae89c2600b578ea428473ad339db333"},"package":"4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"}
\ No newline at end of file diff --git a/vendor/chrono/AUTHORS.txt b/vendor/chrono/AUTHORS.txt index 9501a9d05..caff57915 100644 --- a/vendor/chrono/AUTHORS.txt +++ b/vendor/chrono/AUTHORS.txt @@ -22,6 +22,7 @@ Eunchong Yu <kroisse@gmail.com> Frans Skarman <frans.skarman@gmail.com> Huon Wilson <dbau.pp+github@gmail.com> Igor Gnatenko <ignatenko@redhat.com> +Jake Vossen <jake@vossen.dev> Jim Turner <jturner314@gmail.com> Jisoo Park <xxxyel@gmail.com> Joe Wilm <joe@jwilm.com> @@ -39,3 +40,4 @@ Steve Klabnik <steve@steveklabnik.com> Tom Gallacher <tomgallacher23@gmail.com> klutzy <klutzytheklutzy@gmail.com> kud1ing <github@kudling.de> +Yohan Boogaert <yozhgoor@outlook.com> diff --git a/vendor/chrono/CHANGELOG.md b/vendor/chrono/CHANGELOG.md index be289ae88..1e6f6f935 100644 --- a/vendor/chrono/CHANGELOG.md +++ b/vendor/chrono/CHANGELOG.md @@ -1,17 +1,9 @@ ChangeLog for Chrono ==================== -This documents all notable changes to [Chrono](https://github.com/chronotope/chrono). - -Chrono obeys the principle of [Semantic Versioning](http://semver.org/), with one caveat: we may -move previously-existing code behind a feature gate and put it behind a new feature. This new -feature will always be placed in the `previously-default` feature, which you can use to prevent -breakage if you use `no-default-features`. - -There were/are numerous minor versions before 1.0 due to the language changes. -Versions with only mechanical changes will be omitted from the following list. - -## 0.4.20 (unreleased) +This documents notable changes to [Chrono](https://github.com/chronotope/chrono) +up to and including version 0.4.19. For later releases, please review the +release notes on [GitHub](https://github.com/chronotope/chrono/releases). ## 0.4.19 @@ -737,4 +729,3 @@ and replaced by 0.2.25 very shortly. Duh.) ## 0.1.0 (2014-11-20) The initial version that was available to `crates.io`. - diff --git a/vendor/chrono/Cargo.toml b/vendor/chrono/Cargo.toml index 2d3e76433..eaf0e6945 100644 --- a/vendor/chrono/Cargo.toml +++ b/vendor/chrono/Cargo.toml @@ -3,28 +3,36 @@ # 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 +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] +edition = "2018" name = "chrono" -version = "0.4.19" -authors = ["Kang Seonghoon <public+rust@mearie.org>", "Brandon W Maister <quodlibetor@gmail.com>"] -exclude = ["/ci/*", "/.travis.yml", "/appveyor.yml", "/Makefile"] +version = "0.4.24" +exclude = ["/ci/*"] description = "Date and time library for Rust" homepage = "https://github.com/chronotope/chrono" documentation = "https://docs.rs/chrono/" readme = "README.md" -keywords = ["date", "time", "calendar"] +keywords = [ + "date", + "time", + "calendar", +] categories = ["date-and-time"] license = "MIT/Apache-2.0" repository = "https://github.com/chronotope/chrono" + [package.metadata.docs.rs] features = ["serde"] +rustdoc-args = [ + "--cfg", + "docsrs", +] [package.metadata.playground] features = ["serde"] @@ -40,9 +48,18 @@ required-features = ["__internal_bench"] [[bench]] name = "serde" harness = false -required-features = ["serde"] -[dependencies.libc] -version = "0.2.69" +required-features = [ + "__internal_bench", + "serde", +] + +[dependencies.arbitrary] +version = "1.0.0" +features = ["derive"] +optional = true + +[dependencies.criterion] +version = "0.4.0" optional = true [dependencies.num-integer] @@ -57,6 +74,10 @@ default-features = false version = "0.5.2" optional = true +[dependencies.rkyv] +version = "0.7" +optional = true + [dependencies.rustc-serialize] version = "0.3.20" optional = true @@ -69,11 +90,9 @@ default-features = false [dependencies.time] version = "0.1.43" optional = true -[dev-dependencies.bincode] -version = "0.8.0" -[dev-dependencies.criterion] -version = "0.3" +[dev-dependencies.bincode] +version = "1.3.0" [dev-dependencies.doc-comment] version = "0.3" @@ -91,14 +110,31 @@ version = "1" [features] __doctest = [] -__internal_bench = [] +__internal_bench = ["criterion"] alloc = [] -clock = ["libc", "std", "winapi"] -default = ["clock", "std", "oldtime"] +clock = [ + "std", + "winapi", + "iana-time-zone", +] +default = [ + "clock", + "std", + "oldtime", + "wasmbind", +] +libc = [] oldtime = ["time"] std = [] -unstable-locales = ["pure-rust-locales", "alloc"] -wasmbind = ["wasm-bindgen", "js-sys"] +unstable-locales = [ + "pure-rust-locales", + "alloc", +] +wasmbind = [ + "wasm-bindgen", + "js-sys", +] + [target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.js-sys] version = "0.3" optional = true @@ -106,14 +142,21 @@ optional = true [target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.wasm-bindgen] version = "0.2" optional = true + [target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dev-dependencies.wasm-bindgen-test] version = "0.3" + +[target."cfg(unix)".dependencies.iana-time-zone] +version = "0.1.45" +features = ["fallback"] +optional = true + [target."cfg(windows)".dependencies.winapi] version = "0.3.0" -features = ["std", "minwinbase", "minwindef", "timezoneapi"] +features = [ + "std", + "minwinbase", + "minwindef", + "timezoneapi", +] optional = true -[badges.appveyor] -repository = "chronotope/chrono" - -[badges.travis-ci] -repository = "chronotope/chrono" diff --git a/vendor/chrono/Makefile b/vendor/chrono/Makefile new file mode 100644 index 000000000..63aef15ac --- /dev/null +++ b/vendor/chrono/Makefile @@ -0,0 +1,26 @@ +# this Makefile is mostly for the packaging convenience. +# casual users should use `cargo` to retrieve the appropriate version of Chrono. + +CHANNEL=stable + +.PHONY: all +all: + @echo 'Try `cargo build` instead.' + +.PHONY: authors +authors: + echo 'Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,' > AUTHORS.txt + echo 'and also the following people (in ascending order):' >> AUTHORS.txt + echo >> AUTHORS.txt + git log --format='%aN <%aE>' | grep -v 'Kang Seonghoon' | sort -u >> AUTHORS.txt + +.PHONY: readme README.md +readme: README.md + +.PHONY: test +test: + CHANNEL=$(CHANNEL) ./ci/travis.sh + +.PHONY: doc +doc: authors readme + cargo doc --features 'serde rustc-serialize bincode' diff --git a/vendor/chrono/README.md b/vendor/chrono/README.md index 5a5a74b42..ad9591889 100644 --- a/vendor/chrono/README.md +++ b/vendor/chrono/README.md @@ -6,7 +6,7 @@ [![Chrono on docs.rs][docsrs-image]][docsrs] [![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter] -[gh-image]: https://github.com/chronotope/chrono/workflows/test/badge.svg +[gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg [gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest [cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio]: https://crates.io/crates/chrono @@ -31,365 +31,6 @@ which Chrono builds upon and should acknowledge: * Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs) * Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime) -Any significant changes to Chrono are documented in -the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) file. - - -## Usage - -Put this in your `Cargo.toml`: - -```toml -[dependencies] -chrono = "0.4" -``` - -### Features - -Chrono supports various runtime environments and operating systems, and has -several features that may be enabled or disabled. - -Default features: - -- `alloc`: Enable features that depend on allocation (primarily string formatting) -- `std`: Enables functionality that depends on the standard library. This - is a superset of `alloc` and adds interoperation with standard library types - and traits. -- `clock`: enables reading the system time (`now`), independent of whether - `std::time::SystemTime` is present, depends on having a libc. - -Optional features: - -- `wasmbind`: Enable integration with [wasm-bindgen][] and its `js-sys` project -- [`serde`][]: Enable serialization/deserialization via serde. -- `unstable-locales`: Enable localization. This adds various methods with a - `_localized` suffix. The implementation and API may change or even be - removed in a patch release. Feedback welcome. - -[`serde`]: https://github.com/serde-rs/serde -[wasm-bindgen]: https://github.com/rustwasm/wasm-bindgen - -See the [cargo docs][] for examples of specifying features. - -[cargo docs]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features - -## Overview - -### Duration - -Chrono currently uses its own [`Duration`] type to represent the magnitude -of a time span. Since this has the same name as the newer, standard type for -duration, the reference will refer this type as `OldDuration`. - -Note that this is an "accurate" duration represented as seconds and -nanoseconds and does not represent "nominal" components such as days or -months. - -When the `oldtime` feature is enabled, [`Duration`] is an alias for the -[`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) -type from v0.1 of the time crate. time v0.1 is deprecated, so new code -should disable the `oldtime` feature and use the `chrono::Duration` type -instead. The `oldtime` feature is enabled by default for backwards -compatibility, but future versions of Chrono are likely to remove the -feature entirely. - -Chrono does not yet natively support -the standard [`Duration`](https://doc.rust-lang.org/std/time/struct.Duration.html) type, -but it will be supported in the future. -Meanwhile you can convert between two types with -[`Duration::from_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.from_std) -and -[`Duration::to_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.to_std) -methods. - -### Date and Time - -Chrono provides a -[**`DateTime`**](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html) -type to represent a date and a time in a timezone. - -For more abstract moment-in-time tracking such as internal timekeeping -that is unconcerned with timezones, consider -[`time::SystemTime`](https://doc.rust-lang.org/std/time/struct.SystemTime.html), -which tracks your system clock, or -[`time::Instant`](https://doc.rust-lang.org/std/time/struct.Instant.html), which -is an opaque but monotonically-increasing representation of a moment in time. - -`DateTime` is timezone-aware and must be constructed from -the [**`TimeZone`**](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html) object, -which defines how the local date is converted to and back from the UTC date. -There are three well-known `TimeZone` implementations: - -* [**`Utc`**](https://docs.rs/chrono/0.4/chrono/offset/struct.Utc.html) specifies the UTC time zone. It is most efficient. - -* [**`Local`**](https://docs.rs/chrono/0.4/chrono/offset/struct.Local.html) specifies the system local time zone. - -* [**`FixedOffset`**](https://docs.rs/chrono/0.4/chrono/offset/struct.FixedOffset.html) specifies - an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. - This often results from the parsed textual date and time. - Since it stores the most information and does not depend on the system environment, - you would want to normalize other `TimeZone`s into this type. - -`DateTime`s with different `TimeZone` types are distinct and do not mix, -but can be converted to each other using -the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.with_timezone) method. - -You can get the current date and time in the UTC time zone -([`Utc::now()`](https://docs.rs/chrono/0.4/chrono/offset/struct.Utc.html#method.now)) -or in the local time zone -([`Local::now()`](https://docs.rs/chrono/0.4/chrono/offset/struct.Local.html#method.now)). - -```rust -use chrono::prelude::*; - -let utc: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z` -let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00` -``` - -Alternatively, you can create your own date and time. -This is a bit verbose due to Rust's lack of function and method overloading, -but in turn we get a rich combination of initialization methods. - -```rust -use chrono::prelude::*; -use chrono::offset::LocalResult; - -let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` -// July 8 is 188th day of the year 2014 (`o` for "ordinal") -assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11)); -// July 8 is Tuesday in ISO week 28 of the year 2014. -assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11)); - -let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z` -assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000)); -assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000)); - -// dynamic verification -assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), - LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33))); -assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); -assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); - -// other time zone objects can be used to construct a local datetime. -// obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. -let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); -let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12); -assert_eq!(dt, fixed_dt); -``` - -Various properties are available to the date and time, and can be altered individually. -Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4/chrono/trait.Datelike.html) and -[`Timelike`](https://docs.rs/chrono/0.4/chrono/trait.Timelike.html) which you should `use` before. -Addition and subtraction is also supported. -The following illustrates most supported operations to the date and time: - -```rust - -use chrono::prelude::*; -use chrono::Duration; - -// assume this returned `2014-11-28T21:45:59.324310806+09:00`: -let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806); - -// property accessors -assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); -assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls -assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59)); -assert_eq!(dt.weekday(), Weekday::Fri); -assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sun=7 -assert_eq!(dt.ordinal(), 332); // the day of year -assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1 - -// time zone accessor and manipulation -assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); -assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); -assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); - -// a sample of property manipulations (validates dynamically) -assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday -assert_eq!(dt.with_day(32), None); -assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE - -// arithmetic operations -let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); -let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8); -assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); -assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2)); -assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000), - Utc.ymd(2001, 9, 9).and_hms(1, 46, 40)); -assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000), - Utc.ymd(1938, 4, 24).and_hms(22, 13, 20)); -``` - -### Formatting and Parsing - -Formatting is done via the [`format`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.format) method, -which format is equivalent to the familiar `strftime` format. - -See [`format::strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers) -documentation for full syntax and list of specifiers. - -The default `to_string` method and `{:?}` specifier also give a reasonable representation. -Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.to_rfc2822) and -[`to_rfc3339`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.to_rfc3339) methods -for well-known formats. - -Chrono now also provides date formatting in almost any language without the -help of an additional C library. This functionality is under the feature -`unstable-locales`: - -```text -chrono { version = "0.4", features = ["unstable-locales"] -``` - -The `unstable-locales` feature requires and implies at least the `alloc` feature. - -```rust -use chrono::prelude::*; - -let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); -assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09"); -assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); -assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09"); -assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); - -assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC"); -assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000"); -assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00"); -assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); - -// Note that milli/nanoseconds are only printed if they are non-zero -let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1); -assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z"); -``` - -Parsing can be done with three methods: - -1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait - (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method - on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and - `DateTime<Local>` values. This parses what the `{:?}` - ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html)) - format specifier prints, and requires the offset to be present. - -2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_str) parses - a date and time with offsets and returns `DateTime<FixedOffset>`. - This should be used when the offset is a part of input and the caller cannot guess that. - It *cannot* be used when the offset can be missing. - [`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_rfc2822) - and - [`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.parse_from_rfc3339) - are similar but for well-known formats. - -3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is - similar but returns `DateTime` of given offset. - When the explicit offset is missing from the input, it simply uses given offset. - It issues an error when the input contains an explicit offset different - from the current offset. - -More detailed control over the parsing process is available via -[`format`](https://docs.rs/chrono/0.4/chrono/format/index.html) module. - -```rust -use chrono::prelude::*; - -let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); -let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); - -// method 1 -assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone())); -assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone())); -assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone())); - -// method 2 -assert_eq!(DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"), - Ok(fixed_dt.clone())); -assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), - Ok(fixed_dt.clone())); -assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); - -// method 3 -assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone())); -assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone())); - -// oops, the year is missing! -assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); -// oops, the format string does not include the year at all! -assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); -// oops, the weekday is incorrect! -assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); -``` - -Again : See [`format::strftime`](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html#specifiers) -documentation for full syntax and list of specifiers. - -### Conversion from and to EPOCH timestamps - -Use [`Utc.timestamp(seconds, nanoseconds)`](https://docs.rs/chrono/0.4/chrono/offset/trait.TimeZone.html#method.timestamp) -to construct a [`DateTime<Utc>`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html) from a UNIX timestamp -(seconds, nanoseconds that passed since January 1st 1970). - -Use [`DateTime.timestamp`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.timestamp) to get the timestamp (in seconds) -from a [`DateTime`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html). Additionally, you can use -[`DateTime.timestamp_subsec_nanos`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.timestamp_subsec_nanos) -to get the number of additional number of nanoseconds. - -```rust -// We need the trait in scope to use Utc::timestamp(). -use chrono::{DateTime, TimeZone, Utc}; - -// Construct a datetime from epoch: -let dt = Utc.timestamp(1_500_000_000, 0); -assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); - -// Get epoch value from a datetime: -let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); -assert_eq!(dt.timestamp(), 1_500_000_000); -``` - -### Individual date - -Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4/chrono/struct.Date.html)). -It also has time zones attached, and have to be constructed via time zones. -Most operations available to `DateTime` are also available to `Date` whenever appropriate. - -```rust -use chrono::prelude::*; -use chrono::offset::LocalResult; - -assert_eq!(Utc::today(), Utc::now().date()); -assert_eq!(Local::today(), Local::now().date()); - -assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri); -assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None); -assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), - "070809"); -``` - -There is no timezone-aware `Time` due to the lack of usefulness and also the complexity. - -`DateTime` has [`date`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.date) method -which returns a `Date` which represents its date component. -There is also a [`time`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.time) method, -which simply returns a naive local time described below. - -### Naive date and time - -Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` -as [**`NaiveDate`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDate.html), -[**`NaiveTime`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveTime.html) and -[**`NaiveDateTime`**](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveDateTime.html) respectively. - -They have almost equivalent interfaces as their timezone-aware twins, -but are not associated to time zones obviously and can be quite low-level. -They are mostly useful for building blocks for higher-level types. - -Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -[`naive_local`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.naive_local) returns -a view to the naive local time, -and [`naive_utc`](https://docs.rs/chrono/0.4/chrono/struct.DateTime.html#method.naive_utc) returns -a view to the naive UTC time. - ## Limitations Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported. @@ -408,7 +49,7 @@ if you want. Chrono inherently does not support an inaccurate or partial date and time representation. Any operation that can be ambiguous will return `None` in such cases. For example, "a month later" of 2014-01-30 is not well-defined -and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. +and consequently `Utc.ymd_opt(2014, 1, 30).unwrap().with_month(2)` returns `None`. Non ISO week handling is not yet supported. For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext) @@ -416,4 +57,3 @@ crate ([sources](https://github.com/bcourtine/chrono-ext/)). Advanced time zone handling is not yet supported. For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead. - diff --git a/vendor/chrono/appveyor.yml b/vendor/chrono/appveyor.yml new file mode 100644 index 000000000..6057e851e --- /dev/null +++ b/vendor/chrono/appveyor.yml @@ -0,0 +1,12 @@ +# TODO: delete this without breaking all PRs +environment: + matrix: + - TARGET: nightly-i686-pc-windows-gnu +matrix: + allow_failures: + - channel: nightly + +build: false + +test_script: + - echo "stub" diff --git a/vendor/chrono/benches/chrono.rs b/vendor/chrono/benches/chrono.rs index 1c640634a..246271b81 100644 --- a/vendor/chrono/benches/chrono.rs +++ b/vendor/chrono/benches/chrono.rs @@ -1,12 +1,10 @@ //! Benchmarks for chrono that just depend on std - -extern crate chrono; -extern crate criterion; +#![cfg(feature = "__internal_bench")] use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use chrono::prelude::*; -use chrono::{DateTime, FixedOffset, Utc, __BenchYearFlags}; +use chrono::{DateTime, FixedOffset, Local, Utc, __BenchYearFlags}; fn bench_datetime_parse_from_rfc2822(c: &mut Criterion) { c.bench_function("bench_datetime_parse_from_rfc2822", |b| { @@ -37,14 +35,28 @@ fn bench_datetime_from_str(c: &mut Criterion) { } fn bench_datetime_to_rfc2822(c: &mut Criterion) { - let pst = FixedOffset::east(8 * 60 * 60); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000); + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_000) + .unwrap(), + ) + .unwrap(); c.bench_function("bench_datetime_to_rfc2822", |b| b.iter(|| black_box(dt).to_rfc2822())); } fn bench_datetime_to_rfc3339(c: &mut Criterion) { - let pst = FixedOffset::east(8 * 60 * 60); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000); + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_000) + .unwrap(), + ) + .unwrap(); c.bench_function("bench_datetime_to_rfc3339", |b| b.iter(|| black_box(dt).to_rfc3339())); } @@ -58,6 +70,14 @@ fn bench_year_flags_from_year(c: &mut Criterion) { }); } +fn bench_get_local_time(c: &mut Criterion) { + c.bench_function("bench_get_local_time", |b| { + b.iter(|| { + let _ = Local::now(); + }) + }); +} + /// Returns the number of multiples of `div` in the range `start..end`. /// /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the @@ -92,7 +112,7 @@ fn num_days_from_ce_alt<Date: Datelike>(date: &Date) -> i32 { fn bench_num_days_from_ce(c: &mut Criterion) { let mut group = c.benchmark_group("num_days_from_ce"); for year in &[1, 500, 2000, 2019] { - let d = NaiveDate::from_ymd(*year, 1, 1); + let d = NaiveDate::from_ymd_opt(*year, 1, 1).unwrap(); group.bench_with_input(BenchmarkId::new("new", year), &d, |b, y| { b.iter(|| num_days_from_ce_alt(y)) }); @@ -111,6 +131,7 @@ criterion_group!( bench_datetime_to_rfc3339, bench_year_flags_from_year, bench_num_days_from_ce, + bench_get_local_time, ); criterion_main!(benches); diff --git a/vendor/chrono/benches/serde.rs b/vendor/chrono/benches/serde.rs index 860b06e1a..e9de4408e 100644 --- a/vendor/chrono/benches/serde.rs +++ b/vendor/chrono/benches/serde.rs @@ -1,5 +1,4 @@ -extern crate chrono; -extern crate criterion; +#![cfg(feature = "__internal_bench")] use criterion::{black_box, criterion_group, criterion_main, Criterion}; diff --git a/vendor/chrono/clippy.toml b/vendor/chrono/clippy.toml new file mode 100644 index 000000000..749c3b58a --- /dev/null +++ b/vendor/chrono/clippy.toml @@ -0,0 +1 @@ +msrv = "1.38" diff --git a/vendor/chrono/deny.toml b/vendor/chrono/deny.toml new file mode 100644 index 000000000..13b9fac5f --- /dev/null +++ b/vendor/chrono/deny.toml @@ -0,0 +1,13 @@ +[licenses] +allow-osi-fsf-free = "either" +copyleft = "deny" + +[advisories] +ignore = [ + "RUSTSEC-2020-0071", # time 0.1, doesn't affect the API we use + "RUSTSEC-2021-0145", # atty (dev-deps only, dependency of criterion) + "RUSTSEC-2022-0004", # rustc_serialize, cannot remove due to compatibility +] +unmaintained = "deny" +unsound = "deny" +yanked = "deny" diff --git a/vendor/chrono/src/date.rs b/vendor/chrono/src/date.rs index 0012d3604..bad4bfbb8 100644 --- a/vendor/chrono/src/date.rs +++ b/vendor/chrono/src/date.rs @@ -2,57 +2,74 @@ // See README.md and LICENSE.txt for details. //! ISO 8601 calendar date with time zone. +#![allow(deprecated)] #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::cmp::Ordering; -use core::ops::{Add, Sub}; +use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::{fmt, hash}; -use oldtime::Duration as OldDuration; + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; #[cfg(feature = "unstable-locales")] -use format::Locale; +use crate::format::Locale; #[cfg(any(feature = "alloc", feature = "std", test))] -use format::{DelayedFormat, Item, StrftimeItems}; -use naive::{self, IsoWeek, NaiveDate, NaiveTime}; -use offset::{TimeZone, Utc}; -use DateTime; -use {Datelike, Weekday}; +use crate::format::{DelayedFormat, Item, StrftimeItems}; +use crate::naive::{IsoWeek, NaiveDate, NaiveTime}; +use crate::offset::{TimeZone, Utc}; +use crate::oldtime::Duration as OldDuration; +use crate::DateTime; +use crate::{Datelike, Weekday}; /// ISO 8601 calendar date with time zone. /// -/// This type should be considered ambiguous at best, -/// due to the inherent lack of precision required for the time zone resolution. -/// For serialization and deserialization uses, it is best to use `NaiveDate` instead. +/// You almost certainly want to be using a [`NaiveDate`] instead of this type. +/// +/// This type primarily exists to aid in the construction of DateTimes that +/// have a timezone by way of the [`TimeZone`] datelike constructors (e.g. +/// [`TimeZone::ymd`]). +/// +/// This type should be considered ambiguous at best, due to the inherent lack +/// of precision required for the time zone resolution. +/// /// There are some guarantees on the usage of `Date<Tz>`: /// -/// - If properly constructed via `TimeZone::ymd` and others without an error, +/// - If properly constructed via [`TimeZone::ymd`] and others without an error, /// the corresponding local date should exist for at least a moment. /// (It may still have a gap from the offset changes.) /// -/// - The `TimeZone` is free to assign *any* `Offset` to the local date, -/// as long as that offset did occur in given day. +/// - The `TimeZone` is free to assign *any* [`Offset`](crate::offset::Offset) to the +/// local date, as long as that offset did occur in given day. +/// /// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`, /// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00` /// but *not* `2015-03-08+00:00` and others. /// -/// - Once constructed as a full `DateTime`, -/// `DateTime::date` and other associated methods should return those for the original `Date`. -/// For example, if `dt = tz.ymd(y,m,d).hms(h,n,s)` were valid, `dt.date() == tz.ymd(y,m,d)`. +/// - Once constructed as a full `DateTime`, [`DateTime::date`] and other associated +/// methods should return those for the original `Date`. For example, if `dt = +/// tz.ymd_opt(y,m,d).unwrap().hms(h,n,s)` were valid, `dt.date() == tz.ymd_opt(y,m,d).unwrap()`. /// /// - The date is timezone-agnostic up to one day (i.e. practically always), /// so the local date and UTC date should be equal for most cases /// even though the raw calculation between `NaiveDate` and `Duration` may not. +#[deprecated(since = "0.4.23", note = "Use `NaiveDate` or `DateTime<Tz>` instead")] #[derive(Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct Date<Tz: TimeZone> { date: NaiveDate, offset: Tz::Offset, } /// The minimum possible `Date`. -pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc }; +#[allow(deprecated)] +#[deprecated(since = "0.4.20", note = "Use Date::MIN_UTC instead")] +pub const MIN_DATE: Date<Utc> = Date::<Utc>::MIN_UTC; /// The maximum possible `Date`. -pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc }; +#[allow(deprecated)] +#[deprecated(since = "0.4.20", note = "Use Date::MAX_UTC instead")] +pub const MAX_DATE: Date<Utc> = Date::<Utc>::MAX_UTC; impl<Tz: TimeZone> Date<Tz> { /// Makes a new `Date` with given *UTC* date and offset. @@ -61,7 +78,7 @@ impl<Tz: TimeZone> Date<Tz> { // note: this constructor is purposely not named to `new` to discourage the direct usage. #[inline] pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> { - Date { date: date, offset: offset } + Date { date, offset } } /// Makes a new `DateTime` from the current date and given `NaiveTime`. @@ -78,6 +95,7 @@ impl<Tz: TimeZone> Date<Tz> { /// The offset in the current date is preserved. /// /// Panics on invalid hour, minute and/or second. + #[deprecated(since = "0.4.23", note = "Use and_hms_opt() instead")] #[inline] pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> { self.and_hms_opt(hour, min, sec).expect("invalid time") @@ -97,6 +115,7 @@ impl<Tz: TimeZone> Date<Tz> { /// The offset in the current date is preserved. /// /// Panics on invalid hour, minute, second and/or millisecond. + #[deprecated(since = "0.4.23", note = "Use and_hms_milli_opt() instead")] #[inline] pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> { self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") @@ -123,6 +142,7 @@ impl<Tz: TimeZone> Date<Tz> { /// The offset in the current date is preserved. /// /// Panics on invalid hour, minute, second and/or microsecond. + #[deprecated(since = "0.4.23", note = "Use and_hms_micro_opt() instead")] #[inline] pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> { self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") @@ -149,6 +169,7 @@ impl<Tz: TimeZone> Date<Tz> { /// The offset in the current date is preserved. /// /// Panics on invalid hour, minute, second and/or nanosecond. + #[deprecated(since = "0.4.23", note = "Use and_hms_nano_opt() instead")] #[inline] pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> { self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") @@ -173,6 +194,7 @@ impl<Tz: TimeZone> Date<Tz> { /// Makes a new `Date` for the next date. /// /// Panics when `self` is the last representable date. + #[deprecated(since = "0.4.23", note = "Use succ_opt() instead")] #[inline] pub fn succ(&self) -> Date<Tz> { self.succ_opt().expect("out of bound") @@ -189,6 +211,7 @@ impl<Tz: TimeZone> Date<Tz> { /// Makes a new `Date` for the prior date. /// /// Panics when `self` is the first representable date. + #[deprecated(since = "0.4.23", note = "Use pred_opt() instead")] #[inline] pub fn pred(&self) -> Date<Tz> { self.pred_opt().expect("out of bound") @@ -226,8 +249,8 @@ impl<Tz: TimeZone> Date<Tz> { /// Returns `None` when it will result in overflow. #[inline] pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> { - let date = try_opt!(self.date.checked_add_signed(rhs)); - Some(Date { date: date, offset: self.offset }) + let date = self.date.checked_add_signed(rhs)?; + Some(Date { date, offset: self.offset }) } /// Subtracts given `Duration` from the current date. @@ -235,8 +258,8 @@ impl<Tz: TimeZone> Date<Tz> { /// Returns `None` when it will result in overflow. #[inline] pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> { - let date = try_opt!(self.date.checked_sub_signed(rhs)); - Some(Date { date: date, offset: self.offset }) + let date = self.date.checked_sub_signed(rhs)?; + Some(Date { date, offset: self.offset }) } /// Subtracts another `Date` from the current date. @@ -264,6 +287,16 @@ impl<Tz: TimeZone> Date<Tz> { pub fn naive_local(&self) -> NaiveDate { self.date } + + /// Returns the number of whole years from the given `base` until `self`. + pub fn years_since(&self, base: Self) -> Option<u32> { + self.date.years_since(base.date) + } + + /// The minimum possible `Date`. + pub const MIN_UTC: Date<Utc> = Date { date: NaiveDate::MIN, offset: Utc }; + /// The maximum possible `Date`. + pub const MAX_UTC: Date<Utc> = Date { date: NaiveDate::MAX, offset: Utc }; } /// Maps the local date to other date with given conversion function. @@ -280,6 +313,7 @@ where { /// Formats the date with the specified formatting items. #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> where @@ -290,9 +324,10 @@ where } /// Formats the date with the specified format string. - /// See the [`format::strftime` module](./format/strftime/index.html) + /// See the [`crate::format::strftime`] module /// on the supported escape sequences. #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { self.format_with_items(StrftimeItems::new(fmt)) @@ -300,6 +335,7 @@ where /// Formats the date with the specified formatting items and locale. #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] #[inline] pub fn format_localized_with_items<'a, I, B>( &self, @@ -320,9 +356,10 @@ where } /// Formats the date with the specified format string and locale. - /// See the [`format::strftime` module](./format/strftime/index.html) + /// See the [`crate::format::strftime`] module /// on the supported escape sequences. #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] #[inline] pub fn format_localized<'a>( &self, @@ -446,6 +483,13 @@ impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> { } } +impl<Tz: TimeZone> AddAssign<OldDuration> for Date<Tz> { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + self.date = self.date.checked_add_signed(rhs).expect("`Date + Duration` overflowed"); + } +} + impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> { type Output = Date<Tz>; @@ -455,6 +499,13 @@ impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> { } } +impl<Tz: TimeZone> SubAssign<OldDuration> for Date<Tz> { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + self.date = self.date.checked_sub_signed(rhs).expect("`Date - Duration` overflowed"); + } +} + impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> { type Output = OldDuration; @@ -466,7 +517,8 @@ impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> { impl<Tz: TimeZone> fmt::Debug for Date<Tz> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}{:?}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + self.offset.fmt(f) } } @@ -475,6 +527,119 @@ where Tz::Offset: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}{}", self.naive_local(), self.offset) + self.naive_local().fmt(f)?; + self.offset.fmt(f) + } +} + +// Note that implementation of Arbitrary cannot be automatically derived for Date<Tz>, due to +// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary. +#[cfg(feature = "arbitrary")] +impl<'a, Tz> arbitrary::Arbitrary<'a> for Date<Tz> +where + Tz: TimeZone, + <Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Date<Tz>> { + let date = NaiveDate::arbitrary(u)?; + let offset = <Tz as TimeZone>::Offset::arbitrary(u)?; + Ok(Date::from_utc(date, offset)) + } +} + +#[cfg(test)] +mod tests { + use super::Date; + + use crate::oldtime::Duration; + use crate::{FixedOffset, NaiveDate, Utc}; + + #[cfg(feature = "clock")] + use crate::offset::{Local, TimeZone}; + + #[test] + #[cfg(feature = "clock")] + fn test_years_elapsed() { + const WEEKS_PER_YEAR: f32 = 52.1775; + + // This is always at least one year because 1 year = 52.1775 weeks. + let one_year_ago = Utc::today() - Duration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); + // A bit more than 2 years. + let two_year_ago = Utc::today() - Duration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); + + assert_eq!(Utc::today().years_since(one_year_ago), Some(1)); + assert_eq!(Utc::today().years_since(two_year_ago), Some(2)); + + // If the given DateTime is later than now, the function will always return 0. + let future = Utc::today() + Duration::weeks(12); + assert_eq!(Utc::today().years_since(future), None); + } + + #[test] + fn test_date_add_assign() { + let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); + let date = Date::<Utc>::from_utc(naivedate, Utc); + let mut date_add = date; + + date_add += Duration::days(5); + assert_eq!(date_add, date + Duration::days(5)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let date = date.with_timezone(&timezone); + let date_add = date_add.with_timezone(&timezone); + + assert_eq!(date_add, date + Duration::days(5)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let date = date.with_timezone(&timezone); + let date_add = date_add.with_timezone(&timezone); + + assert_eq!(date_add, date + Duration::days(5)); + } + + #[test] + #[cfg(feature = "clock")] + fn test_date_add_assign_local() { + let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); + + let date = Local.from_utc_date(&naivedate); + let mut date_add = date; + + date_add += Duration::days(5); + assert_eq!(date_add, date + Duration::days(5)); + } + + #[test] + fn test_date_sub_assign() { + let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); + let date = Date::<Utc>::from_utc(naivedate, Utc); + let mut date_sub = date; + + date_sub -= Duration::days(5); + assert_eq!(date_sub, date - Duration::days(5)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let date = date.with_timezone(&timezone); + let date_sub = date_sub.with_timezone(&timezone); + + assert_eq!(date_sub, date - Duration::days(5)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let date = date.with_timezone(&timezone); + let date_sub = date_sub.with_timezone(&timezone); + + assert_eq!(date_sub, date - Duration::days(5)); + } + + #[test] + #[cfg(feature = "clock")] + fn test_date_sub_assign_local() { + let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); + + let date = Local.from_utc_date(&naivedate); + let mut date_sub = date; + + date_sub -= Duration::days(5); + assert_eq!(date_sub, date - Duration::days(5)); } } diff --git a/vendor/chrono/src/datetime.rs b/vendor/chrono/src/datetime.rs deleted file mode 100644 index ecd464250..000000000 --- a/vendor/chrono/src/datetime.rs +++ /dev/null @@ -1,2589 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 date and time with time zone. - -use core::cmp::Ordering; -use core::ops::{Add, Sub}; -use core::{fmt, hash, str}; -use oldtime::Duration as OldDuration; -#[cfg(any(feature = "std", test))] -use std::time::{SystemTime, UNIX_EPOCH}; - -#[cfg(all(not(feature = "std"), feature = "alloc"))] -use alloc::string::{String, ToString}; -#[cfg(feature = "std")] -use std::string::ToString; - -#[cfg(any(feature = "alloc", feature = "std", test))] -use core::borrow::Borrow; -#[cfg(any(feature = "alloc", feature = "std", test))] -use format::DelayedFormat; -#[cfg(feature = "unstable-locales")] -use format::Locale; -use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; -use format::{Fixed, Item}; -use naive::{self, IsoWeek, NaiveDateTime, NaiveTime}; -#[cfg(feature = "clock")] -use offset::Local; -use offset::{FixedOffset, Offset, TimeZone, Utc}; -use Date; -use {Datelike, Timelike, Weekday}; - -/// Specific formatting options for seconds. This may be extended in the -/// future, so exhaustive matching in external code is not recommended. -/// -/// See the `TimeZone::to_rfc3339_opts` function for usage. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum SecondsFormat { - /// Format whole seconds only, with no decimal point nor subseconds. - Secs, - - /// Use fixed 3 subsecond digits. This corresponds to - /// [Fixed::Nanosecond3](format/enum.Fixed.html#variant.Nanosecond3). - Millis, - - /// Use fixed 6 subsecond digits. This corresponds to - /// [Fixed::Nanosecond6](format/enum.Fixed.html#variant.Nanosecond6). - Micros, - - /// Use fixed 9 subsecond digits. This corresponds to - /// [Fixed::Nanosecond9](format/enum.Fixed.html#variant.Nanosecond9). - Nanos, - - /// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to - /// display all available non-zero sub-second digits. This corresponds to - /// [Fixed::Nanosecond](format/enum.Fixed.html#variant.Nanosecond). - AutoSi, - - // Do not match against this. - #[doc(hidden)] - __NonExhaustive, -} - -/// ISO 8601 combined date and time with time zone. -/// -/// There are some constructors implemented here (the `from_*` methods), but -/// the general-purpose constructors are all via the methods on the -/// [`TimeZone`](./offset/trait.TimeZone.html) implementations. -#[derive(Clone)] -pub struct DateTime<Tz: TimeZone> { - datetime: NaiveDateTime, - offset: Tz::Offset, -} - -/// The minimum possible `DateTime<Utc>`. -pub const MIN_DATETIME: DateTime<Utc> = DateTime { datetime: naive::MIN_DATETIME, offset: Utc }; -/// The maximum possible `DateTime<Utc>`. -pub const MAX_DATETIME: DateTime<Utc> = DateTime { datetime: naive::MAX_DATETIME, offset: Utc }; - -impl<Tz: TimeZone> DateTime<Tz> { - /// Makes a new `DateTime` with given *UTC* datetime and offset. - /// The local datetime should be constructed via the `TimeZone` trait. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; - /// - /// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); - /// assert_eq!(Utc.timestamp(61, 0), dt); - /// ~~~~ - // - // note: this constructor is purposely not named to `new` to discourage the direct usage. - #[inline] - pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> { - DateTime { datetime: datetime, offset: offset } - } - - /// Retrieves a date component. - #[inline] - pub fn date(&self) -> Date<Tz> { - Date::from_utc(self.naive_local().date(), self.offset.clone()) - } - - /// Retrieves a time component. - /// Unlike `date`, this is not associated to the time zone. - #[inline] - pub fn time(&self) -> NaiveTime { - self.datetime.time() + self.offset.fix() - } - - /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC - /// (aka "UNIX timestamp"). - #[inline] - pub fn timestamp(&self) -> i64 { - self.datetime.timestamp() - } - - /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC - /// - /// Note that this does reduce the number of years that can be represented - /// from ~584 Billion to ~584 Million. (If this is a problem, please file - /// an issue to let me know what domain needs millisecond precision over - /// billions of years, I'm curious.) - /// - /// # Example - /// - /// ~~~~ - /// use chrono::Utc; - /// use chrono::TimeZone; - /// - /// let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 444); - /// assert_eq!(dt.timestamp_millis(), 1_444); - /// - /// let dt = Utc.ymd(2001, 9, 9).and_hms_milli(1, 46, 40, 555); - /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); - /// ~~~~ - #[inline] - pub fn timestamp_millis(&self) -> i64 { - self.datetime.timestamp_millis() - } - - /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC - /// - /// Note that this does reduce the number of years that can be represented - /// from ~584 Billion to ~584. (If this is a problem, please file - /// an issue to let me know what domain needs nanosecond precision over - /// millennia, I'm curious.) - /// - /// # Example - /// - /// ~~~~ - /// use chrono::Utc; - /// use chrono::TimeZone; - /// - /// let dt = Utc.ymd(1970, 1, 1).and_hms_nano(0, 0, 1, 444); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); - /// - /// let dt = Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 40, 555); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); - /// ~~~~ - #[inline] - pub fn timestamp_nanos(&self) -> i64 { - self.datetime.timestamp_nanos() - } - - /// Returns the number of milliseconds since the last second boundary - /// - /// warning: in event of a leap second, this may exceed 999 - /// - /// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC - #[inline] - pub fn timestamp_subsec_millis(&self) -> u32 { - self.datetime.timestamp_subsec_millis() - } - - /// Returns the number of microseconds since the last second boundary - /// - /// warning: in event of a leap second, this may exceed 999_999 - /// - /// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC - #[inline] - pub fn timestamp_subsec_micros(&self) -> u32 { - self.datetime.timestamp_subsec_micros() - } - - /// Returns the number of nanoseconds since the last second boundary - /// - /// warning: in event of a leap second, this may exceed 999_999_999 - /// - /// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC - #[inline] - pub fn timestamp_subsec_nanos(&self) -> u32 { - self.datetime.timestamp_subsec_nanos() - } - - /// Retrieves an associated offset from UTC. - #[inline] - pub fn offset(&self) -> &Tz::Offset { - &self.offset - } - - /// Retrieves an associated time zone. - #[inline] - pub fn timezone(&self) -> Tz { - TimeZone::from_offset(&self.offset) - } - - /// Changes the associated time zone. - /// This does not change the actual `DateTime` (but will change the string representation). - #[inline] - pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> { - tz.from_utc_datetime(&self.datetime) - } - - /// Adds given `Duration` to the current date and time. - /// - /// Returns `None` when it will result in overflow. - #[inline] - pub fn checked_add_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { - let datetime = try_opt!(self.datetime.checked_add_signed(rhs)); - let tz = self.timezone(); - Some(tz.from_utc_datetime(&datetime)) - } - - /// Subtracts given `Duration` from the current date and time. - /// - /// Returns `None` when it will result in overflow. - #[inline] - pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { - let datetime = try_opt!(self.datetime.checked_sub_signed(rhs)); - let tz = self.timezone(); - Some(tz.from_utc_datetime(&datetime)) - } - - /// Subtracts another `DateTime` from the current date and time. - /// This does not overflow or underflow at all. - #[inline] - pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> OldDuration { - self.datetime.signed_duration_since(rhs.datetime) - } - - /// Returns a view to the naive UTC datetime. - #[inline] - pub fn naive_utc(&self) -> NaiveDateTime { - self.datetime - } - - /// Returns a view to the naive local datetime. - #[inline] - pub fn naive_local(&self) -> NaiveDateTime { - self.datetime + self.offset.fix() - } -} - -/// Convert a `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. -impl From<DateTime<Utc>> for DateTime<FixedOffset> { - /// Convert this `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. - /// - /// Conversion is done via [`DateTime::with_timezone`]. Note that the converted value returned by - /// this will be created with a fixed timezone offset of 0. - fn from(src: DateTime<Utc>) -> Self { - src.with_timezone(&FixedOffset::east(0)) - } -} - -/// Convert a `DateTime<Utc>` instance into a `DateTime<Local>` instance. -#[cfg(feature = "clock")] -impl From<DateTime<Utc>> for DateTime<Local> { - /// Convert this `DateTime<Utc>` instance into a `DateTime<Local>` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in timezones. - fn from(src: DateTime<Utc>) -> Self { - src.with_timezone(&Local) - } -} - -/// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. -impl From<DateTime<FixedOffset>> for DateTime<Utc> { - /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the timezone - /// difference. - fn from(src: DateTime<FixedOffset>) -> Self { - src.with_timezone(&Utc) - } -} - -/// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. -#[cfg(feature = "clock")] -impl From<DateTime<FixedOffset>> for DateTime<Local> { - /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`]. Returns the equivalent value in local - /// time. - fn from(src: DateTime<FixedOffset>) -> Self { - src.with_timezone(&Local) - } -} - -/// Convert a `DateTime<Local>` instance into a `DateTime<Utc>` instance. -#[cfg(feature = "clock")] -impl From<DateTime<Local>> for DateTime<Utc> { - /// Convert this `DateTime<Local>` instance into a `DateTime<Utc>` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in - /// timezones. - fn from(src: DateTime<Local>) -> Self { - src.with_timezone(&Utc) - } -} - -/// Convert a `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. -#[cfg(feature = "clock")] -impl From<DateTime<Local>> for DateTime<FixedOffset> { - /// Convert this `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`]. Note that the converted value returned - /// by this will be created with a fixed timezone offset of 0. - fn from(src: DateTime<Local>) -> Self { - src.with_timezone(&FixedOffset::east(0)) - } -} - -/// Maps the local datetime to other datetime with given conversion function. -fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz>> -where - F: FnMut(NaiveDateTime) -> Option<NaiveDateTime>, -{ - f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) -} - -impl DateTime<FixedOffset> { - /// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`, - /// then returns a new `DateTime` with a parsed `FixedOffset`. - /// - /// RFC 2822 is the internet message standard that specifices the - /// representation of times in HTTP and email headers. - /// - /// ``` - /// # use chrono::{DateTime, FixedOffset, TimeZone}; - /// assert_eq!( - /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), - /// FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9) - /// ); - /// ``` - pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> { - const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_datetime() - } - - /// Parses an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`, - /// then returns a new `DateTime` with a parsed `FixedOffset`. - /// - /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows some freedom - /// over the syntax and RFC 3339 exercises that freedom to rigidly define a fixed format. - pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> { - const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_datetime() - } - - /// Parses a string with the specified format string and - /// returns a new `DateTime` with a parsed `FixedOffset`. - /// See the [`format::strftime` module](./format/strftime/index.html) - /// on the supported escape sequences. - /// - /// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone. - /// - /// Note that this method *requires a timezone* in the string. See - /// [`NaiveDateTime::parse_from_str`](./naive/struct.NaiveDateTime.html#method.parse_from_str) - /// for a version that does not require a timezone in the to-be-parsed str. - /// - /// # Example - /// - /// ```rust - /// use chrono::{DateTime, FixedOffset, TimeZone}; - /// - /// let dt = DateTime::parse_from_str( - /// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); - /// assert_eq!(dt, Ok(FixedOffset::east(0).ymd(1983, 4, 13).and_hms_milli(12, 9, 14, 274))); - /// ``` - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_datetime() - } -} - -impl<Tz: TimeZone> DateTime<Tz> -where - Tz::Offset: fmt::Display, -{ - /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. - #[cfg(any(feature = "alloc", feature = "std", test))] - pub fn to_rfc2822(&self) -> String { - const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; - self.format_with_items(ITEMS.iter()).to_string() - } - - /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. - #[cfg(any(feature = "alloc", feature = "std", test))] - pub fn to_rfc3339(&self) -> String { - const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; - self.format_with_items(ITEMS.iter()).to_string() - } - - /// Return an RFC 3339 and ISO 8601 date and time string with subseconds - /// formatted as per a `SecondsFormat`. If passed `use_z` true and the - /// timezone is UTC (offset 0), use 'Z', as per - /// [Fixed::TimezoneOffsetColonZ](format/enum.Fixed.html#variant.TimezoneOffsetColonZ). - /// If passed `use_z` false, use - /// [Fixed::TimezoneOffsetColon](format/enum.Fixed.html#variant.TimezoneOffsetColon). - /// - /// # Examples - /// - /// ```rust - /// # use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc}; - /// let dt = Utc.ymd(2018, 1, 26).and_hms_micro(18, 30, 9, 453_829); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), - /// "2018-01-26T18:30:09.453+00:00"); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), - /// "2018-01-26T18:30:09.453Z"); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), - /// "2018-01-26T18:30:09Z"); - /// - /// let pst = FixedOffset::east(8 * 60 * 60); - /// let dt = pst.ymd(2018, 1, 26).and_hms_micro(10, 30, 9, 453_829); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), - /// "2018-01-26T10:30:09+08:00"); - /// ``` - #[cfg(any(feature = "alloc", feature = "std", test))] - pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String { - use format::Numeric::*; - use format::Pad::Zero; - use SecondsFormat::*; - - debug_assert!(secform != __NonExhaustive, "Do not use __NonExhaustive!"); - - const PREFIX: &'static [Item<'static>] = &[ - Item::Numeric(Year, Zero), - Item::Literal("-"), - Item::Numeric(Month, Zero), - Item::Literal("-"), - Item::Numeric(Day, Zero), - Item::Literal("T"), - Item::Numeric(Hour, Zero), - Item::Literal(":"), - Item::Numeric(Minute, Zero), - Item::Literal(":"), - Item::Numeric(Second, Zero), - ]; - - let ssitem = match secform { - Secs => None, - Millis => Some(Item::Fixed(Fixed::Nanosecond3)), - Micros => Some(Item::Fixed(Fixed::Nanosecond6)), - Nanos => Some(Item::Fixed(Fixed::Nanosecond9)), - AutoSi => Some(Item::Fixed(Fixed::Nanosecond)), - __NonExhaustive => unreachable!(), - }; - - let tzitem = Item::Fixed(if use_z { - Fixed::TimezoneOffsetColonZ - } else { - Fixed::TimezoneOffsetColon - }); - - match ssitem { - None => self.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(), - Some(s) => self.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(), - } - } - - /// Formats the combined date and time with the specified formatting items. - #[cfg(any(feature = "alloc", feature = "std", test))] - #[inline] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> - where - I: Iterator<Item = B> + Clone, - B: Borrow<Item<'a>>, - { - let local = self.naive_local(); - DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) - } - - /// Formats the combined date and time with the specified format string. - /// See the [`format::strftime` module](./format/strftime/index.html) - /// on the supported escape sequences. - #[cfg(any(feature = "alloc", feature = "std", test))] - #[inline] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Formats the combined date and time with the specified formatting items and locale. - #[cfg(feature = "unstable-locales")] - #[inline] - pub fn format_localized_with_items<'a, I, B>( - &self, - items: I, - locale: Locale, - ) -> DelayedFormat<I> - where - I: Iterator<Item = B> + Clone, - B: Borrow<Item<'a>>, - { - let local = self.naive_local(); - DelayedFormat::new_with_offset_and_locale( - Some(local.date()), - Some(local.time()), - &self.offset, - items, - locale, - ) - } - - /// Formats the combined date and time with the specified format string and locale. - /// See the [`format::strftime` module](./format/strftime/index.html) - /// on the supported escape sequences. - #[cfg(feature = "unstable-locales")] - #[inline] - pub fn format_localized<'a>( - &self, - fmt: &'a str, - locale: Locale, - ) -> DelayedFormat<StrftimeItems<'a>> { - self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) - } -} - -impl<Tz: TimeZone> Datelike for DateTime<Tz> { - #[inline] - fn year(&self) -> i32 { - self.naive_local().year() - } - #[inline] - fn month(&self) -> u32 { - self.naive_local().month() - } - #[inline] - fn month0(&self) -> u32 { - self.naive_local().month0() - } - #[inline] - fn day(&self) -> u32 { - self.naive_local().day() - } - #[inline] - fn day0(&self) -> u32 { - self.naive_local().day0() - } - #[inline] - fn ordinal(&self) -> u32 { - self.naive_local().ordinal() - } - #[inline] - fn ordinal0(&self) -> u32 { - self.naive_local().ordinal0() - } - #[inline] - fn weekday(&self) -> Weekday { - self.naive_local().weekday() - } - #[inline] - fn iso_week(&self) -> IsoWeek { - self.naive_local().iso_week() - } - - #[inline] - fn with_year(&self, year: i32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_year(year)) - } - - #[inline] - fn with_month(&self, month: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_month(month)) - } - - #[inline] - fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_month0(month0)) - } - - #[inline] - fn with_day(&self, day: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_day(day)) - } - - #[inline] - fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_day0(day0)) - } - - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_ordinal(ordinal)) - } - - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_ordinal0(ordinal0)) - } -} - -impl<Tz: TimeZone> Timelike for DateTime<Tz> { - #[inline] - fn hour(&self) -> u32 { - self.naive_local().hour() - } - #[inline] - fn minute(&self) -> u32 { - self.naive_local().minute() - } - #[inline] - fn second(&self) -> u32 { - self.naive_local().second() - } - #[inline] - fn nanosecond(&self) -> u32 { - self.naive_local().nanosecond() - } - - #[inline] - fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_hour(hour)) - } - - #[inline] - fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_minute(min)) - } - - #[inline] - fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_second(sec)) - } - - #[inline] - fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> { - map_local(self, |datetime| datetime.with_nanosecond(nano)) - } -} - -// we need them as automatic impls cannot handle associated types -impl<Tz: TimeZone> Copy for DateTime<Tz> where <Tz as TimeZone>::Offset: Copy {} -unsafe impl<Tz: TimeZone> Send for DateTime<Tz> where <Tz as TimeZone>::Offset: Send {} - -impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> { - fn eq(&self, other: &DateTime<Tz2>) -> bool { - self.datetime == other.datetime - } -} - -impl<Tz: TimeZone> Eq for DateTime<Tz> {} - -impl<Tz: TimeZone, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> { - /// Compare two DateTimes based on their true time, ignoring time zones - /// - /// # Example - /// - /// ``` - /// use chrono::prelude::*; - /// - /// let earlier = Utc.ymd(2015, 5, 15).and_hms(2, 0, 0).with_timezone(&FixedOffset::west(1 * 3600)); - /// let later = Utc.ymd(2015, 5, 15).and_hms(3, 0, 0).with_timezone(&FixedOffset::west(5 * 3600)); - /// - /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00"); - /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00"); - /// - /// assert!(later > earlier); - /// ``` - fn partial_cmp(&self, other: &DateTime<Tz2>) -> Option<Ordering> { - self.datetime.partial_cmp(&other.datetime) - } -} - -impl<Tz: TimeZone> Ord for DateTime<Tz> { - fn cmp(&self, other: &DateTime<Tz>) -> Ordering { - self.datetime.cmp(&other.datetime) - } -} - -impl<Tz: TimeZone> hash::Hash for DateTime<Tz> { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.datetime.hash(state) - } -} - -impl<Tz: TimeZone> Add<OldDuration> for DateTime<Tz> { - type Output = DateTime<Tz>; - - #[inline] - fn add(self, rhs: OldDuration) -> DateTime<Tz> { - self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed") - } -} - -impl<Tz: TimeZone> Sub<OldDuration> for DateTime<Tz> { - type Output = DateTime<Tz>; - - #[inline] - fn sub(self, rhs: OldDuration) -> DateTime<Tz> { - self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed") - } -} - -impl<Tz: TimeZone> Sub<DateTime<Tz>> for DateTime<Tz> { - type Output = OldDuration; - - #[inline] - fn sub(self, rhs: DateTime<Tz>) -> OldDuration { - self.signed_duration_since(rhs) - } -} - -impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}{:?}", self.naive_local(), self.offset) - } -} - -impl<Tz: TimeZone> fmt::Display for DateTime<Tz> -where - Tz::Offset: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.naive_local(), self.offset) - } -} - -impl str::FromStr for DateTime<Utc> { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult<DateTime<Utc>> { - s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&Utc)) - } -} - -#[cfg(feature = "clock")] -impl str::FromStr for DateTime<Local> { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult<DateTime<Local>> { - s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&Local)) - } -} - -#[cfg(any(feature = "std", test))] -impl From<SystemTime> for DateTime<Utc> { - fn from(t: SystemTime) -> DateTime<Utc> { - let (sec, nsec) = match t.duration_since(UNIX_EPOCH) { - Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()), - Err(e) => { - // unlikely but should be handled - let dur = e.duration(); - let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos()); - if nsec == 0 { - (-sec, 0) - } else { - (-sec - 1, 1_000_000_000 - nsec) - } - } - }; - Utc.timestamp(sec, nsec) - } -} - -#[cfg(feature = "clock")] -impl From<SystemTime> for DateTime<Local> { - fn from(t: SystemTime) -> DateTime<Local> { - DateTime::<Utc>::from(t).with_timezone(&Local) - } -} - -#[cfg(any(feature = "std", test))] -impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime { - fn from(dt: DateTime<Tz>) -> SystemTime { - use std::time::Duration; - - let sec = dt.timestamp(); - let nsec = dt.timestamp_subsec_nanos(); - if sec < 0 { - // unlikely but should be handled - UNIX_EPOCH - Duration::new(-sec as u64, 0) + Duration::new(0, nsec) - } else { - UNIX_EPOCH + Duration::new(sec as u64, nsec) - } - } -} - -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] -impl From<js_sys::Date> for DateTime<Utc> { - fn from(date: js_sys::Date) -> DateTime<Utc> { - DateTime::<Utc>::from(&date) - } -} - -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] -impl From<&js_sys::Date> for DateTime<Utc> { - fn from(date: &js_sys::Date) -> DateTime<Utc> { - let millisecs_since_unix_epoch: u64 = date.get_time() as u64; - let secs = millisecs_since_unix_epoch / 1000; - let nanos = 1_000_000 * (millisecs_since_unix_epoch % 1000); - let naive = NaiveDateTime::from_timestamp(secs as i64, nanos as u32); - DateTime::from_utc(naive, Utc) - } -} - -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] -impl From<DateTime<Utc>> for js_sys::Date { - fn from(date: DateTime<Utc>) -> js_sys::Date { - let js_date = js_sys::Date::new_0(); - - js_date.set_utc_full_year_with_month_date( - date.year() as u32, - date.month0() as i32, - date.day() as i32, - ); - - js_date.set_utc_hours(date.hour()); - js_date.set_utc_minutes(date.minute()); - js_date.set_utc_seconds(date.second()); - - js_date - } -} - -#[test] -fn test_auto_conversion() { - let utc_dt = Utc.ymd(2018, 9, 5).and_hms(23, 58, 0); - let cdt_dt = FixedOffset::west(5 * 60 * 60).ymd(2018, 9, 5).and_hms(18, 58, 0); - let utc_dt2: DateTime<Utc> = cdt_dt.into(); - assert_eq!(utc_dt, utc_dt2); -} - -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed) -where - FUtc: Fn(&DateTime<Utc>) -> Result<String, E>, - FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>, - E: ::core::fmt::Debug, -{ - assert_eq!( - to_string_utc(&Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06Z""#.into()) - ); - - assert_eq!( - to_string_fixed(&FixedOffset::east(3660).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06+01:01""#.into()) - ); - assert_eq!( - to_string_fixed(&FixedOffset::east(3650).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06+01:00:50""#.into()) - ); -} - -#[cfg(all(test, feature = "clock", any(feature = "rustc-serialize", feature = "serde")))] -fn test_decodable_json<FUtc, FFixed, FLocal, E>( - utc_from_str: FUtc, - fixed_from_str: FFixed, - local_from_str: FLocal, -) where - FUtc: Fn(&str) -> Result<DateTime<Utc>, E>, - FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>, - FLocal: Fn(&str) -> Result<DateTime<Local>, E>, - E: ::core::fmt::Debug, -{ - // should check against the offset as well (the normal DateTime comparison will ignore them) - fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { - dt.as_ref().map(|dt| (dt, dt.offset())) - } - - assert_eq!( - norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), - norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6))) - ); - assert_eq!( - norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), - norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6))) - ); - - assert_eq!( - norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), - norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6))) - ); - assert_eq!( - norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), - norm(&Some(FixedOffset::east(60 * 60 + 23 * 60).ymd(2014, 7, 24).and_hms(13, 57, 6))) - ); - - // we don't know the exact local offset but we can check that - // the conversion didn't change the instant itself - assert_eq!( - local_from_str(r#""2014-07-24T12:34:06Z""#).expect("local shouuld parse"), - Utc.ymd(2014, 7, 24).and_hms(12, 34, 6) - ); - assert_eq!( - local_from_str(r#""2014-07-24T13:57:06+01:23""#).expect("local should parse with offset"), - Utc.ymd(2014, 7, 24).and_hms(12, 34, 6) - ); - - assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); - assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); -} - -#[cfg(all(test, feature = "clock", feature = "rustc-serialize"))] -fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>( - utc_from_str: FUtc, - fixed_from_str: FFixed, - local_from_str: FLocal, -) where - FUtc: Fn(&str) -> Result<rustc_serialize::TsSeconds<Utc>, E>, - FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, E>, - FLocal: Fn(&str) -> Result<rustc_serialize::TsSeconds<Local>, E>, - E: ::core::fmt::Debug, -{ - fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { - dt.as_ref().map(|dt| (dt, dt.offset())) - } - - assert_eq!( - norm(&utc_from_str("0").ok().map(DateTime::from)), - norm(&Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0))) - ); - assert_eq!( - norm(&utc_from_str("-1").ok().map(DateTime::from)), - norm(&Some(Utc.ymd(1969, 12, 31).and_hms(23, 59, 59))) - ); - - assert_eq!( - norm(&fixed_from_str("0").ok().map(DateTime::from)), - norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0))) - ); - assert_eq!( - norm(&fixed_from_str("-1").ok().map(DateTime::from)), - norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59))) - ); - - assert_eq!( - *fixed_from_str("0").expect("0 timestamp should parse"), - Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - ); - assert_eq!( - *local_from_str("-1").expect("-1 timestamp should parse"), - Utc.ymd(1969, 12, 31).and_hms(23, 59, 59) - ); -} - -#[cfg(feature = "rustc-serialize")] -pub mod rustc_serialize { - use super::DateTime; - use core::fmt; - use core::ops::Deref; - #[cfg(feature = "clock")] - use offset::Local; - use offset::{FixedOffset, LocalResult, TimeZone, Utc}; - use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - - impl<Tz: TimeZone> Encodable for DateTime<Tz> { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - format!("{:?}", self).encode(s) - } - } - - // lik? function to convert a LocalResult into a serde-ish Result - fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error> - where - D: Decoder, - T: fmt::Display, - { - match me { - LocalResult::None => Err(d.error("value is not a legal timestamp")), - LocalResult::Ambiguous(..) => Err(d.error("value is an ambiguous timestamp")), - LocalResult::Single(val) => Ok(val), - } - } - - impl Decodable for DateTime<FixedOffset> { - fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<FixedOffset>, D::Error> { - d.read_str()? - .parse::<DateTime<FixedOffset>>() - .map_err(|_| d.error("invalid date and time")) - } - } - - #[allow(deprecated)] - impl Decodable for TsSeconds<FixedOffset> { - #[allow(deprecated)] - fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<FixedOffset>, D::Error> { - from(FixedOffset::east(0).timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) - } - } - - impl Decodable for DateTime<Utc> { - fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Utc>, D::Error> { - d.read_str()? - .parse::<DateTime<FixedOffset>>() - .map(|dt| dt.with_timezone(&Utc)) - .map_err(|_| d.error("invalid date and time")) - } - } - - /// A `DateTime` that can be deserialized from a timestamp - /// - /// A timestamp here is seconds since the epoch - #[derive(Debug)] - pub struct TsSeconds<Tz: TimeZone>(DateTime<Tz>); - - #[allow(deprecated)] - impl<Tz: TimeZone> From<TsSeconds<Tz>> for DateTime<Tz> { - /// Pull the inner DateTime<Tz> out - #[allow(deprecated)] - fn from(obj: TsSeconds<Tz>) -> DateTime<Tz> { - obj.0 - } - } - - #[allow(deprecated)] - impl<Tz: TimeZone> Deref for TsSeconds<Tz> { - type Target = DateTime<Tz>; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - #[allow(deprecated)] - impl Decodable for TsSeconds<Utc> { - fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Utc>, D::Error> { - from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) - } - } - - #[cfg(feature = "clock")] - impl Decodable for DateTime<Local> { - fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Local>, D::Error> { - match d.read_str()?.parse::<DateTime<FixedOffset>>() { - Ok(dt) => Ok(dt.with_timezone(&Local)), - Err(_) => Err(d.error("invalid date and time")), - } - } - } - - #[cfg(feature = "clock")] - #[allow(deprecated)] - impl Decodable for TsSeconds<Local> { - #[allow(deprecated)] - fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Local>, D::Error> { - from(Utc.timestamp_opt(d.read_i64()?, 0), d) - .map(|dt| TsSeconds(dt.with_timezone(&Local))) - } - } - - #[cfg(test)] - use rustc_serialize::json; - - #[test] - fn test_encodable() { - super::test_encodable_json(json::encode, json::encode); - } - - #[cfg(feature = "clock")] - #[test] - fn test_decodable() { - super::test_decodable_json(json::decode, json::decode, json::decode); - } - - #[cfg(feature = "clock")] - #[test] - fn test_decodable_timestamps() { - super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); - } -} - -/// documented at re-export site -#[cfg(feature = "serde")] -pub mod serde { - use super::DateTime; - use core::fmt; - #[cfg(feature = "clock")] - use offset::Local; - use offset::{FixedOffset, LocalResult, TimeZone, Utc}; - use serdelib::{de, ser}; - use {ne_timestamp, SerdeError}; - - #[doc(hidden)] - #[derive(Debug)] - pub struct SecondsTimestampVisitor; - - #[doc(hidden)] - #[derive(Debug)] - pub struct NanoSecondsTimestampVisitor; - - #[doc(hidden)] - #[derive(Debug)] - pub struct MilliSecondsTimestampVisitor; - - // lik? function to convert a LocalResult into a serde-ish Result - fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E> - where - E: de::Error, - V: fmt::Display, - T: fmt::Display, - { - match me { - LocalResult::None => Err(E::custom(ne_timestamp(ts))), - LocalResult::Ambiguous(min, max) => { - Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min: min, max: max })) - } - LocalResult::Single(val) => Ok(val), - } - } - - /// Ser/de to/from timestamps in nanoseconds - /// - /// Intended for use with `serde`'s `with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_nanoseconds")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_nanoseconds { - use core::fmt; - use serdelib::{de, ser}; - - use offset::TimeZone; - use {DateTime, Utc}; - - use super::{serde_from, NanoSecondsTimestampVisitor}; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_nanos()) - } - - /// Deserialize a `DateTime` from a nanosecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(NanoSecondsTimestampVisitor)?) - } - - impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { - type Value = DateTime<Utc>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a unix timestamp in nanoseconds") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_i64<E>(self, value: i64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from( - Utc.timestamp_opt(value / 1_000_000_000, (value % 1_000_000_000) as u32), - &value, - ) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_u64<E>(self, value: u64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from( - Utc.timestamp_opt( - (value / 1_000_000_000) as i64, - (value % 1_000_000_000) as u32, - ), - &value, - ) - } - } - } - - /// Ser/de to/from optional timestamps in nanoseconds - /// - /// Intended for use with `serde`'s `with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds_option; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_nanoseconds_option")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733)); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_nanoseconds_option { - use core::fmt; - use serdelib::{de, ser}; - - use {DateTime, Utc}; - - use super::NanoSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733)), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{DateTime, Utc}; - /// use chrono::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_option(OptionNanoSecondsTimestampVisitor)?) - } - - struct OptionNanoSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { - type Value = Option<DateTime<Utc>>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - } - } - - /// Ser/de to/from timestamps in milliseconds - /// - /// Intended for use with `serde`s `with` attribute. - /// - /// # Example - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_milliseconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_milliseconds")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_milliseconds { - use core::fmt; - use serdelib::{de, ser}; - - use offset::TimeZone; - use {DateTime, Utc}; - - use super::{serde_from, MilliSecondsTimestampVisitor}; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_millis()) - } - - /// Deserialize a `DateTime` from a millisecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{DateTime, Utc}; - /// use chrono::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))?) - } - - impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { - type Value = DateTime<Utc>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_i64<E>(self, value: i64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from( - Utc.timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32), - &value, - ) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_u64<E>(self, value: u64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from( - Utc.timestamp_opt((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32), - &value, - ) - } - } - } - - /// Ser/de to/from optional timestamps in milliseconds - /// - /// Intended for use with `serde`s `with` attribute. - /// - /// # Example - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_milliseconds_option; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_milliseconds_option")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918)); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_milliseconds_option { - use core::fmt; - use serdelib::{de, ser}; - - use {DateTime, Utc}; - - use super::MilliSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918)), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a millisecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::prelude::*; - /// use chrono::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// #[serde(untagged)] - /// enum E<T> { - /// V(T), - /// } - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// struct S { - /// #[serde(default, deserialize_with = "from_milli_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<(), serde_json::Error> { - /// let my_s: E<S> = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, E::V(S { time: Some(Utc.timestamp(1526522699, 918000000)) })); - /// let s: E<S> = serde_json::from_str(r#"{ "time": null }"#)?; - /// assert_eq!(s, E::V(S { time: None })); - /// let t: E<S> = serde_json::from_str(r#"{}"#)?; - /// assert_eq!(t, E::V(S { time: None })); - /// # Ok(()) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_option(OptionMilliSecondsTimestampVisitor) - .map(|opt| opt.map(|dt| dt.with_timezone(&Utc)))?) - } - - struct OptionMilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { - type Value = Option<DateTime<Utc>>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - } - } - - /// Ser/de to/from timestamps in seconds - /// - /// Intended for use with `serde`'s `with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_seconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_seconds")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Utc.ymd(2015, 5, 15).and_hms(10, 0, 0); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_seconds { - use core::fmt; - use serdelib::{de, ser}; - - use offset::TimeZone; - use {DateTime, Utc}; - - use super::{serde_from, SecondsTimestampVisitor}; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Utc.ymd(2015, 5, 15).and_hms(10, 0, 0), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp()) - } - - /// Deserialize a `DateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{DateTime, Utc}; - /// use chrono::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: DateTime<Utc> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(SecondsTimestampVisitor)?) - } - - impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { - type Value = DateTime<Utc>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_i64<E>(self, value: i64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from(Utc.timestamp_opt(value, 0), &value) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_u64<E>(self, value: u64) -> Result<DateTime<Utc>, E> - where - E: de::Error, - { - serde_from(Utc.timestamp_opt(value as i64, 0), &value) - } - } - } - - /// Ser/de to/from optional timestamps in seconds - /// - /// Intended for use with `serde`'s `with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_seconds_option; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_seconds_option")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = Some(Utc.ymd(2015, 5, 15).and_hms(10, 0, 0)); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_seconds_option { - use core::fmt; - use serdelib::{de, ser}; - - use {DateTime, Utc}; - - use super::SecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{TimeZone, DateTime, Utc}; - /// use chrono::serde::ts_seconds_option::serialize as to_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: Some(Utc.ymd(2015, 5, 15).and_hms(10, 0, 0)), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a seconds timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate chrono; - /// # use chrono::{DateTime, Utc}; - /// use chrono::serde::ts_seconds_option::deserialize as from_tsopt; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_tsopt")] - /// time: Option<DateTime<Utc>> - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_option(OptionSecondsTimestampVisitor)?) - } - - struct OptionSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { - type Value = Option<DateTime<Utc>>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit<E>(self) -> Result<Option<DateTime<Utc>>, E> - where - E: de::Error, - { - Ok(None) - } - } - } - - impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> { - /// Serialize into a rfc3339 time string - /// - /// See [the `serde` module](./serde/index.html) for alternate - /// serializations. - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - struct FormatWrapped<'a, D: 'a> { - inner: &'a D, - } - - impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } - } - - // Debug formatting is correct RFC3339, and it allows Zulu. - serializer.collect_str(&FormatWrapped { inner: &self }) - } - } - - struct DateTimeVisitor; - - impl<'de> de::Visitor<'de> for DateTimeVisitor { - type Value = DateTime<FixedOffset>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date and time string or a unix timestamp") - } - - fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E> - where - E: de::Error, - { - value.parse().map_err(|err: ::format::ParseError| E::custom(err)) - } - } - - /// Deserialize a value that optionally includes a timezone offset in its - /// string representation - /// - /// The value to be deserialized must be an rfc3339 string. - /// - /// See [the `serde` module](./serde/index.html) for alternate - /// deserialization formats. - impl<'de> de::Deserialize<'de> for DateTime<FixedOffset> { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor) - } - } - - /// Deserialize into a UTC value - /// - /// The value to be deserialized must be an rfc3339 string. - /// - /// See [the `serde` module](./serde/index.html) for alternate - /// deserialization formats. - impl<'de> de::Deserialize<'de> for DateTime<Utc> { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Utc)) - } - } - - /// Deserialize a value that includes no timezone in its string - /// representation - /// - /// The value to be deserialized must be an rfc3339 string. - /// - /// See [the `serde` module](./serde/index.html) for alternate - /// serialization formats. - #[cfg(feature = "clock")] - impl<'de> de::Deserialize<'de> for DateTime<Local> { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local)) - } - } - - #[cfg(test)] - extern crate bincode; - #[cfg(test)] - extern crate serde_json; - - #[test] - fn test_serde_serialize() { - super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string); - } - - #[cfg(feature = "clock")] - #[test] - fn test_serde_deserialize() { - super::test_decodable_json( - |input| self::serde_json::from_str(&input), - |input| self::serde_json::from_str(&input), - |input| self::serde_json::from_str(&input), - ); - } - - #[test] - fn test_serde_bincode() { - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - use self::bincode::{deserialize, serialize, Infinite}; - - let dt = Utc.ymd(2014, 7, 24).and_hms(12, 34, 6); - let encoded = serialize(&dt, Infinite).unwrap(); - let decoded: DateTime<Utc> = deserialize(&encoded).unwrap(); - assert_eq!(dt, decoded); - assert_eq!(dt.offset(), decoded.offset()); - } -} - -#[cfg(test)] -mod tests { - use super::DateTime; - use naive::{NaiveDate, NaiveTime}; - #[cfg(feature = "clock")] - use offset::Local; - use offset::{FixedOffset, TimeZone, Utc}; - use oldtime::Duration; - use std::time::{SystemTime, UNIX_EPOCH}; - #[cfg(feature = "clock")] - use Datelike; - - #[test] - #[allow(non_snake_case)] - fn test_datetime_offset() { - let Est = FixedOffset::west(5 * 60 * 60); - let Edt = FixedOffset::west(4 * 60 * 60); - let Kst = FixedOffset::east(9 * 60 * 60); - - assert_eq!(format!("{}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06 07:08:09 UTC"); - assert_eq!( - format!("{}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)), - "2014-05-06 07:08:09 -04:00" - ); - assert_eq!( - format!("{}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)), - "2014-05-06 07:08:09 +09:00" - ); - assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)), "2014-05-06T07:08:09Z"); - assert_eq!( - format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)), - "2014-05-06T07:08:09-04:00" - ); - assert_eq!( - format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)), - "2014-05-06T07:08:09+09:00" - ); - - // edge cases - assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(0, 0, 0)), "2014-05-06T00:00:00Z"); - assert_eq!( - format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(0, 0, 0)), - "2014-05-06T00:00:00-04:00" - ); - assert_eq!( - format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(0, 0, 0)), - "2014-05-06T00:00:00+09:00" - ); - assert_eq!( - format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(23, 59, 59)), - "2014-05-06T23:59:59Z" - ); - assert_eq!( - format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(23, 59, 59)), - "2014-05-06T23:59:59-04:00" - ); - assert_eq!( - format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(23, 59, 59)), - "2014-05-06T23:59:59+09:00" - ); - - let dt = Utc.ymd(2014, 5, 6).and_hms(7, 8, 9); - assert_eq!(dt, Edt.ymd(2014, 5, 6).and_hms(3, 8, 9)); - assert_eq!(dt + Duration::seconds(3600 + 60 + 1), Utc.ymd(2014, 5, 6).and_hms(8, 9, 10)); - assert_eq!( - dt.signed_duration_since(Edt.ymd(2014, 5, 6).and_hms(10, 11, 12)), - Duration::seconds(-7 * 3600 - 3 * 60 - 3) - ); - - assert_eq!(*Utc.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Utc); - assert_eq!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Edt); - assert!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != Est); - } - - #[test] - fn test_datetime_date_and_time() { - let tz = FixedOffset::east(5 * 60 * 60); - let d = tz.ymd(2014, 5, 6).and_hms(7, 8, 9); - assert_eq!(d.time(), NaiveTime::from_hms(7, 8, 9)); - assert_eq!(d.date(), tz.ymd(2014, 5, 6)); - assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2014, 5, 6)); - assert_eq!(d.date().and_time(d.time()), Some(d)); - - let tz = FixedOffset::east(4 * 60 * 60); - let d = tz.ymd(2016, 5, 4).and_hms(3, 2, 1); - assert_eq!(d.time(), NaiveTime::from_hms(3, 2, 1)); - assert_eq!(d.date(), tz.ymd(2016, 5, 4)); - assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2016, 5, 4)); - assert_eq!(d.date().and_time(d.time()), Some(d)); - - let tz = FixedOffset::west(13 * 60 * 60); - let d = tz.ymd(2017, 8, 9).and_hms(12, 34, 56); - assert_eq!(d.time(), NaiveTime::from_hms(12, 34, 56)); - assert_eq!(d.date(), tz.ymd(2017, 8, 9)); - assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2017, 8, 9)); - assert_eq!(d.date().and_time(d.time()), Some(d)); - - let utc_d = Utc.ymd(2017, 8, 9).and_hms(12, 34, 56); - assert!(utc_d < d); - } - - #[test] - #[cfg(feature = "clock")] - fn test_datetime_with_timezone() { - let local_now = Local::now(); - let utc_now = local_now.with_timezone(&Utc); - let local_now2 = utc_now.with_timezone(&Local); - assert_eq!(local_now, local_now2); - } - - #[test] - #[allow(non_snake_case)] - fn test_datetime_rfc2822_and_rfc3339() { - let EDT = FixedOffset::east(5 * 60 * 60); - assert_eq!( - Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(), - "Wed, 18 Feb 2015 23:16:09 +0000" - ); - assert_eq!( - Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(), - "2015-02-18T23:16:09+00:00" - ); - assert_eq!( - EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(), - "Wed, 18 Feb 2015 23:16:09 +0500" - ); - assert_eq!( - EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - assert_eq!( - EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc2822(), - "Wed, 18 Feb 2015 23:59:60 +0500" - ); - assert_eq!( - EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567).to_rfc3339(), - "2015-02-18T23:59:60.234567+05:00" - ); - - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), - Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), - Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), - Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)) - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), - Ok(EDT.ymd(2015, 2, 18).and_hms_milli(23, 59, 59, 1_000)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), - Ok(EDT.ymd(2015, 2, 18).and_hms_micro(23, 59, 59, 1_234_567)) - ); - } - - #[test] - fn test_rfc3339_opts() { - use SecondsFormat::*; - let pst = FixedOffset::east(8 * 60 * 60); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000); - assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); - assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); - assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); - assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00"); - assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00"); - assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00"); - - let ut = DateTime::<Utc>::from_utc(dt.naive_utc(), Utc); - assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00"); - assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z"); - assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00"); - assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z"); - assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z"); - assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z"); - assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z"); - } - - #[test] - #[should_panic] - fn test_rfc3339_opts_nonexhaustive() { - use SecondsFormat; - let dt = Utc.ymd(1999, 10, 9).and_hms(1, 2, 3); - dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); - } - - #[test] - fn test_datetime_from_str() { - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), - Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(), - Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(), - Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(), - Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), - Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(), - Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150)) - ); - assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err()); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(), - Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(), - Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)) - ); - assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err()); - - // no test for `DateTime<Local>`, we cannot verify that much. - } - - #[test] - fn test_datetime_parse_from_str() { - let ymdhms = |y, m, d, h, n, s, off| FixedOffset::east(off).ymd(y, m, d).and_hms(h, n, s); - assert_eq!( - DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570 * 60)) - ); // ignore offset - assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset - assert!(DateTime::parse_from_str( - "Fri, 09 Aug 2013 23:54:35 GMT", - "%a, %d %b %Y %H:%M:%S GMT" - ) - .is_err()); - assert_eq!( - Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), - Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35)) - ); - } - - #[test] - fn test_to_string_round_trip() { - let dt = Utc.ymd(2000, 1, 1).and_hms(0, 0, 0); - let _dt: DateTime<Utc> = dt.to_string().parse().unwrap(); - - let ndt_fixed = dt.with_timezone(&FixedOffset::east(3600)); - let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); - - let ndt_fixed = dt.with_timezone(&FixedOffset::east(0)); - let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); - } - - #[test] - #[cfg(feature = "clock")] - fn test_to_string_round_trip_with_local() { - let ndt = Local::now(); - let _dt: DateTime<FixedOffset> = ndt.to_string().parse().unwrap(); - } - - #[test] - #[cfg(feature = "clock")] - fn test_datetime_format_with_local() { - // if we are not around the year boundary, local and UTC date should have the same year - let dt = Local::now().with_month(5).unwrap(); - assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string()); - } - - #[test] - #[cfg(feature = "clock")] - fn test_datetime_is_copy() { - // UTC is known to be `Copy`. - let a = Utc::now(); - let b = a; - assert_eq!(a, b); - } - - #[test] - #[cfg(feature = "clock")] - fn test_datetime_is_send() { - use std::thread; - - // UTC is known to be `Send`. - let a = Utc::now(); - thread::spawn(move || { - let _ = a; - }) - .join() - .unwrap(); - } - - #[test] - fn test_subsecond_part() { - let datetime = Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 1234567); - - assert_eq!(1, datetime.timestamp_subsec_millis()); - assert_eq!(1234, datetime.timestamp_subsec_micros()); - assert_eq!(1234567, datetime.timestamp_subsec_nanos()); - } - - #[test] - #[cfg(not(target_os = "windows"))] - fn test_from_system_time() { - use std::time::Duration; - - let epoch = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); - let nanos = 999_999_999; - - // SystemTime -> DateTime<Utc> - assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); - assert_eq!( - DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), - Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos) - ); - assert_eq!( - DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), - Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1) - ); - - // DateTime<Utc> -> SystemTime - assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); - assert_eq!( - SystemTime::from(Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos)), - UNIX_EPOCH + Duration::new(999_999_999, nanos) - ); - assert_eq!( - SystemTime::from(Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1)), - UNIX_EPOCH - Duration::new(999_999_999, 999_999_999) - ); - - // DateTime<any tz> -> SystemTime (via `with_timezone`) - #[cfg(feature = "clock")] - { - assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); - } - assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); - assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); - } - - #[test] - #[cfg(target_os = "windows")] - fn test_from_system_time() { - use std::time::Duration; - - let nanos = 999_999_000; - - let epoch = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); - - // SystemTime -> DateTime<Utc> - assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); - assert_eq!( - DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), - Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos) - ); - assert_eq!( - DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), - Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1_000) - ); - - // DateTime<Utc> -> SystemTime - assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); - assert_eq!( - SystemTime::from(Utc.ymd(2001, 9, 9).and_hms_nano(1, 46, 39, nanos)), - UNIX_EPOCH + Duration::new(999_999_999, nanos) - ); - assert_eq!( - SystemTime::from(Utc.ymd(1938, 4, 24).and_hms_nano(22, 13, 20, 1_000)), - UNIX_EPOCH - Duration::new(999_999_999, nanos) - ); - - // DateTime<any tz> -> SystemTime (via `with_timezone`) - #[cfg(feature = "clock")] - { - assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); - } - assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::east(32400))), UNIX_EPOCH); - assert_eq!(SystemTime::from(epoch.with_timezone(&FixedOffset::west(28800))), UNIX_EPOCH); - } - - #[test] - fn test_datetime_format_alignment() { - let datetime = Utc.ymd(2007, 01, 02); - - // Item::Literal - let percent = datetime.format("%%"); - assert_eq!(" %", format!("{:>3}", percent)); - assert_eq!("% ", format!("{:<3}", percent)); - assert_eq!(" % ", format!("{:^3}", percent)); - - // Item::Numeric - let year = datetime.format("%Y"); - assert_eq!(" 2007", format!("{:>6}", year)); - assert_eq!("2007 ", format!("{:<6}", year)); - assert_eq!(" 2007 ", format!("{:^6}", year)); - - // Item::Fixed - let tz = datetime.format("%Z"); - assert_eq!(" UTC", format!("{:>5}", tz)); - assert_eq!("UTC ", format!("{:<5}", tz)); - assert_eq!(" UTC ", format!("{:^5}", tz)); - - // [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric] - let ymd = datetime.format("%Y %B %d"); - let ymd_formatted = "2007 January 02"; - assert_eq!(format!(" {}", ymd_formatted), format!("{:>17}", ymd)); - assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd)); - assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd)); - } -} diff --git a/vendor/chrono/src/datetime/mod.rs b/vendor/chrono/src/datetime/mod.rs new file mode 100644 index 000000000..38416cb6f --- /dev/null +++ b/vendor/chrono/src/datetime/mod.rs @@ -0,0 +1,1322 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 date and time with time zone. + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +use alloc::string::{String, ToString}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::fmt::Write; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::{fmt, hash, str}; +#[cfg(feature = "std")] +use std::string::ToString; +#[cfg(any(feature = "std", test))] +use std::time::{SystemTime, UNIX_EPOCH}; + +#[cfg(any(feature = "alloc", feature = "std", test))] +use crate::format::DelayedFormat; +#[cfg(feature = "unstable-locales")] +use crate::format::Locale; +use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{Fixed, Item}; +use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; +#[cfg(feature = "clock")] +use crate::offset::Local; +use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; +use crate::oldtime::Duration as OldDuration; +#[allow(deprecated)] +use crate::Date; +use crate::Months; +use crate::{Datelike, Timelike, Weekday}; + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +#[cfg(feature = "rustc-serialize")] +pub(super) mod rustc_serialize; + +/// documented at re-export site +#[cfg(feature = "serde")] +pub(super) mod serde; + +#[cfg(test)] +mod tests; + +/// Specific formatting options for seconds. This may be extended in the +/// future, so exhaustive matching in external code is not recommended. +/// +/// See the `TimeZone::to_rfc3339_opts` function for usage. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum SecondsFormat { + /// Format whole seconds only, with no decimal point nor subseconds. + Secs, + + /// Use fixed 3 subsecond digits. This corresponds to + /// [Fixed::Nanosecond3](format/enum.Fixed.html#variant.Nanosecond3). + Millis, + + /// Use fixed 6 subsecond digits. This corresponds to + /// [Fixed::Nanosecond6](format/enum.Fixed.html#variant.Nanosecond6). + Micros, + + /// Use fixed 9 subsecond digits. This corresponds to + /// [Fixed::Nanosecond9](format/enum.Fixed.html#variant.Nanosecond9). + Nanos, + + /// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to + /// display all available non-zero sub-second digits. This corresponds to + /// [Fixed::Nanosecond](format/enum.Fixed.html#variant.Nanosecond). + AutoSi, + + // Do not match against this. + #[doc(hidden)] + __NonExhaustive, +} + +/// ISO 8601 combined date and time with time zone. +/// +/// There are some constructors implemented here (the `from_*` methods), but +/// the general-purpose constructors are all via the methods on the +/// [`TimeZone`](./offset/trait.TimeZone.html) implementations. +#[derive(Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +pub struct DateTime<Tz: TimeZone> { + datetime: NaiveDateTime, + offset: Tz::Offset, +} + +/// The minimum possible `DateTime<Utc>`. +#[deprecated(since = "0.4.20", note = "Use DateTime::MIN_UTC instead")] +pub const MIN_DATETIME: DateTime<Utc> = DateTime::<Utc>::MIN_UTC; +/// The maximum possible `DateTime<Utc>`. +#[deprecated(since = "0.4.20", note = "Use DateTime::MAX_UTC instead")] +pub const MAX_DATETIME: DateTime<Utc> = DateTime::<Utc>::MAX_UTC; + +impl<Tz: TimeZone> DateTime<Tz> { + /// Makes a new `DateTime` with given *UTC* datetime and offset. + /// The local datetime should be constructed via the `TimeZone` trait. + /// + /// # Example + /// + /// ``` + /// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; + /// + /// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp_opt(61, 0).unwrap(), Utc); + /// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt); + /// ``` + // + // note: this constructor is purposely not named to `new` to discourage the direct usage. + #[inline] + pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> { + DateTime { datetime, offset } + } + + /// Makes a new `DateTime` with given **local** datetime and offset that + /// presents local timezone. + /// + /// # Example + /// + /// ``` + /// use chrono::DateTime; + /// use chrono::naive::NaiveDate; + /// use chrono::offset::{Utc, FixedOffset}; + /// + /// let naivedatetime_utc = NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap(); + /// let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc); + /// + /// let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + /// let naivedatetime_east = NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap(); + /// let datetime_east = DateTime::<FixedOffset>::from_local(naivedatetime_east, timezone_east); + /// + /// let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap(); + /// let naivedatetime_west = NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap(); + /// let datetime_west = DateTime::<FixedOffset>::from_local(naivedatetime_west, timezone_west); + + /// assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); + /// assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); + /// ``` + #[inline] + pub fn from_local(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> { + let datetime_utc = datetime - offset.fix(); + + DateTime { datetime: datetime_utc, offset } + } + + /// Retrieves a date component + /// + /// Unless you are immediately planning on turning this into a `DateTime` + /// with the same Timezone you should use the + /// [`date_naive`](DateTime::date_naive) method. + #[inline] + #[deprecated(since = "0.4.23", note = "Use `date_naive()` instead")] + #[allow(deprecated)] + pub fn date(&self) -> Date<Tz> { + Date::from_utc(self.naive_local().date(), self.offset.clone()) + } + + /// Retrieves the Date without an associated timezone + /// + /// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it, + /// so should be preferred to [`Date`] any time you truly want to operate on Dates. + /// + /// ``` + /// use chrono::prelude::*; + /// + /// let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); + /// let other: DateTime<FixedOffset> = FixedOffset::east_opt(23).unwrap().with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); + /// assert_eq!(date.date_naive(), other.date_naive()); + /// ``` + #[inline] + pub fn date_naive(&self) -> NaiveDate { + let local = self.naive_local(); + NaiveDate::from_ymd_opt(local.year(), local.month(), local.day()).unwrap() + } + + /// Retrieves a time component. + /// Unlike `date`, this is not associated to the time zone. + #[inline] + pub fn time(&self) -> NaiveTime { + self.datetime.time() + self.offset.fix() + } + + /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC + /// (aka "UNIX timestamp"). + #[inline] + pub fn timestamp(&self) -> i64 { + self.datetime.timestamp() + } + + /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC + /// + /// Note that this does reduce the number of years that can be represented + /// from ~584 Billion to ~584 Million. (If this is a problem, please file + /// an issue to let me know what domain needs millisecond precision over + /// billions of years, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::{Utc, TimeZone, NaiveDate}; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_millis(), 1_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); + /// ``` + #[inline] + pub fn timestamp_millis(&self) -> i64 { + self.datetime.timestamp_millis() + } + + /// Returns the number of non-leap-microseconds since January 1, 1970 UTC + /// + /// Note that this does reduce the number of years that can be represented + /// from ~584 Billion to ~584 Thousand. (If this is a problem, please file + /// an issue to let me know what domain needs microsecond precision over + /// millennia, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::{Utc, TimeZone, NaiveDate}; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_micros(), 1_000_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); + /// ``` + #[inline] + pub fn timestamp_micros(&self) -> i64 { + self.datetime.timestamp_micros() + } + + /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC + /// + /// Note that this does reduce the number of years that can be represented + /// from ~584 Billion to ~584. (If this is a problem, please file + /// an issue to let me know what domain needs nanosecond precision over + /// millennia, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::{Utc, TimeZone, NaiveDate}; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); + /// ``` + #[inline] + pub fn timestamp_nanos(&self) -> i64 { + self.datetime.timestamp_nanos() + } + + /// Returns the number of milliseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999 + /// + /// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_millis(&self) -> u32 { + self.datetime.timestamp_subsec_millis() + } + + /// Returns the number of microseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999_999 + /// + /// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_micros(&self) -> u32 { + self.datetime.timestamp_subsec_micros() + } + + /// Returns the number of nanoseconds since the last second boundary + /// + /// warning: in event of a leap second, this may exceed 999_999_999 + /// + /// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC + #[inline] + pub fn timestamp_subsec_nanos(&self) -> u32 { + self.datetime.timestamp_subsec_nanos() + } + + /// Retrieves an associated offset from UTC. + #[inline] + pub fn offset(&self) -> &Tz::Offset { + &self.offset + } + + /// Retrieves an associated time zone. + #[inline] + pub fn timezone(&self) -> Tz { + TimeZone::from_offset(&self.offset) + } + + /// Changes the associated time zone. + /// The returned `DateTime` references the same instant of time from the perspective of the provided time zone. + #[inline] + pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> { + tz.from_utc_datetime(&self.datetime) + } + + /// Adds given `Duration` to the current date and time. + /// + /// Returns `None` when it will result in overflow. + #[inline] + pub fn checked_add_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { + let datetime = self.datetime.checked_add_signed(rhs)?; + let tz = self.timezone(); + Some(tz.from_utc_datetime(&datetime)) + } + + /// Adds given `Months` to the current date and time. + /// + /// Returns `None` when it will result in overflow, or if the + /// local time is not valid on the newly calculated date. + /// + /// See [`NaiveDate::checked_add_months`] for more details on behavior + pub fn checked_add_months(self, rhs: Months) -> Option<DateTime<Tz>> { + self.naive_local() + .checked_add_months(rhs)? + .and_local_timezone(Tz::from_offset(&self.offset)) + .single() + } + + /// Subtracts given `Duration` from the current date and time. + /// + /// Returns `None` when it will result in overflow. + #[inline] + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> { + let datetime = self.datetime.checked_sub_signed(rhs)?; + let tz = self.timezone(); + Some(tz.from_utc_datetime(&datetime)) + } + + /// Subtracts given `Months` from the current date and time. + /// + /// Returns `None` when it will result in overflow, or if the + /// local time is not valid on the newly calculated date. + /// + /// See [`NaiveDate::checked_sub_months`] for more details on behavior + pub fn checked_sub_months(self, rhs: Months) -> Option<DateTime<Tz>> { + self.naive_local() + .checked_sub_months(rhs)? + .and_local_timezone(Tz::from_offset(&self.offset)) + .single() + } + + /// Add a duration in [`Days`] to the date part of the `DateTime` + /// + /// Returns `None` if the resulting date would be out of range. + pub fn checked_add_days(self, days: Days) -> Option<Self> { + self.naive_local() + .checked_add_days(days)? + .and_local_timezone(TimeZone::from_offset(&self.offset)) + .single() + } + + /// Subtract a duration in [`Days`] from the date part of the `DateTime` + /// + /// Returns `None` if the resulting date would be out of range. + pub fn checked_sub_days(self, days: Days) -> Option<Self> { + self.naive_local() + .checked_sub_days(days)? + .and_local_timezone(TimeZone::from_offset(&self.offset)) + .single() + } + + /// Subtracts another `DateTime` from the current date and time. + /// This does not overflow or underflow at all. + #[inline] + pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> OldDuration { + self.datetime.signed_duration_since(rhs.datetime) + } + + /// Returns a view to the naive UTC datetime. + #[inline] + pub fn naive_utc(&self) -> NaiveDateTime { + self.datetime + } + + /// Returns a view to the naive local datetime. + #[inline] + pub fn naive_local(&self) -> NaiveDateTime { + self.datetime + self.offset.fix() + } + + /// Retrieve the elapsed years from now to the given [`DateTime`]. + pub fn years_since(&self, base: Self) -> Option<u32> { + let mut years = self.year() - base.year(); + let earlier_time = + (self.month(), self.day(), self.time()) < (base.month(), base.day(), base.time()); + + years -= match earlier_time { + true => 1, + false => 0, + }; + + match years >= 0 { + true => Some(years as u32), + false => None, + } + } + + /// The minimum possible `DateTime<Utc>`. + pub const MIN_UTC: DateTime<Utc> = DateTime { datetime: NaiveDateTime::MIN, offset: Utc }; + /// The maximum possible `DateTime<Utc>`. + pub const MAX_UTC: DateTime<Utc> = DateTime { datetime: NaiveDateTime::MAX, offset: Utc }; +} + +impl Default for DateTime<Utc> { + fn default() -> Self { + Utc.from_utc_datetime(&NaiveDateTime::default()) + } +} + +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl Default for DateTime<Local> { + fn default() -> Self { + Local.from_utc_datetime(&NaiveDateTime::default()) + } +} + +impl Default for DateTime<FixedOffset> { + fn default() -> Self { + FixedOffset::west_opt(0).unwrap().from_utc_datetime(&NaiveDateTime::default()) + } +} + +/// Convert a `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. +impl From<DateTime<Utc>> for DateTime<FixedOffset> { + /// Convert this `DateTime<Utc>` instance into a `DateTime<FixedOffset>` instance. + /// + /// Conversion is done via [`DateTime::with_timezone`]. Note that the converted value returned by + /// this will be created with a fixed timezone offset of 0. + fn from(src: DateTime<Utc>) -> Self { + src.with_timezone(&FixedOffset::east_opt(0).unwrap()) + } +} + +/// Convert a `DateTime<Utc>` instance into a `DateTime<Local>` instance. +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl From<DateTime<Utc>> for DateTime<Local> { + /// Convert this `DateTime<Utc>` instance into a `DateTime<Local>` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in timezones. + fn from(src: DateTime<Utc>) -> Self { + src.with_timezone(&Local) + } +} + +/// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. +impl From<DateTime<FixedOffset>> for DateTime<Utc> { + /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Utc>` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the timezone + /// difference. + fn from(src: DateTime<FixedOffset>) -> Self { + src.with_timezone(&Utc) + } +} + +/// Convert a `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl From<DateTime<FixedOffset>> for DateTime<Local> { + /// Convert this `DateTime<FixedOffset>` instance into a `DateTime<Local>` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`]. Returns the equivalent value in local + /// time. + fn from(src: DateTime<FixedOffset>) -> Self { + src.with_timezone(&Local) + } +} + +/// Convert a `DateTime<Local>` instance into a `DateTime<Utc>` instance. +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl From<DateTime<Local>> for DateTime<Utc> { + /// Convert this `DateTime<Local>` instance into a `DateTime<Utc>` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in + /// timezones. + fn from(src: DateTime<Local>) -> Self { + src.with_timezone(&Utc) + } +} + +/// Convert a `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl From<DateTime<Local>> for DateTime<FixedOffset> { + /// Convert this `DateTime<Local>` instance into a `DateTime<FixedOffset>` instance. + /// + /// Conversion is performed via [`DateTime::with_timezone`]. Note that the converted value returned + /// by this will be created with a fixed timezone offset of 0. + fn from(src: DateTime<Local>) -> Self { + src.with_timezone(&FixedOffset::east_opt(0).unwrap()) + } +} + +/// Maps the local datetime to other datetime with given conversion function. +fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz>> +where + F: FnMut(NaiveDateTime) -> Option<NaiveDateTime>, +{ + f(dt.naive_local()).and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) +} + +impl DateTime<FixedOffset> { + /// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`, + /// then returns a new [`DateTime`] with a parsed [`FixedOffset`]. + /// + /// RFC 2822 is the internet message standard that specifies the + /// representation of times in HTTP and email headers. + /// + /// ``` + /// # use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate}; + /// assert_eq!( + /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), + /// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() + /// ); + /// ``` + pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> { + const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_datetime() + } + + /// Parses an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`, + /// then returns a new [`DateTime`] with a parsed [`FixedOffset`]. + /// + /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows some freedom + /// over the syntax and RFC 3339 exercises that freedom to rigidly define a fixed format. + pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> { + const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_datetime() + } + + /// Parses a string with the specified format string and returns a new + /// [`DateTime`] with a parsed [`FixedOffset`]. + /// + /// See the [`crate::format::strftime`] module on the supported escape + /// sequences. + /// + /// See also [`TimeZone::datetime_from_str`] which gives a local + /// [`DateTime`] on specific time zone. + /// + /// Note that this method *requires a timezone* in the string. See + /// [`NaiveDateTime::parse_from_str`] + /// for a version that does not require a timezone in the to-be-parsed str. + /// + /// # Example + /// + /// ```rust + /// use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate}; + /// + /// let dt = DateTime::parse_from_str( + /// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); + /// assert_eq!(dt, Ok(FixedOffset::east_opt(0).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(1983, 4, 13).unwrap().and_hms_milli_opt(12, 9, 14, 274).unwrap()).unwrap())); + /// ``` + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_datetime() + } +} + +impl<Tz: TimeZone> DateTime<Tz> +where + Tz::Offset: fmt::Display, +{ + /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + pub fn to_rfc2822(&self) -> String { + let mut result = String::with_capacity(32); + crate::format::write_rfc2822(&mut result, self.naive_local(), self.offset.fix()) + .expect("writing rfc2822 datetime to string should never fail"); + result + } + + /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + pub fn to_rfc3339(&self) -> String { + let mut result = String::with_capacity(32); + crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix()) + .expect("writing rfc3339 datetime to string should never fail"); + result + } + + /// Return an RFC 3339 and ISO 8601 date and time string with subseconds + /// formatted as per a `SecondsFormat`. + /// + /// If passed `use_z` true and the timezone is UTC (offset 0), use 'Z', as + /// per [`Fixed::TimezoneOffsetColonZ`] If passed `use_z` false, use + /// [`Fixed::TimezoneOffsetColon`] + /// + /// # Examples + /// + /// ```rust + /// # use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(18, 30, 9, 453_829).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), + /// "2018-01-26T18:30:09.453+00:00"); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), + /// "2018-01-26T18:30:09.453Z"); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), + /// "2018-01-26T18:30:09Z"); + /// + /// let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + /// let dt = pst.from_local_datetime(&NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(10, 30, 9, 453_829).unwrap()).unwrap(); + /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), + /// "2018-01-26T10:30:09+08:00"); + /// ``` + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String { + use crate::format::Numeric::*; + use crate::format::Pad::Zero; + use crate::SecondsFormat::*; + + debug_assert!(secform != __NonExhaustive, "Do not use __NonExhaustive!"); + + const PREFIX: &[Item<'static>] = &[ + Item::Numeric(Year, Zero), + Item::Literal("-"), + Item::Numeric(Month, Zero), + Item::Literal("-"), + Item::Numeric(Day, Zero), + Item::Literal("T"), + Item::Numeric(Hour, Zero), + Item::Literal(":"), + Item::Numeric(Minute, Zero), + Item::Literal(":"), + Item::Numeric(Second, Zero), + ]; + + let ssitem = match secform { + Secs => None, + Millis => Some(Item::Fixed(Fixed::Nanosecond3)), + Micros => Some(Item::Fixed(Fixed::Nanosecond6)), + Nanos => Some(Item::Fixed(Fixed::Nanosecond9)), + AutoSi => Some(Item::Fixed(Fixed::Nanosecond)), + __NonExhaustive => unreachable!(), + }; + + let tzitem = Item::Fixed(if use_z { + Fixed::TimezoneOffsetColonZ + } else { + Fixed::TimezoneOffsetColon + }); + + match ssitem { + None => self.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(), + Some(s) => self.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(), + } + } + + /// Formats the combined date and time with the specified formatting items. + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> + where + I: Iterator<Item = B> + Clone, + B: Borrow<Item<'a>>, + { + let local = self.naive_local(); + DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) + } + + /// Formats the combined date and time with the specified format string. + /// See the [`crate::format::strftime`] module + /// on the supported escape sequences. + /// + /// # Example + /// ```rust + /// use chrono::prelude::*; + /// + /// let date_time: DateTime<Utc> = Utc.with_ymd_and_hms(2017, 04, 02, 12, 50, 32).unwrap(); + /// let formatted = format!("{}", date_time.format("%d/%m/%Y %H:%M")); + /// assert_eq!(formatted, "02/04/2017 12:50"); + /// ``` + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Formats the combined date and time with the specified formatting items and locale. + #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] + #[inline] + pub fn format_localized_with_items<'a, I, B>( + &self, + items: I, + locale: Locale, + ) -> DelayedFormat<I> + where + I: Iterator<Item = B> + Clone, + B: Borrow<Item<'a>>, + { + let local = self.naive_local(); + DelayedFormat::new_with_offset_and_locale( + Some(local.date()), + Some(local.time()), + &self.offset, + items, + locale, + ) + } + + /// Formats the combined date and time with the specified format string and + /// locale. + /// + /// See the [`crate::format::strftime`] module on the supported escape + /// sequences. + #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] + #[inline] + pub fn format_localized<'a>( + &self, + fmt: &'a str, + locale: Locale, + ) -> DelayedFormat<StrftimeItems<'a>> { + self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) + } +} + +impl<Tz: TimeZone> Datelike for DateTime<Tz> { + #[inline] + fn year(&self) -> i32 { + self.naive_local().year() + } + #[inline] + fn month(&self) -> u32 { + self.naive_local().month() + } + #[inline] + fn month0(&self) -> u32 { + self.naive_local().month0() + } + #[inline] + fn day(&self) -> u32 { + self.naive_local().day() + } + #[inline] + fn day0(&self) -> u32 { + self.naive_local().day0() + } + #[inline] + fn ordinal(&self) -> u32 { + self.naive_local().ordinal() + } + #[inline] + fn ordinal0(&self) -> u32 { + self.naive_local().ordinal0() + } + #[inline] + fn weekday(&self) -> Weekday { + self.naive_local().weekday() + } + #[inline] + fn iso_week(&self) -> IsoWeek { + self.naive_local().iso_week() + } + + #[inline] + fn with_year(&self, year: i32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_year(year)) + } + + #[inline] + fn with_month(&self, month: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_month(month)) + } + + #[inline] + fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_month0(month0)) + } + + #[inline] + fn with_day(&self, day: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_day(day)) + } + + #[inline] + fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_day0(day0)) + } + + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_ordinal(ordinal)) + } + + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_ordinal0(ordinal0)) + } +} + +impl<Tz: TimeZone> Timelike for DateTime<Tz> { + #[inline] + fn hour(&self) -> u32 { + self.naive_local().hour() + } + #[inline] + fn minute(&self) -> u32 { + self.naive_local().minute() + } + #[inline] + fn second(&self) -> u32 { + self.naive_local().second() + } + #[inline] + fn nanosecond(&self) -> u32 { + self.naive_local().nanosecond() + } + + #[inline] + fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_hour(hour)) + } + + #[inline] + fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_minute(min)) + } + + #[inline] + fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_second(sec)) + } + + #[inline] + fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> { + map_local(self, |datetime| datetime.with_nanosecond(nano)) + } +} + +// we need them as automatic impls cannot handle associated types +impl<Tz: TimeZone> Copy for DateTime<Tz> where <Tz as TimeZone>::Offset: Copy {} +unsafe impl<Tz: TimeZone> Send for DateTime<Tz> where <Tz as TimeZone>::Offset: Send {} + +impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> { + fn eq(&self, other: &DateTime<Tz2>) -> bool { + self.datetime == other.datetime + } +} + +impl<Tz: TimeZone> Eq for DateTime<Tz> {} + +impl<Tz: TimeZone, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> { + /// Compare two DateTimes based on their true time, ignoring time zones + /// + /// # Example + /// + /// ``` + /// use chrono::prelude::*; + /// + /// let earlier = Utc.with_ymd_and_hms(2015, 5, 15, 2, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(1 * 3600).unwrap()); + /// let later = Utc.with_ymd_and_hms(2015, 5, 15, 3, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(5 * 3600).unwrap()); + /// + /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00"); + /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00"); + /// + /// assert!(later > earlier); + /// ``` + fn partial_cmp(&self, other: &DateTime<Tz2>) -> Option<Ordering> { + self.datetime.partial_cmp(&other.datetime) + } +} + +impl<Tz: TimeZone> Ord for DateTime<Tz> { + fn cmp(&self, other: &DateTime<Tz>) -> Ordering { + self.datetime.cmp(&other.datetime) + } +} + +impl<Tz: TimeZone> hash::Hash for DateTime<Tz> { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.datetime.hash(state) + } +} + +impl<Tz: TimeZone> Add<OldDuration> for DateTime<Tz> { + type Output = DateTime<Tz>; + + #[inline] + fn add(self, rhs: OldDuration) -> DateTime<Tz> { + self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed") + } +} + +impl<Tz: TimeZone> AddAssign<OldDuration> for DateTime<Tz> { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + let datetime = + self.datetime.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed"); + let tz = self.timezone(); + *self = tz.from_utc_datetime(&datetime); + } +} + +impl<Tz: TimeZone> Add<Months> for DateTime<Tz> { + type Output = DateTime<Tz>; + + fn add(self, rhs: Months) -> Self::Output { + self.checked_add_months(rhs).unwrap() + } +} + +impl<Tz: TimeZone> Sub<OldDuration> for DateTime<Tz> { + type Output = DateTime<Tz>; + + #[inline] + fn sub(self, rhs: OldDuration) -> DateTime<Tz> { + self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed") + } +} + +impl<Tz: TimeZone> SubAssign<OldDuration> for DateTime<Tz> { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + let datetime = + self.datetime.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed"); + let tz = self.timezone(); + *self = tz.from_utc_datetime(&datetime) + } +} + +impl<Tz: TimeZone> Sub<Months> for DateTime<Tz> { + type Output = DateTime<Tz>; + + fn sub(self, rhs: Months) -> Self::Output { + self.checked_sub_months(rhs).unwrap() + } +} + +impl<Tz: TimeZone> Sub<DateTime<Tz>> for DateTime<Tz> { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: DateTime<Tz>) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +impl<Tz: TimeZone> Add<Days> for DateTime<Tz> { + type Output = DateTime<Tz>; + + fn add(self, days: Days) -> Self::Output { + self.checked_add_days(days).unwrap() + } +} + +impl<Tz: TimeZone> Sub<Days> for DateTime<Tz> { + type Output = DateTime<Tz>; + + fn sub(self, days: Days) -> Self::Output { + self.checked_sub_days(days).unwrap() + } +} + +impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.naive_local().fmt(f)?; + self.offset.fmt(f) + } +} + +impl<Tz: TimeZone> fmt::Display for DateTime<Tz> +where + Tz::Offset: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.naive_local().fmt(f)?; + f.write_char(' ')?; + self.offset.fmt(f) + } +} + +/// Accepts a relaxed form of RFC3339. +/// A space or a 'T' are acepted as the separator between the date and time +/// parts. Additional spaces are allowed between each component. +/// +/// All of these examples are equivalent: +/// ``` +/// # use chrono::{DateTime, Utc}; +/// "2012-12-12T12:12:12Z".parse::<DateTime<Utc>>(); +/// "2012-12-12 12:12:12Z".parse::<DateTime<Utc>>(); +/// "2012- 12-12T12: 12:12Z".parse::<DateTime<Utc>>(); +/// ``` +impl str::FromStr for DateTime<Utc> { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult<DateTime<Utc>> { + s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&Utc)) + } +} + +/// Accepts a relaxed form of RFC3339. +/// A space or a 'T' are acepted as the separator between the date and time +/// parts. Additional spaces are allowed between each component. +/// +/// All of these examples are equivalent: +/// ``` +/// # use chrono::{DateTime, Local}; +/// "2012-12-12T12:12:12Z".parse::<DateTime<Local>>(); +/// "2012-12-12 12:12:12Z".parse::<DateTime<Local>>(); +/// "2012- 12-12T12: 12:12Z".parse::<DateTime<Local>>(); +/// ``` +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl str::FromStr for DateTime<Local> { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult<DateTime<Local>> { + s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&Local)) + } +} + +#[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From<SystemTime> for DateTime<Utc> { + fn from(t: SystemTime) -> DateTime<Utc> { + let (sec, nsec) = match t.duration_since(UNIX_EPOCH) { + Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()), + Err(e) => { + // unlikely but should be handled + let dur = e.duration(); + let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos()); + if nsec == 0 { + (-sec, 0) + } else { + (-sec - 1, 1_000_000_000 - nsec) + } + } + }; + Utc.timestamp_opt(sec, nsec).unwrap() + } +} + +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl From<SystemTime> for DateTime<Local> { + fn from(t: SystemTime) -> DateTime<Local> { + DateTime::<Utc>::from(t).with_timezone(&Local) + } +} + +#[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime { + fn from(dt: DateTime<Tz>) -> SystemTime { + use std::time::Duration; + + let sec = dt.timestamp(); + let nsec = dt.timestamp_subsec_nanos(); + if sec < 0 { + // unlikely but should be handled + UNIX_EPOCH - Duration::new(-sec as u64, 0) + Duration::new(0, nsec) + } else { + UNIX_EPOCH + Duration::new(sec as u64, nsec) + } + } +} + +#[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +))] +#[cfg_attr( + docsrs, + doc(cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))) +)] +impl From<js_sys::Date> for DateTime<Utc> { + fn from(date: js_sys::Date) -> DateTime<Utc> { + DateTime::<Utc>::from(&date) + } +} + +#[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +))] +#[cfg_attr( + docsrs, + doc(cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))) +)] +impl From<&js_sys::Date> for DateTime<Utc> { + fn from(date: &js_sys::Date) -> DateTime<Utc> { + Utc.timestamp_millis_opt(date.get_time() as i64).unwrap() + } +} + +#[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +))] +#[cfg_attr( + docsrs, + doc(cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))) +)] +impl From<DateTime<Utc>> for js_sys::Date { + /// Converts a `DateTime<Utc>` to a JS `Date`. The resulting value may be lossy, + /// any values that have a millisecond timestamp value greater/less than ±8,640,000,000,000,000 + /// (April 20, 271821 BCE ~ September 13, 275760 CE) will become invalid dates in JS. + fn from(date: DateTime<Utc>) -> js_sys::Date { + let js_millis = wasm_bindgen::JsValue::from_f64(date.timestamp_millis() as f64); + js_sys::Date::new(&js_millis) + } +} + +// Note that implementation of Arbitrary cannot be simply derived for DateTime<Tz>, due to +// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary. +#[cfg(feature = "arbitrary")] +impl<'a, Tz> arbitrary::Arbitrary<'a> for DateTime<Tz> +where + Tz: TimeZone, + <Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<DateTime<Tz>> { + let datetime = NaiveDateTime::arbitrary(u)?; + let offset = <Tz as TimeZone>::Offset::arbitrary(u)?; + Ok(DateTime::from_utc(datetime, offset)) + } +} + +#[test] +fn test_add_sub_months() { + let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); + assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap()); + + let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap(); + assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); + assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap()); + + let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); + assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap()); + + let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap(); + assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); + assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap()); +} + +#[test] +fn test_auto_conversion() { + let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); + let cdt_dt = FixedOffset::west_opt(5 * 60 * 60) + .unwrap() + .with_ymd_and_hms(2018, 9, 5, 18, 58, 0) + .unwrap(); + let utc_dt2: DateTime<Utc> = cdt_dt.into(); + assert_eq!(utc_dt, utc_dt2); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed) +where + FUtc: Fn(&DateTime<Utc>) -> Result<String, E>, + FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>, + E: ::core::fmt::Debug, +{ + assert_eq!( + to_string_utc(&Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()).ok(), + Some(r#""2014-07-24T12:34:06Z""#.into()) + ); + + assert_eq!( + to_string_fixed( + &FixedOffset::east_opt(3660).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() + ) + .ok(), + Some(r#""2014-07-24T12:34:06+01:01""#.into()) + ); + assert_eq!( + to_string_fixed( + &FixedOffset::east_opt(3650).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() + ) + .ok(), + Some(r#""2014-07-24T12:34:06+01:00:50""#.into()) + ); +} + +#[cfg(all(test, feature = "clock", any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json<FUtc, FFixed, FLocal, E>( + utc_from_str: FUtc, + fixed_from_str: FFixed, + local_from_str: FLocal, +) where + FUtc: Fn(&str) -> Result<DateTime<Utc>, E>, + FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>, + FLocal: Fn(&str) -> Result<DateTime<Local>, E>, + E: ::core::fmt::Debug, +{ + // should check against the offset as well (the normal DateTime comparison will ignore them) + fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!( + norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap())) + ); + assert_eq!( + norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap())) + ); + + assert_eq!( + norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), + norm(&Some( + FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() + )) + ); + assert_eq!( + norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), + norm(&Some( + FixedOffset::east_opt(60 * 60 + 23 * 60) + .unwrap() + .with_ymd_and_hms(2014, 7, 24, 13, 57, 6) + .unwrap() + )) + ); + + // we don't know the exact local offset but we can check that + // the conversion didn't change the instant itself + assert_eq!( + local_from_str(r#""2014-07-24T12:34:06Z""#).expect("local shouuld parse"), + Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() + ); + assert_eq!( + local_from_str(r#""2014-07-24T13:57:06+01:23""#).expect("local should parse with offset"), + Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() + ); + + assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); + assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); +} + +#[cfg(all(test, feature = "clock", feature = "rustc-serialize"))] +fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>( + utc_from_str: FUtc, + fixed_from_str: FFixed, + local_from_str: FLocal, +) where + FUtc: Fn(&str) -> Result<rustc_serialize::TsSeconds<Utc>, E>, + FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, E>, + FLocal: Fn(&str) -> Result<rustc_serialize::TsSeconds<Local>, E>, + E: ::core::fmt::Debug, +{ + fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> { + dt.as_ref().map(|dt| (dt, dt.offset())) + } + + assert_eq!( + norm(&utc_from_str("0").ok().map(DateTime::from)), + norm(&Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap())) + ); + assert_eq!( + norm(&utc_from_str("-1").ok().map(DateTime::from)), + norm(&Some(Utc.with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap())) + ); + + assert_eq!( + norm(&fixed_from_str("0").ok().map(DateTime::from)), + norm(&Some( + FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() + )) + ); + assert_eq!( + norm(&fixed_from_str("-1").ok().map(DateTime::from)), + norm(&Some( + FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap() + )) + ); + + assert_eq!( + *fixed_from_str("0").expect("0 timestamp should parse"), + Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() + ); + assert_eq!( + *local_from_str("-1").expect("-1 timestamp should parse"), + Utc.with_ymd_and_hms(1969, 12, 31, 23, 59, 59).unwrap() + ); +} diff --git a/vendor/chrono/src/datetime/rustc_serialize.rs b/vendor/chrono/src/datetime/rustc_serialize.rs new file mode 100644 index 000000000..18a67d622 --- /dev/null +++ b/vendor/chrono/src/datetime/rustc_serialize.rs @@ -0,0 +1,123 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "rustc-serialize")))] + +use super::DateTime; +#[cfg(feature = "clock")] +use crate::offset::Local; +use crate::offset::{FixedOffset, LocalResult, TimeZone, Utc}; +use core::fmt; +use core::ops::Deref; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + +impl<Tz: TimeZone> Encodable for DateTime<Tz> { + fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } +} + +// lik? function to convert a LocalResult into a serde-ish Result +fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error> +where + D: Decoder, + T: fmt::Display, +{ + match me { + LocalResult::None => Err(d.error("value is not a legal timestamp")), + LocalResult::Ambiguous(..) => Err(d.error("value is an ambiguous timestamp")), + LocalResult::Single(val) => Ok(val), + } +} + +impl Decodable for DateTime<FixedOffset> { + fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<FixedOffset>, D::Error> { + d.read_str()?.parse::<DateTime<FixedOffset>>().map_err(|_| d.error("invalid date and time")) + } +} + +#[allow(deprecated)] +impl Decodable for TsSeconds<FixedOffset> { + #[allow(deprecated)] + fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<FixedOffset>, D::Error> { + from(FixedOffset::east_opt(0).unwrap().timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) + } +} + +impl Decodable for DateTime<Utc> { + fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Utc>, D::Error> { + d.read_str()? + .parse::<DateTime<FixedOffset>>() + .map(|dt| dt.with_timezone(&Utc)) + .map_err(|_| d.error("invalid date and time")) + } +} + +/// A [`DateTime`] that can be deserialized from a timestamp +/// +/// A timestamp here is seconds since the epoch +#[derive(Debug)] +pub struct TsSeconds<Tz: TimeZone>(DateTime<Tz>); + +#[allow(deprecated)] +impl<Tz: TimeZone> From<TsSeconds<Tz>> for DateTime<Tz> { + /// Pull the inner `DateTime<Tz>` out + #[allow(deprecated)] + fn from(obj: TsSeconds<Tz>) -> DateTime<Tz> { + obj.0 + } +} + +#[allow(deprecated)] +impl<Tz: TimeZone> Deref for TsSeconds<Tz> { + type Target = DateTime<Tz>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[allow(deprecated)] +impl Decodable for TsSeconds<Utc> { + fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Utc>, D::Error> { + from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(TsSeconds) + } +} + +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl Decodable for DateTime<Local> { + fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Local>, D::Error> { + match d.read_str()?.parse::<DateTime<FixedOffset>>() { + Ok(dt) => Ok(dt.with_timezone(&Local)), + Err(_) => Err(d.error("invalid date and time")), + } + } +} + +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +#[allow(deprecated)] +impl Decodable for TsSeconds<Local> { + #[allow(deprecated)] + fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<Local>, D::Error> { + from(Utc.timestamp_opt(d.read_i64()?, 0), d).map(|dt| TsSeconds(dt.with_timezone(&Local))) + } +} + +#[cfg(test)] +use rustc_serialize::json; + +#[test] +fn test_encodable() { + super::test_encodable_json(json::encode, json::encode); +} + +#[cfg(feature = "clock")] +#[test] +fn test_decodable() { + super::test_decodable_json(json::decode, json::decode, json::decode); +} + +#[cfg(feature = "clock")] +#[test] +fn test_decodable_timestamps() { + super::test_decodable_json_timestamps(json::decode, json::decode, json::decode); +} diff --git a/vendor/chrono/src/datetime/serde.rs b/vendor/chrono/src/datetime/serde.rs new file mode 100644 index 000000000..ab0126e2b --- /dev/null +++ b/vendor/chrono/src/datetime/serde.rs @@ -0,0 +1,1150 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "serde")))] + +use core::fmt; +use serde::{de, ser}; + +use super::DateTime; +use crate::naive::datetime::serde::serde_from; +#[cfg(feature = "clock")] +use crate::offset::Local; +use crate::offset::{FixedOffset, TimeZone, Utc}; + +#[doc(hidden)] +#[derive(Debug)] +pub struct SecondsTimestampVisitor; + +#[doc(hidden)] +#[derive(Debug)] +pub struct NanoSecondsTimestampVisitor; + +#[doc(hidden)] +#[derive(Debug)] +pub struct MicroSecondsTimestampVisitor; + +#[doc(hidden)] +#[derive(Debug)] +pub struct MilliSecondsTimestampVisitor; + +/// Serialize into a rfc3339 time string +/// +/// See [the `serde` module](./serde/index.html) for alternate +/// serializations. +impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + struct FormatWrapped<'a, D: 'a> { + inner: &'a D, + } + + impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } + } + + // Debug formatting is correct RFC3339, and it allows Zulu. + serializer.collect_str(&FormatWrapped { inner: &self }) + } +} + +struct DateTimeVisitor; + +impl<'de> de::Visitor<'de> for DateTimeVisitor { + type Value = DateTime<FixedOffset>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a formatted date and time string or a unix timestamp") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + value.parse().map_err(E::custom) + } +} + +/// Deserialize a value that optionally includes a timezone offset in its +/// string representation +/// +/// The value to be deserialized must be an rfc3339 string. +/// +/// See [the `serde` module](./serde/index.html) for alternate +/// deserialization formats. +impl<'de> de::Deserialize<'de> for DateTime<FixedOffset> { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(DateTimeVisitor) + } +} + +/// Deserialize into a UTC value +/// +/// The value to be deserialized must be an rfc3339 string. +/// +/// See [the `serde` module](./serde/index.html) for alternate +/// deserialization formats. +impl<'de> de::Deserialize<'de> for DateTime<Utc> { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Utc)) + } +} + +/// Deserialize a value that includes no timezone in its string +/// representation +/// +/// The value to be deserialized must be an rfc3339 string. +/// +/// See [the `serde` module](./serde/index.html) for alternate +/// serialization formats. +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +impl<'de> de::Deserialize<'de> for DateTime<Local> { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local)) + } +} + +/// Ser/de to/from timestamps in nanoseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_nanoseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_nanoseconds")] +/// time: DateTime<Utc> +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_nanoseconds { + use core::fmt; + use serde::{de, ser}; + + use crate::offset::TimeZone; + use crate::{DateTime, Utc}; + + use super::{serde_from, NanoSecondsTimestampVisitor}; + + /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_nanos()) + } + + /// Deserialize a [`DateTime`] from a nanosecond timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_nanoseconds::deserialize as from_nano_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimestampVisitor) + } + + impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { + type Value = DateTime<Utc>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in nanoseconds") + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt(value / 1_000_000_000, (value % 1_000_000_000) as u32), + &value, + ) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32), + &value, + ) + } + } +} + +/// Ser/de to/from optional timestamps in nanoseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_nanoseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_nanoseconds_option")] +/// time: Option<DateTime<Utc>> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_nanoseconds_option { + use core::fmt; + use serde::{de, ser}; + + use crate::{DateTime, Utc}; + + use super::NanoSecondsTimestampVisitor; + + /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a nanosecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionNanoSecondsTimestampVisitor) + } + + struct OptionNanoSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { + type Value = Option<DateTime<Utc>>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in nanoseconds or none") + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Ser/de to/from timestamps in microseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_microseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_microseconds")] +/// time: DateTime<Utc> +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_microseconds { + use core::fmt; + use serde::{de, ser}; + + use super::{serde_from, MicroSecondsTimestampVisitor}; + use crate::offset::TimeZone; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of microseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_microseconds::serialize as to_micro_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_micros()) + } + + /// Deserialize a `DateTime` from a microsecond timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_microseconds::deserialize as from_micro_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimestampVisitor) + } + + impl<'de> de::Visitor<'de> for MicroSecondsTimestampVisitor { + type Value = DateTime<Utc>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in microseconds") + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt(value / 1_000_000, ((value % 1_000_000) * 1_000) as u32), + &value, + ) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt((value / 1_000_000) as i64, ((value % 1_000_000) * 1_000) as u32), + &value, + ) + } + } +} + +/// Ser/de to/from optional timestamps in microseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_microseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_microseconds_option")] +/// time: Option<DateTime<Utc>> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_microseconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::MicroSecondsTimestampVisitor; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of microseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a microsecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_microseconds_option::deserialize as from_micro_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionMicroSecondsTimestampVisitor) + } + + struct OptionMicroSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { + type Value = Option<DateTime<Utc>>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in microseconds or none") + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Ser/de to/from timestamps in milliseconds +/// +/// Intended for use with `serde`s `with` attribute. +/// +/// # Example +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_milliseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_milliseconds")] +/// time: DateTime<Utc> +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_milliseconds { + use core::fmt; + use serde::{de, ser}; + + use super::{serde_from, MilliSecondsTimestampVisitor}; + use crate::offset::TimeZone; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of milliseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_milliseconds::serialize as to_milli_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_millis()) + } + + /// Deserialize a `DateTime` from a millisecond timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_milliseconds::deserialize as from_milli_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc)) + } + + impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { + type Value = DateTime<Utc>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in milliseconds") + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from(Utc.timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32), &value) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from( + Utc.timestamp_opt((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32), + &value, + ) + } + } +} + +/// Ser/de to/from optional timestamps in milliseconds +/// +/// Intended for use with `serde`s `with` attribute. +/// +/// # Example +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_milliseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_milliseconds_option")] +/// time: Option<DateTime<Utc>> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_milliseconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::MilliSecondsTimestampVisitor; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a millisecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; + /// + /// #[derive(Deserialize, PartialEq, Debug)] + /// #[serde(untagged)] + /// enum E<T> { + /// V(T), + /// } + /// + /// #[derive(Deserialize, PartialEq, Debug)] + /// struct S { + /// #[serde(default, deserialize_with = "from_milli_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s: E<S> = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// assert_eq!(my_s, E::V(S { time: Some(Utc.timestamp(1526522699, 918000000)) })); + /// let s: E<S> = serde_json::from_str(r#"{ "time": null }"#)?; + /// assert_eq!(s, E::V(S { time: None })); + /// let t: E<S> = serde_json::from_str(r#"{}"#)?; + /// assert_eq!(t, E::V(S { time: None })); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionMilliSecondsTimestampVisitor) + .map(|opt| opt.map(|dt| dt.with_timezone(&Utc))) + } + + struct OptionMilliSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { + type Value = Option<DateTime<Utc>>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in milliseconds or none") + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Ser/de to/from timestamps in seconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_seconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_seconds")] +/// time: DateTime<Utc> +/// } +/// +/// let time = Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1431684000}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_seconds { + use core::fmt; + use serde::{de, ser}; + + use super::{serde_from, SecondsTimestampVisitor}; + use crate::offset::TimeZone; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of seconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_seconds::serialize as to_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s = S { + /// time: Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp()) + } + + /// Deserialize a `DateTime` from a seconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_seconds::deserialize as from_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_ts")] + /// time: DateTime<Utc> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimestampVisitor) + } + + impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { + type Value = DateTime<Utc>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in seconds") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from(Utc.timestamp_opt(value, 0), &value) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + serde_from(Utc.timestamp_opt(value as i64, 0), &value) + } + } +} + +/// Ser/de to/from optional timestamps in seconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{TimeZone, DateTime, Utc}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::serde::ts_seconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_seconds_option")] +/// time: Option<DateTime<Utc>> +/// } +/// +/// let time = Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1431684000}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_seconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::SecondsTimestampVisitor; + use crate::{DateTime, Utc}; + + /// Serialize a UTC datetime into an integer number of seconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{TimeZone, DateTime, Utc}; + /// # use serde_derive::Serialize; + /// use chrono::serde::ts_seconds_option::serialize as to_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s = S { + /// time: Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `DateTime` from a seconds timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{DateTime, Utc}; + /// # use serde_derive::Deserialize; + /// use chrono::serde::ts_seconds_option::deserialize as from_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_tsopt")] + /// time: Option<DateTime<Utc>> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionSecondsTimestampVisitor) + } + + struct OptionSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { + type Value = Option<DateTime<Utc>>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in seconds or none") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +#[test] +fn test_serde_serialize() { + super::test_encodable_json(serde_json::to_string, serde_json::to_string); +} + +#[cfg(feature = "clock")] +#[test] +fn test_serde_deserialize() { + super::test_decodable_json( + |input| serde_json::from_str(input), + |input| serde_json::from_str(input), + |input| serde_json::from_str(input), + ); +} + +#[test] +fn test_serde_bincode() { + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + use bincode::{deserialize, serialize}; + + let dt = Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap(); + let encoded = serialize(&dt).unwrap(); + let decoded: DateTime<Utc> = deserialize(&encoded).unwrap(); + assert_eq!(dt, decoded); + assert_eq!(dt.offset(), decoded.offset()); +} diff --git a/vendor/chrono/src/datetime/tests.rs b/vendor/chrono/src/datetime/tests.rs new file mode 100644 index 000000000..ebd32cae9 --- /dev/null +++ b/vendor/chrono/src/datetime/tests.rs @@ -0,0 +1,952 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use super::DateTime; +use crate::naive::{NaiveDate, NaiveTime}; +#[cfg(feature = "clock")] +use crate::offset::Local; +use crate::offset::{FixedOffset, TimeZone, Utc}; +use crate::oldtime::Duration; +#[cfg(feature = "clock")] +use crate::Datelike; +use crate::{Days, LocalResult, Months, NaiveDateTime}; + +#[derive(Clone)] +struct DstTester; + +impl DstTester { + fn winter_offset() -> FixedOffset { + FixedOffset::east_opt(8 * 60 * 60).unwrap() + } + fn summer_offset() -> FixedOffset { + FixedOffset::east_opt(9 * 60 * 60).unwrap() + } + + const TO_WINTER_MONTH_DAY: (u32, u32) = (4, 15); + const TO_SUMMER_MONTH_DAY: (u32, u32) = (9, 15); + + fn transition_start_local() -> NaiveTime { + NaiveTime::from_hms_opt(2, 0, 0).unwrap() + } +} + +impl TimeZone for DstTester { + type Offset = FixedOffset; + + fn from_offset(_: &Self::Offset) -> Self { + DstTester + } + + fn offset_from_local_date(&self, _: &NaiveDate) -> crate::LocalResult<Self::Offset> { + unimplemented!() + } + + fn offset_from_local_datetime( + &self, + local: &NaiveDateTime, + ) -> crate::LocalResult<Self::Offset> { + let local_to_winter_transition_start = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()); + + let local_to_winter_transition_end = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local() - Duration::hours(1)); + + let local_to_summer_transition_start = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()); + + let local_to_summer_transition_end = NaiveDate::from_ymd_opt( + local.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local() + Duration::hours(1)); + + if *local < local_to_winter_transition_end || *local >= local_to_summer_transition_end { + LocalResult::Single(DstTester::summer_offset()) + } else if *local >= local_to_winter_transition_start + && *local < local_to_summer_transition_start + { + LocalResult::Single(DstTester::winter_offset()) + } else if *local >= local_to_winter_transition_end + && *local < local_to_winter_transition_start + { + LocalResult::Ambiguous(DstTester::winter_offset(), DstTester::summer_offset()) + } else if *local >= local_to_summer_transition_start + && *local < local_to_summer_transition_end + { + LocalResult::None + } else { + panic!("Unexpected local time {}", local) + } + } + + fn offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset { + unimplemented!() + } + + fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset { + let utc_to_winter_transition = NaiveDate::from_ymd_opt( + utc.year(), + DstTester::TO_WINTER_MONTH_DAY.0, + DstTester::TO_WINTER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()) + - DstTester::summer_offset(); + + let utc_to_summer_transition = NaiveDate::from_ymd_opt( + utc.year(), + DstTester::TO_SUMMER_MONTH_DAY.0, + DstTester::TO_SUMMER_MONTH_DAY.1, + ) + .unwrap() + .and_time(DstTester::transition_start_local()) + - DstTester::winter_offset(); + + if *utc < utc_to_winter_transition || *utc >= utc_to_summer_transition { + DstTester::summer_offset() + } else if *utc >= utc_to_winter_transition && *utc < utc_to_summer_transition { + DstTester::winter_offset() + } else { + panic!("Unexpected utc time {}", utc) + } + } +} + +#[test] +fn test_datetime_add_days() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-05-11 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-05-11 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), + "2014-06-10 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), + "2014-06-10 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-04-11 07:08:09 +09:00" + ); + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(10)), + "2014-04-16 07:08:09 +08:00" + ); + + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(5)), + "2014-09-11 07:08:09 +08:00" + ); + assert_eq!( + format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(10)), + "2014-09-16 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_sub_days() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), + "2014-05-01 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), + "2014-05-01 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), + "2014-04-01 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), + "2014-04-01 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_add_months() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), + "2014-06-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), + "2014-06-06 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), + "2014-10-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), + "2014-10-06 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_sub_months() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), + "2014-04-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), + "2014-04-06 07:08:09 +09:00" + ); + + assert_eq!( + format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), + "2013-12-06 07:08:09 -05:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), + "2013-12-06 07:08:09 +09:00" + ); +} + +#[test] +fn test_datetime_offset() { + let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); + let edt = FixedOffset::west_opt(4 * 60 * 60).unwrap(); + let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); + + assert_eq!( + format!("{}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 UTC" + ); + assert_eq!( + format!("{}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 -04:00" + ); + assert_eq!( + format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06 07:08:09 +09:00" + ); + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), + "2014-05-06T07:08:09+09:00" + ); + + // edge cases + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), + "2014-05-06T00:00:00+09:00" + ); + assert_eq!( + format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59Z" + ); + assert_eq!( + format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59-04:00" + ); + assert_eq!( + format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), + "2014-05-06T23:59:59+09:00" + ); + + let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); + assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap()); + assert_eq!( + dt + Duration::seconds(3600 + 60 + 1), + Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap() + ); + assert_eq!( + dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()), + Duration::seconds(-7 * 3600 - 3 * 60 - 3) + ); + + assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc); + assert_eq!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), edt); + assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est); +} + +#[test] +fn test_datetime_date_and_time() { + let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap()); + + let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2016, 5, 4, 3, 2, 1).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap()); + + let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap(); + let d = tz.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); + assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap()); + assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap()); + + let utc_d = Utc.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); + assert!(utc_d < d); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_with_timezone() { + let local_now = Local::now(); + let utc_now = local_now.with_timezone(&Utc); + let local_now2 = utc_now.with_timezone(&Local); + assert_eq!(local_now, local_now2); +} + +#[test] +fn test_datetime_rfc2822_and_rfc3339() { + let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); + assert_eq!( + Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0000" + ); + assert_eq!( + Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(), + "2015-02-18T23:16:09+00:00" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:16:09 +0500" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:16:09.150+05:00" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap() + .to_rfc2822(), + "Wed, 18 Feb 2015 23:59:60 +0500" + ); + assert_eq!( + edt.from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap() + .to_rfc3339(), + "2015-02-18T23:59:60.234567+05:00" + ); + + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), + Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) + ); + assert_eq!( + DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), + Ok(edt + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 59, 59, 1_000) + .unwrap() + ) + .unwrap()) + ); + assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); + assert_eq!( + DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), + Ok(edt + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_micro_opt(23, 59, 59, 1_234_567) + .unwrap() + ) + .unwrap()) + ); +} + +#[test] +fn test_rfc3339_opts() { + use crate::SecondsFormat::*; + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_000) + .unwrap(), + ) + .unwrap(); + assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); + assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); + assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00"); + assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00"); + assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00"); + + let ut = DateTime::<Utc>::from_utc(dt.naive_utc(), Utc); + assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00"); + assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z"); + assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00"); + assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z"); + assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z"); + assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z"); + assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z"); +} + +#[test] +#[should_panic] +fn test_rfc3339_opts_nonexhaustive() { + use crate::SecondsFormat; + let dt = Utc.with_ymd_and_hms(1999, 10, 9, 1, 2, 3).unwrap(); + dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); +} + +#[test] +fn test_datetime_from_str() { + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::east_opt(0) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(), + Ok(FixedOffset::west_opt(10 * 3600) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(13, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err()); + + assert_eq!( + "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert_eq!( + "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(), + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2015, 2, 18) + .unwrap() + .and_hms_milli_opt(23, 16, 9, 150) + .unwrap() + ) + .unwrap()) + ); + assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err()); + + // no test for `DateTime<Local>`, we cannot verify that much. +} + +#[test] +fn test_datetime_parse_from_str() { + let ymdhms = |y, m, d, h, n, s, off| { + FixedOffset::east_opt(off).unwrap().with_ymd_and_hms(y, m, d, h, n, s).unwrap() + }; + assert_eq!( + DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56, 570 * 60)) + ); // ignore offset + assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset + assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") + .is_err()); + assert_eq!( + Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), + Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap()) + ); +} + +#[test] +fn test_to_string_round_trip() { + let dt = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); + let _dt: DateTime<Utc> = dt.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap()); + let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); + + let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(0).unwrap()); + let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap(); +} + +#[test] +#[cfg(feature = "clock")] +fn test_to_string_round_trip_with_local() { + let ndt = Local::now(); + let _dt: DateTime<FixedOffset> = ndt.to_string().parse().unwrap(); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_format_with_local() { + // if we are not around the year boundary, local and UTC date should have the same year + let dt = Local::now().with_month(5).unwrap(); + assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string()); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_is_copy() { + // UTC is known to be `Copy`. + let a = Utc::now(); + let b = a; + assert_eq!(a, b); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_is_send() { + use std::thread; + + // UTC is known to be `Send`. + let a = Utc::now(); + thread::spawn(move || { + let _ = a; + }) + .join() + .unwrap(); +} + +#[test] +fn test_subsecond_part() { + let datetime = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2014, 7, 8) + .unwrap() + .and_hms_nano_opt(9, 10, 11, 1234567) + .unwrap(), + ) + .unwrap(); + + assert_eq!(1, datetime.timestamp_subsec_millis()); + assert_eq!(1234, datetime.timestamp_subsec_micros()); + assert_eq!(1234567, datetime.timestamp_subsec_nanos()); +} + +#[test] +#[cfg(not(target_os = "windows"))] +fn test_from_system_time() { + use std::time::Duration; + + let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); + let nanos = 999_999_999; + + // SystemTime -> DateTime<Utc> + assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1).unwrap() + ) + .unwrap() + ); + + // DateTime<Utc> -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH - Duration::new(999_999_999, 999_999_999) + ); + + // DateTime<any tz> -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())), + UNIX_EPOCH + ); + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())), + UNIX_EPOCH + ); +} + +#[test] +#[cfg(target_os = "windows")] +fn test_from_system_time() { + use std::time::Duration; + + let nanos = 999_999_000; + + let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); + + // SystemTime -> DateTime<Utc> + assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ); + assert_eq!( + DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1_000) + .unwrap() + ) + .unwrap() + ); + + // DateTime<Utc> -> SystemTime + assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 9, 9) + .unwrap() + .and_hms_nano_opt(1, 46, 39, nanos) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH + Duration::new(999_999_999, nanos) + ); + assert_eq!( + SystemTime::from( + Utc.from_local_datetime( + &NaiveDate::from_ymd_opt(1938, 4, 24) + .unwrap() + .and_hms_nano_opt(22, 13, 20, 1_000) + .unwrap() + ) + .unwrap() + ), + UNIX_EPOCH - Duration::new(999_999_999, nanos) + ); + + // DateTime<any tz> -> SystemTime (via `with_timezone`) + #[cfg(feature = "clock")] + { + assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); + } + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())), + UNIX_EPOCH + ); + assert_eq!( + SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())), + UNIX_EPOCH + ); +} + +#[test] +fn test_datetime_format_alignment() { + let datetime = Utc.with_ymd_and_hms(2007, 1, 2, 0, 0, 0).unwrap(); + + // Item::Literal + let percent = datetime.format("%%"); + assert_eq!(" %", format!("{:>3}", percent)); + assert_eq!("% ", format!("{:<3}", percent)); + assert_eq!(" % ", format!("{:^3}", percent)); + + // Item::Numeric + let year = datetime.format("%Y"); + assert_eq!(" 2007", format!("{:>6}", year)); + assert_eq!("2007 ", format!("{:<6}", year)); + assert_eq!(" 2007 ", format!("{:^6}", year)); + + // Item::Fixed + let tz = datetime.format("%Z"); + assert_eq!(" UTC", format!("{:>5}", tz)); + assert_eq!("UTC ", format!("{:<5}", tz)); + assert_eq!(" UTC ", format!("{:^5}", tz)); + + // [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric] + let ymd = datetime.format("%Y %B %d"); + let ymd_formatted = "2007 January 02"; + assert_eq!(format!(" {}", ymd_formatted), format!("{:>17}", ymd)); + assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd)); + assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd)); +} + +#[test] +fn test_datetime_from_local() { + // 2000-01-12T02:00:00Z + let naivedatetime_utc = + NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap(); + let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc); + + // 2000-01-12T10:00:00+8:00:00 + let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let naivedatetime_east = + NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap(); + let datetime_east = DateTime::<FixedOffset>::from_local(naivedatetime_east, timezone_east); + + // 2000-01-11T19:00:00-7:00:00 + let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap(); + let naivedatetime_west = + NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap(); + let datetime_west = DateTime::<FixedOffset>::from_local(naivedatetime_west, timezone_west); + + assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); + assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_years_elapsed() { + const WEEKS_PER_YEAR: f32 = 52.1775; + + // This is always at least one year because 1 year = 52.1775 weeks. + let one_year_ago = + Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); + // A bit more than 2 years. + let two_year_ago = + Utc::now().date_naive() - Duration::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); + + assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1)); + assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2)); + + // If the given DateTime is later than now, the function will always return 0. + let future = Utc::now().date_naive() + Duration::weeks(12); + assert_eq!(Utc::now().date_naive().years_since(future), None); +} + +#[test] +fn test_datetime_add_assign() { + let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + let datetime = DateTime::<Utc>::from_utc(naivedatetime, Utc); + let mut datetime_add = datetime; + + datetime_add += Duration::seconds(60); + assert_eq!(datetime_add, datetime + Duration::seconds(60)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_add = datetime_add.with_timezone(&timezone); + + assert_eq!(datetime_add, datetime + Duration::seconds(60)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_add = datetime_add.with_timezone(&timezone); + + assert_eq!(datetime_add, datetime + Duration::seconds(60)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_add_assign_local() { + let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + + let datetime = Local.from_utc_datetime(&naivedatetime); + let mut datetime_add = Local.from_utc_datetime(&naivedatetime); + + // ensure we cross a DST transition + for i in 1..=365 { + datetime_add += Duration::days(1); + assert_eq!(datetime_add, datetime + Duration::days(i)) + } +} + +#[test] +fn test_datetime_sub_assign() { + let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(12, 0, 0).unwrap(); + let datetime = DateTime::<Utc>::from_utc(naivedatetime, Utc); + let mut datetime_sub = datetime; + + datetime_sub -= Duration::minutes(90); + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); + + let timezone = FixedOffset::east_opt(60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_sub = datetime_sub.with_timezone(&timezone); + + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); + + let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); + let datetime = datetime.with_timezone(&timezone); + let datetime_sub = datetime_sub.with_timezone(&timezone); + + assert_eq!(datetime_sub, datetime - Duration::minutes(90)); +} + +#[test] +#[cfg(feature = "clock")] +fn test_datetime_sub_assign_local() { + let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + + let datetime = Local.from_utc_datetime(&naivedatetime); + let mut datetime_sub = Local.from_utc_datetime(&naivedatetime); + + // ensure we cross a DST transition + for i in 1..=365 { + datetime_sub -= Duration::days(1); + assert_eq!(datetime_sub, datetime - Duration::days(i)) + } +} diff --git a/vendor/chrono/src/div.rs b/vendor/chrono/src/div.rs deleted file mode 100644 index 64b8e4bce..000000000 --- a/vendor/chrono/src/div.rs +++ /dev/null @@ -1,41 +0,0 @@ -// This is a part of Chrono. -// Portions Copyright 2013-2014 The Rust Project Developers. -// See README.md and LICENSE.txt for details. - -//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/)) - -// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_, -// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf) - -pub use num_integer::{div_floor, div_mod_floor, div_rem, mod_floor}; - -#[cfg(test)] -mod tests { - use super::{div_mod_floor, mod_floor}; - - #[test] - fn test_mod_floor() { - assert_eq!(mod_floor(8, 3), 2); - assert_eq!(mod_floor(8, -3), -1); - assert_eq!(mod_floor(-8, 3), 1); - assert_eq!(mod_floor(-8, -3), -2); - - assert_eq!(mod_floor(1, 2), 1); - assert_eq!(mod_floor(1, -2), -1); - assert_eq!(mod_floor(-1, 2), 1); - assert_eq!(mod_floor(-1, -2), -1); - } - - #[test] - fn test_div_mod_floor() { - assert_eq!(div_mod_floor(8, 3), (2, 2)); - assert_eq!(div_mod_floor(8, -3), (-3, -1)); - assert_eq!(div_mod_floor(-8, 3), (-3, 1)); - assert_eq!(div_mod_floor(-8, -3), (2, -2)); - - assert_eq!(div_mod_floor(1, 2), (0, 1)); - assert_eq!(div_mod_floor(1, -2), (-1, -1)); - assert_eq!(div_mod_floor(-1, 2), (-1, 1)); - assert_eq!(div_mod_floor(-1, -2), (0, -1)); - } -} diff --git a/vendor/chrono/src/format/mod.rs b/vendor/chrono/src/format/mod.rs index a641f196d..c05ba4d04 100644 --- a/vendor/chrono/src/format/mod.rs +++ b/vendor/chrono/src/format/mod.rs @@ -12,10 +12,26 @@ //! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of //! the [`Item`](./enum.Item.html) type. //! They are generated from more readable **format strings**; -//! currently Chrono supports [one built-in syntax closely resembling -//! C's `strftime` format](./strftime/index.html). +//! currently Chrono supports a built-in syntax closely resembling +//! C's `strftime` format. The available options can be found [here](./strftime/index.html). +//! +//! # Example +//! ```rust +//! # use std::error::Error; +//! use chrono::prelude::*; +//! +//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap(); +//! +//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S")); +//! assert_eq!(formatted, "2020-11-10 00:01:32"); +//! +//! let parsed = Utc.datetime_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?; +//! assert_eq!(parsed, date_time); +//! # Ok::<(), chrono::ParseError>(()) +//! ``` -#![allow(ellipsis_inclusive_range_patterns)] +#[cfg(feature = "alloc")] +extern crate alloc; #[cfg(feature = "alloc")] use alloc::boxed::Box; @@ -24,38 +40,40 @@ use alloc::string::{String, ToString}; #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::fmt; +use core::fmt::Write; use core::str::FromStr; #[cfg(any(feature = "std", test))] use std::error::Error; #[cfg(any(feature = "alloc", feature = "std", test))] -use naive::{NaiveDate, NaiveTime}; +use crate::naive::{NaiveDate, NaiveTime}; #[cfg(any(feature = "alloc", feature = "std", test))] -use offset::{FixedOffset, Offset}; +use crate::offset::{FixedOffset, Offset}; #[cfg(any(feature = "alloc", feature = "std", test))] -use {Datelike, Timelike}; -use {Month, ParseMonthError, ParseWeekdayError, Weekday}; +use crate::{Datelike, Timelike}; +use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; #[cfg(feature = "unstable-locales")] pub(crate) mod locales; -pub use self::parse::parse; -pub use self::parsed::Parsed; -pub use self::strftime::StrftimeItems; +pub use parse::parse; +pub use parsed::Parsed; /// L10n locales. #[cfg(feature = "unstable-locales")] pub use pure_rust_locales::Locale; +pub use strftime::StrftimeItems; #[cfg(not(feature = "unstable-locales"))] +#[allow(dead_code)] #[derive(Debug)] struct Locale; /// An uninhabited type used for `InternalNumeric` and `InternalFixed` below. -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Hash)] enum Void {} /// Padding characters for numeric items. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum Pad { /// No padding. None, @@ -78,10 +96,10 @@ pub enum Pad { /// It also trims the preceding whitespace if any. /// It cannot parse the negative number, so some date and time cannot be formatted then /// parsed with the same formatting items. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub enum Numeric { /// Full Gregorian year (FW=4, PW=∞). - /// May accept years before 1 BCE or after 9999 CE, given an initial sign. + /// May accept years before 1 BCE or after 9999 CE, given an initial sign (+/-). Year, /// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year. YearDiv100, @@ -134,24 +152,11 @@ pub enum Numeric { } /// An opaque type representing numeric item types for internal uses only. +#[derive(Clone, Eq, Hash, PartialEq)] pub struct InternalNumeric { _dummy: Void, } -impl Clone for InternalNumeric { - fn clone(&self) -> Self { - match self._dummy {} - } -} - -impl PartialEq for InternalNumeric { - fn eq(&self, _other: &InternalNumeric) -> bool { - match self._dummy {} - } -} - -impl Eq for InternalNumeric {} - impl fmt::Debug for InternalNumeric { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<InternalNumeric>") @@ -162,7 +167,7 @@ impl fmt::Debug for InternalNumeric { /// /// They have their own rules of formatting and parsing. /// Otherwise noted, they print in the specified cases but parse case-insensitively. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub enum Fixed { /// Abbreviated month names. /// @@ -208,6 +213,18 @@ pub enum Fixed { /// The offset is limited from `-24:00` to `+24:00`, /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. TimezoneOffsetColon, + /// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`). + /// + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. + /// The offset is limited from `-24:00:00` to `+24:00:00`, + /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. + TimezoneOffsetDoubleColon, + /// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`). + /// + /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. + /// The offset is limited from `-24` to `+24`, + /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. + TimezoneOffsetTripleColon, /// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`). /// /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace, @@ -234,12 +251,12 @@ pub enum Fixed { } /// An opaque type representing fixed-format item types for internal uses only. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct InternalFixed { val: InternalInternal, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] enum InternalInternal { /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but /// allows missing minutes (per [ISO 8601][iso8601]). @@ -258,18 +275,29 @@ enum InternalInternal { Nanosecond9NoDot, } +#[cfg(any(feature = "alloc", feature = "std", test))] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum Colons { + None, + Single, + Double, + Triple, +} + /// A single formatting item. This is used for both formatting and parsing. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Hash)] pub enum Item<'a> { /// A literally printed and parsed text. Literal(&'a str), /// Same as `Literal` but with the string owned by the item. #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] OwnedLiteral(Box<str>), /// Whitespace. Prints literally but reads zero or more whitespace. Space(&'a str), /// Same as `Space` but with the string owned by the item. #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] OwnedSpace(Box<str>), /// Numeric item. Can be optionally padded to the maximal length (if any) when formatting; /// the parser simply ignores any padded whitespace and zeroes. @@ -317,12 +345,19 @@ macro_rules! internal_fix { } /// An error from the `parse` function. -#[derive(Debug, Clone, PartialEq, Eq, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] pub struct ParseError(ParseErrorKind); +impl ParseError { + /// The category of parse error + pub const fn kind(&self) -> ParseErrorKind { + self.0 + } +} + /// The category of parse error -#[derive(Debug, Clone, PartialEq, Eq, Copy)] -enum ParseErrorKind { +#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] +pub enum ParseErrorKind { /// Given field is out of permitted range. OutOfRange, @@ -350,6 +385,10 @@ enum ParseErrorKind { /// There was an error on the formatting string, or there were non-supported formating items. BadFormat, + + // TODO: Change this to `#[non_exhaustive]` (on the enum) when MSRV is increased + #[doc(hidden)] + __Nonexhaustive, } /// Same as `Result<T, ParseError>`. @@ -365,11 +404,13 @@ impl fmt::Display for ParseError { ParseErrorKind::TooShort => write!(f, "premature end of input"), ParseErrorKind::TooLong => write!(f, "trailing input"), ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"), + _ => unreachable!(), } } } #[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Error for ParseError { #[allow(deprecated)] fn description(&self) -> &str { @@ -386,14 +427,72 @@ const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort); const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong); const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); +#[cfg(any(feature = "alloc", feature = "std", test))] +struct Locales { + short_months: &'static [&'static str], + long_months: &'static [&'static str], + short_weekdays: &'static [&'static str], + long_weekdays: &'static [&'static str], + am_pm: &'static [&'static str], +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl Locales { + fn new(_locale: Option<Locale>) -> Self { + #[cfg(feature = "unstable-locales")] + { + let locale = _locale.unwrap_or(Locale::POSIX); + Self { + short_months: locales::short_months(locale), + long_months: locales::long_months(locale), + short_weekdays: locales::short_weekdays(locale), + long_weekdays: locales::long_weekdays(locale), + am_pm: locales::am_pm(locale), + } + } + #[cfg(not(feature = "unstable-locales"))] + Self { + short_months: &[ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ], + long_months: &[ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ], + short_weekdays: &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], + long_weekdays: &[ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + ], + am_pm: &["AM", "PM"], + } + } +} + /// Formats single formatting item #[cfg(any(feature = "alloc", feature = "std", test))] -pub fn format_item<'a>( +#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] +pub fn format_item( w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>, off: Option<&(String, FixedOffset)>, - item: &Item<'a>, + item: &Item<'_>, ) -> fmt::Result { let mut result = String::new(); format_inner(&mut result, date, time, off, item, None)?; @@ -401,54 +500,17 @@ pub fn format_item<'a>( } #[cfg(any(feature = "alloc", feature = "std", test))] -fn format_inner<'a>( +fn format_inner( result: &mut String, date: Option<&NaiveDate>, time: Option<&NaiveTime>, off: Option<&(String, FixedOffset)>, - item: &Item<'a>, - _locale: Option<Locale>, + item: &Item<'_>, + locale: Option<Locale>, ) -> fmt::Result { - #[cfg(feature = "unstable-locales")] - let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { - let locale = _locale.unwrap_or(Locale::POSIX); - let am_pm = locales::am_pm(locale); - ( - locales::short_months(locale), - locales::long_months(locale), - locales::short_weekdays(locale), - locales::long_weekdays(locale), - am_pm, - &[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()], - ) - }; - #[cfg(not(feature = "unstable-locales"))] - let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = { - ( - &["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - &[ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ], - &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], - &["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - &["AM", "PM"], - &["am", "pm"], - ) - }; + let locale = Locales::new(locale); - use core::fmt::Write; - use div::{div_floor, mod_floor}; + use num_integer::{div_floor, mod_floor}; match *item { Item::Literal(s) | Item::Space(s) => result.push_str(s), @@ -458,12 +520,8 @@ fn format_inner<'a>( Item::Numeric(ref spec, ref pad) => { use self::Numeric::*; - let week_from_sun = |d: &NaiveDate| { - (d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7 - }; - let week_from_mon = |d: &NaiveDate| { - (d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7 - }; + let week_from_sun = |d: &NaiveDate| d.weeks_from(Weekday::Sun); + let week_from_mon = |d: &NaiveDate| d.weeks_from(Weekday::Mon); let (width, v) = match *spec { Year => (4, date.map(|d| i64::from(d.year()))), @@ -501,7 +559,7 @@ fn format_inner<'a>( }; if let Some(v) = v { - if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) { + if (spec == &Year || spec == &IsoYear) && !(0..10_000).contains(&v) { // non-four-digit years require an explicit sign as per ISO 8601 match *pad { Pad::None => write!(result, "{:+}", v), @@ -523,60 +581,41 @@ fn format_inner<'a>( Item::Fixed(ref spec) => { use self::Fixed::*; - /// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. - /// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. - fn write_local_minus_utc( - result: &mut String, - off: FixedOffset, - allow_zulu: bool, - use_colon: bool, - ) -> fmt::Result { - let off = off.local_minus_utc(); - if !allow_zulu || off != 0 { - let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; - if use_colon { - write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60) - } else { - write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60) - } - } else { - result.push_str("Z"); - Ok(()) - } - } - let ret = match *spec { ShortMonthName => date.map(|d| { - result.push_str(short_months[d.month0() as usize]); + result.push_str(locale.short_months[d.month0() as usize]); Ok(()) }), LongMonthName => date.map(|d| { - result.push_str(long_months[d.month0() as usize]); + result.push_str(locale.long_months[d.month0() as usize]); Ok(()) }), ShortWeekdayName => date.map(|d| { - result - .push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str( + locale.short_weekdays[d.weekday().num_days_from_sunday() as usize], + ); Ok(()) }), LongWeekdayName => date.map(|d| { - result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str( + locale.long_weekdays[d.weekday().num_days_from_sunday() as usize], + ); Ok(()) }), LowerAmPm => time.map(|t| { - #[cfg_attr(feature = "cargo-clippy", allow(useless_asref))] - { - result.push_str(if t.hour12().0 { - am_pm_lowercase[1].as_ref() - } else { - am_pm_lowercase[0].as_ref() - }); + let ampm = if t.hour12().0 { locale.am_pm[1] } else { locale.am_pm[0] }; + for char in ampm.chars() { + result.extend(char.to_lowercase()) } Ok(()) }), UpperAmPm => time.map(|t| { - result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] }); + result.push_str(if t.hour12().0 { + locale.am_pm[1] + } else { + locale.am_pm[0] + }); Ok(()) }), Nanosecond => time.map(|t| { @@ -618,21 +657,23 @@ fn format_inner<'a>( let nano = t.nanosecond() % 1_000_000_000; write!(result, "{:09}", nano) }), - TimezoneName => off.map(|&(ref name, _)| { + TimezoneName => off.map(|(name, _)| { result.push_str(name); Ok(()) }), - TimezoneOffsetColon => { - off.map(|&(_, off)| write_local_minus_utc(result, off, false, true)) - } - TimezoneOffsetColonZ => { - off.map(|&(_, off)| write_local_minus_utc(result, off, true, true)) - } + TimezoneOffsetColon => off + .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Single)), + TimezoneOffsetDoubleColon => off + .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Double)), + TimezoneOffsetTripleColon => off + .map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::Triple)), + TimezoneOffsetColonZ => off + .map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::Single)), TimezoneOffset => { - off.map(|&(_, off)| write_local_minus_utc(result, off, false, false)) + off.map(|&(_, off)| write_local_minus_utc(result, off, false, Colons::None)) } TimezoneOffsetZ => { - off.map(|&(_, off)| write_local_minus_utc(result, off, true, false)) + off.map(|&(_, off)| write_local_minus_utc(result, off, true, Colons::None)) } Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => { panic!("Do not try to write %#z it is undefined") @@ -641,19 +682,7 @@ fn format_inner<'a>( // same as `%a, %d %b %Y %H:%M:%S %z` { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { - let sec = t.second() + t.nanosecond() / 1_000_000_000; - write!( - result, - "{}, {:02} {} {:04} {:02}:{:02}:{:02} ", - short_weekdays[d.weekday().num_days_from_sunday() as usize], - d.day(), - short_months[d.month0() as usize], - d.year(), - t.hour(), - t.minute(), - sec - )?; - Some(write_local_minus_utc(result, off, false, false)) + Some(write_rfc2822_inner(result, d, t, off, locale)) } else { None } @@ -662,10 +691,7 @@ fn format_inner<'a>( // same as `%Y-%m-%dT%H:%M:%S%.f%:z` { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { - // reuse `Debug` impls which already print ISO 8601 format. - // this is faster in this way. - write!(result, "{:?}T{:?}", d, t)?; - Some(write_local_minus_utc(result, off, false, true)) + Some(write_rfc3339(result, crate::NaiveDateTime::new(*d, *t), off)) } else { None } @@ -683,9 +709,114 @@ fn format_inner<'a>( Ok(()) } +/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`. +/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true. +#[cfg(any(feature = "alloc", feature = "std", test))] +fn write_local_minus_utc( + result: &mut String, + off: FixedOffset, + allow_zulu: bool, + colon_type: Colons, +) -> fmt::Result { + let off = off.local_minus_utc(); + if allow_zulu && off == 0 { + result.push('Z'); + return Ok(()); + } + let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; + result.push(sign); + + write_hundreds(result, (off / 3600) as u8)?; + + match colon_type { + Colons::None => write_hundreds(result, (off / 60 % 60) as u8), + Colons::Single => { + result.push(':'); + write_hundreds(result, (off / 60 % 60) as u8) + } + Colons::Double => { + result.push(':'); + write_hundreds(result, (off / 60 % 60) as u8)?; + result.push(':'); + write_hundreds(result, (off % 60) as u8) + } + Colons::Triple => Ok(()), + } +} + +/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub(crate) fn write_rfc3339( + result: &mut String, + dt: crate::NaiveDateTime, + off: FixedOffset, +) -> fmt::Result { + // reuse `Debug` impls which already print ISO 8601 format. + // this is faster in this way. + write!(result, "{:?}", dt)?; + write_local_minus_utc(result, off, false, Colons::Single) +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` +pub(crate) fn write_rfc2822( + result: &mut String, + dt: crate::NaiveDateTime, + off: FixedOffset, +) -> fmt::Result { + write_rfc2822_inner(result, &dt.date(), &dt.time(), off, Locales::new(None)) +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` +fn write_rfc2822_inner( + result: &mut String, + d: &NaiveDate, + t: &NaiveTime, + off: FixedOffset, + locale: Locales, +) -> fmt::Result { + let year = d.year(); + // RFC2822 is only defined on years 0 through 9999 + if !(0..=9999).contains(&year) { + return Err(fmt::Error); + } + + result.push_str(locale.short_weekdays[d.weekday().num_days_from_sunday() as usize]); + result.push_str(", "); + write_hundreds(result, d.day() as u8)?; + result.push(' '); + result.push_str(locale.short_months[d.month0() as usize]); + result.push(' '); + write_hundreds(result, (year / 100) as u8)?; + write_hundreds(result, (year % 100) as u8)?; + result.push(' '); + write_hundreds(result, t.hour() as u8)?; + result.push(':'); + write_hundreds(result, t.minute() as u8)?; + result.push(':'); + let sec = t.second() + t.nanosecond() / 1_000_000_000; + write_hundreds(result, sec as u8)?; + result.push(' '); + write_local_minus_utc(result, off, false, Colons::None) +} + +/// Equivalent to `{:02}` formatting for n < 100. +pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result { + if n >= 100 { + return Err(fmt::Error); + } + + let tens = b'0' + n / 10; + let ones = b'0' + n % 10; + w.write_char(tens as char)?; + w.write_char(ones as char) +} + /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. #[cfg(any(feature = "alloc", feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] pub fn format<'a, I, B>( w: &mut fmt::Formatter, date: Option<&NaiveDate>, @@ -715,6 +846,7 @@ pub mod strftime; /// A *temporary* object which can be used as an argument to `format!` or others. /// This is normally constructed via `format` methods of each date and time type. #[cfg(any(feature = "alloc", feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[derive(Debug)] pub struct DelayedFormat<I> { /// The date view, if any. @@ -726,6 +858,9 @@ pub struct DelayedFormat<I> { /// An iterator returning formatting items. items: I, /// Locale used for text. + // TODO: Only used with the locale feature. We should make this property + // only present when the feature is enabled. + #[cfg(feature = "unstable-locales")] locale: Option<Locale>, } @@ -733,7 +868,14 @@ pub struct DelayedFormat<I> { impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { /// Makes a new `DelayedFormat` value out of local date and time. pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> { - DelayedFormat { date: date, time: time, off: None, items: items, locale: None } + DelayedFormat { + date, + time, + off: None, + items, + #[cfg(feature = "unstable-locales")] + locale: None, + } } /// Makes a new `DelayedFormat` value out of local date and time and UTC offset. @@ -748,27 +890,30 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { { let name_and_diff = (offset.to_string(), offset.fix()); DelayedFormat { - date: date, - time: time, + date, + time, off: Some(name_and_diff), - items: items, + items, + #[cfg(feature = "unstable-locales")] locale: None, } } /// Makes a new `DelayedFormat` value out of local date and time and locale. #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] pub fn new_with_locale( date: Option<NaiveDate>, time: Option<NaiveTime>, items: I, locale: Locale, ) -> DelayedFormat<I> { - DelayedFormat { date: date, time: time, off: None, items: items, locale: Some(locale) } + DelayedFormat { date, time, off: None, items, locale: Some(locale) } } /// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale. #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] pub fn new_with_offset_and_locale<Off>( date: Option<NaiveDate>, time: Option<NaiveTime>, @@ -780,13 +925,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> { Off: Offset + fmt::Display, { let name_and_diff = (offset.to_string(), offset.fix()); - DelayedFormat { - date: date, - time: time, - off: Some(name_and_diff), - items: items, - locale: Some(locale), - } + DelayedFormat { date, time, off: Some(name_and_diff), items, locale: Some(locale) } } } @@ -817,26 +956,26 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for De /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::Weekday; /// /// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun)); /// assert!("any day".parse::<Weekday>().is_err()); -/// ~~~~ +/// ``` /// /// The parsing is case-insensitive. /// -/// ~~~~ +/// ``` /// # use chrono::Weekday; /// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon)); -/// ~~~~ +/// ``` /// /// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted. /// -/// ~~~~ +/// ``` /// # use chrono::Weekday; /// assert!("thurs".parse::<Weekday>().is_err()); -/// ~~~~ +/// ``` impl FromStr for Weekday { type Err = ParseWeekdayError; @@ -851,6 +990,7 @@ impl FromStr for Weekday { /// Formats single formatting item #[cfg(feature = "unstable-locales")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] pub fn format_item_localized<'a>( w: &mut fmt::Formatter, date: Option<&NaiveDate>, @@ -867,6 +1007,7 @@ pub fn format_item_localized<'a>( /// Tries to format given arguments with given formatting items. /// Internally used by `DelayedFormat`. #[cfg(feature = "unstable-locales")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] pub fn format_localized<'a, I, B>( w: &mut fmt::Formatter, date: Option<&NaiveDate>, @@ -890,27 +1031,27 @@ where /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::Month; /// /// assert_eq!("January".parse::<Month>(), Ok(Month::January)); /// assert!("any day".parse::<Month>().is_err()); -/// ~~~~ +/// ``` /// /// The parsing is case-insensitive. /// -/// ~~~~ +/// ``` /// # use chrono::Month; /// assert_eq!("fEbruARy".parse::<Month>(), Ok(Month::February)); -/// ~~~~ +/// ``` /// /// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted. /// -/// ~~~~ +/// ``` /// # use chrono::Month; /// assert!("septem".parse::<Month>().is_err()); /// assert!("Augustin".parse::<Month>().is_err()); -/// ~~~~ +/// ``` impl FromStr for Month { type Err = ParseMonthError; diff --git a/vendor/chrono/src/format/parse.rs b/vendor/chrono/src/format/parse.rs index 2fce8277b..69204d2e9 100644 --- a/vendor/chrono/src/format/parse.rs +++ b/vendor/chrono/src/format/parse.rs @@ -14,7 +14,7 @@ use super::scan; use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed}; use super::{ParseError, ParseErrorKind, ParseResult}; use super::{BAD_FORMAT, INVALID, NOT_ENOUGH, OUT_OF_RANGE, TOO_LONG, TOO_SHORT}; -use {DateTime, FixedOffset, Weekday}; +use crate::{DateTime, FixedOffset, Weekday}; fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> { p.set_weekday(match v { @@ -53,7 +53,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st // an adapted RFC 2822 syntax from Section 3.3 and 4.3: // - // date-time = [ day-of-week "," ] date 1*S time *S + // c-char = <any char except '(', ')' and '\\'> + // c-escape = "\" <any char> + // comment = "(" *(comment / c-char / c-escape) ")" *S + // date-time = [ day-of-week "," ] date 1*S time *S *comment // day-of-week = *S day-name *S // day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" // date = day month year @@ -79,9 +82,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st // // - we do not recognize a folding white space (FWS) or comment (CFWS). // for our purposes, instead, we accept any sequence of Unicode - // white space characters (denoted here to `S`). any actual RFC 2822 - // parser is expected to parse FWS and/or CFWS themselves and replace - // it with a single SP (`%x20`); this is legitimate. + // white space characters (denoted here to `S`). For comments, we accept + // any text within parentheses while respecting escaped parentheses. + // Any actual RFC 2822 parser is expected to parse FWS and/or CFWS themselves + // and replace it with a single SP (`%x20`); this is legitimate. // // - two-digit year < 50 should be interpreted by adding 2000. // two-digit year >= 50 or three-digit year should be interpreted @@ -117,10 +121,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st let mut year = try_consume!(scan::number(s, 2, usize::MAX)); let yearlen = prevlen - s.len(); match (yearlen, year) { - (2, 0...49) => { + (2, 0..=49) => { year += 2000; } // 47 -> 2047, 05 -> 2005 - (2, 50...99) => { + (2, 50..=99) => { year += 1900; } // 79 -> 1979 (3, _) => { @@ -145,6 +149,11 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st parsed.set_offset(i64::from(offset))?; } + // optional comments + while let Ok((s_out, ())) = scan::comment_2822(s) { + s = s_out; + } + Ok((s, ())) } @@ -411,7 +420,10 @@ where try_consume!(scan::timezone_name_skip(s)); } - &TimezoneOffsetColon | &TimezoneOffset => { + &TimezoneOffsetColon + | &TimezoneOffsetDoubleColon + | &TimezoneOffsetTripleColon + | &TimezoneOffset => { let offset = try_consume!(scan::timezone_offset( s.trim_left(), scan::colon_or_space @@ -455,11 +467,22 @@ where } } +/// Accepts a relaxed form of RFC3339. +/// A space or a 'T' are acepted as the separator between the date and time +/// parts. Additional spaces are allowed between each component. +/// +/// All of these examples are equivalent: +/// ``` +/// # use chrono::{DateTime, offset::FixedOffset}; +/// "2012-12-12T12:12:12Z".parse::<DateTime<FixedOffset>>(); +/// "2012-12-12 12:12:12Z".parse::<DateTime<FixedOffset>>(); +/// "2012- 12-12T12: 12:12Z".parse::<DateTime<FixedOffset>>(); +/// ``` impl str::FromStr for DateTime<FixedOffset> { type Err = ParseError; fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> { - const DATE_ITEMS: &'static [Item<'static>] = &[ + const DATE_ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Year, Pad::Zero), Item::Space(""), Item::Literal("-"), @@ -468,7 +491,7 @@ impl str::FromStr for DateTime<FixedOffset> { Item::Literal("-"), Item::Numeric(Numeric::Day, Pad::Zero), ]; - const TIME_ITEMS: &'static [Item<'static>] = &[ + const TIME_ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Hour, Pad::Zero), Item::Space(""), Item::Literal(":"), @@ -488,11 +511,11 @@ impl str::FromStr for DateTime<FixedOffset> { if remainder.starts_with('T') || remainder.starts_with(' ') { parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?; } else { - Err(INVALID)?; + return Err(INVALID); } } - Err((_s, e)) => Err(e)?, - Ok(_) => Err(NOT_ENOUGH)?, + Err((_s, e)) => return Err(e), + Ok(_) => return Err(NOT_ENOUGH), }; parsed.to_datetime() } @@ -557,7 +580,7 @@ fn test_parse() { check!(" \t987", [num!(Year)]; year: 987); check!("5", [num!(Year)]; year: 5); check!("5\0", [num!(Year)]; TOO_LONG); - check!("\05", [num!(Year)]; INVALID); + check!("\x005", [num!(Year)]; INVALID); check!("", [num!(Year)]; TOO_SHORT); check!("12345", [num!(Year), lit!("5")]; year: 1234); check!("12345", [nums!(Year), lit!("5")]; year: 1234); @@ -798,14 +821,25 @@ fn test_parse() { fn test_rfc2822() { use super::NOT_ENOUGH; use super::*; - use offset::FixedOffset; - use DateTime; + use crate::offset::FixedOffset; + use crate::DateTime; // Test data - (input, Ok(expected result after parse and format) or Err(error code)) let testdates = [ ("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case ("Fri, 2 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // folding whitespace ("Fri, 02 Jan 2015 17:35:20 -0800", Ok("Fri, 02 Jan 2015 17:35:20 -0800")), // leading zero + ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // trailing comment + ( + r"Tue, 20 Jan 2015 17:35:20 -0800 ( (UTC ) (\( (a)\(( \t ) ) \\( \) ))", + Ok("Tue, 20 Jan 2015 17:35:20 -0800"), + ), // complex trailing comment + (r"Tue, 20 Jan 2015 17:35:20 -0800 (UTC\)", Err(TOO_LONG)), // incorrect comment, not enough closing parentheses + ( + "Tue, 20 Jan 2015 17:35:20 -0800 (UTC)\t \r\n(Anothercomment)", + Ok("Tue, 20 Jan 2015 17:35:20 -0800"), + ), // multiple comments + ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC) ", Err(TOO_LONG)), // trailing whitespace after comment ("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week ("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month ("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second @@ -853,12 +887,12 @@ fn test_rfc2822() { #[cfg(test)] #[test] fn parse_rfc850() { - use {TimeZone, Utc}; + use crate::{TimeZone, Utc}; - static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT"; + static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT"; let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT"; - let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37); + let dt = Utc.with_ymd_and_hms(1994, 11, 6, 8, 49, 37).unwrap(); // Check that the format is what we expect assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str); @@ -869,12 +903,21 @@ fn parse_rfc850() { // Check that the rest of the weekdays parse correctly (this test originally failed because // Sunday parsed incorrectly). let testdates = [ - (Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"), - (Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"), - (Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"), - (Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"), - (Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"), - (Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"), + (Utc.with_ymd_and_hms(1994, 11, 7, 8, 49, 37).unwrap(), "Monday, 07-Nov-94 08:49:37 GMT"), + (Utc.with_ymd_and_hms(1994, 11, 8, 8, 49, 37).unwrap(), "Tuesday, 08-Nov-94 08:49:37 GMT"), + ( + Utc.with_ymd_and_hms(1994, 11, 9, 8, 49, 37).unwrap(), + "Wednesday, 09-Nov-94 08:49:37 GMT", + ), + ( + Utc.with_ymd_and_hms(1994, 11, 10, 8, 49, 37).unwrap(), + "Thursday, 10-Nov-94 08:49:37 GMT", + ), + (Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(), "Friday, 11-Nov-94 08:49:37 GMT"), + ( + Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(), + "Saturday, 12-Nov-94 08:49:37 GMT", + ), ]; for val in &testdates { @@ -886,8 +929,8 @@ fn parse_rfc850() { #[test] fn test_rfc3339() { use super::*; - use offset::FixedOffset; - use DateTime; + use crate::offset::FixedOffset; + use crate::DateTime; // Test data - (input, Ok(expected result after parse and format) or Err(error code)) let testdates = [ diff --git a/vendor/chrono/src/format/parsed.rs b/vendor/chrono/src/format/parsed.rs index b8ed2d90f..6cc29e9d4 100644 --- a/vendor/chrono/src/format/parsed.rs +++ b/vendor/chrono/src/format/parsed.rs @@ -4,16 +4,16 @@ //! A collection of parsed date and time items. //! They can be constructed incrementally while being checked for consistency. +use num_integer::div_rem; use num_traits::ToPrimitive; -use oldtime::Duration as OldDuration; use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; -use div::div_rem; -use naive::{NaiveDate, NaiveDateTime, NaiveTime}; -use offset::{FixedOffset, LocalResult, Offset, TimeZone}; -use DateTime; -use Weekday; -use {Datelike, Timelike}; +use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone}; +use crate::oldtime::Duration as OldDuration; +use crate::DateTime; +use crate::Weekday; +use crate::{Datelike, Timelike}; /// Parsed parts of date and time. There are two classes of methods: /// @@ -22,8 +22,7 @@ use {Datelike, Timelike}; /// /// - `to_*` methods try to make a concrete date and time value out of set fields. /// It fully checks any remaining out-of-range conditions and inconsistent/impossible fields. -#[allow(missing_copy_implementations)] -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] pub struct Parsed { /// Year. /// @@ -126,34 +125,6 @@ fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<( } } -impl Default for Parsed { - fn default() -> Parsed { - Parsed { - year: None, - year_div_100: None, - year_mod_100: None, - isoyear: None, - isoyear_div_100: None, - isoyear_mod_100: None, - month: None, - week_from_sun: None, - week_from_mon: None, - isoweek: None, - weekday: None, - ordinal: None, - day: None, - hour_div_12: None, - hour_mod_12: None, - minute: None, - second: None, - nanosecond: None, - timestamp: None, - offset: None, - _dummy: (), - } - } -} - impl Parsed { /// Returns the initial value of parsed parts. pub fn new() -> Parsed { @@ -254,14 +225,14 @@ impl Parsed { /// (`false` for AM, `true` for PM) #[inline] pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { - set_if_consistent(&mut self.hour_div_12, if value { 1 } else { 0 }) + set_if_consistent(&mut self.hour_div_12, u32::from(value)) } /// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from /// given hour number in 12-hour clocks. #[inline] pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> { - if value < 1 || value > 12 { + if !(1..=12).contains(&value) { return Err(OUT_OF_RANGE); } set_if_consistent(&mut self.hour_mod_12, value as u32 % 12) @@ -333,7 +304,7 @@ impl Parsed { // check if present quotient and/or modulo is consistent to the full year. // since the presence of those fields means a positive full year, // we should filter a negative full year first. - (Some(y), q, r @ Some(0...99)) | (Some(y), q, r @ None) => { + (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => { if y < 0 { return Err(OUT_OF_RANGE); } @@ -347,7 +318,7 @@ impl Parsed { // the full year is missing but we have quotient and modulo. // reconstruct the full year. make sure that the result is always positive. - (None, Some(q), Some(r @ 0...99)) => { + (None, Some(q), Some(r @ 0..=99)) => { if q < 0 { return Err(OUT_OF_RANGE); } @@ -357,7 +328,7 @@ impl Parsed { // we only have modulo. try to interpret a modulo as a conventional two-digit year. // note: we are affected by Rust issue #18060. avoid multiple range patterns. - (None, None, Some(r @ 0...99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), + (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), // otherwise it is an out-of-bound or insufficient condition. (None, Some(_), None) => Err(NOT_ENOUGH), @@ -408,9 +379,8 @@ impl Parsed { // verify the ordinal and other (non-ISO) week dates. let verify_ordinal = |date: NaiveDate| { let ordinal = date.ordinal(); - let weekday = date.weekday(); - let week_from_sun = (ordinal as i32 - weekday.num_days_from_sunday() as i32 + 7) / 7; - let week_from_mon = (ordinal as i32 - weekday.num_days_from_monday() as i32 + 7) / 7; + let week_from_sun = date.weeks_from(Weekday::Sun); + let week_from_mon = date.weeks_from(Weekday::Mon); self.ordinal.unwrap_or(ordinal) == ordinal && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon @@ -528,32 +498,32 @@ impl Parsed { /// It is able to handle leap seconds when given second is 60. pub fn to_naive_time(&self) -> ParseResult<NaiveTime> { let hour_div_12 = match self.hour_div_12 { - Some(v @ 0...1) => v, + Some(v @ 0..=1) => v, Some(_) => return Err(OUT_OF_RANGE), None => return Err(NOT_ENOUGH), }; let hour_mod_12 = match self.hour_mod_12 { - Some(v @ 0...11) => v, + Some(v @ 0..=11) => v, Some(_) => return Err(OUT_OF_RANGE), None => return Err(NOT_ENOUGH), }; let hour = hour_div_12 * 12 + hour_mod_12; let minute = match self.minute { - Some(v @ 0...59) => v, + Some(v @ 0..=59) => v, Some(_) => return Err(OUT_OF_RANGE), None => return Err(NOT_ENOUGH), }; // we allow omitting seconds or nanoseconds, but they should be in the range. let (second, mut nano) = match self.second.unwrap_or(0) { - v @ 0...59 => (v, 0), + v @ 0..=59 => (v, 0), 60 => (59, 1_000_000_000), _ => return Err(OUT_OF_RANGE), }; nano += match self.nanosecond { - Some(v @ 0...999_999_999) if self.second.is_some() => v, - Some(0...999_999_999) => return Err(NOT_ENOUGH), // second is missing + Some(v @ 0..=999_999_999) if self.second.is_some() => v, + Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing Some(_) => return Err(OUT_OF_RANGE), None => 0, }; @@ -655,6 +625,12 @@ impl Parsed { let offset = self.offset.ok_or(NOT_ENOUGH)?; let datetime = self.to_naive_datetime_with_offset(offset)?; let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; + + // this is used to prevent an overflow when calling FixedOffset::from_local_datetime + datetime + .checked_sub_signed(OldDuration::seconds(i64::from(offset.local_minus_utc()))) + .ok_or(OUT_OF_RANGE)?; + match offset.from_local_datetime(&datetime) { LocalResult::None => Err(IMPOSSIBLE), LocalResult::Single(t) => Ok(t), @@ -721,10 +697,10 @@ impl Parsed { mod tests { use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; use super::Parsed; - use naive::{NaiveDate, NaiveTime, MAX_DATE, MIN_DATE}; - use offset::{FixedOffset, TimeZone, Utc}; - use Datelike; - use Weekday::*; + use crate::naive::{NaiveDate, NaiveTime}; + use crate::offset::{FixedOffset, TimeZone, Utc}; + use crate::Datelike; + use crate::Weekday::*; #[test] fn test_parsed_set_fields() { @@ -805,7 +781,7 @@ mod tests { ) } - let ymd = |y, m, d| Ok(NaiveDate::from_ymd(y, m, d)); + let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap()); // ymd: omission of fields assert_eq!(parse!(), Err(NOT_ENOUGH)); @@ -851,7 +827,7 @@ mod tests { assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE)); assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1)); assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(OUT_OF_RANGE)); - let max_year = MAX_DATE.year(); + let max_year = NaiveDate::MAX.year(); assert_eq!( parse!(year_div_100: max_year / 100, year_mod_100: max_year % 100, month: 1, day: 1), @@ -992,8 +968,8 @@ mod tests { ) } - let hms = |h, m, s| Ok(NaiveTime::from_hms(h, m, s)); - let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano(h, m, s, n)); + let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap()); + let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap()); // omission of fields assert_eq!(parse!(), Err(NOT_ENOUGH)); @@ -1048,9 +1024,12 @@ mod tests { ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*)) } - let ymdhms = |y, m, d, h, n, s| Ok(NaiveDate::from_ymd(y, m, d).and_hms(h, n, s)); - let ymdhmsn = - |y, m, d, h, n, s, nano| Ok(NaiveDate::from_ymd(y, m, d).and_hms_nano(h, n, s, nano)); + let ymdhms = |y, m, d, h, n, s| { + Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()) + }; + let ymdhmsn = |y, m, d, h, n, s, nano| { + Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()) + }; // omission of fields assert_eq!(parse!(), Err(NOT_ENOUGH)); @@ -1104,14 +1083,15 @@ mod tests { // more timestamps let max_days_from_year_1970 = - MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); - let year_0_from_year_1970 = - NaiveDate::from_ymd(0, 1, 1).signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); + let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1) + .unwrap() + .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); let min_days_from_year_1970 = - MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970, 1, 1)); + NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); assert_eq!( parse!(timestamp: min_days_from_year_1970.num_seconds()), - ymdhms(MIN_DATE.year(), 1, 1, 0, 0, 0) + ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0) ); assert_eq!( parse!(timestamp: year_0_from_year_1970.num_seconds()), @@ -1119,7 +1099,7 @@ mod tests { ); assert_eq!( parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399), - ymdhms(MAX_DATE.year(), 12, 31, 23, 59, 59) + ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59) ); // leap seconds #1: partial fields @@ -1198,7 +1178,15 @@ mod tests { } let ymdhmsn = |y, m, d, h, n, s, nano, off| { - Ok(FixedOffset::east(off).ymd(y, m, d).and_hms_nano(h, n, s, nano)) + Ok(FixedOffset::east_opt(off) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(y, m, d) + .unwrap() + .and_hms_nano_opt(h, n, s, nano) + .unwrap(), + ) + .unwrap()) }; assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH)); @@ -1242,7 +1230,14 @@ mod tests { parse!(Utc; year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), - Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)) + Ok(Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2014, 12, 31) + .unwrap() + .and_hms_nano_opt(4, 26, 40, 12_345_678) + .unwrap() + ) + .unwrap()) ); assert_eq!( parse!(Utc; @@ -1251,31 +1246,42 @@ mod tests { Err(IMPOSSIBLE) ); assert_eq!( - parse!(FixedOffset::east(32400); + parse!(FixedOffset::east_opt(32400).unwrap(); year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), Err(IMPOSSIBLE) ); assert_eq!( - parse!(FixedOffset::east(32400); + parse!(FixedOffset::east_opt(32400).unwrap(); year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), - Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms_nano(13, 26, 40, 12_345_678)) + Ok(FixedOffset::east_opt(32400) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2014, 12, 31) + .unwrap() + .and_hms_nano_opt(13, 26, 40, 12_345_678) + .unwrap() + ) + .unwrap()) ); // single result from timestamp assert_eq!( parse!(Utc; timestamp: 1_420_000_000, offset: 0), - Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40)) + Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap()) ); assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); assert_eq!( - parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0), + parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0), Err(IMPOSSIBLE) ); assert_eq!( - parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 32400), - Ok(FixedOffset::east(32400).ymd(2014, 12, 31).and_hms(13, 26, 40)) + parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400), + Ok(FixedOffset::east_opt(32400) + .unwrap() + .with_ymd_and_hms(2014, 12, 31, 13, 26, 40) + .unwrap()) ); // TODO test with a variable time zone (for None and Ambiguous cases) diff --git a/vendor/chrono/src/format/scan.rs b/vendor/chrono/src/format/scan.rs index 0efb1ee3d..263fec556 100644 --- a/vendor/chrono/src/format/scan.rs +++ b/vendor/chrono/src/format/scan.rs @@ -8,13 +8,13 @@ #![allow(deprecated)] use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT}; -use Weekday; +use crate::Weekday; /// Returns true when two slices are equal case-insensitively (in ASCII). /// Assumes that the `pattern` is already converted to lower case. fn equals(s: &str, pattern: &str) -> bool { let mut xs = s.as_bytes().iter().map(|&c| match c { - b'A'...b'Z' => c + 32, + b'A'..=b'Z' => c + 32, _ => c, }); let mut ys = pattern.as_bytes().iter().cloned(); @@ -34,7 +34,7 @@ fn equals(s: &str, pattern: &str) -> bool { /// More than `max` digits are consumed up to the first `max` digits. /// Any number that does not fit in `i64` is an error. #[inline] -pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { +pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { assert!(min <= max); // We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on @@ -48,7 +48,7 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { let mut n = 0i64; for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied() - if c < b'0' || b'9' < c { + if !(b'0'..=b'9').contains(&c) { if i < min { return Err(INVALID); } else { @@ -62,12 +62,12 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { }; } - Ok((&s[::core::cmp::min(max, bytes.len())..], n)) + Ok((&s[core::cmp::min(max, bytes.len())..], n)) } /// Tries to consume at least one digits as a fractional second. /// Returns the number of whole nanoseconds (0--999,999,999). -pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> { +pub(super) fn nanosecond(s: &str) -> ParseResult<(&str, i64)> { // record the number of digits consumed for later scaling. let origlen = s.len(); let (s, v) = number(s, 1, 9)?; @@ -79,14 +79,14 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> { let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?; // if there are more than 9 digits, skip next digits. - let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9'); + let s = s.trim_left_matches(|c: char| ('0'..='9').contains(&c)); Ok((s, v)) } /// Tries to consume a fixed number of digits as a fractional second. /// Returns the number of whole nanoseconds (0--999,999,999). -pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { +pub(super) fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { // record the number of digits consumed for later scaling. let (s, v) = number(s, digits, digits)?; @@ -99,7 +99,7 @@ pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { } /// Tries to parse the month index (0 through 11) with the first three ASCII letters. -pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> { +pub(super) fn short_month0(s: &str) -> ParseResult<(&str, u8)> { if s.len() < 3 { return Err(TOO_SHORT); } @@ -123,7 +123,7 @@ pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> { } /// Tries to parse the weekday with the first three ASCII letters. -pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> { +pub(super) fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> { if s.len() < 3 { return Err(TOO_SHORT); } @@ -143,9 +143,9 @@ pub fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> { /// Tries to parse the month index (0 through 11) with short or long month names. /// It prefers long month names to short month names when both are possible. -pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> { +pub(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> { // lowercased month names, minus first three chars - static LONG_MONTH_SUFFIXES: [&'static str; 12] = + static LONG_MONTH_SUFFIXES: [&str; 12] = ["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"]; let (mut s, month0) = short_month0(s)?; @@ -161,9 +161,9 @@ pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> { /// Tries to parse the weekday with short or long weekday names. /// It prefers long weekday names to short weekday names when both are possible. -pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> { +pub(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> { // lowercased weekday names, minus first three chars - static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] = + static LONG_WEEKDAY_SUFFIXES: [&str; 7] = ["day", "sday", "nesday", "rsday", "day", "urday", "day"]; let (mut s, weekday) = short_weekday(s)?; @@ -178,7 +178,7 @@ pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> { } /// Tries to consume exactly one given character. -pub fn char(s: &str, c1: u8) -> ParseResult<&str> { +pub(super) fn char(s: &str, c1: u8) -> ParseResult<&str> { match s.as_bytes().first() { Some(&c) if c == c1 => Ok(&s[1..]), Some(_) => Err(INVALID), @@ -187,7 +187,7 @@ pub fn char(s: &str, c1: u8) -> ParseResult<&str> { } /// Tries to consume one or more whitespace. -pub fn space(s: &str) -> ParseResult<&str> { +pub(super) fn space(s: &str) -> ParseResult<&str> { let s_ = s.trim_left(); if s_.len() < s.len() { Ok(s_) @@ -199,7 +199,7 @@ pub fn space(s: &str) -> ParseResult<&str> { } /// Consumes any number (including zero) of colon or spaces. -pub fn colon_or_space(s: &str) -> ParseResult<&str> { +pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> { Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace())) } @@ -207,7 +207,7 @@ pub fn colon_or_space(s: &str) -> ParseResult<&str> { /// /// The additional `colon` may be used to parse a mandatory or optional `:` /// between hours and minutes, and should return either a new suffix or `Err` when parsing fails. -pub fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)> +pub(super) fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)> where F: FnMut(&str) -> ParseResult<&str>, { @@ -240,7 +240,7 @@ where // hours (00--99) let hours = match digits(s)? { - (h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')), + (h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')), _ => return Err(INVALID), }; s = &s[2..]; @@ -252,8 +252,8 @@ where // if the next two items are digits then we have to add minutes let minutes = if let Ok(ds) = digits(s) { match ds { - (m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')), - (b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE), + (m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')), + (b'6'..=b'9', b'0'..=b'9') => return Err(OUT_OF_RANGE), _ => return Err(INVALID), } } else if allow_missing_minutes { @@ -272,7 +272,7 @@ where } /// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`. -pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)> +pub(super) fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)> where F: FnMut(&str) -> ParseResult<&str>, { @@ -296,7 +296,7 @@ where /// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as /// `+00:00`, and allows missing minutes entirely. -pub fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)> +pub(super) fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)> where F: FnMut(&str) -> ParseResult<&str>, { @@ -308,16 +308,16 @@ where /// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones. /// May return `None` which indicates an insufficient offset data (i.e. `-0000`). -pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> { +pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> { // tries to parse legacy time zone names let upto = s .as_bytes() .iter() .position(|&c| match c { - b'a'...b'z' | b'A'...b'Z' => false, + b'a'..=b'z' | b'A'..=b'Z' => false, _ => true, }) - .unwrap_or_else(|| s.len()); + .unwrap_or(s.len()); if upto > 0 { let name = &s[..upto]; let s = &s[upto..]; @@ -343,8 +343,73 @@ pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> { } } -/// Tries to consume everyting until next whitespace-like symbol. +/// Tries to consume everything until next whitespace-like symbol. /// Does not provide any offset information from the consumed data. -pub fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> { +pub(super) fn timezone_name_skip(s: &str) -> ParseResult<(&str, ())> { Ok((s.trim_left_matches(|c: char| !c.is_whitespace()), ())) } + +/// Tries to consume an RFC2822 comment including preceding ` `. +/// +/// Returns the remaining string after the closing parenthesis. +pub(super) fn comment_2822(s: &str) -> ParseResult<(&str, ())> { + use CommentState::*; + + let s = s.trim_start(); + + let mut state = Start; + for (i, c) in s.bytes().enumerate() { + state = match (state, c) { + (Start, b'(') => Next(1), + (Next(1), b')') => return Ok((&s[i + 1..], ())), + (Next(depth), b'\\') => Escape(depth), + (Next(depth), b'(') => Next(depth + 1), + (Next(depth), b')') => Next(depth - 1), + (Next(depth), _) | (Escape(depth), _) => Next(depth), + _ => return Err(INVALID), + }; + } + + Err(TOO_SHORT) +} + +enum CommentState { + Start, + Next(usize), + Escape(usize), +} + +#[cfg(test)] +#[test] +fn test_rfc2822_comments() { + let testdata = [ + ("", Err(TOO_SHORT)), + (" ", Err(TOO_SHORT)), + ("x", Err(INVALID)), + ("(", Err(TOO_SHORT)), + ("()", Ok("")), + (" \r\n\t()", Ok("")), + ("() ", Ok(" ")), + ("()z", Ok("z")), + ("(x)", Ok("")), + ("(())", Ok("")), + ("((()))", Ok("")), + ("(x(x(x)x)x)", Ok("")), + ("( x ( x ( x ) x ) x )", Ok("")), + (r"(\)", Err(TOO_SHORT)), + (r"(\()", Ok("")), + (r"(\))", Ok("")), + (r"(\\)", Ok("")), + ("(()())", Ok("")), + ("( x ( x ) x ( x ) x )", Ok("")), + ]; + + for (test_in, expected) in testdata.iter() { + let actual = comment_2822(test_in).map(|(s, _)| s); + assert_eq!( + *expected, actual, + "{:?} expected to produce {:?}, but produced {:?}.", + test_in, expected, actual + ); + } +} diff --git a/vendor/chrono/src/format/strftime.rs b/vendor/chrono/src/format/strftime.rs index 93820a232..dcaabe49f 100644 --- a/vendor/chrono/src/format/strftime.rs +++ b/vendor/chrono/src/format/strftime.rs @@ -11,9 +11,9 @@ The following specifiers are available both to formatting and parsing. | Spec. | Example | Description | |-------|----------|----------------------------------------------------------------------------| | | | **DATE SPECIFIERS:** | -| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [^1] | -| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^2] | -| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] | +| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. chrono supports years from -262144 to 262143. Note: years before 1 BCE or after 9999 CE, require an initial sign (+/-).| +| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^1] | +| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^1] | | | | | | `%m` | `07` | Month number (01--12), zero-padded to 2 digits. | | `%b` | `Jul` | Abbreviated month name. Always 3 letters. | @@ -28,12 +28,12 @@ The following specifiers are available both to formatting and parsing. | `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. | | `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) | | | | | -| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^3] | +| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^2] | | `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.| | | | | -| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^4] | -| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^4] | -| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^4] | +| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^3] | +| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^3] | +| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^3] | | | | | | `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. | | | | | @@ -52,15 +52,15 @@ The following specifiers are available both to formatting and parsing. | `%p` | `AM` | `AM` or `PM` in 12-hour clocks. | | | | | | `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. | -| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^5] | -| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^8] | -| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^8] | -| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^8] | -| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^8] | -| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^8] | -| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^8] | -| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^8] | -| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^8] | +| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^4] | +| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^7] | +| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^7] | +| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^7] | +| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^7] | +| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^7] | +| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^7] | +| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^7] | +| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^7] | | | | | | `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. | | `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. | @@ -68,16 +68,18 @@ The following specifiers are available both to formatting and parsing. | `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. | | | | | | | | **TIME ZONE SPECIFIERS:** | -| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^9] | +| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^8] | | `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). | | `%:z` | `+09:30` | Same as `%z` but with a colon. | +|`%::z`|`+09:30:00`| Offset from the local time to UTC with seconds. | +|`%:::z`| `+09` | Offset from the local time to UTC without minutes. | | `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. | | | | | | | | **DATE & TIME SPECIFIERS:** | |`%c`|`Sun Jul 8 00:34:60 2001`|Locale's date and time (e.g., Thu Mar 3 23:05:25 2005). | -| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] | +| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^5] | | | | | -| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^7]| +| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^6]| | | | | | | | **SPECIAL SPECIFIERS:** | | `%t` | | Literal tab (`\t`). | @@ -95,38 +97,42 @@ Modifier | Description Notes: -[^1]: `%Y`: - Negative years are allowed in formatting but not in parsing. - -[^2]: `%C`, `%y`: +[^1]: `%C`, `%y`: This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively. -[^3]: `%U`: +[^2]: `%U`: Week 1 starts with the first Sunday in that year. It is possible to have week 0 for days before the first Sunday. -[^4]: `%G`, `%g`, `%V`: +[^3]: `%G`, `%g`, `%V`: Week 1 is the first week with at least 4 days in that year. Week 0 does not exist, so this should be used with `%G` or `%g`. -[^5]: `%S`: +[^4]: `%S`: It accounts for leap seconds, so `60` is possible. -[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional +[^5]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional digits for seconds and colons in the time zone offset. <br> <br> + This format also supports having a `Z` or `UTC` in place of `%:z`. They + are equivalent to `+00:00`. + <br> + <br> + Note that all `T`, `Z`, and `UTC` are parsed case-insensitively. + <br> + <br> The typical `strftime` implementations have different (and locale-dependent) formats for this specifier. While Chrono's format for `%+` is far more stable, it is best to avoid this specifier if you want to control the exact output. -[^7]: `%s`: +[^6]: `%s`: This is not padded and can be negative. For the purpose of Chrono, it only accounts for non-leap seconds so it slightly differs from ISO C `strftime` behavior. -[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`: +[^7]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`: <br> The default `%f` is right-aligned and always zero-padded to 9 digits for the compatibility with glibc and others, @@ -157,7 +163,7 @@ Notes: and parsing `07`, `070000` etc. will yield the same. Note that they can read nothing if the fractional part is zero. -[^9]: `%Z`: +[^8]: `%Z`: Offset will not be populated from the parsed data, nor will it be validated. Timezone is completely ignored. Similar to the glibc `strptime` treatment of this format code. @@ -169,6 +175,12 @@ Notes: */ #[cfg(feature = "unstable-locales")] +extern crate alloc; + +#[cfg(feature = "unstable-locales")] +use alloc::vec::Vec; + +#[cfg(feature = "unstable-locales")] use super::{locales, Locale}; use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad}; @@ -177,9 +189,9 @@ type Fmt<'a> = Vec<Item<'a>>; #[cfg(not(feature = "unstable-locales"))] type Fmt<'a> = &'static [Item<'static>]; -static D_FMT: &'static [Item<'static>] = +static D_FMT: &[Item<'static>] = &[num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)]; -static D_T_FMT: &'static [Item<'static>] = &[ +static D_T_FMT: &[Item<'static>] = &[ fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName), @@ -194,8 +206,7 @@ static D_T_FMT: &'static [Item<'static>] = &[ sp!(" "), num0!(Year), ]; -static T_FMT: &'static [Item<'static>] = - &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)]; +static T_FMT: &[Item<'static>] = &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)]; /// Parsing iterator for `strftime`-like format strings. #[derive(Clone, Debug)] @@ -222,23 +233,18 @@ impl<'a> StrftimeItems<'a> { /// Creates a new parsing iterator from the `strftime`-like format string. #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] pub fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> { let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect(); let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect(); let t_fmt = StrftimeItems::new(locales::t_fmt(locale)).collect(); - StrftimeItems { - remainder: s, - recons: Vec::new(), - d_fmt: d_fmt, - d_t_fmt: d_t_fmt, - t_fmt: t_fmt, - } + StrftimeItems { remainder: s, recons: Vec::new(), d_fmt, d_t_fmt, t_fmt } } #[cfg(not(feature = "unstable-locales"))] fn with_remainer(s: &'a str) -> StrftimeItems<'a> { - static FMT_NONE: &'static [Item<'static>; 0] = &[]; + static FMT_NONE: &[Item<'static>; 0] = &[]; StrftimeItems { remainder: s, @@ -261,7 +267,7 @@ impl<'a> StrftimeItems<'a> { } } -const HAVE_ALTERNATES: &'static str = "z"; +const HAVE_ALTERNATES: &str = "z"; impl<'a> Iterator for StrftimeItems<'a> { type Item = Item<'a>; @@ -407,10 +413,20 @@ impl<'a> Iterator for StrftimeItems<'a> { } } '+' => fix!(RFC3339), - ':' => match next!() { - 'z' => fix!(TimezoneOffsetColon), - _ => Item::Error, - }, + ':' => { + if self.remainder.starts_with("::z") { + self.remainder = &self.remainder[3..]; + fix!(TimezoneOffsetTripleColon) + } else if self.remainder.starts_with(":z") { + self.remainder = &self.remainder[2..]; + fix!(TimezoneOffsetDoubleColon) + } else if self.remainder.starts_with('z') { + self.remainder = &self.remainder[1..]; + fix!(TimezoneOffsetColon) + } else { + Item::Error + } + } '.' => match next!() { '3' => match next!() { 'f' => fix!(Nanosecond3), @@ -462,7 +478,7 @@ impl<'a> Iterator for StrftimeItems<'a> { let nextspec = self .remainder .find(|c: char| !c.is_whitespace()) - .unwrap_or_else(|| self.remainder.len()); + .unwrap_or(self.remainder.len()); assert!(nextspec > 0); let item = sp!(&self.remainder[..nextspec]); self.remainder = &self.remainder[nextspec..]; @@ -474,7 +490,7 @@ impl<'a> Iterator for StrftimeItems<'a> { let nextspec = self .remainder .find(|c: char| c.is_whitespace() || c == '%') - .unwrap_or_else(|| self.remainder.len()); + .unwrap_or(self.remainder.len()); assert!(nextspec > 0); let item = lit!(&self.remainder[..nextspec]); self.remainder = &self.remainder[nextspec..]; @@ -487,11 +503,11 @@ impl<'a> Iterator for StrftimeItems<'a> { #[cfg(test)] #[test] fn test_strftime_items() { - fn parse_and_collect<'a>(s: &'a str) -> Vec<Item<'a>> { + fn parse_and_collect(s: &str) -> Vec<Item<'_>> { // map any error into `[Item::Error]`. useful for easy testing. let items = StrftimeItems::new(s); let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) }); - items.collect::<Option<Vec<_>>>().unwrap_or(vec![Item::Error]) + items.collect::<Option<Vec<_>>>().unwrap_or_else(|| vec![Item::Error]) } assert_eq!(parse_and_collect(""), []); @@ -540,9 +556,18 @@ fn test_strftime_items() { #[cfg(test)] #[test] fn test_strftime_docs() { - use {FixedOffset, TimeZone, Timelike}; - - let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708); + use crate::NaiveDate; + use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc}; + + let dt = FixedOffset::east_opt(34200) + .unwrap() + .from_local_datetime( + &NaiveDate::from_ymd_opt(2001, 7, 8) + .unwrap() + .and_hms_nano_opt(0, 34, 59, 1_026_490_708) + .unwrap(), + ) + .unwrap(); // date specifiers assert_eq!(dt.format("%Y").to_string(), "2001"); @@ -559,7 +584,7 @@ fn test_strftime_docs() { assert_eq!(dt.format("%A").to_string(), "Sunday"); assert_eq!(dt.format("%w").to_string(), "0"); assert_eq!(dt.format("%u").to_string(), "7"); - assert_eq!(dt.format("%U").to_string(), "28"); + assert_eq!(dt.format("%U").to_string(), "27"); assert_eq!(dt.format("%W").to_string(), "27"); assert_eq!(dt.format("%G").to_string(), "2001"); assert_eq!(dt.format("%g").to_string(), "01"); @@ -599,10 +624,30 @@ fn test_strftime_docs() { //assert_eq!(dt.format("%Z").to_string(), "ACST"); assert_eq!(dt.format("%z").to_string(), "+0930"); assert_eq!(dt.format("%:z").to_string(), "+09:30"); + assert_eq!(dt.format("%::z").to_string(), "+09:30:00"); + assert_eq!(dt.format("%:::z").to_string(), "+09"); // date & time specifiers assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001"); assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30"); + + assert_eq!( + dt.with_timezone(&Utc).format("%+").to_string(), + "2001-07-07T15:04:60.026490708+00:00" + ); + assert_eq!( + dt.with_timezone(&Utc), + DateTime::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap() + ); + assert_eq!( + dt.with_timezone(&Utc), + DateTime::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap() + ); + assert_eq!( + dt.with_timezone(&Utc), + DateTime::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap() + ); + assert_eq!( dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(), "2001-07-08T00:34:60.026490+09:30" @@ -618,9 +663,14 @@ fn test_strftime_docs() { #[cfg(feature = "unstable-locales")] #[test] fn test_strftime_docs_localized() { - use {FixedOffset, TimeZone}; + use crate::{FixedOffset, NaiveDate, TimeZone}; - let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708); + let dt = FixedOffset::east_opt(34200).unwrap().ymd_opt(2001, 7, 8).unwrap().and_hms_nano( + 0, + 34, + 59, + 1_026_490_708, + ); // date specifiers assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui"); @@ -646,4 +696,17 @@ fn test_strftime_docs_localized() { dt.format_localized("%c", Locale::fr_BE).to_string(), "dim 08 jui 2001 00:34:60 +09:30" ); + + let nd = NaiveDate::from_ymd_opt(2001, 7, 8).unwrap(); + + // date specifiers + assert_eq!(nd.format_localized("%b", Locale::de_DE).to_string(), "Jul"); + assert_eq!(nd.format_localized("%B", Locale::de_DE).to_string(), "Juli"); + assert_eq!(nd.format_localized("%h", Locale::de_DE).to_string(), "Jul"); + assert_eq!(nd.format_localized("%a", Locale::de_DE).to_string(), "So"); + assert_eq!(nd.format_localized("%A", Locale::de_DE).to_string(), "Sonntag"); + assert_eq!(nd.format_localized("%D", Locale::de_DE).to_string(), "07/08/01"); + assert_eq!(nd.format_localized("%x", Locale::de_DE).to_string(), "08.07.2001"); + assert_eq!(nd.format_localized("%F", Locale::de_DE).to_string(), "2001-07-08"); + assert_eq!(nd.format_localized("%v", Locale::de_DE).to_string(), " 8-Jul-2001"); } diff --git a/vendor/chrono/src/lib.rs b/vendor/chrono/src/lib.rs index 9d66ae324..861ee1059 100644 --- a/vendor/chrono/src/lib.rs +++ b/vendor/chrono/src/lib.rs @@ -1,6 +1,3 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - //! # Chrono: Date and Time for Rust //! //! It aims to be a feature-complete superset of @@ -19,18 +16,6 @@ //! * Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs) //! * Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime) //! -//! Any significant changes to Chrono are documented in -//! the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) file. -//! -//! ## Usage -//! -//! Put this in your `Cargo.toml`: -//! -//! ```toml -//! [dependencies] -//! chrono = "0.4" -//! ``` -//! //! ### Features //! //! Chrono supports various runtime environments and operating systems, and has @@ -42,12 +27,11 @@ //! - `std`: Enables functionality that depends on the standard library. This //! is a superset of `alloc` and adds interoperation with standard library types //! and traits. -//! - `clock`: enables reading the system time (`now`), independent of whether -//! `std::time::SystemTime` is present, depends on having a libc. +//! - `clock`: Enables reading the system time (`now`) that depends on the standard library for +//! UNIX-like operating systems and the Windows API (`winapi`) for Windows. //! //! Optional features: //! -//! - `wasmbind`: Enable integration with [wasm-bindgen][] and its `js-sys` project //! - [`serde`][]: Enable serialization/deserialization via serde. //! - `unstable-locales`: Enable localization. This adds various methods with a //! `_localized` suffix. The implementation and API may change or even be @@ -142,26 +126,26 @@ //! use chrono::prelude::*; //! use chrono::offset::LocalResult; //! -//! let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z` +//! let dt = Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap(); // `2014-07-08T09:10:11Z` //! // July 8 is 188th day of the year 2014 (`o` for "ordinal") -//! assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11)); +//! assert_eq!(dt, Utc.yo(2014, 189).and_hms_opt(9, 10, 11).unwrap()); //! // July 8 is Tuesday in ISO week 28 of the year 2014. -//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11)); +//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms_opt(9, 10, 11).unwrap()); //! -//! let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z` -//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000)); -//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000)); +//! let dt = NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap().and_local_timezone(Utc).unwrap(); // `2014-07-08T09:10:11.012Z` +//! assert_eq!(dt, NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_micro_opt(9, 10, 11, 12_000).unwrap().and_local_timezone(Utc).unwrap()); +//! assert_eq!(dt, NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 12_000_000).unwrap().and_local_timezone(Utc).unwrap()); //! //! // dynamic verification //! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), -//! LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33))); +//! LocalResult::Single(Utc.with_ymd_and_hms(2014, 7, 8, 21, 15, 33).unwrap())); //! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); //! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); //! //! // other time zone objects can be used to construct a local datetime. //! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. -//! let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); -//! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12); +//! let local_dt = Local.from_local_datetime(&NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap()).unwrap(); +//! let fixed_dt = FixedOffset::east_opt(9 * 3600).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(18, 10, 11, 12).unwrap()).unwrap(); //! assert_eq!(dt, fixed_dt); //! # let _ = local_dt; //! ``` @@ -173,14 +157,11 @@ //! The following illustrates most supported operations to the date and time: //! //! ```rust -//! # extern crate chrono; -//! -//! # fn main() { //! use chrono::prelude::*; //! use chrono::Duration; //! //! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: -//! let dt = FixedOffset::east(9*3600).ymd(2014, 11, 28).and_hms_nano(21, 45, 59, 324310806); +//! let dt = FixedOffset::east_opt(9*3600).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(21, 45, 59, 324310806).unwrap()).unwrap(); //! //! // property accessors //! assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); @@ -193,8 +174,8 @@ //! //! // time zone accessor and manipulation //! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); -//! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600)); -//! assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806)); +//! assert_eq!(dt.timezone(), FixedOffset::east_opt(9 * 3600).unwrap()); +//! assert_eq!(dt.with_timezone(&Utc), NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 45, 59, 324310806).unwrap().and_local_timezone(Utc).unwrap()); //! //! // a sample of property manipulations (validates dynamically) //! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday @@ -202,15 +183,14 @@ //! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE //! //! // arithmetic operations -//! let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); -//! let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8); +//! let dt1 = Utc.with_ymd_and_hms(2014, 11, 14, 8, 9, 10).unwrap(); +//! let dt2 = Utc.with_ymd_and_hms(2014, 11, 14, 10, 9, 8).unwrap(); //! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2)); //! assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2)); -//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000), -//! Utc.ymd(2001, 9, 9).and_hms(1, 46, 40)); -//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000), -//! Utc.ymd(1938, 4, 24).and_hms(22, 13, 20)); -//! # } +//! assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() + Duration::seconds(1_000_000_000), +//! Utc.with_ymd_and_hms(2001, 9, 9, 1, 46, 40).unwrap()); +//! assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() - Duration::seconds(1_000_000_000), +//! Utc.with_ymd_and_hms(1938, 4, 24, 22, 13, 20).unwrap()); //! ``` //! //! ### Formatting and Parsing @@ -230,8 +210,8 @@ //! help of an additional C library. This functionality is under the feature //! `unstable-locales`: //! -//! ```text -//! chrono { version = "0.4", features = ["unstable-locales"] +//! ```toml +//! chrono = { version = "0.4", features = ["unstable-locales"] } //! ``` //! //! The `unstable-locales` feature requires and implies at least the `alloc` feature. @@ -239,20 +219,28 @@ //! ```rust //! use chrono::prelude::*; //! -//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); +//! # #[cfg(feature = "unstable-locales")] +//! # fn test() { +//! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap(); //! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09"); //! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); //! assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09"); -//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); //! +//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); //! assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC"); //! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000"); //! assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00"); //! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); //! //! // Note that milli/nanoseconds are only printed if they are non-zero -//! let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1); +//! let dt_nano = NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 0, 9, 1).unwrap().and_local_timezone(Utc).unwrap(); //! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z"); +//! # } +//! # #[cfg(not(feature = "unstable-locales"))] +//! # fn test() {} +//! # if cfg!(feature = "unstable-locales") { +//! # test(); +//! # } //! ``` //! //! Parsing can be done with three methods: @@ -285,8 +273,8 @@ //! ```rust //! use chrono::prelude::*; //! -//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9); -//! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600)); +//! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap(); +//! let fixed_dt = dt.with_timezone(&FixedOffset::east_opt(9*3600).unwrap()); //! //! // method 1 //! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone())); @@ -353,9 +341,9 @@ //! assert_eq!(Utc::today(), Utc::now().date()); //! assert_eq!(Local::today(), Local::now().date()); //! -//! assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri); +//! assert_eq!(Utc.ymd_opt(2014, 11, 28).unwrap().weekday(), Weekday::Fri); //! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None); -//! assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(), +//! assert_eq!(NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_milli_opt(7, 8, 9, 10).unwrap().and_local_timezone(Utc).unwrap().format("%H%M%S").to_string(), //! "070809"); //! ``` //! @@ -401,7 +389,7 @@ //! Chrono inherently does not support an inaccurate or partial date and time representation. //! Any operation that can be ambiguous will return `None` in such cases. //! For example, "a month later" of 2014-01-30 is not well-defined -//! and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`. +//! and consequently `Utc.ymd_opt(2014, 1, 30).unwrap().with_month(2)` returns `None`. //! //! Non ISO week handling is not yet supported. //! For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext) @@ -414,1122 +402,126 @@ #![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507 #![deny(missing_docs)] #![deny(missing_debug_implementations)] +#![warn(unreachable_pub)] #![deny(dead_code)] -// lints are added all the time, we test on 1.13 -#![allow(unknown_lints)] #![cfg_attr(not(any(feature = "std", test)), no_std)] -#![cfg_attr(feature = "cargo-clippy", allow( - renamed_and_removed_lints, - // The explicit 'static lifetimes are still needed for rustc 1.13-16 - // backward compatibility, and this appeases clippy. If minimum rustc - // becomes 1.17, should be able to remove this, those 'static lifetimes, - // and use `static` in a lot of places `const` is used now. - redundant_static_lifetimes, - // Similarly, redundant_field_names lints on not using the - // field-init-shorthand, which was stabilized in rust 1.17. - redundant_field_names, - // Changing trivially_copy_pass_by_ref would require an incompatible version - // bump. - trivially_copy_pass_by_ref, - try_err, - // Currently deprecated, we use the separate implementation to add docs - // warning that putting a time in a hash table is probably a bad idea - derive_hash_xor_eq, -))] - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(all(feature = "std", not(feature = "alloc")))] -extern crate std as alloc; -#[cfg(any(feature = "std", test))] -extern crate std as core; +// can remove this if/when rustc-serialize support is removed +// keeps clippy happy in the meantime +#![cfg_attr(feature = "rustc-serialize", allow(deprecated))] +#![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(feature = "oldtime")] +#[cfg_attr(docsrs, doc(cfg(feature = "oldtime")))] extern crate time as oldtime; #[cfg(not(feature = "oldtime"))] mod oldtime; +// this reexport is to aid the transition and should not be in the prelude! +pub use oldtime::{Duration, OutOfRangeError}; -#[cfg(feature = "clock")] -extern crate libc; -#[cfg(all(feature = "clock", windows))] -extern crate winapi; -#[cfg(all( - feature = "clock", - not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")) -))] -mod sys; - -extern crate num_integer; -extern crate num_traits; -#[cfg(feature = "rustc-serialize")] -extern crate rustc_serialize; -#[cfg(feature = "serde")] -extern crate serde as serdelib; #[cfg(feature = "__doctest")] #[cfg_attr(feature = "__doctest", cfg(doctest))] -#[macro_use] -extern crate doc_comment; -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] -extern crate js_sys; -#[cfg(feature = "unstable-locales")] -extern crate pure_rust_locales; -#[cfg(feature = "bench")] -extern crate test; -#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] -extern crate wasm_bindgen; +use doc_comment::doctest; #[cfg(feature = "__doctest")] #[cfg_attr(feature = "__doctest", cfg(doctest))] doctest!("../README.md"); -// this reexport is to aid the transition and should not be in the prelude! -pub use oldtime::Duration; - -pub use date::{Date, MAX_DATE, MIN_DATE}; -#[cfg(feature = "rustc-serialize")] -pub use datetime::rustc_serialize::TsSeconds; -pub use datetime::{DateTime, SecondsFormat, MAX_DATETIME, MIN_DATETIME}; -/// L10n locales. -#[cfg(feature = "unstable-locales")] -pub use format::Locale; -pub use format::{ParseError, ParseResult}; -#[doc(no_inline)] -pub use naive::{IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; -#[cfg(feature = "clock")] -#[doc(no_inline)] -pub use offset::Local; -#[doc(no_inline)] -pub use offset::{FixedOffset, LocalResult, Offset, TimeZone, Utc}; -pub use round::{DurationRound, RoundingError, SubsecRound}; - /// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). pub mod prelude { #[doc(no_inline)] - pub use Date; + #[allow(deprecated)] + pub use crate::Date; #[cfg(feature = "clock")] + #[cfg_attr(docsrs, doc(cfg(feature = "clock")))] #[doc(no_inline)] - pub use Local; + pub use crate::Local; #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] #[doc(no_inline)] - pub use Locale; + pub use crate::Locale; #[doc(no_inline)] - pub use SubsecRound; + pub use crate::SubsecRound; #[doc(no_inline)] - pub use {DateTime, SecondsFormat}; + pub use crate::{DateTime, SecondsFormat}; #[doc(no_inline)] - pub use {Datelike, Month, Timelike, Weekday}; + pub use crate::{Datelike, Month, Timelike, Weekday}; #[doc(no_inline)] - pub use {FixedOffset, Utc}; + pub use crate::{FixedOffset, Utc}; #[doc(no_inline)] - pub use {NaiveDate, NaiveDateTime, NaiveTime}; + pub use crate::{NaiveDate, NaiveDateTime, NaiveTime}; #[doc(no_inline)] - pub use {Offset, TimeZone}; + pub use crate::{Offset, TimeZone}; } -// useful throughout the codebase -macro_rules! try_opt { - ($e:expr) => { - match $e { - Some(v) => v, - None => return None, - } - }; -} +mod date; +#[allow(deprecated)] +pub use date::{Date, MAX_DATE, MIN_DATE}; -mod div; -pub mod offset; -pub mod naive { - //! Date and time types unconcerned with timezones. - //! - //! They are primarily building blocks for other types - //! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), - //! but can be also used for the simpler date and time handling. +mod datetime; +#[cfg(feature = "rustc-serialize")] +#[cfg_attr(docsrs, doc(cfg(feature = "rustc-serialize")))] +pub use datetime::rustc_serialize::TsSeconds; +#[allow(deprecated)] +pub use datetime::{DateTime, SecondsFormat, MAX_DATETIME, MIN_DATETIME}; - mod date; - mod datetime; - mod internals; - mod isoweek; - mod time; +pub mod format; +/// L10n locales. +#[cfg(feature = "unstable-locales")] +#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] +pub use format::Locale; +pub use format::{ParseError, ParseResult}; - pub use self::date::{NaiveDate, MAX_DATE, MIN_DATE}; - #[cfg(feature = "rustc-serialize")] - #[allow(deprecated)] - pub use self::datetime::rustc_serialize::TsSeconds; - pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME}; - pub use self::isoweek::IsoWeek; - pub use self::time::NaiveTime; +pub mod naive; +#[doc(no_inline)] +pub use naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime, NaiveWeek}; - #[cfg(feature = "__internal_bench")] - #[doc(hidden)] - pub use self::internals::YearFlags as __BenchYearFlags; +pub mod offset; +#[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] +#[doc(no_inline)] +pub use offset::Local; +#[doc(no_inline)] +pub use offset::{FixedOffset, LocalResult, Offset, TimeZone, Utc}; - /// Serialization/Deserialization of naive types in alternate formats - /// - /// The various modules in here are intended to be used with serde's [`with` - /// annotation][1] to serialize as something other than the default [RFC - /// 3339][2] format. - /// - /// [1]: https://serde.rs/attributes.html#field-attributes - /// [2]: https://tools.ietf.org/html/rfc3339 - #[cfg(feature = "serde")] - pub mod serde { - pub use super::datetime::serde::*; - } -} -mod date; -mod datetime; -pub mod format; mod round; +pub use round::{DurationRound, RoundingError, SubsecRound}; + +mod weekday; +pub use weekday::{ParseWeekdayError, Weekday}; + +mod month; +pub use month::{Month, Months, ParseMonthError}; + +mod traits; +pub use traits::{Datelike, Timelike}; #[cfg(feature = "__internal_bench")] #[doc(hidden)] pub use naive::__BenchYearFlags; -/// Serialization/Deserialization in alternate formats +/// Serialization/Deserialization with serde. +/// +/// This module provides default implementations for `DateTime` using the [RFC 3339][1] format and various +/// alternatives for use with serde's [`with` annotation][1]. /// -/// The various modules in here are intended to be used with serde's [`with` -/// annotation][1] to serialize as something other than the default [RFC -/// 3339][2] format. +/// *Available on crate feature 'serde' only.* /// -/// [1]: https://serde.rs/attributes.html#field-attributes -/// [2]: https://tools.ietf.org/html/rfc3339 +/// [1]: https://tools.ietf.org/html/rfc3339 +/// [2]: https://serde.rs/attributes.html#field-attributes #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] pub mod serde { pub use super::datetime::serde::*; } -// Until rust 1.18 there is no "pub(crate)" so to share this we need it in the root - -#[cfg(feature = "serde")] -enum SerdeError<V: fmt::Display, D: fmt::Display> { - NonExistent { timestamp: V }, - Ambiguous { timestamp: V, min: D, max: D }, -} - -/// Construct a [`SerdeError::NonExistent`] -#[cfg(feature = "serde")] -fn ne_timestamp<T: fmt::Display>(ts: T) -> SerdeError<T, u8> { - SerdeError::NonExistent::<T, u8> { timestamp: ts } -} - -#[cfg(feature = "serde")] -impl<V: fmt::Display, D: fmt::Display> fmt::Debug for SerdeError<V, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ChronoSerdeError({})", self) - } -} - -// impl<V: fmt::Display, D: fmt::Debug> core::error::Error for SerdeError<V, D> {} -#[cfg(feature = "serde")] -impl<V: fmt::Display, D: fmt::Display> fmt::Display for SerdeError<V, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &SerdeError::NonExistent { ref timestamp } => { - write!(f, "value is not a legal timestamp: {}", timestamp) - } - &SerdeError::Ambiguous { ref timestamp, ref min, ref max } => write!( - f, - "value is an ambiguous timestamp: {}, could be either of {}, {}", - timestamp, min, max - ), - } - } -} - -/// The day of week. -/// -/// The order of the days of week depends on the context. -/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.) -/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result. -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] -pub enum Weekday { - /// Monday. - Mon = 0, - /// Tuesday. - Tue = 1, - /// Wednesday. - Wed = 2, - /// Thursday. - Thu = 3, - /// Friday. - Fri = 4, - /// Saturday. - Sat = 5, - /// Sunday. - Sun = 6, -} - -impl Weekday { - /// The next day in the week. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon` - #[inline] - pub fn succ(&self) -> Weekday { - match *self { - Weekday::Mon => Weekday::Tue, - Weekday::Tue => Weekday::Wed, - Weekday::Wed => Weekday::Thu, - Weekday::Thu => Weekday::Fri, - Weekday::Fri => Weekday::Sat, - Weekday::Sat => Weekday::Sun, - Weekday::Sun => Weekday::Mon, - } - } - - /// The previous day in the week. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` - #[inline] - pub fn pred(&self) -> Weekday { - match *self { - Weekday::Mon => Weekday::Sun, - Weekday::Tue => Weekday::Mon, - Weekday::Wed => Weekday::Tue, - Weekday::Thu => Weekday::Wed, - Weekday::Fri => Weekday::Thu, - Weekday::Sat => Weekday::Fri, - Weekday::Sun => Weekday::Sat, - } - } - - /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number) - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7 - #[inline] - pub fn number_from_monday(&self) -> u32 { - match *self { - Weekday::Mon => 1, - Weekday::Tue => 2, - Weekday::Wed => 3, - Weekday::Thu => 4, - Weekday::Fri => 5, - Weekday::Sat => 6, - Weekday::Sun => 7, - } - } - - /// Returns a day-of-week number starting from Sunday = 1. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1 - #[inline] - pub fn number_from_sunday(&self) -> u32 { - match *self { - Weekday::Mon => 2, - Weekday::Tue => 3, - Weekday::Wed => 4, - Weekday::Thu => 5, - Weekday::Fri => 6, - Weekday::Sat => 7, - Weekday::Sun => 1, - } - } - - /// Returns a day-of-week number starting from Monday = 0. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6 - #[inline] - pub fn num_days_from_monday(&self) -> u32 { - match *self { - Weekday::Mon => 0, - Weekday::Tue => 1, - Weekday::Wed => 2, - Weekday::Thu => 3, - Weekday::Fri => 4, - Weekday::Sat => 5, - Weekday::Sun => 6, - } - } - - /// Returns a day-of-week number starting from Sunday = 0. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0 - #[inline] - pub fn num_days_from_sunday(&self) -> u32 { - match *self { - Weekday::Mon => 1, - Weekday::Tue => 2, - Weekday::Wed => 3, - Weekday::Thu => 4, - Weekday::Fri => 5, - Weekday::Sat => 6, - Weekday::Sun => 0, - } - } -} - -impl fmt::Display for Weekday { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - Weekday::Mon => "Mon", - Weekday::Tue => "Tue", - Weekday::Wed => "Wed", - Weekday::Thu => "Thu", - Weekday::Fri => "Fri", - Weekday::Sat => "Sat", - Weekday::Sun => "Sun", - }) - } -} - -/// Any weekday can be represented as an integer from 0 to 6, which equals to -/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation. -/// Do not heavily depend on this though; use explicit methods whenever possible. -impl num_traits::FromPrimitive for Weekday { - #[inline] - fn from_i64(n: i64) -> Option<Weekday> { - match n { - 0 => Some(Weekday::Mon), - 1 => Some(Weekday::Tue), - 2 => Some(Weekday::Wed), - 3 => Some(Weekday::Thu), - 4 => Some(Weekday::Fri), - 5 => Some(Weekday::Sat), - 6 => Some(Weekday::Sun), - _ => None, - } - } - - #[inline] - fn from_u64(n: u64) -> Option<Weekday> { - match n { - 0 => Some(Weekday::Mon), - 1 => Some(Weekday::Tue), - 2 => Some(Weekday::Wed), - 3 => Some(Weekday::Thu), - 4 => Some(Weekday::Fri), - 5 => Some(Weekday::Sat), - 6 => Some(Weekday::Sun), - _ => None, - } - } -} - -use core::fmt; - -/// An error resulting from reading `Weekday` value with `FromStr`. -#[derive(Clone, PartialEq)] -pub struct ParseWeekdayError { - _dummy: (), -} - -impl fmt::Debug for ParseWeekdayError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParseWeekdayError {{ .. }}") - } -} - -// the actual `FromStr` implementation is in the `format` module to leverage the existing code - -#[cfg(feature = "serde")] -mod weekday_serde { - use super::Weekday; - use core::fmt; - use serdelib::{de, ser}; - - impl ser::Serialize for Weekday { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.collect_str(&self) - } - } - - struct WeekdayVisitor; - - impl<'de> de::Visitor<'de> for WeekdayVisitor { - type Value = Weekday; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Weekday") - } - - fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> - where - E: de::Error, - { - value.parse().map_err(|_| E::custom("short or long weekday names expected")) - } - } - - impl<'de> de::Deserialize<'de> for Weekday { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(WeekdayVisitor) - } - } - - #[cfg(test)] - extern crate serde_json; - - #[test] - fn test_serde_serialize() { - use self::serde_json::to_string; - use Weekday::*; - - let cases: Vec<(Weekday, &str)> = vec![ - (Mon, "\"Mon\""), - (Tue, "\"Tue\""), - (Wed, "\"Wed\""), - (Thu, "\"Thu\""), - (Fri, "\"Fri\""), - (Sat, "\"Sat\""), - (Sun, "\"Sun\""), - ]; - - for (weekday, expected_str) in cases { - let string = to_string(&weekday).unwrap(); - assert_eq!(string, expected_str); - } - } - - #[test] - fn test_serde_deserialize() { - use self::serde_json::from_str; - use Weekday::*; - - let cases: Vec<(&str, Weekday)> = vec![ - ("\"mon\"", Mon), - ("\"MONDAY\"", Mon), - ("\"MonDay\"", Mon), - ("\"mOn\"", Mon), - ("\"tue\"", Tue), - ("\"tuesday\"", Tue), - ("\"wed\"", Wed), - ("\"wednesday\"", Wed), - ("\"thu\"", Thu), - ("\"thursday\"", Thu), - ("\"fri\"", Fri), - ("\"friday\"", Fri), - ("\"sat\"", Sat), - ("\"saturday\"", Sat), - ("\"sun\"", Sun), - ("\"sunday\"", Sun), - ]; - - for (str, expected_weekday) in cases { - let weekday = from_str::<Weekday>(str).unwrap(); - assert_eq!(weekday, expected_weekday); - } - - let errors: Vec<&str> = - vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""]; - - for str in errors { - from_str::<Weekday>(str).unwrap_err(); - } - } -} - -/// The month of the year. -/// -/// This enum is just a convenience implementation. -/// The month in dates created by DateLike objects does not return this enum. -/// -/// It is possible to convert from a date to a month independently -/// ``` -/// # extern crate num_traits; -/// use num_traits::FromPrimitive; -/// use chrono::prelude::*; -/// let date = Utc.ymd(2019, 10, 28).and_hms(9, 10, 11); -/// // `2019-10-28T09:10:11Z` -/// let month = Month::from_u32(date.month()); -/// assert_eq!(month, Some(Month::October)) -/// ``` -/// Or from a Month to an integer usable by dates -/// ``` -/// # use chrono::prelude::*; -/// let month = Month::January; -/// let dt = Utc.ymd(2019, month.number_from_month(), 28).and_hms(9, 10, 11); -/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); -/// ``` -/// Allows mapping from and to month, from 1-January to 12-December. -/// Can be Serialized/Deserialized with serde -// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] -pub enum Month { - /// January - January = 0, - /// February - February = 1, - /// March - March = 2, - /// April - April = 3, - /// May - May = 4, - /// June - June = 5, - /// July - July = 6, - /// August - August = 7, - /// September - September = 8, - /// October - October = 9, - /// November - November = 10, - /// December - December = 11, -} - -impl Month { - /// The next month. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// ----------- | --------- | ---------- | --- | --------- - /// `m.succ()`: | `February` | `March` | `...` | `January` - #[inline] - pub fn succ(&self) -> Month { - match *self { - Month::January => Month::February, - Month::February => Month::March, - Month::March => Month::April, - Month::April => Month::May, - Month::May => Month::June, - Month::June => Month::July, - Month::July => Month::August, - Month::August => Month::September, - Month::September => Month::October, - Month::October => Month::November, - Month::November => Month::December, - Month::December => Month::January, - } - } - - /// The previous month. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// ----------- | --------- | ---------- | --- | --------- - /// `m.succ()`: | `December` | `January` | `...` | `November` - #[inline] - pub fn pred(&self) -> Month { - match *self { - Month::January => Month::December, - Month::February => Month::January, - Month::March => Month::February, - Month::April => Month::March, - Month::May => Month::April, - Month::June => Month::May, - Month::July => Month::June, - Month::August => Month::July, - Month::September => Month::August, - Month::October => Month::September, - Month::November => Month::October, - Month::December => Month::November, - } - } - - /// Returns a month-of-year number starting from January = 1. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// -------------------------| --------- | ---------- | --- | ----- - /// `m.number_from_month()`: | 1 | 2 | `...` | 12 - #[inline] - pub fn number_from_month(&self) -> u32 { - match *self { - Month::January => 1, - Month::February => 2, - Month::March => 3, - Month::April => 4, - Month::May => 5, - Month::June => 6, - Month::July => 7, - Month::August => 8, - Month::September => 9, - Month::October => 10, - Month::November => 11, - Month::December => 12, - } - } - - /// Get the name of the month - /// - /// ``` - /// use chrono::Month; - /// - /// assert_eq!(Month::January.name(), "January") - /// ``` - pub fn name(&self) -> &'static str { - match *self { - Month::January => "January", - Month::February => "February", - Month::March => "March", - Month::April => "April", - Month::May => "May", - Month::June => "June", - Month::July => "July", - Month::August => "August", - Month::September => "September", - Month::October => "October", - Month::November => "November", - Month::December => "December", - } - } -} - -impl num_traits::FromPrimitive for Month { - /// Returns an Option<Month> from a i64, assuming a 1-index, January = 1. - /// - /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12` - /// ---------------------------| -------------------- | --------------------- | ... | ----- - /// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December) - - #[inline] - fn from_u64(n: u64) -> Option<Month> { - Self::from_u32(n as u32) - } - - #[inline] - fn from_i64(n: i64) -> Option<Month> { - Self::from_u32(n as u32) - } - - #[inline] - fn from_u32(n: u32) -> Option<Month> { - match n { - 1 => Some(Month::January), - 2 => Some(Month::February), - 3 => Some(Month::March), - 4 => Some(Month::April), - 5 => Some(Month::May), - 6 => Some(Month::June), - 7 => Some(Month::July), - 8 => Some(Month::August), - 9 => Some(Month::September), - 10 => Some(Month::October), - 11 => Some(Month::November), - 12 => Some(Month::December), - _ => None, - } - } -} - -/// An error resulting from reading `<Month>` value with `FromStr`. -#[derive(Clone, PartialEq)] -pub struct ParseMonthError { - _dummy: (), -} - -impl fmt::Debug for ParseMonthError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParseMonthError {{ .. }}") - } -} - -#[cfg(feature = "serde")] -mod month_serde { - use super::Month; - use serdelib::{de, ser}; - - use core::fmt; - - impl ser::Serialize for Month { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.collect_str(self.name()) - } - } - - struct MonthVisitor; - - impl<'de> de::Visitor<'de> for MonthVisitor { - type Value = Month; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Month") - } - - fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> - where - E: de::Error, - { - value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected")) - } - } - - impl<'de> de::Deserialize<'de> for Month { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(MonthVisitor) - } - } - - #[cfg(test)] - extern crate serde_json; - - #[test] - fn test_serde_serialize() { - use self::serde_json::to_string; - use Month::*; - - let cases: Vec<(Month, &str)> = vec![ - (January, "\"January\""), - (February, "\"February\""), - (March, "\"March\""), - (April, "\"April\""), - (May, "\"May\""), - (June, "\"June\""), - (July, "\"July\""), - (August, "\"August\""), - (September, "\"September\""), - (October, "\"October\""), - (November, "\"November\""), - (December, "\"December\""), - ]; - - for (month, expected_str) in cases { - let string = to_string(&month).unwrap(); - assert_eq!(string, expected_str); - } - } - - #[test] - fn test_serde_deserialize() { - use self::serde_json::from_str; - use Month::*; - - let cases: Vec<(&str, Month)> = vec![ - ("\"january\"", January), - ("\"jan\"", January), - ("\"FeB\"", February), - ("\"MAR\"", March), - ("\"mar\"", March), - ("\"april\"", April), - ("\"may\"", May), - ("\"june\"", June), - ("\"JULY\"", July), - ("\"august\"", August), - ("\"september\"", September), - ("\"October\"", October), - ("\"November\"", November), - ("\"DECEmbEr\"", December), - ]; - - for (string, expected_month) in cases { - let month = from_str::<Month>(string).unwrap(); - assert_eq!(month, expected_month); - } - - let errors: Vec<&str> = - vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""]; - - for string in errors { - from_str::<Month>(string).unwrap_err(); - } - } -} - -/// The common set of methods for date component. -pub trait Datelike: Sized { - /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date). - fn year(&self) -> i32; - - /// Returns the absolute year number starting from 1 with a boolean flag, - /// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD). - #[inline] - fn year_ce(&self) -> (bool, u32) { - let year = self.year(); - if year < 1 { - (false, (1 - year) as u32) - } else { - (true, year as u32) - } - } - - /// Returns the month number starting from 1. - /// - /// The return value ranges from 1 to 12. - fn month(&self) -> u32; - - /// Returns the month number starting from 0. - /// - /// The return value ranges from 0 to 11. - fn month0(&self) -> u32; - - /// Returns the day of month starting from 1. - /// - /// The return value ranges from 1 to 31. (The last day of month differs by months.) - fn day(&self) -> u32; - - /// Returns the day of month starting from 0. - /// - /// The return value ranges from 0 to 30. (The last day of month differs by months.) - fn day0(&self) -> u32; - - /// Returns the day of year starting from 1. - /// - /// The return value ranges from 1 to 366. (The last day of year differs by years.) - fn ordinal(&self) -> u32; - - /// Returns the day of year starting from 0. - /// - /// The return value ranges from 0 to 365. (The last day of year differs by years.) - fn ordinal0(&self) -> u32; - - /// Returns the day of week. - fn weekday(&self) -> Weekday; - - /// Returns the ISO week. - fn iso_week(&self) -> IsoWeek; - - /// Makes a new value with the year number changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_year(&self, year: i32) -> Option<Self>; - - /// Makes a new value with the month number (starting from 1) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_month(&self, month: u32) -> Option<Self>; - - /// Makes a new value with the month number (starting from 0) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_month0(&self, month0: u32) -> Option<Self>; - - /// Makes a new value with the day of month (starting from 1) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_day(&self, day: u32) -> Option<Self>; - - /// Makes a new value with the day of month (starting from 0) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_day0(&self, day0: u32) -> Option<Self>; - - /// Makes a new value with the day of year (starting from 1) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_ordinal(&self, ordinal: u32) -> Option<Self>; - - /// Makes a new value with the day of year (starting from 0) changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>; - - /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1. - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Datelike}; - /// - /// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719_163); - /// assert_eq!(NaiveDate::from_ymd(2, 1, 1).num_days_from_ce(), 366); - /// assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1); - /// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365); - /// ``` - fn num_days_from_ce(&self) -> i32 { - // See test_num_days_from_ce_against_alternative_impl below for a more straightforward - // implementation. - - // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range. - let mut year = self.year() - 1; - let mut ndays = 0; - if year < 0 { - let excess = 1 + (-year) / 400; - year += excess * 400; - ndays -= excess * 146_097; - } - let div_100 = year / 100; - ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2); - ndays + self.ordinal() as i32 - } -} - -/// The common set of methods for time component. -pub trait Timelike: Sized { - /// Returns the hour number from 0 to 23. - fn hour(&self) -> u32; - - /// Returns the hour number from 1 to 12 with a boolean flag, - /// which is false for AM and true for PM. - #[inline] - fn hour12(&self) -> (bool, u32) { - let hour = self.hour(); - let mut hour12 = hour % 12; - if hour12 == 0 { - hour12 = 12; - } - (hour >= 12, hour12) - } - - /// Returns the minute number from 0 to 59. - fn minute(&self) -> u32; - - /// Returns the second number from 0 to 59. - fn second(&self) -> u32; - - /// Returns the number of nanoseconds since the whole non-leap second. - /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling). - fn nanosecond(&self) -> u32; - - /// Makes a new value with the hour number changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_hour(&self, hour: u32) -> Option<Self>; - - /// Makes a new value with the minute number changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_minute(&self, min: u32) -> Option<Self>; - - /// Makes a new value with the second number changed. - /// - /// Returns `None` when the resulting value would be invalid. - /// As with the [`second`](#tymethod.second) method, - /// the input range is restricted to 0 through 59. - fn with_second(&self, sec: u32) -> Option<Self>; - - /// Makes a new value with nanoseconds since the whole non-leap second changed. - /// - /// Returns `None` when the resulting value would be invalid. - /// As with the [`nanosecond`](#tymethod.nanosecond) method, - /// the input range can exceed 1,000,000,000 for leap seconds. - fn with_nanosecond(&self, nano: u32) -> Option<Self>; - - /// Returns the number of non-leap seconds past the last midnight. - #[inline] - fn num_seconds_from_midnight(&self) -> u32 { - self.hour() * 3600 + self.minute() * 60 + self.second() - } -} - +/// MSRV 1.42 #[cfg(test)] -extern crate num_iter; - -mod test { - #[allow(unused_imports)] - use super::*; - - #[test] - fn test_readme_doomsday() { - use num_iter::range_inclusive; - - for y in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) { - // even months - let d4 = NaiveDate::from_ymd(y, 4, 4); - let d6 = NaiveDate::from_ymd(y, 6, 6); - let d8 = NaiveDate::from_ymd(y, 8, 8); - let d10 = NaiveDate::from_ymd(y, 10, 10); - let d12 = NaiveDate::from_ymd(y, 12, 12); - - // nine to five, seven-eleven - let d59 = NaiveDate::from_ymd(y, 5, 9); - let d95 = NaiveDate::from_ymd(y, 9, 5); - let d711 = NaiveDate::from_ymd(y, 7, 11); - let d117 = NaiveDate::from_ymd(y, 11, 7); - - // "March 0" - let d30 = NaiveDate::from_ymd(y, 3, 1).pred(); - - let weekday = d30.weekday(); - let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117]; - assert!(other_dates.iter().all(|d| d.weekday() == weekday)); +#[macro_export] +macro_rules! matches { + ($expression:expr, $(|)? $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false } } - - #[test] - fn test_month_enum_primitive_parse() { - use num_traits::FromPrimitive; - - let jan_opt = Month::from_u32(1); - let feb_opt = Month::from_u64(2); - let dec_opt = Month::from_i64(12); - let no_month = Month::from_u32(13); - assert_eq!(jan_opt, Some(Month::January)); - assert_eq!(feb_opt, Some(Month::February)); - assert_eq!(dec_opt, Some(Month::December)); - assert_eq!(no_month, None); - - let date = Utc.ymd(2019, 10, 28).and_hms(9, 10, 11); - assert_eq!(Month::from_u32(date.month()), Some(Month::October)); - - let month = Month::January; - let dt = Utc.ymd(2019, month.number_from_month(), 28).and_hms(9, 10, 11); - assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); - } -} - -/// Tests `Datelike::num_days_from_ce` against an alternative implementation. -/// -/// The alternative implementation is not as short as the current one but it is simpler to -/// understand, with less unexplained magic constants. -#[test] -fn test_num_days_from_ce_against_alternative_impl() { - /// Returns the number of multiples of `div` in the range `start..end`. - /// - /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the - /// behaviour is defined by the following equation: - /// `in_between(start, end, div) == - in_between(end, start, div)`. - /// - /// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`. - /// - /// # Panics - /// - /// Panics if `div` is not positive. - fn in_between(start: i32, end: i32, div: i32) -> i32 { - assert!(div > 0, "in_between: nonpositive div = {}", div); - let start = (start.div_euclid(div), start.rem_euclid(div)); - let end = (end.div_euclid(div), end.rem_euclid(div)); - // The lowest multiple of `div` greater than or equal to `start`, divided. - let start = start.0 + (start.1 != 0) as i32; - // The lowest multiple of `div` greater than or equal to `end`, divided. - let end = end.0 + (end.1 != 0) as i32; - end - start - } - - /// Alternative implementation to `Datelike::num_days_from_ce` - fn num_days_from_ce<Date: Datelike>(date: &Date) -> i32 { - let year = date.year(); - let diff = move |div| in_between(1, year, div); - // 365 days a year, one more in leap years. In the gregorian calendar, leap years are all - // the multiples of 4 except multiples of 100 but including multiples of 400. - date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400) - } - - use num_iter::range_inclusive; - - for year in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) { - let jan1_year = NaiveDate::from_ymd(year, 1, 1); - assert_eq!( - jan1_year.num_days_from_ce(), - num_days_from_ce(&jan1_year), - "on {:?}", - jan1_year - ); - let mid_year = jan1_year + Duration::days(133); - assert_eq!(mid_year.num_days_from_ce(), num_days_from_ce(&mid_year), "on {:?}", mid_year); - } -} - -#[test] -fn test_month_enum_succ_pred() { - assert_eq!(Month::January.succ(), Month::February); - assert_eq!(Month::December.succ(), Month::January); - assert_eq!(Month::January.pred(), Month::December); - assert_eq!(Month::February.pred(), Month::January); } diff --git a/vendor/chrono/src/month.rs b/vendor/chrono/src/month.rs new file mode 100644 index 000000000..46f09d0fb --- /dev/null +++ b/vendor/chrono/src/month.rs @@ -0,0 +1,355 @@ +use core::fmt; + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +/// The month of the year. +/// +/// This enum is just a convenience implementation. +/// The month in dates created by DateLike objects does not return this enum. +/// +/// It is possible to convert from a date to a month independently +/// ``` +/// use num_traits::FromPrimitive; +/// use chrono::prelude::*; +/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); +/// // `2019-10-28T09:10:11Z` +/// let month = Month::from_u32(date.month()); +/// assert_eq!(month, Some(Month::October)) +/// ``` +/// Or from a Month to an integer usable by dates +/// ``` +/// # use chrono::prelude::*; +/// let month = Month::January; +/// let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); +/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); +/// ``` +/// Allows mapping from and to month, from 1-January to 12-December. +/// Can be Serialized/Deserialized with serde +// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Month { + /// January + January = 0, + /// February + February = 1, + /// March + March = 2, + /// April + April = 3, + /// May + May = 4, + /// June + June = 5, + /// July + July = 6, + /// August + August = 7, + /// September + September = 8, + /// October + October = 9, + /// November + November = 10, + /// December + December = 11, +} + +impl Month { + /// The next month. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// ----------- | --------- | ---------- | --- | --------- + /// `m.succ()`: | `February` | `March` | `...` | `January` + #[inline] + pub fn succ(&self) -> Month { + match *self { + Month::January => Month::February, + Month::February => Month::March, + Month::March => Month::April, + Month::April => Month::May, + Month::May => Month::June, + Month::June => Month::July, + Month::July => Month::August, + Month::August => Month::September, + Month::September => Month::October, + Month::October => Month::November, + Month::November => Month::December, + Month::December => Month::January, + } + } + + /// The previous month. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// ----------- | --------- | ---------- | --- | --------- + /// `m.pred()`: | `December` | `January` | `...` | `November` + #[inline] + pub fn pred(&self) -> Month { + match *self { + Month::January => Month::December, + Month::February => Month::January, + Month::March => Month::February, + Month::April => Month::March, + Month::May => Month::April, + Month::June => Month::May, + Month::July => Month::June, + Month::August => Month::July, + Month::September => Month::August, + Month::October => Month::September, + Month::November => Month::October, + Month::December => Month::November, + } + } + + /// Returns a month-of-year number starting from January = 1. + /// + /// `m`: | `January` | `February` | `...` | `December` + /// -------------------------| --------- | ---------- | --- | ----- + /// `m.number_from_month()`: | 1 | 2 | `...` | 12 + #[inline] + pub fn number_from_month(&self) -> u32 { + match *self { + Month::January => 1, + Month::February => 2, + Month::March => 3, + Month::April => 4, + Month::May => 5, + Month::June => 6, + Month::July => 7, + Month::August => 8, + Month::September => 9, + Month::October => 10, + Month::November => 11, + Month::December => 12, + } + } + + /// Get the name of the month + /// + /// ``` + /// use chrono::Month; + /// + /// assert_eq!(Month::January.name(), "January") + /// ``` + pub fn name(&self) -> &'static str { + match *self { + Month::January => "January", + Month::February => "February", + Month::March => "March", + Month::April => "April", + Month::May => "May", + Month::June => "June", + Month::July => "July", + Month::August => "August", + Month::September => "September", + Month::October => "October", + Month::November => "November", + Month::December => "December", + } + } +} + +impl num_traits::FromPrimitive for Month { + /// Returns an `Option<Month>` from a i64, assuming a 1-index, January = 1. + /// + /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12` + /// ---------------------------| -------------------- | --------------------- | ... | ----- + /// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December) + + #[inline] + fn from_u64(n: u64) -> Option<Month> { + Self::from_u32(n as u32) + } + + #[inline] + fn from_i64(n: i64) -> Option<Month> { + Self::from_u32(n as u32) + } + + #[inline] + fn from_u32(n: u32) -> Option<Month> { + match n { + 1 => Some(Month::January), + 2 => Some(Month::February), + 3 => Some(Month::March), + 4 => Some(Month::April), + 5 => Some(Month::May), + 6 => Some(Month::June), + 7 => Some(Month::July), + 8 => Some(Month::August), + 9 => Some(Month::September), + 10 => Some(Month::October), + 11 => Some(Month::November), + 12 => Some(Month::December), + _ => None, + } + } +} + +/// A duration in calendar months +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Months(pub(crate) u32); + +impl Months { + /// Construct a new `Months` from a number of months + pub const fn new(num: u32) -> Self { + Self(num) + } +} + +/// An error resulting from reading `<Month>` value with `FromStr`. +#[derive(Clone, PartialEq, Eq)] +pub struct ParseMonthError { + pub(crate) _dummy: (), +} + +impl fmt::Debug for ParseMonthError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ParseMonthError {{ .. }}") + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +mod month_serde { + use super::Month; + use serde::{de, ser}; + + use core::fmt; + + impl ser::Serialize for Month { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.collect_str(self.name()) + } + } + + struct MonthVisitor; + + impl<'de> de::Visitor<'de> for MonthVisitor { + type Value = Month; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Month") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected")) + } + } + + impl<'de> de::Deserialize<'de> for Month { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(MonthVisitor) + } + } + + #[test] + fn test_serde_serialize() { + use serde_json::to_string; + use Month::*; + + let cases: Vec<(Month, &str)> = vec![ + (January, "\"January\""), + (February, "\"February\""), + (March, "\"March\""), + (April, "\"April\""), + (May, "\"May\""), + (June, "\"June\""), + (July, "\"July\""), + (August, "\"August\""), + (September, "\"September\""), + (October, "\"October\""), + (November, "\"November\""), + (December, "\"December\""), + ]; + + for (month, expected_str) in cases { + let string = to_string(&month).unwrap(); + assert_eq!(string, expected_str); + } + } + + #[test] + fn test_serde_deserialize() { + use serde_json::from_str; + use Month::*; + + let cases: Vec<(&str, Month)> = vec![ + ("\"january\"", January), + ("\"jan\"", January), + ("\"FeB\"", February), + ("\"MAR\"", March), + ("\"mar\"", March), + ("\"april\"", April), + ("\"may\"", May), + ("\"june\"", June), + ("\"JULY\"", July), + ("\"august\"", August), + ("\"september\"", September), + ("\"October\"", October), + ("\"November\"", November), + ("\"DECEmbEr\"", December), + ]; + + for (string, expected_month) in cases { + let month = from_str::<Month>(string).unwrap(); + assert_eq!(month, expected_month); + } + + let errors: Vec<&str> = + vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""]; + + for string in errors { + from_str::<Month>(string).unwrap_err(); + } + } +} + +#[cfg(test)] +mod tests { + use super::Month; + use crate::{Datelike, TimeZone, Utc}; + + #[test] + fn test_month_enum_primitive_parse() { + use num_traits::FromPrimitive; + + let jan_opt = Month::from_u32(1); + let feb_opt = Month::from_u64(2); + let dec_opt = Month::from_i64(12); + let no_month = Month::from_u32(13); + assert_eq!(jan_opt, Some(Month::January)); + assert_eq!(feb_opt, Some(Month::February)); + assert_eq!(dec_opt, Some(Month::December)); + assert_eq!(no_month, None); + + let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); + assert_eq!(Month::from_u32(date.month()), Some(Month::October)); + + let month = Month::January; + let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); + assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); + } + + #[test] + fn test_month_enum_succ_pred() { + assert_eq!(Month::January.succ(), Month::February); + assert_eq!(Month::December.succ(), Month::January); + assert_eq!(Month::January.pred(), Month::December); + assert_eq!(Month::February.pred(), Month::January); + } +} diff --git a/vendor/chrono/src/naive/date.rs b/vendor/chrono/src/naive/date.rs index 3e34e2074..64af978f3 100644 --- a/vendor/chrono/src/naive/date.rs +++ b/vendor/chrono/src/naive/date.rs @@ -5,18 +5,27 @@ #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; -use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::convert::TryFrom; +use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign}; use core::{fmt, str}; + +use num_integer::div_mod_floor; use num_traits::ToPrimitive; -use oldtime::Duration as OldDuration; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +/// L10n locales. +#[cfg(feature = "unstable-locales")] +use pure_rust_locales::Locale; -use div::div_mod_floor; #[cfg(any(feature = "alloc", feature = "std", test))] -use format::DelayedFormat; -use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; -use format::{Item, Numeric, Pad}; -use naive::{IsoWeek, NaiveDateTime, NaiveTime}; -use {Datelike, Weekday}; +use crate::format::DelayedFormat; +use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{Item, Numeric, Pad}; +use crate::month::Months; +use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; +use crate::oldtime::Duration as OldDuration; +use crate::{Datelike, Duration, Weekday}; use super::internals::{self, DateImpl, Mdf, Of, YearFlags}; use super::isoweek; @@ -42,11 +51,91 @@ const MAX_DAYS_FROM_YEAR_0: i32 = const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + (MIN_YEAR + 400_000) / 4 - (MIN_YEAR + 400_000) / 100 + (MIN_YEAR + 400_000) / 400 - - 146097_000; + - 146_097_000; #[cfg(test)] // only used for testing, but duplicated in naive::datetime const MAX_BITS: usize = 44; +/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first +/// day of the week. +#[derive(Debug)] +pub struct NaiveWeek { + date: NaiveDate, + start: Weekday, +} + +impl NaiveWeek { + /// Returns a date representing the first day of the week. + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Weekday}; + /// + /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); + /// let week = date.week(Weekday::Mon); + /// assert!(week.first_day() <= date); + /// ``` + #[inline] + pub fn first_day(&self) -> NaiveDate { + let start = self.start.num_days_from_monday(); + let end = self.date.weekday().num_days_from_monday(); + let days = if start > end { 7 - start + end } else { end - start }; + self.date - Duration::days(days.into()) + } + + /// Returns a date representing the last day of the week. + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Weekday}; + /// + /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); + /// let week = date.week(Weekday::Mon); + /// assert!(week.last_day() >= date); + /// ``` + #[inline] + pub fn last_day(&self) -> NaiveDate { + self.first_day() + Duration::days(6) + } + + /// Returns a [`RangeInclusive<T>`] representing the whole week bounded by + /// [first_day](./struct.NaiveWeek.html#method.first_day) and + /// [last_day](./struct.NaiveWeek.html#method.last_day) functions. + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Weekday}; + /// + /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); + /// let week = date.week(Weekday::Mon); + /// let days = week.days(); + /// assert!(days.contains(&date)); + /// ``` + #[inline] + pub fn days(&self) -> RangeInclusive<NaiveDate> { + self.first_day()..=self.last_day() + } +} + +/// A duration in calendar days. +/// +/// This is useful because when using `Duration` it is possible +/// that adding `Duration::days(1)` doesn't increment the day value as expected due to it being a +/// fixed number of seconds. This difference applies only when dealing with `DateTime<TimeZone>` data types +/// and in other cases `Duration::days(n)` and `Days::new(n)` are equivalent. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +pub struct Days(pub(crate) u64); + +impl Days { + /// Construct a new `Days` from a number of days + pub const fn new(num: u64) -> Self { + Self(num) + } +} + /// ISO 8601 calendar date without timezone. /// Allows for every [proleptic Gregorian date](#calendar-date) /// from Jan 1, 262145 BCE to Dec 31, 262143 CE. @@ -96,35 +185,48 @@ const MAX_BITS: usize = 44; /// /// This is currently the internal format of Chrono's date types. #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct NaiveDate { ymdf: DateImpl, // (year << 13) | of } /// The minimum possible `NaiveDate` (January 1, 262145 BCE). -pub const MIN_DATE: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; +#[deprecated(since = "0.4.20", note = "Use NaiveDate::MIN instead")] +pub const MIN_DATE: NaiveDate = NaiveDate::MIN; /// The maximum possible `NaiveDate` (December 31, 262143 CE). -pub const MAX_DATE: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o17 /*F*/ }; +#[deprecated(since = "0.4.20", note = "Use NaiveDate::MAX instead")] +pub const MAX_DATE: NaiveDate = NaiveDate::MAX; + +#[cfg(feature = "arbitrary")] +impl arbitrary::Arbitrary<'_> for NaiveDate { + fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveDate> { + let year = u.int_in_range(MIN_YEAR..=MAX_YEAR)?; + let max_days = YearFlags::from_year(year).ndays(); + let ord = u.int_in_range(1..=max_days)?; + NaiveDate::from_yo_opt(year, ord).ok_or(arbitrary::Error::IncorrectFormat) + } +} -// as it is hard to verify year flags in `MIN_DATE` and `MAX_DATE`, +// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`, // we use a separate run-time test. #[test] fn test_date_bounds() { - let calculated_min = NaiveDate::from_ymd(MIN_YEAR, 1, 1); - let calculated_max = NaiveDate::from_ymd(MAX_YEAR, 12, 31); + let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap(); + let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap(); assert!( - MIN_DATE == calculated_min, - "`MIN_DATE` should have a year flag {:?}", + NaiveDate::MIN == calculated_min, + "`NaiveDate::MIN` should have a year flag {:?}", calculated_min.of().flags() ); assert!( - MAX_DATE == calculated_max, - "`MAX_DATE` should have a year flag {:?}", + NaiveDate::MAX == calculated_max, + "`NaiveDate::MAX` should have a year flag {:?}", calculated_max.of().flags() ); // let's also check that the entire range do not exceed 2^44 seconds // (sometimes used for bounding `Duration` against overflow) - let maxsecs = MAX_DATE.signed_duration_since(MIN_DATE).num_seconds(); + let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds(); let maxsecs = maxsecs + 86401; // also take care of DateTime assert!( maxsecs < (1 << MAX_BITS), @@ -134,9 +236,12 @@ fn test_date_bounds() { } impl NaiveDate { + pub(crate) fn weeks_from(&self, day: Weekday) -> i32 { + (self.ordinal() as i32 - self.weekday().num_days_from(day) as i32 + 6) / 7 + } /// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification. fn from_of(year: i32, of: Of) -> Option<NaiveDate> { - if year >= MIN_YEAR && year <= MAX_YEAR && of.valid() { + if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() { let Of(of) = of; Some(NaiveDate { ymdf: (year << 13) | (of as DateImpl) }) } else { @@ -153,22 +258,7 @@ impl NaiveDate { /// (year, month and day). /// /// Panics on the out-of-range date, invalid month and/or day. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, Datelike, Weekday}; - /// - /// let d = NaiveDate::from_ymd(2015, 3, 14); - /// assert_eq!(d.year(), 2015); - /// assert_eq!(d.month(), 3); - /// assert_eq!(d.day(), 14); - /// assert_eq!(d.ordinal(), 73); // day of year - /// assert_eq!(d.iso_week().year(), 2015); - /// assert_eq!(d.iso_week().week(), 11); - /// assert_eq!(d.weekday(), Weekday::Sat); - /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_ymd_opt()` instead")] pub fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate { NaiveDate::from_ymd_opt(year, month, day).expect("invalid or out-of-range date") } @@ -180,7 +270,7 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// /// let from_ymd_opt = NaiveDate::from_ymd_opt; @@ -191,32 +281,17 @@ impl NaiveDate { /// assert!(from_ymd_opt(-4, 2, 29).is_some()); // 5 BCE is a leap year /// assert!(from_ymd_opt(400000, 1, 1).is_none()); /// assert!(from_ymd_opt(-400000, 1, 1).is_none()); - /// ~~~~ + /// ``` pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<NaiveDate> { let flags = YearFlags::from_year(year); - NaiveDate::from_mdf(year, Mdf::new(month, day, flags)) + NaiveDate::from_mdf(year, Mdf::new(month, day, flags)?) } /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) /// (year and day of the year). /// /// Panics on the out-of-range date and/or invalid day of year. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, Datelike, Weekday}; - /// - /// let d = NaiveDate::from_yo(2015, 73); - /// assert_eq!(d.ordinal(), 73); - /// assert_eq!(d.year(), 2015); - /// assert_eq!(d.month(), 3); - /// assert_eq!(d.day(), 14); - /// assert_eq!(d.iso_week().year(), 2015); - /// assert_eq!(d.iso_week().week(), 11); - /// assert_eq!(d.weekday(), Weekday::Sat); - /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_yo_opt()` instead")] pub fn from_yo(year: i32, ordinal: u32) -> NaiveDate { NaiveDate::from_yo_opt(year, ordinal).expect("invalid or out-of-range date") } @@ -228,7 +303,7 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// /// let from_yo_opt = NaiveDate::from_yo_opt; @@ -240,10 +315,10 @@ impl NaiveDate { /// assert!(from_yo_opt(-4, 366).is_some()); // 5 BCE is a leap year /// assert!(from_yo_opt(400000, 1).is_none()); /// assert!(from_yo_opt(-400000, 1).is_none()); - /// ~~~~ + /// ``` pub fn from_yo_opt(year: i32, ordinal: u32) -> Option<NaiveDate> { let flags = YearFlags::from_year(year); - NaiveDate::from_of(year, Of::new(ordinal, flags)) + NaiveDate::from_of(year, Of::new(ordinal, flags)?) } /// Makes a new `NaiveDate` from the [ISO week date](#week-date) @@ -251,22 +326,7 @@ impl NaiveDate { /// The resulting `NaiveDate` may have a different year from the input year. /// /// Panics on the out-of-range date and/or invalid week number. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, Datelike, Weekday}; - /// - /// let d = NaiveDate::from_isoywd(2015, 11, Weekday::Sat); - /// assert_eq!(d.iso_week().year(), 2015); - /// assert_eq!(d.iso_week().week(), 11); - /// assert_eq!(d.weekday(), Weekday::Sat); - /// assert_eq!(d.year(), 2015); - /// assert_eq!(d.month(), 3); - /// assert_eq!(d.day(), 14); - /// assert_eq!(d.ordinal(), 73); // day of year - /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_isoywd_opt()` instead")] pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate { NaiveDate::from_isoywd_opt(year, week, weekday).expect("invalid or out-of-range date") } @@ -279,7 +339,7 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Weekday}; /// /// let from_ymd = NaiveDate::from_ymd; @@ -292,11 +352,11 @@ impl NaiveDate { /// /// assert_eq!(from_isoywd_opt(400000, 10, Weekday::Fri), None); /// assert_eq!(from_isoywd_opt(-400000, 10, Weekday::Sat), None); - /// ~~~~ + /// ``` /// /// The year number of ISO week date may differ from that of the calendar date. /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveDate, Weekday}; /// # let from_ymd = NaiveDate::from_ymd; /// # let from_isoywd_opt = NaiveDate::from_isoywd_opt; @@ -314,7 +374,7 @@ impl NaiveDate { /// assert_eq!(from_isoywd_opt(2015, 53, Weekday::Sun), Some(from_ymd(2016, 1, 3))); /// assert_eq!(from_isoywd_opt(2015, 54, Weekday::Mon), None); /// assert_eq!(from_isoywd_opt(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4))); - /// ~~~~ + /// ``` pub fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option<NaiveDate> { let flags = YearFlags::from_year(year); let nweeks = flags.nisoweeks(); @@ -327,18 +387,18 @@ impl NaiveDate { let prevflags = YearFlags::from_year(year - 1); NaiveDate::from_of( year - 1, - Of::new(weekord + prevflags.ndays() - delta, prevflags), + Of::new(weekord + prevflags.ndays() - delta, prevflags)?, ) } else { let ordinal = weekord - delta; let ndays = flags.ndays(); if ordinal <= ndays { // this year - NaiveDate::from_of(year, Of::new(ordinal, flags)) + NaiveDate::from_of(year, Of::new(ordinal, flags)?) } else { // ordinal > ndays, next year let nextflags = YearFlags::from_year(year + 1); - NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)) + NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)?) } } } else { @@ -350,45 +410,7 @@ impl NaiveDate { /// January 1, 1 being day 1. /// /// Panics if the date is out of range. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, Datelike, Weekday}; - /// - /// let d = NaiveDate::from_num_days_from_ce(735671); - /// assert_eq!(d.num_days_from_ce(), 735671); // days since January 1, 1 CE - /// assert_eq!(d.year(), 2015); - /// assert_eq!(d.month(), 3); - /// assert_eq!(d.day(), 14); - /// assert_eq!(d.ordinal(), 73); // day of year - /// assert_eq!(d.iso_week().year(), 2015); - /// assert_eq!(d.iso_week().week(), 11); - /// assert_eq!(d.weekday(), Weekday::Sat); - /// ~~~~ - /// - /// While not directly supported by Chrono, - /// it is easy to convert from the Julian day number - /// (January 1, 4713 BCE in the *Julian* calendar being Day 0) - /// to Gregorian with this method. - /// (Note that this panics when `jd` is out of range.) - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// fn jd_to_date(jd: i32) -> NaiveDate { - /// // keep in mind that the Julian day number is 0-based - /// // while this method requires an 1-based number. - /// NaiveDate::from_num_days_from_ce(jd - 1721425) - /// } - /// - /// // January 1, 4713 BCE in Julian = November 24, 4714 BCE in Gregorian - /// assert_eq!(jd_to_date(0), NaiveDate::from_ymd(-4713, 11, 24)); - /// - /// assert_eq!(jd_to_date(1721426), NaiveDate::from_ymd(1, 1, 1)); - /// assert_eq!(jd_to_date(2450000), NaiveDate::from_ymd(1995, 10, 9)); - /// assert_eq!(jd_to_date(2451545), NaiveDate::from_ymd(2000, 1, 1)); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_num_days_from_ce_opt()` instead")] #[inline] pub fn from_num_days_from_ce(days: i32) -> NaiveDate { NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date") @@ -401,11 +423,11 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// /// let from_ndays_opt = NaiveDate::from_num_days_from_ce_opt; - /// let from_ymd = NaiveDate::from_ymd; + /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); /// /// assert_eq!(from_ndays_opt(730_000), Some(from_ymd(1999, 9, 3))); /// assert_eq!(from_ndays_opt(1), Some(from_ymd(1, 1, 1))); @@ -413,13 +435,13 @@ impl NaiveDate { /// assert_eq!(from_ndays_opt(-1), Some(from_ymd(0, 12, 30))); /// assert_eq!(from_ndays_opt(100_000_000), None); /// assert_eq!(from_ndays_opt(-100_000_000), None); - /// ~~~~ + /// ``` pub fn from_num_days_from_ce_opt(days: i32) -> Option<NaiveDate> { let days = days + 365; // make December 31, 1 BCE equal to day 0 let (year_div_400, cycle) = div_mod_floor(days, 146_097); let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?) } /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week @@ -432,21 +454,7 @@ impl NaiveDate { /// of `weekday` in `month` (eg. the 6th Friday of March 2017) then this function will panic. /// /// `n` is 1-indexed. Passing `n=0` will cause a panic. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, Weekday}; - /// - /// let from_weekday_of_month = NaiveDate::from_weekday_of_month; - /// let from_ymd = NaiveDate::from_ymd; - /// - /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Wed, 1), from_ymd(2018, 8, 1)); - /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 1), from_ymd(2018, 8, 3)); - /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Tue, 2), from_ymd(2018, 8, 14)); - /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 4), from_ymd(2018, 8, 24)); - /// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 5), from_ymd(2018, 8, 31)); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_weekday_of_month_opt()` instead")] pub fn from_weekday_of_month(year: i32, month: u32, weekday: Weekday, n: u8) -> NaiveDate { NaiveDate::from_weekday_of_month_opt(year, month, weekday, n).expect("out-of-range date") } @@ -455,11 +463,11 @@ impl NaiveDate { /// since the beginning of the given month. For instance, if you want the 2nd Friday of March /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. `n` is 1-indexed. /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Weekday}; /// assert_eq!(NaiveDate::from_weekday_of_month_opt(2017, 3, Weekday::Fri, 2), /// NaiveDate::from_ymd_opt(2017, 3, 10)) - /// ~~~~ + /// ``` /// /// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in /// `month` (eg. the 6th Friday of March 2017), or if `n == 0`. @@ -472,7 +480,7 @@ impl NaiveDate { if n == 0 { return None; } - let first = NaiveDate::from_ymd(year, month, 1).weekday(); + let first = NaiveDate::from_ymd_opt(year, month, 1)?.weekday(); let first_to_dow = (7 + weekday.number_from_monday() - first.number_from_monday()) % 7; let day = (u32::from(n) - 1) * 7 + first_to_dow + 1; NaiveDate::from_ymd_opt(year, month, day) @@ -484,64 +492,223 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// /// let parse_from_str = NaiveDate::parse_from_str; /// /// assert_eq!(parse_from_str("2015-09-05", "%Y-%m-%d"), - /// Ok(NaiveDate::from_ymd(2015, 9, 5))); + /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap())); /// assert_eq!(parse_from_str("5sep2015", "%d%b%Y"), - /// Ok(NaiveDate::from_ymd(2015, 9, 5))); - /// ~~~~ + /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap())); + /// ``` /// /// Time and offset is ignored for the purpose of parsing. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveDate; /// # let parse_from_str = NaiveDate::parse_from_str; /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveDate::from_ymd(2014, 5, 17))); - /// ~~~~ + /// Ok(NaiveDate::from_ymd_opt(2014, 5, 17).unwrap())); + /// ``` /// /// Out-of-bound dates or insufficient fields are errors. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveDate; /// # let parse_from_str = NaiveDate::parse_from_str; /// assert!(parse_from_str("2015/9", "%Y/%m").is_err()); /// assert!(parse_from_str("2015/9/31", "%Y/%m/%d").is_err()); - /// ~~~~ + /// ``` /// /// All parsed fields should be consistent to each other, otherwise it's an error. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveDate; /// # let parse_from_str = NaiveDate::parse_from_str; /// assert!(parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); - /// ~~~~ + /// ``` pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> { let mut parsed = Parsed::new(); parse(&mut parsed, s, StrftimeItems::new(fmt))?; parsed.to_naive_date() } + /// Add a duration in [`Months`] to the date + /// + /// If the day would be out of range for the resulting month, use the last day for that month. + /// + /// Returns `None` if the resulting date would be out of range. + /// + /// ``` + /// # use chrono::{NaiveDate, Months}; + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_add_months(Months::new(6)), + /// Some(NaiveDate::from_ymd_opt(2022, 8, 20).unwrap()) + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_months(Months::new(2)), + /// Some(NaiveDate::from_ymd_opt(2022, 9, 30).unwrap()) + /// ); + /// ``` + pub fn checked_add_months(self, months: Months) -> Option<Self> { + if months.0 == 0 { + return Some(self); + } + + match months.0 <= core::i32::MAX as u32 { + true => self.diff_months(months.0 as i32), + false => None, + } + } + + /// Subtract a duration in [`Months`] from the date + /// + /// If the day would be out of range for the resulting month, use the last day for that month. + /// + /// Returns `None` if the resulting date would be out of range. + /// + /// ``` + /// # use chrono::{NaiveDate, Months}; + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_months(Months::new(6)), + /// Some(NaiveDate::from_ymd_opt(2021, 8, 20).unwrap()) + /// ); + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap() + /// .checked_sub_months(Months::new(core::i32::MAX as u32 + 1)), + /// None + /// ); + /// ``` + pub fn checked_sub_months(self, months: Months) -> Option<Self> { + if months.0 == 0 { + return Some(self); + } + + // Copy `i32::MAX` here so we don't have to do a complicated cast + match months.0 <= 2_147_483_647 { + true => self.diff_months(-(months.0 as i32)), + false => None, + } + } + + fn diff_months(self, months: i32) -> Option<Self> { + let (years, left) = ((months / 12), (months % 12)); + + // Determine new year (without taking months into account for now + + let year = if (years > 0 && years > (MAX_YEAR - self.year())) + || (years < 0 && years < (MIN_YEAR - self.year())) + { + return None; + } else { + self.year() + years + }; + + // Determine new month + + let month = self.month() as i32 + left; + let (year, month) = if month <= 0 { + if year == MIN_YEAR { + return None; + } + + (year - 1, month + 12) + } else if month > 12 { + if year == MAX_YEAR { + return None; + } + + (year + 1, month - 12) + } else { + (year, month) + }; + + // Clamp original day in case new month is shorter + + let flags = YearFlags::from_year(year); + let feb_days = if flags.ndays() == 366 { 29 } else { 28 }; + let days = [31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + let day = Ord::min(self.day(), days[(month - 1) as usize]); + + NaiveDate::from_mdf(year, Mdf::new(month as u32, day, flags)?) + } + + /// Add a duration in [`Days`] to the date + /// + /// Returns `None` if the resulting date would be out of range. + /// + /// ``` + /// # use chrono::{NaiveDate, Days}; + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_add_days(Days::new(9)), + /// Some(NaiveDate::from_ymd_opt(2022, 3, 1).unwrap()) + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(2)), + /// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap()) + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(1000000000000)), + /// None + /// ); + /// ``` + pub fn checked_add_days(self, days: Days) -> Option<Self> { + if days.0 == 0 { + return Some(self); + } + + i64::try_from(days.0).ok().and_then(|d| self.diff_days(d)) + } + + /// Subtract a duration in [`Days`] from the date + /// + /// Returns `None` if the resulting date would be out of range. + /// + /// ``` + /// # use chrono::{NaiveDate, Days}; + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(6)), + /// Some(NaiveDate::from_ymd_opt(2022, 2, 14).unwrap()) + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(1000000000000)), + /// None + /// ); + /// ``` + pub fn checked_sub_days(self, days: Days) -> Option<Self> { + if days.0 == 0 { + return Some(self); + } + + i64::try_from(days.0).ok().and_then(|d| self.diff_days(-d)) + } + + fn diff_days(self, days: i64) -> Option<Self> { + let secs = days.checked_mul(86400)?; // 86400 seconds in one day + if secs >= core::i64::MAX / 1000 || secs <= core::i64::MIN / 1000 { + return None; // See the `time` 0.1 crate. Outside these bounds, `Duration::seconds` will panic + } + self.checked_add_signed(Duration::seconds(secs)) + } + /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime}; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); - /// let t = NaiveTime::from_hms_milli(12, 34, 56, 789); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); + /// let t = NaiveTime::from_hms_milli_opt(12, 34, 56, 789).unwrap(); /// /// let dt: NaiveDateTime = d.and_time(t); /// assert_eq!(dt.date(), d); /// assert_eq!(dt.time(), t); - /// ~~~~ + /// ``` #[inline] - pub fn and_time(&self, time: NaiveTime) -> NaiveDateTime { + pub const fn and_time(&self, time: NaiveTime) -> NaiveDateTime { NaiveDateTime::new(*self, time) } @@ -551,19 +718,7 @@ impl NaiveDate { /// use `NaiveDate::and_hms_*` methods with a subsecond parameter instead. /// /// Panics on invalid hour, minute and/or second. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; - /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); - /// - /// let dt: NaiveDateTime = d.and_hms(12, 34, 56); - /// assert_eq!(dt.year(), 2015); - /// assert_eq!(dt.weekday(), Weekday::Wed); - /// assert_eq!(dt.second(), 56); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `and_hms_opt()` instead")] #[inline] pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> NaiveDateTime { self.and_hms_opt(hour, min, sec).expect("invalid time") @@ -578,15 +733,15 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); /// assert!(d.and_hms_opt(12, 34, 56).is_some()); /// assert!(d.and_hms_opt(12, 34, 60).is_none()); // use `and_hms_milli_opt` instead /// assert!(d.and_hms_opt(12, 60, 56).is_none()); /// assert!(d.and_hms_opt(24, 34, 56).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<NaiveDateTime> { NaiveTime::from_hms_opt(hour, min, sec).map(|time| self.and_time(time)) @@ -598,20 +753,7 @@ impl NaiveDate { /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or millisecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; - /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); - /// - /// let dt: NaiveDateTime = d.and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.year(), 2015); - /// assert_eq!(dt.weekday(), Weekday::Wed); - /// assert_eq!(dt.second(), 56); - /// assert_eq!(dt.nanosecond(), 789_000_000); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `and_hms_milli_opt()` instead")] #[inline] pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> NaiveDateTime { self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") @@ -626,17 +768,17 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); /// assert!(d.and_hms_milli_opt(12, 34, 56, 789).is_some()); /// assert!(d.and_hms_milli_opt(12, 34, 59, 1_789).is_some()); // leap second /// assert!(d.and_hms_milli_opt(12, 34, 59, 2_789).is_none()); /// assert!(d.and_hms_milli_opt(12, 34, 60, 789).is_none()); /// assert!(d.and_hms_milli_opt(12, 60, 56, 789).is_none()); /// assert!(d.and_hms_milli_opt(24, 34, 56, 789).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn and_hms_milli_opt( &self, @@ -657,17 +799,18 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); /// /// let dt: NaiveDateTime = d.and_hms_micro(12, 34, 56, 789_012); /// assert_eq!(dt.year(), 2015); /// assert_eq!(dt.weekday(), Weekday::Wed); /// assert_eq!(dt.second(), 56); /// assert_eq!(dt.nanosecond(), 789_012_000); - /// ~~~~ + /// ``` + #[deprecated(since = "0.4.23", note = "use `and_hms_micro_opt()` instead")] #[inline] pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> NaiveDateTime { self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") @@ -682,17 +825,17 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); /// assert!(d.and_hms_micro_opt(12, 34, 56, 789_012).is_some()); /// assert!(d.and_hms_micro_opt(12, 34, 59, 1_789_012).is_some()); // leap second /// assert!(d.and_hms_micro_opt(12, 34, 59, 2_789_012).is_none()); /// assert!(d.and_hms_micro_opt(12, 34, 60, 789_012).is_none()); /// assert!(d.and_hms_micro_opt(12, 60, 56, 789_012).is_none()); /// assert!(d.and_hms_micro_opt(24, 34, 56, 789_012).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn and_hms_micro_opt( &self, @@ -710,20 +853,7 @@ impl NaiveDate { /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or nanosecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday}; - /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); - /// - /// let dt: NaiveDateTime = d.and_hms_nano(12, 34, 56, 789_012_345); - /// assert_eq!(dt.year(), 2015); - /// assert_eq!(dt.weekday(), Weekday::Wed); - /// assert_eq!(dt.second(), 56); - /// assert_eq!(dt.nanosecond(), 789_012_345); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `and_hms_nano_opt()` instead")] #[inline] pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> NaiveDateTime { self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") @@ -738,17 +868,17 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); /// assert!(d.and_hms_nano_opt(12, 34, 56, 789_012_345).is_some()); /// assert!(d.and_hms_nano_opt(12, 34, 59, 1_789_012_345).is_some()); // leap second /// assert!(d.and_hms_nano_opt(12, 34, 59, 2_789_012_345).is_none()); /// assert!(d.and_hms_nano_opt(12, 34, 60, 789_012_345).is_none()); /// assert!(d.and_hms_nano_opt(12, 60, 56, 789_012_345).is_none()); /// assert!(d.and_hms_nano_opt(24, 34, 56, 789_012_345).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn and_hms_nano_opt( &self, @@ -768,7 +898,7 @@ impl NaiveDate { /// Returns the packed ordinal-flags. #[inline] - fn of(&self) -> Of { + const fn of(&self) -> Of { Of((self.ymdf & 0b1_1111_1111_1111) as u32) } @@ -796,16 +926,7 @@ impl NaiveDate { /// Makes a new `NaiveDate` for the next calendar date. /// /// Panics when `self` is the last representable date. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).succ(), NaiveDate::from_ymd(2015, 6, 4)); - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 30).succ(), NaiveDate::from_ymd(2015, 7, 1)); - /// assert_eq!(NaiveDate::from_ymd(2015, 12, 31).succ(), NaiveDate::from_ymd(2016, 1, 1)); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `succ_opt()` instead")] #[inline] pub fn succ(&self) -> NaiveDate { self.succ_opt().expect("out of bound") @@ -817,14 +938,13 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; - /// use chrono::naive::MAX_DATE; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).succ_opt(), - /// Some(NaiveDate::from_ymd(2015, 6, 4))); - /// assert_eq!(MAX_DATE.succ_opt(), None); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 6, 3).unwrap().succ_opt(), + /// Some(NaiveDate::from_ymd_opt(2015, 6, 4).unwrap())); + /// assert_eq!(NaiveDate::MAX.succ_opt(), None); + /// ``` #[inline] pub fn succ_opt(&self) -> Option<NaiveDate> { self.with_of(self.of().succ()).or_else(|| NaiveDate::from_ymd_opt(self.year() + 1, 1, 1)) @@ -833,16 +953,7 @@ impl NaiveDate { /// Makes a new `NaiveDate` for the previous calendar date. /// /// Panics when `self` is the first representable date. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).pred(), NaiveDate::from_ymd(2015, 6, 2)); - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 1).pred(), NaiveDate::from_ymd(2015, 5, 31)); - /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).pred(), NaiveDate::from_ymd(2014, 12, 31)); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `pred_opt()` instead")] #[inline] pub fn pred(&self) -> NaiveDate { self.pred_opt().expect("out of bound") @@ -854,14 +965,13 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; - /// use chrono::naive::MIN_DATE; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 6, 3).pred_opt(), - /// Some(NaiveDate::from_ymd(2015, 6, 2))); - /// assert_eq!(MIN_DATE.pred_opt(), None); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 6, 3).unwrap().pred_opt(), + /// Some(NaiveDate::from_ymd_opt(2015, 6, 2).unwrap())); + /// assert_eq!(NaiveDate::MIN.pred_opt(), None); + /// ``` #[inline] pub fn pred_opt(&self) -> Option<NaiveDate> { self.with_of(self.of().pred()).or_else(|| NaiveDate::from_ymd_opt(self.year() - 1, 12, 31)) @@ -873,32 +983,29 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveDate}; - /// use chrono::naive::MAX_DATE; /// - /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(d.checked_add_signed(Duration::days(40)), - /// Some(NaiveDate::from_ymd(2015, 10, 15))); + /// Some(NaiveDate::from_ymd_opt(2015, 10, 15).unwrap())); /// assert_eq!(d.checked_add_signed(Duration::days(-40)), - /// Some(NaiveDate::from_ymd(2015, 7, 27))); + /// Some(NaiveDate::from_ymd_opt(2015, 7, 27).unwrap())); /// assert_eq!(d.checked_add_signed(Duration::days(1_000_000_000)), None); /// assert_eq!(d.checked_add_signed(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MAX_DATE.checked_add_signed(Duration::days(1)), None); - /// # } - /// ~~~~ + /// assert_eq!(NaiveDate::MAX.checked_add_signed(Duration::days(1)), None); + /// ``` pub fn checked_add_signed(self, rhs: OldDuration) -> Option<NaiveDate> { let year = self.year(); let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); - let cycle = try_opt!((cycle as i32).checked_add(try_opt!(rhs.num_days().to_i32()))); + let cycle = (cycle as i32).checked_add(rhs.num_days().to_i32()?)?; let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); year_div_400 += cycle_div_400y; let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?) } /// Subtracts the `days` part of given `Duration` from the current date. @@ -907,32 +1014,29 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveDate}; - /// use chrono::naive::MIN_DATE; /// - /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(d.checked_sub_signed(Duration::days(40)), - /// Some(NaiveDate::from_ymd(2015, 7, 27))); + /// Some(NaiveDate::from_ymd_opt(2015, 7, 27).unwrap())); /// assert_eq!(d.checked_sub_signed(Duration::days(-40)), - /// Some(NaiveDate::from_ymd(2015, 10, 15))); + /// Some(NaiveDate::from_ymd_opt(2015, 10, 15).unwrap())); /// assert_eq!(d.checked_sub_signed(Duration::days(1_000_000_000)), None); /// assert_eq!(d.checked_sub_signed(Duration::days(-1_000_000_000)), None); - /// assert_eq!(MIN_DATE.checked_sub_signed(Duration::days(1)), None); - /// # } - /// ~~~~ + /// assert_eq!(NaiveDate::MIN.checked_sub_signed(Duration::days(1)), None); + /// ``` pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<NaiveDate> { let year = self.year(); let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); - let cycle = try_opt!((cycle as i32).checked_sub(try_opt!(rhs.num_days().to_i32()))); + let cycle = (cycle as i32).checked_sub(rhs.num_days().to_i32()?)?; let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); year_div_400 += cycle_div_400y; let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) + NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?) } /// Subtracts another `NaiveDate` from the current date. @@ -943,8 +1047,7 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveDate}; /// /// let from_ymd = NaiveDate::from_ymd; @@ -957,8 +1060,7 @@ impl NaiveDate { /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 1, 1)), Duration::days(365)); /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2010, 1, 1)), Duration::days(365*4 + 1)); /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(1614, 1, 1)), Duration::days(365*400 + 97)); - /// # } - /// ~~~~ + /// ``` pub fn signed_duration_since(self, rhs: NaiveDate) -> OldDuration { let year1 = self.year(); let year2 = rhs.year(); @@ -971,6 +1073,19 @@ impl NaiveDate { ) } + /// Returns the number of whole years from the given `base` until `self`. + pub fn years_since(&self, base: Self) -> Option<u32> { + let mut years = self.year() - base.year(); + if (self.month(), self.day()) < (base.month(), base.day()) { + years -= 1; + } + + match years >= 0 { + true => Some(years as u32), + false => None, + } + } + /// Formats the date with the specified formatting items. /// Otherwise it is the same as the ordinary `format` method. /// @@ -979,26 +1094,27 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// use chrono::format::strftime::StrftimeItems; /// /// let fmt = StrftimeItems::new("%Y-%m-%d"); - /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(d.format_with_items(fmt.clone()).to_string(), "2015-09-05"); /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); - /// ~~~~ + /// ``` /// /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveDate; /// # use chrono::format::strftime::StrftimeItems; /// # let fmt = StrftimeItems::new("%Y-%m-%d").clone(); - /// # let d = NaiveDate::from_ymd(2015, 9, 5); + /// # let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05"); - /// ~~~~ + /// ``` #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> where @@ -1024,29 +1140,61 @@ impl NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveDate; /// - /// let d = NaiveDate::from_ymd(2015, 9, 5); + /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); /// assert_eq!(d.format("%A, %-d %B, %C%y").to_string(), "Saturday, 5 September, 2015"); - /// ~~~~ + /// ``` /// /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveDate; - /// # let d = NaiveDate::from_ymd(2015, 9, 5); + /// # let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); /// assert_eq!(format!("{}", d.format("%Y-%m-%d")), "2015-09-05"); /// assert_eq!(format!("{}", d.format("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015"); - /// ~~~~ + /// ``` #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { self.format_with_items(StrftimeItems::new(fmt)) } - /// Returns an iterator that steps by days until the last representable date. + /// Formats the date with the specified formatting items and locale. + #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] + #[inline] + pub fn format_localized_with_items<'a, I, B>( + &self, + items: I, + locale: Locale, + ) -> DelayedFormat<I> + where + I: Iterator<Item = B> + Clone, + B: Borrow<Item<'a>>, + { + DelayedFormat::new_with_locale(Some(*self), None, items, locale) + } + + /// Formats the date with the specified format string and locale. + /// + /// See the [`crate::format::strftime`] module on the supported escape + /// sequences. + #[cfg(feature = "unstable-locales")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))] + #[inline] + pub fn format_localized<'a>( + &self, + fmt: &'a str, + locale: Locale, + ) -> DelayedFormat<StrftimeItems<'a>> { + self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) + } + + /// Returns an iterator that steps by days across all representable dates. /// /// # Example /// @@ -1054,25 +1202,30 @@ impl NaiveDate { /// # use chrono::NaiveDate; /// /// let expected = [ - /// NaiveDate::from_ymd(2016, 2, 27), - /// NaiveDate::from_ymd(2016, 2, 28), - /// NaiveDate::from_ymd(2016, 2, 29), - /// NaiveDate::from_ymd(2016, 3, 1), + /// NaiveDate::from_ymd_opt(2016, 2, 27).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 2, 28).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 2, 29).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 3, 1).unwrap(), /// ]; /// /// let mut count = 0; - /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_days().take(4).enumerate() { + /// for (idx, d) in NaiveDate::from_ymd_opt(2016, 2, 27).unwrap().iter_days().take(4).enumerate() { /// assert_eq!(d, expected[idx]); /// count += 1; /// } /// assert_eq!(count, 4); + /// + /// for d in NaiveDate::from_ymd_opt(2016, 3, 1).unwrap().iter_days().rev().take(4) { + /// count -= 1; + /// assert_eq!(d, expected[count]); + /// } /// ``` #[inline] - pub fn iter_days(&self) -> NaiveDateDaysIterator { + pub const fn iter_days(&self) -> NaiveDateDaysIterator { NaiveDateDaysIterator { value: *self } } - /// Returns an iterator that steps by weeks until the last representable date. + /// Returns an iterator that steps by weeks across all representable dates. /// /// # Example /// @@ -1080,23 +1233,40 @@ impl NaiveDate { /// # use chrono::NaiveDate; /// /// let expected = [ - /// NaiveDate::from_ymd(2016, 2, 27), - /// NaiveDate::from_ymd(2016, 3, 5), - /// NaiveDate::from_ymd(2016, 3, 12), - /// NaiveDate::from_ymd(2016, 3, 19), + /// NaiveDate::from_ymd_opt(2016, 2, 27).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 3, 5).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 3, 12).unwrap(), + /// NaiveDate::from_ymd_opt(2016, 3, 19).unwrap(), /// ]; /// /// let mut count = 0; - /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_weeks().take(4).enumerate() { + /// for (idx, d) in NaiveDate::from_ymd_opt(2016, 2, 27).unwrap().iter_weeks().take(4).enumerate() { /// assert_eq!(d, expected[idx]); /// count += 1; /// } /// assert_eq!(count, 4); + /// + /// for d in NaiveDate::from_ymd_opt(2016, 3, 19).unwrap().iter_weeks().rev().take(4) { + /// count -= 1; + /// assert_eq!(d, expected[count]); + /// } /// ``` #[inline] - pub fn iter_weeks(&self) -> NaiveDateWeeksIterator { + pub const fn iter_weeks(&self) -> NaiveDateWeeksIterator { NaiveDateWeeksIterator { value: *self } } + + /// Returns the [`NaiveWeek`] that the date belongs to, starting with the [`Weekday`] + /// specified. + #[inline] + pub const fn week(&self, start: Weekday) -> NaiveWeek { + NaiveWeek { date: *self, start } + } + + /// The minimum possible `NaiveDate` (January 1, 262145 BCE). + pub const MIN: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; + /// The maximum possible `NaiveDate` (December 31, 262143 CE). + pub const MAX: NaiveDate = NaiveDate { ymdf: (MAX_YEAR << 13) | (365 << 4) | 0o17 /*F*/ }; } impl Datelike for NaiveDate { @@ -1104,12 +1274,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).year(), 2015); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).year(), -308); // 309 BCE - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().year(), 2015); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().year(), -308); // 309 BCE + /// ``` #[inline] fn year(&self) -> i32 { self.ymdf >> 13 @@ -1121,12 +1291,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).month(), 9); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).month(), 3); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().month(), 9); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().month(), 3); + /// ``` #[inline] fn month(&self) -> u32 { self.mdf().month() @@ -1138,12 +1308,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).month0(), 8); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).month0(), 2); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().month0(), 8); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().month0(), 2); + /// ``` #[inline] fn month0(&self) -> u32 { self.mdf().month() - 1 @@ -1155,24 +1325,24 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).day(), 8); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).day(), 14); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().day(), 8); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().day(), 14); + /// ``` /// /// Combined with [`NaiveDate::pred`](#method.pred), /// one can determine the number of days in a particular month. /// (Note that this panics when `year` is out of range.) /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// /// fn ndays_in_month(year: i32, month: u32) -> u32 { /// // the first day of the next month... /// let (y, m) = if month == 12 { (year + 1, 1) } else { (year, month + 1) }; - /// let d = NaiveDate::from_ymd(y, m, 1); + /// let d = NaiveDate::from_ymd_opt(y, m, 1).unwrap(); /// /// // ...is preceded by the last day of the original month /// d.pred().day() @@ -1183,7 +1353,7 @@ impl Datelike for NaiveDate { /// assert_eq!(ndays_in_month(2015, 12), 31); /// assert_eq!(ndays_in_month(2016, 2), 29); /// assert_eq!(ndays_in_month(2017, 2), 28); - /// ~~~~ + /// ``` #[inline] fn day(&self) -> u32 { self.mdf().day() @@ -1195,12 +1365,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).day0(), 7); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).day0(), 13); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().day0(), 7); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().day0(), 13); + /// ``` #[inline] fn day0(&self) -> u32 { self.mdf().day() - 1 @@ -1212,23 +1382,23 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).ordinal(), 251); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).ordinal(), 74); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().ordinal(), 251); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().ordinal(), 74); + /// ``` /// /// Combined with [`NaiveDate::pred`](#method.pred), /// one can determine the number of days in a particular year. /// (Note that this panics when `year` is out of range.) /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// /// fn ndays_in_year(year: i32) -> u32 { /// // the first day of the next year... - /// let d = NaiveDate::from_ymd(year + 1, 1, 1); + /// let d = NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap(); /// /// // ...is preceded by the last day of the original year /// d.pred().ordinal() @@ -1239,7 +1409,7 @@ impl Datelike for NaiveDate { /// assert_eq!(ndays_in_year(2017), 365); /// assert_eq!(ndays_in_year(2000), 366); /// assert_eq!(ndays_in_year(2100), 365); - /// ~~~~ + /// ``` #[inline] fn ordinal(&self) -> u32 { self.of().ordinal() @@ -1251,12 +1421,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).ordinal0(), 250); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).ordinal0(), 73); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().ordinal0(), 250); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().ordinal0(), 73); + /// ``` #[inline] fn ordinal0(&self) -> u32 { self.of().ordinal() - 1 @@ -1266,12 +1436,12 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike, Weekday}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).weekday(), Weekday::Tue); - /// assert_eq!(NaiveDate::from_ymd(-308, 3, 14).weekday(), Weekday::Fri); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().weekday(), Weekday::Tue); + /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().weekday(), Weekday::Fri); + /// ``` #[inline] fn weekday(&self) -> Weekday { self.of().weekday() @@ -1288,22 +1458,22 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_year(2016), - /// Some(NaiveDate::from_ymd(2016, 9, 8))); - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_year(-308), - /// Some(NaiveDate::from_ymd(-308, 9, 8))); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_year(2016), + /// Some(NaiveDate::from_ymd_opt(2016, 9, 8).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_year(-308), + /// Some(NaiveDate::from_ymd_opt(-308, 9, 8).unwrap())); + /// ``` /// /// A leap day (February 29) is a good example that this method can return `None`. /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveDate, Datelike}; - /// assert!(NaiveDate::from_ymd(2016, 2, 29).with_year(2015).is_none()); - /// assert!(NaiveDate::from_ymd(2016, 2, 29).with_year(2020).is_some()); - /// ~~~~ + /// assert!(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().with_year(2015).is_none()); + /// assert!(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().with_year(2020).is_some()); + /// ``` #[inline] fn with_year(&self, year: i32) -> Option<NaiveDate> { // we need to operate with `mdf` since we should keep the month and day number as is @@ -1322,17 +1492,17 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month(10), - /// Some(NaiveDate::from_ymd(2015, 10, 8))); - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month(13), None); // no month 13 - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).with_month(2), None); // no February 30 - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month(10), + /// Some(NaiveDate::from_ymd_opt(2015, 10, 8).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month(13), None); // no month 13 + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().with_month(2), None); // no February 30 + /// ``` #[inline] fn with_month(&self, month: u32) -> Option<NaiveDate> { - self.with_mdf(self.mdf().with_month(month)) + self.with_mdf(self.mdf().with_month(month)?) } /// Makes a new `NaiveDate` with the month number (starting from 0) changed. @@ -1341,17 +1511,17 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month0(9), - /// Some(NaiveDate::from_ymd(2015, 10, 8))); - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_month0(12), None); // no month 13 - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 30).with_month0(1), None); // no February 30 - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month0(9), + /// Some(NaiveDate::from_ymd_opt(2015, 10, 8).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month0(12), None); // no month 13 + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().with_month0(1), None); // no February 30 + /// ``` #[inline] fn with_month0(&self, month0: u32) -> Option<NaiveDate> { - self.with_mdf(self.mdf().with_month(month0 + 1)) + self.with_mdf(self.mdf().with_month(month0 + 1)?) } /// Makes a new `NaiveDate` with the day of month (starting from 1) changed. @@ -1360,17 +1530,17 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day(30), - /// Some(NaiveDate::from_ymd(2015, 9, 30))); - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day(31), + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day(30), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day(31), /// None); // no September 31 - /// ~~~~ + /// ``` #[inline] fn with_day(&self, day: u32) -> Option<NaiveDate> { - self.with_mdf(self.mdf().with_day(day)) + self.with_mdf(self.mdf().with_day(day)?) } /// Makes a new `NaiveDate` with the day of month (starting from 0) changed. @@ -1379,17 +1549,17 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day0(29), - /// Some(NaiveDate::from_ymd(2015, 9, 30))); - /// assert_eq!(NaiveDate::from_ymd(2015, 9, 8).with_day0(30), + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day0(29), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day0(30), /// None); // no September 31 - /// ~~~~ + /// ``` #[inline] fn with_day0(&self, day0: u32) -> Option<NaiveDate> { - self.with_mdf(self.mdf().with_day(day0 + 1)) + self.with_mdf(self.mdf().with_day(day0 + 1)?) } /// Makes a new `NaiveDate` with the day of year (starting from 1) changed. @@ -1398,22 +1568,22 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal(60), - /// Some(NaiveDate::from_ymd(2015, 3, 1))); - /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal(366), + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal(60), + /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal(366), /// None); // 2015 had only 365 days /// - /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal(60), - /// Some(NaiveDate::from_ymd(2016, 2, 29))); - /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal(366), - /// Some(NaiveDate::from_ymd(2016, 12, 31))); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal(60), + /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal(366), + /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap())); + /// ``` #[inline] fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDate> { - self.with_of(self.of().with_ordinal(ordinal)) + self.with_of(self.of().with_ordinal(ordinal)?) } /// Makes a new `NaiveDate` with the day of year (starting from 0) changed. @@ -1422,22 +1592,22 @@ impl Datelike for NaiveDate { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike}; /// - /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal0(59), - /// Some(NaiveDate::from_ymd(2015, 3, 1))); - /// assert_eq!(NaiveDate::from_ymd(2015, 1, 1).with_ordinal0(365), + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal0(59), + /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal0(365), /// None); // 2015 had only 365 days /// - /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal0(59), - /// Some(NaiveDate::from_ymd(2016, 2, 29))); - /// assert_eq!(NaiveDate::from_ymd(2016, 1, 1).with_ordinal0(365), - /// Some(NaiveDate::from_ymd(2016, 12, 31))); - /// ~~~~ + /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal0(59), + /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap())); + /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal0(365), + /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap())); + /// ``` #[inline] fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDate> { - self.with_of(self.of().with_ordinal(ordinal0 + 1)) + self.with_of(self.of().with_ordinal(ordinal0 + 1)?) } } @@ -1449,8 +1619,7 @@ impl Datelike for NaiveDate { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveDate}; /// /// let from_ymd = NaiveDate::from_ymd; @@ -1463,8 +1632,7 @@ impl Datelike for NaiveDate { /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(364), from_ymd(2014, 12, 31)); /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(365*4 + 1), from_ymd(2018, 1, 1)); /// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(365*400 + 97), from_ymd(2414, 1, 1)); -/// # } -/// ~~~~ +/// ``` impl Add<OldDuration> for NaiveDate { type Output = NaiveDate; @@ -1481,6 +1649,75 @@ impl AddAssign<OldDuration> for NaiveDate { } } +impl Add<Months> for NaiveDate { + type Output = NaiveDate; + + /// An addition of months to `NaiveDate` clamped to valid days in resulting month. + /// + /// # Panics + /// + /// Panics if the resulting date would be out of range. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDate, Months}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// assert_eq!(from_ymd(2014, 1, 1) + Months::new(1), from_ymd(2014, 2, 1)); + /// assert_eq!(from_ymd(2014, 1, 1) + Months::new(11), from_ymd(2014, 12, 1)); + /// assert_eq!(from_ymd(2014, 1, 1) + Months::new(12), from_ymd(2015, 1, 1)); + /// assert_eq!(from_ymd(2014, 1, 1) + Months::new(13), from_ymd(2015, 2, 1)); + /// assert_eq!(from_ymd(2014, 1, 31) + Months::new(1), from_ymd(2014, 2, 28)); + /// assert_eq!(from_ymd(2020, 1, 31) + Months::new(1), from_ymd(2020, 2, 29)); + /// ``` + fn add(self, months: Months) -> Self::Output { + self.checked_add_months(months).unwrap() + } +} + +impl Sub<Months> for NaiveDate { + type Output = NaiveDate; + + /// A subtraction of Months from `NaiveDate` clamped to valid days in resulting month. + /// + /// # Panics + /// + /// Panics if the resulting date would be out of range. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDate, Months}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// assert_eq!(from_ymd(2014, 1, 1) - Months::new(11), from_ymd(2013, 2, 1)); + /// assert_eq!(from_ymd(2014, 1, 1) - Months::new(12), from_ymd(2013, 1, 1)); + /// assert_eq!(from_ymd(2014, 1, 1) - Months::new(13), from_ymd(2012, 12, 1)); + /// ``` + fn sub(self, months: Months) -> Self::Output { + self.checked_sub_months(months).unwrap() + } +} + +impl Add<Days> for NaiveDate { + type Output = NaiveDate; + + fn add(self, days: Days) -> Self::Output { + self.checked_add_days(days).unwrap() + } +} + +impl Sub<Days> for NaiveDate { + type Output = NaiveDate; + + fn sub(self, days: Days) -> Self::Output { + self.checked_sub_days(days).unwrap() + } +} + /// A subtraction of `Duration` from `NaiveDate` discards the fractional days, /// rounding to the closest integral number of days towards `Duration::zero()`. /// It is the same as the addition with a negated `Duration`. @@ -1490,8 +1727,7 @@ impl AddAssign<OldDuration> for NaiveDate { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveDate}; /// /// let from_ymd = NaiveDate::from_ymd; @@ -1504,8 +1740,7 @@ impl AddAssign<OldDuration> for NaiveDate { /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(364), from_ymd(2013, 1, 2)); /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(365*4 + 1), from_ymd(2010, 1, 1)); /// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(365*400 + 97), from_ymd(1614, 1, 1)); -/// # } -/// ~~~~ +/// ``` impl Sub<OldDuration> for NaiveDate { type Output = NaiveDate; @@ -1533,8 +1768,7 @@ impl SubAssign<OldDuration> for NaiveDate { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveDate}; /// /// let from_ymd = NaiveDate::from_ymd; @@ -1546,8 +1780,7 @@ impl SubAssign<OldDuration> for NaiveDate { /// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 1, 1), Duration::days(365)); /// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2010, 1, 1), Duration::days(365*4 + 1)); /// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(1614, 1, 1), Duration::days(365*400 + 97)); -/// # } -/// ~~~~ +/// ``` impl Sub<NaiveDate> for NaiveDate { type Output = OldDuration; @@ -1567,24 +1800,35 @@ impl Iterator for NaiveDateDaysIterator { type Item = NaiveDate; fn next(&mut self) -> Option<Self::Item> { - if self.value == MAX_DATE { + if self.value == NaiveDate::MAX { return None; } - // current < MAX_DATE from here on: + // current < NaiveDate::MAX from here on: let current = self.value; - // This can't panic because current is < MAX_DATE: - self.value = current.succ(); + // This can't panic because current is < NaiveDate::MAX: + self.value = current.succ_opt().unwrap(); Some(current) } fn size_hint(&self) -> (usize, Option<usize>) { - let exact_size = MAX_DATE.signed_duration_since(self.value).num_days(); + let exact_size = NaiveDate::MAX.signed_duration_since(self.value).num_days(); (exact_size as usize, Some(exact_size as usize)) } } impl ExactSizeIterator for NaiveDateDaysIterator {} +impl DoubleEndedIterator for NaiveDateDaysIterator { + fn next_back(&mut self) -> Option<Self::Item> { + if self.value == NaiveDate::MIN { + return None; + } + let current = self.value; + self.value = current.pred_opt().unwrap(); + Some(current) + } +} + #[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] pub struct NaiveDateWeeksIterator { value: NaiveDate, @@ -1594,7 +1838,7 @@ impl Iterator for NaiveDateWeeksIterator { type Item = NaiveDate; fn next(&mut self) -> Option<Self::Item> { - if MAX_DATE - self.value < OldDuration::weeks(1) { + if NaiveDate::MAX - self.value < OldDuration::weeks(1) { return None; } let current = self.value; @@ -1603,13 +1847,24 @@ impl Iterator for NaiveDateWeeksIterator { } fn size_hint(&self) -> (usize, Option<usize>) { - let exact_size = MAX_DATE.signed_duration_since(self.value).num_weeks(); + let exact_size = NaiveDate::MAX.signed_duration_since(self.value).num_weeks(); (exact_size as usize, Some(exact_size as usize)) } } impl ExactSizeIterator for NaiveDateWeeksIterator {} +impl DoubleEndedIterator for NaiveDateWeeksIterator { + fn next_back(&mut self) -> Option<Self::Item> { + if self.value - NaiveDate::MIN < OldDuration::weeks(1) { + return None; + } + let current = self.value; + self.value = current - OldDuration::weeks(1); + Some(current) + } +} + // TODO: NaiveDateDaysIterator and NaiveDateWeeksIterator should implement FusedIterator, // TrustedLen, and Step once they becomes stable. // See: https://github.com/chronotope/chrono/issues/208 @@ -1621,31 +1876,39 @@ impl ExactSizeIterator for NaiveDateWeeksIterator {} /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveDate; /// -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5)), "2015-09-05"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 1)), "0000-01-01"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31)), "9999-12-31"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()), "2015-09-05"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt( 0, 1, 1).unwrap()), "0000-01-01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap()), "9999-12-31"); +/// ``` /// /// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. /// -/// ~~~~ +/// ``` /// # use chrono::NaiveDate; -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( -1, 1, 1)), "-0001-01-01"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31)), "+10000-12-31"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt( -1, 1, 1).unwrap()), "-0001-01-01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap()), "+10000-12-31"); +/// ``` impl fmt::Debug for NaiveDate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use core::fmt::Write; + let year = self.year(); let mdf = self.mdf(); - if 0 <= year && year <= 9999 { - write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day()) + if (0..=9999).contains(&year) { + write_hundreds(f, (year / 100) as u8)?; + write_hundreds(f, (year % 100) as u8)?; } else { // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day()) + write!(f, "{:+05}", year)?; } + + f.write_char('-')?; + write_hundreds(f, mdf.month() as u8)?; + f.write_char('-')?; + write_hundreds(f, mdf.day() as u8) } } @@ -1656,21 +1919,21 @@ impl fmt::Debug for NaiveDate { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveDate; /// -/// assert_eq!(format!("{}", NaiveDate::from_ymd(2015, 9, 5)), "2015-09-05"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd( 0, 1, 1)), "0000-01-01"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd(9999, 12, 31)), "9999-12-31"); -/// ~~~~ +/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()), "2015-09-05"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt( 0, 1, 1).unwrap()), "0000-01-01"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap()), "9999-12-31"); +/// ``` /// /// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. /// -/// ~~~~ +/// ``` /// # use chrono::NaiveDate; -/// assert_eq!(format!("{}", NaiveDate::from_ymd( -1, 1, 1)), "-0001-01-01"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd(10000, 12, 31)), "+10000-12-31"); -/// ~~~~ +/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt( -1, 1, 1).unwrap()), "-0001-01-01"); +/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap()), "+10000-12-31"); +/// ``` impl fmt::Display for NaiveDate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) @@ -1682,22 +1945,22 @@ impl fmt::Display for NaiveDate { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveDate; /// -/// let d = NaiveDate::from_ymd(2015, 9, 18); +/// let d = NaiveDate::from_ymd_opt(2015, 9, 18).unwrap(); /// assert_eq!("2015-09-18".parse::<NaiveDate>(), Ok(d)); /// -/// let d = NaiveDate::from_ymd(12345, 6, 7); +/// let d = NaiveDate::from_ymd_opt(12345, 6, 7).unwrap(); /// assert_eq!("+12345-6-7".parse::<NaiveDate>(), Ok(d)); /// /// assert!("foo".parse::<NaiveDate>().is_err()); -/// ~~~~ +/// ``` impl str::FromStr for NaiveDate { type Err = ParseError; fn from_str(s: &str) -> ParseResult<NaiveDate> { - const ITEMS: &'static [Item<'static>] = &[ + const ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Year, Pad::Zero), Item::Space(""), Item::Literal("-"), @@ -1714,17 +1977,42 @@ impl str::FromStr for NaiveDate { } } +/// The default value for a NaiveDate is 1st of January 1970. +/// +/// # Example +/// +/// ```rust +/// use chrono::NaiveDate; +/// +/// let default_date = NaiveDate::default(); +/// assert_eq!(default_date, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); +/// ``` +impl Default for NaiveDate { + fn default() -> Self { + NaiveDate::from_ymd_opt(1970, 1, 1).unwrap() + } +} + #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_encodable_json<F, E>(to_string: F) where F: Fn(&NaiveDate) -> Result<String, E>, E: ::std::fmt::Debug, { - assert_eq!(to_string(&NaiveDate::from_ymd(2014, 7, 24)).ok(), Some(r#""2014-07-24""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(0, 1, 1)).ok(), Some(r#""0000-01-01""#.into())); - assert_eq!(to_string(&NaiveDate::from_ymd(-1, 12, 31)).ok(), Some(r#""-0001-12-31""#.into())); - assert_eq!(to_string(&MIN_DATE).ok(), Some(r#""-262144-01-01""#.into())); - assert_eq!(to_string(&MAX_DATE).ok(), Some(r#""+262143-12-31""#.into())); + assert_eq!( + to_string(&NaiveDate::from_ymd_opt(2014, 7, 24).unwrap()).ok(), + Some(r#""2014-07-24""#.into()) + ); + assert_eq!( + to_string(&NaiveDate::from_ymd_opt(0, 1, 1).unwrap()).ok(), + Some(r#""0000-01-01""#.into()) + ); + assert_eq!( + to_string(&NaiveDate::from_ymd_opt(-1, 12, 31).unwrap()).ok(), + Some(r#""-0001-12-31""#.into()) + ); + assert_eq!(to_string(&NaiveDate::MIN).ok(), Some(r#""-262144-01-01""#.into())); + assert_eq!(to_string(&NaiveDate::MAX).ok(), Some(r#""+262143-12-31""#.into())); } #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] @@ -1735,14 +2023,20 @@ where { use std::{i32, i64}; - assert_eq!(from_str(r#""2016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""+002016-07-08""#).ok(), Some(NaiveDate::from_ymd(2016, 7, 8))); - assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_str(r#""-0001-12-31""#).ok(), Some(NaiveDate::from_ymd(-1, 12, 31))); - assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(MIN_DATE)); - assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(MAX_DATE)); + assert_eq!( + from_str(r#""2016-07-08""#).ok(), + Some(NaiveDate::from_ymd_opt(2016, 7, 8).unwrap()) + ); + assert_eq!(from_str(r#""2016-7-8""#).ok(), Some(NaiveDate::from_ymd_opt(2016, 7, 8).unwrap())); + assert_eq!(from_str(r#""+002016-07-08""#).ok(), NaiveDate::from_ymd_opt(2016, 7, 8)); + assert_eq!(from_str(r#""0000-01-01""#).ok(), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap())); + assert_eq!(from_str(r#""0-1-1""#).ok(), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap())); + assert_eq!( + from_str(r#""-0001-12-31""#).ok(), + Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap()) + ); + assert_eq!(from_str(r#""-262144-01-01""#).ok(), Some(NaiveDate::MIN)); + assert_eq!(from_str(r#""+262143-12-31""#).ok(), Some(NaiveDate::MAX)); // bad formats assert!(from_str(r#""""#).is_err()); @@ -1765,6 +2059,7 @@ where } #[cfg(feature = "rustc-serialize")] +#[cfg_attr(docsrs, doc(cfg(feature = "rustc-serialize")))] mod rustc_serialize { use super::NaiveDate; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -1796,10 +2091,11 @@ mod rustc_serialize { } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] mod serde { use super::NaiveDate; use core::fmt; - use serdelib::{de, ser}; + use serde::{de, ser}; // TODO not very optimized for space (binary formats would want something better) @@ -1828,11 +2124,11 @@ mod serde { type Value = NaiveDate; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date string") + formatter.write_str("a formatted date string") } #[cfg(any(feature = "std", test))] - fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E> + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error, { @@ -1840,7 +2136,7 @@ mod serde { } #[cfg(not(any(feature = "std", test)))] - fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E> + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error, { @@ -1857,29 +2153,24 @@ mod serde { } } - #[cfg(test)] - extern crate bincode; - #[cfg(test)] - extern crate serde_json; - #[test] fn test_serde_serialize() { - super::test_encodable_json(self::serde_json::to_string); + super::test_encodable_json(serde_json::to_string); } #[test] fn test_serde_deserialize() { - super::test_decodable_json(|input| self::serde_json::from_str(&input)); + super::test_decodable_json(|input| serde_json::from_str(input)); } #[test] fn test_serde_bincode() { // Bincode is relevant to test separately from JSON because // it is not self-describing. - use self::bincode::{deserialize, serialize, Infinite}; + use bincode::{deserialize, serialize}; - let d = NaiveDate::from_ymd(2014, 7, 24); - let encoded = serialize(&d, Infinite).unwrap(); + let d = NaiveDate::from_ymd_opt(2014, 7, 24).unwrap(); + let encoded = serialize(&d).unwrap(); let decoded: NaiveDate = deserialize(&encoded).unwrap(); assert_eq!(d, decoded); } @@ -1887,16 +2178,131 @@ mod serde { #[cfg(test)] mod tests { - use super::NaiveDate; - use super::{MAX_DATE, MAX_DAYS_FROM_YEAR_0, MAX_YEAR}; - use super::{MIN_DATE, MIN_DAYS_FROM_YEAR_0, MIN_YEAR}; - use oldtime::Duration; - use std::{i32, u32}; - use {Datelike, Weekday}; + use super::{ + Days, Months, NaiveDate, MAX_DAYS_FROM_YEAR_0, MAX_YEAR, MIN_DAYS_FROM_YEAR_0, MIN_YEAR, + }; + use crate::oldtime::Duration; + use crate::{Datelike, Weekday}; + use std::{ + convert::{TryFrom, TryInto}, + i32, u32, + }; + + #[test] + fn diff_months() { + // identity + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(0)), + Some(NaiveDate::from_ymd_opt(2022, 8, 3).unwrap()) + ); + + // add with months exceeding `i32::MAX` + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3) + .unwrap() + .checked_add_months(Months::new(i32::MAX as u32 + 1)), + None + ); + + // sub with months exceeding `i32::MIN` + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3) + .unwrap() + .checked_sub_months(Months::new((i32::MIN as i64).abs() as u32 + 1)), + None + ); + + // add overflowing year + assert_eq!(NaiveDate::MAX.checked_add_months(Months::new(1)), None); + + // add underflowing year + assert_eq!(NaiveDate::MIN.checked_sub_months(Months::new(1)), None); + + // sub crossing year 0 boundary + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(2050 * 12)), + Some(NaiveDate::from_ymd_opt(-28, 8, 3).unwrap()) + ); + + // add crossing year boundary + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(6)), + Some(NaiveDate::from_ymd_opt(2023, 2, 3).unwrap()) + ); + + // sub crossing year boundary + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(10)), + Some(NaiveDate::from_ymd_opt(2021, 10, 3).unwrap()) + ); + + // add clamping day, non-leap year + assert_eq!( + NaiveDate::from_ymd_opt(2022, 1, 29).unwrap().checked_add_months(Months::new(1)), + Some(NaiveDate::from_ymd_opt(2022, 2, 28).unwrap()) + ); + + // add to leap day + assert_eq!( + NaiveDate::from_ymd_opt(2022, 10, 29).unwrap().checked_add_months(Months::new(16)), + Some(NaiveDate::from_ymd_opt(2024, 2, 29).unwrap()) + ); + + // add into december + assert_eq!( + NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_add_months(Months::new(2)), + Some(NaiveDate::from_ymd_opt(2022, 12, 31).unwrap()) + ); + + // sub into december + assert_eq!( + NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_sub_months(Months::new(10)), + Some(NaiveDate::from_ymd_opt(2021, 12, 31).unwrap()) + ); + + // add into january + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(5)), + Some(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap()) + ); + + // sub into january + assert_eq!( + NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(7)), + Some(NaiveDate::from_ymd_opt(2022, 1, 3).unwrap()) + ); + } + + #[test] + fn test_readme_doomsday() { + use num_iter::range_inclusive; + + for y in range_inclusive(NaiveDate::MIN.year(), NaiveDate::MAX.year()) { + // even months + let d4 = NaiveDate::from_ymd_opt(y, 4, 4).unwrap(); + let d6 = NaiveDate::from_ymd_opt(y, 6, 6).unwrap(); + let d8 = NaiveDate::from_ymd_opt(y, 8, 8).unwrap(); + let d10 = NaiveDate::from_ymd_opt(y, 10, 10).unwrap(); + let d12 = NaiveDate::from_ymd_opt(y, 12, 12).unwrap(); + + // nine to five, seven-eleven + let d59 = NaiveDate::from_ymd_opt(y, 5, 9).unwrap(); + let d95 = NaiveDate::from_ymd_opt(y, 9, 5).unwrap(); + let d711 = NaiveDate::from_ymd_opt(y, 7, 11).unwrap(); + let d117 = NaiveDate::from_ymd_opt(y, 11, 7).unwrap(); + + // "March 0" + let d30 = NaiveDate::from_ymd_opt(y, 3, 1).unwrap().pred_opt().unwrap(); + + let weekday = d30.weekday(); + let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117]; + assert!(other_dates.iter().all(|d| d.weekday() == weekday)); + } + } #[test] fn test_date_from_ymd() { - let ymd_opt = |y, m, d| NaiveDate::from_ymd_opt(y, m, d); + let ymd_opt = NaiveDate::from_ymd_opt; assert!(ymd_opt(2012, 0, 1).is_none()); assert!(ymd_opt(2012, 1, 1).is_some()); @@ -1912,8 +2318,8 @@ mod tests { #[test] fn test_date_from_yo() { - let yo_opt = |y, o| NaiveDate::from_yo_opt(y, o); - let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + let yo_opt = NaiveDate::from_yo_opt; + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); assert_eq!(yo_opt(2012, 0), None); assert_eq!(yo_opt(2012, 1), Some(ymd(2012, 1, 1))); @@ -1942,8 +2348,8 @@ mod tests { #[test] fn test_date_from_isoywd() { - let isoywd_opt = |y, w, d| NaiveDate::from_isoywd_opt(y, w, d); - let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + let isoywd_opt = NaiveDate::from_isoywd_opt; + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); assert_eq!(isoywd_opt(2004, 0, Weekday::Sun), None); assert_eq!(isoywd_opt(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29))); @@ -1985,8 +2391,7 @@ mod tests { .iter() { let d = NaiveDate::from_isoywd_opt(year, week, weekday); - if d.is_some() { - let d = d.unwrap(); + if let Some(d) = d { assert_eq!(d.weekday(), weekday); let w = d.iso_week(); assert_eq!(w.year(), year); @@ -2000,11 +2405,10 @@ mod tests { for month in 1..13 { for day in 1..32 { let d = NaiveDate::from_ymd_opt(year, month, day); - if d.is_some() { - let d = d.unwrap(); + if let Some(d) = d { let w = d.iso_week(); - let d_ = NaiveDate::from_isoywd(w.year(), w.week(), d.weekday()); - assert_eq!(d, d_); + let d_ = NaiveDate::from_isoywd_opt(w.year(), w.week(), d.weekday()); + assert_eq!(d, d_.unwrap()); } } } @@ -2013,63 +2417,111 @@ mod tests { #[test] fn test_date_from_num_days_from_ce() { - let from_ndays_from_ce = |days| NaiveDate::from_num_days_from_ce_opt(days); - assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd(1, 1, 1))); - assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd(1, 1, 2))); - assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd(1, 1, 31))); - assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd(1, 2, 1))); - assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd(1, 2, 28))); - assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd(1, 3, 1))); - assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd(1, 12, 31))); - assert_eq!(from_ndays_from_ce(365 * 1 + 1), Some(NaiveDate::from_ymd(2, 1, 1))); - assert_eq!(from_ndays_from_ce(365 * 2 + 1), Some(NaiveDate::from_ymd(3, 1, 1))); - assert_eq!(from_ndays_from_ce(365 * 3 + 1), Some(NaiveDate::from_ymd(4, 1, 1))); - assert_eq!(from_ndays_from_ce(365 * 4 + 2), Some(NaiveDate::from_ymd(5, 1, 1))); - assert_eq!(from_ndays_from_ce(146097 + 1), Some(NaiveDate::from_ymd(401, 1, 1))); - assert_eq!(from_ndays_from_ce(146097 * 5 + 1), Some(NaiveDate::from_ymd(2001, 1, 1))); - assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd(1970, 1, 1))); - assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd(0, 12, 31))); // 1 BCE - assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd(0, 1, 1))); - assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd(-1, 12, 31))); // 2 BCE + let from_ndays_from_ce = NaiveDate::from_num_days_from_ce_opt; + assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap())); + assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd_opt(1, 1, 2).unwrap())); + assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd_opt(1, 1, 31).unwrap())); + assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd_opt(1, 2, 1).unwrap())); + assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd_opt(1, 2, 28).unwrap())); + assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd_opt(1, 3, 1).unwrap())); + assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd_opt(1, 12, 31).unwrap())); + assert_eq!(from_ndays_from_ce(365 + 1), Some(NaiveDate::from_ymd_opt(2, 1, 1).unwrap())); + assert_eq!( + from_ndays_from_ce(365 * 2 + 1), + Some(NaiveDate::from_ymd_opt(3, 1, 1).unwrap()) + ); + assert_eq!( + from_ndays_from_ce(365 * 3 + 1), + Some(NaiveDate::from_ymd_opt(4, 1, 1).unwrap()) + ); + assert_eq!( + from_ndays_from_ce(365 * 4 + 2), + Some(NaiveDate::from_ymd_opt(5, 1, 1).unwrap()) + ); + assert_eq!( + from_ndays_from_ce(146097 + 1), + Some(NaiveDate::from_ymd_opt(401, 1, 1).unwrap()) + ); + assert_eq!( + from_ndays_from_ce(146097 * 5 + 1), + Some(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap()) + ); + assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap())); + assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd_opt(0, 12, 31).unwrap())); // 1 BCE + assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap())); + assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap())); // 2 BCE for days in (-9999..10001).map(|x| x * 100) { assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days)); } - assert_eq!(from_ndays_from_ce(MIN_DATE.num_days_from_ce()), Some(MIN_DATE)); - assert_eq!(from_ndays_from_ce(MIN_DATE.num_days_from_ce() - 1), None); - assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce()), Some(MAX_DATE)); - assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce() + 1), None); + assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce()), Some(NaiveDate::MIN)); + assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce() - 1), None); + assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce()), Some(NaiveDate::MAX)); + assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce() + 1), None); } #[test] fn test_date_from_weekday_of_month_opt() { - let ymwd = |y, m, w, n| NaiveDate::from_weekday_of_month_opt(y, m, w, n); + let ymwd = NaiveDate::from_weekday_of_month_opt; assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None); - assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd(2018, 8, 1))); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd(2018, 8, 2))); - assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd(2018, 8, 5))); - assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd(2018, 8, 6))); - assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd(2018, 8, 7))); - assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd(2018, 8, 8))); - assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd(2018, 8, 12))); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd(2018, 8, 16))); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd(2018, 8, 23))); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd(2018, 8, 30))); - assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd(2018, 8, 31))); + assert_eq!( + ymwd(2018, 8, Weekday::Wed, 1), + Some(NaiveDate::from_ymd_opt(2018, 8, 1).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Thu, 1), + Some(NaiveDate::from_ymd_opt(2018, 8, 2).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Sun, 1), + Some(NaiveDate::from_ymd_opt(2018, 8, 5).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Mon, 1), + Some(NaiveDate::from_ymd_opt(2018, 8, 6).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Tue, 1), + Some(NaiveDate::from_ymd_opt(2018, 8, 7).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Wed, 2), + Some(NaiveDate::from_ymd_opt(2018, 8, 8).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Sun, 2), + Some(NaiveDate::from_ymd_opt(2018, 8, 12).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Thu, 3), + Some(NaiveDate::from_ymd_opt(2018, 8, 16).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Thu, 4), + Some(NaiveDate::from_ymd_opt(2018, 8, 23).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Thu, 5), + Some(NaiveDate::from_ymd_opt(2018, 8, 30).unwrap()) + ); + assert_eq!( + ymwd(2018, 8, Weekday::Fri, 5), + Some(NaiveDate::from_ymd_opt(2018, 8, 31).unwrap()) + ); assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None); } #[test] fn test_date_fields() { fn check(year: i32, month: u32, day: u32, ordinal: u32) { - let d1 = NaiveDate::from_ymd(year, month, day); + let d1 = NaiveDate::from_ymd_opt(year, month, day).unwrap(); assert_eq!(d1.year(), year); assert_eq!(d1.month(), month); assert_eq!(d1.day(), day); assert_eq!(d1.ordinal(), ordinal); - let d2 = NaiveDate::from_yo(year, ordinal); + let d2 = NaiveDate::from_yo_opt(year, ordinal).unwrap(); assert_eq!(d2.year(), year); assert_eq!(d2.month(), month); assert_eq!(d2.day(), day); @@ -2101,88 +2553,88 @@ mod tests { #[test] fn test_date_weekday() { - assert_eq!(NaiveDate::from_ymd(1582, 10, 15).weekday(), Weekday::Fri); + assert_eq!(NaiveDate::from_ymd_opt(1582, 10, 15).unwrap().weekday(), Weekday::Fri); // May 20, 1875 = ISO 8601 reference date - assert_eq!(NaiveDate::from_ymd(1875, 5, 20).weekday(), Weekday::Thu); - assert_eq!(NaiveDate::from_ymd(2000, 1, 1).weekday(), Weekday::Sat); + assert_eq!(NaiveDate::from_ymd_opt(1875, 5, 20).unwrap().weekday(), Weekday::Thu); + assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().weekday(), Weekday::Sat); } #[test] fn test_date_with_fields() { - let d = NaiveDate::from_ymd(2000, 2, 29); - assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd(-400, 2, 29))); + let d = NaiveDate::from_ymd_opt(2000, 2, 29).unwrap(); + assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd_opt(-400, 2, 29).unwrap())); assert_eq!(d.with_year(-100), None); - assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd(1600, 2, 29))); + assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd_opt(1600, 2, 29).unwrap())); assert_eq!(d.with_year(1900), None); - assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd(2000, 2, 29))); + assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); assert_eq!(d.with_year(2001), None); - assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd(2004, 2, 29))); + assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd_opt(2004, 2, 29).unwrap())); assert_eq!(d.with_year(i32::MAX), None); - let d = NaiveDate::from_ymd(2000, 4, 30); + let d = NaiveDate::from_ymd_opt(2000, 4, 30).unwrap(); assert_eq!(d.with_month(0), None); - assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd(2000, 1, 30))); + assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd_opt(2000, 1, 30).unwrap())); assert_eq!(d.with_month(2), None); - assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd(2000, 3, 30))); - assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd(2000, 4, 30))); - assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd(2000, 12, 30))); + assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd_opt(2000, 3, 30).unwrap())); + assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd_opt(2000, 4, 30).unwrap())); + assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd_opt(2000, 12, 30).unwrap())); assert_eq!(d.with_month(13), None); assert_eq!(d.with_month(u32::MAX), None); - let d = NaiveDate::from_ymd(2000, 2, 8); + let d = NaiveDate::from_ymd_opt(2000, 2, 8).unwrap(); assert_eq!(d.with_day(0), None); - assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd(2000, 2, 1))); - assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd(2000, 2, 29))); + assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd_opt(2000, 2, 1).unwrap())); + assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); assert_eq!(d.with_day(30), None); assert_eq!(d.with_day(u32::MAX), None); - let d = NaiveDate::from_ymd(2000, 5, 5); + let d = NaiveDate::from_ymd_opt(2000, 5, 5).unwrap(); assert_eq!(d.with_ordinal(0), None); - assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd(2000, 1, 1))); - assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd(2000, 2, 29))); - assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd(2000, 3, 1))); - assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd(2000, 12, 31))); + assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap())); + assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); + assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd_opt(2000, 3, 1).unwrap())); + assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd_opt(2000, 12, 31).unwrap())); assert_eq!(d.with_ordinal(367), None); assert_eq!(d.with_ordinal(u32::MAX), None); } #[test] fn test_date_num_days_from_ce() { - assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1); + assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1); for year in -9999..10001 { assert_eq!( - NaiveDate::from_ymd(year, 1, 1).num_days_from_ce(), - NaiveDate::from_ymd(year - 1, 12, 31).num_days_from_ce() + 1 + NaiveDate::from_ymd_opt(year, 1, 1).unwrap().num_days_from_ce(), + NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().num_days_from_ce() + 1 ); } } #[test] fn test_date_succ() { - let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); assert_eq!(ymd(2014, 5, 6).succ_opt(), Some(ymd(2014, 5, 7))); assert_eq!(ymd(2014, 5, 31).succ_opt(), Some(ymd(2014, 6, 1))); assert_eq!(ymd(2014, 12, 31).succ_opt(), Some(ymd(2015, 1, 1))); assert_eq!(ymd(2016, 2, 28).succ_opt(), Some(ymd(2016, 2, 29))); - assert_eq!(ymd(MAX_DATE.year(), 12, 31).succ_opt(), None); + assert_eq!(ymd(NaiveDate::MAX.year(), 12, 31).succ_opt(), None); } #[test] fn test_date_pred() { - let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); assert_eq!(ymd(2016, 3, 1).pred_opt(), Some(ymd(2016, 2, 29))); assert_eq!(ymd(2015, 1, 1).pred_opt(), Some(ymd(2014, 12, 31))); assert_eq!(ymd(2014, 6, 1).pred_opt(), Some(ymd(2014, 5, 31))); assert_eq!(ymd(2014, 5, 7).pred_opt(), Some(ymd(2014, 5, 6))); - assert_eq!(ymd(MIN_DATE.year(), 1, 1).pred_opt(), None); + assert_eq!(ymd(NaiveDate::MIN.year(), 1, 1).pred_opt(), None); } #[test] fn test_date_add() { fn check((y1, m1, d1): (i32, u32, u32), rhs: Duration, ymd: Option<(i32, u32, u32)>) { - let lhs = NaiveDate::from_ymd(y1, m1, d1); - let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd(y, m, d)); + let lhs = NaiveDate::from_ymd_opt(y1, m1, d1).unwrap(); + let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd_opt(y, m, d).unwrap()); assert_eq!(lhs.checked_add_signed(rhs), sum); assert_eq!(lhs.checked_sub_signed(-rhs), sum); } @@ -2211,8 +2663,8 @@ mod tests { #[test] fn test_date_sub() { fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: Duration) { - let lhs = NaiveDate::from_ymd(y1, m1, d1); - let rhs = NaiveDate::from_ymd(y2, m2, d2); + let lhs = NaiveDate::from_ymd_opt(y1, m1, d1).unwrap(); + let rhs = NaiveDate::from_ymd_opt(y2, m2, d2).unwrap(); assert_eq!(lhs.signed_duration_since(rhs), diff); assert_eq!(rhs.signed_duration_since(lhs), -diff); } @@ -2229,8 +2681,53 @@ mod tests { } #[test] + fn test_date_add_days() { + fn check((y1, m1, d1): (i32, u32, u32), rhs: Days, ymd: Option<(i32, u32, u32)>) { + let lhs = NaiveDate::from_ymd_opt(y1, m1, d1).unwrap(); + let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd_opt(y, m, d).unwrap()); + assert_eq!(lhs.checked_add_days(rhs), sum); + } + + check((2014, 1, 1), Days::new(0), Some((2014, 1, 1))); + // always round towards zero + check((2014, 1, 1), Days::new(1), Some((2014, 1, 2))); + check((2014, 1, 1), Days::new(364), Some((2014, 12, 31))); + check((2014, 1, 1), Days::new(365 * 4 + 1), Some((2018, 1, 1))); + check((2014, 1, 1), Days::new(365 * 400 + 97), Some((2414, 1, 1))); + + check((-7, 1, 1), Days::new(365 * 12 + 3), Some((5, 1, 1))); + + // overflow check + check( + (0, 1, 1), + Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), + Some((MAX_YEAR, 12, 31)), + ); + check((0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None); + } + + #[test] + fn test_date_sub_days() { + fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: Days) { + let lhs = NaiveDate::from_ymd_opt(y1, m1, d1).unwrap(); + let rhs = NaiveDate::from_ymd_opt(y2, m2, d2).unwrap(); + assert_eq!(lhs - diff, rhs); + } + + check((2014, 1, 1), (2014, 1, 1), Days::new(0)); + check((2014, 1, 2), (2014, 1, 1), Days::new(1)); + check((2014, 12, 31), (2014, 1, 1), Days::new(364)); + check((2015, 1, 3), (2014, 1, 1), Days::new(365 + 2)); + check((2018, 1, 1), (2014, 1, 1), Days::new(365 * 4 + 1)); + check((2414, 1, 1), (2014, 1, 1), Days::new(365 * 400 + 97)); + + check((MAX_YEAR, 12, 31), (0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap())); + check((0, 1, 1), (MIN_YEAR, 1, 1), Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap())); + } + + #[test] fn test_date_addassignment() { - let ymd = NaiveDate::from_ymd; + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); let mut date = ymd(2016, 10, 1); date += Duration::days(10); assert_eq!(date, ymd(2016, 10, 11)); @@ -2240,7 +2737,7 @@ mod tests { #[test] fn test_date_subassignment() { - let ymd = NaiveDate::from_ymd; + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); let mut date = ymd(2016, 10, 11); date -= Duration::days(10); assert_eq!(date, ymd(2016, 10, 1)); @@ -2250,19 +2747,22 @@ mod tests { #[test] fn test_date_fmt() { - assert_eq!(format!("{:?}", NaiveDate::from_ymd(2012, 3, 4)), "2012-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd(0, 3, 4)), "0000-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd(-307, 3, 4)), "-0307-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd(12345, 3, 4)), "+12345-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2012, 3, 4).unwrap()), "2012-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 3, 4).unwrap()), "0000-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(-307, 3, 4).unwrap()), "-0307-03-04"); + assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(12345, 3, 4).unwrap()), "+12345-03-04"); - assert_eq!(NaiveDate::from_ymd(2012, 3, 4).to_string(), "2012-03-04"); - assert_eq!(NaiveDate::from_ymd(0, 3, 4).to_string(), "0000-03-04"); - assert_eq!(NaiveDate::from_ymd(-307, 3, 4).to_string(), "-0307-03-04"); - assert_eq!(NaiveDate::from_ymd(12345, 3, 4).to_string(), "+12345-03-04"); + assert_eq!(NaiveDate::from_ymd_opt(2012, 3, 4).unwrap().to_string(), "2012-03-04"); + assert_eq!(NaiveDate::from_ymd_opt(0, 3, 4).unwrap().to_string(), "0000-03-04"); + assert_eq!(NaiveDate::from_ymd_opt(-307, 3, 4).unwrap().to_string(), "-0307-03-04"); + assert_eq!(NaiveDate::from_ymd_opt(12345, 3, 4).unwrap().to_string(), "+12345-03-04"); // the format specifier should have no effect on `NaiveTime` - assert_eq!(format!("{:+30?}", NaiveDate::from_ymd(1234, 5, 6)), "1234-05-06"); - assert_eq!(format!("{:30?}", NaiveDate::from_ymd(12345, 6, 7)), "+12345-06-07"); + assert_eq!(format!("{:+30?}", NaiveDate::from_ymd_opt(1234, 5, 6).unwrap()), "1234-05-06"); + assert_eq!( + format!("{:30?}", NaiveDate::from_ymd_opt(12345, 6, 7).unwrap()), + "+12345-06-07" + ); } #[test] @@ -2318,7 +2818,7 @@ mod tests { #[test] fn test_date_parse_from_str() { - let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d); + let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); assert_eq!( NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), Ok(ymd(2014, 5, 7)) @@ -2334,11 +2834,21 @@ mod tests { assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err()); assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient + + assert_eq!( + NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(), + NaiveDate::from_ymd_opt(2020, 1, 12), + ); + + assert_eq!( + NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(), + NaiveDate::from_ymd_opt(2019, 1, 13), + ); } #[test] fn test_date_format() { - let d = NaiveDate::from_ymd(2012, 3, 4); + let d = NaiveDate::from_ymd_opt(2012, 3, 4).unwrap(); assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12"); assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March"); assert_eq!(d.format("%d,%e").to_string(), "04, 4"); @@ -2351,33 +2861,40 @@ mod tests { assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); // non-four-digit years - assert_eq!(NaiveDate::from_ymd(12345, 1, 1).format("%Y").to_string(), "+12345"); - assert_eq!(NaiveDate::from_ymd(1234, 1, 1).format("%Y").to_string(), "1234"); - assert_eq!(NaiveDate::from_ymd(123, 1, 1).format("%Y").to_string(), "0123"); - assert_eq!(NaiveDate::from_ymd(12, 1, 1).format("%Y").to_string(), "0012"); - assert_eq!(NaiveDate::from_ymd(1, 1, 1).format("%Y").to_string(), "0001"); - assert_eq!(NaiveDate::from_ymd(0, 1, 1).format("%Y").to_string(), "0000"); - assert_eq!(NaiveDate::from_ymd(-1, 1, 1).format("%Y").to_string(), "-0001"); - assert_eq!(NaiveDate::from_ymd(-12, 1, 1).format("%Y").to_string(), "-0012"); - assert_eq!(NaiveDate::from_ymd(-123, 1, 1).format("%Y").to_string(), "-0123"); - assert_eq!(NaiveDate::from_ymd(-1234, 1, 1).format("%Y").to_string(), "-1234"); - assert_eq!(NaiveDate::from_ymd(-12345, 1, 1).format("%Y").to_string(), "-12345"); + assert_eq!( + NaiveDate::from_ymd_opt(12345, 1, 1).unwrap().format("%Y").to_string(), + "+12345" + ); + assert_eq!(NaiveDate::from_ymd_opt(1234, 1, 1).unwrap().format("%Y").to_string(), "1234"); + assert_eq!(NaiveDate::from_ymd_opt(123, 1, 1).unwrap().format("%Y").to_string(), "0123"); + assert_eq!(NaiveDate::from_ymd_opt(12, 1, 1).unwrap().format("%Y").to_string(), "0012"); + assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().format("%Y").to_string(), "0001"); + assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().format("%Y").to_string(), "0000"); + assert_eq!(NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().format("%Y").to_string(), "-0001"); + assert_eq!(NaiveDate::from_ymd_opt(-12, 1, 1).unwrap().format("%Y").to_string(), "-0012"); + assert_eq!(NaiveDate::from_ymd_opt(-123, 1, 1).unwrap().format("%Y").to_string(), "-0123"); + assert_eq!(NaiveDate::from_ymd_opt(-1234, 1, 1).unwrap().format("%Y").to_string(), "-1234"); + assert_eq!( + NaiveDate::from_ymd_opt(-12345, 1, 1).unwrap().format("%Y").to_string(), + "-12345" + ); // corner cases assert_eq!( - NaiveDate::from_ymd(2007, 12, 31).format("%G,%g,%U,%W,%V").to_string(), - "2008,08,53,53,01" + NaiveDate::from_ymd_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(), + "2008,08,52,53,01" ); assert_eq!( - NaiveDate::from_ymd(2010, 1, 3).format("%G,%g,%U,%W,%V").to_string(), + NaiveDate::from_ymd_opt(2010, 1, 3).unwrap().format("%G,%g,%U,%W,%V").to_string(), "2009,09,01,00,53" ); } #[test] fn test_day_iterator_limit() { + assert_eq!(NaiveDate::from_ymd_opt(262143, 12, 29).unwrap().iter_days().take(4).count(), 2); assert_eq!( - NaiveDate::from_ymd(262143, 12, 29).iter_days().take(4).collect::<Vec<_>>().len(), + NaiveDate::from_ymd_opt(-262144, 1, 3).unwrap().iter_days().rev().take(4).count(), 2 ); } @@ -2385,8 +2902,96 @@ mod tests { #[test] fn test_week_iterator_limit() { assert_eq!( - NaiveDate::from_ymd(262143, 12, 12).iter_weeks().take(4).collect::<Vec<_>>().len(), + NaiveDate::from_ymd_opt(262143, 12, 12).unwrap().iter_weeks().take(4).count(), + 2 + ); + assert_eq!( + NaiveDate::from_ymd_opt(-262144, 1, 15).unwrap().iter_weeks().rev().take(4).count(), 2 ); } + + #[test] + fn test_naiveweek() { + let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap(); + let asserts = vec![ + (Weekday::Mon, "2022-05-16", "2022-05-22"), + (Weekday::Tue, "2022-05-17", "2022-05-23"), + (Weekday::Wed, "2022-05-18", "2022-05-24"), + (Weekday::Thu, "2022-05-12", "2022-05-18"), + (Weekday::Fri, "2022-05-13", "2022-05-19"), + (Weekday::Sat, "2022-05-14", "2022-05-20"), + (Weekday::Sun, "2022-05-15", "2022-05-21"), + ]; + for (start, first_day, last_day) in asserts { + let week = date.week(start); + let days = week.days(); + assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%Y-%m-%d")); + assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%Y-%m-%d")); + assert!(days.contains(&date)); + } + } + + #[test] + fn test_weeks_from() { + // tests per: https://github.com/chronotope/chrono/issues/961 + // these internally use `weeks_from` via the parsing infrastructure + assert_eq!( + NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(), + NaiveDate::from_ymd_opt(2020, 1, 12), + ); + assert_eq!( + NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(), + NaiveDate::from_ymd_opt(2019, 1, 13), + ); + + // direct tests + for (y, starts_on) in &[ + (2019, Weekday::Tue), + (2020, Weekday::Wed), + (2021, Weekday::Fri), + (2022, Weekday::Sat), + (2023, Weekday::Sun), + (2024, Weekday::Mon), + (2025, Weekday::Wed), + (2026, Weekday::Thu), + ] { + for day in &[ + Weekday::Mon, + Weekday::Tue, + Weekday::Wed, + Weekday::Thu, + Weekday::Fri, + Weekday::Sat, + Weekday::Sun, + ] { + assert_eq!( + NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)), + Some(if day == starts_on { 1 } else { 0 }) + ); + + // last day must always be in week 52 or 53 + assert!([52, 53] + .contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)),); + } + } + + let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap(); + + // 400 years covers all year types + for day in &[ + Weekday::Mon, + Weekday::Tue, + Weekday::Wed, + Weekday::Thu, + Weekday::Fri, + Weekday::Sat, + Weekday::Sun, + ] { + // must always be below 54 + for dplus in 1..(400 * 366) { + assert!((base + Days::new(dplus)).weeks_from(*day) < 54) + } + } + } } diff --git a/vendor/chrono/src/naive/datetime.rs b/vendor/chrono/src/naive/datetime.rs deleted file mode 100644 index 92d6c2855..000000000 --- a/vendor/chrono/src/naive/datetime.rs +++ /dev/null @@ -1,2507 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 date and time without timezone. - -#[cfg(any(feature = "alloc", feature = "std", test))] -use core::borrow::Borrow; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::{fmt, hash, str}; -use num_traits::ToPrimitive; -use oldtime::Duration as OldDuration; - -use div::div_mod_floor; -#[cfg(any(feature = "alloc", feature = "std", test))] -use format::DelayedFormat; -use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; -use format::{Fixed, Item, Numeric, Pad}; -use naive::date::{MAX_DATE, MIN_DATE}; -use naive::time::{MAX_TIME, MIN_TIME}; -use naive::{IsoWeek, NaiveDate, NaiveTime}; -use {Datelike, Timelike, Weekday}; - -/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS` -/// will always overflow the addition with any date and time type. -/// -/// So why is this needed? `Duration::seconds(rhs)` may overflow, and we don't have -/// an alternative returning `Option` or `Result`. Thus we need some early bound to avoid -/// touching that call when we are already sure that it WILL overflow... -const MAX_SECS_BITS: usize = 44; - -/// The minimum possible `NaiveDateTime`. -pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime { date: MIN_DATE, time: MIN_TIME }; -/// The maximum possible `NaiveDateTime`. -pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime { date: MAX_DATE, time: MAX_TIME }; - -/// ISO 8601 combined date and time without timezone. -/// -/// # Example -/// -/// `NaiveDateTime` is commonly created from [`NaiveDate`](./struct.NaiveDate.html). -/// -/// ~~~~ -/// use chrono::{NaiveDate, NaiveDateTime}; -/// -/// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); -/// # let _ = dt; -/// ~~~~ -/// -/// You can use typical [date-like](../trait.Datelike.html) and -/// [time-like](../trait.Timelike.html) methods, -/// provided that relevant traits are in the scope. -/// -/// ~~~~ -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); -/// use chrono::{Datelike, Timelike, Weekday}; -/// -/// assert_eq!(dt.weekday(), Weekday::Fri); -/// assert_eq!(dt.num_seconds_from_midnight(), 33011); -/// ~~~~ -#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] -pub struct NaiveDateTime { - date: NaiveDate, - time: NaiveTime, -} - -impl NaiveDateTime { - /// Makes a new `NaiveDateTime` from date and time components. - /// Equivalent to [`date.and_time(time)`](./struct.NaiveDate.html#method.and_time) - /// and many other helper constructors on `NaiveDate`. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime}; - /// - /// let d = NaiveDate::from_ymd(2015, 6, 3); - /// let t = NaiveTime::from_hms_milli(12, 34, 56, 789); - /// - /// let dt = NaiveDateTime::new(d, t); - /// assert_eq!(dt.date(), d); - /// assert_eq!(dt.time(), t); - /// ~~~~ - #[inline] - pub fn new(date: NaiveDate, time: NaiveTime) -> NaiveDateTime { - NaiveDateTime { date: date, time: time } - } - - /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, - /// from the number of non-leap seconds - /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// For a non-naive version of this function see - /// [`TimeZone::timestamp`](../offset/trait.TimeZone.html#method.timestamp). - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent the - /// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX - /// timestamp" cannot represent a leap second unambiguously.) - /// - /// Panics on the out-of-range number of seconds and/or invalid nanosecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDateTime, NaiveDate}; - /// - /// let dt = NaiveDateTime::from_timestamp(0, 42_000_000); - /// assert_eq!(dt, NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 0, 42)); - /// - /// let dt = NaiveDateTime::from_timestamp(1_000_000_000, 0); - /// assert_eq!(dt, NaiveDate::from_ymd(2001, 9, 9).and_hms(1, 46, 40)); - /// ~~~~ - #[inline] - pub fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { - let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs); - datetime.expect("invalid or out-of-range datetime") - } - - /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, - /// from the number of non-leap seconds - /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// The nanosecond part can exceed 1,000,000,000 - /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// Returns `None` on the out-of-range number of seconds and/or invalid nanosecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDateTime, NaiveDate}; - /// use std::i64; - /// - /// let from_timestamp_opt = NaiveDateTime::from_timestamp_opt; - /// - /// assert!(from_timestamp_opt(0, 0).is_some()); - /// assert!(from_timestamp_opt(0, 999_999_999).is_some()); - /// assert!(from_timestamp_opt(0, 1_500_000_000).is_some()); // leap second - /// assert!(from_timestamp_opt(0, 2_000_000_000).is_none()); - /// assert!(from_timestamp_opt(i64::MAX, 0).is_none()); - /// ~~~~ - #[inline] - pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option<NaiveDateTime> { - let (days, secs) = div_mod_floor(secs, 86_400); - let date = days - .to_i32() - .and_then(|days| days.checked_add(719_163)) - .and_then(NaiveDate::from_num_days_from_ce_opt); - let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs); - match (date, time) { - (Some(date), Some(time)) => Some(NaiveDateTime { date: date, time: time }), - (_, _) => None, - } - } - - /// Parses a string with the specified format string and returns a new `NaiveDateTime`. - /// See the [`format::strftime` module](../format/strftime/index.html) - /// on the supported escape sequences. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDateTime, NaiveDate}; - /// - /// let parse_from_str = NaiveDateTime::parse_from_str; - /// - /// assert_eq!(parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S"), - /// Ok(NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4))); - /// assert_eq!(parse_from_str("5sep2015pm012345.6789", "%d%b%Y%p%I%M%S%.f"), - /// Ok(NaiveDate::from_ymd(2015, 9, 5).and_hms_micro(13, 23, 45, 678_900))); - /// ~~~~ - /// - /// Offset is ignored for the purpose of parsing. - /// - /// ~~~~ - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveDate::from_ymd(2014, 5, 17).and_hms(12, 34, 56))); - /// ~~~~ - /// - /// [Leap seconds](./struct.NaiveTime.html#leap-second-handling) are correctly handled by - /// treating any time of the form `hh:mm:60` as a leap second. - /// (This equally applies to the formatting, so the round trip is possible.) - /// - /// ~~~~ - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!(parse_from_str("2015-07-01 08:59:60.123", "%Y-%m-%d %H:%M:%S%.f"), - /// Ok(NaiveDate::from_ymd(2015, 7, 1).and_hms_milli(8, 59, 59, 1_123))); - /// ~~~~ - /// - /// Missing seconds are assumed to be zero, - /// but out-of-bound times or insufficient fields are errors otherwise. - /// - /// ~~~~ - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!(parse_from_str("94/9/4 7:15", "%y/%m/%d %H:%M"), - /// Ok(NaiveDate::from_ymd(1994, 9, 4).and_hms(7, 15, 0))); - /// - /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); - /// assert!(parse_from_str("94/9/4 12", "%y/%m/%d %H").is_err()); - /// assert!(parse_from_str("94/9/4 17:60", "%y/%m/%d %H:%M").is_err()); - /// assert!(parse_from_str("94/9/4 24:00:00", "%y/%m/%d %H:%M:%S").is_err()); - /// ~~~~ - /// - /// All parsed fields should be consistent to each other, otherwise it's an error. - /// - /// ~~~~ - /// # use chrono::NaiveDateTime; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// let fmt = "%Y-%m-%d %H:%M:%S = UNIX timestamp %s"; - /// assert!(parse_from_str("2001-09-09 01:46:39 = UNIX timestamp 999999999", fmt).is_ok()); - /// assert!(parse_from_str("1970-01-01 00:00:00 = UNIX timestamp 1", fmt).is_err()); - /// ~~~~ - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_datetime_with_offset(0) // no offset adjustment - } - - /// Retrieves a date component. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); - /// assert_eq!(dt.date(), NaiveDate::from_ymd(2016, 7, 8)); - /// ~~~~ - #[inline] - pub fn date(&self) -> NaiveDate { - self.date - } - - /// Retrieves a time component. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveTime}; - /// - /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms(9, 10, 11); - /// assert_eq!(dt.time(), NaiveTime::from_hms(9, 10, 11)); - /// ~~~~ - #[inline] - pub fn time(&self) -> NaiveTime { - self.time - } - - /// Returns the number of non-leap seconds since the midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 980); - /// assert_eq!(dt.timestamp(), 1); - /// - /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms(1, 46, 40); - /// assert_eq!(dt.timestamp(), 1_000_000_000); - /// - /// let dt = NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59); - /// assert_eq!(dt.timestamp(), -1); - /// - /// let dt = NaiveDate::from_ymd(-1, 1, 1).and_hms(0, 0, 0); - /// assert_eq!(dt.timestamp(), -62198755200); - /// ~~~~ - #[inline] - pub fn timestamp(&self) -> i64 { - const UNIX_EPOCH_DAY: i64 = 719_163; - let gregorian_day = i64::from(self.date.num_days_from_ce()); - let seconds_from_midnight = i64::from(self.time.num_seconds_from_midnight()); - (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight - } - - /// Returns the number of non-leap *milliseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// Note also that this does reduce the number of years that can be - /// represented from ~584 Billion to ~584 Million. (If this is a problem, - /// please file an issue to let me know what domain needs millisecond - /// precision over billions of years, I'm curious.) - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 444); - /// assert_eq!(dt.timestamp_millis(), 1_444); - /// - /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms_milli(1, 46, 40, 555); - /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); - /// - /// let dt = NaiveDate::from_ymd(1969, 12, 31).and_hms_milli(23, 59, 59, 100); - /// assert_eq!(dt.timestamp_millis(), -900); - /// ~~~~ - #[inline] - pub fn timestamp_millis(&self) -> i64 { - let as_ms = self.timestamp() * 1000; - as_ms + i64::from(self.timestamp_subsec_millis()) - } - - /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Panics - /// - /// Note also that this does reduce the number of years that can be - /// represented from ~584 Billion to ~584 years. The dates that can be - /// represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// (If this is a problem, please file an issue to let me know what domain - /// needs nanosecond precision over millennia, I'm curious.) - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime}; - /// - /// let dt = NaiveDate::from_ymd(1970, 1, 1).and_hms_nano(0, 0, 1, 444); - /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); - /// - /// let dt = NaiveDate::from_ymd(2001, 9, 9).and_hms_nano(1, 46, 40, 555); - /// - /// const A_BILLION: i64 = 1_000_000_000; - /// let nanos = dt.timestamp_nanos(); - /// assert_eq!(nanos, 1_000_000_000_000_000_555); - /// assert_eq!( - /// dt, - /// NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) - /// ); - /// ~~~~ - #[inline] - pub fn timestamp_nanos(&self) -> i64 { - let as_ns = self.timestamp() * 1_000_000_000; - as_ns + i64::from(self.timestamp_subsec_nanos()) - } - - /// Returns the number of milliseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); - /// assert_eq!(dt.timestamp_subsec_millis(), 123); - /// - /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); - /// assert_eq!(dt.timestamp_subsec_millis(), 1_234); - /// ~~~~ - #[inline] - pub fn timestamp_subsec_millis(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000_000 - } - - /// Returns the number of microseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999,999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); - /// assert_eq!(dt.timestamp_subsec_micros(), 123_456); - /// - /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); - /// assert_eq!(dt.timestamp_subsec_micros(), 1_234_567); - /// ~~~~ - #[inline] - pub fn timestamp_subsec_micros(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000 - } - - /// Returns the number of nanoseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999,999,999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_nano(9, 10, 11, 123_456_789); - /// assert_eq!(dt.timestamp_subsec_nanos(), 123_456_789); - /// - /// let dt = NaiveDate::from_ymd(2015, 7, 1).and_hms_nano(8, 59, 59, 1_234_567_890); - /// assert_eq!(dt.timestamp_subsec_nanos(), 1_234_567_890); - /// ~~~~ - #[inline] - pub fn timestamp_subsec_nanos(&self) -> u32 { - self.time.nanosecond() - } - - /// Adds given `Duration` to the current date and time. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the addition assumes that **there is no leap second ever**, - /// except when the `NaiveDateTime` itself represents a leap second - /// in which case the assumption becomes that **there is exactly a single leap second ever**. - /// - /// Returns `None` when it will result in overflow. - /// - /// # Example - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// use chrono::{Duration, NaiveDate}; - /// - /// let from_ymd = NaiveDate::from_ymd; - /// - /// let d = from_ymd(2016, 7, 8); - /// let hms = |h, m, s| d.and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::zero()), - /// Some(hms(3, 5, 7))); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(1)), - /// Some(hms(3, 5, 8))); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(-1)), - /// Some(hms(3, 5, 6))); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(3600 + 60)), - /// Some(hms(4, 6, 7))); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(86_400)), - /// Some(from_ymd(2016, 7, 9).and_hms(3, 5, 7))); - /// - /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); - /// assert_eq!(hmsm(3, 5, 7, 980).checked_add_signed(Duration::milliseconds(450)), - /// Some(hmsm(3, 5, 8, 430))); - /// # } - /// ~~~~ - /// - /// Overflow returns `None`. - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// # use chrono::{Duration, NaiveDate}; - /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::days(1_000_000_000)), None); - /// # } - /// ~~~~ - /// - /// Leap seconds are handled, - /// but the addition assumes that it is the only leap second happened. - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// # use chrono::{Duration, NaiveDate}; - /// # let from_ymd = NaiveDate::from_ymd; - /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); - /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_add_signed(Duration::zero()), - /// Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(-500)), - /// Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(500)), - /// Some(hmsm(3, 5, 59, 1_800))); - /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(800)), - /// Some(hmsm(3, 6, 0, 100))); - /// assert_eq!(leap.checked_add_signed(Duration::seconds(10)), - /// Some(hmsm(3, 6, 9, 300))); - /// assert_eq!(leap.checked_add_signed(Duration::seconds(-10)), - /// Some(hmsm(3, 5, 50, 300))); - /// assert_eq!(leap.checked_add_signed(Duration::days(1)), - /// Some(from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300))); - /// # } - /// ~~~~ - pub fn checked_add_signed(self, rhs: OldDuration) -> Option<NaiveDateTime> { - let (time, rhs) = self.time.overflowing_add_signed(rhs); - - // early checking to avoid overflow in OldDuration::seconds - if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { - return None; - } - - let date = try_opt!(self.date.checked_add_signed(OldDuration::seconds(rhs))); - Some(NaiveDateTime { date: date, time: time }) - } - - /// Subtracts given `Duration` from the current date and time. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the subtraction assumes that **there is no leap second ever**, - /// except when the `NaiveDateTime` itself represents a leap second - /// in which case the assumption becomes that **there is exactly a single leap second ever**. - /// - /// Returns `None` when it will result in overflow. - /// - /// # Example - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// use chrono::{Duration, NaiveDate}; - /// - /// let from_ymd = NaiveDate::from_ymd; - /// - /// let d = from_ymd(2016, 7, 8); - /// let hms = |h, m, s| d.and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::zero()), - /// Some(hms(3, 5, 7))); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(1)), - /// Some(hms(3, 5, 6))); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(-1)), - /// Some(hms(3, 5, 8))); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(3600 + 60)), - /// Some(hms(2, 4, 7))); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(86_400)), - /// Some(from_ymd(2016, 7, 7).and_hms(3, 5, 7))); - /// - /// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); - /// assert_eq!(hmsm(3, 5, 7, 450).checked_sub_signed(Duration::milliseconds(670)), - /// Some(hmsm(3, 5, 6, 780))); - /// # } - /// ~~~~ - /// - /// Overflow returns `None`. - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// # use chrono::{Duration, NaiveDate}; - /// # let hms = |h, m, s| NaiveDate::from_ymd(2016, 7, 8).and_hms(h, m, s); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::days(1_000_000_000)), None); - /// # } - /// ~~~~ - /// - /// Leap seconds are handled, - /// but the subtraction assumes that it is the only leap second happened. - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// # use chrono::{Duration, NaiveDate}; - /// # let from_ymd = NaiveDate::from_ymd; - /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); - /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_sub_signed(Duration::zero()), - /// Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(200)), - /// Some(hmsm(3, 5, 59, 1_100))); - /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(500)), - /// Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_sub_signed(Duration::seconds(60)), - /// Some(hmsm(3, 5, 0, 300))); - /// assert_eq!(leap.checked_sub_signed(Duration::days(1)), - /// Some(from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300))); - /// # } - /// ~~~~ - pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<NaiveDateTime> { - let (time, rhs) = self.time.overflowing_sub_signed(rhs); - - // early checking to avoid overflow in OldDuration::seconds - if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { - return None; - } - - let date = try_opt!(self.date.checked_sub_signed(OldDuration::seconds(rhs))); - Some(NaiveDateTime { date: date, time: time }) - } - - /// Subtracts another `NaiveDateTime` from the current date and time. - /// This does not overflow or underflow at all. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the subtraction assumes that **there is no leap second ever**, - /// except when any of the `NaiveDateTime`s themselves represents a leap second - /// in which case the assumption becomes that - /// **there are exactly one (or two) leap second(s) ever**. - /// - /// # Example - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// use chrono::{Duration, NaiveDate}; - /// - /// let from_ymd = NaiveDate::from_ymd; - /// - /// let d = from_ymd(2016, 7, 8); - /// assert_eq!(d.and_hms(3, 5, 7).signed_duration_since(d.and_hms(2, 4, 6)), - /// Duration::seconds(3600 + 60 + 1)); - /// - /// // July 8 is 190th day in the year 2016 - /// let d0 = from_ymd(2016, 1, 1); - /// assert_eq!(d.and_hms_milli(0, 7, 6, 500).signed_duration_since(d0.and_hms(0, 0, 0)), - /// Duration::seconds(189 * 86_400 + 7 * 60 + 6) + Duration::milliseconds(500)); - /// # } - /// ~~~~ - /// - /// Leap seconds are handled, but the subtraction assumes that - /// there were no other leap seconds happened. - /// - /// ~~~~ - /// # extern crate chrono; fn main() { - /// # use chrono::{Duration, NaiveDate}; - /// # let from_ymd = NaiveDate::from_ymd; - /// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); - /// assert_eq!(leap.signed_duration_since(from_ymd(2015, 6, 30).and_hms(23, 0, 0)), - /// Duration::seconds(3600) + Duration::milliseconds(500)); - /// assert_eq!(from_ymd(2015, 7, 1).and_hms(1, 0, 0).signed_duration_since(leap), - /// Duration::seconds(3600) - Duration::milliseconds(500)); - /// # } - /// ~~~~ - pub fn signed_duration_since(self, rhs: NaiveDateTime) -> OldDuration { - self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time) - } - - /// Formats the combined date and time with the specified formatting items. - /// Otherwise it is the same as the ordinary [`format`](#method.format) method. - /// - /// The `Iterator` of items should be `Clone`able, - /// since the resulting `DelayedFormat` value may be formatted multiple times. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// use chrono::format::strftime::StrftimeItems; - /// - /// let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S"); - /// let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); - /// assert_eq!(dt.format_with_items(fmt.clone()).to_string(), "2015-09-05 23:56:04"); - /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); - /// ~~~~ - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ~~~~ - /// # use chrono::NaiveDate; - /// # use chrono::format::strftime::StrftimeItems; - /// # let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S").clone(); - /// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); - /// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04"); - /// ~~~~ - #[cfg(any(feature = "alloc", feature = "std", test))] - #[inline] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> - where - I: Iterator<Item = B> + Clone, - B: Borrow<Item<'a>>, - { - DelayedFormat::new(Some(self.date), Some(self.time), items) - } - - /// Formats the combined date and time with the specified format string. - /// See the [`format::strftime` module](../format/strftime/index.html) - /// on the supported escape sequences. - /// - /// This returns a `DelayedFormat`, - /// which gets converted to a string only when actual formatting happens. - /// You may use the `to_string` method to get a `String`, - /// or just feed it into `print!` and other formatting macros. - /// (In this way it avoids the redundant memory allocation.) - /// - /// A wrong format string does *not* issue an error immediately. - /// Rather, converting or formatting the `DelayedFormat` fails. - /// You are recommended to immediately use `DelayedFormat` for this reason. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); - /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); - /// assert_eq!(dt.format("around %l %p on %b %-d").to_string(), "around 11 PM on Sep 5"); - /// ~~~~ - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ~~~~ - /// # use chrono::NaiveDate; - /// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4); - /// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04"); - /// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5"); - /// ~~~~ - #[cfg(any(feature = "alloc", feature = "std", test))] - #[inline] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { - self.format_with_items(StrftimeItems::new(fmt)) - } -} - -impl Datelike for NaiveDateTime { - /// Returns the year number in the [calendar date](./index.html#calendar-date). - /// - /// See also the [`NaiveDate::year`](./struct.NaiveDate.html#method.year) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.year(), 2015); - /// ~~~~ - #[inline] - fn year(&self) -> i32 { - self.date.year() - } - - /// Returns the month number starting from 1. - /// - /// The return value ranges from 1 to 12. - /// - /// See also the [`NaiveDate::month`](./struct.NaiveDate.html#method.month) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.month(), 9); - /// ~~~~ - #[inline] - fn month(&self) -> u32 { - self.date.month() - } - - /// Returns the month number starting from 0. - /// - /// The return value ranges from 0 to 11. - /// - /// See also the [`NaiveDate::month0`](./struct.NaiveDate.html#method.month0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.month0(), 8); - /// ~~~~ - #[inline] - fn month0(&self) -> u32 { - self.date.month0() - } - - /// Returns the day of month starting from 1. - /// - /// The return value ranges from 1 to 31. (The last day of month differs by months.) - /// - /// See also the [`NaiveDate::day`](./struct.NaiveDate.html#method.day) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.day(), 25); - /// ~~~~ - #[inline] - fn day(&self) -> u32 { - self.date.day() - } - - /// Returns the day of month starting from 0. - /// - /// The return value ranges from 0 to 30. (The last day of month differs by months.) - /// - /// See also the [`NaiveDate::day0`](./struct.NaiveDate.html#method.day0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.day0(), 24); - /// ~~~~ - #[inline] - fn day0(&self) -> u32 { - self.date.day0() - } - - /// Returns the day of year starting from 1. - /// - /// The return value ranges from 1 to 366. (The last day of year differs by years.) - /// - /// See also the [`NaiveDate::ordinal`](./struct.NaiveDate.html#method.ordinal) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.ordinal(), 268); - /// ~~~~ - #[inline] - fn ordinal(&self) -> u32 { - self.date.ordinal() - } - - /// Returns the day of year starting from 0. - /// - /// The return value ranges from 0 to 365. (The last day of year differs by years.) - /// - /// See also the [`NaiveDate::ordinal0`](./struct.NaiveDate.html#method.ordinal0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.ordinal0(), 267); - /// ~~~~ - #[inline] - fn ordinal0(&self) -> u32 { - self.date.ordinal0() - } - - /// Returns the day of week. - /// - /// See also the [`NaiveDate::weekday`](./struct.NaiveDate.html#method.weekday) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Weekday}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.weekday(), Weekday::Fri); - /// ~~~~ - #[inline] - fn weekday(&self) -> Weekday { - self.date.weekday() - } - - #[inline] - fn iso_week(&self) -> IsoWeek { - self.date.iso_week() - } - - /// Makes a new `NaiveDateTime` with the year number changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_year`](./struct.NaiveDate.html#method.with_year) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 25).and_hms(12, 34, 56); - /// assert_eq!(dt.with_year(2016), Some(NaiveDate::from_ymd(2016, 9, 25).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_year(-308), Some(NaiveDate::from_ymd(-308, 9, 25).and_hms(12, 34, 56))); - /// ~~~~ - #[inline] - fn with_year(&self, year: i32) -> Option<NaiveDateTime> { - self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the month number (starting from 1) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_month`](./struct.NaiveDate.html#method.with_month) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56); - /// assert_eq!(dt.with_month(10), Some(NaiveDate::from_ymd(2015, 10, 30).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_month(13), None); // no month 13 - /// assert_eq!(dt.with_month(2), None); // no February 30 - /// ~~~~ - #[inline] - fn with_month(&self, month: u32) -> Option<NaiveDateTime> { - self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the month number (starting from 0) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_month0`](./struct.NaiveDate.html#method.with_month0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56); - /// assert_eq!(dt.with_month0(9), Some(NaiveDate::from_ymd(2015, 10, 30).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_month0(12), None); // no month 13 - /// assert_eq!(dt.with_month0(1), None); // no February 30 - /// ~~~~ - #[inline] - fn with_month0(&self, month0: u32) -> Option<NaiveDateTime> { - self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of month (starting from 1) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_day`](./struct.NaiveDate.html#method.with_day) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_day(30), Some(NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_day(31), None); // no September 31 - /// ~~~~ - #[inline] - fn with_day(&self, day: u32) -> Option<NaiveDateTime> { - self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of month (starting from 0) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_day0`](./struct.NaiveDate.html#method.with_day0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_day0(29), Some(NaiveDate::from_ymd(2015, 9, 30).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_day0(30), None); // no September 31 - /// ~~~~ - #[inline] - fn with_day0(&self, day0: u32) -> Option<NaiveDateTime> { - self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of year (starting from 1) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_ordinal`](./struct.NaiveDate.html#method.with_ordinal) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_ordinal(60), - /// Some(NaiveDate::from_ymd(2015, 3, 1).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_ordinal(366), None); // 2015 had only 365 days - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_ordinal(60), - /// Some(NaiveDate::from_ymd(2016, 2, 29).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_ordinal(366), - /// Some(NaiveDate::from_ymd(2016, 12, 31).and_hms(12, 34, 56))); - /// ~~~~ - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDateTime> { - self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of year (starting from 0) changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveDate::with_ordinal0`](./struct.NaiveDate.html#method.with_ordinal0) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_ordinal0(59), - /// Some(NaiveDate::from_ymd(2015, 3, 1).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_ordinal0(365), None); // 2015 had only 365 days - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2016, 9, 8).and_hms(12, 34, 56); - /// assert_eq!(dt.with_ordinal0(59), - /// Some(NaiveDate::from_ymd(2016, 2, 29).and_hms(12, 34, 56))); - /// assert_eq!(dt.with_ordinal0(365), - /// Some(NaiveDate::from_ymd(2016, 12, 31).and_hms(12, 34, 56))); - /// ~~~~ - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDateTime> { - self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self }) - } -} - -impl Timelike for NaiveDateTime { - /// Returns the hour number from 0 to 23. - /// - /// See also the [`NaiveTime::hour`](./struct.NaiveTime.html#method.hour) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.hour(), 12); - /// ~~~~ - #[inline] - fn hour(&self) -> u32 { - self.time.hour() - } - - /// Returns the minute number from 0 to 59. - /// - /// See also the [`NaiveTime::minute`](./struct.NaiveTime.html#method.minute) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.minute(), 34); - /// ~~~~ - #[inline] - fn minute(&self) -> u32 { - self.time.minute() - } - - /// Returns the second number from 0 to 59. - /// - /// See also the [`NaiveTime::second`](./struct.NaiveTime.html#method.second) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.second(), 56); - /// ~~~~ - #[inline] - fn second(&self) -> u32 { - self.time.second() - } - - /// Returns the number of nanoseconds since the whole non-leap second. - /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./struct.NaiveTime.html#leap-second-handling). - /// - /// See also the - /// [`NaiveTime::nanosecond`](./struct.NaiveTime.html#method.nanosecond) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.nanosecond(), 789_000_000); - /// ~~~~ - #[inline] - fn nanosecond(&self) -> u32 { - self.time.nanosecond() - } - - /// Makes a new `NaiveDateTime` with the hour number changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveTime::with_hour`](./struct.NaiveTime.html#method.with_hour) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.with_hour(7), - /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(7, 34, 56, 789))); - /// assert_eq!(dt.with_hour(24), None); - /// ~~~~ - #[inline] - fn with_hour(&self, hour: u32) -> Option<NaiveDateTime> { - self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the minute number changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// - /// See also the - /// [`NaiveTime::with_minute`](./struct.NaiveTime.html#method.with_minute) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.with_minute(45), - /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 45, 56, 789))); - /// assert_eq!(dt.with_minute(60), None); - /// ~~~~ - #[inline] - fn with_minute(&self, min: u32) -> Option<NaiveDateTime> { - self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the second number changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// As with the [`second`](#method.second) method, - /// the input range is restricted to 0 through 59. - /// - /// See also the - /// [`NaiveTime::with_second`](./struct.NaiveTime.html#method.with_second) method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.with_second(17), - /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 17, 789))); - /// assert_eq!(dt.with_second(60), None); - /// ~~~~ - #[inline] - fn with_second(&self, sec: u32) -> Option<NaiveDateTime> { - self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with nanoseconds since the whole non-leap second changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// As with the [`nanosecond`](#method.nanosecond) method, - /// the input range can exceed 1,000,000,000 for leap seconds. - /// - /// See also the - /// [`NaiveTime::with_nanosecond`](./struct.NaiveTime.html#method.with_nanosecond) - /// method. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = NaiveDate::from_ymd(2015, 9, 8).and_hms_milli(12, 34, 56, 789); - /// assert_eq!(dt.with_nanosecond(333_333_333), - /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_nano(12, 34, 56, 333_333_333))); - /// assert_eq!(dt.with_nanosecond(1_333_333_333), // leap second - /// Some(NaiveDate::from_ymd(2015, 9, 8).and_hms_nano(12, 34, 56, 1_333_333_333))); - /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); - /// ~~~~ - #[inline] - fn with_nanosecond(&self, nano: u32) -> Option<NaiveDateTime> { - self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self }) - } -} - -/// `NaiveDateTime` can be used as a key to the hash maps (in principle). -/// -/// Practically this also takes account of fractional seconds, so it is not recommended. -/// (For the obvious reason this also distinguishes leap seconds from non-leap seconds.) -impl hash::Hash for NaiveDateTime { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.date.hash(state); - self.time.hash(state); - } -} - -/// An addition of `Duration` to `NaiveDateTime` yields another `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), -/// the addition assumes that **there is no leap second ever**, -/// except when the `NaiveDateTime` itself represents a leap second -/// in which case the assumption becomes that **there is exactly a single leap second ever**. -/// -/// Panics on underflow or overflow. -/// Use [`NaiveDateTime::checked_add_signed`](#method.checked_add_signed) to detect that. -/// -/// # Example -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// use chrono::{Duration, NaiveDate}; -/// -/// let from_ymd = NaiveDate::from_ymd; -/// -/// let d = from_ymd(2016, 7, 8); -/// let hms = |h, m, s| d.and_hms(h, m, s); -/// assert_eq!(hms(3, 5, 7) + Duration::zero(), hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) + Duration::seconds(1), hms(3, 5, 8)); -/// assert_eq!(hms(3, 5, 7) + Duration::seconds(-1), hms(3, 5, 6)); -/// assert_eq!(hms(3, 5, 7) + Duration::seconds(3600 + 60), hms(4, 6, 7)); -/// assert_eq!(hms(3, 5, 7) + Duration::seconds(86_400), -/// from_ymd(2016, 7, 9).and_hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) + Duration::days(365), -/// from_ymd(2017, 7, 8).and_hms(3, 5, 7)); -/// -/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); -/// assert_eq!(hmsm(3, 5, 7, 980) + Duration::milliseconds(450), hmsm(3, 5, 8, 430)); -/// # } -/// ~~~~ -/// -/// Leap seconds are handled, -/// but the addition assumes that it is the only leap second happened. -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// # use chrono::{Duration, NaiveDate}; -/// # let from_ymd = NaiveDate::from_ymd; -/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); -/// let leap = hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap + Duration::zero(), hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap + Duration::milliseconds(-500), hmsm(3, 5, 59, 800)); -/// assert_eq!(leap + Duration::milliseconds(500), hmsm(3, 5, 59, 1_800)); -/// assert_eq!(leap + Duration::milliseconds(800), hmsm(3, 6, 0, 100)); -/// assert_eq!(leap + Duration::seconds(10), hmsm(3, 6, 9, 300)); -/// assert_eq!(leap + Duration::seconds(-10), hmsm(3, 5, 50, 300)); -/// assert_eq!(leap + Duration::days(1), -/// from_ymd(2016, 7, 9).and_hms_milli(3, 5, 59, 300)); -/// # } -/// ~~~~ -impl Add<OldDuration> for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - fn add(self, rhs: OldDuration) -> NaiveDateTime { - self.checked_add_signed(rhs).expect("`NaiveDateTime + Duration` overflowed") - } -} - -impl AddAssign<OldDuration> for NaiveDateTime { - #[inline] - fn add_assign(&mut self, rhs: OldDuration) { - *self = self.add(rhs); - } -} - -/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`. -/// It is the same as the addition with a negated `Duration`. -/// -/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), -/// the addition assumes that **there is no leap second ever**, -/// except when the `NaiveDateTime` itself represents a leap second -/// in which case the assumption becomes that **there is exactly a single leap second ever**. -/// -/// Panics on underflow or overflow. -/// Use [`NaiveDateTime::checked_sub_signed`](#method.checked_sub_signed) to detect that. -/// -/// # Example -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// use chrono::{Duration, NaiveDate}; -/// -/// let from_ymd = NaiveDate::from_ymd; -/// -/// let d = from_ymd(2016, 7, 8); -/// let hms = |h, m, s| d.and_hms(h, m, s); -/// assert_eq!(hms(3, 5, 7) - Duration::zero(), hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) - Duration::seconds(1), hms(3, 5, 6)); -/// assert_eq!(hms(3, 5, 7) - Duration::seconds(-1), hms(3, 5, 8)); -/// assert_eq!(hms(3, 5, 7) - Duration::seconds(3600 + 60), hms(2, 4, 7)); -/// assert_eq!(hms(3, 5, 7) - Duration::seconds(86_400), -/// from_ymd(2016, 7, 7).and_hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) - Duration::days(365), -/// from_ymd(2015, 7, 9).and_hms(3, 5, 7)); -/// -/// let hmsm = |h, m, s, milli| d.and_hms_milli(h, m, s, milli); -/// assert_eq!(hmsm(3, 5, 7, 450) - Duration::milliseconds(670), hmsm(3, 5, 6, 780)); -/// # } -/// ~~~~ -/// -/// Leap seconds are handled, -/// but the subtraction assumes that it is the only leap second happened. -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// # use chrono::{Duration, NaiveDate}; -/// # let from_ymd = NaiveDate::from_ymd; -/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli(h, m, s, milli); -/// let leap = hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap - Duration::zero(), hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap - Duration::milliseconds(200), hmsm(3, 5, 59, 1_100)); -/// assert_eq!(leap - Duration::milliseconds(500), hmsm(3, 5, 59, 800)); -/// assert_eq!(leap - Duration::seconds(60), hmsm(3, 5, 0, 300)); -/// assert_eq!(leap - Duration::days(1), -/// from_ymd(2016, 7, 7).and_hms_milli(3, 6, 0, 300)); -/// # } -/// ~~~~ -impl Sub<OldDuration> for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - fn sub(self, rhs: OldDuration) -> NaiveDateTime { - self.checked_sub_signed(rhs).expect("`NaiveDateTime - Duration` overflowed") - } -} - -impl SubAssign<OldDuration> for NaiveDateTime { - #[inline] - fn sub_assign(&mut self, rhs: OldDuration) { - *self = self.sub(rhs); - } -} - -/// Subtracts another `NaiveDateTime` from the current date and time. -/// This does not overflow or underflow at all. -/// -/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), -/// the subtraction assumes that **there is no leap second ever**, -/// except when any of the `NaiveDateTime`s themselves represents a leap second -/// in which case the assumption becomes that -/// **there are exactly one (or two) leap second(s) ever**. -/// -/// The implementation is a wrapper around -/// [`NaiveDateTime::signed_duration_since`](#method.signed_duration_since). -/// -/// # Example -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// use chrono::{Duration, NaiveDate}; -/// -/// let from_ymd = NaiveDate::from_ymd; -/// -/// let d = from_ymd(2016, 7, 8); -/// assert_eq!(d.and_hms(3, 5, 7) - d.and_hms(2, 4, 6), Duration::seconds(3600 + 60 + 1)); -/// -/// // July 8 is 190th day in the year 2016 -/// let d0 = from_ymd(2016, 1, 1); -/// assert_eq!(d.and_hms_milli(0, 7, 6, 500) - d0.and_hms(0, 0, 0), -/// Duration::seconds(189 * 86_400 + 7 * 60 + 6) + Duration::milliseconds(500)); -/// # } -/// ~~~~ -/// -/// Leap seconds are handled, but the subtraction assumes that -/// there were no other leap seconds happened. -/// -/// ~~~~ -/// # extern crate chrono; fn main() { -/// # use chrono::{Duration, NaiveDate}; -/// # let from_ymd = NaiveDate::from_ymd; -/// let leap = from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); -/// assert_eq!(leap - from_ymd(2015, 6, 30).and_hms(23, 0, 0), -/// Duration::seconds(3600) + Duration::milliseconds(500)); -/// assert_eq!(from_ymd(2015, 7, 1).and_hms(1, 0, 0) - leap, -/// Duration::seconds(3600) - Duration::milliseconds(500)); -/// # } -/// ~~~~ -impl Sub<NaiveDateTime> for NaiveDateTime { - type Output = OldDuration; - - #[inline] - fn sub(self, rhs: NaiveDateTime) -> OldDuration { - self.signed_duration_since(rhs) - } -} - -/// The `Debug` output of the naive date and time `dt` is the same as -/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../format/strftime/index.html). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ~~~~ -/// use chrono::NaiveDate; -/// -/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24); -/// assert_eq!(format!("{:?}", dt), "2016-11-15T07:39:24"); -/// ~~~~ -/// -/// Leap seconds may also be used. -/// -/// ~~~~ -/// # use chrono::NaiveDate; -/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); -/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60.500"); -/// ~~~~ -impl fmt::Debug for NaiveDateTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}T{:?}", self.date, self.time) - } -} - -/// The `Display` output of the naive date and time `dt` is the same as -/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../format/strftime/index.html). -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ~~~~ -/// use chrono::NaiveDate; -/// -/// let dt = NaiveDate::from_ymd(2016, 11, 15).and_hms(7, 39, 24); -/// assert_eq!(format!("{}", dt), "2016-11-15 07:39:24"); -/// ~~~~ -/// -/// Leap seconds may also be used. -/// -/// ~~~~ -/// # use chrono::NaiveDate; -/// let dt = NaiveDate::from_ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_500); -/// assert_eq!(format!("{}", dt), "2015-06-30 23:59:60.500"); -/// ~~~~ -impl fmt::Display for NaiveDateTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.date, self.time) - } -} - -/// Parsing a `str` into a `NaiveDateTime` uses the same format, -/// [`%Y-%m-%dT%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug`. -/// -/// # Example -/// -/// ~~~~ -/// use chrono::{NaiveDateTime, NaiveDate}; -/// -/// let dt = NaiveDate::from_ymd(2015, 9, 18).and_hms(23, 56, 4); -/// assert_eq!("2015-09-18T23:56:04".parse::<NaiveDateTime>(), Ok(dt)); -/// -/// let dt = NaiveDate::from_ymd(12345, 6, 7).and_hms_milli(7, 59, 59, 1_500); // leap second -/// assert_eq!("+12345-6-7T7:59:60.5".parse::<NaiveDateTime>(), Ok(dt)); -/// -/// assert!("foo".parse::<NaiveDateTime>().is_err()); -/// ~~~~ -impl str::FromStr for NaiveDateTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult<NaiveDateTime> { - const ITEMS: &'static [Item<'static>] = &[ - Item::Numeric(Numeric::Year, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Month, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Day, Pad::Zero), - Item::Space(""), - Item::Literal("T"), // XXX shouldn't this be case-insensitive? - Item::Numeric(Numeric::Hour, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Minute, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Second, Pad::Zero), - Item::Fixed(Fixed::Nanosecond), - Item::Space(""), - ]; - - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_naive_datetime_with_offset(0) - } -} - -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_encodable_json<F, E>(to_string: F) -where - F: Fn(&NaiveDateTime) -> Result<String, E>, - E: ::std::fmt::Debug, -{ - use naive::{MAX_DATE, MIN_DATE}; - - assert_eq!( - to_string(&NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)).ok(), - Some(r#""2016-07-08T09:10:48.090""#.into()) - ); - assert_eq!( - to_string(&NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(), - Some(r#""2014-07-24T12:34:06""#.into()) - ); - assert_eq!( - to_string(&NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)).ok(), - Some(r#""0000-01-01T00:00:60""#.into()) - ); - assert_eq!( - to_string(&NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)).ok(), - Some(r#""-0001-12-31T23:59:59.000000007""#.into()) - ); - assert_eq!( - to_string(&MIN_DATE.and_hms(0, 0, 0)).ok(), - Some(r#""-262144-01-01T00:00:00""#.into()) - ); - assert_eq!( - to_string(&MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999)).ok(), - Some(r#""+262143-12-31T23:59:60.999999999""#.into()) - ); -} - -#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] -fn test_decodable_json<F, E>(from_str: F) -where - F: Fn(&str) -> Result<NaiveDateTime, E>, - E: ::std::fmt::Debug, -{ - use naive::{MAX_DATE, MIN_DATE}; - - assert_eq!( - from_str(r#""2016-07-08T09:10:48.090""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)) - ); - assert_eq!( - from_str(r#""2016-7-8T9:10:48.09""#).ok(), - Some(NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90)) - ); - assert_eq!( - from_str(r#""2014-07-24T12:34:06""#).ok(), - Some(NaiveDate::from_ymd(2014, 7, 24).and_hms(12, 34, 6)) - ); - assert_eq!( - from_str(r#""0000-01-01T00:00:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)) - ); - assert_eq!( - from_str(r#""0-1-1T0:0:60""#).ok(), - Some(NaiveDate::from_ymd(0, 1, 1).and_hms_milli(0, 0, 59, 1_000)) - ); - assert_eq!( - from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), - Some(NaiveDate::from_ymd(-1, 12, 31).and_hms_nano(23, 59, 59, 7)) - ); - assert_eq!(from_str(r#""-262144-01-01T00:00:00""#).ok(), Some(MIN_DATE.and_hms(0, 0, 0))); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), - Some(MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999)) - ); - assert_eq!( - from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(MAX_DATE.and_hms_nano(23, 59, 59, 1_999_999_999)) - ); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""2016-07-08""#).is_err()); - assert!(from_str(r#""09:10:48.090""#).is_err()); - assert!(from_str(r#""20160708T091048.090""#).is_err()); - assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); - assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); - assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); - assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); - assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); - assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); - assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); - assert!(from_str(r#"20160708000000"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - // pre-0.3.0 rustc-serialize format is now invalid - assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(from_str(r#"null"#).is_err()); -} - -#[cfg(all(test, feature = "rustc-serialize"))] -fn test_decodable_json_timestamp<F, E>(from_str: F) -where - F: Fn(&str) -> Result<rustc_serialize::TsSeconds, E>, - E: ::std::fmt::Debug, -{ - assert_eq!( - *from_str("0").unwrap(), - NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0), - "should parse integers as timestamps" - ); - assert_eq!( - *from_str("-1").unwrap(), - NaiveDate::from_ymd(1969, 12, 31).and_hms(23, 59, 59), - "should parse integers as timestamps" - ); -} - -#[cfg(feature = "rustc-serialize")] -pub mod rustc_serialize { - use super::NaiveDateTime; - use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - use std::ops::Deref; - - impl Encodable for NaiveDateTime { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - format!("{:?}", self).encode(s) - } - } - - impl Decodable for NaiveDateTime { - fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDateTime, D::Error> { - d.read_str()?.parse().map_err(|_| d.error("invalid date time string")) - } - } - - /// A `DateTime` that can be deserialized from a seconds-based timestamp - #[derive(Debug)] - #[deprecated( - since = "1.4.2", - note = "RustcSerialize will be removed before chrono 1.0, use Serde instead" - )] - pub struct TsSeconds(NaiveDateTime); - - #[allow(deprecated)] - impl From<TsSeconds> for NaiveDateTime { - /// Pull the internal NaiveDateTime out - #[allow(deprecated)] - fn from(obj: TsSeconds) -> NaiveDateTime { - obj.0 - } - } - - #[allow(deprecated)] - impl Deref for TsSeconds { - type Target = NaiveDateTime; - - #[allow(deprecated)] - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - #[allow(deprecated)] - impl Decodable for TsSeconds { - #[allow(deprecated)] - fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds, D::Error> { - Ok(TsSeconds( - NaiveDateTime::from_timestamp_opt(d.read_i64()?, 0) - .ok_or_else(|| d.error("invalid timestamp"))?, - )) - } - } - - #[cfg(test)] - use rustc_serialize::json; - - #[test] - fn test_encodable() { - super::test_encodable_json(json::encode); - } - - #[test] - fn test_decodable() { - super::test_decodable_json(json::decode); - } - - #[test] - fn test_decodable_timestamps() { - super::test_decodable_json_timestamp(json::decode); - } -} - -/// Tools to help serializing/deserializing `NaiveDateTime`s -#[cfg(feature = "serde")] -pub mod serde { - use super::NaiveDateTime; - use core::fmt; - use serdelib::{de, ser}; - - /// Serialize a `NaiveDateTime` as an RFC 3339 string - /// - /// See [the `serde` module](./serde/index.html) for alternate - /// serialization formats. - impl ser::Serialize for NaiveDateTime { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - struct FormatWrapped<'a, D: 'a> { - inner: &'a D, - } - - impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } - } - - serializer.collect_str(&FormatWrapped { inner: &self }) - } - } - - struct NaiveDateTimeVisitor; - - impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted date and time string") - } - - fn visit_str<E>(self, value: &str) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - value.parse().map_err(E::custom) - } - } - - impl<'de> de::Deserialize<'de> for NaiveDateTime { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(NaiveDateTimeVisitor) - } - } - - /// Used to serialize/deserialize from nanosecond-precision timestamps - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// use chrono::naive::serde::ts_nanoseconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_nanoseconds")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = NaiveDate::from_ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_nanoseconds { - use core::fmt; - use serdelib::{de, ser}; - - use {ne_timestamp, NaiveDateTime}; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # #[macro_use] extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// # use serde::Serialize; - /// use chrono::naive::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: NaiveDate::from_ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_nanos()) - } - - /// Deserialize a `DateTime` from a nanoseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{NaiveDateTime, Utc}; - /// # use serde::Deserialize; - /// use chrono::naive::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)?) - } - - struct NaiveDateTimeFromNanoSecondsVisitor; - - impl<'de> de::Visitor<'de> for NaiveDateTimeFromNanoSecondsVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64<E>(self, value: i64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt( - value / 1_000_000_000, - (value % 1_000_000_000) as u32, - ) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - - fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt( - value as i64 / 1_000_000_000, - (value as i64 % 1_000_000_000) as u32, - ) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - } - } - - /// Used to serialize/deserialize from millisecond-precision timestamps - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// use chrono::naive::serde::ts_milliseconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_milliseconds")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = NaiveDate::from_ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_milliseconds { - use core::fmt; - use serdelib::{de, ser}; - - use {ne_timestamp, NaiveDateTime}; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # #[macro_use] extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// # use serde::Serialize; - /// use chrono::naive::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: NaiveDate::from_ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_millis()) - } - - /// Deserialize a `DateTime` from a milliseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{NaiveDateTime, Utc}; - /// # use serde::Deserialize; - /// use chrono::naive::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)?) - } - - struct NaiveDateTimeFromMilliSecondsVisitor; - - impl<'de> de::Visitor<'de> for NaiveDateTimeFromMilliSecondsVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64<E>(self, value: i64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - - fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt( - (value / 1000) as i64, - ((value % 1000) * 1_000_000) as u32, - ) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - } - } - - /// Used to serialize/deserialize from second-precision timestamps - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// use chrono::naive::serde::ts_seconds; - /// #[derive(Deserialize, Serialize)] - /// struct S { - /// #[serde(with = "ts_seconds")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let time = NaiveDate::from_ymd(2015, 5, 15).and_hms(10, 0, 0); - /// let my_s = S { - /// time: time.clone(), - /// }; - /// - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// let my_s: S = serde_json::from_str(&as_string)?; - /// assert_eq!(my_s.time, time); - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub mod ts_seconds { - use core::fmt; - use serdelib::{de, ser}; - - use {ne_timestamp, NaiveDateTime}; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # #[macro_use] extern crate serde; - /// # extern crate chrono; - /// # use chrono::{TimeZone, NaiveDate, NaiveDateTime, Utc}; - /// # use serde::Serialize; - /// use chrono::naive::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<String, serde_json::Error> { - /// let my_s = S { - /// time: NaiveDate::from_ymd(2015, 5, 15).and_hms(10, 0, 0), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok(as_string) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp()) - } - - /// Deserialize a `DateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # // We mark this ignored so that we can test on 1.13 (which does not - /// # // support custom derive), and run tests with --ignored on beta and - /// # // nightly to actually trigger these. - /// # - /// # #[macro_use] extern crate serde_derive; - /// # #[macro_use] extern crate serde_json; - /// # extern crate serde; - /// # extern crate chrono; - /// # use chrono::{NaiveDateTime, Utc}; - /// # use serde::Deserialize; - /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: NaiveDateTime - /// } - /// - /// # fn example() -> Result<S, serde_json::Error> { - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// # Ok(my_s) - /// # } - /// # fn main() { example().unwrap(); } - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> - where - D: de::Deserializer<'de>, - { - Ok(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)?) - } - - struct NaiveDateTimeFromSecondsVisitor; - - impl<'de> de::Visitor<'de> for NaiveDateTimeFromSecondsVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64<E>(self, value: i64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt(value, 0) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - - fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E> - where - E: de::Error, - { - NaiveDateTime::from_timestamp_opt(value as i64, 0) - .ok_or_else(|| E::custom(ne_timestamp(value))) - } - } - } - - #[cfg(test)] - extern crate bincode; - #[cfg(test)] - extern crate serde_derive; - #[cfg(test)] - extern crate serde_json; - - #[test] - fn test_serde_serialize() { - super::test_encodable_json(self::serde_json::to_string); - } - - #[test] - fn test_serde_deserialize() { - super::test_decodable_json(|input| self::serde_json::from_str(&input)); - } - - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - #[test] - fn test_serde_bincode() { - use self::bincode::{deserialize, serialize, Infinite}; - use naive::NaiveDate; - - let dt = NaiveDate::from_ymd(2016, 7, 8).and_hms_milli(9, 10, 48, 90); - let encoded = serialize(&dt, Infinite).unwrap(); - let decoded: NaiveDateTime = deserialize(&encoded).unwrap(); - assert_eq!(dt, decoded); - } - - #[test] - fn test_serde_bincode_optional() { - use self::bincode::{deserialize, serialize, Infinite}; - use self::serde_derive::{Deserialize, Serialize}; - use prelude::*; - use serde::ts_nanoseconds_option; - - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Test { - one: Option<i64>, - #[serde(with = "ts_nanoseconds_option")] - two: Option<DateTime<Utc>>, - } - - let expected = Test { one: Some(1), two: Some(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1)) }; - let bytes: Vec<u8> = serialize(&expected, Infinite).unwrap(); - let actual = deserialize::<Test>(&(bytes)).unwrap(); - - assert_eq!(expected, actual); - } -} - -#[cfg(test)] -mod tests { - use super::NaiveDateTime; - use naive::{NaiveDate, MAX_DATE, MIN_DATE}; - use oldtime::Duration; - use std::i64; - use Datelike; - - #[test] - fn test_datetime_from_timestamp() { - let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0); - let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); - assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); - assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); - assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); - assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); - assert_eq!(from_timestamp(i64::MIN), None); - assert_eq!(from_timestamp(i64::MAX), None); - } - - #[test] - fn test_datetime_add() { - fn check( - (y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32), - rhs: Duration, - result: Option<(i32, u32, u32, u32, u32, u32)>, - ) { - let lhs = NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - let sum = - result.map(|(y, m, d, h, n, s)| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s)); - assert_eq!(lhs.checked_add_signed(rhs), sum); - assert_eq!(lhs.checked_sub_signed(-rhs), sum); - }; - - check( - (2014, 5, 6, 7, 8, 9), - Duration::seconds(3600 + 60 + 1), - Some((2014, 5, 6, 8, 9, 10)), - ); - check( - (2014, 5, 6, 7, 8, 9), - Duration::seconds(-(3600 + 60 + 1)), - Some((2014, 5, 6, 6, 7, 8)), - ); - check((2014, 5, 6, 7, 8, 9), Duration::seconds(86399), Some((2014, 5, 7, 7, 8, 8))); - check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); - check((2014, 5, 6, 7, 8, 9), Duration::seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9))); - check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); - - // overflow check - // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`. - // (they are private constants, but the equivalence is tested in that module.) - let max_days_from_year_0 = MAX_DATE.signed_duration_since(NaiveDate::from_ymd(0, 1, 1)); - check((0, 1, 1, 0, 0, 0), max_days_from_year_0, Some((MAX_DATE.year(), 12, 31, 0, 0, 0))); - check( - (0, 1, 1, 0, 0, 0), - max_days_from_year_0 + Duration::seconds(86399), - Some((MAX_DATE.year(), 12, 31, 23, 59, 59)), - ); - check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + Duration::seconds(86_400), None); - check((0, 1, 1, 0, 0, 0), Duration::max_value(), None); - - let min_days_from_year_0 = MIN_DATE.signed_duration_since(NaiveDate::from_ymd(0, 1, 1)); - check((0, 1, 1, 0, 0, 0), min_days_from_year_0, Some((MIN_DATE.year(), 1, 1, 0, 0, 0))); - check((0, 1, 1, 0, 0, 0), min_days_from_year_0 - Duration::seconds(1), None); - check((0, 1, 1, 0, 0, 0), Duration::min_value(), None); - } - - #[test] - fn test_datetime_sub() { - let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - let since = NaiveDateTime::signed_duration_since; - assert_eq!( - since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), - Duration::zero() - ); - assert_eq!( - since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)), - Duration::seconds(1) - ); - assert_eq!( - since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), - Duration::seconds(-1) - ); - assert_eq!( - since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), - Duration::seconds(86399) - ); - assert_eq!( - since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)), - Duration::seconds(999_999_999) - ); - } - - #[test] - fn test_datetime_addassignment() { - let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - let mut date = ymdhms(2016, 10, 1, 10, 10, 10); - date += Duration::minutes(10_000_000); - assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10)); - date += Duration::days(10); - assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10)); - } - - #[test] - fn test_datetime_subassignment() { - let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - let mut date = ymdhms(2016, 10, 1, 10, 10, 10); - date -= Duration::minutes(10_000_000); - assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10)); - date -= Duration::days(10); - assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10)); - } - - #[test] - fn test_datetime_timestamp() { - let to_timestamp = - |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s).timestamp(); - assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); - assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); - assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); - } - - #[test] - fn test_datetime_from_str() { - // valid cases - let valid = [ - "2015-2-18T23:16:9.15", - "-77-02-18T23:16:09", - " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", - ]; - for &s in &valid { - let d = match s.parse::<NaiveDateTime>() { - Ok(d) => d, - Err(e) => panic!("parsing `{}` has failed: {}", s, e), - }; - let s_ = format!("{:?}", d); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::<NaiveDateTime>() { - Ok(d) => d, - Err(e) => { - panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) - } - }; - assert!( - d == d_, - "`{}` is parsed into `{:?}`, but reparsed result \ - `{:?}` does not match", - s, - d, - d_ - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - assert!("".parse::<NaiveDateTime>().is_err()); - assert!("x".parse::<NaiveDateTime>().is_err()); - assert!("15".parse::<NaiveDateTime>().is_err()); - assert!("15:8:9".parse::<NaiveDateTime>().is_err()); - assert!("15-8-9".parse::<NaiveDateTime>().is_err()); - assert!("2015-15-15T15:15:15".parse::<NaiveDateTime>().is_err()); - assert!("2012-12-12T12:12:12x".parse::<NaiveDateTime>().is_err()); - assert!("2012-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); - assert!("+ 82701-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); - assert!("+802701-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); // out-of-bound - } - - #[test] - fn test_datetime_parse_from_str() { - let ymdhms = |y, m, d, h, n, s| NaiveDate::from_ymd(y, m, d).and_hms(h, n, s); - let ymdhmsn = - |y, m, d, h, n, s, nano| NaiveDate::from_ymd(y, m, d).and_hms_nano(h, n, s, nano); - assert_eq!( - NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymdhms(2014, 5, 7, 12, 34, 56)) - ); // ignore offset - assert_eq!( - NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), - Ok(ymdhms(2015, 2, 2, 0, 0, 0)) - ); - assert_eq!( - NaiveDateTime::parse_from_str( - "Fri, 09 Aug 2013 23:54:35 GMT", - "%a, %d %b %Y %H:%M:%S GMT" - ), - Ok(ymdhms(2013, 8, 9, 23, 54, 35)) - ); - assert!(NaiveDateTime::parse_from_str( - "Sat, 09 Aug 2013 23:54:35 GMT", - "%a, %d %b %Y %H:%M:%S GMT" - ) - .is_err()); - assert!(NaiveDateTime::parse_from_str("2014-5-7 12:3456", "%Y-%m-%d %H:%M:%S").is_err()); - assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient - assert_eq!( - NaiveDateTime::parse_from_str("1441497364", "%s"), - Ok(ymdhms(2015, 9, 5, 23, 56, 4)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"), - Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"), - Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"), - Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"), - Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) - ); - } - - #[test] - fn test_datetime_format() { - let dt = NaiveDate::from_ymd(2010, 9, 8).and_hms_milli(7, 6, 54, 321); - assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010"); - assert_eq!(dt.format("%s").to_string(), "1283929614"); - assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); - - // a horror of leap second: coming near to you. - let dt = NaiveDate::from_ymd(2012, 6, 30).and_hms_milli(23, 59, 59, 1_000); - assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012"); - assert_eq!(dt.format("%s").to_string(), "1341100799"); // not 1341100800, it's intentional. - } - - #[test] - fn test_datetime_add_sub_invariant() { - // issue #37 - let base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); - let t = -946684799990000; - let time = base + Duration::microseconds(t); - assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap()); - } - - #[test] - fn test_nanosecond_range() { - const A_BILLION: i64 = 1_000_000_000; - let maximum = "2262-04-11T23:47:16.854775804"; - let parsed: NaiveDateTime = maximum.parse().unwrap(); - let nanos = parsed.timestamp_nanos(); - assert_eq!( - parsed, - NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) - ); - - let minimum = "1677-09-21T00:12:44.000000000"; - let parsed: NaiveDateTime = minimum.parse().unwrap(); - let nanos = parsed.timestamp_nanos(); - assert_eq!( - parsed, - NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) - ); - } -} diff --git a/vendor/chrono/src/naive/datetime/mod.rs b/vendor/chrono/src/naive/datetime/mod.rs new file mode 100644 index 000000000..ec0d842c0 --- /dev/null +++ b/vendor/chrono/src/naive/datetime/mod.rs @@ -0,0 +1,1946 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! ISO 8601 date and time without timezone. + +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::borrow::Borrow; +use core::convert::TryFrom; +use core::fmt::Write; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use core::{fmt, str}; + +use num_integer::div_mod_floor; +use num_traits::ToPrimitive; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +#[cfg(any(feature = "alloc", feature = "std", test))] +use crate::format::DelayedFormat; +use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{Fixed, Item, Numeric, Pad}; +use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime}; +use crate::oldtime::Duration as OldDuration; +use crate::{DateTime, Datelike, LocalResult, Months, TimeZone, Timelike, Weekday}; +use core::cmp::Ordering; + +#[cfg(feature = "rustc-serialize")] +pub(super) mod rustc_serialize; + +/// Tools to help serializing/deserializing `NaiveDateTime`s +#[cfg(feature = "serde")] +pub(crate) mod serde; + +#[cfg(test)] +mod tests; + +/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS` +/// will always overflow the addition with any date and time type. +/// +/// So why is this needed? `Duration::seconds(rhs)` may overflow, and we don't have +/// an alternative returning `Option` or `Result`. Thus we need some early bound to avoid +/// touching that call when we are already sure that it WILL overflow... +const MAX_SECS_BITS: usize = 44; + +/// Number of nanoseconds in a millisecond +const NANOS_IN_MILLISECOND: u32 = 1_000_000; +/// Number of nanoseconds in a second +const NANOS_IN_SECOND: u32 = 1000 * NANOS_IN_MILLISECOND; + +/// The minimum possible `NaiveDateTime`. +#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MIN instead")] +pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime::MIN; +/// The maximum possible `NaiveDateTime`. +#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MAX instead")] +pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime::MAX; + +/// ISO 8601 combined date and time without timezone. +/// +/// # Example +/// +/// `NaiveDateTime` is commonly created from [`NaiveDate`](./struct.NaiveDate.html). +/// +/// ``` +/// use chrono::{NaiveDate, NaiveDateTime}; +/// +/// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); +/// # let _ = dt; +/// ``` +/// +/// You can use typical [date-like](../trait.Datelike.html) and +/// [time-like](../trait.Timelike.html) methods, +/// provided that relevant traits are in the scope. +/// +/// ``` +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); +/// use chrono::{Datelike, Timelike, Weekday}; +/// +/// assert_eq!(dt.weekday(), Weekday::Fri); +/// assert_eq!(dt.num_seconds_from_midnight(), 33011); +/// ``` +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct NaiveDateTime { + date: NaiveDate, + time: NaiveTime, +} + +/// The unit of a timestamp expressed in fractions of a second. +/// Currently either milliseconds or microseconds. +/// +/// This is a private type, used in the implementation of +/// [NaiveDateTime::from_timestamp_millis] and [NaiveDateTime::from_timestamp_micros]. +#[derive(Clone, Copy, Debug)] +enum TimestampUnit { + Millis, + Micros, +} + +impl TimestampUnit { + fn per_second(self) -> u32 { + match self { + TimestampUnit::Millis => 1_000, + TimestampUnit::Micros => 1_000_000, + } + } + fn nanos_per(self) -> u32 { + match self { + TimestampUnit::Millis => 1_000_000, + TimestampUnit::Micros => 1_000, + } + } +} + +impl NaiveDateTime { + /// Makes a new `NaiveDateTime` from date and time components. + /// Equivalent to [`date.and_time(time)`](./struct.NaiveDate.html#method.and_time) + /// and many other helper constructors on `NaiveDate`. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime}; + /// + /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); + /// let t = NaiveTime::from_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// + /// let dt = NaiveDateTime::new(d, t); + /// assert_eq!(dt.date(), d); + /// assert_eq!(dt.time(), t); + /// ``` + #[inline] + pub const fn new(date: NaiveDate, time: NaiveTime) -> NaiveDateTime { + NaiveDateTime { date, time } + } + + /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, + /// from the number of non-leap seconds + /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") + /// and the number of nanoseconds since the last whole non-leap second. + /// + /// For a non-naive version of this function see + /// [`TimeZone::timestamp`](../offset/trait.TimeZone.html#method.timestamp). + /// + /// The nanosecond part can exceed 1,000,000,000 in order to represent the + /// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX + /// timestamp" cannot represent a leap second unambiguously.) + /// + /// Panics on the out-of-range number of seconds and/or invalid nanosecond. + #[deprecated(since = "0.4.23", note = "use `from_timestamp_opt()` instead")] + #[inline] + pub fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { + let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs); + datetime.expect("invalid or out-of-range datetime") + } + + /// Creates a new [NaiveDateTime] from milliseconds since the UNIX epoch. + /// + /// The UNIX epoch starts on midnight, January 1, 1970, UTC. + /// + /// Returns `None` on an out-of-range number of milliseconds. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDateTime; + /// let timestamp_millis: i64 = 1662921288000; //Sunday, September 11, 2022 6:34:48 PM + /// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_millis: i64 = -2208936075000; //Mon Jan 01 1900 14:38:45 GMT+0000 + /// let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); + /// ``` + #[inline] + pub fn from_timestamp_millis(millis: i64) -> Option<NaiveDateTime> { + Self::from_timestamp_unit(millis, TimestampUnit::Millis) + } + + /// Creates a new [NaiveDateTime] from microseconds since the UNIX epoch. + /// + /// The UNIX epoch starts on midnight, January 1, 1970, UTC. + /// + /// Returns `None` on an out-of-range number of microseconds. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDateTime; + /// let timestamp_micros: i64 = 1662921288000000; //Sunday, September 11, 2022 6:34:48 PM + /// let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); + /// + /// // Negative timestamps (before the UNIX epoch) are supported as well. + /// let timestamp_micros: i64 = -2208936075000000; //Mon Jan 01 1900 14:38:45 GMT+0000 + /// let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); + /// assert!(naive_datetime.is_some()); + /// assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); + /// ``` + #[inline] + pub fn from_timestamp_micros(micros: i64) -> Option<NaiveDateTime> { + Self::from_timestamp_unit(micros, TimestampUnit::Micros) + } + + /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, + /// from the number of non-leap seconds + /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") + /// and the number of nanoseconds since the last whole non-leap second. + /// + /// The nanosecond part can exceed 1,000,000,000 + /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling). + /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) + /// + /// Returns `None` on the out-of-range number of seconds (more than 262 000 years away + /// from common era) and/or invalid nanosecond (2 seconds or more). + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDateTime, NaiveDate}; + /// use std::i64; + /// + /// let from_timestamp_opt = NaiveDateTime::from_timestamp_opt; + /// + /// assert!(from_timestamp_opt(0, 0).is_some()); + /// assert!(from_timestamp_opt(0, 999_999_999).is_some()); + /// assert!(from_timestamp_opt(0, 1_500_000_000).is_some()); // leap second + /// assert!(from_timestamp_opt(0, 2_000_000_000).is_none()); + /// assert!(from_timestamp_opt(i64::MAX, 0).is_none()); + /// ``` + #[inline] + pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option<NaiveDateTime> { + let (days, secs) = div_mod_floor(secs, 86_400); + let date = days + .to_i32() + .and_then(|days| days.checked_add(719_163)) + .and_then(NaiveDate::from_num_days_from_ce_opt); + let time = NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs); + match (date, time) { + (Some(date), Some(time)) => Some(NaiveDateTime { date, time }), + (_, _) => None, + } + } + + /// Parses a string with the specified format string and returns a new `NaiveDateTime`. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDateTime, NaiveDate}; + /// + /// let parse_from_str = NaiveDateTime::parse_from_str; + /// + /// assert_eq!(parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S"), + /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap())); + /// assert_eq!(parse_from_str("5sep2015pm012345.6789", "%d%b%Y%p%I%M%S%.f"), + /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_micro_opt(13, 23, 45, 678_900).unwrap())); + /// ``` + /// + /// Offset is ignored for the purpose of parsing. + /// + /// ``` + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + /// Ok(NaiveDate::from_ymd_opt(2014, 5, 17).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// ``` + /// + /// [Leap seconds](./struct.NaiveTime.html#leap-second-handling) are correctly handled by + /// treating any time of the form `hh:mm:60` as a leap second. + /// (This equally applies to the formatting, so the round trip is possible.) + /// + /// ``` + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("2015-07-01 08:59:60.123", "%Y-%m-%d %H:%M:%S%.f"), + /// Ok(NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_milli_opt(8, 59, 59, 1_123).unwrap())); + /// ``` + /// + /// Missing seconds are assumed to be zero, + /// but out-of-bound times or insufficient fields are errors otherwise. + /// + /// ``` + /// # use chrono::{NaiveDateTime, NaiveDate}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// assert_eq!(parse_from_str("94/9/4 7:15", "%y/%m/%d %H:%M"), + /// Ok(NaiveDate::from_ymd_opt(1994, 9, 4).unwrap().and_hms_opt(7, 15, 0).unwrap())); + /// + /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); + /// assert!(parse_from_str("94/9/4 12", "%y/%m/%d %H").is_err()); + /// assert!(parse_from_str("94/9/4 17:60", "%y/%m/%d %H:%M").is_err()); + /// assert!(parse_from_str("94/9/4 24:00:00", "%y/%m/%d %H:%M:%S").is_err()); + /// ``` + /// + /// All parsed fields should be consistent to each other, otherwise it's an error. + /// + /// ``` + /// # use chrono::NaiveDateTime; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// let fmt = "%Y-%m-%d %H:%M:%S = UNIX timestamp %s"; + /// assert!(parse_from_str("2001-09-09 01:46:39 = UNIX timestamp 999999999", fmt).is_ok()); + /// assert!(parse_from_str("1970-01-01 00:00:00 = UNIX timestamp 1", fmt).is_err()); + /// ``` + /// + /// Years before 1 BCE or after 9999 CE, require an initial sign + /// + ///``` + /// # use chrono::{NaiveDate, NaiveDateTime}; + /// # let parse_from_str = NaiveDateTime::parse_from_str; + /// let fmt = "%Y-%m-%d %H:%M:%S"; + /// assert!(parse_from_str("10000-09-09 01:46:39", fmt).is_err()); + /// assert!(parse_from_str("+10000-09-09 01:46:39", fmt).is_ok()); + ///``` + pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> { + let mut parsed = Parsed::new(); + parse(&mut parsed, s, StrftimeItems::new(fmt))?; + parsed.to_naive_datetime_with_offset(0) // no offset adjustment + } + + /// Retrieves a date component. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); + /// assert_eq!(dt.date(), NaiveDate::from_ymd_opt(2016, 7, 8).unwrap()); + /// ``` + #[inline] + pub const fn date(&self) -> NaiveDate { + self.date + } + + /// Retrieves a time component. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveTime}; + /// + /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); + /// assert_eq!(dt.time(), NaiveTime::from_hms_opt(9, 10, 11).unwrap()); + /// ``` + #[inline] + pub const fn time(&self) -> NaiveTime { + self.time + } + + /// Returns the number of non-leap seconds since the midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 980).unwrap(); + /// assert_eq!(dt.timestamp(), 1); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_opt(1, 46, 40).unwrap(); + /// assert_eq!(dt.timestamp(), 1_000_000_000); + /// + /// let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 59).unwrap(); + /// assert_eq!(dt.timestamp(), -1); + /// + /// let dt = NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + /// assert_eq!(dt.timestamp(), -62198755200); + /// ``` + #[inline] + pub fn timestamp(&self) -> i64 { + const UNIX_EPOCH_DAY: i64 = 719_163; + let gregorian_day = i64::from(self.date.num_days_from_ce()); + let seconds_from_midnight = i64::from(self.time.num_seconds_from_midnight()); + (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight + } + + /// Returns the number of non-leap *milliseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// Note also that this does reduce the number of years that can be + /// represented from ~584 Billion to ~584 Million. (If this is a problem, + /// please file an issue to let me know what domain needs millisecond + /// precision over billions of years, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap(); + /// assert_eq!(dt.timestamp_millis(), 1_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap(); + /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); + /// + /// let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_milli_opt(23, 59, 59, 100).unwrap(); + /// assert_eq!(dt.timestamp_millis(), -900); + /// ``` + #[inline] + pub fn timestamp_millis(&self) -> i64 { + let as_ms = self.timestamp() * 1000; + as_ms + i64::from(self.timestamp_subsec_millis()) + } + + /// Returns the number of non-leap *microseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// Note also that this does reduce the number of years that can be + /// represented from ~584 Billion to ~584 Thousand. (If this is a problem, + /// please file an issue to let me know what domain needs microsecond + /// precision over millennia, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap(); + /// assert_eq!(dt.timestamp_micros(), 1_000_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap(); + /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); + /// ``` + #[inline] + pub fn timestamp_micros(&self) -> i64 { + let as_us = self.timestamp() * 1_000_000; + as_us + i64::from(self.timestamp_subsec_micros()) + } + + /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. + /// + /// Note that this does *not* account for the timezone! + /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. + /// + /// # Panics + /// + /// Note also that this does reduce the number of years that can be + /// represented from ~584 Billion to ~584 years. The dates that can be + /// represented as nanoseconds are between 1677-09-21T00:12:44.0 and + /// 2262-04-11T23:47:16.854775804. + /// + /// (If this is a problem, please file an issue to let me know what domain + /// needs nanosecond precision over millennia, I'm curious.) + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime}; + /// + /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap(); + /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); + /// + /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap(); + /// + /// const A_BILLION: i64 = 1_000_000_000; + /// let nanos = dt.timestamp_nanos(); + /// assert_eq!(nanos, 1_000_000_000_000_000_555); + /// assert_eq!( + /// dt, + /// NaiveDateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) + /// ); + /// ``` + #[inline] + pub fn timestamp_nanos(&self) -> i64 { + let as_ns = self.timestamp() * 1_000_000_000; + as_ns + i64::from(self.timestamp_subsec_nanos()) + } + + /// Returns the number of milliseconds since the last whole non-leap second. + /// + /// The return value ranges from 0 to 999, + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 123_456_789).unwrap(); + /// assert_eq!(dt.timestamp_subsec_millis(), 123); + /// + /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_nano_opt(8, 59, 59, 1_234_567_890).unwrap(); + /// assert_eq!(dt.timestamp_subsec_millis(), 1_234); + /// ``` + #[inline] + pub fn timestamp_subsec_millis(&self) -> u32 { + self.timestamp_subsec_nanos() / 1_000_000 + } + + /// Returns the number of microseconds since the last whole non-leap second. + /// + /// The return value ranges from 0 to 999,999, + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 123_456_789).unwrap(); + /// assert_eq!(dt.timestamp_subsec_micros(), 123_456); + /// + /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_nano_opt(8, 59, 59, 1_234_567_890).unwrap(); + /// assert_eq!(dt.timestamp_subsec_micros(), 1_234_567); + /// ``` + #[inline] + pub fn timestamp_subsec_micros(&self) -> u32 { + self.timestamp_subsec_nanos() / 1_000 + } + + /// Returns the number of nanoseconds since the last whole non-leap second. + /// + /// The return value ranges from 0 to 999,999,999, + /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 123_456_789).unwrap(); + /// assert_eq!(dt.timestamp_subsec_nanos(), 123_456_789); + /// + /// let dt = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_nano_opt(8, 59, 59, 1_234_567_890).unwrap(); + /// assert_eq!(dt.timestamp_subsec_nanos(), 1_234_567_890); + /// ``` + #[inline] + pub fn timestamp_subsec_nanos(&self) -> u32 { + self.time.nanosecond() + } + + /// Adds given `Duration` to the current date and time. + /// + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), + /// the addition assumes that **there is no leap second ever**, + /// except when the `NaiveDateTime` itself represents a leap second + /// in which case the assumption becomes that **there is exactly a single leap second ever**. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDate}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// let d = from_ymd(2016, 7, 8); + /// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::zero()), + /// Some(hms(3, 5, 7))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(1)), + /// Some(hms(3, 5, 8))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(-1)), + /// Some(hms(3, 5, 6))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(3600 + 60)), + /// Some(hms(4, 6, 7))); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::seconds(86_400)), + /// Some(from_ymd(2016, 7, 9).and_hms_opt(3, 5, 7).unwrap())); + /// + /// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); + /// assert_eq!(hmsm(3, 5, 7, 980).checked_add_signed(Duration::milliseconds(450)), + /// Some(hmsm(3, 5, 8, 430))); + /// ``` + /// + /// Overflow returns `None`. + /// + /// ``` + /// # use chrono::{Duration, NaiveDate}; + /// # let hms = |h, m, s| NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(h, m, s).unwrap(); + /// assert_eq!(hms(3, 5, 7).checked_add_signed(Duration::days(1_000_000_000)), None); + /// ``` + /// + /// Leap seconds are handled, + /// but the addition assumes that it is the only leap second happened. + /// + /// ``` + /// # use chrono::{Duration, NaiveDate}; + /// # let from_ymd = NaiveDate::from_ymd; + /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); + /// let leap = hmsm(3, 5, 59, 1_300); + /// assert_eq!(leap.checked_add_signed(Duration::zero()), + /// Some(hmsm(3, 5, 59, 1_300))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(-500)), + /// Some(hmsm(3, 5, 59, 800))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(500)), + /// Some(hmsm(3, 5, 59, 1_800))); + /// assert_eq!(leap.checked_add_signed(Duration::milliseconds(800)), + /// Some(hmsm(3, 6, 0, 100))); + /// assert_eq!(leap.checked_add_signed(Duration::seconds(10)), + /// Some(hmsm(3, 6, 9, 300))); + /// assert_eq!(leap.checked_add_signed(Duration::seconds(-10)), + /// Some(hmsm(3, 5, 50, 300))); + /// assert_eq!(leap.checked_add_signed(Duration::days(1)), + /// Some(from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap())); + /// ``` + pub fn checked_add_signed(self, rhs: OldDuration) -> Option<NaiveDateTime> { + let (time, rhs) = self.time.overflowing_add_signed(rhs); + + // early checking to avoid overflow in OldDuration::seconds + if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { + return None; + } + + let date = self.date.checked_add_signed(OldDuration::seconds(rhs))?; + Some(NaiveDateTime { date, time }) + } + + /// Adds given `Months` to the current date and time. + /// + /// Returns `None` when it will result in overflow. + /// + /// Overflow returns `None`. + /// + /// # Example + /// + /// ``` + /// use std::str::FromStr; + /// use chrono::{Months, NaiveDate, NaiveDateTime}; + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + /// .checked_add_months(Months::new(1)), + /// Some(NaiveDate::from_ymd_opt(2014, 2, 1).unwrap().and_hms_opt(1, 0, 0).unwrap()) + /// ); + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + /// .checked_add_months(Months::new(core::i32::MAX as u32 + 1)), + /// None + /// ); + /// ``` + pub fn checked_add_months(self, rhs: Months) -> Option<NaiveDateTime> { + Some(Self { date: self.date.checked_add_months(rhs)?, time: self.time }) + } + + /// Subtracts given `Duration` from the current date and time. + /// + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), + /// the subtraction assumes that **there is no leap second ever**, + /// except when the `NaiveDateTime` itself represents a leap second + /// in which case the assumption becomes that **there is exactly a single leap second ever**. + /// + /// Returns `None` when it will result in overflow. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDate}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// let d = from_ymd(2016, 7, 8); + /// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::zero()), + /// Some(hms(3, 5, 7))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(1)), + /// Some(hms(3, 5, 6))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(-1)), + /// Some(hms(3, 5, 8))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(3600 + 60)), + /// Some(hms(2, 4, 7))); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::seconds(86_400)), + /// Some(from_ymd(2016, 7, 7).and_hms_opt(3, 5, 7).unwrap())); + /// + /// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); + /// assert_eq!(hmsm(3, 5, 7, 450).checked_sub_signed(Duration::milliseconds(670)), + /// Some(hmsm(3, 5, 6, 780))); + /// ``` + /// + /// Overflow returns `None`. + /// + /// ``` + /// # use chrono::{Duration, NaiveDate}; + /// # let hms = |h, m, s| NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(h, m, s).unwrap(); + /// assert_eq!(hms(3, 5, 7).checked_sub_signed(Duration::days(1_000_000_000)), None); + /// ``` + /// + /// Leap seconds are handled, + /// but the subtraction assumes that it is the only leap second happened. + /// + /// ``` + /// # use chrono::{Duration, NaiveDate}; + /// # let from_ymd = NaiveDate::from_ymd; + /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); + /// let leap = hmsm(3, 5, 59, 1_300); + /// assert_eq!(leap.checked_sub_signed(Duration::zero()), + /// Some(hmsm(3, 5, 59, 1_300))); + /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(200)), + /// Some(hmsm(3, 5, 59, 1_100))); + /// assert_eq!(leap.checked_sub_signed(Duration::milliseconds(500)), + /// Some(hmsm(3, 5, 59, 800))); + /// assert_eq!(leap.checked_sub_signed(Duration::seconds(60)), + /// Some(hmsm(3, 5, 0, 300))); + /// assert_eq!(leap.checked_sub_signed(Duration::days(1)), + /// Some(from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap())); + /// ``` + pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<NaiveDateTime> { + let (time, rhs) = self.time.overflowing_sub_signed(rhs); + + // early checking to avoid overflow in OldDuration::seconds + if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) { + return None; + } + + let date = self.date.checked_sub_signed(OldDuration::seconds(rhs))?; + Some(NaiveDateTime { date, time }) + } + + /// Subtracts given `Months` from the current date and time. + /// + /// Returns `None` when it will result in overflow. + /// + /// Overflow returns `None`. + /// + /// # Example + /// + /// ``` + /// use std::str::FromStr; + /// use chrono::{Months, NaiveDate, NaiveDateTime}; + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + /// .checked_sub_months(Months::new(1)), + /// Some(NaiveDate::from_ymd_opt(2013, 12, 1).unwrap().and_hms_opt(1, 0, 0).unwrap()) + /// ); + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + /// .checked_sub_months(Months::new(core::i32::MAX as u32 + 1)), + /// None + /// ); + /// ``` + pub fn checked_sub_months(self, rhs: Months) -> Option<NaiveDateTime> { + Some(Self { date: self.date.checked_sub_months(rhs)?, time: self.time }) + } + + /// Add a duration in [`Days`] to the date part of the `NaiveDateTime` + /// + /// Returns `None` if the resulting date would be out of range. + pub fn checked_add_days(self, days: Days) -> Option<Self> { + Some(Self { date: self.date.checked_add_days(days)?, ..self }) + } + + /// Subtract a duration in [`Days`] from the date part of the `NaiveDateTime` + /// + /// Returns `None` if the resulting date would be out of range. + pub fn checked_sub_days(self, days: Days) -> Option<Self> { + Some(Self { date: self.date.checked_sub_days(days)?, ..self }) + } + + /// Subtracts another `NaiveDateTime` from the current date and time. + /// This does not overflow or underflow at all. + /// + /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), + /// the subtraction assumes that **there is no leap second ever**, + /// except when any of the `NaiveDateTime`s themselves represents a leap second + /// in which case the assumption becomes that + /// **there are exactly one (or two) leap second(s) ever**. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDate}; + /// + /// let from_ymd = NaiveDate::from_ymd; + /// + /// let d = from_ymd(2016, 7, 8); + /// assert_eq!(d.and_hms_opt(3, 5, 7).unwrap().signed_duration_since(d.and_hms_opt(2, 4, 6).unwrap()), + /// Duration::seconds(3600 + 60 + 1)); + /// + /// // July 8 is 190th day in the year 2016 + /// let d0 = from_ymd(2016, 1, 1); + /// assert_eq!(d.and_hms_milli_opt(0, 7, 6, 500).unwrap().signed_duration_since(d0.and_hms_opt(0, 0, 0).unwrap()), + /// Duration::seconds(189 * 86_400 + 7 * 60 + 6) + Duration::milliseconds(500)); + /// ``` + /// + /// Leap seconds are handled, but the subtraction assumes that + /// there were no other leap seconds happened. + /// + /// ``` + /// # use chrono::{Duration, NaiveDate}; + /// # let from_ymd = NaiveDate::from_ymd; + /// let leap = from_ymd(2015, 6, 30).and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); + /// assert_eq!(leap.signed_duration_since(from_ymd(2015, 6, 30).and_hms_opt(23, 0, 0).unwrap()), + /// Duration::seconds(3600) + Duration::milliseconds(500)); + /// assert_eq!(from_ymd(2015, 7, 1).and_hms_opt(1, 0, 0).unwrap().signed_duration_since(leap), + /// Duration::seconds(3600) - Duration::milliseconds(500)); + /// ``` + pub fn signed_duration_since(self, rhs: NaiveDateTime) -> OldDuration { + self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time) + } + + /// Formats the combined date and time with the specified formatting items. + /// Otherwise it is the same as the ordinary [`format`](#method.format) method. + /// + /// The `Iterator` of items should be `Clone`able, + /// since the resulting `DelayedFormat` value may be formatted multiple times. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// use chrono::format::strftime::StrftimeItems; + /// + /// let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S"); + /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); + /// assert_eq!(dt.format_with_items(fmt.clone()).to_string(), "2015-09-05 23:56:04"); + /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); + /// ``` + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ``` + /// # use chrono::NaiveDate; + /// # use chrono::format::strftime::StrftimeItems; + /// # let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S").clone(); + /// # let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); + /// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04"); + /// ``` + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + #[inline] + pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> + where + I: Iterator<Item = B> + Clone, + B: Borrow<Item<'a>>, + { + DelayedFormat::new(Some(self.date), Some(self.time), items) + } + + /// Formats the combined date and time with the specified format string. + /// See the [`format::strftime` module](../format/strftime/index.html) + /// on the supported escape sequences. + /// + /// This returns a `DelayedFormat`, + /// which gets converted to a string only when actual formatting happens. + /// You may use the `to_string` method to get a `String`, + /// or just feed it into `print!` and other formatting macros. + /// (In this way it avoids the redundant memory allocation.) + /// + /// A wrong format string does *not* issue an error immediately. + /// Rather, converting or formatting the `DelayedFormat` fails. + /// You are recommended to immediately use `DelayedFormat` for this reason. + /// + /// # Example + /// + /// ``` + /// use chrono::NaiveDate; + /// + /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); + /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); + /// assert_eq!(dt.format("around %l %p on %b %-d").to_string(), "around 11 PM on Sep 5"); + /// ``` + /// + /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. + /// + /// ``` + /// # use chrono::NaiveDate; + /// # let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); + /// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04"); + /// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5"); + /// ``` + #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] + #[inline] + pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { + self.format_with_items(StrftimeItems::new(fmt)) + } + + /// Converts the `NaiveDateTime` into the timezone-aware `DateTime<Tz>` + /// with the provided timezone, if possible. + /// + /// This can fail in cases where the local time represented by the `NaiveDateTime` + /// is not a valid local timestamp in the target timezone due to an offset transition + /// for example if the target timezone had a change from +00:00 to +01:00 + /// occuring at 2015-09-05 22:59:59, then a local time of 2015-09-05 23:56:04 + /// could never occur. Similarly, if the offset transitioned in the opposite direction + /// then there would be two local times of 2015-09-05 23:56:04, one at +00:00 and one + /// at +01:00. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, Utc}; + /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap().and_local_timezone(Utc).unwrap(); + /// assert_eq!(dt.timezone(), Utc); + pub fn and_local_timezone<Tz: TimeZone>(&self, tz: Tz) -> LocalResult<DateTime<Tz>> { + tz.from_local_datetime(self) + } + + /// The minimum possible `NaiveDateTime`. + pub const MIN: Self = Self { date: NaiveDate::MIN, time: NaiveTime::MIN }; + /// The maximum possible `NaiveDateTime`. + pub const MAX: Self = Self { date: NaiveDate::MAX, time: NaiveTime::MAX }; + + /// Creates a new [NaiveDateTime] from milliseconds or microseconds since the UNIX epoch. + /// + /// This is a private function used by [from_timestamp_millis] and [from_timestamp_micros]. + #[inline] + fn from_timestamp_unit(value: i64, unit: TimestampUnit) -> Option<NaiveDateTime> { + let (secs, subsecs) = + (value / i64::from(unit.per_second()), value % i64::from(unit.per_second())); + + match subsecs.cmp(&0) { + Ordering::Less => { + // in the case where our subsec part is negative, then we are actually in the earlier second + // hence we subtract one from the seconds part, and we then add a whole second worth of nanos + // to our nanos part. Due to the use of u32 datatype, it is more convenient to subtract + // the absolute value of the subsec nanos from a whole second worth of nanos + let nsecs = u32::try_from(subsecs.abs()).ok()? * unit.nanos_per(); + NaiveDateTime::from_timestamp_opt( + secs.checked_sub(1)?, + NANOS_IN_SECOND.checked_sub(nsecs)?, + ) + } + Ordering::Equal => NaiveDateTime::from_timestamp_opt(secs, 0), + Ordering::Greater => { + // convert the subsec millis into nanosecond scale so they can be supplied + // as the nanoseconds parameter + let nsecs = u32::try_from(subsecs).ok()? * unit.nanos_per(); + NaiveDateTime::from_timestamp_opt(secs, nsecs) + } + } + } +} + +impl Datelike for NaiveDateTime { + /// Returns the year number in the [calendar date](./index.html#calendar-date). + /// + /// See also the [`NaiveDate::year`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.year(), 2015); + /// ``` + #[inline] + fn year(&self) -> i32 { + self.date.year() + } + + /// Returns the month number starting from 1. + /// + /// The return value ranges from 1 to 12. + /// + /// See also the [`NaiveDate::month`](./struct.NaiveDate.html#method.month) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.month(), 9); + /// ``` + #[inline] + fn month(&self) -> u32 { + self.date.month() + } + + /// Returns the month number starting from 0. + /// + /// The return value ranges from 0 to 11. + /// + /// See also the [`NaiveDate::month0`](./struct.NaiveDate.html#method.month0) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.month0(), 8); + /// ``` + #[inline] + fn month0(&self) -> u32 { + self.date.month0() + } + + /// Returns the day of month starting from 1. + /// + /// The return value ranges from 1 to 31. (The last day of month differs by months.) + /// + /// See also the [`NaiveDate::day`](./struct.NaiveDate.html#method.day) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.day(), 25); + /// ``` + #[inline] + fn day(&self) -> u32 { + self.date.day() + } + + /// Returns the day of month starting from 0. + /// + /// The return value ranges from 0 to 30. (The last day of month differs by months.) + /// + /// See also the [`NaiveDate::day0`](./struct.NaiveDate.html#method.day0) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.day0(), 24); + /// ``` + #[inline] + fn day0(&self) -> u32 { + self.date.day0() + } + + /// Returns the day of year starting from 1. + /// + /// The return value ranges from 1 to 366. (The last day of year differs by years.) + /// + /// See also the [`NaiveDate::ordinal`](./struct.NaiveDate.html#method.ordinal) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.ordinal(), 268); + /// ``` + #[inline] + fn ordinal(&self) -> u32 { + self.date.ordinal() + } + + /// Returns the day of year starting from 0. + /// + /// The return value ranges from 0 to 365. (The last day of year differs by years.) + /// + /// See also the [`NaiveDate::ordinal0`](./struct.NaiveDate.html#method.ordinal0) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.ordinal0(), 267); + /// ``` + #[inline] + fn ordinal0(&self) -> u32 { + self.date.ordinal0() + } + + /// Returns the day of week. + /// + /// See also the [`NaiveDate::weekday`](./struct.NaiveDate.html#method.weekday) method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Weekday}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.weekday(), Weekday::Fri); + /// ``` + #[inline] + fn weekday(&self) -> Weekday { + self.date.weekday() + } + + #[inline] + fn iso_week(&self) -> IsoWeek { + self.date.iso_week() + } + + /// Makes a new `NaiveDateTime` with the year number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_year`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_year(2016), Some(NaiveDate::from_ymd_opt(2016, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_year(-308), Some(NaiveDate::from_ymd_opt(-308, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// ``` + #[inline] + fn with_year(&self, year: i32) -> Option<NaiveDateTime> { + self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the month number (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_month`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_month(10), Some(NaiveDate::from_ymd_opt(2015, 10, 30).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_month(13), None); // no month 13 + /// assert_eq!(dt.with_month(2), None); // no February 30 + /// ``` + #[inline] + fn with_month(&self, month: u32) -> Option<NaiveDateTime> { + self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the month number (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_month0`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_month0(9), Some(NaiveDate::from_ymd_opt(2015, 10, 30).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_month0(12), None); // no month 13 + /// assert_eq!(dt.with_month0(1), None); // no February 30 + /// ``` + #[inline] + fn with_month0(&self, month0: u32) -> Option<NaiveDateTime> { + self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of month (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_day`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_day(30), Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_day(31), None); // no September 31 + /// ``` + #[inline] + fn with_day(&self, day: u32) -> Option<NaiveDateTime> { + self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of month (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_day0`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_day0(29), Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_day0(30), None); // no September 31 + /// ``` + #[inline] + fn with_day0(&self, day0: u32) -> Option<NaiveDateTime> { + self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of year (starting from 1) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_ordinal`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_ordinal(60), + /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_ordinal(366), None); // 2015 had only 365 days + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_ordinal(60), + /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_ordinal(366), + /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// ``` + #[inline] + fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDateTime> { + self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the day of year (starting from 0) changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveDate::with_ordinal0`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Datelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_ordinal0(59), + /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_ordinal0(365), None); // 2015 had only 365 days + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); + /// assert_eq!(dt.with_ordinal0(59), + /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// assert_eq!(dt.with_ordinal0(365), + /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap().and_hms_opt(12, 34, 56).unwrap())); + /// ``` + #[inline] + fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDateTime> { + self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self }) + } +} + +impl Timelike for NaiveDateTime { + /// Returns the hour number from 0 to 23. + /// + /// See also the [`NaiveTime::hour`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.hour(), 12); + /// ``` + #[inline] + fn hour(&self) -> u32 { + self.time.hour() + } + + /// Returns the minute number from 0 to 59. + /// + /// See also the [`NaiveTime::minute`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.minute(), 34); + /// ``` + #[inline] + fn minute(&self) -> u32 { + self.time.minute() + } + + /// Returns the second number from 0 to 59. + /// + /// See also the [`NaiveTime::second`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.second(), 56); + /// ``` + #[inline] + fn second(&self) -> u32 { + self.time.second() + } + + /// Returns the number of nanoseconds since the whole non-leap second. + /// The range from 1,000,000,000 to 1,999,999,999 represents + /// the [leap second](./struct.NaiveTime.html#leap-second-handling). + /// + /// See also the [`NaiveTime::nanosecond`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.nanosecond(), 789_000_000); + /// ``` + #[inline] + fn nanosecond(&self) -> u32 { + self.time.nanosecond() + } + + /// Makes a new `NaiveDateTime` with the hour number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the [`NaiveTime::with_hour`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.with_hour(7), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(7, 34, 56, 789).unwrap())); + /// assert_eq!(dt.with_hour(24), None); + /// ``` + #[inline] + fn with_hour(&self, hour: u32) -> Option<NaiveDateTime> { + self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the minute number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// + /// See also the + /// [`NaiveTime::with_minute`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.with_minute(45), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 45, 56, 789).unwrap())); + /// assert_eq!(dt.with_minute(60), None); + /// ``` + #[inline] + fn with_minute(&self, min: u32) -> Option<NaiveDateTime> { + self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with the second number changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. As + /// with the [`NaiveDateTime::second`] method, the input range is + /// restricted to 0 through 59. + /// + /// See also the [`NaiveTime::with_second`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.with_second(17), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 17, 789).unwrap())); + /// assert_eq!(dt.with_second(60), None); + /// ``` + #[inline] + fn with_second(&self, sec: u32) -> Option<NaiveDateTime> { + self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self }) + } + + /// Makes a new `NaiveDateTime` with nanoseconds since the whole non-leap second changed. + /// + /// Returns `None` when the resulting `NaiveDateTime` would be invalid. + /// As with the [`NaiveDateTime::nanosecond`] method, + /// the input range can exceed 1,000,000,000 for leap seconds. + /// + /// See also the [`NaiveTime::with_nanosecond`] method. + /// + /// # Example + /// + /// ``` + /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; + /// + /// let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); + /// assert_eq!(dt.with_nanosecond(333_333_333), + /// Some(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_nano_opt(12, 34, 56, 333_333_333).unwrap())); + /// assert_eq!(dt.with_nanosecond(1_333_333_333), // leap second + /// Some(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_nano_opt(12, 34, 56, 1_333_333_333).unwrap())); + /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); + /// ``` + #[inline] + fn with_nanosecond(&self, nano: u32) -> Option<NaiveDateTime> { + self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self }) + } +} + +/// An addition of `Duration` to `NaiveDateTime` yields another `NaiveDateTime`. +/// +/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), +/// the addition assumes that **there is no leap second ever**, +/// except when the `NaiveDateTime` itself represents a leap second +/// in which case the assumption becomes that **there is exactly a single leap second ever**. +/// +/// Panics on underflow or overflow. Use [`NaiveDateTime::checked_add_signed`] +/// to detect that. +/// +/// # Example +/// +/// ``` +/// use chrono::{Duration, NaiveDate}; +/// +/// let from_ymd = NaiveDate::from_ymd; +/// +/// let d = from_ymd(2016, 7, 8); +/// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); +/// assert_eq!(hms(3, 5, 7) + Duration::zero(), hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(1), hms(3, 5, 8)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(-1), hms(3, 5, 6)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(3600 + 60), hms(4, 6, 7)); +/// assert_eq!(hms(3, 5, 7) + Duration::seconds(86_400), +/// from_ymd(2016, 7, 9).and_hms_opt(3, 5, 7).unwrap()); +/// assert_eq!(hms(3, 5, 7) + Duration::days(365), +/// from_ymd(2017, 7, 8).and_hms_opt(3, 5, 7).unwrap()); +/// +/// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); +/// assert_eq!(hmsm(3, 5, 7, 980) + Duration::milliseconds(450), hmsm(3, 5, 8, 430)); +/// ``` +/// +/// Leap seconds are handled, +/// but the addition assumes that it is the only leap second happened. +/// +/// ``` +/// # use chrono::{Duration, NaiveDate}; +/// # let from_ymd = NaiveDate::from_ymd; +/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); +/// let leap = hmsm(3, 5, 59, 1_300); +/// assert_eq!(leap + Duration::zero(), hmsm(3, 5, 59, 1_300)); +/// assert_eq!(leap + Duration::milliseconds(-500), hmsm(3, 5, 59, 800)); +/// assert_eq!(leap + Duration::milliseconds(500), hmsm(3, 5, 59, 1_800)); +/// assert_eq!(leap + Duration::milliseconds(800), hmsm(3, 6, 0, 100)); +/// assert_eq!(leap + Duration::seconds(10), hmsm(3, 6, 9, 300)); +/// assert_eq!(leap + Duration::seconds(-10), hmsm(3, 5, 50, 300)); +/// assert_eq!(leap + Duration::days(1), +/// from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap()); +/// ``` +impl Add<OldDuration> for NaiveDateTime { + type Output = NaiveDateTime; + + #[inline] + fn add(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_add_signed(rhs).expect("`NaiveDateTime + Duration` overflowed") + } +} + +impl AddAssign<OldDuration> for NaiveDateTime { + #[inline] + fn add_assign(&mut self, rhs: OldDuration) { + *self = self.add(rhs); + } +} + +impl Add<Months> for NaiveDateTime { + type Output = NaiveDateTime; + + /// An addition of months to `NaiveDateTime` clamped to valid days in resulting month. + /// + /// # Panics + /// + /// Panics if the resulting date would be out of range. + /// + /// # Example + /// + /// ``` + /// use chrono::{Duration, NaiveDateTime, Months, NaiveDate}; + /// use std::str::FromStr; + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + Months::new(1), + /// NaiveDate::from_ymd_opt(2014, 2, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 2, 0).unwrap() + Months::new(11), + /// NaiveDate::from_ymd_opt(2014, 12, 1).unwrap().and_hms_opt(0, 2, 0).unwrap() + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 0, 3).unwrap() + Months::new(12), + /// NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().and_hms_opt(0, 0, 3).unwrap() + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 0, 4).unwrap() + Months::new(13), + /// NaiveDate::from_ymd_opt(2015, 2, 1).unwrap().and_hms_opt(0, 0, 4).unwrap() + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2014, 1, 31).unwrap().and_hms_opt(0, 5, 0).unwrap() + Months::new(1), + /// NaiveDate::from_ymd_opt(2014, 2, 28).unwrap().and_hms_opt(0, 5, 0).unwrap() + /// ); + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2020, 1, 31).unwrap().and_hms_opt(6, 0, 0).unwrap() + Months::new(1), + /// NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().and_hms_opt(6, 0, 0).unwrap() + /// ); + /// ``` + fn add(self, rhs: Months) -> Self::Output { + Self { date: self.date.checked_add_months(rhs).unwrap(), time: self.time } + } +} + +/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`. +/// It is the same as the addition with a negated `Duration`. +/// +/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), +/// the addition assumes that **there is no leap second ever**, +/// except when the `NaiveDateTime` itself represents a leap second +/// in which case the assumption becomes that **there is exactly a single leap second ever**. +/// +/// Panics on underflow or overflow. Use [`NaiveDateTime::checked_sub_signed`] +/// to detect that. +/// +/// # Example +/// +/// ``` +/// use chrono::{Duration, NaiveDate}; +/// +/// let from_ymd = NaiveDate::from_ymd; +/// +/// let d = from_ymd(2016, 7, 8); +/// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); +/// assert_eq!(hms(3, 5, 7) - Duration::zero(), hms(3, 5, 7)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(1), hms(3, 5, 6)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(-1), hms(3, 5, 8)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(3600 + 60), hms(2, 4, 7)); +/// assert_eq!(hms(3, 5, 7) - Duration::seconds(86_400), +/// from_ymd(2016, 7, 7).and_hms_opt(3, 5, 7).unwrap()); +/// assert_eq!(hms(3, 5, 7) - Duration::days(365), +/// from_ymd(2015, 7, 9).and_hms_opt(3, 5, 7).unwrap()); +/// +/// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); +/// assert_eq!(hmsm(3, 5, 7, 450) - Duration::milliseconds(670), hmsm(3, 5, 6, 780)); +/// ``` +/// +/// Leap seconds are handled, +/// but the subtraction assumes that it is the only leap second happened. +/// +/// ``` +/// # use chrono::{Duration, NaiveDate}; +/// # let from_ymd = NaiveDate::from_ymd; +/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); +/// let leap = hmsm(3, 5, 59, 1_300); +/// assert_eq!(leap - Duration::zero(), hmsm(3, 5, 59, 1_300)); +/// assert_eq!(leap - Duration::milliseconds(200), hmsm(3, 5, 59, 1_100)); +/// assert_eq!(leap - Duration::milliseconds(500), hmsm(3, 5, 59, 800)); +/// assert_eq!(leap - Duration::seconds(60), hmsm(3, 5, 0, 300)); +/// assert_eq!(leap - Duration::days(1), +/// from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap()); +/// ``` +impl Sub<OldDuration> for NaiveDateTime { + type Output = NaiveDateTime; + + #[inline] + fn sub(self, rhs: OldDuration) -> NaiveDateTime { + self.checked_sub_signed(rhs).expect("`NaiveDateTime - Duration` overflowed") + } +} + +impl SubAssign<OldDuration> for NaiveDateTime { + #[inline] + fn sub_assign(&mut self, rhs: OldDuration) { + *self = self.sub(rhs); + } +} + +/// A subtraction of Months from `NaiveDateTime` clamped to valid days in resulting month. +/// +/// # Panics +/// +/// Panics if the resulting date would be out of range. +/// +/// # Example +/// +/// ``` +/// use chrono::{Duration, NaiveDateTime, Months, NaiveDate}; +/// use std::str::FromStr; +/// +/// assert_eq!( +/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(01, 00, 00).unwrap() - Months::new(11), +/// NaiveDate::from_ymd_opt(2013, 02, 01).unwrap().and_hms_opt(01, 00, 00).unwrap() +/// ); +/// assert_eq!( +/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(00, 02, 00).unwrap() - Months::new(12), +/// NaiveDate::from_ymd_opt(2013, 01, 01).unwrap().and_hms_opt(00, 02, 00).unwrap() +/// ); +/// assert_eq!( +/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(00, 00, 03).unwrap() - Months::new(13), +/// NaiveDate::from_ymd_opt(2012, 12, 01).unwrap().and_hms_opt(00, 00, 03).unwrap() +/// ); +/// ``` +impl Sub<Months> for NaiveDateTime { + type Output = NaiveDateTime; + + fn sub(self, rhs: Months) -> Self::Output { + Self { date: self.date.checked_sub_months(rhs).unwrap(), time: self.time } + } +} + +/// Subtracts another `NaiveDateTime` from the current date and time. +/// This does not overflow or underflow at all. +/// +/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), +/// the subtraction assumes that **there is no leap second ever**, +/// except when any of the `NaiveDateTime`s themselves represents a leap second +/// in which case the assumption becomes that +/// **there are exactly one (or two) leap second(s) ever**. +/// +/// The implementation is a wrapper around [`NaiveDateTime::signed_duration_since`]. +/// +/// # Example +/// +/// ``` +/// use chrono::{Duration, NaiveDate}; +/// +/// let from_ymd = NaiveDate::from_ymd; +/// +/// let d = from_ymd(2016, 7, 8); +/// assert_eq!(d.and_hms_opt(3, 5, 7).unwrap() - d.and_hms_opt(2, 4, 6).unwrap(), Duration::seconds(3600 + 60 + 1)); +/// +/// // July 8 is 190th day in the year 2016 +/// let d0 = from_ymd(2016, 1, 1); +/// assert_eq!(d.and_hms_milli_opt(0, 7, 6, 500).unwrap() - d0.and_hms_opt(0, 0, 0).unwrap(), +/// Duration::seconds(189 * 86_400 + 7 * 60 + 6) + Duration::milliseconds(500)); +/// ``` +/// +/// Leap seconds are handled, but the subtraction assumes that no other leap +/// seconds happened. +/// +/// ``` +/// # use chrono::{Duration, NaiveDate}; +/// # let from_ymd = NaiveDate::from_ymd; +/// let leap = from_ymd(2015, 6, 30).and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); +/// assert_eq!(leap - from_ymd(2015, 6, 30).and_hms_opt(23, 0, 0).unwrap(), +/// Duration::seconds(3600) + Duration::milliseconds(500)); +/// assert_eq!(from_ymd(2015, 7, 1).and_hms_opt(1, 0, 0).unwrap() - leap, +/// Duration::seconds(3600) - Duration::milliseconds(500)); +/// ``` +impl Sub<NaiveDateTime> for NaiveDateTime { + type Output = OldDuration; + + #[inline] + fn sub(self, rhs: NaiveDateTime) -> OldDuration { + self.signed_duration_since(rhs) + } +} + +impl Add<Days> for NaiveDateTime { + type Output = NaiveDateTime; + + fn add(self, days: Days) -> Self::Output { + self.checked_add_days(days).unwrap() + } +} + +impl Sub<Days> for NaiveDateTime { + type Output = NaiveDateTime; + + fn sub(self, days: Days) -> Self::Output { + self.checked_sub_days(days).unwrap() + } +} + +/// The `Debug` output of the naive date and time `dt` is the same as +/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](crate::format::strftime). +/// +/// The string printed can be readily parsed via the `parse` method on `str`. +/// +/// It should be noted that, for leap seconds not on the minute boundary, +/// it may print a representation not distinguishable from non-leap seconds. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (By the time of the first leap second on 1972-06-30, +/// every time zone offset around the world has standardized to the 5-minute alignment.) +/// +/// # Example +/// +/// ``` +/// use chrono::NaiveDate; +/// +/// let dt = NaiveDate::from_ymd_opt(2016, 11, 15).unwrap().and_hms_opt(7, 39, 24).unwrap(); +/// assert_eq!(format!("{:?}", dt), "2016-11-15T07:39:24"); +/// ``` +/// +/// Leap seconds may also be used. +/// +/// ``` +/// # use chrono::NaiveDate; +/// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); +/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60.500"); +/// ``` +impl fmt::Debug for NaiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.date.fmt(f)?; + f.write_char('T')?; + self.time.fmt(f) + } +} + +/// The `Display` output of the naive date and time `dt` is the same as +/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](crate::format::strftime). +/// +/// It should be noted that, for leap seconds not on the minute boundary, +/// it may print a representation not distinguishable from non-leap seconds. +/// This doesn't matter in practice, since such leap seconds never happened. +/// (By the time of the first leap second on 1972-06-30, +/// every time zone offset around the world has standardized to the 5-minute alignment.) +/// +/// # Example +/// +/// ``` +/// use chrono::NaiveDate; +/// +/// let dt = NaiveDate::from_ymd_opt(2016, 11, 15).unwrap().and_hms_opt(7, 39, 24).unwrap(); +/// assert_eq!(format!("{}", dt), "2016-11-15 07:39:24"); +/// ``` +/// +/// Leap seconds may also be used. +/// +/// ``` +/// # use chrono::NaiveDate; +/// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); +/// assert_eq!(format!("{}", dt), "2015-06-30 23:59:60.500"); +/// ``` +impl fmt::Display for NaiveDateTime { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.date.fmt(f)?; + f.write_char(' ')?; + self.time.fmt(f) + } +} + +/// Parsing a `str` into a `NaiveDateTime` uses the same format, +/// [`%Y-%m-%dT%H:%M:%S%.f`](crate::format::strftime), as in `Debug`. +/// +/// # Example +/// +/// ``` +/// use chrono::{NaiveDateTime, NaiveDate}; +/// +/// let dt = NaiveDate::from_ymd_opt(2015, 9, 18).unwrap().and_hms_opt(23, 56, 4).unwrap(); +/// assert_eq!("2015-09-18T23:56:04".parse::<NaiveDateTime>(), Ok(dt)); +/// +/// let dt = NaiveDate::from_ymd_opt(12345, 6, 7).unwrap().and_hms_milli_opt(7, 59, 59, 1_500).unwrap(); // leap second +/// assert_eq!("+12345-6-7T7:59:60.5".parse::<NaiveDateTime>(), Ok(dt)); +/// +/// assert!("foo".parse::<NaiveDateTime>().is_err()); +/// ``` +impl str::FromStr for NaiveDateTime { + type Err = ParseError; + + fn from_str(s: &str) -> ParseResult<NaiveDateTime> { + const ITEMS: &[Item<'static>] = &[ + Item::Numeric(Numeric::Year, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Month, Pad::Zero), + Item::Space(""), + Item::Literal("-"), + Item::Numeric(Numeric::Day, Pad::Zero), + Item::Space(""), + Item::Literal("T"), // XXX shouldn't this be case-insensitive? + Item::Numeric(Numeric::Hour, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Minute, Pad::Zero), + Item::Space(""), + Item::Literal(":"), + Item::Numeric(Numeric::Second, Pad::Zero), + Item::Fixed(Fixed::Nanosecond), + Item::Space(""), + ]; + + let mut parsed = Parsed::new(); + parse(&mut parsed, s, ITEMS.iter())?; + parsed.to_naive_datetime_with_offset(0) + } +} + +/// The default value for a NaiveDateTime is one with epoch 0 +/// that is, 1st of January 1970 at 00:00:00. +/// +/// # Example +/// +/// ```rust +/// use chrono::NaiveDateTime; +/// +/// let default_date = NaiveDateTime::default(); +/// assert_eq!(default_date, NaiveDateTime::from_timestamp(0, 0)); +/// ``` +impl Default for NaiveDateTime { + fn default() -> Self { + NaiveDateTime::from_timestamp_opt(0, 0).unwrap() + } +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_encodable_json<F, E>(to_string: F) +where + F: Fn(&NaiveDateTime) -> Result<String, E>, + E: ::std::fmt::Debug, +{ + assert_eq!( + to_string( + &NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap() + ) + .ok(), + Some(r#""2016-07-08T09:10:48.090""#.into()) + ); + assert_eq!( + to_string(&NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap()) + .ok(), + Some(r#""2014-07-24T12:34:06""#.into()) + ); + assert_eq!( + to_string( + &NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap() + ) + .ok(), + Some(r#""0000-01-01T00:00:60""#.into()) + ); + assert_eq!( + to_string( + &NaiveDate::from_ymd_opt(-1, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 7).unwrap() + ) + .ok(), + Some(r#""-0001-12-31T23:59:59.000000007""#.into()) + ); + assert_eq!( + to_string(&NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap()).ok(), + Some(r#""-262144-01-01T00:00:00""#.into()) + ); + assert_eq!( + to_string(&NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()).ok(), + Some(r#""+262143-12-31T23:59:60.999999999""#.into()) + ); +} + +#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] +fn test_decodable_json<F, E>(from_str: F) +where + F: Fn(&str) -> Result<NaiveDateTime, E>, + E: ::std::fmt::Debug, +{ + assert_eq!( + from_str(r#""2016-07-08T09:10:48.090""#).ok(), + Some( + NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap() + ) + ); + assert_eq!( + from_str(r#""2016-7-8T9:10:48.09""#).ok(), + Some( + NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap() + ) + ); + assert_eq!( + from_str(r#""2014-07-24T12:34:06""#).ok(), + Some(NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap()) + ); + assert_eq!( + from_str(r#""0000-01-01T00:00:60""#).ok(), + Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap()) + ); + assert_eq!( + from_str(r#""0-1-1T0:0:60""#).ok(), + Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().and_hms_milli_opt(0, 0, 59, 1_000).unwrap()) + ); + assert_eq!( + from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), + Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 7).unwrap()) + ); + assert_eq!( + from_str(r#""-262144-01-01T00:00:00""#).ok(), + Some(NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap()) + ); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.999999999""#).ok(), + Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) + ); + assert_eq!( + from_str(r#""+262143-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored + Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) + ); + + // bad formats + assert!(from_str(r#""""#).is_err()); + assert!(from_str(r#""2016-07-08""#).is_err()); + assert!(from_str(r#""09:10:48.090""#).is_err()); + assert!(from_str(r#""20160708T091048.090""#).is_err()); + assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); + assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); + assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); + assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); + assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); + assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); + assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); + assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); + assert!(from_str(r#"20160708000000"#).is_err()); + assert!(from_str(r#"{}"#).is_err()); + // pre-0.3.0 rustc-serialize format is now invalid + assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); + assert!(from_str(r#"null"#).is_err()); +} + +#[cfg(all(test, feature = "rustc-serialize"))] +fn test_decodable_json_timestamp<F, E>(from_str: F) +where + F: Fn(&str) -> Result<rustc_serialize::TsSeconds, E>, + E: ::std::fmt::Debug, +{ + assert_eq!( + *from_str("0").unwrap(), + NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(), + "should parse integers as timestamps" + ); + assert_eq!( + *from_str("-1").unwrap(), + NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 59).unwrap(), + "should parse integers as timestamps" + ); +} diff --git a/vendor/chrono/src/naive/datetime/rustc_serialize.rs b/vendor/chrono/src/naive/datetime/rustc_serialize.rs new file mode 100644 index 000000000..6e33829e0 --- /dev/null +++ b/vendor/chrono/src/naive/datetime/rustc_serialize.rs @@ -0,0 +1,73 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "rustc-serialize")))] + +use super::NaiveDateTime; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use std::ops::Deref; + +impl Encodable for NaiveDateTime { + fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } +} + +impl Decodable for NaiveDateTime { + fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDateTime, D::Error> { + d.read_str()?.parse().map_err(|_| d.error("invalid date time string")) + } +} + +/// A `DateTime` that can be deserialized from a seconds-based timestamp +#[derive(Debug)] +#[deprecated( + since = "1.4.2", + note = "RustcSerialize will be removed before chrono 1.0, use Serde instead" +)] +pub struct TsSeconds(NaiveDateTime); + +#[allow(deprecated)] +impl From<TsSeconds> for NaiveDateTime { + /// Pull the internal NaiveDateTime out + #[allow(deprecated)] + fn from(obj: TsSeconds) -> NaiveDateTime { + obj.0 + } +} + +#[allow(deprecated)] +impl Deref for TsSeconds { + type Target = NaiveDateTime; + + #[allow(deprecated)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[allow(deprecated)] +impl Decodable for TsSeconds { + #[allow(deprecated)] + fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds, D::Error> { + Ok(TsSeconds( + NaiveDateTime::from_timestamp_opt(d.read_i64()?, 0) + .ok_or_else(|| d.error("invalid timestamp"))?, + )) + } +} + +#[cfg(test)] +use rustc_serialize::json; + +#[test] +fn test_encodable() { + super::test_encodable_json(json::encode); +} + +#[test] +fn test_decodable() { + super::test_decodable_json(json::decode); +} + +#[test] +fn test_decodable_timestamps() { + super::test_decodable_json_timestamp(json::decode); +} diff --git a/vendor/chrono/src/naive/datetime/serde.rs b/vendor/chrono/src/naive/datetime/serde.rs new file mode 100644 index 000000000..40695fa74 --- /dev/null +++ b/vendor/chrono/src/naive/datetime/serde.rs @@ -0,0 +1,1133 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "serde")))] + +use core::fmt; +use serde::{de, ser}; + +use super::NaiveDateTime; +use crate::offset::LocalResult; + +/// Serialize a `NaiveDateTime` as an RFC 3339 string +/// +/// See [the `serde` module](./serde/index.html) for alternate +/// serialization formats. +impl ser::Serialize for NaiveDateTime { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + struct FormatWrapped<'a, D: 'a> { + inner: &'a D, + } + + impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } + } + + serializer.collect_str(&FormatWrapped { inner: &self }) + } +} + +struct NaiveDateTimeVisitor; + +impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a formatted date and time string") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + value.parse().map_err(E::custom) + } +} + +impl<'de> de::Deserialize<'de> for NaiveDateTime { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(NaiveDateTimeVisitor) + } +} + +/// Used to serialize/deserialize from nanosecond-precision timestamps +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_nanoseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_nanoseconds")] +/// time: NaiveDateTime +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_nanoseconds { + use core::fmt; + use serde::{de, ser}; + + use super::ne_timestamp; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of nanoseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_nanoseconds::serialize as to_nano_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_nanos()) + } + + /// Deserialize a `NaiveDateTime` from a nanoseconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::NaiveDateTime; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_nanoseconds::deserialize as from_nano_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimestampVisitor) + } + + pub(super) struct NanoSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt(value / 1_000_000_000, (value % 1_000_000_000) as u32) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + value as i64 / 1_000_000_000, + (value as i64 % 1_000_000_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } +} + +/// Ser/de to/from optional timestamps in nanoseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::naive::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_nanoseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_nanoseconds_option")] +/// time: Option<NaiveDateTime> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_nanoseconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::ts_nanoseconds::NanoSecondsTimestampVisitor; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of nanoseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_nano_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_nano_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionNanoSecondsTimestampVisitor) + } + + struct OptionNanoSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { + type Value = Option<NaiveDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in nanoseconds or none") + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in nanoseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Used to serialize/deserialize from microsecond-precision timestamps +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_microseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_microseconds")] +/// time: NaiveDateTime +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_microseconds { + use core::fmt; + use serde::{de, ser}; + + use super::ne_timestamp; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of microseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_microseconds::serialize as to_micro_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_micros()) + } + + /// Deserialize a `NaiveDateTime` from a microseconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::NaiveDateTime; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_microseconds::deserialize as from_micro_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimestampVisitor) + } + + pub(super) struct MicroSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for MicroSecondsTimestampVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + value / 1_000_000, + ((value % 1_000_000) * 1000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + (value / 1_000_000) as i64, + ((value % 1_000_000) * 1_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } +} + +/// Ser/de to/from optional timestamps in microseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::naive::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_microseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_microseconds_option")] +/// time: Option<NaiveDateTime> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_microseconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::ts_microseconds::MicroSecondsTimestampVisitor; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of microseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_microseconds_option::serialize as to_micro_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_micro_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_microseconds_option::deserialize as from_micro_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_micro_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionMicroSecondsTimestampVisitor) + } + + struct OptionMicroSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { + type Value = Option<NaiveDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in microseconds or none") + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in microseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Used to serialize/deserialize from millisecond-precision timestamps +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_milliseconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_milliseconds")] +/// time: NaiveDateTime +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_milliseconds { + use core::fmt; + use serde::{de, ser}; + + use super::ne_timestamp; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of milliseconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_milliseconds::serialize as to_milli_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp_millis()) + } + + /// Deserialize a `NaiveDateTime` from a milliseconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::NaiveDateTime; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_milliseconds::deserialize as from_milli_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimestampVisitor) + } + + pub(super) struct MilliSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt(value / 1000, ((value % 1000) * 1_000_000) as u32) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt( + (value / 1000) as i64, + ((value % 1000) * 1_000_000) as u32, + ) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } +} + +/// Ser/de to/from optional timestamps in milliseconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::naive::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_milliseconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_milliseconds_option")] +/// time: Option<NaiveDateTime> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699918}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_milliseconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::ts_milliseconds::MilliSecondsTimestampVisitor; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of milliseconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_milliseconds_option::serialize as to_milli_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_milli_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699918}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_milli_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionMilliSecondsTimestampVisitor) + } + + struct OptionMilliSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { + type Value = Option<NaiveDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in milliseconds or none") + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in milliseconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +/// Used to serialize/deserialize from second-precision timestamps +/// +/// # Example: +/// +/// ```rust +/// # use chrono::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_seconds; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_seconds")] +/// time: NaiveDateTime +/// } +/// +/// let time = NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1431684000}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_seconds { + use core::fmt; + use serde::{de, ser}; + + use super::ne_timestamp; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of seconds since the epoch + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_seconds::serialize as to_ts; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s = S { + /// time: NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1431684000}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.serialize_i64(dt.timestamp()) + } + + /// Deserialize a `NaiveDateTime` from a seconds timestamp + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::NaiveDateTime; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_ts")] + /// time: NaiveDateTime + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimestampVisitor) + } + + pub(super) struct SecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for SecondsTimestampVisitor { + type Value = NaiveDateTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp") + } + + fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt(value, 0) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + + fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> + where + E: de::Error, + { + NaiveDateTime::from_timestamp_opt(value as i64, 0) + .ok_or_else(|| E::custom(ne_timestamp(value))) + } + } +} + +/// Ser/de to/from optional timestamps in seconds +/// +/// Intended for use with `serde`'s `with` attribute. +/// +/// # Example: +/// +/// ```rust +/// # use chrono::naive::{NaiveDate, NaiveDateTime}; +/// # use serde_derive::{Deserialize, Serialize}; +/// use chrono::naive::serde::ts_seconds_option; +/// #[derive(Deserialize, Serialize)] +/// struct S { +/// #[serde(with = "ts_seconds_option")] +/// time: Option<NaiveDateTime> +/// } +/// +/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap()); +/// let my_s = S { +/// time: time.clone(), +/// }; +/// +/// let as_string = serde_json::to_string(&my_s)?; +/// assert_eq!(as_string, r#"{"time":1526522699}"#); +/// let my_s: S = serde_json::from_str(&as_string)?; +/// assert_eq!(my_s.time, time); +/// # Ok::<(), serde_json::Error>(()) +/// ``` +pub mod ts_seconds_option { + use core::fmt; + use serde::{de, ser}; + + use super::ts_seconds::SecondsTimestampVisitor; + use crate::NaiveDateTime; + + /// Serialize a datetime into an integer number of seconds since the epoch or none + /// + /// Intended for use with `serde`s `serialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Serialize; + /// use chrono::naive::serde::ts_seconds_option::serialize as to_tsopt; + /// #[derive(Serialize)] + /// struct S { + /// #[serde(serialize_with = "to_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s = S { + /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap()), + /// }; + /// let as_string = serde_json::to_string(&my_s)?; + /// assert_eq!(as_string, r#"{"time":1526522699}"#); + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + match *opt { + Some(ref dt) => serializer.serialize_some(&dt.timestamp()), + None => serializer.serialize_none(), + } + } + + /// Deserialize a `NaiveDateTime` from a second timestamp or none + /// + /// Intended for use with `serde`s `deserialize_with` attribute. + /// + /// # Example: + /// + /// ```rust + /// # use chrono::naive::{NaiveDate, NaiveDateTime}; + /// # use serde_derive::Deserialize; + /// use chrono::naive::serde::ts_seconds_option::deserialize as from_tsopt; + /// #[derive(Deserialize)] + /// struct S { + /// #[serde(deserialize_with = "from_tsopt")] + /// time: Option<NaiveDateTime> + /// } + /// + /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; + /// # Ok::<(), serde_json::Error>(()) + /// ``` + pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_option(OptionSecondsTimestampVisitor) + } + + struct OptionSecondsTimestampVisitor; + + impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { + type Value = Option<NaiveDateTime>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a unix timestamp in seconds or none") + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error> + where + D: de::Deserializer<'de>, + { + d.deserialize_i64(SecondsTimestampVisitor).map(Some) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_none<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + + /// Deserialize a timestamp in seconds since the epoch + fn visit_unit<E>(self) -> Result<Self::Value, E> + where + E: de::Error, + { + Ok(None) + } + } +} + +#[test] +fn test_serde_serialize() { + super::test_encodable_json(serde_json::to_string); +} + +#[test] +fn test_serde_deserialize() { + super::test_decodable_json(|input| serde_json::from_str(input)); +} + +// Bincode is relevant to test separately from JSON because +// it is not self-describing. +#[test] +fn test_serde_bincode() { + use crate::NaiveDate; + use bincode::{deserialize, serialize}; + + let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap(); + let encoded = serialize(&dt).unwrap(); + let decoded: NaiveDateTime = deserialize(&encoded).unwrap(); + assert_eq!(dt, decoded); +} + +#[test] +fn test_serde_bincode_optional() { + use crate::prelude::*; + use crate::serde::ts_nanoseconds_option; + use bincode::{deserialize, serialize}; + use serde_derive::{Deserialize, Serialize}; + + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Test { + one: Option<i64>, + #[serde(with = "ts_nanoseconds_option")] + two: Option<DateTime<Utc>>, + } + + let expected = + Test { one: Some(1), two: Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap()) }; + let bytes: Vec<u8> = serialize(&expected).unwrap(); + let actual = deserialize::<Test>(&(bytes)).unwrap(); + + assert_eq!(expected, actual); +} + +// lik? function to convert a LocalResult into a serde-ish Result +pub(crate) fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E> +where + E: de::Error, + V: fmt::Display, + T: fmt::Display, +{ + match me { + LocalResult::None => Err(E::custom(ne_timestamp(ts))), + LocalResult::Ambiguous(min, max) => { + Err(E::custom(SerdeError::Ambiguous { timestamp: ts, min, max })) + } + LocalResult::Single(val) => Ok(val), + } +} + +enum SerdeError<V: fmt::Display, D: fmt::Display> { + NonExistent { timestamp: V }, + Ambiguous { timestamp: V, min: D, max: D }, +} + +/// Construct a [`SerdeError::NonExistent`] +fn ne_timestamp<T: fmt::Display>(ts: T) -> SerdeError<T, u8> { + SerdeError::NonExistent::<T, u8> { timestamp: ts } +} + +impl<V: fmt::Display, D: fmt::Display> fmt::Debug for SerdeError<V, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChronoSerdeError({})", self) + } +} + +// impl<V: fmt::Display, D: fmt::Debug> core::error::Error for SerdeError<V, D> {} +impl<V: fmt::Display, D: fmt::Display> fmt::Display for SerdeError<V, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SerdeError::NonExistent { timestamp } => { + write!(f, "value is not a legal timestamp: {}", timestamp) + } + SerdeError::Ambiguous { timestamp, min, max } => write!( + f, + "value is an ambiguous timestamp: {}, could be either of {}, {}", + timestamp, min, max + ), + } + } +} diff --git a/vendor/chrono/src/naive/datetime/tests.rs b/vendor/chrono/src/naive/datetime/tests.rs new file mode 100644 index 000000000..202bdb34d --- /dev/null +++ b/vendor/chrono/src/naive/datetime/tests.rs @@ -0,0 +1,343 @@ +use super::NaiveDateTime; +use crate::oldtime::Duration; +use crate::NaiveDate; +use crate::{Datelike, FixedOffset, Utc}; +use std::i64; + +#[test] +fn test_datetime_from_timestamp_millis() { + let valid_map = [ + (1662921288000, "2022-09-11 18:34:48.000000000"), + (1662921288123, "2022-09-11 18:34:48.123000000"), + (1662921287890, "2022-09-11 18:34:47.890000000"), + (-2208936075000, "1900-01-01 14:38:45.000000000"), + (0, "1970-01-01 00:00:00.000000000"), + (119731017000, "1973-10-17 18:36:57.000000000"), + (1234567890000, "2009-02-13 23:31:30.000000000"), + (2034061609000, "2034-06-16 09:06:49.000000000"), + ]; + + for (timestamp_millis, formatted) in valid_map.iter().copied() { + let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); + assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis()); + assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), formatted); + } + + let invalid = [i64::MAX, i64::MIN]; + + for timestamp_millis in invalid.iter().copied() { + let naive_datetime = NaiveDateTime::from_timestamp_millis(timestamp_millis); + assert!(naive_datetime.is_none()); + } + + // Test that the result of `from_timestamp_millis` compares equal to + // that of `from_timestamp_opt`. + let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; + for secs in secs_test.iter().cloned() { + assert_eq!( + NaiveDateTime::from_timestamp_millis(secs * 1000), + NaiveDateTime::from_timestamp_opt(secs, 0) + ); + } +} + +#[test] +fn test_datetime_from_timestamp_micros() { + let valid_map = [ + (1662921288000000, "2022-09-11 18:34:48.000000000"), + (1662921288123456, "2022-09-11 18:34:48.123456000"), + (1662921287890000, "2022-09-11 18:34:47.890000000"), + (-2208936075000000, "1900-01-01 14:38:45.000000000"), + (0, "1970-01-01 00:00:00.000000000"), + (119731017000000, "1973-10-17 18:36:57.000000000"), + (1234567890000000, "2009-02-13 23:31:30.000000000"), + (2034061609000000, "2034-06-16 09:06:49.000000000"), + ]; + + for (timestamp_micros, formatted) in valid_map.iter().copied() { + let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); + assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros()); + assert_eq!(naive_datetime.unwrap().format("%F %T%.9f").to_string(), formatted); + } + + let invalid = [i64::MAX, i64::MIN]; + + for timestamp_micros in invalid.iter().copied() { + let naive_datetime = NaiveDateTime::from_timestamp_micros(timestamp_micros); + assert!(naive_datetime.is_none()); + } + + // Test that the result of `from_timestamp_micros` compares equal to + // that of `from_timestamp_opt`. + let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; + for secs in secs_test.iter().copied() { + assert_eq!( + NaiveDateTime::from_timestamp_micros(secs * 1_000_000), + NaiveDateTime::from_timestamp_opt(secs, 0) + ); + } +} + +#[test] +fn test_datetime_from_timestamp() { + let from_timestamp = |secs| NaiveDateTime::from_timestamp_opt(secs, 0); + let ymdhms = + |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + assert_eq!(from_timestamp(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); + assert_eq!(from_timestamp(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); + assert_eq!(from_timestamp(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); + assert_eq!(from_timestamp(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); + assert_eq!(from_timestamp(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); + assert_eq!(from_timestamp(i64::MIN), None); + assert_eq!(from_timestamp(i64::MAX), None); +} + +#[test] +fn test_datetime_add() { + fn check( + (y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32), + rhs: Duration, + result: Option<(i32, u32, u32, u32, u32, u32)>, + ) { + let lhs = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + let sum = result.map(|(y, m, d, h, n, s)| { + NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap() + }); + assert_eq!(lhs.checked_add_signed(rhs), sum); + assert_eq!(lhs.checked_sub_signed(-rhs), sum); + } + + check((2014, 5, 6, 7, 8, 9), Duration::seconds(3600 + 60 + 1), Some((2014, 5, 6, 8, 9, 10))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(-(3600 + 60 + 1)), Some((2014, 5, 6, 6, 7, 8))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86399), Some((2014, 5, 7, 7, 8, 8))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9))); + check((2014, 5, 6, 7, 8, 9), Duration::seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); + + // overflow check + // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`. + // (they are private constants, but the equivalence is tested in that module.) + let max_days_from_year_0 = + NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()); + check((0, 1, 1, 0, 0, 0), max_days_from_year_0, Some((NaiveDate::MAX.year(), 12, 31, 0, 0, 0))); + check( + (0, 1, 1, 0, 0, 0), + max_days_from_year_0 + Duration::seconds(86399), + Some((NaiveDate::MAX.year(), 12, 31, 23, 59, 59)), + ); + check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + Duration::seconds(86_400), None); + check((0, 1, 1, 0, 0, 0), Duration::max_value(), None); + + let min_days_from_year_0 = + NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()); + check((0, 1, 1, 0, 0, 0), min_days_from_year_0, Some((NaiveDate::MIN.year(), 1, 1, 0, 0, 0))); + check((0, 1, 1, 0, 0, 0), min_days_from_year_0 - Duration::seconds(1), None); + check((0, 1, 1, 0, 0, 0), Duration::min_value(), None); +} + +#[test] +fn test_datetime_sub() { + let ymdhms = + |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + let since = NaiveDateTime::signed_duration_since; + assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), Duration::zero()); + assert_eq!( + since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)), + Duration::seconds(1) + ); + assert_eq!( + since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), + Duration::seconds(-1) + ); + assert_eq!( + since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), + Duration::seconds(86399) + ); + assert_eq!( + since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)), + Duration::seconds(999_999_999) + ); +} + +#[test] +fn test_datetime_addassignment() { + let ymdhms = + |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + let mut date = ymdhms(2016, 10, 1, 10, 10, 10); + date += Duration::minutes(10_000_000); + assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10)); + date += Duration::days(10); + assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10)); +} + +#[test] +fn test_datetime_subassignment() { + let ymdhms = + |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + let mut date = ymdhms(2016, 10, 1, 10, 10, 10); + date -= Duration::minutes(10_000_000); + assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10)); + date -= Duration::days(10); + assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10)); +} + +#[test] +fn test_datetime_timestamp() { + let to_timestamp = |y, m, d, h, n, s| { + NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().timestamp() + }; + assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); + assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); + assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); + assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); +} + +#[test] +fn test_datetime_from_str() { + // valid cases + let valid = [ + "2015-2-18T23:16:9.15", + "-77-02-18T23:16:09", + " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", + ]; + for &s in &valid { + let d = match s.parse::<NaiveDateTime>() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::<NaiveDateTime>() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + assert!("".parse::<NaiveDateTime>().is_err()); + assert!("x".parse::<NaiveDateTime>().is_err()); + assert!("15".parse::<NaiveDateTime>().is_err()); + assert!("15:8:9".parse::<NaiveDateTime>().is_err()); + assert!("15-8-9".parse::<NaiveDateTime>().is_err()); + assert!("2015-15-15T15:15:15".parse::<NaiveDateTime>().is_err()); + assert!("2012-12-12T12:12:12x".parse::<NaiveDateTime>().is_err()); + assert!("2012-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); + assert!("+ 82701-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); + assert!("+802701-123-12T12:12:12".parse::<NaiveDateTime>().is_err()); // out-of-bound +} + +#[test] +fn test_datetime_parse_from_str() { + let ymdhms = + |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); + let ymdhmsn = |y, m, d, h, n, s, nano| { + NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap() + }; + assert_eq!( + NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56)) + ); // ignore offset + assert_eq!( + NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), + Ok(ymdhms(2015, 2, 2, 0, 0, 0)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), + Ok(ymdhms(2013, 8, 9, 23, 54, 35)) + ); + assert!(NaiveDateTime::parse_from_str( + "Sat, 09 Aug 2013 23:54:35 GMT", + "%a, %d %b %Y %H:%M:%S GMT" + ) + .is_err()); + assert!(NaiveDateTime::parse_from_str("2014-5-7 12:3456", "%Y-%m-%d %H:%M:%S").is_err()); + assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient + assert_eq!( + NaiveDateTime::parse_from_str("1441497364", "%s"), + Ok(ymdhms(2015, 9, 5, 23, 56, 4)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"), + Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"), + Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"), + Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) + ); + assert_eq!( + NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"), + Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) + ); +} + +#[test] +fn test_datetime_format() { + let dt = NaiveDate::from_ymd_opt(2010, 9, 8).unwrap().and_hms_milli_opt(7, 6, 54, 321).unwrap(); + assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010"); + assert_eq!(dt.format("%s").to_string(), "1283929614"); + assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); + + // a horror of leap second: coming near to you. + let dt = + NaiveDate::from_ymd_opt(2012, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap(); + assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012"); + assert_eq!(dt.format("%s").to_string(), "1341100799"); // not 1341100800, it's intentional. +} + +#[test] +fn test_datetime_add_sub_invariant() { + // issue #37 + let base = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + let t = -946684799990000; + let time = base + Duration::microseconds(t); + assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap()); +} + +#[test] +fn test_nanosecond_range() { + const A_BILLION: i64 = 1_000_000_000; + let maximum = "2262-04-11T23:47:16.854775804"; + let parsed: NaiveDateTime = maximum.parse().unwrap(); + let nanos = parsed.timestamp_nanos(); + assert_eq!( + parsed, + NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); + + let minimum = "1677-09-21T00:12:44.000000000"; + let parsed: NaiveDateTime = minimum.parse().unwrap(); + let nanos = parsed.timestamp_nanos(); + assert_eq!( + parsed, + NaiveDateTime::from_timestamp_opt(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() + ); +} + +#[test] +fn test_and_timezone() { + let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap(); + let dt_utc = ndt.and_local_timezone(Utc).unwrap(); + assert_eq!(dt_utc.naive_local(), ndt); + assert_eq!(dt_utc.timezone(), Utc); + + let offset_tz = FixedOffset::west_opt(4 * 3600).unwrap(); + let dt_offset = ndt.and_local_timezone(offset_tz).unwrap(); + assert_eq!(dt_offset.naive_local(), ndt); + assert_eq!(dt_offset.timezone(), offset_tz); +} diff --git a/vendor/chrono/src/naive/internals.rs b/vendor/chrono/src/naive/internals.rs index 346063c37..05305b506 100644 --- a/vendor/chrono/src/naive/internals.rs +++ b/vendor/chrono/src/naive/internals.rs @@ -13,19 +13,18 @@ //! but the conversion keeps the valid value valid and the invalid value invalid //! so that the user-facing `NaiveDate` can validate the input as late as possible. -#![allow(dead_code)] // some internal methods have been left for consistency #![cfg_attr(feature = "__internal_bench", allow(missing_docs))] +use crate::Weekday; use core::{fmt, i32}; -use div::{div_rem, mod_floor}; +use num_integer::{div_rem, mod_floor}; use num_traits::FromPrimitive; -use Weekday; /// The internal date representation. This also includes the packed `Mdf` value. -pub type DateImpl = i32; +pub(super) type DateImpl = i32; -pub const MAX_YEAR: DateImpl = i32::MAX >> 13; -pub const MIN_YEAR: DateImpl = i32::MIN >> 13; +pub(super) const MAX_YEAR: DateImpl = i32::MAX >> 13; +pub(super) const MIN_YEAR: DateImpl = i32::MIN >> 13; /// The year flags (aka the dominical letter). /// @@ -35,23 +34,24 @@ pub const MIN_YEAR: DateImpl = i32::MIN >> 13; /// where `a` is `1` for the common year (simplifies the `Of` validation) /// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year /// (simplifies the day of week calculation from the 1-based ordinal). -#[derive(PartialEq, Eq, Copy, Clone)] -pub struct YearFlags(pub u8); - -pub const A: YearFlags = YearFlags(0o15); -pub const AG: YearFlags = YearFlags(0o05); -pub const B: YearFlags = YearFlags(0o14); -pub const BA: YearFlags = YearFlags(0o04); -pub const C: YearFlags = YearFlags(0o13); -pub const CB: YearFlags = YearFlags(0o03); -pub const D: YearFlags = YearFlags(0o12); -pub const DC: YearFlags = YearFlags(0o02); -pub const E: YearFlags = YearFlags(0o11); -pub const ED: YearFlags = YearFlags(0o01); -pub const F: YearFlags = YearFlags(0o17); -pub const FE: YearFlags = YearFlags(0o07); -pub const G: YearFlags = YearFlags(0o16); -pub const GF: YearFlags = YearFlags(0o06); +#[allow(unreachable_pub)] // public as an alias for benchmarks only +#[derive(PartialEq, Eq, Copy, Clone, Hash)] +pub struct YearFlags(pub(super) u8); + +pub(super) const A: YearFlags = YearFlags(0o15); +pub(super) const AG: YearFlags = YearFlags(0o05); +pub(super) const B: YearFlags = YearFlags(0o14); +pub(super) const BA: YearFlags = YearFlags(0o04); +pub(super) const C: YearFlags = YearFlags(0o13); +pub(super) const CB: YearFlags = YearFlags(0o03); +pub(super) const D: YearFlags = YearFlags(0o12); +pub(super) const DC: YearFlags = YearFlags(0o02); +pub(super) const E: YearFlags = YearFlags(0o11); +pub(super) const ED: YearFlags = YearFlags(0o01); +pub(super) const F: YearFlags = YearFlags(0o17); +pub(super) const FE: YearFlags = YearFlags(0o07); +pub(super) const G: YearFlags = YearFlags(0o16); +pub(super) const GF: YearFlags = YearFlags(0o06); static YEAR_TO_FLAGS: [YearFlags; 400] = [ BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, @@ -94,7 +94,7 @@ static YEAR_DELTAS: [u8; 401] = [ 96, 97, 97, 97, 97, // 400+1 ]; -pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { +pub(super) fn cycle_to_yo(cycle: u32) -> (u32, u32) { let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365); let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]); if ordinal0 < delta { @@ -106,11 +106,13 @@ pub fn cycle_to_yo(cycle: u32) -> (u32, u32) { (year_mod_400, ordinal0 + 1) } -pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { +pub(super) fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1 } impl YearFlags { + #[allow(unreachable_pub)] // public as an alias for benchmarks only + #[doc(hidden)] // for benchmarks only #[inline] pub fn from_year(year: i32) -> YearFlags { let year = mod_floor(year, 400); @@ -118,18 +120,18 @@ impl YearFlags { } #[inline] - pub fn from_year_mod_400(year: i32) -> YearFlags { + pub(super) fn from_year_mod_400(year: i32) -> YearFlags { YEAR_TO_FLAGS[year as usize] } #[inline] - pub fn ndays(&self) -> u32 { + pub(super) fn ndays(&self) -> u32 { let YearFlags(flags) = *self; 366 - u32::from(flags >> 3) } #[inline] - pub fn isoweek_delta(&self) -> u32 { + pub(super) fn isoweek_delta(&self) -> u32 { let YearFlags(flags) = *self; let mut delta = u32::from(flags) & 0b0111; if delta < 3 { @@ -139,7 +141,7 @@ impl YearFlags { } #[inline] - pub fn nisoweeks(&self) -> u32 { + pub(super) const fn nisoweeks(&self) -> u32 { let YearFlags(flags) = *self; 52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1) } @@ -170,10 +172,9 @@ impl fmt::Debug for YearFlags { } } -pub const MIN_OL: u32 = 1 << 1; -pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` -pub const MIN_MDL: u32 = (1 << 6) | (1 << 1); -pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; +pub(super) const MIN_OL: u32 = 1 << 1; +pub(super) const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1` +pub(super) const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; const XX: i8 = -128; static MDL_TO_OL: [i8; MAX_MDL as usize + 1] = [ @@ -265,26 +266,19 @@ static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [ /// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag), /// which is an index to the `OL_TO_MDL` lookup table. #[derive(PartialEq, PartialOrd, Copy, Clone)] -pub struct Of(pub u32); +pub(super) struct Of(pub(crate) u32); impl Of { #[inline] - fn clamp_ordinal(ordinal: u32) -> u32 { - if ordinal > 366 { - 0 - } else { - ordinal + pub(super) fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> { + match ordinal <= 366 { + true => Some(Of((ordinal << 4) | u32::from(flags))), + false => None, } } #[inline] - pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - Of((ordinal << 4) | u32::from(flags)) - } - - #[inline] - pub fn from_mdf(Mdf(mdf): Mdf) -> Of { + pub(super) fn from_mdf(Mdf(mdf): Mdf) -> Of { let mdl = mdf >> 3; match MDL_TO_OL.get(mdl as usize) { Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)), @@ -293,64 +287,62 @@ impl Of { } #[inline] - pub fn valid(&self) -> bool { + pub(super) fn valid(&self) -> bool { let Of(of) = *self; let ol = of >> 3; - MIN_OL <= ol && ol <= MAX_OL + (MIN_OL..=MAX_OL).contains(&ol) } #[inline] - pub fn ordinal(&self) -> u32 { + pub(super) const fn ordinal(&self) -> u32 { let Of(of) = *self; of >> 4 } #[inline] - pub fn with_ordinal(&self, ordinal: u32) -> Of { - let ordinal = Of::clamp_ordinal(ordinal); - let Of(of) = *self; - Of((of & 0b1111) | (ordinal << 4)) - } + pub(super) fn with_ordinal(&self, ordinal: u32) -> Option<Of> { + if ordinal > 366 { + return None; + } - #[inline] - pub fn flags(&self) -> YearFlags { let Of(of) = *self; - YearFlags((of & 0b1111) as u8) + Some(Of((of & 0b1111) | (ordinal << 4))) } #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of { + pub(super) const fn flags(&self) -> YearFlags { let Of(of) = *self; - Of((of & !0b1111) | u32::from(flags)) + YearFlags((of & 0b1111) as u8) } #[inline] - pub fn weekday(&self) -> Weekday { + pub(super) fn weekday(&self) -> Weekday { let Of(of) = *self; Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap() } #[inline] - pub fn isoweekdate_raw(&self) -> (u32, Weekday) { + pub(super) fn isoweekdate_raw(&self) -> (u32, Weekday) { // week ordinal = ordinal + delta let Of(of) = *self; let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta()); (weekord / 7, Weekday::from_u32(weekord % 7).unwrap()) } + #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))] #[inline] - pub fn to_mdf(&self) -> Mdf { + pub(super) fn to_mdf(&self) -> Mdf { Mdf::from_of(*self) } #[inline] - pub fn succ(&self) -> Of { + pub(super) const fn succ(&self) -> Of { let Of(of) = *self; Of(of + (1 << 4)) } #[inline] - pub fn pred(&self) -> Of { + pub(super) const fn pred(&self) -> Of { let Of(of) = *self; Of(of - (1 << 4)) } @@ -375,36 +367,19 @@ impl fmt::Debug for Of { /// (month, day of month and leap flag), /// which is an index to the `MDL_TO_OL` lookup table. #[derive(PartialEq, PartialOrd, Copy, Clone)] -pub struct Mdf(pub u32); +pub(super) struct Mdf(pub(super) u32); impl Mdf { #[inline] - fn clamp_month(month: u32) -> u32 { - if month > 12 { - 0 - } else { - month - } - } - - #[inline] - fn clamp_day(day: u32) -> u32 { - if day > 31 { - 0 - } else { - day + pub(super) fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> { + match month <= 12 && day <= 31 { + true => Some(Mdf((month << 9) | (day << 4) | u32::from(flags))), + false => None, } } #[inline] - pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf { - let month = Mdf::clamp_month(month); - let day = Mdf::clamp_day(day); - Mdf((month << 9) | (day << 4) | u32::from(flags)) - } - - #[inline] - pub fn from_of(Of(of): Of) -> Mdf { + pub(super) fn from_of(Of(of): Of) -> Mdf { let ol = of >> 3; match OL_TO_MDL.get(ol as usize) { Some(&v) => Mdf(of + (u32::from(v) << 3)), @@ -412,8 +387,8 @@ impl Mdf { } } - #[inline] - pub fn valid(&self) -> bool { + #[cfg(test)] + pub(super) fn valid(&self) -> bool { let Mdf(mdf) = *self; let mdl = mdf >> 3; match MDL_TO_OL.get(mdl as usize) { @@ -423,45 +398,46 @@ impl Mdf { } #[inline] - pub fn month(&self) -> u32 { + pub(super) const fn month(&self) -> u32 { let Mdf(mdf) = *self; mdf >> 9 } #[inline] - pub fn with_month(&self, month: u32) -> Mdf { - let month = Mdf::clamp_month(month); + pub(super) fn with_month(&self, month: u32) -> Option<Mdf> { + if month > 12 { + return None; + } + let Mdf(mdf) = *self; - Mdf((mdf & 0b1_1111_1111) | (month << 9)) + Some(Mdf((mdf & 0b1_1111_1111) | (month << 9))) } #[inline] - pub fn day(&self) -> u32 { + pub(super) const fn day(&self) -> u32 { let Mdf(mdf) = *self; (mdf >> 4) & 0b1_1111 } #[inline] - pub fn with_day(&self, day: u32) -> Mdf { - let day = Mdf::clamp_day(day); - let Mdf(mdf) = *self; - Mdf((mdf & !0b1_1111_0000) | (day << 4)) - } + pub(super) fn with_day(&self, day: u32) -> Option<Mdf> { + if day > 31 { + return None; + } - #[inline] - pub fn flags(&self) -> YearFlags { let Mdf(mdf) = *self; - YearFlags((mdf & 0b1111) as u8) + Some(Mdf((mdf & !0b1_1111_0000) | (day << 4))) } #[inline] - pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { + pub(super) fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { let Mdf(mdf) = *self; Mdf((mdf & !0b1111) | u32::from(flags)) } + #[cfg_attr(feature = "cargo-clippy", allow(clippy::wrong_self_convention))] #[inline] - pub fn to_of(&self) -> Of { + pub(super) fn to_of(&self) -> Of { Of::from_mdf(*self) } } @@ -482,14 +458,12 @@ impl fmt::Debug for Mdf { #[cfg(test)] mod tests { - #[cfg(test)] - extern crate num_iter; + use num_iter::range_inclusive; + use std::u32; - use self::num_iter::range_inclusive; use super::{Mdf, Of}; use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF}; - use std::u32; - use Weekday; + use crate::Weekday; const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; @@ -534,7 +508,12 @@ mod tests { fn test_of() { fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) { for ordinal in range_inclusive(ordinal1, ordinal2) { - let of = Of::new(ordinal, flags); + let of = match Of::new(ordinal, flags) { + Some(of) => of, + None if !expected => continue, + None => panic!("Of::new({}, {:?}) returned None", ordinal, flags), + }; + assert!( of.valid() == expected, "ordinal {} = {:?} should be {} for dominical year {:?}", @@ -566,7 +545,12 @@ mod tests { fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) { for month in range_inclusive(month1, month2) { for day in range_inclusive(day1, day2) { - let mdf = Mdf::new(month, day, flags); + let mdf = match Mdf::new(month, day, flags) { + Some(mdf) => mdf, + None if !expected => continue, + None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags), + }; + assert!( mdf.valid() == expected, "month {} day {} = {:?} should be {} for dominical year {:?}", @@ -651,7 +635,7 @@ mod tests { fn test_of_fields() { for &flags in FLAGS.iter() { for ordinal in range_inclusive(1u32, 366) { - let of = Of::new(ordinal, flags); + let of = Of::new(ordinal, flags).unwrap(); if of.valid() { assert_eq!(of.ordinal(), ordinal); } @@ -662,11 +646,16 @@ mod tests { #[test] fn test_of_with_fields() { fn check(flags: YearFlags, ordinal: u32) { - let of = Of::new(ordinal, flags); + let of = Of::new(ordinal, flags).unwrap(); for ordinal in range_inclusive(0u32, 1024) { - let of = of.with_ordinal(ordinal); - assert_eq!(of.valid(), Of::new(ordinal, flags).valid()); + let of = match of.with_ordinal(ordinal) { + Some(of) => of, + None if ordinal > 366 => continue, + None => panic!("failed to create Of with ordinal {}", ordinal), + }; + + assert_eq!(of.valid(), Of::new(ordinal, flags).unwrap().valid()); if of.valid() { assert_eq!(of.ordinal(), ordinal); } @@ -685,25 +674,25 @@ mod tests { #[test] fn test_of_weekday() { - assert_eq!(Of::new(1, A).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, B).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, C).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, D).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, E).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, F).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, G).weekday(), Weekday::Mon); - assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun); - assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat); - assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri); - assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu); - assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed); - assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue); - assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon); + assert_eq!(Of::new(1, A).unwrap().weekday(), Weekday::Sun); + assert_eq!(Of::new(1, B).unwrap().weekday(), Weekday::Sat); + assert_eq!(Of::new(1, C).unwrap().weekday(), Weekday::Fri); + assert_eq!(Of::new(1, D).unwrap().weekday(), Weekday::Thu); + assert_eq!(Of::new(1, E).unwrap().weekday(), Weekday::Wed); + assert_eq!(Of::new(1, F).unwrap().weekday(), Weekday::Tue); + assert_eq!(Of::new(1, G).unwrap().weekday(), Weekday::Mon); + assert_eq!(Of::new(1, AG).unwrap().weekday(), Weekday::Sun); + assert_eq!(Of::new(1, BA).unwrap().weekday(), Weekday::Sat); + assert_eq!(Of::new(1, CB).unwrap().weekday(), Weekday::Fri); + assert_eq!(Of::new(1, DC).unwrap().weekday(), Weekday::Thu); + assert_eq!(Of::new(1, ED).unwrap().weekday(), Weekday::Wed); + assert_eq!(Of::new(1, FE).unwrap().weekday(), Weekday::Tue); + assert_eq!(Of::new(1, GF).unwrap().weekday(), Weekday::Mon); for &flags in FLAGS.iter() { - let mut prev = Of::new(1, flags).weekday(); + let mut prev = Of::new(1, flags).unwrap().weekday(); for ordinal in range_inclusive(2u32, flags.ndays()) { - let of = Of::new(ordinal, flags); + let of = Of::new(ordinal, flags).unwrap(); let expected = prev.succ(); assert_eq!(of.weekday(), expected); prev = expected; @@ -716,7 +705,11 @@ mod tests { for &flags in FLAGS.iter() { for month in range_inclusive(1u32, 12) { for day in range_inclusive(1u32, 31) { - let mdf = Mdf::new(month, day, flags); + let mdf = match Mdf::new(month, day, flags) { + Some(mdf) => mdf, + None => continue, + }; + if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); @@ -729,11 +722,15 @@ mod tests { #[test] fn test_mdf_with_fields() { fn check(flags: YearFlags, month: u32, day: u32) { - let mdf = Mdf::new(month, day, flags); + let mdf = Mdf::new(month, day, flags).unwrap(); for month in range_inclusive(0u32, 16) { - let mdf = mdf.with_month(month); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); + let mdf = match mdf.with_month(month) { + Some(mdf) => mdf, + None if month > 12 => continue, + None => panic!("failed to create Mdf with month {}", month), + }; + if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); @@ -741,8 +738,12 @@ mod tests { } for day in range_inclusive(0u32, 1024) { - let mdf = mdf.with_day(day); - assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); + let mdf = match mdf.with_day(day) { + Some(mdf) => mdf, + None if day > 31 => continue, + None => panic!("failed to create Mdf with month {}", month), + }; + if mdf.valid() { assert_eq!(mdf.month(), month); assert_eq!(mdf.day(), day); @@ -772,7 +773,7 @@ mod tests { fn test_of_isoweekdate_raw() { for &flags in FLAGS.iter() { // January 4 should be in the first week - let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw(); + let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw(); assert_eq!(week, 1); } } diff --git a/vendor/chrono/src/naive/isoweek.rs b/vendor/chrono/src/naive/isoweek.rs index ece10f250..6a4fcfd11 100644 --- a/vendor/chrono/src/naive/isoweek.rs +++ b/vendor/chrono/src/naive/isoweek.rs @@ -7,13 +7,17 @@ use core::fmt; use super::internals::{DateImpl, Of, YearFlags}; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + /// ISO 8601 week. /// /// This type, combined with [`Weekday`](../enum.Weekday.html), -/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date). +/// constitutes the ISO 8601 [week date](./struct.NaiveDate.html#week-date). /// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types /// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method. -#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct IsoWeek { // note that this allows for larger year range than `NaiveDate`. // this is crucial because we have an edge case for the first and last week supported, @@ -27,7 +31,7 @@ pub struct IsoWeek { // because the year range for the week date and the calendar date do not match and // it is confusing to have a date that is out of range in one and not in another. // currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`. -pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek { +pub(super) fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek { let (rawweek, _) = of.isoweekdate_raw(); let (year, week) = if rawweek < 1 { // previous year @@ -50,24 +54,24 @@ impl IsoWeek { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike, Weekday}; /// /// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon); /// assert_eq!(d.iso_week().year(), 2015); - /// ~~~~ + /// ``` /// /// This year number might not match the calendar year number. /// Continuing the example... /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveDate, Datelike, Weekday}; /// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon); /// assert_eq!(d.year(), 2014); - /// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29)); - /// ~~~~ + /// assert_eq!(d, NaiveDate::from_ymd_opt(2014, 12, 29).unwrap()); + /// ``` #[inline] - pub fn year(&self) -> i32 { + pub const fn year(&self) -> i32 { self.ywf >> 10 } @@ -77,14 +81,14 @@ impl IsoWeek { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike, Weekday}; /// /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon); /// assert_eq!(d.iso_week().week(), 15); - /// ~~~~ + /// ``` #[inline] - pub fn week(&self) -> u32 { + pub const fn week(&self) -> u32 { ((self.ywf >> 4) & 0x3f) as u32 } @@ -94,14 +98,14 @@ impl IsoWeek { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveDate, Datelike, Weekday}; /// /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon); /// assert_eq!(d.iso_week().week0(), 14); - /// ~~~~ + /// ``` #[inline] - pub fn week0(&self) -> u32 { + pub const fn week0(&self) -> u32 { ((self.ywf >> 4) & 0x3f) as u32 - 1 } } @@ -112,26 +116,26 @@ impl IsoWeek { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::{NaiveDate, Datelike}; /// -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().iso_week()), "2015-W36"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt( 0, 1, 3).unwrap().iso_week()), "0000-W01"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap().iso_week()), "9999-W52"); +/// ``` /// /// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. /// -/// ~~~~ +/// ``` /// # use chrono::{NaiveDate, Datelike}; -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt( 0, 1, 2).unwrap().iso_week()), "-0001-W52"); +/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap().iso_week()), "+10000-W52"); +/// ``` impl fmt::Debug for IsoWeek { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let year = self.year(); let week = self.week(); - if 0 <= year && year <= 9999 { + if (0..=9999).contains(&year) { write!(f, "{:04}-W{:02}", year, week) } else { // ISO 8601 requires the explicit sign for out-of-range years @@ -142,22 +146,22 @@ impl fmt::Debug for IsoWeek { #[cfg(test)] mod tests { - use naive::{internals, MAX_DATE, MIN_DATE}; - use Datelike; + use crate::naive::{internals, NaiveDate}; + use crate::Datelike; #[test] fn test_iso_week_extremes() { - let minweek = MIN_DATE.iso_week(); - let maxweek = MAX_DATE.iso_week(); + let minweek = NaiveDate::MIN.iso_week(); + let maxweek = NaiveDate::MAX.iso_week(); assert_eq!(minweek.year(), internals::MIN_YEAR); assert_eq!(minweek.week(), 1); assert_eq!(minweek.week0(), 0); - assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string()); + assert_eq!(format!("{:?}", minweek), NaiveDate::MIN.format("%G-W%V").to_string()); assert_eq!(maxweek.year(), internals::MAX_YEAR + 1); assert_eq!(maxweek.week(), 1); assert_eq!(maxweek.week0(), 0); - assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string()); + assert_eq!(format!("{:?}", maxweek), NaiveDate::MAX.format("%G-W%V").to_string()); } } diff --git a/vendor/chrono/src/naive/mod.rs b/vendor/chrono/src/naive/mod.rs new file mode 100644 index 000000000..c41acba8d --- /dev/null +++ b/vendor/chrono/src/naive/mod.rs @@ -0,0 +1,39 @@ +//! Date and time types unconcerned with timezones. +//! +//! They are primarily building blocks for other types +//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), +//! but can be also used for the simpler date and time handling. + +mod date; +pub(crate) mod datetime; +mod internals; +mod isoweek; +mod time; + +#[allow(deprecated)] +pub use self::date::{Days, NaiveDate, NaiveWeek, MAX_DATE, MIN_DATE}; +#[cfg(feature = "rustc-serialize")] +#[allow(deprecated)] +pub use self::datetime::rustc_serialize::TsSeconds; +#[allow(deprecated)] +pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME}; +pub use self::isoweek::IsoWeek; +pub use self::time::NaiveTime; + +#[cfg(feature = "__internal_bench")] +#[doc(hidden)] +pub use self::internals::YearFlags as __BenchYearFlags; + +/// Serialization/Deserialization of naive types in alternate formats +/// +/// The various modules in here are intended to be used with serde's [`with` +/// annotation][1] to serialize as something other than the default [RFC +/// 3339][2] format. +/// +/// [1]: https://serde.rs/attributes.html#field-attributes +/// [2]: https://tools.ietf.org/html/rfc3339 +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +pub mod serde { + pub use super::datetime::serde::*; +} diff --git a/vendor/chrono/src/naive/time.rs b/vendor/chrono/src/naive/time/mod.rs index 1ddc9fbed..1d36583aa 100644 --- a/vendor/chrono/src/naive/time.rs +++ b/vendor/chrono/src/naive/time/mod.rs @@ -6,18 +6,27 @@ #[cfg(any(feature = "alloc", feature = "std", test))] use core::borrow::Borrow; use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::{fmt, hash, str}; -use oldtime::Duration as OldDuration; +use core::{fmt, str}; + +use num_integer::div_mod_floor; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; -use div::div_mod_floor; #[cfg(any(feature = "alloc", feature = "std", test))] -use format::DelayedFormat; -use format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; -use format::{Fixed, Item, Numeric, Pad}; -use Timelike; +use crate::format::DelayedFormat; +use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems}; +use crate::format::{Fixed, Item, Numeric, Pad}; +use crate::oldtime::Duration as OldDuration; +use crate::Timelike; -pub const MIN_TIME: NaiveTime = NaiveTime { secs: 0, frac: 0 }; -pub const MAX_TIME: NaiveTime = NaiveTime { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; +#[cfg(feature = "rustc-serialize")] +mod rustc_serialize; + +#[cfg(feature = "serde")] +mod serde; + +#[cfg(test)] +mod tests; /// ISO 8601 time without timezone. /// Allows for the nanosecond precision and optional leap second representation. @@ -64,16 +73,16 @@ pub const MAX_TIME: NaiveTime = NaiveTime { secs: 23 * 3600 + 59 * 60 + 59, frac /// /// All methods accepting fractional seconds will accept such values. /// -/// ~~~~ +/// ``` /// use chrono::{NaiveDate, NaiveTime, Utc, TimeZone}; /// -/// let t = NaiveTime::from_hms_milli(8, 59, 59, 1_000); +/// let t = NaiveTime::from_hms_milli_opt(8, 59, 59, 1_000).unwrap(); /// -/// let dt1 = NaiveDate::from_ymd(2015, 7, 1).and_hms_micro(8, 59, 59, 1_000_000); +/// let dt1 = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_micro_opt(8, 59, 59, 1_000_000).unwrap(); /// -/// let dt2 = Utc.ymd(2015, 6, 30).and_hms_nano(23, 59, 59, 1_000_000_000); +/// let dt2 = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_nano_opt(23, 59, 59, 1_000_000_000).unwrap().and_local_timezone(Utc).unwrap(); /// # let _ = (t, dt1, dt2); -/// ~~~~ +/// ``` /// /// Note that the leap second can happen anytime given an appropriate time zone; /// 2015-07-01 01:23:60 would be a proper leap second if UTC+01:24 had existed. @@ -151,12 +160,12 @@ pub const MAX_TIME: NaiveTime = NaiveTime { secs: 23 * 3600 + 59 * 60 + 59, frac /// The leap second in the human-readable representation /// will be represented as the second part being 60, as required by ISO 8601. /// -/// ~~~~ -/// use chrono::{Utc, TimeZone}; +/// ``` +/// use chrono::{Utc, TimeZone, NaiveDate}; /// -/// let dt = Utc.ymd(2015, 6, 30).and_hms_milli(23, 59, 59, 1_000); +/// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); -/// ~~~~ +/// ``` /// /// There are hypothetical leap seconds not on the minute boundary /// nevertheless supported by Chrono. @@ -165,25 +174,37 @@ pub const MAX_TIME: NaiveTime = NaiveTime { secs: 23 * 3600 + 59 * 60 + 59, frac /// For such cases the human-readable representation is ambiguous /// and would be read back to the next non-leap second. /// -/// ~~~~ -/// use chrono::{DateTime, Utc, TimeZone}; +/// ``` +/// use chrono::{DateTime, Utc, TimeZone, NaiveDate}; /// -/// let dt = Utc.ymd(2015, 6, 30).and_hms_milli(23, 56, 4, 1_000); +/// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 56, 4, 1_000).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// -/// let dt = Utc.ymd(2015, 6, 30).and_hms(23, 56, 5); +/// let dt = Utc.with_ymd_and_hms(2015, 6, 30, 23, 56, 5).unwrap(); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// assert_eq!(DateTime::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); -/// ~~~~ +/// ``` /// /// Since Chrono alone cannot determine any existence of leap seconds, /// **there is absolutely no guarantee that the leap second read has actually happened**. -#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct NaiveTime { secs: u32, frac: u32, } +#[cfg(feature = "arbitrary")] +impl arbitrary::Arbitrary<'_> for NaiveTime { + fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveTime> { + let secs = u.int_in_range(0..=86_399)?; + let nano = u.int_in_range(0..=1_999_999_999)?; + let time = NaiveTime::from_num_seconds_from_midnight_opt(secs, nano) + .expect("Could not generate a valid chrono::NaiveTime. It looks like implementation of Arbitrary for NaiveTime is erroneous."); + Ok(time) + } +} + impl NaiveTime { /// Makes a new `NaiveTime` from hour, minute and second. /// @@ -191,18 +212,7 @@ impl NaiveTime { /// use `NaiveTime::from_hms_*` methods with a subsecond parameter instead. /// /// Panics on invalid hour, minute and/or second. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveTime, Timelike}; - /// - /// let t = NaiveTime::from_hms(23, 56, 4); - /// assert_eq!(t.hour(), 23); - /// assert_eq!(t.minute(), 56); - /// assert_eq!(t.second(), 4); - /// assert_eq!(t.nanosecond(), 0); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_hms_opt()` instead")] #[inline] pub fn from_hms(hour: u32, min: u32, sec: u32) -> NaiveTime { NaiveTime::from_hms_opt(hour, min, sec).expect("invalid time") @@ -217,7 +227,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let from_hms_opt = NaiveTime::from_hms_opt; @@ -227,7 +237,7 @@ impl NaiveTime { /// assert!(from_hms_opt(24, 0, 0).is_none()); /// assert!(from_hms_opt(23, 60, 0).is_none()); /// assert!(from_hms_opt(23, 59, 60).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option<NaiveTime> { NaiveTime::from_hms_nano_opt(hour, min, sec, 0) @@ -239,18 +249,7 @@ impl NaiveTime { /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or millisecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveTime, Timelike}; - /// - /// let t = NaiveTime::from_hms_milli(23, 56, 4, 12); - /// assert_eq!(t.hour(), 23); - /// assert_eq!(t.minute(), 56); - /// assert_eq!(t.second(), 4); - /// assert_eq!(t.nanosecond(), 12_000_000); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_hms_milli_opt()` instead")] #[inline] pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> NaiveTime { NaiveTime::from_hms_milli_opt(hour, min, sec, milli).expect("invalid time") @@ -265,7 +264,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let from_hmsm_opt = NaiveTime::from_hms_milli_opt; @@ -277,7 +276,7 @@ impl NaiveTime { /// assert!(from_hmsm_opt(23, 60, 0, 0).is_none()); /// assert!(from_hmsm_opt(23, 59, 60, 0).is_none()); /// assert!(from_hmsm_opt(23, 59, 59, 2_000).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn from_hms_milli_opt(hour: u32, min: u32, sec: u32, milli: u32) -> Option<NaiveTime> { milli @@ -291,18 +290,7 @@ impl NaiveTime { /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or microsecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveTime, Timelike}; - /// - /// let t = NaiveTime::from_hms_micro(23, 56, 4, 12_345); - /// assert_eq!(t.hour(), 23); - /// assert_eq!(t.minute(), 56); - /// assert_eq!(t.second(), 4); - /// assert_eq!(t.nanosecond(), 12_345_000); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_hms_micro_opt()` instead")] #[inline] pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> NaiveTime { NaiveTime::from_hms_micro_opt(hour, min, sec, micro).expect("invalid time") @@ -317,7 +305,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let from_hmsu_opt = NaiveTime::from_hms_micro_opt; @@ -329,7 +317,7 @@ impl NaiveTime { /// assert!(from_hmsu_opt(23, 60, 0, 0).is_none()); /// assert!(from_hmsu_opt(23, 59, 60, 0).is_none()); /// assert!(from_hmsu_opt(23, 59, 59, 2_000_000).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn from_hms_micro_opt(hour: u32, min: u32, sec: u32, micro: u32) -> Option<NaiveTime> { micro.checked_mul(1_000).and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano)) @@ -341,18 +329,7 @@ impl NaiveTime { /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid hour, minute, second and/or nanosecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveTime, Timelike}; - /// - /// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); - /// assert_eq!(t.hour(), 23); - /// assert_eq!(t.minute(), 56); - /// assert_eq!(t.second(), 4); - /// assert_eq!(t.nanosecond(), 12_345_678); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_hms_nano_opt()` instead")] #[inline] pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> NaiveTime { NaiveTime::from_hms_nano_opt(hour, min, sec, nano).expect("invalid time") @@ -367,7 +344,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let from_hmsn_opt = NaiveTime::from_hms_nano_opt; @@ -379,14 +356,14 @@ impl NaiveTime { /// assert!(from_hmsn_opt(23, 60, 0, 0).is_none()); /// assert!(from_hmsn_opt(23, 59, 60, 0).is_none()); /// assert!(from_hmsn_opt(23, 59, 59, 2_000_000_000).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option<NaiveTime> { if hour >= 24 || min >= 60 || sec >= 60 || nano >= 2_000_000_000 { return None; } let secs = hour * 3600 + min * 60 + sec; - Some(NaiveTime { secs: secs, frac: nano }) + Some(NaiveTime { secs, frac: nano }) } /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond. @@ -395,18 +372,7 @@ impl NaiveTime { /// in order to represent the [leap second](#leap-second-handling). /// /// Panics on invalid number of seconds and/or nanosecond. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{NaiveTime, Timelike}; - /// - /// let t = NaiveTime::from_num_seconds_from_midnight(86164, 12_345_678); - /// assert_eq!(t.hour(), 23); - /// assert_eq!(t.minute(), 56); - /// assert_eq!(t.second(), 4); - /// assert_eq!(t.nanosecond(), 12_345_678); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `from_num_seconds_from_midnight_opt()` instead")] #[inline] pub fn from_num_seconds_from_midnight(secs: u32, nano: u32) -> NaiveTime { NaiveTime::from_num_seconds_from_midnight_opt(secs, nano).expect("invalid time") @@ -421,7 +387,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let from_nsecs_opt = NaiveTime::from_num_seconds_from_midnight_opt; @@ -431,13 +397,13 @@ impl NaiveTime { /// assert!(from_nsecs_opt(86399, 1_999_999_999).is_some()); // a leap second after 23:59:59 /// assert!(from_nsecs_opt(86_400, 0).is_none()); /// assert!(from_nsecs_opt(86399, 2_000_000_000).is_none()); - /// ~~~~ + /// ``` #[inline] pub fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option<NaiveTime> { if secs >= 86_400 || nano >= 2_000_000_000 { return None; } - Some(NaiveTime { secs: secs, frac: nano }) + Some(NaiveTime { secs, frac: nano }) } /// Parses a string with the specified format string and returns a new `NaiveTime`. @@ -446,61 +412,61 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// /// let parse_from_str = NaiveTime::parse_from_str; /// /// assert_eq!(parse_from_str("23:56:04", "%H:%M:%S"), - /// Ok(NaiveTime::from_hms(23, 56, 4))); + /// Ok(NaiveTime::from_hms_opt(23, 56, 4).unwrap())); /// assert_eq!(parse_from_str("pm012345.6789", "%p%I%M%S%.f"), - /// Ok(NaiveTime::from_hms_micro(13, 23, 45, 678_900))); - /// ~~~~ + /// Ok(NaiveTime::from_hms_micro_opt(13, 23, 45, 678_900).unwrap())); + /// ``` /// /// Date and offset is ignored for the purpose of parsing. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; /// # let parse_from_str = NaiveTime::parse_from_str; /// assert_eq!(parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveTime::from_hms(12, 34, 56))); - /// ~~~~ + /// Ok(NaiveTime::from_hms_opt(12, 34, 56).unwrap())); + /// ``` /// /// [Leap seconds](#leap-second-handling) are correctly handled by /// treating any time of the form `hh:mm:60` as a leap second. /// (This equally applies to the formatting, so the round trip is possible.) /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; /// # let parse_from_str = NaiveTime::parse_from_str; /// assert_eq!(parse_from_str("08:59:60.123", "%H:%M:%S%.f"), - /// Ok(NaiveTime::from_hms_milli(8, 59, 59, 1_123))); - /// ~~~~ + /// Ok(NaiveTime::from_hms_milli_opt(8, 59, 59, 1_123).unwrap())); + /// ``` /// /// Missing seconds are assumed to be zero, /// but out-of-bound times or insufficient fields are errors otherwise. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; /// # let parse_from_str = NaiveTime::parse_from_str; /// assert_eq!(parse_from_str("7:15", "%H:%M"), - /// Ok(NaiveTime::from_hms(7, 15, 0))); + /// Ok(NaiveTime::from_hms_opt(7, 15, 0).unwrap())); /// /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); /// assert!(parse_from_str("12", "%H").is_err()); /// assert!(parse_from_str("17:60", "%H:%M").is_err()); /// assert!(parse_from_str("24:00:00", "%H:%M:%S").is_err()); - /// ~~~~ + /// ``` /// /// All parsed fields should be consistent to each other, otherwise it's an error. /// Here `%H` is for 24-hour clocks, unlike `%I`, /// and thus can be independently determined without AM/PM. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; /// # let parse_from_str = NaiveTime::parse_from_str; /// assert!(parse_from_str("13:07 AM", "%H:%M %p").is_err()); - /// ~~~~ + /// ``` pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> { let mut parsed = Parsed::new(); parse(&mut parsed, s, StrftimeItems::new(fmt))?; @@ -514,8 +480,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hms = NaiveTime::from_hms; @@ -526,9 +491,7 @@ impl NaiveTime { /// (from_hms(2, 4, 5), 86_400)); /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(-7)), /// (from_hms(20, 4, 5), -86_400)); - /// # } - /// ~~~~ - #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] + /// ``` pub fn overflowing_add_signed(&self, mut rhs: OldDuration) -> (NaiveTime, i64) { let mut secs = self.secs; let mut frac = self.frac; @@ -548,7 +511,7 @@ impl NaiveTime { } else { frac = (i64::from(frac) + rhs.num_nanoseconds().unwrap()) as u32; debug_assert!(frac < 2_000_000_000); - return (NaiveTime { secs: secs, frac: frac }, 0); + return (NaiveTime { secs, frac }, 0); } } debug_assert!(secs <= 86_400); @@ -577,8 +540,8 @@ impl NaiveTime { frac -= 1_000_000_000; secs += 1; } - debug_assert!(-86_400 <= secs && secs < 2 * 86_400); - debug_assert!(0 <= frac && frac < 1_000_000_000); + debug_assert!((-86_400..2 * 86_400).contains(&secs)); + debug_assert!((0..1_000_000_000).contains(&frac)); if secs < 0 { secs += 86_400; @@ -587,7 +550,7 @@ impl NaiveTime { secs -= 86_400; morerhssecs += 86_400; } - debug_assert!(0 <= secs && secs < 86_400); + debug_assert!((0..86_400).contains(&secs)); (NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs) } @@ -599,8 +562,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hms = NaiveTime::from_hms; @@ -611,8 +573,7 @@ impl NaiveTime { /// (from_hms(10, 4, 5), 86_400)); /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(-22)), /// (from_hms(1, 4, 5), -86_400)); - /// # } - /// ~~~~ + /// ``` #[inline] pub fn overflowing_sub_signed(&self, rhs: OldDuration) -> (NaiveTime, i64) { let (time, rhs) = self.overflowing_add_signed(-rhs); @@ -631,8 +592,7 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hmsm = NaiveTime::from_hms_milli; @@ -654,14 +614,12 @@ impl NaiveTime { /// Duration::seconds(-3600)); /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(2, 4, 6, 800)), /// Duration::seconds(3600 + 60 + 1) + Duration::milliseconds(100)); - /// # } - /// ~~~~ + /// ``` /// /// Leap seconds are handled, but the subtraction assumes that /// there were no other leap seconds happened. /// - /// ~~~~ - /// # extern crate chrono; fn main() { + /// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// # let since = NaiveTime::signed_duration_since; @@ -675,8 +633,7 @@ impl NaiveTime { /// Duration::seconds(1)); /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(2, 59, 59, 1_000)), /// Duration::seconds(61)); - /// # } - /// ~~~~ + /// ``` pub fn signed_duration_since(self, rhs: NaiveTime) -> OldDuration { // | | :leap| | | | | | | :leap| | // | | : | | | | | | | : | | @@ -695,13 +652,7 @@ impl NaiveTime { // `secs` may contain a leap second yet to be counted let adjust = match self.secs.cmp(&rhs.secs) { - Ordering::Greater => { - if rhs.frac >= 1_000_000_000 { - 1 - } else { - 0 - } - } + Ordering::Greater => i64::from(rhs.frac >= 1_000_000_000), Ordering::Equal => 0, Ordering::Less => { if self.frac >= 1_000_000_000 { @@ -723,26 +674,27 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// use chrono::format::strftime::StrftimeItems; /// /// let fmt = StrftimeItems::new("%H:%M:%S"); - /// let t = NaiveTime::from_hms(23, 56, 4); + /// let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); /// assert_eq!(t.format_with_items(fmt.clone()).to_string(), "23:56:04"); /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); - /// ~~~~ + /// ``` /// /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; /// # use chrono::format::strftime::StrftimeItems; /// # let fmt = StrftimeItems::new("%H:%M:%S").clone(); - /// # let t = NaiveTime::from_hms(23, 56, 4); + /// # let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); /// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04"); - /// ~~~~ + /// ``` #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I> where @@ -768,25 +720,26 @@ impl NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::NaiveTime; /// - /// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); /// assert_eq!(t.format("%H:%M:%S%.6f").to_string(), "23:56:04.012345"); /// assert_eq!(t.format("%-I:%M %p").to_string(), "11:56 PM"); - /// ~~~~ + /// ``` /// /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. /// - /// ~~~~ + /// ``` /// # use chrono::NaiveTime; - /// # let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// # let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); /// assert_eq!(format!("{}", t.format("%H:%M:%S")), "23:56:04"); /// assert_eq!(format!("{}", t.format("%H:%M:%S%.6f")), "23:56:04.012345"); /// assert_eq!(format!("{}", t.format("%-I:%M %p")), "11:56 PM"); - /// ~~~~ + /// ``` #[cfg(any(feature = "alloc", feature = "std", test))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[inline] pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { self.format_with_items(StrftimeItems::new(fmt)) @@ -798,6 +751,10 @@ impl NaiveTime { let (hour, min) = div_mod_floor(mins, 60); (hour, min, sec) } + + /// The earliest possible `NaiveTime` + pub const MIN: Self = Self { secs: 0, frac: 0 }; + pub(super) const MAX: Self = Self { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; } impl Timelike for NaiveTime { @@ -805,12 +762,12 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// assert_eq!(NaiveTime::from_hms(0, 0, 0).hour(), 0); - /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).hour(), 23); - /// ~~~~ + /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().hour(), 0); + /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().hour(), 23); + /// ``` #[inline] fn hour(&self) -> u32 { self.hms().0 @@ -820,12 +777,12 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// assert_eq!(NaiveTime::from_hms(0, 0, 0).minute(), 0); - /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).minute(), 56); - /// ~~~~ + /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().minute(), 0); + /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().minute(), 56); + /// ``` #[inline] fn minute(&self) -> u32 { self.hms().1 @@ -835,23 +792,23 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// assert_eq!(NaiveTime::from_hms(0, 0, 0).second(), 0); - /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).second(), 4); - /// ~~~~ + /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().second(), 0); + /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().second(), 4); + /// ``` /// /// This method never returns 60 even when it is a leap second. /// ([Why?](#leap-second-handling)) /// Use the proper [formatting method](#method.format) to get a human-readable representation. /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveTime, Timelike}; - /// let leap = NaiveTime::from_hms_milli(23, 59, 59, 1_000); + /// let leap = NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap(); /// assert_eq!(leap.second(), 59); /// assert_eq!(leap.format("%H:%M:%S").to_string(), "23:59:60"); - /// ~~~~ + /// ``` #[inline] fn second(&self) -> u32 { self.hms().2 @@ -863,23 +820,23 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// assert_eq!(NaiveTime::from_hms(0, 0, 0).nanosecond(), 0); - /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).nanosecond(), 12_345_678); - /// ~~~~ + /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().nanosecond(), 0); + /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().nanosecond(), 12_345_678); + /// ``` /// /// Leap seconds may have seemingly out-of-range return values. /// You can reduce the range with `time.nanosecond() % 1_000_000_000`, or /// use the proper [formatting method](#method.format) to get a human-readable representation. /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveTime, Timelike}; - /// let leap = NaiveTime::from_hms_milli(23, 59, 59, 1_000); + /// let leap = NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap(); /// assert_eq!(leap.nanosecond(), 1_000_000_000); /// assert_eq!(leap.format("%H:%M:%S%.9f").to_string(), "23:59:60.000000000"); - /// ~~~~ + /// ``` #[inline] fn nanosecond(&self) -> u32 { self.frac @@ -891,20 +848,20 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); - /// assert_eq!(dt.with_hour(7), Some(NaiveTime::from_hms_nano(7, 56, 4, 12_345_678))); + /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); + /// assert_eq!(dt.with_hour(7), Some(NaiveTime::from_hms_nano_opt(7, 56, 4, 12_345_678).unwrap())); /// assert_eq!(dt.with_hour(24), None); - /// ~~~~ + /// ``` #[inline] fn with_hour(&self, hour: u32) -> Option<NaiveTime> { if hour >= 24 { return None; } let secs = hour * 3600 + self.secs % 3600; - Some(NaiveTime { secs: secs, ..*self }) + Some(NaiveTime { secs, ..*self }) } /// Makes a new `NaiveTime` with the minute number changed. @@ -913,20 +870,20 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); - /// assert_eq!(dt.with_minute(45), Some(NaiveTime::from_hms_nano(23, 45, 4, 12_345_678))); + /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); + /// assert_eq!(dt.with_minute(45), Some(NaiveTime::from_hms_nano_opt(23, 45, 4, 12_345_678).unwrap())); /// assert_eq!(dt.with_minute(60), None); - /// ~~~~ + /// ``` #[inline] fn with_minute(&self, min: u32) -> Option<NaiveTime> { if min >= 60 { return None; } let secs = self.secs / 3600 * 3600 + min * 60 + self.secs % 60; - Some(NaiveTime { secs: secs, ..*self }) + Some(NaiveTime { secs, ..*self }) } /// Makes a new `NaiveTime` with the second number changed. @@ -937,20 +894,20 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); - /// assert_eq!(dt.with_second(17), Some(NaiveTime::from_hms_nano(23, 56, 17, 12_345_678))); + /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); + /// assert_eq!(dt.with_second(17), Some(NaiveTime::from_hms_nano_opt(23, 56, 17, 12_345_678).unwrap())); /// assert_eq!(dt.with_second(60), None); - /// ~~~~ + /// ``` #[inline] fn with_second(&self, sec: u32) -> Option<NaiveTime> { if sec >= 60 { return None; } let secs = self.secs / 60 * 60 + sec; - Some(NaiveTime { secs: secs, ..*self }) + Some(NaiveTime { secs, ..*self }) } /// Makes a new `NaiveTime` with nanoseconds since the whole non-leap second changed. @@ -961,26 +918,26 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); /// assert_eq!(dt.with_nanosecond(333_333_333), - /// Some(NaiveTime::from_hms_nano(23, 56, 4, 333_333_333))); + /// Some(NaiveTime::from_hms_nano_opt(23, 56, 4, 333_333_333).unwrap())); /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); - /// ~~~~ + /// ``` /// /// Leap seconds can theoretically follow *any* whole second. /// The following would be a proper leap second at the time zone offset of UTC-00:03:57 /// (there are several historical examples comparable to this "non-sense" offset), /// and therefore is allowed. /// - /// ~~~~ + /// ``` /// # use chrono::{NaiveTime, Timelike}; - /// # let dt = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); + /// # let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); /// assert_eq!(dt.with_nanosecond(1_333_333_333), - /// Some(NaiveTime::from_hms_nano(23, 56, 4, 1_333_333_333))); - /// ~~~~ + /// Some(NaiveTime::from_hms_nano_opt(23, 56, 4, 1_333_333_333).unwrap())); + /// ``` #[inline] fn with_nanosecond(&self, nano: u32) -> Option<NaiveTime> { if nano >= 2_000_000_000 { @@ -993,33 +950,22 @@ impl Timelike for NaiveTime { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{NaiveTime, Timelike}; /// - /// assert_eq!(NaiveTime::from_hms(1, 2, 3).num_seconds_from_midnight(), + /// assert_eq!(NaiveTime::from_hms_opt(1, 2, 3).unwrap().num_seconds_from_midnight(), /// 3723); - /// assert_eq!(NaiveTime::from_hms_nano(23, 56, 4, 12_345_678).num_seconds_from_midnight(), + /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().num_seconds_from_midnight(), /// 86164); - /// assert_eq!(NaiveTime::from_hms_milli(23, 59, 59, 1_000).num_seconds_from_midnight(), + /// assert_eq!(NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().num_seconds_from_midnight(), /// 86399); - /// ~~~~ + /// ``` #[inline] fn num_seconds_from_midnight(&self) -> u32 { self.secs // do not repeat the calculation! } } -/// `NaiveTime` can be used as a key to the hash maps (in principle). -/// -/// Practically this also takes account of fractional seconds, so it is not recommended. -/// (For the obvious reason this also distinguishes leap seconds from non-leap seconds.) -impl hash::Hash for NaiveTime { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.secs.hash(state); - self.frac.hash(state); - } -} - /// An addition of `Duration` to `NaiveTime` wraps around and never overflows or underflows. /// In particular the addition ignores integral number of days. /// @@ -1030,8 +976,7 @@ impl hash::Hash for NaiveTime { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hmsm = NaiveTime::from_hms_milli; @@ -1044,25 +989,21 @@ impl hash::Hash for NaiveTime { /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::milliseconds(80), from_hmsm(3, 5, 7, 80)); /// assert_eq!(from_hmsm(3, 5, 7, 950) + Duration::milliseconds(280), from_hmsm(3, 5, 8, 230)); /// assert_eq!(from_hmsm(3, 5, 7, 950) + Duration::milliseconds(-980), from_hmsm(3, 5, 6, 970)); -/// # } -/// ~~~~ +/// ``` /// /// The addition wraps around. /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(22*60*60), from_hmsm(1, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(-8*60*60), from_hmsm(19, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::days(800), from_hmsm(3, 5, 7, 0)); -/// # } -/// ~~~~ +/// ``` /// /// Leap seconds are handled, but the addition assumes that it is the only leap second happened. /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); @@ -1073,8 +1014,7 @@ impl hash::Hash for NaiveTime { /// assert_eq!(leap + Duration::seconds(10), from_hmsm(3, 6, 9, 300)); /// assert_eq!(leap + Duration::seconds(-10), from_hmsm(3, 5, 50, 300)); /// assert_eq!(leap + Duration::days(1), from_hmsm(3, 5, 59, 300)); -/// # } -/// ~~~~ +/// ``` impl Add<OldDuration> for NaiveTime { type Output = NaiveTime; @@ -1102,8 +1042,7 @@ impl AddAssign<OldDuration> for NaiveTime { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hmsm = NaiveTime::from_hms_milli; @@ -1114,24 +1053,20 @@ impl AddAssign<OldDuration> for NaiveTime { /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(2*60*60 + 6*60), from_hmsm(0, 59, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::milliseconds(80), from_hmsm(3, 5, 6, 920)); /// assert_eq!(from_hmsm(3, 5, 7, 950) - Duration::milliseconds(280), from_hmsm(3, 5, 7, 670)); -/// # } -/// ~~~~ +/// ``` /// /// The subtraction wraps around. /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(8*60*60), from_hmsm(19, 5, 7, 0)); /// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::days(800), from_hmsm(3, 5, 7, 0)); -/// # } -/// ~~~~ +/// ``` /// /// Leap seconds are handled, but the subtraction assumes that it is the only leap second happened. /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// let leap = from_hmsm(3, 5, 59, 1_300); @@ -1140,8 +1075,7 @@ impl AddAssign<OldDuration> for NaiveTime { /// assert_eq!(leap - Duration::milliseconds(500), from_hmsm(3, 5, 59, 800)); /// assert_eq!(leap - Duration::seconds(60), from_hmsm(3, 5, 0, 300)); /// assert_eq!(leap - Duration::days(1), from_hmsm(3, 6, 0, 300)); -/// # } -/// ~~~~ +/// ``` impl Sub<OldDuration> for NaiveTime { type Output = NaiveTime; @@ -1173,8 +1107,7 @@ impl SubAssign<OldDuration> for NaiveTime { /// /// # Example /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// use chrono::{Duration, NaiveTime}; /// /// let from_hmsm = NaiveTime::from_hms_milli; @@ -1188,14 +1121,12 @@ impl SubAssign<OldDuration> for NaiveTime { /// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(4, 5, 7, 900), Duration::seconds(-3600)); /// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(2, 4, 6, 800), /// Duration::seconds(3600 + 60 + 1) + Duration::milliseconds(100)); -/// # } -/// ~~~~ +/// ``` /// /// Leap seconds are handled, but the subtraction assumes that /// there were no other leap seconds happened. /// -/// ~~~~ -/// # extern crate chrono; fn main() { +/// ``` /// # use chrono::{Duration, NaiveTime}; /// # let from_hmsm = NaiveTime::from_hms_milli; /// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 59, 0), Duration::seconds(1)); @@ -1205,8 +1136,7 @@ impl SubAssign<OldDuration> for NaiveTime { /// assert_eq!(from_hmsm(3, 0, 0, 0) - from_hmsm(2, 59, 59, 1_000), Duration::seconds(1)); /// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(2, 59, 59, 1_000), /// Duration::seconds(61)); -/// # } -/// ~~~~ +/// ``` impl Sub<NaiveTime> for NaiveTime { type Output = OldDuration; @@ -1229,21 +1159,21 @@ impl Sub<NaiveTime> for NaiveTime { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveTime; /// -/// assert_eq!(format!("{:?}", NaiveTime::from_hms(23, 56, 4)), "23:56:04"); -/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli(23, 56, 4, 12)), "23:56:04.012"); -/// assert_eq!(format!("{:?}", NaiveTime::from_hms_micro(23, 56, 4, 1234)), "23:56:04.001234"); -/// assert_eq!(format!("{:?}", NaiveTime::from_hms_nano(23, 56, 4, 123456)), "23:56:04.000123456"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_opt(23, 56, 4).unwrap()), "23:56:04"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli_opt(23, 56, 4, 12).unwrap()), "23:56:04.012"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_micro_opt(23, 56, 4, 1234).unwrap()), "23:56:04.001234"); +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_nano_opt(23, 56, 4, 123456).unwrap()), "23:56:04.000123456"); +/// ``` /// /// Leap seconds may also be used. /// -/// ~~~~ +/// ``` /// # use chrono::NaiveTime; -/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli(6, 59, 59, 1_500)), "06:59:60.500"); -/// ~~~~ +/// assert_eq!(format!("{:?}", NaiveTime::from_hms_milli_opt(6, 59, 59, 1_500).unwrap()), "06:59:60.500"); +/// ``` impl fmt::Debug for NaiveTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (hour, min, sec) = self.hms(); @@ -1253,7 +1183,13 @@ impl fmt::Debug for NaiveTime { (sec, self.frac) }; - write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?; + use core::fmt::Write; + write_hundreds(f, hour as u8)?; + f.write_char(':')?; + write_hundreds(f, min as u8)?; + f.write_char(':')?; + write_hundreds(f, sec as u8)?; + if nano == 0 { Ok(()) } else if nano % 1_000_000 == 0 { @@ -1279,21 +1215,21 @@ impl fmt::Debug for NaiveTime { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveTime; /// -/// assert_eq!(format!("{}", NaiveTime::from_hms(23, 56, 4)), "23:56:04"); -/// assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 56, 4, 12)), "23:56:04.012"); -/// assert_eq!(format!("{}", NaiveTime::from_hms_micro(23, 56, 4, 1234)), "23:56:04.001234"); -/// assert_eq!(format!("{}", NaiveTime::from_hms_nano(23, 56, 4, 123456)), "23:56:04.000123456"); -/// ~~~~ +/// assert_eq!(format!("{}", NaiveTime::from_hms_opt(23, 56, 4).unwrap()), "23:56:04"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_milli_opt(23, 56, 4, 12).unwrap()), "23:56:04.012"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_micro_opt(23, 56, 4, 1234).unwrap()), "23:56:04.001234"); +/// assert_eq!(format!("{}", NaiveTime::from_hms_nano_opt(23, 56, 4, 123456).unwrap()), "23:56:04.000123456"); +/// ``` /// /// Leap seconds may also be used. /// -/// ~~~~ +/// ``` /// # use chrono::NaiveTime; -/// assert_eq!(format!("{}", NaiveTime::from_hms_milli(6, 59, 59, 1_500)), "06:59:60.500"); -/// ~~~~ +/// assert_eq!(format!("{}", NaiveTime::from_hms_milli_opt(6, 59, 59, 1_500).unwrap()), "06:59:60.500"); +/// ``` impl fmt::Display for NaiveTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) @@ -1305,25 +1241,25 @@ impl fmt::Display for NaiveTime { /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::NaiveTime; /// -/// let t = NaiveTime::from_hms(23, 56, 4); +/// let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); /// assert_eq!("23:56:04".parse::<NaiveTime>(), Ok(t)); /// -/// let t = NaiveTime::from_hms_nano(23, 56, 4, 12_345_678); +/// let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); /// assert_eq!("23:56:4.012345678".parse::<NaiveTime>(), Ok(t)); /// -/// let t = NaiveTime::from_hms_nano(23, 59, 59, 1_234_567_890); // leap second +/// let t = NaiveTime::from_hms_nano_opt(23, 59, 59, 1_234_567_890).unwrap(); // leap second /// assert_eq!("23:59:60.23456789".parse::<NaiveTime>(), Ok(t)); /// /// assert!("foo".parse::<NaiveTime>().is_err()); -/// ~~~~ +/// ``` impl str::FromStr for NaiveTime { type Err = ParseError; fn from_str(s: &str) -> ParseResult<NaiveTime> { - const ITEMS: &'static [Item<'static>] = &[ + const ITEMS: &[Item<'static>] = &[ Item::Numeric(Numeric::Hour, Pad::Zero), Item::Space(""), Item::Literal(":"), @@ -1341,33 +1277,58 @@ impl str::FromStr for NaiveTime { } } +/// The default value for a NaiveTime is midnight, 00:00:00 exactly. +/// +/// # Example +/// +/// ```rust +/// use chrono::NaiveTime; +/// +/// let default_time = NaiveTime::default(); +/// assert_eq!(default_time, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); +/// ``` +impl Default for NaiveTime { + fn default() -> Self { + NaiveTime::from_hms_opt(0, 0, 0).unwrap() + } +} + #[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))] fn test_encodable_json<F, E>(to_string: F) where F: Fn(&NaiveTime) -> Result<String, E>, E: ::std::fmt::Debug, { - assert_eq!(to_string(&NaiveTime::from_hms(0, 0, 0)).ok(), Some(r#""00:00:00""#.into())); assert_eq!( - to_string(&NaiveTime::from_hms_milli(0, 0, 0, 950)).ok(), + to_string(&NaiveTime::from_hms_opt(0, 0, 0).unwrap()).ok(), + Some(r#""00:00:00""#.into()) + ); + assert_eq!( + to_string(&NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()).ok(), Some(r#""00:00:00.950""#.into()) ); assert_eq!( - to_string(&NaiveTime::from_hms_milli(0, 0, 59, 1_000)).ok(), + to_string(&NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()).ok(), Some(r#""00:00:60""#.into()) ); - assert_eq!(to_string(&NaiveTime::from_hms(0, 1, 2)).ok(), Some(r#""00:01:02""#.into())); assert_eq!( - to_string(&NaiveTime::from_hms_nano(3, 5, 7, 98765432)).ok(), + to_string(&NaiveTime::from_hms_opt(0, 1, 2).unwrap()).ok(), + Some(r#""00:01:02""#.into()) + ); + assert_eq!( + to_string(&NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()).ok(), Some(r#""03:05:07.098765432""#.into()) ); - assert_eq!(to_string(&NaiveTime::from_hms(7, 8, 9)).ok(), Some(r#""07:08:09""#.into())); assert_eq!( - to_string(&NaiveTime::from_hms_micro(12, 34, 56, 789)).ok(), + to_string(&NaiveTime::from_hms_opt(7, 8, 9).unwrap()).ok(), + Some(r#""07:08:09""#.into()) + ); + assert_eq!( + to_string(&NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()).ok(), Some(r#""12:34:56.000789""#.into()) ); assert_eq!( - to_string(&NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)).ok(), + to_string(&NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()).ok(), Some(r#""23:59:60.999999999""#.into()) ); } @@ -1378,28 +1339,37 @@ where F: Fn(&str) -> Result<NaiveTime, E>, E: ::std::fmt::Debug, { - assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""0:0:0""#).ok(), Some(NaiveTime::from_hms(0, 0, 0))); - assert_eq!(from_str(r#""00:00:00.950""#).ok(), Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""0:0:0.95""#).ok(), Some(NaiveTime::from_hms_milli(0, 0, 0, 950))); - assert_eq!(from_str(r#""00:00:60""#).ok(), Some(NaiveTime::from_hms_milli(0, 0, 59, 1_000))); - assert_eq!(from_str(r#""00:01:02""#).ok(), Some(NaiveTime::from_hms(0, 1, 2))); + assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap())); + assert_eq!(from_str(r#""0:0:0""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap())); + assert_eq!( + from_str(r#""00:00:00.950""#).ok(), + Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()) + ); + assert_eq!( + from_str(r#""0:0:0.95""#).ok(), + Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()) + ); + assert_eq!( + from_str(r#""00:00:60""#).ok(), + Some(NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()) + ); + assert_eq!(from_str(r#""00:01:02""#).ok(), Some(NaiveTime::from_hms_opt(0, 1, 2).unwrap())); assert_eq!( from_str(r#""03:05:07.098765432""#).ok(), - Some(NaiveTime::from_hms_nano(3, 5, 7, 98765432)) + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()) ); - assert_eq!(from_str(r#""07:08:09""#).ok(), Some(NaiveTime::from_hms(7, 8, 9))); + assert_eq!(from_str(r#""07:08:09""#).ok(), Some(NaiveTime::from_hms_opt(7, 8, 9).unwrap())); assert_eq!( from_str(r#""12:34:56.000789""#).ok(), - Some(NaiveTime::from_hms_micro(12, 34, 56, 789)) + Some(NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()) ); assert_eq!( from_str(r#""23:59:60.999999999""#).ok(), - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)) + Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) ); assert_eq!( from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(NaiveTime::from_hms_nano(23, 59, 59, 1_999_999_999)) + Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) ); // bad formats @@ -1418,397 +1388,3 @@ where assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); assert!(from_str(r#"null"#).is_err()); } - -#[cfg(feature = "rustc-serialize")] -mod rustc_serialize { - use super::NaiveTime; - use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; - - impl Encodable for NaiveTime { - fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { - format!("{:?}", self).encode(s) - } - } - - impl Decodable for NaiveTime { - fn decode<D: Decoder>(d: &mut D) -> Result<NaiveTime, D::Error> { - d.read_str()?.parse().map_err(|_| d.error("invalid time")) - } - } - - #[cfg(test)] - use rustc_serialize::json; - - #[test] - fn test_encodable() { - super::test_encodable_json(json::encode); - } - - #[test] - fn test_decodable() { - super::test_decodable_json(json::decode); - } -} - -#[cfg(feature = "serde")] -mod serde { - use super::NaiveTime; - use core::fmt; - use serdelib::{de, ser}; - - // TODO not very optimized for space (binary formats would want something better) - // TODO round-trip for general leap seconds (not just those with second = 60) - - impl ser::Serialize for NaiveTime { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: ser::Serializer, - { - serializer.collect_str(&self) - } - } - - struct NaiveTimeVisitor; - - impl<'de> de::Visitor<'de> for NaiveTimeVisitor { - type Value = NaiveTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a formatted time string") - } - - fn visit_str<E>(self, value: &str) -> Result<NaiveTime, E> - where - E: de::Error, - { - value.parse().map_err(E::custom) - } - } - - impl<'de> de::Deserialize<'de> for NaiveTime { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(NaiveTimeVisitor) - } - } - - #[cfg(test)] - extern crate bincode; - #[cfg(test)] - extern crate serde_json; - - #[test] - fn test_serde_serialize() { - super::test_encodable_json(self::serde_json::to_string); - } - - #[test] - fn test_serde_deserialize() { - super::test_decodable_json(|input| self::serde_json::from_str(&input)); - } - - #[test] - fn test_serde_bincode() { - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - use self::bincode::{deserialize, serialize, Infinite}; - - let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); - let encoded = serialize(&t, Infinite).unwrap(); - let decoded: NaiveTime = deserialize(&encoded).unwrap(); - assert_eq!(t, decoded); - } -} - -#[cfg(test)] -mod tests { - use super::NaiveTime; - use oldtime::Duration; - use std::u32; - use Timelike; - - #[test] - fn test_time_from_hms_milli() { - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 7, 0), - Some(NaiveTime::from_hms_nano(3, 5, 7, 0)) - ); - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 7, 777), - Some(NaiveTime::from_hms_nano(3, 5, 7, 777_000_000)) - ); - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 7, 1_999), - Some(NaiveTime::from_hms_nano(3, 5, 7, 1_999_000_000)) - ); - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 2_000), None); - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 5_000), None); // overflow check - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, u32::MAX), None); - } - - #[test] - fn test_time_from_hms_micro() { - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 0), - Some(NaiveTime::from_hms_nano(3, 5, 7, 0)) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 333), - Some(NaiveTime::from_hms_nano(3, 5, 7, 333_000)) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777), - Some(NaiveTime::from_hms_nano(3, 5, 7, 777_777_000)) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 1_999_999), - Some(NaiveTime::from_hms_nano(3, 5, 7, 1_999_999_000)) - ); - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 2_000_000), None); - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 5_000_000), None); // overflow check - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, u32::MAX), None); - } - - #[test] - fn test_time_hms() { - assert_eq!(NaiveTime::from_hms(3, 5, 7).hour(), 3); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(0), Some(NaiveTime::from_hms(0, 5, 7))); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(23), Some(NaiveTime::from_hms(23, 5, 7))); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(24), None); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_hour(u32::MAX), None); - - assert_eq!(NaiveTime::from_hms(3, 5, 7).minute(), 5); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(0), Some(NaiveTime::from_hms(3, 0, 7))); - assert_eq!( - NaiveTime::from_hms(3, 5, 7).with_minute(59), - Some(NaiveTime::from_hms(3, 59, 7)) - ); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(60), None); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_minute(u32::MAX), None); - - assert_eq!(NaiveTime::from_hms(3, 5, 7).second(), 7); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(0), Some(NaiveTime::from_hms(3, 5, 0))); - assert_eq!( - NaiveTime::from_hms(3, 5, 7).with_second(59), - Some(NaiveTime::from_hms(3, 5, 59)) - ); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(60), None); - assert_eq!(NaiveTime::from_hms(3, 5, 7).with_second(u32::MAX), None); - } - - #[test] - fn test_time_add() { - macro_rules! check { - ($lhs:expr, $rhs:expr, $sum:expr) => {{ - assert_eq!($lhs + $rhs, $sum); - //assert_eq!($rhs + $lhs, $sum); - }}; - } - - let hmsm = |h, m, s, mi| NaiveTime::from_hms_milli(h, m, s, mi); - - check!(hmsm(3, 5, 7, 900), Duration::zero(), hmsm(3, 5, 7, 900)); - check!(hmsm(3, 5, 7, 900), Duration::milliseconds(100), hmsm(3, 5, 8, 0)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-1800), hmsm(3, 5, 6, 500)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-800), hmsm(3, 5, 7, 500)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-100), hmsm(3, 5, 7, 1_200)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(100), hmsm(3, 5, 7, 1_400)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(800), hmsm(3, 5, 8, 100)); - check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(1800), hmsm(3, 5, 9, 100)); - check!(hmsm(3, 5, 7, 900), Duration::seconds(86399), hmsm(3, 5, 6, 900)); // overwrap - check!(hmsm(3, 5, 7, 900), Duration::seconds(-86399), hmsm(3, 5, 8, 900)); - check!(hmsm(3, 5, 7, 900), Duration::days(12345), hmsm(3, 5, 7, 900)); - check!(hmsm(3, 5, 7, 1_300), Duration::days(1), hmsm(3, 5, 7, 300)); - check!(hmsm(3, 5, 7, 1_300), Duration::days(-1), hmsm(3, 5, 8, 300)); - - // regression tests for #37 - check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-990), hmsm(23, 59, 59, 10)); - check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-9990), hmsm(23, 59, 50, 10)); - } - - #[test] - fn test_time_overflowing_add() { - let hmsm = NaiveTime::from_hms_milli; - - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(11)), - (hmsm(14, 4, 5, 678), 0) - ); - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(23)), - (hmsm(2, 4, 5, 678), 86_400) - ); - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(-7)), - (hmsm(20, 4, 5, 678), -86_400) - ); - - // overflowing_add_signed with leap seconds may be counter-intuitive - assert_eq!( - hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(1)), - (hmsm(3, 4, 5, 678), 86_400) - ); - assert_eq!( - hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(-1)), - (hmsm(3, 4, 6, 678), -86_400) - ); - } - - #[test] - fn test_time_addassignment() { - let hms = NaiveTime::from_hms; - let mut time = hms(12, 12, 12); - time += Duration::hours(10); - assert_eq!(time, hms(22, 12, 12)); - time += Duration::hours(10); - assert_eq!(time, hms(8, 12, 12)); - } - - #[test] - fn test_time_subassignment() { - let hms = NaiveTime::from_hms; - let mut time = hms(12, 12, 12); - time -= Duration::hours(10); - assert_eq!(time, hms(2, 12, 12)); - time -= Duration::hours(10); - assert_eq!(time, hms(16, 12, 12)); - } - - #[test] - fn test_time_sub() { - macro_rules! check { - ($lhs:expr, $rhs:expr, $diff:expr) => {{ - // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration` - assert_eq!($lhs.signed_duration_since($rhs), $diff); - assert_eq!($rhs.signed_duration_since($lhs), -$diff); - }}; - } - - let hmsm = |h, m, s, mi| NaiveTime::from_hms_milli(h, m, s, mi); - - check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), Duration::zero()); - check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), Duration::milliseconds(300)); - check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), Duration::seconds(3600 + 60 + 1)); - check!( - hmsm(3, 5, 7, 200), - hmsm(2, 4, 6, 300), - Duration::seconds(3600 + 60) + Duration::milliseconds(900) - ); - - // treats the leap second as if it coincides with the prior non-leap second, - // as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence. - check!(hmsm(3, 5, 7, 200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(400)); - check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(1400)); - check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), Duration::milliseconds(1400)); - - // additional equality: `time1 + duration = time2` is equivalent to - // `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second. - assert_eq!(hmsm(3, 5, 6, 800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); - assert_eq!(hmsm(3, 5, 6, 1_800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); - } - - #[test] - fn test_time_fmt() { - assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 999)), "23:59:59.999"); - assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_000)), "23:59:60"); - assert_eq!(format!("{}", NaiveTime::from_hms_milli(23, 59, 59, 1_001)), "23:59:60.001"); - assert_eq!(format!("{}", NaiveTime::from_hms_micro(0, 0, 0, 43210)), "00:00:00.043210"); - assert_eq!(format!("{}", NaiveTime::from_hms_nano(0, 0, 0, 6543210)), "00:00:00.006543210"); - - // the format specifier should have no effect on `NaiveTime` - assert_eq!(format!("{:30}", NaiveTime::from_hms_milli(3, 5, 7, 9)), "03:05:07.009"); - } - - #[test] - fn test_date_from_str() { - // valid cases - let valid = [ - "0:0:0", - "0:0:0.0000000", - "0:0:0.0000003", - " 4 : 3 : 2.1 ", - " 09:08:07 ", - " 9:8:07 ", - "23:59:60.373929310237", - ]; - for &s in &valid { - let d = match s.parse::<NaiveTime>() { - Ok(d) => d, - Err(e) => panic!("parsing `{}` has failed: {}", s, e), - }; - let s_ = format!("{:?}", d); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::<NaiveTime>() { - Ok(d) => d, - Err(e) => { - panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) - } - }; - assert!( - d == d_, - "`{}` is parsed into `{:?}`, but reparsed result \ - `{:?}` does not match", - s, - d, - d_ - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - assert!("".parse::<NaiveTime>().is_err()); - assert!("x".parse::<NaiveTime>().is_err()); - assert!("15".parse::<NaiveTime>().is_err()); - assert!("15:8".parse::<NaiveTime>().is_err()); - assert!("15:8:x".parse::<NaiveTime>().is_err()); - assert!("15:8:9x".parse::<NaiveTime>().is_err()); - assert!("23:59:61".parse::<NaiveTime>().is_err()); - assert!("12:34:56.x".parse::<NaiveTime>().is_err()); - assert!("12:34:56. 0".parse::<NaiveTime>().is_err()); - } - - #[test] - fn test_time_parse_from_str() { - let hms = |h, m, s| NaiveTime::from_hms(h, m, s); - assert_eq!( - NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(hms(12, 34, 56)) - ); // ignore date and offset - assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0))); - assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); - } - - #[test] - fn test_time_format() { - let t = NaiveTime::from_hms_nano(3, 5, 7, 98765432); - assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM"); - assert_eq!(t.format("%M").to_string(), "05"); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432"); - assert_eq!(t.format("%R").to_string(), "03:05"); - assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07"); - assert_eq!(t.format("%r").to_string(), "03:05:07 AM"); - assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); - - let t = NaiveTime::from_hms_micro(3, 5, 7, 432100); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000"); - - let t = NaiveTime::from_hms_milli(3, 5, 7, 210); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000"); - - let t = NaiveTime::from_hms(3, 5, 7); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000"); - - // corner cases - assert_eq!(NaiveTime::from_hms(13, 57, 9).format("%r").to_string(), "01:57:09 PM"); - assert_eq!( - NaiveTime::from_hms_milli(23, 59, 59, 1_000).format("%X").to_string(), - "23:59:60" - ); - } -} diff --git a/vendor/chrono/src/naive/time/rustc_serialize.rs b/vendor/chrono/src/naive/time/rustc_serialize.rs new file mode 100644 index 000000000..9eaf68219 --- /dev/null +++ b/vendor/chrono/src/naive/time/rustc_serialize.rs @@ -0,0 +1,29 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "rustc-serialize")))] + +use super::NaiveTime; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + +impl Encodable for NaiveTime { + fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { + format!("{:?}", self).encode(s) + } +} + +impl Decodable for NaiveTime { + fn decode<D: Decoder>(d: &mut D) -> Result<NaiveTime, D::Error> { + d.read_str()?.parse().map_err(|_| d.error("invalid time")) + } +} + +#[cfg(test)] +use rustc_serialize::json; + +#[test] +fn test_encodable() { + super::test_encodable_json(json::encode); +} + +#[test] +fn test_decodable() { + super::test_decodable_json(json::decode); +} diff --git a/vendor/chrono/src/naive/time/serde.rs b/vendor/chrono/src/naive/time/serde.rs new file mode 100644 index 000000000..c7394fb57 --- /dev/null +++ b/vendor/chrono/src/naive/time/serde.rs @@ -0,0 +1,65 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "serde")))] + +use super::NaiveTime; +use core::fmt; +use serde::{de, ser}; + +// TODO not very optimized for space (binary formats would want something better) +// TODO round-trip for general leap seconds (not just those with second = 60) + +impl ser::Serialize for NaiveTime { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.collect_str(&self) + } +} + +struct NaiveTimeVisitor; + +impl<'de> de::Visitor<'de> for NaiveTimeVisitor { + type Value = NaiveTime; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a formatted time string") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + value.parse().map_err(E::custom) + } +} + +impl<'de> de::Deserialize<'de> for NaiveTime { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(NaiveTimeVisitor) + } +} + +#[test] +fn test_serde_serialize() { + super::test_encodable_json(serde_json::to_string); +} + +#[test] +fn test_serde_deserialize() { + super::test_decodable_json(|input| serde_json::from_str(input)); +} + +#[test] +fn test_serde_bincode() { + // Bincode is relevant to test separately from JSON because + // it is not self-describing. + use bincode::{deserialize, serialize}; + + let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap(); + let encoded = serialize(&t).unwrap(); + let decoded: NaiveTime = deserialize(&encoded).unwrap(); + assert_eq!(t, decoded); +} diff --git a/vendor/chrono/src/naive/time/tests.rs b/vendor/chrono/src/naive/time/tests.rs new file mode 100644 index 000000000..62c46a247 --- /dev/null +++ b/vendor/chrono/src/naive/time/tests.rs @@ -0,0 +1,317 @@ +use super::NaiveTime; +use crate::oldtime::Duration; +use crate::Timelike; +use std::u32; + +#[test] +fn test_time_from_hms_milli() { + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 0), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 777), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_000_000).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_milli_opt(3, 5, 7, 1_999), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 1_999_000_000).unwrap()) + ); + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 2_000), None); + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, 5_000), None); // overflow check + assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 7, u32::MAX), None); +} + +#[test] +fn test_time_from_hms_micro() { + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 0), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 333), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 333_000).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_777_000).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_micro_opt(3, 5, 7, 1_999_999), + Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 1_999_999_000).unwrap()) + ); + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 2_000_000), None); + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, 5_000_000), None); // overflow check + assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 7, u32::MAX), None); +} + +#[test] +fn test_time_hms() { + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().hour(), 3); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(0), + Some(NaiveTime::from_hms_opt(0, 5, 7).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(23), + Some(NaiveTime::from_hms_opt(23, 5, 7).unwrap()) + ); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(24), None); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(u32::MAX), None); + + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().minute(), 5); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(0), + Some(NaiveTime::from_hms_opt(3, 0, 7).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(59), + Some(NaiveTime::from_hms_opt(3, 59, 7).unwrap()) + ); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(60), None); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(u32::MAX), None); + + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().second(), 7); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(0), + Some(NaiveTime::from_hms_opt(3, 5, 0).unwrap()) + ); + assert_eq!( + NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(59), + Some(NaiveTime::from_hms_opt(3, 5, 59).unwrap()) + ); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(60), None); + assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(u32::MAX), None); +} + +#[test] +fn test_time_add() { + macro_rules! check { + ($lhs:expr, $rhs:expr, $sum:expr) => {{ + assert_eq!($lhs + $rhs, $sum); + //assert_eq!($rhs + $lhs, $sum); + }}; + } + + let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); + + check!(hmsm(3, 5, 7, 900), Duration::zero(), hmsm(3, 5, 7, 900)); + check!(hmsm(3, 5, 7, 900), Duration::milliseconds(100), hmsm(3, 5, 8, 0)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-1800), hmsm(3, 5, 6, 500)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-800), hmsm(3, 5, 7, 500)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(-100), hmsm(3, 5, 7, 1_200)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(100), hmsm(3, 5, 7, 1_400)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(800), hmsm(3, 5, 8, 100)); + check!(hmsm(3, 5, 7, 1_300), Duration::milliseconds(1800), hmsm(3, 5, 9, 100)); + check!(hmsm(3, 5, 7, 900), Duration::seconds(86399), hmsm(3, 5, 6, 900)); // overwrap + check!(hmsm(3, 5, 7, 900), Duration::seconds(-86399), hmsm(3, 5, 8, 900)); + check!(hmsm(3, 5, 7, 900), Duration::days(12345), hmsm(3, 5, 7, 900)); + check!(hmsm(3, 5, 7, 1_300), Duration::days(1), hmsm(3, 5, 7, 300)); + check!(hmsm(3, 5, 7, 1_300), Duration::days(-1), hmsm(3, 5, 8, 300)); + + // regression tests for #37 + check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-990), hmsm(23, 59, 59, 10)); + check!(hmsm(0, 0, 0, 0), Duration::milliseconds(-9990), hmsm(23, 59, 50, 10)); +} + +#[test] +fn test_time_overflowing_add() { + let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); + + assert_eq!( + hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(11)), + (hmsm(14, 4, 5, 678), 0) + ); + assert_eq!( + hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(23)), + (hmsm(2, 4, 5, 678), 86_400) + ); + assert_eq!( + hmsm(3, 4, 5, 678).overflowing_add_signed(Duration::hours(-7)), + (hmsm(20, 4, 5, 678), -86_400) + ); + + // overflowing_add_signed with leap seconds may be counter-intuitive + assert_eq!( + hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(1)), + (hmsm(3, 4, 5, 678), 86_400) + ); + assert_eq!( + hmsm(3, 4, 5, 1_678).overflowing_add_signed(Duration::days(-1)), + (hmsm(3, 4, 6, 678), -86_400) + ); +} + +#[test] +fn test_time_addassignment() { + let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); + let mut time = hms(12, 12, 12); + time += Duration::hours(10); + assert_eq!(time, hms(22, 12, 12)); + time += Duration::hours(10); + assert_eq!(time, hms(8, 12, 12)); +} + +#[test] +fn test_time_subassignment() { + let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); + let mut time = hms(12, 12, 12); + time -= Duration::hours(10); + assert_eq!(time, hms(2, 12, 12)); + time -= Duration::hours(10); + assert_eq!(time, hms(16, 12, 12)); +} + +#[test] +fn test_time_sub() { + macro_rules! check { + ($lhs:expr, $rhs:expr, $diff:expr) => {{ + // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration` + assert_eq!($lhs.signed_duration_since($rhs), $diff); + assert_eq!($rhs.signed_duration_since($lhs), -$diff); + }}; + } + + let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); + + check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), Duration::zero()); + check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), Duration::milliseconds(300)); + check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), Duration::seconds(3600 + 60 + 1)); + check!( + hmsm(3, 5, 7, 200), + hmsm(2, 4, 6, 300), + Duration::seconds(3600 + 60) + Duration::milliseconds(900) + ); + + // treats the leap second as if it coincides with the prior non-leap second, + // as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence. + check!(hmsm(3, 5, 7, 200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(400)); + check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), Duration::milliseconds(1400)); + check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), Duration::milliseconds(1400)); + + // additional equality: `time1 + duration = time2` is equivalent to + // `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second. + assert_eq!(hmsm(3, 5, 6, 800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); + assert_eq!(hmsm(3, 5, 6, 1_800) + Duration::milliseconds(400), hmsm(3, 5, 7, 200)); +} + +#[test] +fn test_time_fmt() { + assert_eq!( + format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 999).unwrap()), + "23:59:59.999" + ); + assert_eq!( + format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap()), + "23:59:60" + ); + assert_eq!( + format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_001).unwrap()), + "23:59:60.001" + ); + assert_eq!( + format!("{}", NaiveTime::from_hms_micro_opt(0, 0, 0, 43210).unwrap()), + "00:00:00.043210" + ); + assert_eq!( + format!("{}", NaiveTime::from_hms_nano_opt(0, 0, 0, 6543210).unwrap()), + "00:00:00.006543210" + ); + + // the format specifier should have no effect on `NaiveTime` + assert_eq!( + format!("{:30}", NaiveTime::from_hms_milli_opt(3, 5, 7, 9).unwrap()), + "03:05:07.009" + ); +} + +#[test] +fn test_date_from_str() { + // valid cases + let valid = [ + "0:0:0", + "0:0:0.0000000", + "0:0:0.0000003", + " 4 : 3 : 2.1 ", + " 09:08:07 ", + " 9:8:07 ", + "23:59:60.373929310237", + ]; + for &s in &valid { + let d = match s.parse::<NaiveTime>() { + Ok(d) => d, + Err(e) => panic!("parsing `{}` has failed: {}", s, e), + }; + let s_ = format!("{:?}", d); + // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same + let d_ = match s_.parse::<NaiveTime>() { + Ok(d) => d, + Err(e) => { + panic!("`{}` is parsed into `{:?}`, but reparsing that has failed: {}", s, d, e) + } + }; + assert!( + d == d_, + "`{}` is parsed into `{:?}`, but reparsed result \ + `{:?}` does not match", + s, + d, + d_ + ); + } + + // some invalid cases + // since `ParseErrorKind` is private, all we can do is to check if there was an error + assert!("".parse::<NaiveTime>().is_err()); + assert!("x".parse::<NaiveTime>().is_err()); + assert!("15".parse::<NaiveTime>().is_err()); + assert!("15:8".parse::<NaiveTime>().is_err()); + assert!("15:8:x".parse::<NaiveTime>().is_err()); + assert!("15:8:9x".parse::<NaiveTime>().is_err()); + assert!("23:59:61".parse::<NaiveTime>().is_err()); + assert!("12:34:56.x".parse::<NaiveTime>().is_err()); + assert!("12:34:56. 0".parse::<NaiveTime>().is_err()); +} + +#[test] +fn test_time_parse_from_str() { + let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); + assert_eq!( + NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), + Ok(hms(12, 34, 56)) + ); // ignore date and offset + assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0))); + assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); +} + +#[test] +fn test_time_format() { + let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap(); + assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM"); + assert_eq!(t.format("%M").to_string(), "05"); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432"); + assert_eq!(t.format("%R").to_string(), "03:05"); + assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07"); + assert_eq!(t.format("%r").to_string(), "03:05:07 AM"); + assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); + + let t = NaiveTime::from_hms_micro_opt(3, 5, 7, 432100).unwrap(); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000"); + + let t = NaiveTime::from_hms_milli_opt(3, 5, 7, 210).unwrap(); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000"); + + let t = NaiveTime::from_hms_opt(3, 5, 7).unwrap(); + assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,"); + assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000"); + + // corner cases + assert_eq!(NaiveTime::from_hms_opt(13, 57, 9).unwrap().format("%r").to_string(), "01:57:09 PM"); + assert_eq!( + NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().format("%X").to_string(), + "23:59:60" + ); +} diff --git a/vendor/chrono/src/offset/fixed.rs b/vendor/chrono/src/offset/fixed.rs index 83f42a1a4..0989dfa5b 100644 --- a/vendor/chrono/src/offset/fixed.rs +++ b/vendor/chrono/src/offset/fixed.rs @@ -5,21 +5,25 @@ use core::fmt; use core::ops::{Add, Sub}; -use oldtime::Duration as OldDuration; + +use num_integer::div_mod_floor; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; use super::{LocalResult, Offset, TimeZone}; -use div::div_mod_floor; -use naive::{NaiveDate, NaiveDateTime, NaiveTime}; -use DateTime; -use Timelike; +use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use crate::oldtime::Duration as OldDuration; +use crate::DateTime; +use crate::Timelike; /// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. /// /// Using the [`TimeZone`](./trait.TimeZone.html) methods /// on a `FixedOffset` struct is the preferred way to construct -/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and -/// [`west`](#method.west) methods for examples. +/// `DateTime<FixedOffset>` instances. See the [`east_opt`](#method.east_opt) and +/// [`west_opt`](#method.west_opt) methods for examples. #[derive(PartialEq, Eq, Hash, Copy, Clone)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct FixedOffset { local_minus_utc: i32, } @@ -29,16 +33,7 @@ impl FixedOffset { /// The negative `secs` means the Western Hemisphere. /// /// Panics on the out-of-bound `secs`. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{FixedOffset, TimeZone}; - /// let hour = 3600; - /// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08) - /// .and_hms(0, 0, 0); - /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00") - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `east_opt()` instead")] pub fn east(secs: i32) -> FixedOffset { FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds") } @@ -47,6 +42,16 @@ impl FixedOffset { /// The negative `secs` means the Western Hemisphere. /// /// Returns `None` on the out-of-bound `secs`. + /// + /// # Example + /// + /// ``` + /// use chrono::{FixedOffset, TimeZone}; + /// let hour = 3600; + /// let datetime = FixedOffset::east_opt(5 * hour).unwrap().ymd_opt(2016, 11, 08).unwrap() + /// .and_hms_opt(0, 0, 0).unwrap(); + /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00") + /// ``` pub fn east_opt(secs: i32) -> Option<FixedOffset> { if -86_400 < secs && secs < 86_400 { Some(FixedOffset { local_minus_utc: secs }) @@ -59,16 +64,7 @@ impl FixedOffset { /// The negative `secs` means the Eastern Hemisphere. /// /// Panics on the out-of-bound `secs`. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{FixedOffset, TimeZone}; - /// let hour = 3600; - /// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08) - /// .and_hms(0, 0, 0); - /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00") - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `west_opt()` instead")] pub fn west(secs: i32) -> FixedOffset { FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds") } @@ -77,6 +73,16 @@ impl FixedOffset { /// The negative `secs` means the Eastern Hemisphere. /// /// Returns `None` on the out-of-bound `secs`. + /// + /// # Example + /// + /// ``` + /// use chrono::{FixedOffset, TimeZone}; + /// let hour = 3600; + /// let datetime = FixedOffset::west_opt(5 * hour).unwrap().ymd_opt(2016, 11, 08).unwrap() + /// .and_hms_opt(0, 0, 0).unwrap(); + /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00") + /// ``` pub fn west_opt(secs: i32) -> Option<FixedOffset> { if -86_400 < secs && secs < 86_400 { Some(FixedOffset { local_minus_utc: -secs }) @@ -87,13 +93,13 @@ impl FixedOffset { /// Returns the number of seconds to add to convert from UTC to the local time. #[inline] - pub fn local_minus_utc(&self) -> i32 { + pub const fn local_minus_utc(&self) -> i32 { self.local_minus_utc } /// Returns the number of seconds to add to convert from the local time to UTC. #[inline] - pub fn utc_minus_local(&self) -> i32 { + pub const fn utc_minus_local(&self) -> i32 { -self.local_minus_utc } } @@ -146,6 +152,16 @@ impl fmt::Display for FixedOffset { } } +#[cfg(feature = "arbitrary")] +impl arbitrary::Arbitrary<'_> for FixedOffset { + fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<FixedOffset> { + let secs = u.int_in_range(-86_399..=86_399)?; + let fixed_offset = FixedOffset::east_opt(secs) + .expect("Could not generate a valid chrono::FixedOffset. It looks like implementation of Arbitrary for FixedOffset is erroneous."); + Ok(fixed_offset) + } +} + // addition or subtraction of FixedOffset to/from Timelike values is the same as // adding or subtracting the offset's local_minus_utc value // but keep keeps the leap second information. @@ -218,26 +234,50 @@ impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> { #[cfg(test)] mod tests { use super::FixedOffset; - use offset::TimeZone; + use crate::offset::TimeZone; #[test] fn test_date_extreme_offset() { // starting from 0.3 we don't have an offset exceeding one day. // this makes everything easier! assert_eq!( - format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)), - "2012-02-29+23:59:59".to_string() + format!( + "{:?}", + FixedOffset::east_opt(86399) + .unwrap() + .with_ymd_and_hms(2012, 2, 29, 5, 6, 7) + .unwrap() + ), + "2012-02-29T05:06:07+23:59:59".to_string() ); assert_eq!( - format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)), + format!( + "{:?}", + FixedOffset::east_opt(86399) + .unwrap() + .with_ymd_and_hms(2012, 2, 29, 5, 6, 7) + .unwrap() + ), "2012-02-29T05:06:07+23:59:59".to_string() ); assert_eq!( - format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)), - "2012-03-04-23:59:59".to_string() + format!( + "{:?}", + FixedOffset::west_opt(86399) + .unwrap() + .with_ymd_and_hms(2012, 3, 4, 5, 6, 7) + .unwrap() + ), + "2012-03-04T05:06:07-23:59:59".to_string() ); assert_eq!( - format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)), + format!( + "{:?}", + FixedOffset::west_opt(86399) + .unwrap() + .with_ymd_and_hms(2012, 3, 4, 5, 6, 7) + .unwrap() + ), "2012-03-04T05:06:07-23:59:59".to_string() ); } diff --git a/vendor/chrono/src/offset/local.rs b/vendor/chrono/src/offset/local.rs deleted file mode 100644 index 1abb3a9db..000000000 --- a/vendor/chrono/src/offset/local.rs +++ /dev/null @@ -1,227 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! The local (system) time zone. - -#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] -use sys::{self, Timespec}; - -use super::fixed::FixedOffset; -use super::{LocalResult, TimeZone}; -#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] -use naive::NaiveTime; -use naive::{NaiveDate, NaiveDateTime}; -use {Date, DateTime}; -#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] -use {Datelike, Timelike}; - -/// Converts a `time::Tm` struct into the timezone-aware `DateTime`. -/// This assumes that `time` is working correctly, i.e. any error is fatal. -#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] -fn tm_to_datetime(mut tm: sys::Tm) -> DateTime<Local> { - if tm.tm_sec >= 60 { - tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000; - tm.tm_sec = 59; - } - - #[cfg(not(windows))] - fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate { - // from_yo is more efficient than from_ymd (since it's the internal representation). - NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1) - } - - #[cfg(windows)] - fn tm_to_naive_date(tm: &sys::Tm) -> NaiveDate { - // ...but tm_yday is broken in Windows (issue #85) - NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32) - } - - let date = tm_to_naive_date(&tm); - let time = NaiveTime::from_hms_nano( - tm.tm_hour as u32, - tm.tm_min as u32, - tm.tm_sec as u32, - tm.tm_nsec as u32, - ); - let offset = FixedOffset::east(tm.tm_utcoff); - DateTime::from_utc(date.and_time(time) - offset, offset) -} - -/// Converts a local `NaiveDateTime` to the `time::Timespec`. -#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] -fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> sys::Timespec { - // well, this exploits an undocumented `Tm::to_timespec` behavior - // to get the exact function we want (either `timegm` or `mktime`). - // the number 1 is arbitrary but should be non-zero to trigger `mktime`. - let tm_utcoff = if local { 1 } else { 0 }; - - let tm = sys::Tm { - tm_sec: d.second() as i32, - tm_min: d.minute() as i32, - tm_hour: d.hour() as i32, - tm_mday: d.day() as i32, - tm_mon: d.month0() as i32, // yes, C is that strange... - tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`. - tm_wday: 0, // to_local ignores this - tm_yday: 0, // and this - tm_isdst: -1, - tm_utcoff: tm_utcoff, - // do not set this, OS APIs are heavily inconsistent in terms of leap second handling - tm_nsec: 0, - }; - - tm.to_timespec() -} - -/// The local timescale. This is implemented via the standard `time` crate. -/// -/// Using the [`TimeZone`](./trait.TimeZone.html) methods -/// on the Local struct is the preferred way to construct `DateTime<Local>` -/// instances. -/// -/// # Example -/// -/// ~~~~ -/// use chrono::{Local, DateTime, TimeZone}; -/// -/// let dt: DateTime<Local> = Local::now(); -/// let dt: DateTime<Local> = Local.timestamp(0, 0); -/// ~~~~ -#[derive(Copy, Clone, Debug)] -pub struct Local; - -impl Local { - /// Returns a `Date` which corresponds to the current date. - pub fn today() -> Date<Local> { - Local::now().date() - } - - /// Returns a `DateTime` which corresponds to the current date. - #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] - pub fn now() -> DateTime<Local> { - tm_to_datetime(Timespec::now().local()) - } - - /// Returns a `DateTime` which corresponds to the current date. - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] - pub fn now() -> DateTime<Local> { - use super::Utc; - let now: DateTime<Utc> = super::Utc::now(); - - // Workaround missing timezone logic in `time` crate - let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); - DateTime::from_utc(now.naive_utc(), offset) - } -} - -impl TimeZone for Local { - type Offset = FixedOffset; - - fn from_offset(_offset: &FixedOffset) -> Local { - Local - } - - // they are easier to define in terms of the finished date and time unlike other offsets - fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> { - self.from_local_date(local).map(|date| *date.offset()) - } - - fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> { - self.from_local_datetime(local).map(|datetime| *datetime.offset()) - } - - fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset { - *self.from_utc_date(utc).offset() - } - - fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset { - *self.from_utc_datetime(utc).offset() - } - - // override them for avoiding redundant works - fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> { - // this sounds very strange, but required for keeping `TimeZone::ymd` sane. - // in the other words, we use the offset at the local midnight - // but keep the actual date unaltered (much like `FixedOffset`). - let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0)); - midnight.map(|datetime| Date::from_utc(*local, *datetime.offset())) - } - - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] - fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> { - let mut local = local.clone(); - // Get the offset from the js runtime - let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); - local -= ::Duration::seconds(offset.local_minus_utc() as i64); - LocalResult::Single(DateTime::from_utc(local, offset)) - } - - #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] - fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> { - let timespec = datetime_to_timespec(local, true); - - // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them - let mut tm = timespec.local(); - assert_eq!(tm.tm_nsec, 0); - tm.tm_nsec = local.nanosecond() as i32; - - LocalResult::Single(tm_to_datetime(tm)) - } - - fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> { - let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0)); - Date::from_utc(*utc, *midnight.offset()) - } - - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] - fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> { - // Get the offset from the js runtime - let offset = FixedOffset::west((js_sys::Date::new_0().get_timezone_offset() as i32) * 60); - DateTime::from_utc(*utc, offset) - } - - #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] - fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> { - let timespec = datetime_to_timespec(utc, false); - - // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them - let mut tm = timespec.local(); - assert_eq!(tm.tm_nsec, 0); - tm.tm_nsec = utc.nanosecond() as i32; - - tm_to_datetime(tm) - } -} - -#[cfg(test)] -mod tests { - use super::Local; - use offset::TimeZone; - use Datelike; - - #[test] - fn test_local_date_sanity_check() { - // issue #27 - assert_eq!(Local.ymd(2999, 12, 28).day(), 28); - } - - #[test] - fn test_leap_second() { - // issue #123 - let today = Local::today(); - - let dt = today.and_hms_milli(1, 2, 59, 1000); - let timestr = dt.time().to_string(); - // the OS API may or may not support the leap second, - // but there are only two sensible options. - assert!(timestr == "01:02:60" || timestr == "01:03:00", "unexpected timestr {:?}", timestr); - - let dt = today.and_hms_milli(1, 2, 3, 1234); - let timestr = dt.time().to_string(); - assert!( - timestr == "01:02:03.234" || timestr == "01:02:04.234", - "unexpected timestr {:?}", - timestr - ); - } -} diff --git a/vendor/chrono/src/offset/local/mod.rs b/vendor/chrono/src/offset/local/mod.rs new file mode 100644 index 000000000..e280c7800 --- /dev/null +++ b/vendor/chrono/src/offset/local/mod.rs @@ -0,0 +1,260 @@ +// This is a part of Chrono. +// See README.md and LICENSE.txt for details. + +//! The local (system) time zone. + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +use super::fixed::FixedOffset; +use super::{LocalResult, TimeZone}; +use crate::naive::{NaiveDate, NaiveDateTime}; +#[allow(deprecated)] +use crate::{Date, DateTime}; + +// we don't want `stub.rs` when the target_os is not wasi or emscripten +// as we use js-sys to get the date instead +#[cfg(all( + not(unix), + not(windows), + not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )) +))] +#[path = "stub.rs"] +mod inner; + +#[cfg(unix)] +#[path = "unix.rs"] +mod inner; + +#[cfg(windows)] +#[path = "windows.rs"] +mod inner; + +#[cfg(unix)] +mod tz_info; + +/// The local timescale. This is implemented via the standard `time` crate. +/// +/// Using the [`TimeZone`](./trait.TimeZone.html) methods +/// on the Local struct is the preferred way to construct `DateTime<Local>` +/// instances. +/// +/// # Example +/// +/// ``` +/// use chrono::{Local, DateTime, TimeZone}; +/// +/// let dt: DateTime<Local> = Local::now(); +/// let dt: DateTime<Local> = Local.timestamp(0, 0); +/// ``` +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub struct Local; + +impl Local { + /// Returns a `Date` which corresponds to the current date. + #[deprecated(since = "0.4.23", note = "use `Local::now()` instead")] + #[allow(deprecated)] + pub fn today() -> Date<Local> { + Local::now().date() + } + + /// Returns a `DateTime` which corresponds to the current date and time. + #[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )))] + pub fn now() -> DateTime<Local> { + inner::now() + } + + /// Returns a `DateTime` which corresponds to the current date and time. + #[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))] + pub fn now() -> DateTime<Local> { + use super::Utc; + let now: DateTime<Utc> = super::Utc::now(); + + // Workaround missing timezone logic in `time` crate + let offset = + FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60) + .unwrap(); + DateTime::from_utc(now.naive_utc(), offset) + } +} + +impl TimeZone for Local { + type Offset = FixedOffset; + + fn from_offset(_offset: &FixedOffset) -> Local { + Local + } + + // they are easier to define in terms of the finished date and time unlike other offsets + #[allow(deprecated)] + fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> { + self.from_local_date(local).map(|date| *date.offset()) + } + + fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> { + self.from_local_datetime(local).map(|datetime| *datetime.offset()) + } + + #[allow(deprecated)] + fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset { + *self.from_utc_date(utc).offset() + } + + fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset { + *self.from_utc_datetime(utc).offset() + } + + // override them for avoiding redundant works + #[allow(deprecated)] + fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> { + // this sounds very strange, but required for keeping `TimeZone::ymd` sane. + // in the other words, we use the offset at the local midnight + // but keep the actual date unaltered (much like `FixedOffset`). + let midnight = self.from_local_datetime(&local.and_hms_opt(0, 0, 0).unwrap()); + midnight.map(|datetime| Date::from_utc(*local, *datetime.offset())) + } + + #[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))] + fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> { + let mut local = local.clone(); + // Get the offset from the js runtime + let offset = + FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60) + .unwrap(); + local -= crate::Duration::seconds(offset.local_minus_utc() as i64); + LocalResult::Single(DateTime::from_utc(local, offset)) + } + + #[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )))] + fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> { + inner::naive_to_local(local, true) + } + + #[allow(deprecated)] + fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> { + let midnight = self.from_utc_datetime(&utc.and_hms_opt(0, 0, 0).unwrap()); + Date::from_utc(*utc, *midnight.offset()) + } + + #[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))] + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> { + // Get the offset from the js runtime + let offset = + FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60) + .unwrap(); + DateTime::from_utc(*utc, offset) + } + + #[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )))] + fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> { + // this is OK to unwrap as getting local time from a UTC + // timestamp is never ambiguous + inner::naive_to_local(utc, false).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::Local; + use crate::offset::TimeZone; + use crate::{Datelike, Duration, Utc}; + + #[test] + fn verify_correct_offsets() { + let now = Local::now(); + let from_local = Local.from_local_datetime(&now.naive_local()).unwrap(); + let from_utc = Local.from_utc_datetime(&now.naive_utc()); + + assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc()); + assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); + + assert_eq!(now, from_local); + assert_eq!(now, from_utc); + } + + #[test] + fn verify_correct_offsets_distant_past() { + // let distant_past = Local::now() - Duration::days(365 * 100); + let distant_past = Local::now() - Duration::days(250 * 31); + let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap(); + let from_utc = Local.from_utc_datetime(&distant_past.naive_utc()); + + assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc()); + assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); + + assert_eq!(distant_past, from_local); + assert_eq!(distant_past, from_utc); + } + + #[test] + fn verify_correct_offsets_distant_future() { + let distant_future = Local::now() + Duration::days(250 * 31); + let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap(); + let from_utc = Local.from_utc_datetime(&distant_future.naive_utc()); + + assert_eq!( + distant_future.offset().local_minus_utc(), + from_local.offset().local_minus_utc() + ); + assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); + + assert_eq!(distant_future, from_local); + assert_eq!(distant_future, from_utc); + } + + #[test] + fn test_local_date_sanity_check() { + // issue #27 + assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28); + } + + #[test] + fn test_leap_second() { + // issue #123 + let today = Utc::now().date_naive(); + + let dt = today.and_hms_milli_opt(1, 2, 59, 1000).unwrap(); + let timestr = dt.time().to_string(); + // the OS API may or may not support the leap second, + // but there are only two sensible options. + assert!(timestr == "01:02:60" || timestr == "01:03:00", "unexpected timestr {:?}", timestr); + + let dt = today.and_hms_milli_opt(1, 2, 3, 1234).unwrap(); + let timestr = dt.time().to_string(); + assert!( + timestr == "01:02:03.234" || timestr == "01:02:04.234", + "unexpected timestr {:?}", + timestr + ); + } +} diff --git a/vendor/chrono/src/offset/local/stub.rs b/vendor/chrono/src/offset/local/stub.rs new file mode 100644 index 000000000..9ececd3c2 --- /dev/null +++ b/vendor/chrono/src/offset/local/stub.rs @@ -0,0 +1,236 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::time::{SystemTime, UNIX_EPOCH}; + +use super::{FixedOffset, Local}; +use crate::{DateTime, Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; + +pub(super) fn now() -> DateTime<Local> { + tm_to_datetime(Timespec::now().local()) +} + +/// Converts a local `NaiveDateTime` to the `time::Timespec`. +#[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +)))] +pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> { + let tm = Tm { + tm_sec: d.second() as i32, + tm_min: d.minute() as i32, + tm_hour: d.hour() as i32, + tm_mday: d.day() as i32, + tm_mon: d.month0() as i32, // yes, C is that strange... + tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`. + tm_wday: 0, // to_local ignores this + tm_yday: 0, // and this + tm_isdst: -1, + // This seems pretty fake? + tm_utcoff: if local { 1 } else { 0 }, + // do not set this, OS APIs are heavily inconsistent in terms of leap second handling + tm_nsec: 0, + }; + + let spec = Timespec { + sec: match local { + false => utc_tm_to_time(&tm), + true => local_tm_to_time(&tm), + }, + nsec: tm.tm_nsec, + }; + + // Adjust for leap seconds + let mut tm = spec.local(); + assert_eq!(tm.tm_nsec, 0); + tm.tm_nsec = d.nanosecond() as i32; + + LocalResult::Single(tm_to_datetime(tm)) +} + +/// Converts a `time::Tm` struct into the timezone-aware `DateTime`. +/// This assumes that `time` is working correctly, i.e. any error is fatal. +#[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +)))] +fn tm_to_datetime(mut tm: Tm) -> DateTime<Local> { + if tm.tm_sec >= 60 { + tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000; + tm.tm_sec = 59; + } + + let date = NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1); + let time = NaiveTime::from_hms_nano( + tm.tm_hour as u32, + tm.tm_min as u32, + tm.tm_sec as u32, + tm.tm_nsec as u32, + ); + + let offset = FixedOffset::east_opt(tm.tm_utcoff).unwrap(); + DateTime::from_utc(date.and_time(time) - offset, offset) +} + +/// A record specifying a time value in seconds and nanoseconds, where +/// nanoseconds represent the offset from the given second. +/// +/// For example a timespec of 1.2 seconds after the beginning of the epoch would +/// be represented as {sec: 1, nsec: 200000000}. +struct Timespec { + sec: i64, + nsec: i32, +} + +impl Timespec { + /// Constructs a timespec representing the current time in UTC. + fn now() -> Timespec { + let st = + SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); + Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 } + } + + /// Converts this timespec into the system's local time. + fn local(self) -> Tm { + let mut tm = Tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_utcoff: 0, + tm_nsec: 0, + }; + time_to_local_tm(self.sec, &mut tm); + tm.tm_nsec = self.nsec; + tm + } +} + +/// Holds a calendar date and time broken down into its components (year, month, +/// day, and so on), also called a broken-down time value. +// FIXME: use c_int instead of i32? +#[repr(C)] +pub(super) struct Tm { + /// Seconds after the minute - [0, 60] + tm_sec: i32, + + /// Minutes after the hour - [0, 59] + tm_min: i32, + + /// Hours after midnight - [0, 23] + tm_hour: i32, + + /// Day of the month - [1, 31] + tm_mday: i32, + + /// Months since January - [0, 11] + tm_mon: i32, + + /// Years since 1900 + tm_year: i32, + + /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. + tm_wday: i32, + + /// Days since January 1 - [0, 365] + tm_yday: i32, + + /// Daylight Saving Time flag. + /// + /// This value is positive if Daylight Saving Time is in effect, zero if + /// Daylight Saving Time is not in effect, and negative if this information + /// is not available. + tm_isdst: i32, + + /// Identifies the time zone that was used to compute this broken-down time + /// value, including any adjustment for Daylight Saving Time. This is the + /// number of seconds east of UTC. For example, for U.S. Pacific Daylight + /// Time, the value is `-7*60*60 = -25200`. + tm_utcoff: i32, + + /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1] + tm_nsec: i32, +} + +fn time_to_tm(ts: i64, tm: &mut Tm) { + let leapyear = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }; + + static YTAB: [[i64; 12]; 2] = [ + [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + ]; + + let mut year = 1970; + + let dayclock = ts % 86400; + let mut dayno = ts / 86400; + + tm.tm_sec = (dayclock % 60) as i32; + tm.tm_min = ((dayclock % 3600) / 60) as i32; + tm.tm_hour = (dayclock / 3600) as i32; + tm.tm_wday = ((dayno + 4) % 7) as i32; + loop { + let yearsize = if leapyear(year) { 366 } else { 365 }; + if dayno >= yearsize { + dayno -= yearsize; + year += 1; + } else { + break; + } + } + tm.tm_year = (year - 1900) as i32; + tm.tm_yday = dayno as i32; + let mut mon = 0; + while dayno >= YTAB[if leapyear(year) { 1 } else { 0 }][mon] { + dayno -= YTAB[if leapyear(year) { 1 } else { 0 }][mon]; + mon += 1; + } + tm.tm_mon = mon as i32; + tm.tm_mday = dayno as i32 + 1; + tm.tm_isdst = 0; +} + +fn tm_to_time(tm: &Tm) -> i64 { + let mut y = tm.tm_year as i64 + 1900; + let mut m = tm.tm_mon as i64 + 1; + if m <= 2 { + y -= 1; + m += 12; + } + let d = tm.tm_mday as i64; + let h = tm.tm_hour as i64; + let mi = tm.tm_min as i64; + let s = tm.tm_sec as i64; + (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 + + 3600 * h + + 60 * mi + + s +} + +pub(super) fn time_to_local_tm(sec: i64, tm: &mut Tm) { + // FIXME: Add timezone logic + time_to_tm(sec, tm); +} + +pub(super) fn utc_tm_to_time(tm: &Tm) -> i64 { + tm_to_time(tm) +} + +pub(super) fn local_tm_to_time(tm: &Tm) -> i64 { + // FIXME: Add timezone logic + tm_to_time(tm) +} diff --git a/vendor/chrono/src/offset/local/tz_info/mod.rs b/vendor/chrono/src/offset/local/tz_info/mod.rs new file mode 100644 index 000000000..bd2693b6b --- /dev/null +++ b/vendor/chrono/src/offset/local/tz_info/mod.rs @@ -0,0 +1,131 @@ +#![deny(missing_docs)] +#![allow(dead_code)] +#![warn(unreachable_pub)] + +use std::num::ParseIntError; +use std::str::Utf8Error; +use std::time::SystemTimeError; +use std::{error, fmt, io}; + +mod timezone; +pub(crate) use timezone::TimeZone; + +mod parser; +mod rule; + +/// Unified error type for everything in the crate +#[derive(Debug)] +pub(crate) enum Error { + /// Date time error + DateTime(&'static str), + /// Local time type search error + FindLocalTimeType(&'static str), + /// Local time type error + LocalTimeType(&'static str), + /// Invalid slice for integer conversion + InvalidSlice(&'static str), + /// Invalid Tzif file + InvalidTzFile(&'static str), + /// Invalid TZ string + InvalidTzString(&'static str), + /// I/O error + Io(io::Error), + /// Out of range error + OutOfRange(&'static str), + /// Integer parsing error + ParseInt(ParseIntError), + /// Date time projection error + ProjectDateTime(&'static str), + /// System time error + SystemTime(SystemTimeError), + /// Time zone error + TimeZone(&'static str), + /// Transition rule error + TransitionRule(&'static str), + /// Unsupported Tzif file + UnsupportedTzFile(&'static str), + /// Unsupported TZ string + UnsupportedTzString(&'static str), + /// UTF-8 error + Utf8(Utf8Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Error::*; + match self { + DateTime(error) => write!(f, "invalid date time: {}", error), + FindLocalTimeType(error) => error.fmt(f), + LocalTimeType(error) => write!(f, "invalid local time type: {}", error), + InvalidSlice(error) => error.fmt(f), + InvalidTzString(error) => write!(f, "invalid TZ string: {}", error), + InvalidTzFile(error) => error.fmt(f), + Io(error) => error.fmt(f), + OutOfRange(error) => error.fmt(f), + ParseInt(error) => error.fmt(f), + ProjectDateTime(error) => error.fmt(f), + SystemTime(error) => error.fmt(f), + TransitionRule(error) => write!(f, "invalid transition rule: {}", error), + TimeZone(error) => write!(f, "invalid time zone: {}", error), + UnsupportedTzFile(error) => error.fmt(f), + UnsupportedTzString(error) => write!(f, "unsupported TZ string: {}", error), + Utf8(error) => error.fmt(f), + } + } +} + +impl error::Error for Error {} + +impl From<io::Error> for Error { + fn from(error: io::Error) -> Self { + Error::Io(error) + } +} + +impl From<ParseIntError> for Error { + fn from(error: ParseIntError) -> Self { + Error::ParseInt(error) + } +} + +impl From<SystemTimeError> for Error { + fn from(error: SystemTimeError) -> Self { + Error::SystemTime(error) + } +} + +impl From<Utf8Error> for Error { + fn from(error: Utf8Error) -> Self { + Error::Utf8(error) + } +} + +// MSRV: 1.38 +#[inline] +fn rem_euclid(v: i64, rhs: i64) -> i64 { + let r = v % rhs; + if r < 0 { + if rhs < 0 { + r - rhs + } else { + r + rhs + } + } else { + r + } +} + +/// Number of hours in one day +const HOURS_PER_DAY: i64 = 24; +/// Number of seconds in one hour +const SECONDS_PER_HOUR: i64 = 3600; +/// Number of seconds in one day +const SECONDS_PER_DAY: i64 = SECONDS_PER_HOUR * HOURS_PER_DAY; +/// Number of days in one week +const DAYS_PER_WEEK: i64 = 7; + +/// Month days in a normal year +const DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; +/// Cumulated month days in a normal year +const CUMUL_DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = + [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; diff --git a/vendor/chrono/src/offset/local/tz_info/parser.rs b/vendor/chrono/src/offset/local/tz_info/parser.rs new file mode 100644 index 000000000..5652a0ea9 --- /dev/null +++ b/vendor/chrono/src/offset/local/tz_info/parser.rs @@ -0,0 +1,334 @@ +use std::io::{self, ErrorKind}; +use std::iter; +use std::num::ParseIntError; +use std::str::{self, FromStr}; + +use super::rule::TransitionRule; +use super::timezone::{LeapSecond, LocalTimeType, TimeZone, Transition}; +use super::Error; + +#[allow(clippy::map_clone)] // MSRV: 1.36 +pub(super) fn parse(bytes: &[u8]) -> Result<TimeZone, Error> { + let mut cursor = Cursor::new(bytes); + let state = State::new(&mut cursor, true)?; + let (state, footer) = match state.header.version { + Version::V1 => match cursor.is_empty() { + true => (state, None), + false => { + return Err(Error::InvalidTzFile("remaining data after end of TZif v1 data block")) + } + }, + Version::V2 | Version::V3 => { + let state = State::new(&mut cursor, false)?; + (state, Some(cursor.remaining())) + } + }; + + let mut transitions = Vec::with_capacity(state.header.transition_count); + for (arr_time, &local_time_type_index) in + state.transition_times.chunks_exact(state.time_size).zip(state.transition_types) + { + let unix_leap_time = + state.parse_time(&arr_time[0..state.time_size], state.header.version)?; + let local_time_type_index = local_time_type_index as usize; + transitions.push(Transition::new(unix_leap_time, local_time_type_index)); + } + + let mut local_time_types = Vec::with_capacity(state.header.type_count); + for arr in state.local_time_types.chunks_exact(6) { + let ut_offset = read_be_i32(&arr[..4])?; + + let is_dst = match arr[4] { + 0 => false, + 1 => true, + _ => return Err(Error::InvalidTzFile("invalid DST indicator")), + }; + + let char_index = arr[5] as usize; + if char_index >= state.header.char_count { + return Err(Error::InvalidTzFile("invalid time zone name char index")); + } + + let position = match state.names[char_index..].iter().position(|&c| c == b'\0') { + Some(position) => position, + None => return Err(Error::InvalidTzFile("invalid time zone name char index")), + }; + + let name = &state.names[char_index..char_index + position]; + let name = if !name.is_empty() { Some(name) } else { None }; + local_time_types.push(LocalTimeType::new(ut_offset, is_dst, name)?); + } + + let mut leap_seconds = Vec::with_capacity(state.header.leap_count); + for arr in state.leap_seconds.chunks_exact(state.time_size + 4) { + let unix_leap_time = state.parse_time(&arr[0..state.time_size], state.header.version)?; + let correction = read_be_i32(&arr[state.time_size..state.time_size + 4])?; + leap_seconds.push(LeapSecond::new(unix_leap_time, correction)); + } + + let std_walls_iter = state.std_walls.iter().map(|&i| i).chain(iter::repeat(0)); + let ut_locals_iter = state.ut_locals.iter().map(|&i| i).chain(iter::repeat(0)); + if std_walls_iter.zip(ut_locals_iter).take(state.header.type_count).any(|pair| pair == (0, 1)) { + return Err(Error::InvalidTzFile( + "invalid couple of standard/wall and UT/local indicators", + )); + } + + let extra_rule = match footer { + Some(footer) => { + let footer = str::from_utf8(footer)?; + if !(footer.starts_with('\n') && footer.ends_with('\n')) { + return Err(Error::InvalidTzFile("invalid footer")); + } + + let tz_string = footer.trim_matches(|c: char| c.is_ascii_whitespace()); + if tz_string.starts_with(':') || tz_string.contains('\0') { + return Err(Error::InvalidTzFile("invalid footer")); + } + + match tz_string.is_empty() { + true => None, + false => Some(TransitionRule::from_tz_string( + tz_string.as_bytes(), + state.header.version == Version::V3, + )?), + } + } + None => None, + }; + + TimeZone::new(transitions, local_time_types, leap_seconds, extra_rule) +} + +/// TZif data blocks +struct State<'a> { + header: Header, + /// Time size in bytes + time_size: usize, + /// Transition times data block + transition_times: &'a [u8], + /// Transition types data block + transition_types: &'a [u8], + /// Local time types data block + local_time_types: &'a [u8], + /// Time zone names data block + names: &'a [u8], + /// Leap seconds data block + leap_seconds: &'a [u8], + /// UT/local indicators data block + std_walls: &'a [u8], + /// Standard/wall indicators data block + ut_locals: &'a [u8], +} + +impl<'a> State<'a> { + /// Read TZif data blocks + fn new(cursor: &mut Cursor<'a>, first: bool) -> Result<Self, Error> { + let header = Header::new(cursor)?; + let time_size = match first { + true => 4, // We always parse V1 first + false => 8, + }; + + Ok(Self { + time_size, + transition_times: cursor.read_exact(header.transition_count * time_size)?, + transition_types: cursor.read_exact(header.transition_count)?, + local_time_types: cursor.read_exact(header.type_count * 6)?, + names: cursor.read_exact(header.char_count)?, + leap_seconds: cursor.read_exact(header.leap_count * (time_size + 4))?, + std_walls: cursor.read_exact(header.std_wall_count)?, + ut_locals: cursor.read_exact(header.ut_local_count)?, + header, + }) + } + + /// Parse time values + fn parse_time(&self, arr: &[u8], version: Version) -> Result<i64, Error> { + match version { + Version::V1 => Ok(read_be_i32(&arr[..4])?.into()), + Version::V2 | Version::V3 => read_be_i64(arr), + } + } +} + +/// TZif header +#[derive(Debug)] +struct Header { + /// TZif version + version: Version, + /// Number of UT/local indicators + ut_local_count: usize, + /// Number of standard/wall indicators + std_wall_count: usize, + /// Number of leap-second records + leap_count: usize, + /// Number of transition times + transition_count: usize, + /// Number of local time type records + type_count: usize, + /// Number of time zone names bytes + char_count: usize, +} + +impl Header { + fn new(cursor: &mut Cursor) -> Result<Self, Error> { + let magic = cursor.read_exact(4)?; + if magic != *b"TZif" { + return Err(Error::InvalidTzFile("invalid magic number")); + } + + let version = match cursor.read_exact(1)? { + [0x00] => Version::V1, + [0x32] => Version::V2, + [0x33] => Version::V3, + _ => return Err(Error::UnsupportedTzFile("unsupported TZif version")), + }; + + cursor.read_exact(15)?; + let ut_local_count = cursor.read_be_u32()?; + let std_wall_count = cursor.read_be_u32()?; + let leap_count = cursor.read_be_u32()?; + let transition_count = cursor.read_be_u32()?; + let type_count = cursor.read_be_u32()?; + let char_count = cursor.read_be_u32()?; + + if !(type_count != 0 + && char_count != 0 + && (ut_local_count == 0 || ut_local_count == type_count) + && (std_wall_count == 0 || std_wall_count == type_count)) + { + return Err(Error::InvalidTzFile("invalid header")); + } + + Ok(Self { + version, + ut_local_count: ut_local_count as usize, + std_wall_count: std_wall_count as usize, + leap_count: leap_count as usize, + transition_count: transition_count as usize, + type_count: type_count as usize, + char_count: char_count as usize, + }) + } +} + +/// A `Cursor` contains a slice of a buffer and a read count. +#[derive(Debug, Eq, PartialEq)] +pub(crate) struct Cursor<'a> { + /// Slice representing the remaining data to be read + remaining: &'a [u8], + /// Number of already read bytes + read_count: usize, +} + +impl<'a> Cursor<'a> { + /// Construct a new `Cursor` from remaining data + pub(crate) const fn new(remaining: &'a [u8]) -> Self { + Self { remaining, read_count: 0 } + } + + pub(crate) fn peek(&self) -> Option<&u8> { + self.remaining().first() + } + + /// Returns remaining data + pub(crate) const fn remaining(&self) -> &'a [u8] { + self.remaining + } + + /// Returns `true` if data is remaining + pub(crate) fn is_empty(&self) -> bool { + self.remaining.is_empty() + } + + pub(crate) fn read_be_u32(&mut self) -> Result<u32, Error> { + let mut buf = [0; 4]; + buf.copy_from_slice(self.read_exact(4)?); + Ok(u32::from_be_bytes(buf)) + } + + /// Read exactly `count` bytes, reducing remaining data and incrementing read count + pub(crate) fn read_exact(&mut self, count: usize) -> Result<&'a [u8], io::Error> { + match (self.remaining.get(..count), self.remaining.get(count..)) { + (Some(result), Some(remaining)) => { + self.remaining = remaining; + self.read_count += count; + Ok(result) + } + _ => Err(io::Error::from(ErrorKind::UnexpectedEof)), + } + } + + /// Read bytes and compare them to the provided tag + pub(crate) fn read_tag(&mut self, tag: &[u8]) -> Result<(), io::Error> { + if self.read_exact(tag.len())? == tag { + Ok(()) + } else { + Err(io::Error::from(ErrorKind::InvalidData)) + } + } + + /// Read bytes if the remaining data is prefixed by the provided tag + pub(crate) fn read_optional_tag(&mut self, tag: &[u8]) -> Result<bool, io::Error> { + if self.remaining.starts_with(tag) { + self.read_exact(tag.len())?; + Ok(true) + } else { + Ok(false) + } + } + + /// Read bytes as long as the provided predicate is true + pub(crate) fn read_while<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> { + match self.remaining.iter().position(|x| !f(x)) { + None => self.read_exact(self.remaining.len()), + Some(position) => self.read_exact(position), + } + } + + // Parse an integer out of the ASCII digits + pub(crate) fn read_int<T: FromStr<Err = ParseIntError>>(&mut self) -> Result<T, Error> { + let bytes = self.read_while(u8::is_ascii_digit)?; + Ok(str::from_utf8(bytes)?.parse()?) + } + + /// Read bytes until the provided predicate is true + pub(crate) fn read_until<F: Fn(&u8) -> bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> { + match self.remaining.iter().position(f) { + None => self.read_exact(self.remaining.len()), + Some(position) => self.read_exact(position), + } + } +} + +pub(crate) fn read_be_i32(bytes: &[u8]) -> Result<i32, Error> { + if bytes.len() != 4 { + return Err(Error::InvalidSlice("too short for i32")); + } + + let mut buf = [0; 4]; + buf.copy_from_slice(bytes); + Ok(i32::from_be_bytes(buf)) +} + +pub(crate) fn read_be_i64(bytes: &[u8]) -> Result<i64, Error> { + if bytes.len() != 8 { + return Err(Error::InvalidSlice("too short for i64")); + } + + let mut buf = [0; 8]; + buf.copy_from_slice(bytes); + Ok(i64::from_be_bytes(buf)) +} + +/// TZif version +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Version { + /// Version 1 + V1, + /// Version 2 + V2, + /// Version 3 + V3, +} diff --git a/vendor/chrono/src/offset/local/tz_info/rule.rs b/vendor/chrono/src/offset/local/tz_info/rule.rs new file mode 100644 index 000000000..7befddb5c --- /dev/null +++ b/vendor/chrono/src/offset/local/tz_info/rule.rs @@ -0,0 +1,1046 @@ +use std::cmp::Ordering; + +use super::parser::Cursor; +use super::timezone::{LocalTimeType, SECONDS_PER_WEEK}; +use super::{ + rem_euclid, Error, CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, DAY_IN_MONTHS_NORMAL_YEAR, + SECONDS_PER_DAY, +}; + +/// Transition rule +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(super) enum TransitionRule { + /// Fixed local time type + Fixed(LocalTimeType), + /// Alternate local time types + Alternate(AlternateTime), +} + +impl TransitionRule { + /// Parse a POSIX TZ string containing a time zone description, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html). + /// + /// TZ string extensions from [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536#section-3.3.1) may be used. + /// + pub(super) fn from_tz_string( + tz_string: &[u8], + use_string_extensions: bool, + ) -> Result<Self, Error> { + let mut cursor = Cursor::new(tz_string); + + let std_time_zone = Some(parse_name(&mut cursor)?); + let std_offset = parse_offset(&mut cursor)?; + + if cursor.is_empty() { + return Ok(LocalTimeType::new(-std_offset, false, std_time_zone)?.into()); + } + + let dst_time_zone = Some(parse_name(&mut cursor)?); + + let dst_offset = match cursor.peek() { + Some(&b',') => std_offset - 3600, + Some(_) => parse_offset(&mut cursor)?, + None => { + return Err(Error::UnsupportedTzString("DST start and end rules must be provided")) + } + }; + + if cursor.is_empty() { + return Err(Error::UnsupportedTzString("DST start and end rules must be provided")); + } + + cursor.read_tag(b",")?; + let (dst_start, dst_start_time) = RuleDay::parse(&mut cursor, use_string_extensions)?; + + cursor.read_tag(b",")?; + let (dst_end, dst_end_time) = RuleDay::parse(&mut cursor, use_string_extensions)?; + + if !cursor.is_empty() { + return Err(Error::InvalidTzString("remaining data after parsing TZ string")); + } + + Ok(AlternateTime::new( + LocalTimeType::new(-std_offset, false, std_time_zone)?, + LocalTimeType::new(-dst_offset, true, dst_time_zone)?, + dst_start, + dst_start_time, + dst_end, + dst_end_time, + )? + .into()) + } + + /// Find the local time type associated to the transition rule at the specified Unix time in seconds + pub(super) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { + match self { + TransitionRule::Fixed(local_time_type) => Ok(local_time_type), + TransitionRule::Alternate(alternate_time) => { + alternate_time.find_local_time_type(unix_time) + } + } + } + + /// Find the local time type associated to the transition rule at the specified Unix time in seconds + pub(super) fn find_local_time_type_from_local( + &self, + local_time: i64, + year: i32, + ) -> Result<crate::LocalResult<LocalTimeType>, Error> { + match self { + TransitionRule::Fixed(local_time_type) => { + Ok(crate::LocalResult::Single(*local_time_type)) + } + TransitionRule::Alternate(alternate_time) => { + alternate_time.find_local_time_type_from_local(local_time, year) + } + } + } +} + +impl From<LocalTimeType> for TransitionRule { + fn from(inner: LocalTimeType) -> Self { + TransitionRule::Fixed(inner) + } +} + +impl From<AlternateTime> for TransitionRule { + fn from(inner: AlternateTime) -> Self { + TransitionRule::Alternate(inner) + } +} + +/// Transition rule representing alternate local time types +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(super) struct AlternateTime { + /// Local time type for standard time + pub(super) std: LocalTimeType, + /// Local time type for Daylight Saving Time + pub(super) dst: LocalTimeType, + /// Start day of Daylight Saving Time + dst_start: RuleDay, + /// Local start day time of Daylight Saving Time, in seconds + dst_start_time: i32, + /// End day of Daylight Saving Time + dst_end: RuleDay, + /// Local end day time of Daylight Saving Time, in seconds + dst_end_time: i32, +} + +impl AlternateTime { + /// Construct a transition rule representing alternate local time types + fn new( + std: LocalTimeType, + dst: LocalTimeType, + dst_start: RuleDay, + dst_start_time: i32, + dst_end: RuleDay, + dst_end_time: i32, + ) -> Result<Self, Error> { + // Overflow is not possible + if !((dst_start_time as i64).abs() < SECONDS_PER_WEEK + && (dst_end_time as i64).abs() < SECONDS_PER_WEEK) + { + return Err(Error::TransitionRule("invalid DST start or end time")); + } + + Ok(Self { std, dst, dst_start, dst_start_time, dst_end, dst_end_time }) + } + + /// Find the local time type associated to the alternate transition rule at the specified Unix time in seconds + fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { + // Overflow is not possible + let dst_start_time_in_utc = self.dst_start_time as i64 - self.std.ut_offset as i64; + let dst_end_time_in_utc = self.dst_end_time as i64 - self.dst.ut_offset as i64; + + let current_year = match UtcDateTime::from_timespec(unix_time) { + Ok(dt) => dt.year, + Err(error) => return Err(error), + }; + + // Check if the current year is valid for the following computations + if !(i32::min_value() + 2 <= current_year && current_year <= i32::max_value() - 2) { + return Err(Error::OutOfRange("out of range date time")); + } + + let current_year_dst_start_unix_time = + self.dst_start.unix_time(current_year, dst_start_time_in_utc); + let current_year_dst_end_unix_time = + self.dst_end.unix_time(current_year, dst_end_time_in_utc); + + // Check DST start/end Unix times for previous/current/next years to support for transition day times outside of [0h, 24h] range + let is_dst = + match Ord::cmp(¤t_year_dst_start_unix_time, ¤t_year_dst_end_unix_time) { + Ordering::Less | Ordering::Equal => { + if unix_time < current_year_dst_start_unix_time { + let previous_year_dst_end_unix_time = + self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc); + if unix_time < previous_year_dst_end_unix_time { + let previous_year_dst_start_unix_time = + self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc); + previous_year_dst_start_unix_time <= unix_time + } else { + false + } + } else if unix_time < current_year_dst_end_unix_time { + true + } else { + let next_year_dst_start_unix_time = + self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc); + if next_year_dst_start_unix_time <= unix_time { + let next_year_dst_end_unix_time = + self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc); + unix_time < next_year_dst_end_unix_time + } else { + false + } + } + } + Ordering::Greater => { + if unix_time < current_year_dst_end_unix_time { + let previous_year_dst_start_unix_time = + self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc); + if unix_time < previous_year_dst_start_unix_time { + let previous_year_dst_end_unix_time = + self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc); + unix_time < previous_year_dst_end_unix_time + } else { + true + } + } else if unix_time < current_year_dst_start_unix_time { + false + } else { + let next_year_dst_end_unix_time = + self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc); + if next_year_dst_end_unix_time <= unix_time { + let next_year_dst_start_unix_time = + self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc); + next_year_dst_start_unix_time <= unix_time + } else { + true + } + } + } + }; + + if is_dst { + Ok(&self.dst) + } else { + Ok(&self.std) + } + } + + fn find_local_time_type_from_local( + &self, + local_time: i64, + current_year: i32, + ) -> Result<crate::LocalResult<LocalTimeType>, Error> { + // Check if the current year is valid for the following computations + if !(i32::min_value() + 2 <= current_year && current_year <= i32::max_value() - 2) { + return Err(Error::OutOfRange("out of range date time")); + } + + let dst_start_transition_start = + self.dst_start.unix_time(current_year, 0) + i64::from(self.dst_start_time); + let dst_start_transition_end = self.dst_start.unix_time(current_year, 0) + + i64::from(self.dst_start_time) + + i64::from(self.dst.ut_offset) + - i64::from(self.std.ut_offset); + + let dst_end_transition_start = + self.dst_end.unix_time(current_year, 0) + i64::from(self.dst_end_time); + let dst_end_transition_end = self.dst_end.unix_time(current_year, 0) + + i64::from(self.dst_end_time) + + i64::from(self.std.ut_offset) + - i64::from(self.dst.ut_offset); + + match self.std.ut_offset.cmp(&self.dst.ut_offset) { + Ordering::Equal => Ok(crate::LocalResult::Single(self.std)), + Ordering::Less => { + if self.dst_start.transition_date(current_year).0 + < self.dst_end.transition_date(current_year).0 + { + // northern hemisphere + // For the DST END transition, the `start` happens at a later timestamp than the `end`. + if local_time <= dst_start_transition_start { + Ok(crate::LocalResult::Single(self.std)) + } else if local_time > dst_start_transition_start + && local_time < dst_start_transition_end + { + Ok(crate::LocalResult::None) + } else if local_time >= dst_start_transition_end + && local_time < dst_end_transition_end + { + Ok(crate::LocalResult::Single(self.dst)) + } else if local_time >= dst_end_transition_end + && local_time <= dst_end_transition_start + { + Ok(crate::LocalResult::Ambiguous(self.std, self.dst)) + } else { + Ok(crate::LocalResult::Single(self.std)) + } + } else { + // southern hemisphere regular DST + // For the DST END transition, the `start` happens at a later timestamp than the `end`. + if local_time < dst_end_transition_end { + Ok(crate::LocalResult::Single(self.dst)) + } else if local_time >= dst_end_transition_end + && local_time <= dst_end_transition_start + { + Ok(crate::LocalResult::Ambiguous(self.std, self.dst)) + } else if local_time > dst_end_transition_end + && local_time < dst_start_transition_start + { + Ok(crate::LocalResult::Single(self.std)) + } else if local_time >= dst_start_transition_start + && local_time < dst_start_transition_end + { + Ok(crate::LocalResult::None) + } else { + Ok(crate::LocalResult::Single(self.dst)) + } + } + } + Ordering::Greater => { + if self.dst_start.transition_date(current_year).0 + < self.dst_end.transition_date(current_year).0 + { + // southern hemisphere reverse DST + // For the DST END transition, the `start` happens at a later timestamp than the `end`. + if local_time < dst_start_transition_end { + Ok(crate::LocalResult::Single(self.std)) + } else if local_time >= dst_start_transition_end + && local_time <= dst_start_transition_start + { + Ok(crate::LocalResult::Ambiguous(self.dst, self.std)) + } else if local_time > dst_start_transition_start + && local_time < dst_end_transition_start + { + Ok(crate::LocalResult::Single(self.dst)) + } else if local_time >= dst_end_transition_start + && local_time < dst_end_transition_end + { + Ok(crate::LocalResult::None) + } else { + Ok(crate::LocalResult::Single(self.std)) + } + } else { + // northern hemisphere reverse DST + // For the DST END transition, the `start` happens at a later timestamp than the `end`. + if local_time <= dst_end_transition_start { + Ok(crate::LocalResult::Single(self.dst)) + } else if local_time > dst_end_transition_start + && local_time < dst_end_transition_end + { + Ok(crate::LocalResult::None) + } else if local_time >= dst_end_transition_end + && local_time < dst_start_transition_end + { + Ok(crate::LocalResult::Single(self.std)) + } else if local_time >= dst_start_transition_end + && local_time <= dst_start_transition_start + { + Ok(crate::LocalResult::Ambiguous(self.dst, self.std)) + } else { + Ok(crate::LocalResult::Single(self.dst)) + } + } + } + } + } +} + +/// Parse time zone name +fn parse_name<'a>(cursor: &mut Cursor<'a>) -> Result<&'a [u8], Error> { + match cursor.peek() { + Some(b'<') => {} + _ => return Ok(cursor.read_while(u8::is_ascii_alphabetic)?), + } + + cursor.read_exact(1)?; + let unquoted = cursor.read_until(|&x| x == b'>')?; + cursor.read_exact(1)?; + Ok(unquoted) +} + +/// Parse time zone offset +fn parse_offset(cursor: &mut Cursor) -> Result<i32, Error> { + let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?; + + if !(0..=24).contains(&hour) { + return Err(Error::InvalidTzString("invalid offset hour")); + } + if !(0..=59).contains(&minute) { + return Err(Error::InvalidTzString("invalid offset minute")); + } + if !(0..=59).contains(&second) { + return Err(Error::InvalidTzString("invalid offset second")); + } + + Ok(sign * (hour * 3600 + minute * 60 + second)) +} + +/// Parse transition rule time +fn parse_rule_time(cursor: &mut Cursor) -> Result<i32, Error> { + let (hour, minute, second) = parse_hhmmss(cursor)?; + + if !(0..=24).contains(&hour) { + return Err(Error::InvalidTzString("invalid day time hour")); + } + if !(0..=59).contains(&minute) { + return Err(Error::InvalidTzString("invalid day time minute")); + } + if !(0..=59).contains(&second) { + return Err(Error::InvalidTzString("invalid day time second")); + } + + Ok(hour * 3600 + minute * 60 + second) +} + +/// Parse transition rule time with TZ string extensions +fn parse_rule_time_extended(cursor: &mut Cursor) -> Result<i32, Error> { + let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?; + + if !(-167..=167).contains(&hour) { + return Err(Error::InvalidTzString("invalid day time hour")); + } + if !(0..=59).contains(&minute) { + return Err(Error::InvalidTzString("invalid day time minute")); + } + if !(0..=59).contains(&second) { + return Err(Error::InvalidTzString("invalid day time second")); + } + + Ok(sign * (hour * 3600 + minute * 60 + second)) +} + +/// Parse hours, minutes and seconds +fn parse_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32), Error> { + let hour = cursor.read_int()?; + + let mut minute = 0; + let mut second = 0; + + if cursor.read_optional_tag(b":")? { + minute = cursor.read_int()?; + + if cursor.read_optional_tag(b":")? { + second = cursor.read_int()?; + } + } + + Ok((hour, minute, second)) +} + +/// Parse signed hours, minutes and seconds +fn parse_signed_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32, i32), Error> { + let mut sign = 1; + if let Some(&c) = cursor.peek() { + if c == b'+' || c == b'-' { + cursor.read_exact(1)?; + if c == b'-' { + sign = -1; + } + } + } + + let (hour, minute, second) = parse_hhmmss(cursor)?; + Ok((sign, hour, minute, second)) +} + +/// Transition rule day +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum RuleDay { + /// Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable + Julian1WithoutLeap(u16), + /// Zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account + Julian0WithLeap(u16), + /// Day represented by a month, a month week and a week day + MonthWeekday { + /// Month in `[1, 12]` + month: u8, + /// Week of the month in `[1, 5]`, with `5` representing the last week of the month + week: u8, + /// Day of the week in `[0, 6]` from Sunday + week_day: u8, + }, +} + +impl RuleDay { + /// Parse transition rule + fn parse(cursor: &mut Cursor, use_string_extensions: bool) -> Result<(Self, i32), Error> { + let date = match cursor.peek() { + Some(b'M') => { + cursor.read_exact(1)?; + let month = cursor.read_int()?; + cursor.read_tag(b".")?; + let week = cursor.read_int()?; + cursor.read_tag(b".")?; + let week_day = cursor.read_int()?; + RuleDay::month_weekday(month, week, week_day)? + } + Some(b'J') => { + cursor.read_exact(1)?; + RuleDay::julian_1(cursor.read_int()?)? + } + _ => RuleDay::julian_0(cursor.read_int()?)?, + }; + + Ok(( + date, + match (cursor.read_optional_tag(b"/")?, use_string_extensions) { + (false, _) => 2 * 3600, + (true, true) => parse_rule_time_extended(cursor)?, + (true, false) => parse_rule_time(cursor)?, + }, + )) + } + + /// Construct a transition rule day represented by a Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable + fn julian_1(julian_day_1: u16) -> Result<Self, Error> { + if !(1..=365).contains(&julian_day_1) { + return Err(Error::TransitionRule("invalid rule day julian day")); + } + + Ok(RuleDay::Julian1WithoutLeap(julian_day_1)) + } + + /// Construct a transition rule day represented by a zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account + fn julian_0(julian_day_0: u16) -> Result<Self, Error> { + if julian_day_0 > 365 { + return Err(Error::TransitionRule("invalid rule day julian day")); + } + + Ok(RuleDay::Julian0WithLeap(julian_day_0)) + } + + /// Construct a transition rule day represented by a month, a month week and a week day + fn month_weekday(month: u8, week: u8, week_day: u8) -> Result<Self, Error> { + if !(1..=12).contains(&month) { + return Err(Error::TransitionRule("invalid rule day month")); + } + + if !(1..=5).contains(&week) { + return Err(Error::TransitionRule("invalid rule day week")); + } + + if week_day > 6 { + return Err(Error::TransitionRule("invalid rule day week day")); + } + + Ok(RuleDay::MonthWeekday { month, week, week_day }) + } + + /// Get the transition date for the provided year + /// + /// ## Outputs + /// + /// * `month`: Month in `[1, 12]` + /// * `month_day`: Day of the month in `[1, 31]` + fn transition_date(&self, year: i32) -> (usize, i64) { + match *self { + RuleDay::Julian1WithoutLeap(year_day) => { + let year_day = year_day as i64; + + let month = match CUMUL_DAY_IN_MONTHS_NORMAL_YEAR.binary_search(&(year_day - 1)) { + Ok(x) => x + 1, + Err(x) => x, + }; + + let month_day = year_day - CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1]; + + (month, month_day) + } + RuleDay::Julian0WithLeap(year_day) => { + let leap = is_leap_year(year) as i64; + + let cumul_day_in_months = [ + 0, + 31, + 59 + leap, + 90 + leap, + 120 + leap, + 151 + leap, + 181 + leap, + 212 + leap, + 243 + leap, + 273 + leap, + 304 + leap, + 334 + leap, + ]; + + let year_day = year_day as i64; + + let month = match cumul_day_in_months.binary_search(&year_day) { + Ok(x) => x + 1, + Err(x) => x, + }; + + let month_day = 1 + year_day - cumul_day_in_months[month - 1]; + + (month, month_day) + } + RuleDay::MonthWeekday { month: rule_month, week, week_day } => { + let leap = is_leap_year(year) as i64; + + let month = rule_month as usize; + + let mut day_in_month = DAY_IN_MONTHS_NORMAL_YEAR[month - 1]; + if month == 2 { + day_in_month += leap; + } + + let week_day_of_first_month_day = + rem_euclid(4 + days_since_unix_epoch(year, month, 1), DAYS_PER_WEEK); + let first_week_day_occurence_in_month = + 1 + rem_euclid(week_day as i64 - week_day_of_first_month_day, DAYS_PER_WEEK); + + let mut month_day = + first_week_day_occurence_in_month + (week as i64 - 1) * DAYS_PER_WEEK; + if month_day > day_in_month { + month_day -= DAYS_PER_WEEK + } + + (month, month_day) + } + } + } + + /// Returns the UTC Unix time in seconds associated to the transition date for the provided year + fn unix_time(&self, year: i32, day_time_in_utc: i64) -> i64 { + let (month, month_day) = self.transition_date(year); + days_since_unix_epoch(year, month, month_day) * SECONDS_PER_DAY + day_time_in_utc + } +} + +/// UTC date time exprimed in the [proleptic gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar) +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub(crate) struct UtcDateTime { + /// Year + pub(crate) year: i32, + /// Month in `[1, 12]` + pub(crate) month: u8, + /// Day of the month in `[1, 31]` + pub(crate) month_day: u8, + /// Hours since midnight in `[0, 23]` + pub(crate) hour: u8, + /// Minutes in `[0, 59]` + pub(crate) minute: u8, + /// Seconds in `[0, 60]`, with a possible leap second + pub(crate) second: u8, +} + +impl UtcDateTime { + /// Construct a UTC date time from a Unix time in seconds and nanoseconds + pub(crate) fn from_timespec(unix_time: i64) -> Result<Self, Error> { + let seconds = match unix_time.checked_sub(UNIX_OFFSET_SECS) { + Some(seconds) => seconds, + None => return Err(Error::OutOfRange("out of range operation")), + }; + + let mut remaining_days = seconds / SECONDS_PER_DAY; + let mut remaining_seconds = seconds % SECONDS_PER_DAY; + if remaining_seconds < 0 { + remaining_seconds += SECONDS_PER_DAY; + remaining_days -= 1; + } + + let mut cycles_400_years = remaining_days / DAYS_PER_400_YEARS; + remaining_days %= DAYS_PER_400_YEARS; + if remaining_days < 0 { + remaining_days += DAYS_PER_400_YEARS; + cycles_400_years -= 1; + } + + let cycles_100_years = Ord::min(remaining_days / DAYS_PER_100_YEARS, 3); + remaining_days -= cycles_100_years * DAYS_PER_100_YEARS; + + let cycles_4_years = Ord::min(remaining_days / DAYS_PER_4_YEARS, 24); + remaining_days -= cycles_4_years * DAYS_PER_4_YEARS; + + let remaining_years = Ord::min(remaining_days / DAYS_PER_NORMAL_YEAR, 3); + remaining_days -= remaining_years * DAYS_PER_NORMAL_YEAR; + + let mut year = OFFSET_YEAR + + remaining_years + + cycles_4_years * 4 + + cycles_100_years * 100 + + cycles_400_years * 400; + + let mut month = 0; + while month < DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH.len() { + let days = DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH[month]; + if remaining_days < days { + break; + } + remaining_days -= days; + month += 1; + } + month += 2; + + if month >= MONTHS_PER_YEAR as usize { + month -= MONTHS_PER_YEAR as usize; + year += 1; + } + month += 1; + + let month_day = 1 + remaining_days; + + let hour = remaining_seconds / SECONDS_PER_HOUR; + let minute = (remaining_seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR; + let second = remaining_seconds % SECONDS_PER_MINUTE; + + let year = match year >= i32::min_value() as i64 && year <= i32::max_value() as i64 { + true => year as i32, + false => return Err(Error::OutOfRange("i64 is out of range for i32")), + }; + + Ok(Self { + year, + month: month as u8, + month_day: month_day as u8, + hour: hour as u8, + minute: minute as u8, + second: second as u8, + }) + } +} + +/// Number of nanoseconds in one second +const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000; +/// Number of seconds in one minute +const SECONDS_PER_MINUTE: i64 = 60; +/// Number of seconds in one hour +const SECONDS_PER_HOUR: i64 = 3600; +/// Number of minutes in one hour +const MINUTES_PER_HOUR: i64 = 60; +/// Number of months in one year +const MONTHS_PER_YEAR: i64 = 12; +/// Number of days in a normal year +const DAYS_PER_NORMAL_YEAR: i64 = 365; +/// Number of days in 4 years (including 1 leap year) +const DAYS_PER_4_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 4 + 1; +/// Number of days in 100 years (including 24 leap years) +const DAYS_PER_100_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 100 + 24; +/// Number of days in 400 years (including 97 leap years) +const DAYS_PER_400_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 400 + 97; +/// Unix time at `2000-03-01T00:00:00Z` (Wednesday) +const UNIX_OFFSET_SECS: i64 = 951868800; +/// Offset year +const OFFSET_YEAR: i64 = 2000; +/// Month days in a leap year from March +const DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH: [i64; 12] = + [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29]; + +/// Compute the number of days since Unix epoch (`1970-01-01T00:00:00Z`). +/// +/// ## Inputs +/// +/// * `year`: Year +/// * `month`: Month in `[1, 12]` +/// * `month_day`: Day of the month in `[1, 31]` +pub(crate) fn days_since_unix_epoch(year: i32, month: usize, month_day: i64) -> i64 { + let is_leap_year = is_leap_year(year); + + let year = year as i64; + + let mut result = (year - 1970) * 365; + + if year >= 1970 { + result += (year - 1968) / 4; + result -= (year - 1900) / 100; + result += (year - 1600) / 400; + + if is_leap_year && month < 3 { + result -= 1; + } + } else { + result += (year - 1972) / 4; + result -= (year - 2000) / 100; + result += (year - 2000) / 400; + + if is_leap_year && month >= 3 { + result += 1; + } + } + + result += CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1] + month_day - 1; + + result +} + +/// Check if a year is a leap year +pub(crate) fn is_leap_year(year: i32) -> bool { + year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) +} + +#[cfg(test)] +mod tests { + use super::super::timezone::Transition; + use super::super::{Error, TimeZone}; + use super::{AlternateTime, LocalTimeType, RuleDay, TransitionRule}; + use crate::matches; + + #[test] + fn test_quoted() -> Result<(), Error> { + let transition_rule = TransitionRule::from_tz_string(b"<-03>+3<+03>-3,J1,J365", false)?; + assert_eq!( + transition_rule, + AlternateTime::new( + LocalTimeType::new(-10800, false, Some(b"-03"))?, + LocalTimeType::new(10800, true, Some(b"+03"))?, + RuleDay::julian_1(1)?, + 7200, + RuleDay::julian_1(365)?, + 7200, + )? + .into() + ); + Ok(()) + } + + #[test] + fn test_full() -> Result<(), Error> { + let tz_string = b"NZST-12:00:00NZDT-13:00:00,M10.1.0/02:00:00,M3.3.0/02:00:00"; + let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; + assert_eq!( + transition_rule, + AlternateTime::new( + LocalTimeType::new(43200, false, Some(b"NZST"))?, + LocalTimeType::new(46800, true, Some(b"NZDT"))?, + RuleDay::month_weekday(10, 1, 0)?, + 7200, + RuleDay::month_weekday(3, 3, 0)?, + 7200, + )? + .into() + ); + Ok(()) + } + + #[test] + fn test_negative_dst() -> Result<(), Error> { + let tz_string = b"IST-1GMT0,M10.5.0,M3.5.0/1"; + let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; + assert_eq!( + transition_rule, + AlternateTime::new( + LocalTimeType::new(3600, false, Some(b"IST"))?, + LocalTimeType::new(0, true, Some(b"GMT"))?, + RuleDay::month_weekday(10, 5, 0)?, + 7200, + RuleDay::month_weekday(3, 5, 0)?, + 3600, + )? + .into() + ); + Ok(()) + } + + #[test] + fn test_negative_hour() -> Result<(), Error> { + let tz_string = b"<-03>3<-02>,M3.5.0/-2,M10.5.0/-1"; + assert!(TransitionRule::from_tz_string(tz_string, false).is_err()); + + assert_eq!( + TransitionRule::from_tz_string(tz_string, true)?, + AlternateTime::new( + LocalTimeType::new(-10800, false, Some(b"-03"))?, + LocalTimeType::new(-7200, true, Some(b"-02"))?, + RuleDay::month_weekday(3, 5, 0)?, + -7200, + RuleDay::month_weekday(10, 5, 0)?, + -3600, + )? + .into() + ); + Ok(()) + } + + #[test] + fn test_all_year_dst() -> Result<(), Error> { + let tz_string = b"EST5EDT,0/0,J365/25"; + assert!(TransitionRule::from_tz_string(tz_string, false).is_err()); + + assert_eq!( + TransitionRule::from_tz_string(tz_string, true)?, + AlternateTime::new( + LocalTimeType::new(-18000, false, Some(b"EST"))?, + LocalTimeType::new(-14400, true, Some(b"EDT"))?, + RuleDay::julian_0(0)?, + 0, + RuleDay::julian_1(365)?, + 90000, + )? + .into() + ); + Ok(()) + } + + #[test] + fn test_v3_file() -> Result<(), Error> { + let bytes = b"TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\x1c\x20\0\0IST\0TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x04\0\0\0\0\x7f\xe8\x17\x80\0\0\0\x1c\x20\0\0IST\0\x01\x01\x0aIST-2IDT,M3.4.4/26,M10.5.0\x0a"; + + let time_zone = TimeZone::from_tz_data(bytes)?; + + let time_zone_result = TimeZone::new( + vec![Transition::new(2145916800, 0)], + vec![LocalTimeType::new(7200, false, Some(b"IST"))?], + Vec::new(), + Some(TransitionRule::from(AlternateTime::new( + LocalTimeType::new(7200, false, Some(b"IST"))?, + LocalTimeType::new(10800, true, Some(b"IDT"))?, + RuleDay::month_weekday(3, 4, 4)?, + 93600, + RuleDay::month_weekday(10, 5, 0)?, + 7200, + )?)), + )?; + + assert_eq!(time_zone, time_zone_result); + + Ok(()) + } + + #[test] + fn test_rule_day() -> Result<(), Error> { + let rule_day_j1 = RuleDay::julian_1(60)?; + assert_eq!(rule_day_j1.transition_date(2000), (3, 1)); + assert_eq!(rule_day_j1.transition_date(2001), (3, 1)); + assert_eq!(rule_day_j1.unix_time(2000, 43200), 951912000); + + let rule_day_j0 = RuleDay::julian_0(59)?; + assert_eq!(rule_day_j0.transition_date(2000), (2, 29)); + assert_eq!(rule_day_j0.transition_date(2001), (3, 1)); + assert_eq!(rule_day_j0.unix_time(2000, 43200), 951825600); + + let rule_day_mwd = RuleDay::month_weekday(2, 5, 2)?; + assert_eq!(rule_day_mwd.transition_date(2000), (2, 29)); + assert_eq!(rule_day_mwd.transition_date(2001), (2, 27)); + assert_eq!(rule_day_mwd.unix_time(2000, 43200), 951825600); + assert_eq!(rule_day_mwd.unix_time(2001, 43200), 983275200); + + Ok(()) + } + + #[test] + fn test_transition_rule() -> Result<(), Error> { + let transition_rule_fixed = TransitionRule::from(LocalTimeType::new(-36000, false, None)?); + assert_eq!(transition_rule_fixed.find_local_time_type(0)?.offset(), -36000); + + let transition_rule_dst = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(43200, false, Some(b"NZST"))?, + LocalTimeType::new(46800, true, Some(b"NZDT"))?, + RuleDay::month_weekday(10, 1, 0)?, + 7200, + RuleDay::month_weekday(3, 3, 0)?, + 7200, + )?); + + assert_eq!(transition_rule_dst.find_local_time_type(953384399)?.offset(), 46800); + assert_eq!(transition_rule_dst.find_local_time_type(953384400)?.offset(), 43200); + assert_eq!(transition_rule_dst.find_local_time_type(970322399)?.offset(), 43200); + assert_eq!(transition_rule_dst.find_local_time_type(970322400)?.offset(), 46800); + + let transition_rule_negative_dst = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(3600, false, Some(b"IST"))?, + LocalTimeType::new(0, true, Some(b"GMT"))?, + RuleDay::month_weekday(10, 5, 0)?, + 7200, + RuleDay::month_weekday(3, 5, 0)?, + 3600, + )?); + + assert_eq!(transition_rule_negative_dst.find_local_time_type(954032399)?.offset(), 0); + assert_eq!(transition_rule_negative_dst.find_local_time_type(954032400)?.offset(), 3600); + assert_eq!(transition_rule_negative_dst.find_local_time_type(972781199)?.offset(), 3600); + assert_eq!(transition_rule_negative_dst.find_local_time_type(972781200)?.offset(), 0); + + let transition_rule_negative_time_1 = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(0, false, None)?, + LocalTimeType::new(0, true, None)?, + RuleDay::julian_0(100)?, + 0, + RuleDay::julian_0(101)?, + -86500, + )?); + + assert!(transition_rule_negative_time_1.find_local_time_type(8639899)?.is_dst()); + assert!(!transition_rule_negative_time_1.find_local_time_type(8639900)?.is_dst()); + assert!(!transition_rule_negative_time_1.find_local_time_type(8639999)?.is_dst()); + assert!(transition_rule_negative_time_1.find_local_time_type(8640000)?.is_dst()); + + let transition_rule_negative_time_2 = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(-10800, false, Some(b"-03"))?, + LocalTimeType::new(-7200, true, Some(b"-02"))?, + RuleDay::month_weekday(3, 5, 0)?, + -7200, + RuleDay::month_weekday(10, 5, 0)?, + -3600, + )?); + + assert_eq!( + transition_rule_negative_time_2.find_local_time_type(954032399)?.offset(), + -10800 + ); + assert_eq!( + transition_rule_negative_time_2.find_local_time_type(954032400)?.offset(), + -7200 + ); + assert_eq!( + transition_rule_negative_time_2.find_local_time_type(972781199)?.offset(), + -7200 + ); + assert_eq!( + transition_rule_negative_time_2.find_local_time_type(972781200)?.offset(), + -10800 + ); + + let transition_rule_all_year_dst = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(-18000, false, Some(b"EST"))?, + LocalTimeType::new(-14400, true, Some(b"EDT"))?, + RuleDay::julian_0(0)?, + 0, + RuleDay::julian_1(365)?, + 90000, + )?); + + assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702799)?.offset(), -14400); + assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702800)?.offset(), -14400); + + Ok(()) + } + + #[test] + fn test_transition_rule_overflow() -> Result<(), Error> { + let transition_rule_1 = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(-1, false, None)?, + LocalTimeType::new(-1, true, None)?, + RuleDay::julian_1(365)?, + 0, + RuleDay::julian_1(1)?, + 0, + )?); + + let transition_rule_2 = TransitionRule::from(AlternateTime::new( + LocalTimeType::new(1, false, None)?, + LocalTimeType::new(1, true, None)?, + RuleDay::julian_1(365)?, + 0, + RuleDay::julian_1(1)?, + 0, + )?); + + let min_unix_time = -67768100567971200; + let max_unix_time = 67767976233532799; + + assert!(matches!( + transition_rule_1.find_local_time_type(min_unix_time), + Err(Error::OutOfRange(_)) + )); + assert!(matches!( + transition_rule_2.find_local_time_type(max_unix_time), + Err(Error::OutOfRange(_)) + )); + + Ok(()) + } +} diff --git a/vendor/chrono/src/offset/local/tz_info/timezone.rs b/vendor/chrono/src/offset/local/tz_info/timezone.rs new file mode 100644 index 000000000..8572825a8 --- /dev/null +++ b/vendor/chrono/src/offset/local/tz_info/timezone.rs @@ -0,0 +1,904 @@ +//! Types related to a time zone. + +use std::fs::{self, File}; +use std::io::{self, Read}; +use std::path::{Path, PathBuf}; +use std::{cmp::Ordering, fmt, str}; + +use super::rule::{AlternateTime, TransitionRule}; +use super::{parser, Error, DAYS_PER_WEEK, SECONDS_PER_DAY}; + +/// Time zone +#[derive(Debug, Clone, Eq, PartialEq)] +pub(crate) struct TimeZone { + /// List of transitions + transitions: Vec<Transition>, + /// List of local time types (cannot be empty) + local_time_types: Vec<LocalTimeType>, + /// List of leap seconds + leap_seconds: Vec<LeapSecond>, + /// Extra transition rule applicable after the last transition + extra_rule: Option<TransitionRule>, +} + +impl TimeZone { + /// Returns local time zone. + /// + /// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead. + /// + pub(crate) fn local(env_tz: Option<&str>) -> Result<Self, Error> { + match env_tz { + Some(tz) => Self::from_posix_tz(tz), + None => Self::from_posix_tz("localtime"), + } + } + + /// Construct a time zone from a POSIX TZ string, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html). + fn from_posix_tz(tz_string: &str) -> Result<Self, Error> { + if tz_string.is_empty() { + return Err(Error::InvalidTzString("empty TZ string")); + } + + if tz_string == "localtime" { + return Self::from_tz_data(&fs::read("/etc/localtime")?); + } + + let mut chars = tz_string.chars(); + if chars.next() == Some(':') { + return Self::from_file(&mut find_tz_file(chars.as_str())?); + } + + if let Ok(mut file) = find_tz_file(tz_string) { + return Self::from_file(&mut file); + } + + // TZ string extensions are not allowed + let tz_string = tz_string.trim_matches(|c: char| c.is_ascii_whitespace()); + let rule = TransitionRule::from_tz_string(tz_string.as_bytes(), false)?; + Self::new( + vec![], + match rule { + TransitionRule::Fixed(local_time_type) => vec![local_time_type], + TransitionRule::Alternate(AlternateTime { std, dst, .. }) => vec![std, dst], + }, + vec![], + Some(rule), + ) + } + + /// Construct a time zone + pub(super) fn new( + transitions: Vec<Transition>, + local_time_types: Vec<LocalTimeType>, + leap_seconds: Vec<LeapSecond>, + extra_rule: Option<TransitionRule>, + ) -> Result<Self, Error> { + let new = Self { transitions, local_time_types, leap_seconds, extra_rule }; + new.as_ref().validate()?; + Ok(new) + } + + /// Construct a time zone from the contents of a time zone file + fn from_file(file: &mut File) -> Result<Self, Error> { + let mut bytes = Vec::new(); + file.read_to_end(&mut bytes)?; + Self::from_tz_data(&bytes) + } + + /// Construct a time zone from the contents of a time zone file + /// + /// Parse TZif data as described in [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536). + pub(crate) fn from_tz_data(bytes: &[u8]) -> Result<Self, Error> { + parser::parse(bytes) + } + + /// Construct a time zone with the specified UTC offset in seconds + fn fixed(ut_offset: i32) -> Result<Self, Error> { + Ok(Self { + transitions: Vec::new(), + local_time_types: vec![LocalTimeType::with_offset(ut_offset)?], + leap_seconds: Vec::new(), + extra_rule: None, + }) + } + + /// Construct the time zone associated to UTC + pub(crate) fn utc() -> Self { + Self { + transitions: Vec::new(), + local_time_types: vec![LocalTimeType::UTC], + leap_seconds: Vec::new(), + extra_rule: None, + } + } + + /// Find the local time type associated to the time zone at the specified Unix time in seconds + pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { + self.as_ref().find_local_time_type(unix_time) + } + + // should we pass NaiveDateTime all the way through to this fn? + pub(crate) fn find_local_time_type_from_local( + &self, + local_time: i64, + year: i32, + ) -> Result<crate::LocalResult<LocalTimeType>, Error> { + self.as_ref().find_local_time_type_from_local(local_time, year) + } + + /// Returns a reference to the time zone + fn as_ref(&self) -> TimeZoneRef { + TimeZoneRef { + transitions: &self.transitions, + local_time_types: &self.local_time_types, + leap_seconds: &self.leap_seconds, + extra_rule: &self.extra_rule, + } + } +} + +/// Reference to a time zone +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) struct TimeZoneRef<'a> { + /// List of transitions + transitions: &'a [Transition], + /// List of local time types (cannot be empty) + local_time_types: &'a [LocalTimeType], + /// List of leap seconds + leap_seconds: &'a [LeapSecond], + /// Extra transition rule applicable after the last transition + extra_rule: &'a Option<TransitionRule>, +} + +impl<'a> TimeZoneRef<'a> { + /// Find the local time type associated to the time zone at the specified Unix time in seconds + pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&'a LocalTimeType, Error> { + let extra_rule = match self.transitions.last() { + None => match self.extra_rule { + Some(extra_rule) => extra_rule, + None => return Ok(&self.local_time_types[0]), + }, + Some(last_transition) => { + let unix_leap_time = match self.unix_time_to_unix_leap_time(unix_time) { + Ok(unix_leap_time) => unix_leap_time, + Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)), + Err(err) => return Err(err), + }; + + if unix_leap_time >= last_transition.unix_leap_time { + match self.extra_rule { + Some(extra_rule) => extra_rule, + None => { + return Err(Error::FindLocalTimeType( + "no local time type is available for the specified timestamp", + )) + } + } + } else { + let index = match self + .transitions + .binary_search_by_key(&unix_leap_time, Transition::unix_leap_time) + { + Ok(x) => x + 1, + Err(x) => x, + }; + + let local_time_type_index = if index > 0 { + self.transitions[index - 1].local_time_type_index + } else { + 0 + }; + return Ok(&self.local_time_types[local_time_type_index]); + } + } + }; + + match extra_rule.find_local_time_type(unix_time) { + Ok(local_time_type) => Ok(local_time_type), + Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)), + err => err, + } + } + + pub(crate) fn find_local_time_type_from_local( + &self, + local_time: i64, + year: i32, + ) -> Result<crate::LocalResult<LocalTimeType>, Error> { + // #TODO: this is wrong as we need 'local_time_to_local_leap_time ? + // but ... does the local time even include leap seconds ?? + // let unix_leap_time = match self.unix_time_to_unix_leap_time(local_time) { + // Ok(unix_leap_time) => unix_leap_time, + // Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)), + // Err(err) => return Err(err), + // }; + let local_leap_time = local_time; + + // if we have at least one transition, + // we must check _all_ of them, incase of any Overlapping (LocalResult::Ambiguous) or Skipping (LocalResult::None) transitions + if !self.transitions.is_empty() { + let mut prev = Some(self.local_time_types[0]); + + for transition in self.transitions { + let after_ltt = self.local_time_types[transition.local_time_type_index]; + + // the end and start here refers to where the time starts prior to the transition + // and where it ends up after. not the temporal relationship. + let transition_end = transition.unix_leap_time + i64::from(after_ltt.ut_offset); + let transition_start = + transition.unix_leap_time + i64::from(prev.unwrap().ut_offset); + + match transition_start.cmp(&transition_end) { + Ordering::Greater => { + // bakwards transition, eg from DST to regular + // this means a given local time could have one of two possible offsets + if local_leap_time < transition_end { + return Ok(crate::LocalResult::Single(prev.unwrap())); + } else if local_leap_time >= transition_end + && local_leap_time <= transition_start + { + if prev.unwrap().ut_offset < after_ltt.ut_offset { + return Ok(crate::LocalResult::Ambiguous(prev.unwrap(), after_ltt)); + } else { + return Ok(crate::LocalResult::Ambiguous(after_ltt, prev.unwrap())); + } + } + } + Ordering::Equal => { + // should this ever happen? presumably we have to handle it anyway. + if local_leap_time < transition_start { + return Ok(crate::LocalResult::Single(prev.unwrap())); + } else if local_leap_time == transition_end { + if prev.unwrap().ut_offset < after_ltt.ut_offset { + return Ok(crate::LocalResult::Ambiguous(prev.unwrap(), after_ltt)); + } else { + return Ok(crate::LocalResult::Ambiguous(after_ltt, prev.unwrap())); + } + } + } + Ordering::Less => { + // forwards transition, eg from regular to DST + // this means that times that are skipped are invalid local times + if local_leap_time <= transition_start { + return Ok(crate::LocalResult::Single(prev.unwrap())); + } else if local_leap_time < transition_end { + return Ok(crate::LocalResult::None); + } else if local_leap_time == transition_end { + return Ok(crate::LocalResult::Single(after_ltt)); + } + } + } + + // try the next transition, we are fully after this one + prev = Some(after_ltt); + } + }; + + if let Some(extra_rule) = self.extra_rule { + match extra_rule.find_local_time_type_from_local(local_time, year) { + Ok(local_time_type) => Ok(local_time_type), + Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)), + err => err, + } + } else { + Ok(crate::LocalResult::Single(self.local_time_types[0])) + } + } + + /// Check time zone inputs + fn validate(&self) -> Result<(), Error> { + // Check local time types + let local_time_types_size = self.local_time_types.len(); + if local_time_types_size == 0 { + return Err(Error::TimeZone("list of local time types must not be empty")); + } + + // Check transitions + let mut i_transition = 0; + while i_transition < self.transitions.len() { + if self.transitions[i_transition].local_time_type_index >= local_time_types_size { + return Err(Error::TimeZone("invalid local time type index")); + } + + if i_transition + 1 < self.transitions.len() + && self.transitions[i_transition].unix_leap_time + >= self.transitions[i_transition + 1].unix_leap_time + { + return Err(Error::TimeZone("invalid transition")); + } + + i_transition += 1; + } + + // Check leap seconds + if !(self.leap_seconds.is_empty() + || self.leap_seconds[0].unix_leap_time >= 0 + && saturating_abs(self.leap_seconds[0].correction) == 1) + { + return Err(Error::TimeZone("invalid leap second")); + } + + let min_interval = SECONDS_PER_28_DAYS - 1; + + let mut i_leap_second = 0; + while i_leap_second < self.leap_seconds.len() { + if i_leap_second + 1 < self.leap_seconds.len() { + let x0 = &self.leap_seconds[i_leap_second]; + let x1 = &self.leap_seconds[i_leap_second + 1]; + + let diff_unix_leap_time = x1.unix_leap_time.saturating_sub(x0.unix_leap_time); + let abs_diff_correction = + saturating_abs(x1.correction.saturating_sub(x0.correction)); + + if !(diff_unix_leap_time >= min_interval && abs_diff_correction == 1) { + return Err(Error::TimeZone("invalid leap second")); + } + } + i_leap_second += 1; + } + + // Check extra rule + let (extra_rule, last_transition) = match (&self.extra_rule, self.transitions.last()) { + (Some(rule), Some(trans)) => (rule, trans), + _ => return Ok(()), + }; + + let last_local_time_type = &self.local_time_types[last_transition.local_time_type_index]; + let unix_time = match self.unix_leap_time_to_unix_time(last_transition.unix_leap_time) { + Ok(unix_time) => unix_time, + Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)), + Err(err) => return Err(err), + }; + + let rule_local_time_type = match extra_rule.find_local_time_type(unix_time) { + Ok(rule_local_time_type) => rule_local_time_type, + Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)), + Err(err) => return Err(err), + }; + + let check = last_local_time_type.ut_offset == rule_local_time_type.ut_offset + && last_local_time_type.is_dst == rule_local_time_type.is_dst + && match (&last_local_time_type.name, &rule_local_time_type.name) { + (Some(x), Some(y)) => x.equal(y), + (None, None) => true, + _ => false, + }; + + if !check { + return Err(Error::TimeZone( + "extra transition rule is inconsistent with the last transition", + )); + } + + Ok(()) + } + + /// Convert Unix time to Unix leap time, from the list of leap seconds in a time zone + fn unix_time_to_unix_leap_time(&self, unix_time: i64) -> Result<i64, Error> { + let mut unix_leap_time = unix_time; + + let mut i = 0; + while i < self.leap_seconds.len() { + let leap_second = &self.leap_seconds[i]; + + if unix_leap_time < leap_second.unix_leap_time { + break; + } + + unix_leap_time = match unix_time.checked_add(leap_second.correction as i64) { + Some(unix_leap_time) => unix_leap_time, + None => return Err(Error::OutOfRange("out of range operation")), + }; + + i += 1; + } + + Ok(unix_leap_time) + } + + /// Convert Unix leap time to Unix time, from the list of leap seconds in a time zone + fn unix_leap_time_to_unix_time(&self, unix_leap_time: i64) -> Result<i64, Error> { + if unix_leap_time == i64::min_value() { + return Err(Error::OutOfRange("out of range operation")); + } + + let index = match self + .leap_seconds + .binary_search_by_key(&(unix_leap_time - 1), LeapSecond::unix_leap_time) + { + Ok(x) => x + 1, + Err(x) => x, + }; + + let correction = if index > 0 { self.leap_seconds[index - 1].correction } else { 0 }; + + match unix_leap_time.checked_sub(correction as i64) { + Some(unix_time) => Ok(unix_time), + None => Err(Error::OutOfRange("out of range operation")), + } + } + + /// The UTC time zone + const UTC: TimeZoneRef<'static> = TimeZoneRef { + transitions: &[], + local_time_types: &[LocalTimeType::UTC], + leap_seconds: &[], + extra_rule: &None, + }; +} + +/// Transition of a TZif file +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(super) struct Transition { + /// Unix leap time + unix_leap_time: i64, + /// Index specifying the local time type of the transition + local_time_type_index: usize, +} + +impl Transition { + /// Construct a TZif file transition + pub(super) const fn new(unix_leap_time: i64, local_time_type_index: usize) -> Self { + Self { unix_leap_time, local_time_type_index } + } + + /// Returns Unix leap time + const fn unix_leap_time(&self) -> i64 { + self.unix_leap_time + } +} + +/// Leap second of a TZif file +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(super) struct LeapSecond { + /// Unix leap time + unix_leap_time: i64, + /// Leap second correction + correction: i32, +} + +impl LeapSecond { + /// Construct a TZif file leap second + pub(super) const fn new(unix_leap_time: i64, correction: i32) -> Self { + Self { unix_leap_time, correction } + } + + /// Returns Unix leap time + const fn unix_leap_time(&self) -> i64 { + self.unix_leap_time + } +} + +/// ASCII-encoded fixed-capacity string, used for storing time zone names +#[derive(Copy, Clone, Eq, PartialEq)] +struct TimeZoneName { + /// Length-prefixed string buffer + bytes: [u8; 8], +} + +impl TimeZoneName { + /// Construct a time zone name + fn new(input: &[u8]) -> Result<Self, Error> { + let len = input.len(); + + if !(3..=7).contains(&len) { + return Err(Error::LocalTimeType( + "time zone name must have between 3 and 7 characters", + )); + } + + let mut bytes = [0; 8]; + bytes[0] = input.len() as u8; + + let mut i = 0; + while i < len { + let b = input[i]; + match b { + b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'+' | b'-' => {} + _ => return Err(Error::LocalTimeType("invalid characters in time zone name")), + } + + bytes[i + 1] = b; + i += 1; + } + + Ok(Self { bytes }) + } + + /// Returns time zone name as a byte slice + fn as_bytes(&self) -> &[u8] { + match self.bytes[0] { + 3 => &self.bytes[1..4], + 4 => &self.bytes[1..5], + 5 => &self.bytes[1..6], + 6 => &self.bytes[1..7], + 7 => &self.bytes[1..8], + _ => unreachable!(), + } + } + + /// Check if two time zone names are equal + fn equal(&self, other: &Self) -> bool { + self.bytes == other.bytes + } +} + +impl AsRef<str> for TimeZoneName { + fn as_ref(&self) -> &str { + // SAFETY: ASCII is valid UTF-8 + unsafe { str::from_utf8_unchecked(self.as_bytes()) } + } +} + +impl fmt::Debug for TimeZoneName { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +/// Local time type associated to a time zone +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) struct LocalTimeType { + /// Offset from UTC in seconds + pub(super) ut_offset: i32, + /// Daylight Saving Time indicator + is_dst: bool, + /// Time zone name + name: Option<TimeZoneName>, +} + +impl LocalTimeType { + /// Construct a local time type + pub(super) fn new(ut_offset: i32, is_dst: bool, name: Option<&[u8]>) -> Result<Self, Error> { + if ut_offset == i32::min_value() { + return Err(Error::LocalTimeType("invalid UTC offset")); + } + + let name = match name { + Some(name) => TimeZoneName::new(name)?, + None => return Ok(Self { ut_offset, is_dst, name: None }), + }; + + Ok(Self { ut_offset, is_dst, name: Some(name) }) + } + + /// Construct a local time type with the specified UTC offset in seconds + pub(super) fn with_offset(ut_offset: i32) -> Result<Self, Error> { + if ut_offset == i32::min_value() { + return Err(Error::LocalTimeType("invalid UTC offset")); + } + + Ok(Self { ut_offset, is_dst: false, name: None }) + } + + /// Returns offset from UTC in seconds + pub(crate) const fn offset(&self) -> i32 { + self.ut_offset + } + + /// Returns daylight saving time indicator + pub(super) const fn is_dst(&self) -> bool { + self.is_dst + } + + pub(super) const UTC: LocalTimeType = Self { ut_offset: 0, is_dst: false, name: None }; +} + +/// Open the TZif file corresponding to a TZ string +fn find_tz_file(path: impl AsRef<Path>) -> Result<File, Error> { + // Don't check system timezone directories on non-UNIX platforms + #[cfg(not(unix))] + return Ok(File::open(path)?); + + #[cfg(unix)] + { + let path = path.as_ref(); + if path.is_absolute() { + return Ok(File::open(path)?); + } + + for folder in &ZONE_INFO_DIRECTORIES { + if let Ok(file) = File::open(PathBuf::from(folder).join(path)) { + return Ok(file); + } + } + + Err(Error::Io(io::ErrorKind::NotFound.into())) + } +} + +#[inline] +fn saturating_abs(v: i32) -> i32 { + if v.is_positive() { + v + } else if v == i32::min_value() { + i32::max_value() + } else { + -v + } +} + +// Possible system timezone directories +#[cfg(unix)] +const ZONE_INFO_DIRECTORIES: [&str; 3] = + ["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo"]; + +/// Number of seconds in one week +pub(crate) const SECONDS_PER_WEEK: i64 = SECONDS_PER_DAY * DAYS_PER_WEEK; +/// Number of seconds in 28 days +const SECONDS_PER_28_DAYS: i64 = SECONDS_PER_DAY * 28; + +#[cfg(test)] +mod tests { + use super::super::Error; + use super::{LeapSecond, LocalTimeType, TimeZone, TimeZoneName, Transition, TransitionRule}; + use crate::matches; + + #[test] + fn test_no_dst() -> Result<(), Error> { + let tz_string = b"HST10"; + let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; + assert_eq!(transition_rule, LocalTimeType::new(-36000, false, Some(b"HST"))?.into()); + Ok(()) + } + + #[test] + fn test_error() -> Result<(), Error> { + assert!(matches!( + TransitionRule::from_tz_string(b"IST-1GMT0", false), + Err(Error::UnsupportedTzString(_)) + )); + assert!(matches!( + TransitionRule::from_tz_string(b"EET-2EEST", false), + Err(Error::UnsupportedTzString(_)) + )); + + Ok(()) + } + + #[test] + fn test_v1_file_with_leap_seconds() -> Result<(), Error> { + let bytes = b"TZif\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x1b\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\0\0\0UTC\0\x04\xb2\x58\0\0\0\0\x01\x05\xa4\xec\x01\0\0\0\x02\x07\x86\x1f\x82\0\0\0\x03\x09\x67\x53\x03\0\0\0\x04\x0b\x48\x86\x84\0\0\0\x05\x0d\x2b\x0b\x85\0\0\0\x06\x0f\x0c\x3f\x06\0\0\0\x07\x10\xed\x72\x87\0\0\0\x08\x12\xce\xa6\x08\0\0\0\x09\x15\x9f\xca\x89\0\0\0\x0a\x17\x80\xfe\x0a\0\0\0\x0b\x19\x62\x31\x8b\0\0\0\x0c\x1d\x25\xea\x0c\0\0\0\x0d\x21\xda\xe5\x0d\0\0\0\x0e\x25\x9e\x9d\x8e\0\0\0\x0f\x27\x7f\xd1\x0f\0\0\0\x10\x2a\x50\xf5\x90\0\0\0\x11\x2c\x32\x29\x11\0\0\0\x12\x2e\x13\x5c\x92\0\0\0\x13\x30\xe7\x24\x13\0\0\0\x14\x33\xb8\x48\x94\0\0\0\x15\x36\x8c\x10\x15\0\0\0\x16\x43\xb7\x1b\x96\0\0\0\x17\x49\x5c\x07\x97\0\0\0\x18\x4f\xef\x93\x18\0\0\0\x19\x55\x93\x2d\x99\0\0\0\x1a\x58\x68\x46\x9a\0\0\0\x1b\0\0"; + + let time_zone = TimeZone::from_tz_data(bytes)?; + + let time_zone_result = TimeZone::new( + Vec::new(), + vec![LocalTimeType::new(0, false, Some(b"UTC"))?], + vec![ + LeapSecond::new(78796800, 1), + LeapSecond::new(94694401, 2), + LeapSecond::new(126230402, 3), + LeapSecond::new(157766403, 4), + LeapSecond::new(189302404, 5), + LeapSecond::new(220924805, 6), + LeapSecond::new(252460806, 7), + LeapSecond::new(283996807, 8), + LeapSecond::new(315532808, 9), + LeapSecond::new(362793609, 10), + LeapSecond::new(394329610, 11), + LeapSecond::new(425865611, 12), + LeapSecond::new(489024012, 13), + LeapSecond::new(567993613, 14), + LeapSecond::new(631152014, 15), + LeapSecond::new(662688015, 16), + LeapSecond::new(709948816, 17), + LeapSecond::new(741484817, 18), + LeapSecond::new(773020818, 19), + LeapSecond::new(820454419, 20), + LeapSecond::new(867715220, 21), + LeapSecond::new(915148821, 22), + LeapSecond::new(1136073622, 23), + LeapSecond::new(1230768023, 24), + LeapSecond::new(1341100824, 25), + LeapSecond::new(1435708825, 26), + LeapSecond::new(1483228826, 27), + ], + None, + )?; + + assert_eq!(time_zone, time_zone_result); + + Ok(()) + } + + #[test] + fn test_v2_file() -> Result<(), Error> { + let bytes = b"TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\x80\0\0\0\xbb\x05\x43\x48\xbb\x21\x71\x58\xcb\x89\x3d\xc8\xd2\x23\xf4\x70\xd2\x61\x49\x38\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\xff\xff\xff\xff\x74\xe0\x70\xbe\xff\xff\xff\xff\xbb\x05\x43\x48\xff\xff\xff\xff\xbb\x21\x71\x58\xff\xff\xff\xff\xcb\x89\x3d\xc8\xff\xff\xff\xff\xd2\x23\xf4\x70\xff\xff\xff\xff\xd2\x61\x49\x38\xff\xff\xff\xff\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0\x0aHST10\x0a"; + + let time_zone = TimeZone::from_tz_data(bytes)?; + + let time_zone_result = TimeZone::new( + vec![ + Transition::new(-2334101314, 1), + Transition::new(-1157283000, 2), + Transition::new(-1155436200, 1), + Transition::new(-880198200, 3), + Transition::new(-769395600, 4), + Transition::new(-765376200, 1), + Transition::new(-712150200, 5), + ], + vec![ + LocalTimeType::new(-37886, false, Some(b"LMT"))?, + LocalTimeType::new(-37800, false, Some(b"HST"))?, + LocalTimeType::new(-34200, true, Some(b"HDT"))?, + LocalTimeType::new(-34200, true, Some(b"HWT"))?, + LocalTimeType::new(-34200, true, Some(b"HPT"))?, + LocalTimeType::new(-36000, false, Some(b"HST"))?, + ], + Vec::new(), + Some(TransitionRule::from(LocalTimeType::new(-36000, false, Some(b"HST"))?)), + )?; + + assert_eq!(time_zone, time_zone_result); + + assert_eq!( + *time_zone.find_local_time_type(-1156939200)?, + LocalTimeType::new(-34200, true, Some(b"HDT"))? + ); + assert_eq!( + *time_zone.find_local_time_type(1546300800)?, + LocalTimeType::new(-36000, false, Some(b"HST"))? + ); + + Ok(()) + } + + #[test] + fn test_tz_ascii_str() -> Result<(), Error> { + assert!(matches!(TimeZoneName::new(b""), Err(Error::LocalTimeType(_)))); + assert!(matches!(TimeZoneName::new(b"1"), Err(Error::LocalTimeType(_)))); + assert!(matches!(TimeZoneName::new(b"12"), Err(Error::LocalTimeType(_)))); + assert_eq!(TimeZoneName::new(b"123")?.as_bytes(), b"123"); + assert_eq!(TimeZoneName::new(b"1234")?.as_bytes(), b"1234"); + assert_eq!(TimeZoneName::new(b"12345")?.as_bytes(), b"12345"); + assert_eq!(TimeZoneName::new(b"123456")?.as_bytes(), b"123456"); + assert_eq!(TimeZoneName::new(b"1234567")?.as_bytes(), b"1234567"); + assert!(matches!(TimeZoneName::new(b"12345678"), Err(Error::LocalTimeType(_)))); + assert!(matches!(TimeZoneName::new(b"123456789"), Err(Error::LocalTimeType(_)))); + assert!(matches!(TimeZoneName::new(b"1234567890"), Err(Error::LocalTimeType(_)))); + + assert!(matches!(TimeZoneName::new(b"123\0\0\0"), Err(Error::LocalTimeType(_)))); + + Ok(()) + } + + #[test] + fn test_time_zone() -> Result<(), Error> { + let utc = LocalTimeType::UTC; + let cet = LocalTimeType::with_offset(3600)?; + + let utc_local_time_types = vec![utc]; + let fixed_extra_rule = TransitionRule::from(cet); + + let time_zone_1 = TimeZone::new(vec![], utc_local_time_types.clone(), vec![], None)?; + let time_zone_2 = + TimeZone::new(vec![], utc_local_time_types.clone(), vec![], Some(fixed_extra_rule))?; + let time_zone_3 = + TimeZone::new(vec![Transition::new(0, 0)], utc_local_time_types.clone(), vec![], None)?; + let time_zone_4 = TimeZone::new( + vec![Transition::new(i32::min_value().into(), 0), Transition::new(0, 1)], + vec![utc, cet], + Vec::new(), + Some(fixed_extra_rule), + )?; + + assert_eq!(*time_zone_1.find_local_time_type(0)?, utc); + assert_eq!(*time_zone_2.find_local_time_type(0)?, cet); + + assert_eq!(*time_zone_3.find_local_time_type(-1)?, utc); + assert!(matches!(time_zone_3.find_local_time_type(0), Err(Error::FindLocalTimeType(_)))); + + assert_eq!(*time_zone_4.find_local_time_type(-1)?, utc); + assert_eq!(*time_zone_4.find_local_time_type(0)?, cet); + + let time_zone_err = TimeZone::new( + vec![Transition::new(0, 0)], + utc_local_time_types, + vec![], + Some(fixed_extra_rule), + ); + assert!(time_zone_err.is_err()); + + Ok(()) + } + + #[test] + fn test_time_zone_from_posix_tz() -> Result<(), Error> { + #[cfg(unix)] + { + // if the TZ var is set, this essentially _overrides_ the + // time set by the localtime symlink + // so just ensure that ::local() acts as expected + // in this case + if let Ok(tz) = std::env::var("TZ") { + let time_zone_local = TimeZone::local(Some(tz.as_str()))?; + let time_zone_local_1 = TimeZone::from_posix_tz(&tz)?; + assert_eq!(time_zone_local, time_zone_local_1); + } + + let time_zone_utc = TimeZone::from_posix_tz("UTC")?; + assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0); + } + + assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err()); + assert!(TimeZone::from_posix_tz("").is_err()); + + Ok(()) + } + + #[test] + fn test_leap_seconds() -> Result<(), Error> { + let time_zone = TimeZone::new( + Vec::new(), + vec![LocalTimeType::new(0, false, Some(b"UTC"))?], + vec![ + LeapSecond::new(78796800, 1), + LeapSecond::new(94694401, 2), + LeapSecond::new(126230402, 3), + LeapSecond::new(157766403, 4), + LeapSecond::new(189302404, 5), + LeapSecond::new(220924805, 6), + LeapSecond::new(252460806, 7), + LeapSecond::new(283996807, 8), + LeapSecond::new(315532808, 9), + LeapSecond::new(362793609, 10), + LeapSecond::new(394329610, 11), + LeapSecond::new(425865611, 12), + LeapSecond::new(489024012, 13), + LeapSecond::new(567993613, 14), + LeapSecond::new(631152014, 15), + LeapSecond::new(662688015, 16), + LeapSecond::new(709948816, 17), + LeapSecond::new(741484817, 18), + LeapSecond::new(773020818, 19), + LeapSecond::new(820454419, 20), + LeapSecond::new(867715220, 21), + LeapSecond::new(915148821, 22), + LeapSecond::new(1136073622, 23), + LeapSecond::new(1230768023, 24), + LeapSecond::new(1341100824, 25), + LeapSecond::new(1435708825, 26), + LeapSecond::new(1483228826, 27), + ], + None, + )?; + + let time_zone_ref = time_zone.as_ref(); + + assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073621), Ok(1136073599))); + assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073622), Ok(1136073600))); + assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073623), Ok(1136073600))); + assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073624), Ok(1136073601))); + + assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073599), Ok(1136073621))); + assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073600), Ok(1136073623))); + assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073601), Ok(1136073624))); + + Ok(()) + } + + #[test] + fn test_leap_seconds_overflow() -> Result<(), Error> { + let time_zone_err = TimeZone::new( + vec![Transition::new(i64::min_value(), 0)], + vec![LocalTimeType::UTC], + vec![LeapSecond::new(0, 1)], + Some(TransitionRule::from(LocalTimeType::UTC)), + ); + assert!(time_zone_err.is_err()); + + let time_zone = TimeZone::new( + vec![Transition::new(i64::max_value(), 0)], + vec![LocalTimeType::UTC], + vec![LeapSecond::new(0, 1)], + None, + )?; + assert!(matches!( + time_zone.find_local_time_type(i64::max_value()), + Err(Error::FindLocalTimeType(_)) + )); + + Ok(()) + } +} diff --git a/vendor/chrono/src/offset/local/unix.rs b/vendor/chrono/src/offset/local/unix.rs new file mode 100644 index 000000000..32aa31618 --- /dev/null +++ b/vendor/chrono/src/offset/local/unix.rs @@ -0,0 +1,185 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime}; + +use super::tz_info::TimeZone; +use super::{DateTime, FixedOffset, Local, NaiveDateTime}; +use crate::{Datelike, LocalResult, Utc}; + +pub(super) fn now() -> DateTime<Local> { + let now = Utc::now().naive_utc(); + naive_to_local(&now, false).unwrap() +} + +pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> { + TZ_INFO.with(|maybe_cache| { + maybe_cache.borrow_mut().get_or_insert_with(Cache::default).offset(*d, local) + }) +} + +// we have to store the `Cache` in an option as it can't +// be initalized in a static context. +thread_local! { + static TZ_INFO: RefCell<Option<Cache>> = Default::default(); +} + +enum Source { + LocalTime { mtime: SystemTime }, + Environment { hash: u64 }, +} + +impl Source { + fn new(env_tz: Option<&str>) -> Source { + match env_tz { + Some(tz) => { + let mut hasher = hash_map::DefaultHasher::new(); + hasher.write(tz.as_bytes()); + let hash = hasher.finish(); + Source::Environment { hash } + } + None => match fs::symlink_metadata("/etc/localtime") { + Ok(data) => Source::LocalTime { + // we have to pick a sensible default when the mtime fails + // by picking SystemTime::now() we raise the probability of + // the cache being invalidated if/when the mtime starts working + mtime: data.modified().unwrap_or_else(|_| SystemTime::now()), + }, + Err(_) => { + // as above, now() should be a better default than some constant + // TODO: see if we can improve caching in the case where the fallback is a valid timezone + Source::LocalTime { mtime: SystemTime::now() } + } + }, + } + } +} + +struct Cache { + zone: TimeZone, + source: Source, + last_checked: SystemTime, +} + +#[cfg(target_os = "android")] +const TZDB_LOCATION: &str = " /system/usr/share/zoneinfo"; + +#[cfg(target_os = "aix")] +const TZDB_LOCATION: &str = "/usr/share/lib/zoneinfo"; + +#[allow(dead_code)] // keeps the cfg simpler +#[cfg(not(any(target_os = "android", target_os = "aix")))] +const TZDB_LOCATION: &str = "/usr/share/zoneinfo"; + +fn fallback_timezone() -> Option<TimeZone> { + let tz_name = iana_time_zone::get_timezone().ok()?; + let bytes = fs::read(format!("{}/{}", TZDB_LOCATION, tz_name)).ok()?; + TimeZone::from_tz_data(&bytes).ok() +} + +impl Default for Cache { + fn default() -> Cache { + // default to UTC if no local timezone can be found + let env_tz = env::var("TZ").ok(); + let env_ref = env_tz.as_ref().map(|s| s.as_str()); + Cache { + last_checked: SystemTime::now(), + source: Source::new(env_ref), + zone: current_zone(env_ref), + } + } +} + +fn current_zone(var: Option<&str>) -> TimeZone { + TimeZone::local(var).ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc) +} + +impl Cache { + fn offset(&mut self, d: NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> { + let now = SystemTime::now(); + + match now.duration_since(self.last_checked) { + // If the cache has been around for less than a second then we reuse it + // unconditionally. This is a reasonable tradeoff because the timezone + // generally won't be changing _that_ often, but if the time zone does + // change, it will reflect sufficiently quickly from an application + // user's perspective. + Ok(d) if d.as_secs() < 1 => (), + Ok(_) | Err(_) => { + let env_tz = env::var("TZ").ok(); + let env_ref = env_tz.as_ref().map(|s| s.as_str()); + let new_source = Source::new(env_ref); + + let out_of_date = match (&self.source, &new_source) { + // change from env to file or file to env, must recreate the zone + (Source::Environment { .. }, Source::LocalTime { .. }) + | (Source::LocalTime { .. }, Source::Environment { .. }) => true, + // stay as file, but mtime has changed + (Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime }) + if old_mtime != mtime => + { + true + } + // stay as env, but hash of variable has changed + (Source::Environment { hash: old_hash }, Source::Environment { hash }) + if old_hash != hash => + { + true + } + // cache can be reused + _ => false, + }; + + if out_of_date { + self.zone = current_zone(env_ref); + } + + self.last_checked = now; + self.source = new_source; + } + } + + if !local { + let offset = self + .zone + .find_local_time_type(d.timestamp()) + .expect("unable to select local time type") + .offset(); + + return match FixedOffset::east_opt(offset) { + Some(offset) => LocalResult::Single(DateTime::from_utc(d, offset)), + None => LocalResult::None, + }; + } + + // we pass through the year as the year of a local point in time must either be valid in that locale, or + // the entire time was skipped in which case we will return LocalResult::None anywa. + match self + .zone + .find_local_time_type_from_local(d.timestamp(), d.year()) + .expect("unable to select local time type") + { + LocalResult::None => LocalResult::None, + LocalResult::Ambiguous(early, late) => { + let early_offset = FixedOffset::east_opt(early.offset()).unwrap(); + let late_offset = FixedOffset::east_opt(late.offset()).unwrap(); + + LocalResult::Ambiguous( + DateTime::from_utc(d - early_offset, early_offset), + DateTime::from_utc(d - late_offset, late_offset), + ) + } + LocalResult::Single(tt) => { + let offset = FixedOffset::east_opt(tt.offset()).unwrap(); + LocalResult::Single(DateTime::from_utc(d - offset, offset)) + } + } + } +} diff --git a/vendor/chrono/src/offset/local/windows.rs b/vendor/chrono/src/offset/local/windows.rs new file mode 100644 index 000000000..e6415015c --- /dev/null +++ b/vendor/chrono/src/offset/local/windows.rs @@ -0,0 +1,278 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::io; +use std::mem; +use std::time::{SystemTime, UNIX_EPOCH}; + +use winapi::shared::minwindef::*; +use winapi::um::minwinbase::SYSTEMTIME; +use winapi::um::timezoneapi::*; + +use super::{FixedOffset, Local}; +use crate::{DateTime, Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; + +pub(super) fn now() -> DateTime<Local> { + tm_to_datetime(Timespec::now().local()) +} + +/// Converts a local `NaiveDateTime` to the `time::Timespec`. +pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> { + let tm = Tm { + tm_sec: d.second() as i32, + tm_min: d.minute() as i32, + tm_hour: d.hour() as i32, + tm_mday: d.day() as i32, + tm_mon: d.month0() as i32, // yes, C is that strange... + tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`. + tm_wday: 0, // to_local ignores this + tm_yday: 0, // and this + tm_isdst: -1, + // This seems pretty fake? + tm_utcoff: if local { 1 } else { 0 }, + // do not set this, OS APIs are heavily inconsistent in terms of leap second handling + tm_nsec: 0, + }; + + let spec = Timespec { + sec: match local { + false => utc_tm_to_time(&tm), + true => local_tm_to_time(&tm), + }, + nsec: tm.tm_nsec, + }; + + // Adjust for leap seconds + let mut tm = spec.local(); + assert_eq!(tm.tm_nsec, 0); + tm.tm_nsec = d.nanosecond() as i32; + + // #TODO - there should be ambiguous cases, investigate? + LocalResult::Single(tm_to_datetime(tm)) +} + +/// Converts a `time::Tm` struct into the timezone-aware `DateTime`. +fn tm_to_datetime(mut tm: Tm) -> DateTime<Local> { + if tm.tm_sec >= 60 { + tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000; + tm.tm_sec = 59; + } + + let date = NaiveDate::from_ymd_opt(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32) + .unwrap(); + let time = NaiveTime::from_hms_nano( + tm.tm_hour as u32, + tm.tm_min as u32, + tm.tm_sec as u32, + tm.tm_nsec as u32, + ); + + let offset = FixedOffset::east_opt(tm.tm_utcoff).unwrap(); + DateTime::from_utc(date.and_time(time) - offset, offset) +} + +/// A record specifying a time value in seconds and nanoseconds, where +/// nanoseconds represent the offset from the given second. +/// +/// For example a timespec of 1.2 seconds after the beginning of the epoch would +/// be represented as {sec: 1, nsec: 200000000}. +struct Timespec { + sec: i64, + nsec: i32, +} + +impl Timespec { + /// Constructs a timespec representing the current time in UTC. + fn now() -> Timespec { + let st = + SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); + Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 } + } + + /// Converts this timespec into the system's local time. + fn local(self) -> Tm { + let mut tm = Tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_utcoff: 0, + tm_nsec: 0, + }; + time_to_local_tm(self.sec, &mut tm); + tm.tm_nsec = self.nsec; + tm + } +} + +/// Holds a calendar date and time broken down into its components (year, month, +/// day, and so on), also called a broken-down time value. +// FIXME: use c_int instead of i32? +#[repr(C)] +struct Tm { + /// Seconds after the minute - [0, 60] + tm_sec: i32, + + /// Minutes after the hour - [0, 59] + tm_min: i32, + + /// Hours after midnight - [0, 23] + tm_hour: i32, + + /// Day of the month - [1, 31] + tm_mday: i32, + + /// Months since January - [0, 11] + tm_mon: i32, + + /// Years since 1900 + tm_year: i32, + + /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. + tm_wday: i32, + + /// Days since January 1 - [0, 365] + tm_yday: i32, + + /// Daylight Saving Time flag. + /// + /// This value is positive if Daylight Saving Time is in effect, zero if + /// Daylight Saving Time is not in effect, and negative if this information + /// is not available. + tm_isdst: i32, + + /// Identifies the time zone that was used to compute this broken-down time + /// value, including any adjustment for Daylight Saving Time. This is the + /// number of seconds east of UTC. For example, for U.S. Pacific Daylight + /// Time, the value is `-7*60*60 = -25200`. + tm_utcoff: i32, + + /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1] + tm_nsec: i32, +} + +const HECTONANOSECS_IN_SEC: i64 = 10_000_000; +const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; + +fn time_to_file_time(sec: i64) -> FILETIME { + let t = ((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH) as u64; + FILETIME { dwLowDateTime: t as DWORD, dwHighDateTime: (t >> 32) as DWORD } +} + +fn file_time_as_u64(ft: &FILETIME) -> u64 { + ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) +} + +fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { + let t = file_time_as_u64(ft) as i64; + ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 +} + +fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { + unsafe { + let mut ft = mem::zeroed(); + SystemTimeToFileTime(sys, &mut ft); + ft + } +} + +fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { + let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; + sys.wSecond = tm.tm_sec as WORD; + sys.wMinute = tm.tm_min as WORD; + sys.wHour = tm.tm_hour as WORD; + sys.wDay = tm.tm_mday as WORD; + sys.wDayOfWeek = tm.tm_wday as WORD; + sys.wMonth = (tm.tm_mon + 1) as WORD; + sys.wYear = (tm.tm_year + 1900) as WORD; + sys +} + +fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { + tm.tm_sec = sys.wSecond as i32; + tm.tm_min = sys.wMinute as i32; + tm.tm_hour = sys.wHour as i32; + tm.tm_mday = sys.wDay as i32; + tm.tm_wday = sys.wDayOfWeek as i32; + tm.tm_mon = (sys.wMonth - 1) as i32; + tm.tm_year = (sys.wYear - 1900) as i32; + tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + + fn yday(year: i32, month: i32, day: i32) -> i32 { + let leap = if month > 2 { + if year % 4 == 0 { + 1 + } else { + 2 + } + } else { + 0 + }; + let july = if month > 7 { 1 } else { 0 }; + + (month - 1) * 30 + month / 2 + (day - 1) - leap + july + } +} + +macro_rules! call { + ($name:ident($($arg:expr),*)) => { + if $name($($arg),*) == 0 { + panic!(concat!(stringify!($name), " failed with: {}"), + io::Error::last_os_error()); + } + } +} + +fn time_to_local_tm(sec: i64, tm: &mut Tm) { + let ft = time_to_file_time(sec); + unsafe { + let mut utc = mem::zeroed(); + let mut local = mem::zeroed(); + call!(FileTimeToSystemTime(&ft, &mut utc)); + call!(SystemTimeToTzSpecificLocalTime(0 as *const _, &mut utc, &mut local)); + system_time_to_tm(&local, tm); + + let local = system_time_to_file_time(&local); + let local_sec = file_time_to_unix_seconds(&local); + + let mut tz = mem::zeroed(); + GetTimeZoneInformation(&mut tz); + + // SystemTimeToTzSpecificLocalTime already applied the biases so + // check if it non standard + tm.tm_utcoff = (local_sec - sec) as i32; + tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { 0 } else { 1 }; + } +} + +fn utc_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let sys_time = tm_to_system_time(tm); + call!(SystemTimeToFileTime(&sys_time, &mut ft)); + file_time_to_unix_seconds(&ft) + } +} + +fn local_tm_to_time(tm: &Tm) -> i64 { + unsafe { + let mut ft = mem::zeroed(); + let mut utc = mem::zeroed(); + let mut sys_time = tm_to_system_time(tm); + call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, &mut sys_time, &mut utc)); + call!(SystemTimeToFileTime(&utc, &mut ft)); + file_time_to_unix_seconds(&ft) + } +} diff --git a/vendor/chrono/src/offset/mod.rs b/vendor/chrono/src/offset/mod.rs index 0da6bfb42..09d0714e9 100644 --- a/vendor/chrono/src/offset/mod.rs +++ b/vendor/chrono/src/offset/mod.rs @@ -20,10 +20,22 @@ use core::fmt; -use format::{parse, ParseResult, Parsed, StrftimeItems}; -use naive::{NaiveDate, NaiveDateTime, NaiveTime}; -use Weekday; -use {Date, DateTime}; +use crate::format::{parse, ParseResult, Parsed, StrftimeItems}; +use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; +use crate::Weekday; +#[allow(deprecated)] +use crate::{Date, DateTime}; + +mod fixed; +pub use self::fixed::FixedOffset; + +#[cfg(feature = "clock")] +mod local; +#[cfg(feature = "clock")] +pub use self::local::Local; + +mod utc; +pub use self::utc::Utc; /// The conversion result from the local time to the timezone-aware datetime types. #[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)] @@ -73,6 +85,7 @@ impl<T> LocalResult<T> { } } +#[allow(deprecated)] impl<Tz: TimeZone> LocalResult<Date<Tz>> { /// Makes a new `DateTime` from the current date and given `NaiveTime`. /// The offset in the current date is preserved. @@ -195,6 +208,27 @@ pub trait TimeZone: Sized + Clone { /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`. type Offset: Offset; + /// Make a new `DateTime` from year, month, day, time components and current time zone. + /// + /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. + /// + /// Returns `LocalResult::None` on invalid input data. + fn with_ymd_and_hms( + &self, + year: i32, + month: u32, + day: u32, + hour: u32, + min: u32, + sec: u32, + ) -> LocalResult<DateTime<Self>> { + match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec)) + { + Some(dt) => self.from_local_datetime(&dt), + None => LocalResult::None, + } + } + /// Makes a new `Date` from year, month, day and the current time zone. /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. /// @@ -202,14 +236,8 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date, invalid month and/or day. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, TimeZone}; - /// - /// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC"); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")] + #[allow(deprecated)] fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> { self.ymd_opt(year, month, day).unwrap() } @@ -221,15 +249,8 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Returns `None` on the out-of-range date, invalid month and/or day. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, LocalResult, TimeZone}; - /// - /// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC"); - /// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")] + #[allow(deprecated)] fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> { match NaiveDate::from_ymd_opt(year, month, day) { Some(d) => self.from_local_date(&d), @@ -244,14 +265,11 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date and/or invalid DOY. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, TimeZone}; - /// - /// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC"); - /// ~~~~ + #[deprecated( + since = "0.4.23", + note = "use `from_local_datetime()` with a `NaiveDateTime` instead" + )] + #[allow(deprecated)] fn yo(&self, year: i32, ordinal: u32) -> Date<Self> { self.yo_opt(year, ordinal).unwrap() } @@ -263,6 +281,11 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Returns `None` on the out-of-range date and/or invalid DOY. + #[deprecated( + since = "0.4.23", + note = "use `from_local_datetime()` with a `NaiveDateTime` instead" + )] + #[allow(deprecated)] fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> { match NaiveDate::from_yo_opt(year, ordinal) { Some(d) => self.from_local_date(&d), @@ -279,14 +302,11 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Panics on the out-of-range date and/or invalid week number. - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, Weekday, TimeZone}; - /// - /// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC"); - /// ~~~~ + #[deprecated( + since = "0.4.23", + note = "use `from_local_datetime()` with a `NaiveDateTime` instead" + )] + #[allow(deprecated)] fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> { self.isoywd_opt(year, week, weekday).unwrap() } @@ -300,6 +320,11 @@ pub trait TimeZone: Sized + Clone { /// but it will propagate to the `DateTime` values constructed via this date. /// /// Returns `None` on the out-of-range date and/or invalid week number. + #[deprecated( + since = "0.4.23", + note = "use `from_local_datetime()` with a `NaiveDateTime` instead" + )] + #[allow(deprecated)] fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> { match NaiveDate::from_isoywd_opt(year, week, weekday) { Some(d) => self.from_local_date(&d), @@ -313,14 +338,7 @@ pub trait TimeZone: Sized + Clone { /// /// Panics on the out-of-range number of seconds and/or invalid nanosecond, /// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt). - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, TimeZone}; - /// - /// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC"); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")] fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> { self.timestamp_opt(secs, nsecs).unwrap() } @@ -331,6 +349,14 @@ pub trait TimeZone: Sized + Clone { /// /// Returns `LocalResult::None` on out-of-range number of seconds and/or /// invalid nanosecond, otherwise always returns `LocalResult::Single`. + /// + /// # Example + /// + /// ``` + /// use chrono::{Utc, TimeZone}; + /// + /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC"); + /// ``` fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> { match NaiveDateTime::from_timestamp_opt(secs, nsecs) { Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)), @@ -343,14 +369,7 @@ pub trait TimeZone: Sized + Clone { /// /// Panics on out-of-range number of milliseconds for a non-panicking /// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt). - /// - /// # Example - /// - /// ~~~~ - /// use chrono::{Utc, TimeZone}; - /// - /// assert_eq!(Utc.timestamp_millis(1431648000).timestamp(), 1431648); - /// ~~~~ + #[deprecated(since = "0.4.23", note = "use `timestamp_millis_opt()` instead")] fn timestamp_millis(&self, millis: i64) -> DateTime<Self> { self.timestamp_millis_opt(millis).unwrap() } @@ -365,13 +384,13 @@ pub trait TimeZone: Sized + Clone { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{Utc, TimeZone, LocalResult}; /// match Utc.timestamp_millis_opt(1431648000) { /// LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648), /// _ => panic!("Incorrect timestamp_millis"), /// }; - /// ~~~~ + /// ``` fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> { let (mut secs, mut millis) = (millis / 1000, millis % 1000); if millis < 0 { @@ -389,11 +408,11 @@ pub trait TimeZone: Sized + Clone { /// /// # Example /// - /// ~~~~ + /// ``` /// use chrono::{Utc, TimeZone}; /// /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648); - /// ~~~~ + /// ``` fn timestamp_nanos(&self, nanos: i64) -> DateTime<Self> { let (mut secs, mut nanos) = (nanos / 1_000_000_000, nanos % 1_000_000_000); if nanos < 0 { @@ -403,16 +422,17 @@ pub trait TimeZone: Sized + Clone { self.timestamp_opt(secs, nanos as u32).unwrap() } - /// Parses a string with the specified format string and - /// returns a `DateTime` with the current offset. - /// See the [`format::strftime` module](../format/strftime/index.html) - /// on the supported escape sequences. + /// Parses a string with the specified format string and returns a + /// `DateTime` with the current offset. + /// + /// See the [`crate::format::strftime`] module on the + /// supported escape sequences. /// - /// If the format does not include offsets, the current offset is assumed; - /// otherwise the input should have a matching UTC offset. + /// If the to-be-parsed string includes an offset, it *must* match the + /// offset of the TimeZone, otherwise an error will be returned. /// - /// See also `DateTime::parse_from_str` which gives a local `DateTime` - /// with parsed `FixedOffset`. + /// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with + /// parsed [`FixedOffset`]. fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> { let mut parsed = Parsed::new(); parse(&mut parsed, s, StrftimeItems::new(fmt))?; @@ -429,6 +449,9 @@ pub trait TimeZone: Sized + Clone { fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<Self::Offset>; /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible. + #[allow(clippy::wrong_self_convention)] + #[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")] + #[allow(deprecated)] fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> { self.offset_from_local_date(local).map(|offset| { // since FixedOffset is within +/- 1 day, the date is never affected @@ -437,6 +460,7 @@ pub trait TimeZone: Sized + Clone { } /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible. + #[allow(clippy::wrong_self_convention)] fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> { self.offset_from_local_datetime(local) .map(|offset| DateTime::from_utc(*local - offset.fix(), offset)) @@ -450,48 +474,42 @@ pub trait TimeZone: Sized + Clone { /// Converts the UTC `NaiveDate` to the local time. /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). + #[allow(clippy::wrong_self_convention)] + #[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")] + #[allow(deprecated)] fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> { Date::from_utc(*utc, self.offset_from_utc_date(utc)) } /// Converts the UTC `NaiveDateTime` to the local time. /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). + #[allow(clippy::wrong_self_convention)] fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> { DateTime::from_utc(*utc, self.offset_from_utc_datetime(utc)) } } -mod fixed; -#[cfg(feature = "clock")] -mod local; -mod utc; - -pub use self::fixed::FixedOffset; -#[cfg(feature = "clock")] -pub use self::local::Local; -pub use self::utc::Utc; - #[cfg(test)] mod tests { use super::*; #[test] fn test_negative_millis() { - let dt = Utc.timestamp_millis(-1000); + let dt = Utc.timestamp_millis_opt(-1000).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); - let dt = Utc.timestamp_millis(-7000); + let dt = Utc.timestamp_millis_opt(-7000).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC"); - let dt = Utc.timestamp_millis(-7001); + let dt = Utc.timestamp_millis_opt(-7001).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC"); - let dt = Utc.timestamp_millis(-7003); + let dt = Utc.timestamp_millis_opt(-7003).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC"); - let dt = Utc.timestamp_millis(-999); + let dt = Utc.timestamp_millis_opt(-999).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC"); - let dt = Utc.timestamp_millis(-1); + let dt = Utc.timestamp_millis_opt(-1).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC"); - let dt = Utc.timestamp_millis(-60000); + let dt = Utc.timestamp_millis_opt(-60000).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); - let dt = Utc.timestamp_millis(-3600000); + let dt = Utc.timestamp_millis_opt(-3600000).unwrap(); assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); for (millis, expected) in &[ diff --git a/vendor/chrono/src/offset/utc.rs b/vendor/chrono/src/offset/utc.rs index aec6667b0..cfed754b2 100644 --- a/vendor/chrono/src/offset/utc.rs +++ b/vendor/chrono/src/offset/utc.rs @@ -4,16 +4,24 @@ //! The UTC (Coordinated Universal Time) time zone. use core::fmt; - -use super::{FixedOffset, LocalResult, Offset, TimeZone}; -use naive::{NaiveDate, NaiveDateTime}; #[cfg(all( feature = "clock", - not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")) + not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )) ))] use std::time::{SystemTime, UNIX_EPOCH}; + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +use super::{FixedOffset, LocalResult, Offset, TimeZone}; +use crate::naive::{NaiveDate, NaiveDateTime}; #[cfg(feature = "clock")] -use {Date, DateTime}; +#[allow(deprecated)] +use crate::{Date, DateTime}; /// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// It is also used as an offset (which is also a dummy type). @@ -24,35 +32,52 @@ use {Date, DateTime}; /// /// # Example /// -/// ~~~~ +/// ``` /// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc}; /// /// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); /// /// assert_eq!(Utc.timestamp(61, 0), dt); -/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt); -/// ~~~~ -#[derive(Copy, Clone, PartialEq, Eq)] +/// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct Utc; #[cfg(feature = "clock")] +#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] impl Utc { /// Returns a `Date` which corresponds to the current date. + #[deprecated( + since = "0.4.23", + note = "use `Utc::now()` instead, potentially with `.date_naive()`" + )] + #[allow(deprecated)] pub fn today() -> Date<Utc> { Utc::now().date() } - /// Returns a `DateTime` which corresponds to the current date. - #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))] + /// Returns a `DateTime` which corresponds to the current date and time. + #[cfg(not(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + )))] pub fn now() -> DateTime<Utc> { let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); - let naive = NaiveDateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos() as u32); + let naive = + NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos()).unwrap(); DateTime::from_utc(naive, Utc) } - /// Returns a `DateTime` which corresponds to the current date. - #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))] + /// Returns a `DateTime` which corresponds to the current date and time. + #[cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) + ))] pub fn now() -> DateTime<Utc> { let now = js_sys::Date::new_0(); DateTime::<Utc>::from(now) @@ -83,7 +108,7 @@ impl TimeZone for Utc { impl Offset for Utc { fn fix(&self) -> FixedOffset { - FixedOffset::east(0) + FixedOffset::east_opt(0).unwrap() } } diff --git a/vendor/chrono/src/oldtime.rs b/vendor/chrono/src/oldtime.rs index 8656769c5..8e2b3d2c0 100644 --- a/vendor/chrono/src/oldtime.rs +++ b/vendor/chrono/src/oldtime.rs @@ -16,6 +16,9 @@ use core::{fmt, i64}; #[cfg(any(feature = "std", test))] use std::error::Error; +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + /// The number of nanoseconds in a microsecond. const NANOS_PER_MICRO: i32 = 1000; /// The number of nanoseconds in a millisecond. @@ -45,21 +48,23 @@ macro_rules! try_opt { } /// ISO 8601 time duration with nanosecond precision. +/// /// This also allows for the negative duration; see individual methods for details. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] pub struct Duration { secs: i64, nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC } /// The minimum possible `Duration`: `i64::MIN` milliseconds. -pub const MIN: Duration = Duration { +pub(crate) const MIN: Duration = Duration { secs: i64::MIN / MILLIS_PER_SEC - 1, nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, }; /// The maximum possible `Duration`: `i64::MAX` milliseconds. -pub const MAX: Duration = Duration { +pub(crate) const MAX: Duration = Duration { secs: i64::MAX / MILLIS_PER_SEC, nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, }; @@ -115,7 +120,7 @@ impl Duration { /// Makes a new `Duration` with given number of milliseconds. #[inline] - pub fn milliseconds(milliseconds: i64) -> Duration { + pub const fn milliseconds(milliseconds: i64) -> Duration { let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); let nanos = millis as i32 * NANOS_PER_MILLI; Duration { secs: secs, nanos: nanos } @@ -123,7 +128,7 @@ impl Duration { /// Makes a new `Duration` with given number of microseconds. #[inline] - pub fn microseconds(microseconds: i64) -> Duration { + pub const fn microseconds(microseconds: i64) -> Duration { let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); let nanos = micros as i32 * NANOS_PER_MICRO; Duration { secs: secs, nanos: nanos } @@ -131,36 +136,36 @@ impl Duration { /// Makes a new `Duration` with given number of nanoseconds. #[inline] - pub fn nanoseconds(nanos: i64) -> Duration { + pub const fn nanoseconds(nanos: i64) -> Duration { let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); Duration { secs: secs, nanos: nanos as i32 } } /// Returns the total number of whole weeks in the duration. #[inline] - pub fn num_weeks(&self) -> i64 { + pub const fn num_weeks(&self) -> i64 { self.num_days() / 7 } /// Returns the total number of whole days in the duration. - pub fn num_days(&self) -> i64 { + pub const fn num_days(&self) -> i64 { self.num_seconds() / SECS_PER_DAY } /// Returns the total number of whole hours in the duration. #[inline] - pub fn num_hours(&self) -> i64 { + pub const fn num_hours(&self) -> i64 { self.num_seconds() / SECS_PER_HOUR } /// Returns the total number of whole minutes in the duration. #[inline] - pub fn num_minutes(&self) -> i64 { + pub const fn num_minutes(&self) -> i64 { self.num_seconds() / SECS_PER_MINUTE } /// Returns the total number of whole seconds in the duration. - pub fn num_seconds(&self) -> i64 { + pub const fn num_seconds(&self) -> i64 { // If secs is negative, nanos should be subtracted from the duration. if self.secs < 0 && self.nanos > 0 { self.secs + 1 @@ -172,7 +177,7 @@ impl Duration { /// Returns the number of nanoseconds such that /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of /// nanoseconds in the duration. - fn nanos_mod_sec(&self) -> i32 { + const fn nanos_mod_sec(&self) -> i32 { if self.secs < 0 && self.nanos > 0 { self.nanos - NANOS_PER_SEC } else { @@ -181,7 +186,7 @@ impl Duration { } /// Returns the total number of whole milliseconds in the duration, - pub fn num_milliseconds(&self) -> i64 { + pub const fn num_milliseconds(&self) -> i64 { // A proper Duration will not overflow, because MIN and MAX are defined // such that the range is exactly i64 milliseconds. let secs_part = self.num_seconds() * MILLIS_PER_SEC; @@ -191,7 +196,7 @@ impl Duration { /// Returns the total number of whole microseconds in the duration, /// or `None` on overflow (exceeding 2^63 microseconds in either direction). - pub fn num_microseconds(&self) -> Option<i64> { + pub const fn num_microseconds(&self) -> Option<i64> { let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO; secs_part.checked_add(nanos_part as i64) @@ -199,7 +204,7 @@ impl Duration { /// Returns the total number of whole nanoseconds in the duration, /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). - pub fn num_nanoseconds(&self) -> Option<i64> { + pub const fn num_nanoseconds(&self) -> Option<i64> { let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); let nanos_part = self.nanos_mod_sec(); secs_part.checked_add(nanos_part as i64) @@ -243,31 +248,35 @@ impl Duration { /// Returns the duration as an absolute (non-negative) value. #[inline] - pub fn abs(&self) -> Duration { - Duration { secs: self.secs.abs(), nanos: self.nanos } + pub const fn abs(&self) -> Duration { + if self.secs < 0 && self.nanos != 0 { + Duration { secs: (self.secs + 1).abs(), nanos: NANOS_PER_SEC - self.nanos } + } else { + Duration { secs: self.secs.abs(), nanos: self.nanos } + } } /// The minimum possible `Duration`: `i64::MIN` milliseconds. #[inline] - pub fn min_value() -> Duration { + pub const fn min_value() -> Duration { MIN } /// The maximum possible `Duration`: `i64::MAX` milliseconds. #[inline] - pub fn max_value() -> Duration { + pub const fn max_value() -> Duration { MAX } /// A duration where the stored seconds and nanoseconds are equal to zero. #[inline] - pub fn zero() -> Duration { + pub const fn zero() -> Duration { Duration { secs: 0, nanos: 0 } } /// Returns `true` if the duration equals `Duration::zero()`. #[inline] - pub fn is_zero(&self) -> bool { + pub const fn is_zero(&self) -> bool { self.secs == 0 && self.nanos == 0 } @@ -372,7 +381,26 @@ impl Div<i32> for Duration { } } +#[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl<'a> std::iter::Sum<&'a Duration> for Duration { + fn sum<I: Iterator<Item = &'a Duration>>(iter: I) -> Duration { + iter.fold(Duration::zero(), |acc, x| acc + *x) + } +} + +#[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::iter::Sum<Duration> for Duration { + fn sum<I: Iterator<Item = Duration>>(iter: I) -> Duration { + iter.fold(Duration::zero(), |acc, x| acc + x) + } +} + impl fmt::Display for Duration { + /// Format a duration using the [ISO 8601] format + /// + /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601#Durations fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // technically speaking, negative duration is not valid ISO 8601, // but we need to print it anyway. @@ -419,6 +447,7 @@ impl fmt::Display for OutOfRangeError { } #[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Error for OutOfRangeError { #[allow(deprecated)] fn description(&self) -> &str { @@ -428,12 +457,12 @@ impl Error for OutOfRangeError { // Copied from libnum #[inline] -fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { +const fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { (div_floor_64(this, other), mod_floor_64(this, other)) } #[inline] -fn div_floor_64(this: i64, other: i64) -> i64 { +const fn div_floor_64(this: i64, other: i64) -> i64 { match div_rem_64(this, other) { (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, (d, _) => d, @@ -441,7 +470,7 @@ fn div_floor_64(this: i64, other: i64) -> i64 { } #[inline] -fn mod_floor_64(this: i64, other: i64) -> i64 { +const fn mod_floor_64(this: i64, other: i64) -> i64 { match this % other { r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, r => r, @@ -449,10 +478,28 @@ fn mod_floor_64(this: i64, other: i64) -> i64 { } #[inline] -fn div_rem_64(this: i64, other: i64) -> (i64, i64) { +const fn div_rem_64(this: i64, other: i64) -> (i64, i64) { (this / other, this % other) } +#[cfg(feature = "arbitrary")] +impl arbitrary::Arbitrary<'_> for Duration { + fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<Duration> { + const MIN_SECS: i64 = i64::MIN / MILLIS_PER_SEC - 1; + const MAX_SECS: i64 = i64::MAX / MILLIS_PER_SEC; + + let secs: i64 = u.int_in_range(MIN_SECS..=MAX_SECS)?; + let nanos: i32 = u.int_in_range(0..=(NANOS_PER_SEC - 1))?; + let duration = Duration { secs, nanos }; + + if duration < MIN || duration > MAX { + Err(arbitrary::Error::IncorrectFormat) + } else { + Ok(duration) + } + } +} + #[cfg(test)] mod tests { use super::{Duration, OutOfRangeError, MAX, MIN}; @@ -589,6 +636,19 @@ mod tests { } #[test] + fn test_duration_abs() { + assert_eq!(Duration::milliseconds(1300).abs(), Duration::milliseconds(1300)); + assert_eq!(Duration::milliseconds(1000).abs(), Duration::milliseconds(1000)); + assert_eq!(Duration::milliseconds(300).abs(), Duration::milliseconds(300)); + assert_eq!(Duration::milliseconds(0).abs(), Duration::milliseconds(0)); + assert_eq!(Duration::milliseconds(-300).abs(), Duration::milliseconds(300)); + assert_eq!(Duration::milliseconds(-700).abs(), Duration::milliseconds(700)); + assert_eq!(Duration::milliseconds(-1000).abs(), Duration::milliseconds(1000)); + assert_eq!(Duration::milliseconds(-1300).abs(), Duration::milliseconds(1300)); + assert_eq!(Duration::milliseconds(-1700).abs(), Duration::milliseconds(1700)); + } + + #[test] fn test_duration_mul() { assert_eq!(Duration::zero() * i32::MAX, Duration::zero()); assert_eq!(Duration::zero() * i32::MIN, Duration::zero()); @@ -627,6 +687,27 @@ mod tests { } #[test] + fn test_duration_sum() { + let duration_list_1 = [Duration::zero(), Duration::seconds(1)]; + let sum_1: Duration = duration_list_1.iter().sum(); + assert_eq!(sum_1, Duration::seconds(1)); + + let duration_list_2 = + [Duration::zero(), Duration::seconds(1), Duration::seconds(6), Duration::seconds(10)]; + let sum_2: Duration = duration_list_2.iter().sum(); + assert_eq!(sum_2, Duration::seconds(17)); + + let duration_vec = vec![ + Duration::zero(), + Duration::seconds(1), + Duration::seconds(6), + Duration::seconds(10), + ]; + let sum_3: Duration = duration_vec.into_iter().sum(); + assert_eq!(sum_3, Duration::seconds(17)); + } + + #[test] fn test_duration_fmt() { assert_eq!(Duration::zero().to_string(), "PT0S"); assert_eq!(Duration::days(42).to_string(), "P42D"); diff --git a/vendor/chrono/src/round.rs b/vendor/chrono/src/round.rs index 92d7c3a50..b95f24570 100644 --- a/vendor/chrono/src/round.rs +++ b/vendor/chrono/src/round.rs @@ -1,16 +1,15 @@ // This is a part of Chrono. // See README.md and LICENSE.txt for details. +use crate::datetime::DateTime; +use crate::oldtime::Duration; +use crate::NaiveDateTime; +use crate::TimeZone; +use crate::Timelike; use core::cmp::Ordering; use core::fmt; use core::marker::Sized; use core::ops::{Add, Sub}; -use datetime::DateTime; -use oldtime::Duration; -#[cfg(any(feature = "std", test))] -use std; -use TimeZone; -use Timelike; /// Extension trait for subsecond rounding or truncation to a maximum number /// of digits. Rounding can be used to decrease the error variance when @@ -25,8 +24,8 @@ pub trait SubsecRound { /// /// # Example /// ``` rust - /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; - /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000); /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000); /// ``` @@ -37,8 +36,8 @@ pub trait SubsecRound { /// /// # Example /// ``` rust - /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; - /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000); /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000); /// ``` @@ -112,8 +111,8 @@ pub trait DurationRound: Sized { /// /// # Example /// ``` rust - /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc}; - /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!( /// dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(), /// "2018-01-11 12:00:00.150 UTC" @@ -129,8 +128,8 @@ pub trait DurationRound: Sized { /// /// # Example /// ``` rust - /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc}; - /// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154); + /// # use chrono::{DateTime, DurationRound, Duration, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap(); /// assert_eq!( /// dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(), /// "2018-01-11 12:00:00.150 UTC" @@ -150,52 +149,89 @@ impl<Tz: TimeZone> DurationRound for DateTime<Tz> { type Err = RoundingError; fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> { - if let Some(span) = duration.num_nanoseconds() { - if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { - return Err(RoundingError::TimestampExceedsLimit); - } - let stamp = self.timestamp_nanos(); - if span > stamp.abs() { - return Err(RoundingError::DurationExceedsTimestamp); - } - let delta_down = stamp % span; - if delta_down == 0 { - Ok(self) + duration_round(self.naive_local(), self, duration) + } + + fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> { + duration_trunc(self.naive_local(), self, duration) + } +} + +impl DurationRound for NaiveDateTime { + type Err = RoundingError; + + fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> { + duration_round(self, self, duration) + } + + fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> { + duration_trunc(self, self, duration) + } +} + +fn duration_round<T>( + naive: NaiveDateTime, + original: T, + duration: Duration, +) -> Result<T, RoundingError> +where + T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>, +{ + if let Some(span) = duration.num_nanoseconds() { + if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { + return Err(RoundingError::TimestampExceedsLimit); + } + let stamp = naive.timestamp_nanos(); + if span > stamp.abs() { + return Err(RoundingError::DurationExceedsTimestamp); + } + if span == 0 { + return Ok(original); + } + let delta_down = stamp % span; + if delta_down == 0 { + Ok(original) + } else { + let (delta_up, delta_down) = if delta_down < 0 { + (delta_down.abs(), span - delta_down.abs()) + } else { + (span - delta_down, delta_down) + }; + if delta_up <= delta_down { + Ok(original + Duration::nanoseconds(delta_up)) } else { - let (delta_up, delta_down) = if delta_down < 0 { - (delta_down.abs(), span - delta_down.abs()) - } else { - (span - delta_down, delta_down) - }; - if delta_up <= delta_down { - Ok(self + Duration::nanoseconds(delta_up)) - } else { - Ok(self - Duration::nanoseconds(delta_down)) - } + Ok(original - Duration::nanoseconds(delta_down)) } - } else { - Err(RoundingError::DurationExceedsLimit) } + } else { + Err(RoundingError::DurationExceedsLimit) } +} - fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err> { - if let Some(span) = duration.num_nanoseconds() { - if self.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { - return Err(RoundingError::TimestampExceedsLimit); - } - let stamp = self.timestamp_nanos(); - if span > stamp.abs() { - return Err(RoundingError::DurationExceedsTimestamp); - } - let delta_down = stamp % span; - match delta_down.cmp(&0) { - Ordering::Equal => Ok(self), - Ordering::Greater => Ok(self - Duration::nanoseconds(delta_down)), - Ordering::Less => Ok(self - Duration::nanoseconds(span - delta_down.abs())), - } - } else { - Err(RoundingError::DurationExceedsLimit) +fn duration_trunc<T>( + naive: NaiveDateTime, + original: T, + duration: Duration, +) -> Result<T, RoundingError> +where + T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>, +{ + if let Some(span) = duration.num_nanoseconds() { + if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS { + return Err(RoundingError::TimestampExceedsLimit); + } + let stamp = naive.timestamp_nanos(); + if span > stamp.abs() { + return Err(RoundingError::DurationExceedsTimestamp); } + let delta_down = stamp % span; + match delta_down.cmp(&0) { + Ordering::Equal => Ok(original), + Ordering::Greater => Ok(original - Duration::nanoseconds(delta_down)), + Ordering::Less => Ok(original - Duration::nanoseconds(span - delta_down.abs())), + } + } else { + Err(RoundingError::DurationExceedsLimit) } } @@ -208,7 +244,7 @@ pub enum RoundingError { /// /// ``` rust /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; - /// let dt = Utc.ymd(1970, 12, 12).and_hms(0, 0, 0); + /// let dt = Utc.with_ymd_and_hms(1970, 12, 12, 0, 0, 0).unwrap(); /// /// assert_eq!( /// dt.duration_round(Duration::days(365)), @@ -220,8 +256,8 @@ pub enum RoundingError { /// Error when `Duration.num_nanoseconds` exceeds the limit. /// /// ``` rust - /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; - /// let dt = Utc.ymd(2260, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000); + /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc, NaiveDate}; + /// let dt = NaiveDate::from_ymd_opt(2260, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_75_500_000).unwrap().and_local_timezone(Utc).unwrap(); /// /// assert_eq!( /// dt.duration_round(Duration::days(300 * 365)), @@ -234,7 +270,7 @@ pub enum RoundingError { /// /// ``` rust /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc}; - /// let dt = Utc.ymd(2300, 12, 12).and_hms(0, 0, 0); + /// let dt = Utc.with_ymd_and_hms(2300, 12, 12, 0, 0, 0).unwrap(); /// /// assert_eq!(dt.duration_round(Duration::days(1)), Err(RoundingError::TimestampExceedsLimit),); /// ``` @@ -258,6 +294,7 @@ impl fmt::Display for RoundingError { } #[cfg(any(feature = "std", test))] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for RoundingError { #[allow(deprecated)] fn description(&self) -> &str { @@ -268,29 +305,44 @@ impl std::error::Error for RoundingError { #[cfg(test)] mod tests { use super::{Duration, DurationRound, SubsecRound}; - use offset::{FixedOffset, TimeZone, Utc}; - use Timelike; + use crate::offset::{FixedOffset, TimeZone, Utc}; + use crate::NaiveDate; + use crate::Timelike; #[test] fn test_round_subsecs() { - let pst = FixedOffset::east(8 * 60 * 60); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684); + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_684) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.round_subsecs(10), dt); assert_eq!(dt.round_subsecs(9), dt); - assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680); - assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700); - assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000); - assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000); - assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000); - assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000); - assert_eq!(dt.round_subsecs(2).nanosecond(), 080_000_000); + assert_eq!(dt.round_subsecs(8).nanosecond(), 84_660_680); + assert_eq!(dt.round_subsecs(7).nanosecond(), 84_660_700); + assert_eq!(dt.round_subsecs(6).nanosecond(), 84_661_000); + assert_eq!(dt.round_subsecs(5).nanosecond(), 84_660_000); + assert_eq!(dt.round_subsecs(4).nanosecond(), 84_700_000); + assert_eq!(dt.round_subsecs(3).nanosecond(), 85_000_000); + assert_eq!(dt.round_subsecs(2).nanosecond(), 80_000_000); assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000); assert_eq!(dt.round_subsecs(0).nanosecond(), 0); assert_eq!(dt.round_subsecs(0).second(), 13); - let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 27, 750_500_000) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.round_subsecs(9), dt); assert_eq!(dt.round_subsecs(4), dt); assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000); @@ -303,7 +355,14 @@ mod tests { #[test] fn test_round_leap_nanos() { - let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 1_750_500_000) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.round_subsecs(9), dt); assert_eq!(dt.round_subsecs(4), dt); assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000); @@ -316,24 +375,38 @@ mod tests { #[test] fn test_trunc_subsecs() { - let pst = FixedOffset::east(8 * 60 * 60); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684); + let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 13, 84_660_684) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.trunc_subsecs(10), dt); assert_eq!(dt.trunc_subsecs(9), dt); - assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680); - assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600); - assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000); - assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000); - assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000); - assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000); - assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_000_000); + assert_eq!(dt.trunc_subsecs(8).nanosecond(), 84_660_680); + assert_eq!(dt.trunc_subsecs(7).nanosecond(), 84_660_600); + assert_eq!(dt.trunc_subsecs(6).nanosecond(), 84_660_000); + assert_eq!(dt.trunc_subsecs(5).nanosecond(), 84_660_000); + assert_eq!(dt.trunc_subsecs(4).nanosecond(), 84_600_000); + assert_eq!(dt.trunc_subsecs(3).nanosecond(), 84_000_000); + assert_eq!(dt.trunc_subsecs(2).nanosecond(), 80_000_000); assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0); assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); assert_eq!(dt.trunc_subsecs(0).second(), 13); - let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000); + let dt = pst + .from_local_datetime( + &NaiveDate::from_ymd_opt(2018, 1, 11) + .unwrap() + .and_hms_nano_opt(10, 5, 27, 750_500_000) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.trunc_subsecs(9), dt); assert_eq!(dt.trunc_subsecs(4), dt); assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000); @@ -346,7 +419,14 @@ mod tests { #[test] fn test_trunc_leap_nanos() { - let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 1_750_500_000) + .unwrap(), + ) + .unwrap(); assert_eq!(dt.trunc_subsecs(9), dt); assert_eq!(dt.trunc_subsecs(4), dt); assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000); @@ -359,7 +439,19 @@ mod tests { #[test] fn test_duration_round() { - let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 175_500_000); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 175_500_000) + .unwrap(), + ) + .unwrap(); + + assert_eq!( + dt.duration_round(Duration::zero()).unwrap().to_string(), + "2016-12-31 23:59:59.175500 UTC" + ); assert_eq!( dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(), @@ -367,13 +459,27 @@ mod tests { ); // round up - let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 30, 0) + .unwrap(), + ) + .unwrap(); assert_eq!( dt.duration_round(Duration::minutes(5)).unwrap().to_string(), "2012-12-12 18:25:00 UTC" ); // round down - let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 29, 999) + .unwrap(), + ) + .unwrap(); assert_eq!( dt.duration_round(Duration::minutes(5)).unwrap().to_string(), "2012-12-12 18:20:00 UTC" @@ -395,11 +501,104 @@ mod tests { dt.duration_round(Duration::days(1)).unwrap().to_string(), "2012-12-13 00:00:00 UTC" ); + + // timezone east + let dt = + FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); + assert_eq!( + dt.duration_round(Duration::days(1)).unwrap().to_string(), + "2020-10-28 00:00:00 +01:00" + ); + assert_eq!( + dt.duration_round(Duration::weeks(1)).unwrap().to_string(), + "2020-10-29 00:00:00 +01:00" + ); + + // timezone west + let dt = + FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); + assert_eq!( + dt.duration_round(Duration::days(1)).unwrap().to_string(), + "2020-10-28 00:00:00 -01:00" + ); + assert_eq!( + dt.duration_round(Duration::weeks(1)).unwrap().to_string(), + "2020-10-29 00:00:00 -01:00" + ); + } + + #[test] + fn test_duration_round_naive() { + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 175_500_000) + .unwrap(), + ) + .unwrap() + .naive_utc(); + + assert_eq!( + dt.duration_round(Duration::zero()).unwrap().to_string(), + "2016-12-31 23:59:59.175500" + ); + + assert_eq!( + dt.duration_round(Duration::milliseconds(10)).unwrap().to_string(), + "2016-12-31 23:59:59.180" + ); + + // round up + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 30, 0) + .unwrap(), + ) + .unwrap() + .naive_utc(); + assert_eq!( + dt.duration_round(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:25:00" + ); + // round down + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 29, 999) + .unwrap(), + ) + .unwrap() + .naive_utc(); + assert_eq!( + dt.duration_round(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00" + ); + + assert_eq!( + dt.duration_round(Duration::minutes(10)).unwrap().to_string(), + "2012-12-12 18:20:00" + ); + assert_eq!( + dt.duration_round(Duration::minutes(30)).unwrap().to_string(), + "2012-12-12 18:30:00" + ); + assert_eq!( + dt.duration_round(Duration::hours(1)).unwrap().to_string(), + "2012-12-12 18:00:00" + ); + assert_eq!( + dt.duration_round(Duration::days(1)).unwrap().to_string(), + "2012-12-13 00:00:00" + ); } #[test] fn test_duration_round_pre_epoch() { - let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12); + let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); assert_eq!( dt.duration_round(Duration::minutes(10)).unwrap().to_string(), "1969-12-12 12:10:00 UTC" @@ -408,7 +607,14 @@ mod tests { #[test] fn test_duration_trunc() { - let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_75_500_000); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 175_500_000) + .unwrap(), + ) + .unwrap(); assert_eq!( dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(), @@ -416,13 +622,27 @@ mod tests { ); // would round up - let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 30, 0); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 30, 0) + .unwrap(), + ) + .unwrap(); assert_eq!( dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), "2012-12-12 18:20:00 UTC" ); // would round down - let dt = Utc.ymd(2012, 12, 12).and_hms_milli(18, 22, 29, 999); + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 29, 999) + .unwrap(), + ) + .unwrap(); assert_eq!( dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), "2012-12-12 18:20:00 UTC" @@ -443,11 +663,98 @@ mod tests { dt.duration_trunc(Duration::days(1)).unwrap().to_string(), "2012-12-12 00:00:00 UTC" ); + + // timezone east + let dt = + FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); + assert_eq!( + dt.duration_trunc(Duration::days(1)).unwrap().to_string(), + "2020-10-27 00:00:00 +01:00" + ); + assert_eq!( + dt.duration_trunc(Duration::weeks(1)).unwrap().to_string(), + "2020-10-22 00:00:00 +01:00" + ); + + // timezone west + let dt = + FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); + assert_eq!( + dt.duration_trunc(Duration::days(1)).unwrap().to_string(), + "2020-10-27 00:00:00 -01:00" + ); + assert_eq!( + dt.duration_trunc(Duration::weeks(1)).unwrap().to_string(), + "2020-10-22 00:00:00 -01:00" + ); + } + + #[test] + fn test_duration_trunc_naive() { + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2016, 12, 31) + .unwrap() + .and_hms_nano_opt(23, 59, 59, 175_500_000) + .unwrap(), + ) + .unwrap() + .naive_utc(); + + assert_eq!( + dt.duration_trunc(Duration::milliseconds(10)).unwrap().to_string(), + "2016-12-31 23:59:59.170" + ); + + // would round up + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 30, 0) + .unwrap(), + ) + .unwrap() + .naive_utc(); + assert_eq!( + dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00" + ); + // would round down + let dt = Utc + .from_local_datetime( + &NaiveDate::from_ymd_opt(2012, 12, 12) + .unwrap() + .and_hms_milli_opt(18, 22, 29, 999) + .unwrap(), + ) + .unwrap() + .naive_utc(); + assert_eq!( + dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(), + "2012-12-12 18:20:00" + ); + assert_eq!( + dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(), + "2012-12-12 18:20:00" + ); + assert_eq!( + dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(), + "2012-12-12 18:00:00" + ); + assert_eq!( + dt.duration_trunc(Duration::hours(1)).unwrap().to_string(), + "2012-12-12 18:00:00" + ); + assert_eq!( + dt.duration_trunc(Duration::days(1)).unwrap().to_string(), + "2012-12-12 00:00:00" + ); } #[test] fn test_duration_trunc_pre_epoch() { - let dt = Utc.ymd(1969, 12, 12).and_hms(12, 12, 12); + let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); assert_eq!( dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(), "1969-12-12 12:10:00 UTC" diff --git a/vendor/chrono/src/sys.rs b/vendor/chrono/src/sys.rs deleted file mode 100644 index 2e46b7e8e..000000000 --- a/vendor/chrono/src/sys.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Platform wrappers for converting UTC times to and from the local time zone. -//! -//! This code was rescued from v0.1 of the time crate, which is no longer -//! maintained. It has been substantially stripped down to the bare minimum -//! required by chrono. - -use std::time::{SystemTime, UNIX_EPOCH}; - -#[cfg(any(target_arch = "wasm32", target_env = "sgx"))] -#[path = "sys/stub.rs"] -mod inner; - -#[cfg(unix)] -#[path = "sys/unix.rs"] -mod inner; - -#[cfg(windows)] -#[path = "sys/windows.rs"] -mod inner; - -/// A record specifying a time value in seconds and nanoseconds, where -/// nanoseconds represent the offset from the given second. -/// -/// For example a timespec of 1.2 seconds after the beginning of the epoch would -/// be represented as {sec: 1, nsec: 200000000}. -pub struct Timespec { - pub sec: i64, - pub nsec: i32, -} - -impl Timespec { - /// Constructs a timespec representing the current time in UTC. - pub fn now() -> Timespec { - let st = - SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); - Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 } - } - - /// Converts this timespec into the system's local time. - pub fn local(self) -> Tm { - let mut tm = Tm { - tm_sec: 0, - tm_min: 0, - tm_hour: 0, - tm_mday: 0, - tm_mon: 0, - tm_year: 0, - tm_wday: 0, - tm_yday: 0, - tm_isdst: 0, - tm_utcoff: 0, - tm_nsec: 0, - }; - inner::time_to_local_tm(self.sec, &mut tm); - tm.tm_nsec = self.nsec; - tm - } -} - -/// Holds a calendar date and time broken down into its components (year, month, -/// day, and so on), also called a broken-down time value. -// FIXME: use c_int instead of i32? -#[cfg(feature = "clock")] -#[repr(C)] -pub struct Tm { - /// Seconds after the minute - [0, 60] - pub tm_sec: i32, - - /// Minutes after the hour - [0, 59] - pub tm_min: i32, - - /// Hours after midnight - [0, 23] - pub tm_hour: i32, - - /// Day of the month - [1, 31] - pub tm_mday: i32, - - /// Months since January - [0, 11] - pub tm_mon: i32, - - /// Years since 1900 - pub tm_year: i32, - - /// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday. - pub tm_wday: i32, - - /// Days since January 1 - [0, 365] - pub tm_yday: i32, - - /// Daylight Saving Time flag. - /// - /// This value is positive if Daylight Saving Time is in effect, zero if - /// Daylight Saving Time is not in effect, and negative if this information - /// is not available. - pub tm_isdst: i32, - - /// Identifies the time zone that was used to compute this broken-down time - /// value, including any adjustment for Daylight Saving Time. This is the - /// number of seconds east of UTC. For example, for U.S. Pacific Daylight - /// Time, the value is `-7*60*60 = -25200`. - pub tm_utcoff: i32, - - /// Nanoseconds after the second - [0, 10<sup>9</sup> - 1] - pub tm_nsec: i32, -} - -impl Tm { - /// Convert time to the seconds from January 1, 1970 - pub fn to_timespec(&self) -> Timespec { - let sec = match self.tm_utcoff { - 0 => inner::utc_tm_to_time(self), - _ => inner::local_tm_to_time(self), - }; - Timespec { sec: sec, nsec: self.tm_nsec } - } -} diff --git a/vendor/chrono/src/sys/stub.rs b/vendor/chrono/src/sys/stub.rs deleted file mode 100644 index 9172a8522..000000000 --- a/vendor/chrono/src/sys/stub.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::Tm; - -fn time_to_tm(ts: i64, tm: &mut Tm) { - let leapyear = |year| -> bool { year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) }; - - static YTAB: [[i64; 12]; 2] = [ - [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - ]; - - let mut year = 1970; - - let dayclock = ts % 86400; - let mut dayno = ts / 86400; - - tm.tm_sec = (dayclock % 60) as i32; - tm.tm_min = ((dayclock % 3600) / 60) as i32; - tm.tm_hour = (dayclock / 3600) as i32; - tm.tm_wday = ((dayno + 4) % 7) as i32; - loop { - let yearsize = if leapyear(year) { 366 } else { 365 }; - if dayno >= yearsize { - dayno -= yearsize; - year += 1; - } else { - break; - } - } - tm.tm_year = (year - 1900) as i32; - tm.tm_yday = dayno as i32; - let mut mon = 0; - while dayno >= YTAB[if leapyear(year) { 1 } else { 0 }][mon] { - dayno -= YTAB[if leapyear(year) { 1 } else { 0 }][mon]; - mon += 1; - } - tm.tm_mon = mon as i32; - tm.tm_mday = dayno as i32 + 1; - tm.tm_isdst = 0; -} - -fn tm_to_time(tm: &Tm) -> i64 { - let mut y = tm.tm_year as i64 + 1900; - let mut m = tm.tm_mon as i64 + 1; - if m <= 2 { - y -= 1; - m += 12; - } - let d = tm.tm_mday as i64; - let h = tm.tm_hour as i64; - let mi = tm.tm_min as i64; - let s = tm.tm_sec as i64; - (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 - + 3600 * h - + 60 * mi - + s -} - -pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { - // FIXME: Add timezone logic - time_to_tm(sec, tm); -} - -pub fn utc_tm_to_time(tm: &Tm) -> i64 { - tm_to_time(tm) -} - -pub fn local_tm_to_time(tm: &Tm) -> i64 { - // FIXME: Add timezone logic - tm_to_time(tm) -} diff --git a/vendor/chrono/src/sys/unix.rs b/vendor/chrono/src/sys/unix.rs deleted file mode 100644 index 2f845e745..000000000 --- a/vendor/chrono/src/sys/unix.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::Tm; -use libc::{self, time_t}; -use std::io; -use std::mem; - -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -extern "C" { - static timezone: time_t; - static altzone: time_t; -} - -#[cfg(any(target_os = "solaris", target_os = "illumos"))] -fn tzset() { - extern "C" { - fn tzset(); - } - unsafe { tzset() } -} - -fn rust_tm_to_tm(rust_tm: &Tm, tm: &mut libc::tm) { - tm.tm_sec = rust_tm.tm_sec; - tm.tm_min = rust_tm.tm_min; - tm.tm_hour = rust_tm.tm_hour; - tm.tm_mday = rust_tm.tm_mday; - tm.tm_mon = rust_tm.tm_mon; - tm.tm_year = rust_tm.tm_year; - tm.tm_wday = rust_tm.tm_wday; - tm.tm_yday = rust_tm.tm_yday; - tm.tm_isdst = rust_tm.tm_isdst; -} - -fn tm_to_rust_tm(tm: &libc::tm, utcoff: i32, rust_tm: &mut Tm) { - rust_tm.tm_sec = tm.tm_sec; - rust_tm.tm_min = tm.tm_min; - rust_tm.tm_hour = tm.tm_hour; - rust_tm.tm_mday = tm.tm_mday; - rust_tm.tm_mon = tm.tm_mon; - rust_tm.tm_year = tm.tm_year; - rust_tm.tm_wday = tm.tm_wday; - rust_tm.tm_yday = tm.tm_yday; - rust_tm.tm_isdst = tm.tm_isdst; - rust_tm.tm_utcoff = utcoff; -} - -#[cfg(any(target_os = "nacl", target_os = "solaris", target_os = "illumos"))] -unsafe fn timegm(tm: *mut libc::tm) -> time_t { - use std::env::{remove_var, set_var, var_os}; - extern "C" { - fn tzset(); - } - - let ret; - - let current_tz = var_os("TZ"); - set_var("TZ", "UTC"); - tzset(); - - ret = libc::mktime(tm); - - if let Some(tz) = current_tz { - set_var("TZ", tz); - } else { - remove_var("TZ"); - } - tzset(); - - ret -} - -pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { - unsafe { - let sec = sec as time_t; - let mut out = mem::zeroed(); - if libc::localtime_r(&sec, &mut out).is_null() { - panic!("localtime_r failed: {}", io::Error::last_os_error()); - } - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - let gmtoff = { - tzset(); - // < 0 means we don't know; assume we're not in DST. - if out.tm_isdst == 0 { - // timezone is seconds west of UTC, tm_gmtoff is seconds east - -timezone - } else if out.tm_isdst > 0 { - -altzone - } else { - -timezone - } - }; - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] - let gmtoff = out.tm_gmtoff; - tm_to_rust_tm(&out, gmtoff as i32, tm); - } -} - -pub fn utc_tm_to_time(rust_tm: &Tm) -> i64 { - #[cfg(not(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "nacl", - target_os = "solaris", - target_os = "illumos" - )))] - use libc::timegm; - #[cfg(all(target_os = "android", target_pointer_width = "32"))] - use libc::timegm64 as timegm; - - let mut tm = unsafe { mem::zeroed() }; - rust_tm_to_tm(rust_tm, &mut tm); - unsafe { timegm(&mut tm) as i64 } -} - -pub fn local_tm_to_time(rust_tm: &Tm) -> i64 { - let mut tm = unsafe { mem::zeroed() }; - rust_tm_to_tm(rust_tm, &mut tm); - unsafe { libc::mktime(&mut tm) as i64 } -} diff --git a/vendor/chrono/src/sys/windows.rs b/vendor/chrono/src/sys/windows.rs deleted file mode 100644 index 3f90338e4..000000000 --- a/vendor/chrono/src/sys/windows.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::Tm; -use std::io; -use std::mem; - -use winapi::shared::minwindef::*; -use winapi::um::minwinbase::SYSTEMTIME; -use winapi::um::timezoneapi::*; - -const HECTONANOSECS_IN_SEC: i64 = 10_000_000; -const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; - -fn time_to_file_time(sec: i64) -> FILETIME { - let t = ((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH) as u64; - FILETIME { dwLowDateTime: t as DWORD, dwHighDateTime: (t >> 32) as DWORD } -} - -fn file_time_as_u64(ft: &FILETIME) -> u64 { - ((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64) -} - -fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 { - let t = file_time_as_u64(ft) as i64; - ((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64 -} - -fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME { - unsafe { - let mut ft = mem::zeroed(); - SystemTimeToFileTime(sys, &mut ft); - ft - } -} - -fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME { - let mut sys: SYSTEMTIME = unsafe { mem::zeroed() }; - sys.wSecond = tm.tm_sec as WORD; - sys.wMinute = tm.tm_min as WORD; - sys.wHour = tm.tm_hour as WORD; - sys.wDay = tm.tm_mday as WORD; - sys.wDayOfWeek = tm.tm_wday as WORD; - sys.wMonth = (tm.tm_mon + 1) as WORD; - sys.wYear = (tm.tm_year + 1900) as WORD; - sys -} - -fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) { - tm.tm_sec = sys.wSecond as i32; - tm.tm_min = sys.wMinute as i32; - tm.tm_hour = sys.wHour as i32; - tm.tm_mday = sys.wDay as i32; - tm.tm_wday = sys.wDayOfWeek as i32; - tm.tm_mon = (sys.wMonth - 1) as i32; - tm.tm_year = (sys.wYear - 1900) as i32; - tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday); - - fn yday(year: i32, month: i32, day: i32) -> i32 { - let leap = if month > 2 { - if year % 4 == 0 { - 1 - } else { - 2 - } - } else { - 0 - }; - let july = if month > 7 { 1 } else { 0 }; - - (month - 1) * 30 + month / 2 + (day - 1) - leap + july - } -} - -macro_rules! call { - ($name:ident($($arg:expr),*)) => { - if $name($($arg),*) == 0 { - panic!(concat!(stringify!($name), " failed with: {}"), - io::Error::last_os_error()); - } - } -} - -pub fn time_to_local_tm(sec: i64, tm: &mut Tm) { - let ft = time_to_file_time(sec); - unsafe { - let mut utc = mem::zeroed(); - let mut local = mem::zeroed(); - call!(FileTimeToSystemTime(&ft, &mut utc)); - call!(SystemTimeToTzSpecificLocalTime(0 as *const _, &mut utc, &mut local)); - system_time_to_tm(&local, tm); - - let local = system_time_to_file_time(&local); - let local_sec = file_time_to_unix_seconds(&local); - - let mut tz = mem::zeroed(); - GetTimeZoneInformation(&mut tz); - - // SystemTimeToTzSpecificLocalTime already applied the biases so - // check if it non standard - tm.tm_utcoff = (local_sec - sec) as i32; - tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { 0 } else { 1 }; - } -} - -pub fn utc_tm_to_time(tm: &Tm) -> i64 { - unsafe { - let mut ft = mem::zeroed(); - let sys_time = tm_to_system_time(tm); - call!(SystemTimeToFileTime(&sys_time, &mut ft)); - file_time_to_unix_seconds(&ft) - } -} - -pub fn local_tm_to_time(tm: &Tm) -> i64 { - unsafe { - let mut ft = mem::zeroed(); - let mut utc = mem::zeroed(); - let mut sys_time = tm_to_system_time(tm); - call!(TzSpecificLocalTimeToSystemTime(0 as *mut _, &mut sys_time, &mut utc)); - call!(SystemTimeToFileTime(&utc, &mut ft)); - file_time_to_unix_seconds(&ft) - } -} diff --git a/vendor/chrono/src/traits.rs b/vendor/chrono/src/traits.rs new file mode 100644 index 000000000..1b6af6926 --- /dev/null +++ b/vendor/chrono/src/traits.rs @@ -0,0 +1,241 @@ +use crate::{IsoWeek, Weekday}; + +/// The common set of methods for date component. +pub trait Datelike: Sized { + /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date). + fn year(&self) -> i32; + + /// Returns the absolute year number starting from 1 with a boolean flag, + /// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD). + #[inline] + fn year_ce(&self) -> (bool, u32) { + let year = self.year(); + if year < 1 { + (false, (1 - year) as u32) + } else { + (true, year as u32) + } + } + + /// Returns the month number starting from 1. + /// + /// The return value ranges from 1 to 12. + fn month(&self) -> u32; + + /// Returns the month number starting from 0. + /// + /// The return value ranges from 0 to 11. + fn month0(&self) -> u32; + + /// Returns the day of month starting from 1. + /// + /// The return value ranges from 1 to 31. (The last day of month differs by months.) + fn day(&self) -> u32; + + /// Returns the day of month starting from 0. + /// + /// The return value ranges from 0 to 30. (The last day of month differs by months.) + fn day0(&self) -> u32; + + /// Returns the day of year starting from 1. + /// + /// The return value ranges from 1 to 366. (The last day of year differs by years.) + fn ordinal(&self) -> u32; + + /// Returns the day of year starting from 0. + /// + /// The return value ranges from 0 to 365. (The last day of year differs by years.) + fn ordinal0(&self) -> u32; + + /// Returns the day of week. + fn weekday(&self) -> Weekday; + + /// Returns the ISO week. + fn iso_week(&self) -> IsoWeek; + + /// Makes a new value with the year number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_year(&self, year: i32) -> Option<Self>; + + /// Makes a new value with the month number (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_month(&self, month: u32) -> Option<Self>; + + /// Makes a new value with the month number (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_month0(&self, month0: u32) -> Option<Self>; + + /// Makes a new value with the day of month (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_day(&self, day: u32) -> Option<Self>; + + /// Makes a new value with the day of month (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_day0(&self, day0: u32) -> Option<Self>; + + /// Makes a new value with the day of year (starting from 1) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_ordinal(&self, ordinal: u32) -> Option<Self>; + + /// Makes a new value with the day of year (starting from 0) changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>; + + /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1. + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().num_days_from_ce(), 719_163); + /// assert_eq!(NaiveDate::from_ymd_opt(2, 1, 1).unwrap().num_days_from_ce(), 366); + /// assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1); + /// assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().num_days_from_ce(), -365); + /// ``` + fn num_days_from_ce(&self) -> i32 { + // See test_num_days_from_ce_against_alternative_impl below for a more straightforward + // implementation. + + // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range. + let mut year = self.year() - 1; + let mut ndays = 0; + if year < 0 { + let excess = 1 + (-year) / 400; + year += excess * 400; + ndays -= excess * 146_097; + } + let div_100 = year / 100; + ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2); + ndays + self.ordinal() as i32 + } +} + +/// The common set of methods for time component. +pub trait Timelike: Sized { + /// Returns the hour number from 0 to 23. + fn hour(&self) -> u32; + + /// Returns the hour number from 1 to 12 with a boolean flag, + /// which is false for AM and true for PM. + #[inline] + fn hour12(&self) -> (bool, u32) { + let hour = self.hour(); + let mut hour12 = hour % 12; + if hour12 == 0 { + hour12 = 12; + } + (hour >= 12, hour12) + } + + /// Returns the minute number from 0 to 59. + fn minute(&self) -> u32; + + /// Returns the second number from 0 to 59. + fn second(&self) -> u32; + + /// Returns the number of nanoseconds since the whole non-leap second. + /// The range from 1,000,000,000 to 1,999,999,999 represents + /// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling). + fn nanosecond(&self) -> u32; + + /// Makes a new value with the hour number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_hour(&self, hour: u32) -> Option<Self>; + + /// Makes a new value with the minute number changed. + /// + /// Returns `None` when the resulting value would be invalid. + fn with_minute(&self, min: u32) -> Option<Self>; + + /// Makes a new value with the second number changed. + /// + /// Returns `None` when the resulting value would be invalid. + /// As with the [`second`](#tymethod.second) method, + /// the input range is restricted to 0 through 59. + fn with_second(&self, sec: u32) -> Option<Self>; + + /// Makes a new value with nanoseconds since the whole non-leap second changed. + /// + /// Returns `None` when the resulting value would be invalid. + /// As with the [`nanosecond`](#tymethod.nanosecond) method, + /// the input range can exceed 1,000,000,000 for leap seconds. + fn with_nanosecond(&self, nano: u32) -> Option<Self>; + + /// Returns the number of non-leap seconds past the last midnight. + #[inline] + fn num_seconds_from_midnight(&self) -> u32 { + self.hour() * 3600 + self.minute() * 60 + self.second() + } +} + +#[cfg(test)] +mod tests { + use super::Datelike; + use crate::{Duration, NaiveDate}; + + /// Tests `Datelike::num_days_from_ce` against an alternative implementation. + /// + /// The alternative implementation is not as short as the current one but it is simpler to + /// understand, with less unexplained magic constants. + #[test] + fn test_num_days_from_ce_against_alternative_impl() { + /// Returns the number of multiples of `div` in the range `start..end`. + /// + /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the + /// behaviour is defined by the following equation: + /// `in_between(start, end, div) == - in_between(end, start, div)`. + /// + /// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`. + /// + /// # Panics + /// + /// Panics if `div` is not positive. + fn in_between(start: i32, end: i32, div: i32) -> i32 { + assert!(div > 0, "in_between: nonpositive div = {}", div); + let start = (start.div_euclid(div), start.rem_euclid(div)); + let end = (end.div_euclid(div), end.rem_euclid(div)); + // The lowest multiple of `div` greater than or equal to `start`, divided. + let start = start.0 + (start.1 != 0) as i32; + // The lowest multiple of `div` greater than or equal to `end`, divided. + let end = end.0 + (end.1 != 0) as i32; + end - start + } + + /// Alternative implementation to `Datelike::num_days_from_ce` + fn num_days_from_ce<Date: Datelike>(date: &Date) -> i32 { + let year = date.year(); + let diff = move |div| in_between(1, year, div); + // 365 days a year, one more in leap years. In the gregorian calendar, leap years are all + // the multiples of 4 except multiples of 100 but including multiples of 400. + date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400) + } + + use num_iter::range_inclusive; + + for year in range_inclusive(NaiveDate::MIN.year(), NaiveDate::MAX.year()) { + let jan1_year = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); + assert_eq!( + jan1_year.num_days_from_ce(), + num_days_from_ce(&jan1_year), + "on {:?}", + jan1_year + ); + let mid_year = jan1_year + Duration::days(133); + assert_eq!( + mid_year.num_days_from_ce(), + num_days_from_ce(&mid_year), + "on {:?}", + mid_year + ); + } + } +} diff --git a/vendor/chrono/src/weekday.rs b/vendor/chrono/src/weekday.rs new file mode 100644 index 000000000..72e384673 --- /dev/null +++ b/vendor/chrono/src/weekday.rs @@ -0,0 +1,329 @@ +use core::fmt; + +#[cfg(feature = "rkyv")] +use rkyv::{Archive, Deserialize, Serialize}; + +/// The day of week. +/// +/// The order of the days of week depends on the context. +/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.) +/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result. +#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] +#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))] +#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +pub enum Weekday { + /// Monday. + Mon = 0, + /// Tuesday. + Tue = 1, + /// Wednesday. + Wed = 2, + /// Thursday. + Thu = 3, + /// Friday. + Fri = 4, + /// Saturday. + Sat = 5, + /// Sunday. + Sun = 6, +} + +impl Weekday { + /// The next day in the week. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon` + #[inline] + pub fn succ(&self) -> Weekday { + match *self { + Weekday::Mon => Weekday::Tue, + Weekday::Tue => Weekday::Wed, + Weekday::Wed => Weekday::Thu, + Weekday::Thu => Weekday::Fri, + Weekday::Fri => Weekday::Sat, + Weekday::Sat => Weekday::Sun, + Weekday::Sun => Weekday::Mon, + } + } + + /// The previous day in the week. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` + #[inline] + pub fn pred(&self) -> Weekday { + match *self { + Weekday::Mon => Weekday::Sun, + Weekday::Tue => Weekday::Mon, + Weekday::Wed => Weekday::Tue, + Weekday::Thu => Weekday::Wed, + Weekday::Fri => Weekday::Thu, + Weekday::Sat => Weekday::Fri, + Weekday::Sun => Weekday::Sat, + } + } + + /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number) + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7 + #[inline] + pub const fn number_from_monday(&self) -> u32 { + self.num_days_from(Weekday::Mon) + 1 + } + + /// Returns a day-of-week number starting from Sunday = 1. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1 + #[inline] + pub const fn number_from_sunday(&self) -> u32 { + self.num_days_from(Weekday::Sun) + 1 + } + + /// Returns a day-of-week number starting from Monday = 0. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6 + #[inline] + pub const fn num_days_from_monday(&self) -> u32 { + self.num_days_from(Weekday::Mon) + } + + /// Returns a day-of-week number starting from Sunday = 0. + /// + /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` + /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0 + #[inline] + pub const fn num_days_from_sunday(&self) -> u32 { + self.num_days_from(Weekday::Sun) + } + + /// Returns a day-of-week number starting from the parameter `day` (D) = 0. + /// + /// `w`: | `D` | `D+1` | `D+2` | `D+3` | `D+4` | `D+5` | `D+6` + /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- + /// `w.num_days_from(wd)`: | 0 | 1 | 2 | 3 | 4 | 5 | 6 + #[inline] + pub(crate) const fn num_days_from(&self, day: Weekday) -> u32 { + (*self as u32 + 7 - day as u32) % 7 + } +} + +impl fmt::Display for Weekday { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + Weekday::Mon => "Mon", + Weekday::Tue => "Tue", + Weekday::Wed => "Wed", + Weekday::Thu => "Thu", + Weekday::Fri => "Fri", + Weekday::Sat => "Sat", + Weekday::Sun => "Sun", + }) + } +} + +/// Any weekday can be represented as an integer from 0 to 6, which equals to +/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation. +/// Do not heavily depend on this though; use explicit methods whenever possible. +impl num_traits::FromPrimitive for Weekday { + #[inline] + fn from_i64(n: i64) -> Option<Weekday> { + match n { + 0 => Some(Weekday::Mon), + 1 => Some(Weekday::Tue), + 2 => Some(Weekday::Wed), + 3 => Some(Weekday::Thu), + 4 => Some(Weekday::Fri), + 5 => Some(Weekday::Sat), + 6 => Some(Weekday::Sun), + _ => None, + } + } + + #[inline] + fn from_u64(n: u64) -> Option<Weekday> { + match n { + 0 => Some(Weekday::Mon), + 1 => Some(Weekday::Tue), + 2 => Some(Weekday::Wed), + 3 => Some(Weekday::Thu), + 4 => Some(Weekday::Fri), + 5 => Some(Weekday::Sat), + 6 => Some(Weekday::Sun), + _ => None, + } + } +} + +/// An error resulting from reading `Weekday` value with `FromStr`. +#[derive(Clone, PartialEq, Eq)] +pub struct ParseWeekdayError { + pub(crate) _dummy: (), +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for ParseWeekdayError {} + +impl fmt::Display for ParseWeekdayError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("{:?}", self)) + } +} + +impl fmt::Debug for ParseWeekdayError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ParseWeekdayError {{ .. }}") + } +} + +#[cfg(test)] +mod tests { + use num_traits::FromPrimitive; + + use super::Weekday; + + #[test] + fn test_num_days_from() { + for i in 0..7 { + let base_day = Weekday::from_u64(i).unwrap(); + + assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon)); + assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun)); + + assert_eq!(base_day.num_days_from(base_day), 0); + + assert_eq!(base_day.num_days_from(base_day.pred()), 1); + assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2); + assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3); + assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4); + assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5); + assert_eq!( + base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()), + 6 + ); + + assert_eq!(base_day.num_days_from(base_day.succ()), 6); + assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5); + assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4); + assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3); + assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2); + assert_eq!( + base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()), + 1 + ); + } + } +} + +// the actual `FromStr` implementation is in the `format` module to leverage the existing code + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +mod weekday_serde { + use super::Weekday; + use core::fmt; + use serde::{de, ser}; + + impl ser::Serialize for Weekday { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: ser::Serializer, + { + serializer.collect_str(&self) + } + } + + struct WeekdayVisitor; + + impl<'de> de::Visitor<'de> for WeekdayVisitor { + type Value = Weekday; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Weekday") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + value.parse().map_err(|_| E::custom("short or long weekday names expected")) + } + } + + impl<'de> de::Deserialize<'de> for Weekday { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: de::Deserializer<'de>, + { + deserializer.deserialize_str(WeekdayVisitor) + } + } + + #[test] + fn test_serde_serialize() { + use serde_json::to_string; + use Weekday::*; + + let cases: Vec<(Weekday, &str)> = vec![ + (Mon, "\"Mon\""), + (Tue, "\"Tue\""), + (Wed, "\"Wed\""), + (Thu, "\"Thu\""), + (Fri, "\"Fri\""), + (Sat, "\"Sat\""), + (Sun, "\"Sun\""), + ]; + + for (weekday, expected_str) in cases { + let string = to_string(&weekday).unwrap(); + assert_eq!(string, expected_str); + } + } + + #[test] + fn test_serde_deserialize() { + use serde_json::from_str; + use Weekday::*; + + let cases: Vec<(&str, Weekday)> = vec![ + ("\"mon\"", Mon), + ("\"MONDAY\"", Mon), + ("\"MonDay\"", Mon), + ("\"mOn\"", Mon), + ("\"tue\"", Tue), + ("\"tuesday\"", Tue), + ("\"wed\"", Wed), + ("\"wednesday\"", Wed), + ("\"thu\"", Thu), + ("\"thursday\"", Thu), + ("\"fri\"", Fri), + ("\"friday\"", Fri), + ("\"sat\"", Sat), + ("\"saturday\"", Sat), + ("\"sun\"", Sun), + ("\"sunday\"", Sun), + ]; + + for (str, expected_weekday) in cases { + let weekday = from_str::<Weekday>(str).unwrap(); + assert_eq!(weekday, expected_weekday); + } + + let errors: Vec<&str> = + vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""]; + + for str in errors { + from_str::<Weekday>(str).unwrap_err(); + } + } +} diff --git a/vendor/chrono/tests/dateutils.rs b/vendor/chrono/tests/dateutils.rs new file mode 100644 index 000000000..dec6bfe11 --- /dev/null +++ b/vendor/chrono/tests/dateutils.rs @@ -0,0 +1,131 @@ +use chrono::offset::TimeZone; +use chrono::Local; +use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike}; + +use std::{path, process}; + +#[cfg(unix)] +fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) { + let output = process::Command::new(path) + .arg("-d") + .arg(format!("{}-{:02}-{:02} {:02}:05:01", dt.year(), dt.month(), dt.day(), dt.hour())) + .arg("+%Y-%m-%d %H:%M:%S %:z") + .output() + .unwrap(); + + let date_command_str = String::from_utf8(output.stdout).unwrap(); + + // The below would be preferred. At this stage neither earliest() or latest() + // seems to be consistent with the output of the `date` command, so we simply + // compare both. + // let local = Local + // .with_ymd_and_hms(year, month, day, hour, 5, 1) + // // looks like the "date" command always returns a given time when it is ambiguous + // .earliest(); + + // if let Some(local) = local { + // assert_eq!(format!("{}\n", local), date_command_str); + // } else { + // // we are in a "Spring forward gap" due to DST, and so date also returns "" + // assert_eq!("", date_command_str); + // } + + // This is used while a decision is made wheter the `date` output needs to + // be exactly matched, or whether LocalResult::Ambigious should be handled + // differently + + let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap(); + match Local.from_local_datetime(&date.and_hms_opt(dt.hour(), 5, 1).unwrap()) { + chrono::LocalResult::Ambiguous(a, b) => assert!( + format!("{}\n", a) == date_command_str || format!("{}\n", b) == date_command_str + ), + chrono::LocalResult::Single(a) => { + assert_eq!(format!("{}\n", a), date_command_str); + } + chrono::LocalResult::None => { + assert_eq!("", date_command_str); + } + } +} + +#[test] +#[cfg(unix)] +fn try_verify_against_date_command() { + let date_path = "/usr/bin/date"; + + if !path::Path::new(date_path).exists() { + // date command not found, skipping + // avoid running this on macOS, which has path /bin/date + // as the required CLI arguments are not present in the + // macOS build. + return; + } + + let mut date = NaiveDate::from_ymd_opt(1975, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); + + while date.year() < 2078 { + if (1975..=1977).contains(&date.year()) + || (2020..=2022).contains(&date.year()) + || (2073..=2077).contains(&date.year()) + { + verify_against_date_command_local(date_path, date); + } + + date += chrono::Duration::hours(1); + } +} + +#[cfg(target_os = "linux")] +fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTime) { + let required_format = + "d%d D%D F%F H%H I%I j%j k%k l%l m%m M%M S%S T%T u%u U%U w%w W%W X%X y%y Y%Y z%:z"; + // a%a - depends from localization + // A%A - depends from localization + // b%b - depends from localization + // B%B - depends from localization + // h%h - depends from localization + // c%c - depends from localization + // p%p - depends from localization + // r%r - depends from localization + // x%x - fails, date is dd/mm/yyyy, chrono is dd/mm/yy, same as %D + // Z%Z - too many ways to represent it, will most likely fail + + let output = process::Command::new(path) + .arg("-d") + .arg(format!( + "{}-{:02}-{:02} {:02}:{:02}:{:02}", + dt.year(), + dt.month(), + dt.day(), + dt.hour(), + dt.minute(), + dt.second() + )) + .arg(format!("+{}", required_format)) + .output() + .unwrap(); + + let date_command_str = String::from_utf8(output.stdout).unwrap(); + let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap(); + let ldt = Local + .from_local_datetime(&date.and_hms_opt(dt.hour(), dt.minute(), dt.second()).unwrap()) + .unwrap(); + let formated_date = format!("{}\n", ldt.format(required_format)); + assert_eq!(date_command_str, formated_date); +} + +#[test] +#[cfg(target_os = "linux")] +fn try_verify_against_date_command_format() { + let date_path = "/usr/bin/date"; + + if !path::Path::new(date_path).exists() { + // date command not found, skipping + return; + } + let mut date = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(12, 11, 13).unwrap(); + while date.year() < 2008 { + verify_against_date_command_format_local(date_path, date); + date += chrono::Duration::days(55); + } +} diff --git a/vendor/chrono/tests/wasm.rs b/vendor/chrono/tests/wasm.rs index 275d120d3..f003d4db9 100644 --- a/vendor/chrono/tests/wasm.rs +++ b/vendor/chrono/tests/wasm.rs @@ -1,67 +1,81 @@ -#[cfg(all(test, feature = "wasmbind"))] -mod test { - extern crate chrono; - extern crate wasm_bindgen_test; +#![cfg(all( + target_arch = "wasm32", + feature = "wasmbind", + not(any(target_os = "emscripten", target_os = "wasi")) +))] - use self::chrono::prelude::*; - use self::wasm_bindgen_test::*; +use self::chrono::prelude::*; +use self::wasm_bindgen_test::*; - #[wasm_bindgen_test] - fn now() { - let utc: DateTime<Utc> = Utc::now(); - let local: DateTime<Local> = Local::now(); +#[wasm_bindgen_test] +fn now() { + let utc: DateTime<Utc> = Utc::now(); + let local: DateTime<Local> = Local::now(); - // Ensure time set by the test script is correct - let now = env!("NOW"); - let actual = Utc.datetime_from_str(&now, "%s").unwrap(); - let diff = utc - actual; - assert!( - diff < chrono::Duration::minutes(5), - "expected {} - {} == {} < 5m (env var: {})", - utc, - actual, - diff, - now, - ); + // Ensure time set by the test script is correct + let now = env!("NOW"); + let actual = Utc.datetime_from_str(&now, "%s").unwrap(); + let diff = utc - actual; + assert!( + diff < chrono::Duration::minutes(5), + "expected {} - {} == {} < 5m (env var: {})", + utc, + actual, + diff, + now, + ); - let tz = env!("TZ"); - eprintln!("testing with tz={}", tz); + let tz = env!("TZ"); + eprintln!("testing with tz={}", tz); - // Ensure offset retrieved when getting local time is correct - let expected_offset = match tz { - "ACST-9:30" => FixedOffset::east(19 * 30 * 60), - "Asia/Katmandu" => FixedOffset::east(23 * 15 * 60), // No DST thankfully - "EDT" | "EST4" | "-0400" => FixedOffset::east(-4 * 60 * 60), - "EST" | "-0500" => FixedOffset::east(-5 * 60 * 60), - "UTC0" | "+0000" => FixedOffset::east(0), - tz => panic!("unexpected TZ {}", tz), - }; - assert_eq!( - &expected_offset, - local.offset(), - "expected: {:?} local: {:?}", - expected_offset, - local.offset(), - ); - } + // Ensure offset retrieved when getting local time is correct + let expected_offset = match tz { + "ACST-9:30" => FixedOffset::east_opt(19 * 30 * 60).unwrap(), + "Asia/Katmandu" => FixedOffset::east_opt(23 * 15 * 60).unwrap(), // No DST thankfully + "EDT" | "EST4" | "-0400" => FixedOffset::east_opt(-4 * 60 * 60).unwrap(), + "EST" | "-0500" => FixedOffset::east_opt(-5 * 60 * 60).unwrap(), + "UTC0" | "+0000" => FixedOffset::east_opt(0).unwrap(), + tz => panic!("unexpected TZ {}", tz), + }; + assert_eq!( + &expected_offset, + local.offset(), + "expected: {:?} local: {:?}", + expected_offset, + local.offset(), + ); +} + +#[wasm_bindgen_test] +fn from_is_exact() { + let now = js_sys::Date::new_0(); - #[wasm_bindgen_test] - fn from_is_exact() { - let now = js_sys::Date::new_0(); + let dt = DateTime::<Utc>::from(now.clone()); - let dt = DateTime::<Utc>::from(now.clone()); + assert_eq!(now.get_time() as i64, dt.timestamp_millis_opt().unwrap()); +} + +#[wasm_bindgen_test] +fn local_from_local_datetime() { + let now = Local::now(); + let ndt = now.naive_local(); + let res = match Local.from_local_datetime(&ndt).single() { + Some(v) => v, + None => panic! {"Required for test!"}, + }; + assert_eq!(now, res); +} - assert_eq!(now.get_time() as i64, dt.timestamp_millis()); - } +#[wasm_bindgen_test] +fn convert_all_parts_with_milliseconds() { + let time: DateTime<Utc> = "2020-12-01T03:01:55.974Z".parse().unwrap(); + let js_date = js_sys::Date::from(time); - #[wasm_bindgen_test] - fn local_from_local_datetime() { - let now = Local::now(); - let ndt = now.naive_local(); - let res = match Local.from_local_datetime(&ndt).single() { - Some(v) => v, - None => panic! {"Required for test!"}, - }; - assert_eq!(now, res); - } + assert_eq!(js_date.get_utc_full_year(), 2020); + assert_eq!(js_date.get_utc_month(), 12); + assert_eq!(js_date.get_utc_date(), 1); + assert_eq!(js_date.get_utc_hours(), 3); + assert_eq!(js_date.get_utc_minutes(), 1); + assert_eq!(js_date.get_utc_seconds(), 55); + assert_eq!(js_date.get_utc_milliseconds(), 974); } |