Import chrono 0.4.24 upstream upstream/0.4.24
authorWoohyun Jung <wh0705.jung@samsung.com>
Tue, 21 Mar 2023 05:19:12 +0000 (14:19 +0900)
committerWoohyun Jung <wh0705.jung@samsung.com>
Tue, 21 Mar 2023 05:19:12 +0000 (14:19 +0900)
61 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
.git-ignore-revs [new file with mode: 0644]
.github/pull_request_template.md [new file with mode: 0644]
.github/workflows/lint.yml [new file with mode: 0644]
.github/workflows/test.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
AUTHORS.txt [new file with mode: 0644]
CHANGELOG.md [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
appveyor.yml [new file with mode: 0644]
benches/chrono.rs [new file with mode: 0644]
benches/serde.rs [new file with mode: 0644]
clippy.toml [new file with mode: 0644]
deny.toml [new file with mode: 0644]
rustfmt.toml [new file with mode: 0644]
src/date.rs [new file with mode: 0644]
src/datetime/mod.rs [new file with mode: 0644]
src/datetime/rustc_serialize.rs [new file with mode: 0644]
src/datetime/serde.rs [new file with mode: 0644]
src/datetime/tests.rs [new file with mode: 0644]
src/format/locales.rs [new file with mode: 0644]
src/format/mod.rs [new file with mode: 0644]
src/format/parse.rs [new file with mode: 0644]
src/format/parsed.rs [new file with mode: 0644]
src/format/scan.rs [new file with mode: 0644]
src/format/strftime.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/month.rs [new file with mode: 0644]
src/naive/date.rs [new file with mode: 0644]
src/naive/datetime/mod.rs [new file with mode: 0644]
src/naive/datetime/rustc_serialize.rs [new file with mode: 0644]
src/naive/datetime/serde.rs [new file with mode: 0644]
src/naive/datetime/tests.rs [new file with mode: 0644]
src/naive/internals.rs [new file with mode: 0644]
src/naive/isoweek.rs [new file with mode: 0644]
src/naive/mod.rs [new file with mode: 0644]
src/naive/time/mod.rs [new file with mode: 0644]
src/naive/time/rustc_serialize.rs [new file with mode: 0644]
src/naive/time/serde.rs [new file with mode: 0644]
src/naive/time/tests.rs [new file with mode: 0644]
src/offset/fixed.rs [new file with mode: 0644]
src/offset/local/mod.rs [new file with mode: 0644]
src/offset/local/stub.rs [new file with mode: 0644]
src/offset/local/tz_info/mod.rs [new file with mode: 0644]
src/offset/local/tz_info/parser.rs [new file with mode: 0644]
src/offset/local/tz_info/rule.rs [new file with mode: 0644]
src/offset/local/tz_info/timezone.rs [new file with mode: 0644]
src/offset/local/unix.rs [new file with mode: 0644]
src/offset/local/windows.rs [new file with mode: 0644]
src/offset/mod.rs [new file with mode: 0644]
src/offset/utc.rs [new file with mode: 0644]
src/oldtime.rs [new file with mode: 0644]
src/round.rs [new file with mode: 0644]
src/traits.rs [new file with mode: 0644]
src/weekday.rs [new file with mode: 0644]
tests/dateutils.rs [new file with mode: 0644]
tests/wasm.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..b408327
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "daa86a77d36d74f474913fd3b560a40f1424bd77"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.git-ignore-revs b/.git-ignore-revs
new file mode 100644 (file)
index 0000000..5d83f4f
--- /dev/null
@@ -0,0 +1 @@
+febb8dc168325ac471b54591c925c48b6a485962  # cargo fmt
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644 (file)
index 0000000..ba92e74
--- /dev/null
@@ -0,0 +1,3 @@
+### Thanks for contributing to chrono!
+
+Please consider adding a test to ensure your bug fix/feature will not break in the future.
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644 (file)
index 0000000..61bf164
--- /dev/null
@@ -0,0 +1,29 @@
+name: lint
+
+on:
+  push:
+    branches: [main, 0.4.x]
+  pull_request:
+
+jobs:
+  lint:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: Swatinem/rust-cache@v2
+      - run: |
+          cargo fmt --check -- --color=always
+          cargo fmt --check --manifest-path fuzz/Cargo.toml
+      - run: |
+          cargo clippy --color=always -- -D warnings -A clippy::manual-non-exhaustive
+          cargo clippy --manifest-path fuzz/Cargo.toml --color=always -- -D warnings
+        env:
+          RUSTFLAGS: "-Dwarnings"
+
+  cargo-deny:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v3
+    - uses: EmbarkStudios/cargo-deny-action@v1
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644 (file)
index 0000000..8975495
--- /dev/null
@@ -0,0 +1,152 @@
+name: All Tests and Builds
+
+on:
+  push:
+    branches: [main, 0.4.x]
+  pull_request:
+
+jobs:
+  timezones_linux:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo test --all-features --color=always -- --color=always
+
+  timezones_other:
+    strategy:
+      matrix:
+        os: [macos-latest, windows-latest]
+        tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo test --lib --all-features --color=always -- --color=always
+      - run: cargo test --doc --all-features --color=always -- --color=always
+
+  # later this may be able to be included with the below
+  # kept seperate for now as the following don't compile on 1.38.0
+  # * rkyv
+  # * criterion
+  rust_msrv:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: 1.38.0
+      - uses: Swatinem/rust-cache@v2
+      # run --lib and --doc to avoid the long running integration tests which are run elsewhere
+      - run: cargo test --lib --features unstable-locales,wasmbind,oldtime,clock,rustc-serialize,serde,winapi --color=always -- --color=always
+      - run: cargo test --doc --features unstable-locales,wasmbind,oldtime,clock,rustc-serialize,serde,winapi --color=always -- --color=always
+
+  rust_versions:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        rust_version: ["stable", "beta", "nightly"]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{ matrix.rust_version }}
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo check --benches
+      - run: cargo check --manifest-path fuzz/Cargo.toml --all-targets
+      # run --lib and --doc to avoid the long running integration tests which are run elsewhere
+      - run: cargo test --lib --all-features --color=always -- --color=always
+      - run: cargo test --doc --all-features --color=always -- --color=always
+
+  features_check:
+    strategy:
+      matrix:
+        os: [ubuntu-latest, macos-latest, windows-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+      - uses: taiki-e/install-action@cargo-hack
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo hack check --feature-powerset --optional-deps serde,rkyv --skip default --skip __internal_bench --skip __doctest --skip iana-time-zone --skip pure-rust-locales
+
+  no_std:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        target: [thumbv6m-none-eabi, x86_64-fortanix-unknown-sgx]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          targets: ${{ matrix.target }}
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo build --target ${{ matrix.target }} --color=always
+        working-directory: ./ci/core-test
+
+  alternative_targets:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        target: [wasm32-unknown-unknown, wasm32-wasi, wasm32-unknown-emscripten, aarch64-apple-ios, aarch64-linux-android]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          targets: ${{ matrix.target }}
+      - uses: Swatinem/rust-cache@v2
+      - uses: actions/setup-node@v1
+        with:
+          node-version: "12"
+      - run: |
+          export RUST_BACKTRACE=1
+          curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
+          wasm-pack --version
+      - run: cargo build --target ${{ matrix.target }}  --color=always
+
+  features_check_wasm:
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          targets: wasm32-unknown-unknown
+      - uses: taiki-e/install-action@cargo-hack
+      - uses: Swatinem/rust-cache@v2
+      - run: cargo hack check --feature-powerset --optional-deps serde,rkyv --skip default --skip __internal_bench --skip __doctest --skip iana-time-zone --skip pure-rust-locales
+
+  cross-targets:
+    strategy:
+      matrix:
+        target:
+          - x86_64-sun-solaris
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - run: cargo install cross
+      - uses: Swatinem/rust-cache@v2
+      - run: cross check --target ${{ matrix.target }}
+
+  check-docs:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: dtolnay/rust-toolchain@nightly
+      - run: cargo +nightly doc --all-features --no-deps
+        env:
+          RUSTDOCFLAGS: "-D warnings --cfg docsrs"
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f989181
--- /dev/null
@@ -0,0 +1,6 @@
+target
+Cargo.lock
+.tool-versions
+
+# for jetbrains users
+.idea/
diff --git a/AUTHORS.txt b/AUTHORS.txt
new file mode 100644 (file)
index 0000000..caff579
--- /dev/null
@@ -0,0 +1,43 @@
+Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
+and also the following people (in ascending order):
+
+Alex Mikhalev <alexmikhalevalex@gmail.com>
+Alexander Bulaev <alexbool@yandex-team.ru>
+Ashley Mannix <ashleymannix@live.com.au>
+Ben Boeckel <mathstuf@gmail.com>
+Ben Eills <ben@beneills.com>
+Brandon W Maister <bwm@knewton.com>
+Brandon W Maister <quodlibetor@gmail.com>
+Cecile Tonglet <cecile.tonglet@cecton.com>
+Colin Ray <r.colinray@gmail.com>
+Corey Farwell <coreyf@rwell.org>
+Dan <dan@ebip.co.uk>
+Danilo Bargen <mail@dbrgn.ch>
+David Hewson <dev@daveid.co.uk>
+David Ross <daboross@daboross.net>
+David Tolnay <dtolnay@gmail.com>
+David Willie <david.willie.1@gmail.com>
+Eric Findlay <e.findlay@protonmail.ch>
+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>
+John Heitmann <jheitmann@gmail.com>
+John Nagle <nagle@sitetruth.com>
+Jonas mg <jonasmg@yepmail.net>
+János Illés <ijanos@gmail.com>
+Ken Tossell <ken@tossell.net>
+Martin Risell Lilja <martin.risell.lilja@gmail.com>
+Richard Petrie <rap1011@ksu.edu>
+Ryan Lewis <ryansname@gmail.com>
+Sergey V. Shadoy <shadoysv@yandex.ru>
+Sergey V. Galtsev <sergey.v.galtsev@github.com>
+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/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..1e6f6f9
--- /dev/null
@@ -0,0 +1,731 @@
+ChangeLog for Chrono
+====================
+
+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
+
+* Correct build on solaris/illumos
+
+## 0.4.18
+
+* Restore support for x86_64-fortanix-unknown-sgx
+
+## 0.4.17
+
+* Fix a name resolution error in wasm-bindgen code introduced by removing the dependency on time
+  v0.1
+
+## 0.4.16
+
+### Features
+
+* Add %Z specifier to the `FromStr`, similar to the glibc strptime
+  (does not set the offset from the timezone name)
+
+* Drop the dependency on time v0.1, which is deprecated, unless the `oldtime`
+  feature is active. This feature is active by default in v0.4.16 for backwards
+  compatibility, but will likely be removed in v0.5. Code that imports
+  `time::Duration` should be switched to import `chrono::Duration` instead to
+  avoid breakage.
+
+## 0.4.15
+
+### Fixes
+
+* Correct usage of vec in specific feature combinations (@quodlibetor)
+
+## 0.4.14 **YANKED**
+
+### Features
+
+* Add day and week iterators for `NaiveDate` (@gnzlbg & @robyoung)
+* Add a `Month` enum (@hhamana)
+* Add `locales`. All format functions can now use locales, see the documentation for the
+  `unstable-locales` feature.
+* Fix `Local.from_local_datetime` method for wasm
+
+### Improvements
+
+* Added MIN and MAX values for `NaiveTime`, `NaiveDateTime` and `DateTime<Utc>`.
+
+## 0.4.13
+
+### Features
+
+* Add `DurationRound` trait that allows rounding and truncating by `Duration` (@robyoung)
+
+### Internal Improvements
+
+* Code improvements to impl `From` for `js_sys` in wasm to reuse code (@schrieveslaach)
+
+## 0.4.12
+
+### New Methods and impls
+
+* `Duration::abs` to ensure that a duration is just a magnitude (#418 @abreis).
+
+### Compatibility improvements
+
+* impl `From` for `js_sys` in wasm (#424 @schrieveslaach)
+* Bump required version of `time` for redox support.
+
+### Bugfixes
+
+* serde modules do a better job with `Option` types (#417 @mwkroening and #429
+  @fx-kirin)
+* Use js runtime when using wasmbind to get the local offset (#412
+  @quodlibetor)
+
+### Internal Improvements
+
+* Migrate to github actions from travis-ci, make the overall CI experience more comprehensible,
+  significantly faster and more correct (#439 @quodlibetor)
+
+## 0.4.11
+
+### Improvements
+
+* Support a space or `T` in `FromStr` for `DateTime<Tz>`, meaning that e.g.
+  `dt.to_string().parse::<DateTime<Utc>>()` now correctly works on round-trip.
+  (@quodlibetor in #378)
+* Support "negative UTC" in `parse_from_rfc2822` (@quodlibetor #368 reported in
+  #102)
+* Support comparisons of DateTimes with different timezones (@dlalic in #375)
+* Many documentation improvements
+
+### Bitrot and external integration fixes
+
+* Don't use wasmbind on wasi (@coolreader18 #365)
+* Avoid deprecation warnings for `Error::description` (@AnderEnder and
+  @quodlibetor #376)
+
+### Internal improvements
+
+* Use Criterion for benchmarks (@quodlibetor)
+
+## 0.4.10
+
+### Compatibility notes
+
+* Putting some functionality behind an `alloc` feature to improve no-std
+  support (in #341) means that if you were relying on chrono with
+  `no-default-features` *and* using any of the functions that require alloc
+  support (i.e. any of the string-generating functions like `to_rfc3339`) you
+  will need to add the `alloc` feature in your Cargo.toml.
+
+### Improvements
+
+* `DateTime::parse_from_str` is more than 2x faster in some cases. (@michalsrb
+  #358)
+* Significant improvements to no-std and alloc support (This should also make
+  many format/serialization operations induce zero unnecessary allocations)
+  (@CryZe #341)
+
+### Features
+
+* Functions that were accepting `Iterator` of `Item`s (for example
+  `format_with_items`) now accept `Iterator` of `Borrow<Item>`, so one can
+  use values or references. (@michalsrb #358)
+* Add built-in support for structs with nested `Option<Datetime>` etc fields
+  (@manifest #302)
+
+### Internal/doc improvements
+
+* Use markdown footnotes on the `strftime` docs page (@qudlibetor #359)
+* Migrate from `try!` -> `?` (question mark) because it is now emitting
+  deprecation warnings and has been stable since rustc 1.13.0
+* Deny dead code
+
+## 0.4.9
+
+### Fixes
+
+* Make Datetime arithmatic adjust their offsets after discovering their new
+  timestamps (@quodlibetor #337)
+* Put wasm-bindgen related code and dependencies behind a `wasmbind` feature
+  gate. (@quodlibetor #335)
+
+## 0.4.8
+
+### Fixes
+
+* Add '0' to single-digit days in rfc2822 date format (@wyhaya #323)
+* Correctly pad DelayedFormat (@SamokhinIlya #320)
+
+### Features
+
+* Support `wasm-unknown-unknown` via wasm-bindgen (in addition to
+  emscripten/`wasm-unknown-emscripten`). (finished by @evq in #331, initial
+  work by @jjpe #287)
+
+## 0.4.7
+
+### Fixes
+
+* Disable libc default features so that CI continues to work on rust 1.13
+* Fix panic on negative inputs to timestamp_millis (@cmars #292)
+* Make `LocalResult` `Copy/Eq/Hash`
+
+### Features
+
+* Add `std::convert::From` conversions between the different timezone formats
+  (@mqudsi #271)
+* Add `timestamp_nanos` methods (@jean-airoldie #308)
+* Documentation improvements
+
+## 0.4.6
+
+### Maintenance
+
+* Doc improvements -- improve README CI verification, external links
+* winapi upgrade to 0.3
+
+## Unreleased
+
+### Features
+
+* Added `NaiveDate::from_weekday_of_month{,_opt}` for getting eg. the 2nd Friday of March 2017.
+
+## 0.4.5
+
+### Features
+
+* Added several more serde deserialization helpers (@novacrazy #258)
+* Enabled all features on the playground (@davidtwco #267)
+* Derive `Hash` on `FixedOffset` (@LuoZijun #254)
+* Improved docs (@storyfeet #261, @quodlibetor #252)
+
+## 0.4.4
+
+### Features
+
+* Added support for parsing nanoseconds without the leading dot (@emschwartz #251)
+
+## 0.4.3
+
+### Features
+
+* Added methods to DateTime/NaiveDateTime to present the stored value as a number
+  of nanoseconds since the UNIX epoch (@harkonenbade #247)
+* Added a serde serialise/deserialise module for nanosecond timestamps. (@harkonenbade #247)
+* Added "Permissive" timezone parsing which allows a numeric timezone to
+  be specified without minutes. (@quodlibetor #242)
+
+## 0.4.2
+
+### Deprecations
+
+* More strongly deprecate RustcSerialize: remove it from documentation unless
+  the feature is enabled, issue a deprecation warning if the rustc-serialize
+  feature is enabled (@quodlibetor #174)
+
+### Features
+
+* Move all uses of the system clock behind a `clock` feature, for use in
+  environments where we don't have access to the current time. (@jethrogb #236)
+* Implement subtraction of two `Date`s, `Time`s, or `DateTime`s, returning a
+  `Duration` (@tobz1000 #237)
+
+## 0.4.1
+
+### Bug Fixes
+
+* Allow parsing timestamps with subsecond precision (@jonasbb)
+* RFC2822 allows times to not include the second (@upsuper)
+
+### Features
+
+* New `timestamp_millis` method on `DateTime` and `NaiveDateTim` that returns
+  number of milliseconds since the epoch. (@quodlibetor)
+* Support exact decimal width on subsecond display for RFC3339 via a new
+  `to_rfc3339_opts` method on `DateTime` (@dekellum)
+* Use no_std-compatible num dependencies (@cuviper)
+* Add `SubsecRound` trait that allows rounding to the nearest second
+  (@dekellum)
+
+### Code Hygiene and Docs
+
+* Docs! (@alatiera @kosta @quodlibetor @kennytm)
+* Run clippy and various fixes (@quodlibetor)
+
+## 0.4.0 (2017-06-22)
+
+This was originally planned as a minor release but was pushed to a major
+release due to the compatibility concern raised.
+
+### Added
+
+- `IsoWeek` has been added for the ISO week without time zone.
+
+- The `+=` and `-=` operators against `time::Duration` are now supported for
+  `NaiveDate`, `NaiveTime` and `NaiveDateTime`. (#99)
+
+  (Note that this does not invalidate the eventual deprecation of `time::Duration`.)
+
+- `SystemTime` and `DateTime<Tz>` types can be now converted to each other via `From`.
+  Due to the obvious lack of time zone information in `SystemTime`,
+  the forward direction is limited to `DateTime<Utc>` and `DateTime<Local>` only.
+
+### Changed
+
+- Intermediate implementation modules have been flattened (#161),
+  and `UTC` has been renamed to `Utc` in accordance with the current convention (#148).
+
+  The full list of changes is as follows:
+
+  Before                                   | After
+  ---------------------------------------- | ----------------------------
+  `chrono::date::Date`                     | `chrono::Date`
+  `chrono::date::MIN`                      | `chrono::MIN_DATE`
+  `chrono::date::MAX`                      | `chrono::MAX_DATE`
+  `chrono::datetime::DateTime`             | `chrono::DateTime`
+  `chrono::naive::time::NaiveTime`         | `chrono::naive::NaiveTime`
+  `chrono::naive::date::NaiveDate`         | `chrono::naive::NaiveDate`
+  `chrono::naive::date::MIN`               | `chrono::naive::MIN_DATE`
+  `chrono::naive::date::MAX`               | `chrono::naive::MAX_DATE`
+  `chrono::naive::datetime::NaiveDateTime` | `chrono::naive::NaiveDateTime`
+  `chrono::offset::utc::UTC`               | `chrono::offset::Utc`
+  `chrono::offset::fixed::FixedOffset`     | `chrono::offset::FixedOffset`
+  `chrono::offset::local::Local`           | `chrono::offset::Local`
+  `chrono::format::parsed::Parsed`         | `chrono::format::Parsed`
+
+  With an exception of `Utc`, this change does not affect any direct usage of
+  `chrono::*` or `chrono::prelude::*` types.
+
+- `Datelike::isoweekdate` is replaced by `Datelike::iso_week` which only returns the ISO week.
+
+  The original method used to return a tuple of year number, week number and day of the week,
+  but this duplicated the `Datelike::weekday` method and it had been hard to deal with
+  the raw year and week number for the ISO week date.
+  This change isolates any logic and API for the week date into a separate type.
+
+- `NaiveDateTime` and `DateTime` can now be deserialized from an integral UNIX timestamp. (#125)
+
+  This turns out to be very common input for web-related usages.
+  The existing string representation is still supported as well.
+
+- `chrono::serde` and `chrono::naive::serde` modules have been added
+  for the serialization utilities. (#125)
+
+  Currently they contain the `ts_seconds` modules that can be used to
+  serialize `NaiveDateTime` and `DateTime` values into an integral UNIX timestamp.
+  This can be combined with Serde's `[de]serialize_with` attributes
+  to fully support the (de)serialization to/from the timestamp.
+
+  For rustc-serialize, there are separate `chrono::TsSeconds` and `chrono::naive::TsSeconds` types
+  that are newtype wrappers implementing different (de)serialization logics.
+  This is a suboptimal API, however, and it is strongly recommended to migrate to Serde.
+
+### Fixed
+
+- The major version was made to fix the broken Serde dependency issues. (#146, #156, #158, #159)
+
+  The original intention to technically break the dependency was
+  to facilitate the use of Serde 1.0 at the expense of temporary breakage.
+  Whether this was appropriate or not is quite debatable,
+  but it became clear that there are several high-profile crates requiring Serde 0.9
+  and it is not feasible to force them to use Serde 1.0 anyway.
+
+  To the end, the new major release was made with some known lower-priority breaking changes.
+  0.3.1 is now yanked and any remaining 0.3 users can safely roll back to 0.3.0.
+
+- Various documentation fixes and goodies. (#92, #131, #136)
+
+## 0.3.1 (2017-05-02)
+
+### Added
+
+- `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113)
+
+  The syntax is identical to `%A`, i.e. either the shortest or the longest form of English names.
+
+### Changed
+
+- Serde 1.0 is now supported. (#142)
+
+  This is technically a breaking change because Serde 0.9 and 1.0 are not compatible,
+  but this time we decided not to issue a minor version because
+  we have already seen Serde 0.8 and 0.9 compatibility problems even after 0.3.0 and
+  a new minor version turned out to be not very helpful for this kind of issues.
+
+### Fixed
+
+- Fixed a bug that the leap second can be mapped wrongly in the local time zone.
+  Only occurs when the local time zone is behind UTC. (#130)
+
+## 0.3.0 (2017-02-07)
+
+The project has moved to the [Chronotope](https://github.com/chronotope/) organization.
+
+### Added
+
+- `chrono::prelude` module has been added. All other glob imports are now discouraged.
+
+- `FixedOffset` can be added to or subtracted from any timelike types.
+
+    - `FixedOffset::local_minus_utc` and `FixedOffset::utc_minus_local` methods have been added.
+      Note that the old `Offset::local_minus_utc` method is gone; see below.
+
+- Serde support for non-self-describing formats like Bincode is added. (#89)
+
+- Added `Item::Owned{Literal,Space}` variants for owned formatting items. (#76)
+
+- Formatting items and the `Parsed` type have been slightly adjusted so that
+  they can be internally extended without breaking any compatibility.
+
+- `Weekday` is now `Hash`able. (#109)
+
+- `ParseError` now implements `Eq` as well as `PartialEq`. (#114)
+
+- More documentation improvements. (#101, #108, #112)
+
+### Changed
+
+- Chrono now only supports Rust 1.13.0 or later (previously: Rust 1.8.0 or later).
+
+- Serde 0.9 is now supported.
+  Due to the API difference, support for 0.8 or older is discontinued. (#122)
+
+- Rustc-serialize implementations are now on par with corresponding Serde implementations.
+  They both standardize on the `std::fmt::Debug` textual output.
+
+  **This is a silent breaking change (hopefully the last though).**
+  You should be prepared for the format change if you depended on rustc-serialize.
+
+- `Offset::local_minus_utc` is now `Offset::fix`, and returns `FixedOffset` instead of a duration.
+
+  This makes every time zone operation operate within a bias less than one day,
+  and vastly simplifies many logics.
+
+- `chrono::format::format` now receives `FixedOffset` instead of `time::Duration`.
+
+- The following methods and implementations have been renamed and older names have been *removed*.
+  The older names will be reused for the same methods with `std::time::Duration` in the future.
+
+    - `checked_*` â†’ `checked_*_signed` in `Date`, `DateTime`, `NaiveDate` and `NaiveDateTime` types
+
+    - `overflowing_*` â†’ `overflowing_*_signed` in the `NaiveTime` type
+
+    - All subtraction implementations between two time instants have been moved to
+      `signed_duration_since`, following the naming in `std::time`.
+
+### Fixed
+
+- Fixed a panic when the `Local` offset receives a leap second. (#123)
+
+### Removed
+
+- Rustc-serialize support for `Date<Tz>` types and all offset types has been dropped.
+
+  These implementations were automatically derived and never had been in a good shape.
+  Moreover there are no corresponding Serde implementations, limiting their usefulness.
+  In the future they may be revived with more complete implementations.
+
+- The following method aliases deprecated in the 0.2 branch have been removed.
+
+    - `DateTime::num_seconds_from_unix_epoch` (→ `DateTime::timestamp`)
+    - `NaiveDateTime::from_num_seconds_from_unix_epoch` (→ `NaiveDateTime::from_timestamp`)
+    - `NaiveDateTime::from_num_seconds_from_unix_epoch_opt` (→ `NaiveDateTime::from_timestamp_opt`)
+    - `NaiveDateTime::num_seconds_unix_epoch` (→ `NaiveDateTime::timestamp`)
+
+- Formatting items are no longer `Copy`, except for `chrono::format::Pad`.
+
+- `chrono::offset::add_with_leapsecond` has been removed.
+  Use a direct addition with `FixedOffset` instead.
+
+## 0.2.25 (2016-08-04)
+
+This is the last version officially supports Rust 1.12.0 or older.
+
+(0.2.24 was accidentally uploaded without a proper check for warnings in the default state,
+and replaced by 0.2.25 very shortly. Duh.)
+
+### Added
+
+- Serde 0.8 is now supported. 0.7 also remains supported. (#86)
+
+### Fixed
+
+- The deserialization implementation for rustc-serialize now properly verifies the input.
+  All serialization codes are also now thoroughly tested. (#42)
+
+## 0.2.23 (2016-08-03)
+
+### Added
+
+- The documentation was greatly improved for several types,
+  and tons of cross-references have been added. (#77, #78, #80, #82)
+
+- `DateTime::timestamp_subsec_{millis,micros,nanos}` methods have been added. (#81)
+
+### Fixed
+
+- When the system time records a leap second,
+  the nanosecond component was mistakenly reset to zero. (#84)
+
+- `Local` offset misbehaves in Windows for August and later,
+  due to the long-standing libtime bug (dates back to mid-2015).
+  Workaround has been implemented. (#85)
+
+## 0.2.22 (2016-04-22)
+
+### Fixed
+
+- `%.6f` and `%.9f` used to print only three digits when the nanosecond part is zero. (#71)
+- The documentation for `%+` has been updated to reflect the current status. (#71)
+
+## 0.2.21 (2016-03-29)
+
+### Fixed
+
+- `Fixed::LongWeekdayName` was unable to recognize `"sunday"` (whoops). (#66)
+
+## 0.2.20 (2016-03-06)
+
+### Changed
+
+- `serde` dependency has been updated to 0.7. (#63, #64)
+
+## 0.2.19 (2016-02-05)
+
+### Added
+
+- The documentation for `Date` is made clear about its ambiguity and guarantees.
+
+### Fixed
+
+- `DateTime::date` had been wrong when the local date and the UTC date is in disagreement. (#61)
+
+## 0.2.18 (2016-01-23)
+
+### Fixed
+
+- Chrono no longer pulls a superfluous `rand` dependency. (#57)
+
+## 0.2.17 (2015-11-22)
+
+### Added
+
+- Naive date and time types and `DateTime` now have a `serde` support.
+  They serialize as an ISO 8601 / RFC 3339 string just like `Debug`. (#51)
+
+## 0.2.16 (2015-09-06)
+
+### Added
+
+- Added `%.3f`, `%.6f` and `%.9f` specifier for formatting fractional seconds
+  up to 3, 6 or 9 decimal digits. This is a natural extension to the existing `%f`.
+  Note that this is (not yet) generic, no other value of precision is supported. (#45)
+
+### Changed
+
+- Forbade unsized types from implementing `Datelike` and `Timelike`.
+  This does not make a big harm as any type implementing them should be already sized
+  to be practical, but this change still can break highly generic codes. (#46)
+
+### Fixed
+
+- Fixed a broken link in the `README.md`. (#41)
+
+## 0.2.15 (2015-07-05)
+
+### Added
+
+- Padding modifiers `%_?`, `%-?` and `%0?` are implemented.
+  They are glibc extensions which seem to be reasonably widespread (e.g. Ruby).
+
+- Added `%:z` specifier and corresponding formatting items
+  which is essentially the same as `%z` but with a colon.
+
+- Added a new specifier `%.f` which precision adapts from the input.
+  This was added as a response to the UX problems in the original nanosecond specifier `%f`.
+
+### Fixed
+
+- `Numeric::Timestamp` specifier (`%s`) was ignoring the time zone offset when provided.
+
+- Improved the documentation and associated tests for `strftime`.
+
+## 0.2.14 (2015-05-15)
+
+### Fixed
+
+- `NaiveDateTime +/- Duration` or `NaiveTime +/- Duration` could have gone wrong
+  when the `Duration` to be added is negative and has a fractional second part.
+  This was caused by an underflow in the conversion from `Duration` to the parts;
+  the lack of tests for this case allowed a bug. (#37)
+
+## 0.2.13 (2015-04-29)
+
+### Added
+
+- The optional dependency on `rustc_serialize` and
+  relevant `Rustc{En,De}codable` implementations for supported types has been added.
+  This is enabled by the `rustc-serialize` Cargo feature. (#34)
+
+### Changed
+
+- `chrono::Duration` reexport is changed to that of crates.io `time` crate.
+  This enables Rust 1.0 beta compatibility.
+
+## 0.2.4 (2015-03-03)
+
+### Fixed
+
+- Clarified the meaning of `Date<Tz>` and fixed unwanted conversion problem
+  that only occurs with positive UTC offsets. (#27)
+
+## 0.2.3 (2015-02-27)
+
+### Added
+
+- `DateTime<Tz>` and `Date<Tz>` is now `Copy`/`Send` when `Tz::Offset` is `Copy`/`Send`.
+  The implementations for them were mistakenly omitted. (#25)
+
+### Fixed
+
+- `Local::from_utc_datetime` didn't set a correct offset. (#26)
+
+## 0.2.1 (2015-02-21)
+
+### Changed
+
+- `DelayedFormat` no longer conveys a redundant lifetime.
+
+## 0.2.0 (2015-02-19)
+
+### Added
+
+- `Offset` is splitted into `TimeZone` (constructor) and `Offset` (storage) types.
+  You would normally see only the former, as the latter is mostly an implementation detail.
+  Most importantly, `Local` now can be used to directly construct timezone-aware values.
+
+  Some types (currently, `UTC` and `FixedOffset`) are both `TimeZone` and `Offset`,
+  but others aren't (e.g. `Local` is not what is being stored to each `DateTime` values).
+
+- `LocalResult::map` convenience method has been added.
+
+- `TimeZone` now allows a construction of `DateTime` values from UNIX timestamp,
+  via `timestamp` and `timestamp_opt` methods.
+
+- `TimeZone` now also has a method for parsing `DateTime`, namely `datetime_from_str`.
+
+- The following methods have been added to all date and time types:
+
+    - `checked_add`
+    - `checked_sub`
+    - `format_with_items`
+
+- The following methods have been added to all timezone-aware types:
+
+    - `timezone`
+    - `with_timezone`
+    - `naive_utc`
+    - `naive_local`
+
+- `parse_from_str` method has been added to all naive types and `DateTime<FixedOffset>`.
+
+- All naive types and instances of `DateTime` with time zones `UTC`, `Local` and `FixedOffset`
+  implement the `FromStr` trait. They parse what `std::fmt::Debug` would print.
+
+- `chrono::format` has been greatly rewritten.
+
+    - The formatting syntax parser is modular now, available at `chrono::format::strftime`.
+
+    - The parser and resolution algorithm is also modular, the former is available at
+      `chrono::format::parse` while the latter is available at `chrono::format::parsed`.
+
+    - Explicit support for RFC 2822 and 3339 syntaxes is landed.
+
+    - There is a minor formatting difference with atypical values,
+      e.g. for years not between 1 BCE and 9999 CE.
+
+### Changed
+
+- Most uses of `Offset` are converted to `TimeZone`.
+  In fact, *all* user-facing code is expected to be `Offset`-free.
+
+- `[Naive]DateTime::*num_seconds_from_unix_epoch*` methods have been renamed to
+  simply `timestamp` or `from_timestamp*`. The original names have been deprecated.
+
+### Removed
+
+- `Time` has been removed. This also prompts a related set of methods in `TimeZone`.
+
+  This is in principle possible, but in practice has seen a little use
+  because it can only be meaningfully constructed via an existing `DateTime` value.
+  This made many operations to `Time` unintuitive or ambiguous,
+  so we simply let it go.
+
+  In the case that `Time` is really required, one can use a simpler `NaiveTime`.
+  `NaiveTime` and `NaiveDate` can be freely combined and splitted,
+  and `TimeZone::from_{local,utc}_datetime` can be used to convert from/to the local time.
+
+- `with_offset` method has been removed. Use `with_timezone` method instead.
+  (This is not deprecated since it is an integral part of offset reform.)
+
+## 0.1.14 (2015-01-10)
+
+### Added
+
+- Added a missing `std::fmt::String` impl for `Local`.
+
+## 0.1.13 (2015-01-10)
+
+### Changed
+
+- Most types now implement both `std::fmt::Show` and `std::fmt::String`,
+  with the former used for the stricter output and the latter used for more casual output.
+
+### Removed
+
+- `Offset::name` has been replaced by a `std::fmt::String` implementation to `Offset`.
+
+## 0.1.12 (2015-01-08)
+
+### Removed
+
+- `Duration + T` no longer works due to the updated impl reachability rules.
+  Use `T + Duration` as a workaround.
+
+## 0.1.4 (2014-12-13)
+
+### Fixed
+
+- Fixed a bug that `Date::and_*` methods with an offset that can change the date are
+  off by one day.
+
+## 0.1.3 (2014-11-28)
+
+### Added
+
+- `{Date,Time,DateTime}::with_offset` methods have been added.
+
+- `LocalResult` now implements a common set of traits.
+
+- `LocalResult::and_*` methods have been added.
+  They are useful for safely chaining `LocalResult<Date<Off>>` methods
+  to make `LocalResult<DateTime<Off>>`.
+
+### Changed
+
+- `Offset::name` now returns `SendStr`.
+
+- `{Date,Time} - Duration` overloadings are now allowed.
+
+## 0.1.2 (2014-11-24)
+
+### Added
+
+- `Duration + Date` overloading is now allowed.
+
+### Changed
+
+- Chrono no longer needs `num` dependency.
+
+## 0.1.0 (2014-11-20)
+
+The initial version that was available to `crates.io`.
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..eaf0e69
--- /dev/null
@@ -0,0 +1,162 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "chrono"
+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",
+]
+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"]
+
+[lib]
+name = "chrono"
+
+[[bench]]
+name = "chrono"
+harness = false
+required-features = ["__internal_bench"]
+
+[[bench]]
+name = "serde"
+harness = false
+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]
+version = "0.1.36"
+default-features = false
+
+[dependencies.num-traits]
+version = "0.2"
+default-features = false
+
+[dependencies.pure-rust-locales]
+version = "0.5.2"
+optional = true
+
+[dependencies.rkyv]
+version = "0.7"
+optional = true
+
+[dependencies.rustc-serialize]
+version = "0.3.20"
+optional = true
+
+[dependencies.serde]
+version = "1.0.99"
+optional = true
+default-features = false
+
+[dependencies.time]
+version = "0.1.43"
+optional = true
+
+[dev-dependencies.bincode]
+version = "1.3.0"
+
+[dev-dependencies.doc-comment]
+version = "0.3"
+
+[dev-dependencies.num-iter]
+version = "0.1.35"
+default-features = false
+
+[dev-dependencies.serde_derive]
+version = "1"
+default-features = false
+
+[dev-dependencies.serde_json]
+version = "1"
+
+[features]
+__doctest = []
+__internal_bench = ["criterion"]
+alloc = []
+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",
+]
+
+[target."cfg(all(target_arch = \"wasm32\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))".dependencies.js-sys]
+version = "0.3"
+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",
+]
+optional = true
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..e69847a
--- /dev/null
@@ -0,0 +1,77 @@
+[package]
+name = "chrono"
+version = "0.4.24"
+description = "Date and time library for Rust"
+homepage = "https://github.com/chronotope/chrono"
+documentation = "https://docs.rs/chrono/"
+repository = "https://github.com/chronotope/chrono"
+keywords = ["date", "time", "calendar"]
+categories = ["date-and-time"]
+readme = "README.md"
+license = "MIT/Apache-2.0"
+exclude = ["/ci/*"]
+edition = "2018"
+
+[lib]
+name = "chrono"
+
+[features]
+default = ["clock", "std", "oldtime", "wasmbind"]
+alloc = []
+libc = []
+std = []
+clock = ["std", "winapi", "iana-time-zone"]
+oldtime = ["time"]
+wasmbind = ["wasm-bindgen", "js-sys"]
+unstable-locales = ["pure-rust-locales", "alloc"]
+__internal_bench = ["criterion"]
+__doctest = []
+
+[dependencies]
+time = { version = "0.1.43", optional = true }
+num-integer = { version = "0.1.36", default-features = false }
+num-traits = { version = "0.2", default-features = false }
+rustc-serialize = { version = "0.3.20", optional = true }
+serde = { version = "1.0.99", default-features = false, optional = true }
+pure-rust-locales = { version = "0.5.2", optional = true }
+criterion = { version = "0.4.0", optional = true }
+rkyv = {version = "0.7", optional = true}
+arbitrary = { version = "1.0.0", features = ["derive"], optional = true }
+
+[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies]
+wasm-bindgen = { version = "0.2", optional = true }
+js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
+
+
+[target.'cfg(windows)'.dependencies]
+winapi = { version = "0.3.0", features = ["std", "minwinbase", "minwindef", "timezoneapi"], optional = true }
+
+[target.'cfg(unix)'.dependencies]
+iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }
+
+[dev-dependencies]
+serde_json = { version = "1" }
+serde_derive = { version = "1", default-features = false }
+bincode = { version = "1.3.0" }
+num-iter = { version = "0.1.35", default-features = false }
+doc-comment = { version = "0.3" }
+
+[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
+wasm-bindgen-test = "0.3"
+
+[package.metadata.docs.rs]
+features = ["serde"]
+rustdoc-args = ["--cfg", "docsrs"]
+
+[package.metadata.playground]
+features = ["serde"]
+
+[[bench]]
+name = "chrono"
+required-features = ["__internal_bench"]
+harness = false
+
+[[bench]]
+name = "serde"
+required-features = ["__internal_bench", "serde"]
+harness = false
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..924ff57
--- /dev/null
@@ -0,0 +1,240 @@
+Rust-chrono is dual-licensed under The MIT License [1] and
+Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and
+contributors.
+
+Nota Bene: This is same as the Rust Project's own license.
+
+
+[1]: <http://opensource.org/licenses/MIT>, which is reproduced below:
+
+~~~~
+The MIT License (MIT)
+
+Copyright (c) 2014, Kang Seonghoon.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+~~~~
+
+
+[2]: <http://www.apache.org/licenses/LICENSE-2.0>, which is reproduced below:
+
+~~~~
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+~~~~
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..63aef15
--- /dev/null
+++ b/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/README.md b/README.md
new file mode 100644 (file)
index 0000000..ad95918
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+[Chrono][docsrs]: Date and Time for Rust
+========================================
+
+[![Chrono GitHub Actions][gh-image]][gh-checks]
+[![Chrono on crates.io][cratesio-image]][cratesio]
+[![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/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
+[docsrs-image]: https://docs.rs/chrono/badge.svg
+[docsrs]: https://docs.rs/chrono
+[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
+[gitter]: https://gitter.im/chrono-rs/chrono
+
+It aims to be a feature-complete superset of
+the [time](https://github.com/rust-lang-deprecated/time) library.
+In particular,
+
+* Chrono strictly adheres to ISO 8601.
+* Chrono is timezone-aware by default, with separate timezone-naive types.
+* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
+
+There were several previous attempts to bring a good date and time library to Rust,
+which Chrono builds upon and should acknowledge:
+
+* [Initial research on
+   the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
+* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
+* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
+
+## Limitations
+
+Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
+Be very careful if you really have to deal with pre-20C dates, they can be in Julian or others.
+
+Date types are limited in about +/- 262,000 years from the common epoch.
+Time types are limited in the nanosecond accuracy.
+
+[Leap seconds are supported in the representation but
+Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4/chrono/naive/struct.NaiveTime.html#leap-second-handling).
+(The main reason is that leap seconds are not really predictable.)
+Almost *every* operation over the possible leap seconds will ignore them.
+Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
+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_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)
+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/appveyor.yml b/appveyor.yml
new file mode 100644 (file)
index 0000000..6057e85
--- /dev/null
@@ -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/benches/chrono.rs b/benches/chrono.rs
new file mode 100644 (file)
index 0000000..246271b
--- /dev/null
@@ -0,0 +1,137 @@
+//! Benchmarks for chrono that just depend on std
+#![cfg(feature = "__internal_bench")]
+
+use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
+
+use chrono::prelude::*;
+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| {
+        b.iter(|| {
+            let str = black_box("Wed, 18 Feb 2015 23:16:09 +0000");
+            DateTime::parse_from_rfc2822(str).unwrap()
+        })
+    });
+}
+
+fn bench_datetime_parse_from_rfc3339(c: &mut Criterion) {
+    c.bench_function("bench_datetime_parse_from_rfc3339", |b| {
+        b.iter(|| {
+            let str = black_box("2015-02-18T23:59:60.234567+05:00");
+            DateTime::parse_from_rfc3339(str).unwrap()
+        })
+    });
+}
+
+fn bench_datetime_from_str(c: &mut Criterion) {
+    c.bench_function("bench_datetime_from_str", |b| {
+        b.iter(|| {
+            use std::str::FromStr;
+            let str = black_box("2019-03-30T18:46:57.193Z");
+            DateTime::<Utc>::from_str(str).unwrap()
+        })
+    });
+}
+
+fn bench_datetime_to_rfc2822(c: &mut Criterion) {
+    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_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()));
+}
+
+fn bench_year_flags_from_year(c: &mut Criterion) {
+    c.bench_function("bench_year_flags_from_year", |b| {
+        b.iter(|| {
+            for year in -999i32..1000 {
+                __BenchYearFlags::from_year(year);
+            }
+        })
+    });
+}
+
+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
+/// 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_alt<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)
+}
+
+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_opt(*year, 1, 1).unwrap();
+        group.bench_with_input(BenchmarkId::new("new", year), &d, |b, y| {
+            b.iter(|| num_days_from_ce_alt(y))
+        });
+        group.bench_with_input(BenchmarkId::new("classic", year), &d, |b, y| {
+            b.iter(|| y.num_days_from_ce())
+        });
+    }
+}
+
+criterion_group!(
+    benches,
+    bench_datetime_parse_from_rfc2822,
+    bench_datetime_parse_from_rfc3339,
+    bench_datetime_from_str,
+    bench_datetime_to_rfc2822,
+    bench_datetime_to_rfc3339,
+    bench_year_flags_from_year,
+    bench_num_days_from_ce,
+    bench_get_local_time,
+);
+
+criterion_main!(benches);
diff --git a/benches/serde.rs b/benches/serde.rs
new file mode 100644 (file)
index 0000000..e9de440
--- /dev/null
@@ -0,0 +1,29 @@
+#![cfg(feature = "__internal_bench")]
+
+use criterion::{black_box, criterion_group, criterion_main, Criterion};
+
+use chrono::NaiveDateTime;
+
+fn bench_ser_naivedatetime_string(c: &mut Criterion) {
+    c.bench_function("bench_ser_naivedatetime_string", |b| {
+        let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
+        b.iter(|| {
+            black_box(serde_json::to_string(&dt)).unwrap();
+        });
+    });
+}
+
+fn bench_ser_naivedatetime_writer(c: &mut Criterion) {
+    c.bench_function("bench_ser_naivedatetime_writer", |b| {
+        let mut s: Vec<u8> = Vec::with_capacity(20);
+        let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
+        b.iter(|| {
+            let s = &mut s;
+            s.clear();
+            black_box(serde_json::to_writer(s, &dt)).unwrap();
+        });
+    });
+}
+
+criterion_group!(benches, bench_ser_naivedatetime_writer, bench_ser_naivedatetime_string);
+criterion_main!(benches);
diff --git a/clippy.toml b/clippy.toml
new file mode 100644 (file)
index 0000000..749c3b5
--- /dev/null
@@ -0,0 +1 @@
+msrv = "1.38"
diff --git a/deny.toml b/deny.toml
new file mode 100644 (file)
index 0000000..13b9fac
--- /dev/null
+++ b/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/rustfmt.toml b/rustfmt.toml
new file mode 100644 (file)
index 0000000..2a35f02
--- /dev/null
@@ -0,0 +1 @@
+use_small_heuristics = "Max"
diff --git a/src/date.rs b/src/date.rs
new file mode 100644 (file)
index 0000000..bad4bfb
--- /dev/null
@@ -0,0 +1,645 @@
+// This is a part of Chrono.
+// 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, AddAssign, Sub, SubAssign};
+use core::{fmt, hash};
+
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+#[cfg(feature = "unstable-locales")]
+use crate::format::Locale;
+#[cfg(any(feature = "alloc", feature = "std", test))]
+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.
+///
+/// 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,
+///   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`](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_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`.
+#[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`.
+#[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.
+    /// The local date should be constructed via the `TimeZone` trait.
+    //
+    // 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, offset }
+    }
+
+    /// Makes a new `DateTime` from the current date and given `NaiveTime`.
+    /// The offset in the current date is preserved.
+    ///
+    /// Panics on invalid datetime.
+    #[inline]
+    pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
+        let localdt = self.naive_local().and_time(time);
+        self.timezone().from_local_datetime(&localdt).single()
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute and second.
+    /// 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")
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute and second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Returns `None` on invalid hour, minute and/or second.
+    #[inline]
+    pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
+        NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
+    /// The millisecond part can exceed 1,000 in order to represent the leap second.
+    /// 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")
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
+    /// The millisecond part can exceed 1,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or millisecond.
+    #[inline]
+    pub fn and_hms_milli_opt(
+        &self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        milli: u32,
+    ) -> Option<DateTime<Tz>> {
+        NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
+    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
+    /// 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")
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
+    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or microsecond.
+    #[inline]
+    pub fn and_hms_micro_opt(
+        &self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        micro: u32,
+    ) -> Option<DateTime<Tz>> {
+        NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
+    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
+    /// 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")
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
+    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or nanosecond.
+    #[inline]
+    pub fn and_hms_nano_opt(
+        &self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        nano: u32,
+    ) -> Option<DateTime<Tz>> {
+        NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
+    }
+
+    /// 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")
+    }
+
+    /// Makes a new `Date` for the next date.
+    ///
+    /// Returns `None` when `self` is the last representable date.
+    #[inline]
+    pub fn succ_opt(&self) -> Option<Date<Tz>> {
+        self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
+    }
+
+    /// 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")
+    }
+
+    /// Makes a new `Date` for the prior date.
+    ///
+    /// Returns `None` when `self` is the first representable date.
+    #[inline]
+    pub fn pred_opt(&self) -> Option<Date<Tz>> {
+        self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
+    }
+
+    /// 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 `Date` (but will change the string representation).
+    #[inline]
+    pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
+        tz.from_utc_date(&self.date)
+    }
+
+    /// Adds given `Duration` to the current date.
+    ///
+    /// Returns `None` when it will result in overflow.
+    #[inline]
+    pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
+        let date = self.date.checked_add_signed(rhs)?;
+        Some(Date { date, offset: self.offset })
+    }
+
+    /// Subtracts given `Duration` from the current date.
+    ///
+    /// Returns `None` when it will result in overflow.
+    #[inline]
+    pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
+        let date = self.date.checked_sub_signed(rhs)?;
+        Some(Date { date, offset: self.offset })
+    }
+
+    /// Subtracts another `Date` from the current date.
+    /// Returns a `Duration` of integral numbers.
+    ///
+    /// This does not overflow or underflow at all,
+    /// as all possible output fits in the range of `Duration`.
+    #[inline]
+    pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
+        self.date.signed_duration_since(rhs.date)
+    }
+
+    /// Returns a view to the naive UTC date.
+    #[inline]
+    pub fn naive_utc(&self) -> NaiveDate {
+        self.date
+    }
+
+    /// Returns a view to the naive local date.
+    ///
+    /// This is technically the same as [`naive_utc`](#method.naive_utc)
+    /// because the offset is restricted to never exceed one day,
+    /// but provided for the consistency.
+    #[inline]
+    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.
+fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>>
+where
+    F: FnMut(NaiveDate) -> Option<NaiveDate>,
+{
+    f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single())
+}
+
+impl<Tz: TimeZone> Date<Tz>
+where
+    Tz::Offset: fmt::Display,
+{
+    /// 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
+        I: Iterator<Item = B> + Clone,
+        B: Borrow<Item<'a>>,
+    {
+        DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
+    }
+
+    /// Formats the date with the specified format string.
+    /// 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))
+    }
+
+    /// 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_offset_and_locale(
+            Some(self.naive_local()),
+            None,
+            &self.offset,
+            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)
+    }
+}
+
+impl<Tz: TimeZone> Datelike for Date<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<Date<Tz>> {
+        map_local(self, |date| date.with_year(year))
+    }
+
+    #[inline]
+    fn with_month(&self, month: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_month(month))
+    }
+
+    #[inline]
+    fn with_month0(&self, month0: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_month0(month0))
+    }
+
+    #[inline]
+    fn with_day(&self, day: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_day(day))
+    }
+
+    #[inline]
+    fn with_day0(&self, day0: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_day0(day0))
+    }
+
+    #[inline]
+    fn with_ordinal(&self, ordinal: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_ordinal(ordinal))
+    }
+
+    #[inline]
+    fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Tz>> {
+        map_local(self, |date| date.with_ordinal0(ordinal0))
+    }
+}
+
+// we need them as automatic impls cannot handle associated types
+impl<Tz: TimeZone> Copy for Date<Tz> where <Tz as TimeZone>::Offset: Copy {}
+unsafe impl<Tz: TimeZone> Send for Date<Tz> where <Tz as TimeZone>::Offset: Send {}
+
+impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<Date<Tz2>> for Date<Tz> {
+    fn eq(&self, other: &Date<Tz2>) -> bool {
+        self.date == other.date
+    }
+}
+
+impl<Tz: TimeZone> Eq for Date<Tz> {}
+
+impl<Tz: TimeZone> PartialOrd for Date<Tz> {
+    fn partial_cmp(&self, other: &Date<Tz>) -> Option<Ordering> {
+        self.date.partial_cmp(&other.date)
+    }
+}
+
+impl<Tz: TimeZone> Ord for Date<Tz> {
+    fn cmp(&self, other: &Date<Tz>) -> Ordering {
+        self.date.cmp(&other.date)
+    }
+}
+
+impl<Tz: TimeZone> hash::Hash for Date<Tz> {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        self.date.hash(state)
+    }
+}
+
+impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
+    type Output = Date<Tz>;
+
+    #[inline]
+    fn add(self, rhs: OldDuration) -> Date<Tz> {
+        self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
+    }
+}
+
+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>;
+
+    #[inline]
+    fn sub(self, rhs: OldDuration) -> Date<Tz> {
+        self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
+    }
+}
+
+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;
+
+    #[inline]
+    fn sub(self, rhs: Date<Tz>) -> OldDuration {
+        self.signed_duration_since(rhs)
+    }
+}
+
+impl<Tz: TimeZone> fmt::Debug for Date<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 Date<Tz>
+where
+    Tz::Offset: fmt::Display,
+{
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        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/src/datetime/mod.rs b/src/datetime/mod.rs
new file mode 100644 (file)
index 0000000..38416cb
--- /dev/null
@@ -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/src/datetime/rustc_serialize.rs b/src/datetime/rustc_serialize.rs
new file mode 100644 (file)
index 0000000..18a67d6
--- /dev/null
@@ -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/src/datetime/serde.rs b/src/datetime/serde.rs
new file mode 100644 (file)
index 0000000..ab0126e
--- /dev/null
@@ -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/src/datetime/tests.rs b/src/datetime/tests.rs
new file mode 100644 (file)
index 0000000..ebd32ca
--- /dev/null
@@ -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/src/format/locales.rs b/src/format/locales.rs
new file mode 100644 (file)
index 0000000..f7b4bbd
--- /dev/null
@@ -0,0 +1,33 @@
+use pure_rust_locales::{locale_match, Locale};
+
+pub(crate) fn short_months(locale: Locale) -> &'static [&'static str] {
+    locale_match!(locale => LC_TIME::ABMON)
+}
+
+pub(crate) fn long_months(locale: Locale) -> &'static [&'static str] {
+    locale_match!(locale => LC_TIME::MON)
+}
+
+pub(crate) fn short_weekdays(locale: Locale) -> &'static [&'static str] {
+    locale_match!(locale => LC_TIME::ABDAY)
+}
+
+pub(crate) fn long_weekdays(locale: Locale) -> &'static [&'static str] {
+    locale_match!(locale => LC_TIME::DAY)
+}
+
+pub(crate) fn am_pm(locale: Locale) -> &'static [&'static str] {
+    locale_match!(locale => LC_TIME::AM_PM)
+}
+
+pub(crate) fn d_fmt(locale: Locale) -> &'static str {
+    locale_match!(locale => LC_TIME::D_FMT)
+}
+
+pub(crate) fn d_t_fmt(locale: Locale) -> &'static str {
+    locale_match!(locale => LC_TIME::D_T_FMT)
+}
+
+pub(crate) fn t_fmt(locale: Locale) -> &'static str {
+    locale_match!(locale => LC_TIME::T_FMT)
+}
diff --git a/src/format/mod.rs b/src/format/mod.rs
new file mode 100644 (file)
index 0000000..c05ba4d
--- /dev/null
@@ -0,0 +1,1079 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! Formatting (and parsing) utilities for date and time.
+//!
+//! This module provides the common types and routines to implement,
+//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or
+//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods.
+//! For most cases you should use these high-level interfaces.
+//!
+//! Internally the formatting and parsing shares the same abstract **formatting items**,
+//! 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 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>(())
+//! ```
+
+#[cfg(feature = "alloc")]
+extern crate alloc;
+
+#[cfg(feature = "alloc")]
+use alloc::boxed::Box;
+#[cfg(feature = "alloc")]
+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 crate::naive::{NaiveDate, NaiveTime};
+#[cfg(any(feature = "alloc", feature = "std", test))]
+use crate::offset::{FixedOffset, Offset};
+#[cfg(any(feature = "alloc", feature = "std", test))]
+use crate::{Datelike, Timelike};
+use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday};
+
+#[cfg(feature = "unstable-locales")]
+pub(crate) mod locales;
+
+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, Hash)]
+enum Void {}
+
+/// Padding characters for numeric items.
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum Pad {
+    /// No padding.
+    None,
+    /// Zero (`0`) padding.
+    Zero,
+    /// Space padding.
+    Space,
+}
+
+/// Numeric item types.
+/// They have associated formatting width (FW) and parsing width (PW).
+///
+/// The **formatting width** is the minimal width to be formatted.
+/// If the number is too short, and the padding is not [`Pad::None`](./enum.Pad.html#variant.None),
+/// then it is left-padded.
+/// If the number is too long or (in some cases) negative, it is printed as is.
+///
+/// The **parsing width** is the maximal width to be scanned.
+/// The parser only tries to consume from one to given number of digits (greedily).
+/// 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, Hash)]
+pub enum Numeric {
+    /// Full Gregorian year (FW=4, PW=∞).
+    /// 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,
+    /// Gregorian year modulo 100 (FW=PW=2). Cannot be negative.
+    YearMod100,
+    /// Year in the ISO week date (FW=4, PW=∞).
+    /// May accept years before 1 BCE or after 9999 CE, given an initial sign.
+    IsoYear,
+    /// Year in the ISO week date, divided by 100 (FW=PW=2). Implies the non-negative year.
+    IsoYearDiv100,
+    /// Year in the ISO week date, modulo 100 (FW=PW=2). Cannot be negative.
+    IsoYearMod100,
+    /// Month (FW=PW=2).
+    Month,
+    /// Day of the month (FW=PW=2).
+    Day,
+    /// Week number, where the week 1 starts at the first Sunday of January (FW=PW=2).
+    WeekFromSun,
+    /// Week number, where the week 1 starts at the first Monday of January (FW=PW=2).
+    WeekFromMon,
+    /// Week number in the ISO week date (FW=PW=2).
+    IsoWeek,
+    /// Day of the week, where Sunday = 0 and Saturday = 6 (FW=PW=1).
+    NumDaysFromSun,
+    /// Day of the week, where Monday = 1 and Sunday = 7 (FW=PW=1).
+    WeekdayFromMon,
+    /// Day of the year (FW=PW=3).
+    Ordinal,
+    /// Hour number in the 24-hour clocks (FW=PW=2).
+    Hour,
+    /// Hour number in the 12-hour clocks (FW=PW=2).
+    Hour12,
+    /// The number of minutes since the last whole hour (FW=PW=2).
+    Minute,
+    /// The number of seconds since the last whole minute (FW=PW=2).
+    Second,
+    /// The number of nanoseconds since the last whole second (FW=PW=9).
+    /// Note that this is *not* left-aligned;
+    /// see also [`Fixed::Nanosecond`](./enum.Fixed.html#variant.Nanosecond).
+    Nanosecond,
+    /// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
+    /// For formatting, it assumes UTC upon the absence of time zone offset.
+    Timestamp,
+
+    /// Internal uses only.
+    ///
+    /// This item exists so that one can add additional internal-only formatting
+    /// without breaking major compatibility (as enum variants cannot be selectively private).
+    Internal(InternalNumeric),
+}
+
+/// An opaque type representing numeric item types for internal uses only.
+#[derive(Clone, Eq, Hash, PartialEq)]
+pub struct InternalNumeric {
+    _dummy: Void,
+}
+
+impl fmt::Debug for InternalNumeric {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "<InternalNumeric>")
+    }
+}
+
+/// Fixed-format item types.
+///
+/// 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, Hash)]
+pub enum Fixed {
+    /// Abbreviated month names.
+    ///
+    /// Prints a three-letter-long name in the title case, reads the same name in any case.
+    ShortMonthName,
+    /// Full month names.
+    ///
+    /// Prints a full name in the title case, reads either a short or full name in any case.
+    LongMonthName,
+    /// Abbreviated day of the week names.
+    ///
+    /// Prints a three-letter-long name in the title case, reads the same name in any case.
+    ShortWeekdayName,
+    /// Full day of the week names.
+    ///
+    /// Prints a full name in the title case, reads either a short or full name in any case.
+    LongWeekdayName,
+    /// AM/PM.
+    ///
+    /// Prints in lower case, reads in any case.
+    LowerAmPm,
+    /// AM/PM.
+    ///
+    /// Prints in upper case, reads in any case.
+    UpperAmPm,
+    /// An optional dot plus one or more digits for left-aligned nanoseconds.
+    /// May print nothing, 3, 6 or 9 digits according to the available accuracy.
+    /// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond).
+    Nanosecond,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
+    Nanosecond3,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
+    Nanosecond6,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
+    Nanosecond9,
+    /// Timezone name.
+    ///
+    /// It does not support parsing, its use in the parser is an immediate failure.
+    TimezoneName,
+    /// Offset from the local time to UTC (`+09:00` or `-04:00` or `+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` 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,
+    /// and `Z` can be either in upper case or in lower case.
+    /// The offset is limited from `-24:00` to `+24:00`,
+    /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
+    TimezoneOffsetColonZ,
+    /// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
+    /// Parsing allows an optional colon.
+    TimezoneOffset,
+    /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
+    /// Parsing allows an optional colon.
+    TimezoneOffsetZ,
+    /// RFC 2822 date and time syntax. Commonly used for email and MIME date and time.
+    RFC2822,
+    /// RFC 3339 & ISO 8601 date and time syntax.
+    RFC3339,
+
+    /// Internal uses only.
+    ///
+    /// This item exists so that one can add additional internal-only formatting
+    /// without breaking major compatibility (as enum variants cannot be selectively private).
+    Internal(InternalFixed),
+}
+
+/// An opaque type representing fixed-format item types for internal uses only.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct InternalFixed {
+    val: InternalInternal,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+enum InternalInternal {
+    /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but
+    /// allows missing minutes (per [ISO 8601][iso8601]).
+    ///
+    /// # Panics
+    ///
+    /// If you try to use this for printing.
+    ///
+    /// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
+    TimezoneOffsetPermissive,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
+    Nanosecond3NoDot,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
+    Nanosecond6NoDot,
+    /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
+    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, 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.
+    Numeric(Numeric, Pad),
+    /// Fixed-format item.
+    Fixed(Fixed),
+    /// Issues a formatting error. Used to signal an invalid format string.
+    Error,
+}
+
+macro_rules! lit {
+    ($x:expr) => {
+        Item::Literal($x)
+    };
+}
+macro_rules! sp {
+    ($x:expr) => {
+        Item::Space($x)
+    };
+}
+macro_rules! num {
+    ($x:ident) => {
+        Item::Numeric(Numeric::$x, Pad::None)
+    };
+}
+macro_rules! num0 {
+    ($x:ident) => {
+        Item::Numeric(Numeric::$x, Pad::Zero)
+    };
+}
+macro_rules! nums {
+    ($x:ident) => {
+        Item::Numeric(Numeric::$x, Pad::Space)
+    };
+}
+macro_rules! fix {
+    ($x:ident) => {
+        Item::Fixed(Fixed::$x)
+    };
+}
+macro_rules! internal_fix {
+    ($x:ident) => {
+        Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x }))
+    };
+}
+
+/// An error from the `parse` function.
+#[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, Hash)]
+pub enum ParseErrorKind {
+    /// Given field is out of permitted range.
+    OutOfRange,
+
+    /// There is no possible date and time value with given set of fields.
+    ///
+    /// This does not include the out-of-range conditions, which are trivially invalid.
+    /// It includes the case that there are one or more fields that are inconsistent to each other.
+    Impossible,
+
+    /// Given set of fields is not enough to make a requested date and time value.
+    ///
+    /// Note that there *may* be a case that given fields constrain the possible values so much
+    /// that there is a unique possible value. Chrono only tries to be correct for
+    /// most useful sets of fields however, as such constraint solving can be expensive.
+    NotEnough,
+
+    /// The input string has some invalid character sequence for given formatting items.
+    Invalid,
+
+    /// The input string has been prematurely ended.
+    TooShort,
+
+    /// All formatting items have been read but there is a remaining input.
+    TooLong,
+
+    /// 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>`.
+pub type ParseResult<T> = Result<T, ParseError>;
+
+impl fmt::Display for ParseError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.0 {
+            ParseErrorKind::OutOfRange => write!(f, "input is out of range"),
+            ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"),
+            ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"),
+            ParseErrorKind::Invalid => write!(f, "input contains invalid characters"),
+            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 {
+        "parser error, see to_string() for details"
+    }
+}
+
+// to be used in this module and submodules
+const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange);
+const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible);
+const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough);
+const INVALID: ParseError = ParseError(ParseErrorKind::Invalid);
+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))]
+#[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<'_>,
+) -> fmt::Result {
+    let mut result = String::new();
+    format_inner(&mut result, date, time, off, item, None)?;
+    w.pad(&result)
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+fn format_inner(
+    result: &mut String,
+    date: Option<&NaiveDate>,
+    time: Option<&NaiveTime>,
+    off: Option<&(String, FixedOffset)>,
+    item: &Item<'_>,
+    locale: Option<Locale>,
+) -> fmt::Result {
+    let locale = Locales::new(locale);
+
+    use num_integer::{div_floor, mod_floor};
+
+    match *item {
+        Item::Literal(s) | Item::Space(s) => result.push_str(s),
+        #[cfg(any(feature = "alloc", feature = "std", test))]
+        Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
+
+        Item::Numeric(ref spec, ref pad) => {
+            use self::Numeric::*;
+
+            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()))),
+                YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
+                YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
+                IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
+                IsoYearDiv100 => (2, date.map(|d| div_floor(i64::from(d.iso_week().year()), 100))),
+                IsoYearMod100 => (2, date.map(|d| mod_floor(i64::from(d.iso_week().year()), 100))),
+                Month => (2, date.map(|d| i64::from(d.month()))),
+                Day => (2, date.map(|d| i64::from(d.day()))),
+                WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
+                WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
+                IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
+                NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday().num_days_from_sunday()))),
+                WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday().number_from_monday()))),
+                Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
+                Hour => (2, time.map(|t| i64::from(t.hour()))),
+                Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
+                Minute => (2, time.map(|t| i64::from(t.minute()))),
+                Second => (2, time.map(|t| i64::from(t.second() + t.nanosecond() / 1_000_000_000))),
+                Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
+                Timestamp => (
+                    1,
+                    match (date, time, off) {
+                        (Some(d), Some(t), None) => Some(d.and_time(*t).timestamp()),
+                        (Some(d), Some(t), Some(&(_, off))) => {
+                            Some((d.and_time(*t) - off).timestamp())
+                        }
+                        (_, _, _) => None,
+                    },
+                ),
+
+                // for the future expansion
+                Internal(ref int) => match int._dummy {},
+            };
+
+            if let Some(v) = v {
+                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),
+                        Pad::Zero => write!(result, "{:+01$}", v, width + 1),
+                        Pad::Space => write!(result, "{:+1$}", v, width + 1),
+                    }
+                } else {
+                    match *pad {
+                        Pad::None => write!(result, "{}", v),
+                        Pad::Zero => write!(result, "{:01$}", v, width),
+                        Pad::Space => write!(result, "{:1$}", v, width),
+                    }
+                }?
+            } else {
+                return Err(fmt::Error); // insufficient arguments for given format
+            }
+        }
+
+        Item::Fixed(ref spec) => {
+            use self::Fixed::*;
+
+            let ret =
+                match *spec {
+                    ShortMonthName => date.map(|d| {
+                        result.push_str(locale.short_months[d.month0() as usize]);
+                        Ok(())
+                    }),
+                    LongMonthName => date.map(|d| {
+                        result.push_str(locale.long_months[d.month0() as usize]);
+                        Ok(())
+                    }),
+                    ShortWeekdayName => date.map(|d| {
+                        result.push_str(
+                            locale.short_weekdays[d.weekday().num_days_from_sunday() as usize],
+                        );
+                        Ok(())
+                    }),
+                    LongWeekdayName => date.map(|d| {
+                        result.push_str(
+                            locale.long_weekdays[d.weekday().num_days_from_sunday() as usize],
+                        );
+                        Ok(())
+                    }),
+                    LowerAmPm => time.map(|t| {
+                        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 {
+                            locale.am_pm[1]
+                        } else {
+                            locale.am_pm[0]
+                        });
+                        Ok(())
+                    }),
+                    Nanosecond => time.map(|t| {
+                        let nano = t.nanosecond() % 1_000_000_000;
+                        if nano == 0 {
+                            Ok(())
+                        } else if nano % 1_000_000 == 0 {
+                            write!(result, ".{:03}", nano / 1_000_000)
+                        } else if nano % 1_000 == 0 {
+                            write!(result, ".{:06}", nano / 1_000)
+                        } else {
+                            write!(result, ".{:09}", nano)
+                        }
+                    }),
+                    Nanosecond3 => time.map(|t| {
+                        let nano = t.nanosecond() % 1_000_000_000;
+                        write!(result, ".{:03}", nano / 1_000_000)
+                    }),
+                    Nanosecond6 => time.map(|t| {
+                        let nano = t.nanosecond() % 1_000_000_000;
+                        write!(result, ".{:06}", nano / 1_000)
+                    }),
+                    Nanosecond9 => time.map(|t| {
+                        let nano = t.nanosecond() % 1_000_000_000;
+                        write!(result, ".{:09}", nano)
+                    }),
+                    Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => time
+                        .map(|t| {
+                            let nano = t.nanosecond() % 1_000_000_000;
+                            write!(result, "{:03}", nano / 1_000_000)
+                        }),
+                    Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => time
+                        .map(|t| {
+                            let nano = t.nanosecond() % 1_000_000_000;
+                            write!(result, "{:06}", nano / 1_000)
+                        }),
+                    Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => time
+                        .map(|t| {
+                            let nano = t.nanosecond() % 1_000_000_000;
+                            write!(result, "{:09}", nano)
+                        }),
+                    TimezoneName => off.map(|(name, _)| {
+                        result.push_str(name);
+                        Ok(())
+                    }),
+                    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, Colons::None))
+                    }
+                    TimezoneOffsetZ => {
+                        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")
+                    }
+                    RFC2822 =>
+                    // same as `%a, %d %b %Y %H:%M:%S %z`
+                    {
+                        if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
+                            Some(write_rfc2822_inner(result, d, t, off, locale))
+                        } else {
+                            None
+                        }
+                    }
+                    RFC3339 =>
+                    // same as `%Y-%m-%dT%H:%M:%S%.f%:z`
+                    {
+                        if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
+                            Some(write_rfc3339(result, crate::NaiveDateTime::new(*d, *t), off))
+                        } else {
+                            None
+                        }
+                    }
+                };
+
+            match ret {
+                Some(ret) => ret?,
+                None => return Err(fmt::Error), // insufficient arguments for given format
+            }
+        }
+
+        Item::Error => return Err(fmt::Error),
+    }
+    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>,
+    time: Option<&NaiveTime>,
+    off: Option<&(String, FixedOffset)>,
+    items: I,
+) -> fmt::Result
+where
+    I: Iterator<Item = B> + Clone,
+    B: Borrow<Item<'a>>,
+{
+    let mut result = String::new();
+    for item in items {
+        format_inner(&mut result, date, time, off, item.borrow(), None)?;
+    }
+    w.pad(&result)
+}
+
+mod parsed;
+
+// due to the size of parsing routines, they are in separate modules.
+mod parse;
+mod scan;
+
+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.
+    date: Option<NaiveDate>,
+    /// The time view, if any.
+    time: Option<NaiveTime>,
+    /// The name and local-to-UTC difference for the offset (timezone), if any.
+    off: Option<(String, FixedOffset)>,
+    /// 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>,
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+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,
+            time,
+            off: None,
+            items,
+            #[cfg(feature = "unstable-locales")]
+            locale: None,
+        }
+    }
+
+    /// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
+    pub fn new_with_offset<Off>(
+        date: Option<NaiveDate>,
+        time: Option<NaiveTime>,
+        offset: &Off,
+        items: I,
+    ) -> DelayedFormat<I>
+    where
+        Off: Offset + fmt::Display,
+    {
+        let name_and_diff = (offset.to_string(), offset.fix());
+        DelayedFormat {
+            date,
+            time,
+            off: Some(name_and_diff),
+            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, 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>,
+        offset: &Off,
+        items: I,
+        locale: Locale,
+    ) -> DelayedFormat<I>
+    where
+        Off: Offset + fmt::Display,
+    {
+        let name_and_diff = (offset.to_string(), offset.fix());
+        DelayedFormat { date, time, off: Some(name_and_diff), items, locale: Some(locale) }
+    }
+}
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        #[cfg(feature = "unstable-locales")]
+        {
+            if let Some(locale) = self.locale {
+                return format_localized(
+                    f,
+                    self.date.as_ref(),
+                    self.time.as_ref(),
+                    self.off.as_ref(),
+                    self.items.clone(),
+                    locale,
+                );
+            }
+        }
+
+        format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
+    }
+}
+
+// this implementation is here only because we need some private code from `scan`
+
+/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
+///
+/// # 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;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if let Ok(("", w)) = scan::short_or_long_weekday(s) {
+            Ok(w)
+        } else {
+            Err(ParseWeekdayError { _dummy: () })
+        }
+    }
+}
+
+/// 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>,
+    time: Option<&NaiveTime>,
+    off: Option<&(String, FixedOffset)>,
+    item: &Item<'a>,
+    locale: Locale,
+) -> fmt::Result {
+    let mut result = String::new();
+    format_inner(&mut result, date, time, off, item, Some(locale))?;
+    w.pad(&result)
+}
+
+/// 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>,
+    time: Option<&NaiveTime>,
+    off: Option<&(String, FixedOffset)>,
+    items: I,
+    locale: Locale,
+) -> fmt::Result
+where
+    I: Iterator<Item = B> + Clone,
+    B: Borrow<Item<'a>>,
+{
+    let mut result = String::new();
+    for item in items {
+        format_inner(&mut result, date, time, off, item.borrow(), Some(locale))?;
+    }
+    w.pad(&result)
+}
+
+/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html).
+///
+/// # 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;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if let Ok(("", w)) = scan::short_or_long_month0(s) {
+            match w {
+                0 => Ok(Month::January),
+                1 => Ok(Month::February),
+                2 => Ok(Month::March),
+                3 => Ok(Month::April),
+                4 => Ok(Month::May),
+                5 => Ok(Month::June),
+                6 => Ok(Month::July),
+                7 => Ok(Month::August),
+                8 => Ok(Month::September),
+                9 => Ok(Month::October),
+                10 => Ok(Month::November),
+                11 => Ok(Month::December),
+                _ => Err(ParseMonthError { _dummy: () }),
+            }
+        } else {
+            Err(ParseMonthError { _dummy: () })
+        }
+    }
+}
diff --git a/src/format/parse.rs b/src/format/parse.rs
new file mode 100644 (file)
index 0000000..69204d2
--- /dev/null
@@ -0,0 +1,977 @@
+// This is a part of Chrono.
+// Portions copyright (c) 2015, John Nagle.
+// See README.md and LICENSE.txt for details.
+
+//! Date and time parsing routines.
+
+#![allow(deprecated)]
+
+use core::borrow::Borrow;
+use core::str;
+use core::usize;
+
+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 crate::{DateTime, FixedOffset, Weekday};
+
+fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
+    p.set_weekday(match v {
+        0 => Weekday::Sun,
+        1 => Weekday::Mon,
+        2 => Weekday::Tue,
+        3 => Weekday::Wed,
+        4 => Weekday::Thu,
+        5 => Weekday::Fri,
+        6 => Weekday::Sat,
+        _ => return Err(OUT_OF_RANGE),
+    })
+}
+
+fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> {
+    p.set_weekday(match v {
+        1 => Weekday::Mon,
+        2 => Weekday::Tue,
+        3 => Weekday::Wed,
+        4 => Weekday::Thu,
+        5 => Weekday::Fri,
+        6 => Weekday::Sat,
+        7 => Weekday::Sun,
+        _ => return Err(OUT_OF_RANGE),
+    })
+}
+
+fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
+    macro_rules! try_consume {
+        ($e:expr) => {{
+            let (s_, v) = $e?;
+            s = s_;
+            v
+        }};
+    }
+
+    // an adapted RFC 2822 syntax from Section 3.3 and 4.3:
+    //
+    // 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
+    // day         = *S 1*2DIGIT *S
+    // month       = 1*S month-name 1*S
+    // month-name  = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
+    //               "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
+    // year        = *S 2*DIGIT *S
+    // time        = time-of-day 1*S zone
+    // time-of-day = hour ":" minute [ ":" second ]
+    // hour        = *S 2DIGIT *S
+    // minute      = *S 2DIGIT *S
+    // second      = *S 2DIGIT *S
+    // zone        = ( "+" / "-" ) 4DIGIT /
+    //               "UT" / "GMT" /                  ; same as +0000
+    //               "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
+    //               "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
+    //               1*(%d65-90 / %d97-122)          ; same as -0000
+    //
+    // some notes:
+    //
+    // - quoted characters can be in any mixture of lower and upper cases.
+    //
+    // - 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`). 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
+    //   by adding 1900. note that four-or-more-digit years less than 1000
+    //   are *never* affected by this rule.
+    //
+    // - mismatching day-of-week is always an error, which is consistent to
+    //   Chrono's own rules.
+    //
+    // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not
+    //   support offsets larger than 24 hours. this is not *that* problematic
+    //   since we do not directly go to a `DateTime` so one can recover
+    //   the offset information from `Parsed` anyway.
+
+    s = s.trim_left();
+
+    if let Ok((s_, weekday)) = scan::short_weekday(s) {
+        if !s_.starts_with(',') {
+            return Err(INVALID);
+        }
+        s = &s_[1..];
+        parsed.set_weekday(weekday)?;
+    }
+
+    s = s.trim_left();
+    parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
+    s = scan::space(s)?; // mandatory
+    parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
+    s = scan::space(s)?; // mandatory
+
+    // distinguish two- and three-digit years from four-digit years
+    let prevlen = s.len();
+    let mut year = try_consume!(scan::number(s, 2, usize::MAX));
+    let yearlen = prevlen - s.len();
+    match (yearlen, year) {
+        (2, 0..=49) => {
+            year += 2000;
+        } //   47 -> 2047,   05 -> 2005
+        (2, 50..=99) => {
+            year += 1900;
+        } //   79 -> 1979
+        (3, _) => {
+            year += 1900;
+        } //  112 -> 2012,  009 -> 1909
+        (_, _) => {} // 1987 -> 1987, 0654 -> 0654
+    }
+    parsed.set_year(year)?;
+
+    s = scan::space(s)?; // mandatory
+    parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
+    s = scan::char(s.trim_left(), b':')?.trim_left(); // *S ":" *S
+    parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
+    if let Ok(s_) = scan::char(s.trim_left(), b':') {
+        // [ ":" *S 2DIGIT ]
+        parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
+    }
+
+    s = scan::space(s)?; // mandatory
+    if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) {
+        // only set the offset when it is definitely known (i.e. not `-0000`)
+        parsed.set_offset(i64::from(offset))?;
+    }
+
+    // optional comments
+    while let Ok((s_out, ())) = scan::comment_2822(s) {
+        s = s_out;
+    }
+
+    Ok((s, ()))
+}
+
+fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
+    macro_rules! try_consume {
+        ($e:expr) => {{
+            let (s_, v) = $e?;
+            s = s_;
+            v
+        }};
+    }
+
+    // an adapted RFC 3339 syntax from Section 5.6:
+    //
+    // date-fullyear  = 4DIGIT
+    // date-month     = 2DIGIT ; 01-12
+    // date-mday      = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
+    // time-hour      = 2DIGIT ; 00-23
+    // time-minute    = 2DIGIT ; 00-59
+    // time-second    = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
+    // time-secfrac   = "." 1*DIGIT
+    // time-numoffset = ("+" / "-") time-hour ":" time-minute
+    // time-offset    = "Z" / time-numoffset
+    // partial-time   = time-hour ":" time-minute ":" time-second [time-secfrac]
+    // full-date      = date-fullyear "-" date-month "-" date-mday
+    // full-time      = partial-time time-offset
+    // date-time      = full-date "T" full-time
+    //
+    // some notes:
+    //
+    // - quoted characters can be in any mixture of lower and upper cases.
+    //
+    // - it may accept any number of fractional digits for seconds.
+    //   for Chrono, this means that we should skip digits past first 9 digits.
+    //
+    // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59.
+    //   note that this restriction is unique to RFC 3339 and not ISO 8601.
+    //   since this is not a typical Chrono behavior, we check it earlier.
+
+    parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
+    s = scan::char(s, b'-')?;
+    parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
+    s = scan::char(s, b'-')?;
+    parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
+
+    s = match s.as_bytes().first() {
+        Some(&b't') | Some(&b'T') => &s[1..],
+        Some(_) => return Err(INVALID),
+        None => return Err(TOO_SHORT),
+    };
+
+    parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
+    s = scan::char(s, b':')?;
+    parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
+    s = scan::char(s, b':')?;
+    parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
+    if s.starts_with('.') {
+        let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
+        parsed.set_nanosecond(nanosecond)?;
+    }
+
+    let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':')));
+    if offset <= -86_400 || offset >= 86_400 {
+        return Err(OUT_OF_RANGE);
+    }
+    parsed.set_offset(i64::from(offset))?;
+
+    Ok((s, ()))
+}
+
+/// Tries to parse given string into `parsed` with given formatting items.
+/// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used).
+/// There should be no trailing string after parsing;
+/// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces.
+///
+/// This particular date and time parser is:
+///
+/// - Greedy. It will consume the longest possible prefix.
+///   For example, `April` is always consumed entirely when the long month name is requested;
+///   it equally accepts `Apr`, but prefers the longer prefix in this case.
+///
+/// - Padding-agnostic (for numeric items).
+///   The [`Pad`](./enum.Pad.html) field is completely ignored,
+///   so one can prepend any number of whitespace then any number of zeroes before numbers.
+///
+/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
+pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
+where
+    I: Iterator<Item = B>,
+    B: Borrow<Item<'a>>,
+{
+    parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
+}
+
+fn parse_internal<'a, 'b, I, B>(
+    parsed: &mut Parsed,
+    mut s: &'b str,
+    items: I,
+) -> Result<&'b str, (&'b str, ParseError)>
+where
+    I: Iterator<Item = B>,
+    B: Borrow<Item<'a>>,
+{
+    macro_rules! try_consume {
+        ($e:expr) => {{
+            match $e {
+                Ok((s_, v)) => {
+                    s = s_;
+                    v
+                }
+                Err(e) => return Err((s, e)),
+            }
+        }};
+    }
+
+    for item in items {
+        match *item.borrow() {
+            Item::Literal(prefix) => {
+                if s.len() < prefix.len() {
+                    return Err((s, TOO_SHORT));
+                }
+                if !s.starts_with(prefix) {
+                    return Err((s, INVALID));
+                }
+                s = &s[prefix.len()..];
+            }
+
+            #[cfg(any(feature = "alloc", feature = "std", test))]
+            Item::OwnedLiteral(ref prefix) => {
+                if s.len() < prefix.len() {
+                    return Err((s, TOO_SHORT));
+                }
+                if !s.starts_with(&prefix[..]) {
+                    return Err((s, INVALID));
+                }
+                s = &s[prefix.len()..];
+            }
+
+            Item::Space(_) => {
+                s = s.trim_left();
+            }
+
+            #[cfg(any(feature = "alloc", feature = "std", test))]
+            Item::OwnedSpace(_) => {
+                s = s.trim_left();
+            }
+
+            Item::Numeric(ref spec, ref _pad) => {
+                use super::Numeric::*;
+                type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
+
+                let (width, signed, set): (usize, bool, Setter) = match *spec {
+                    Year => (4, true, Parsed::set_year),
+                    YearDiv100 => (2, false, Parsed::set_year_div_100),
+                    YearMod100 => (2, false, Parsed::set_year_mod_100),
+                    IsoYear => (4, true, Parsed::set_isoyear),
+                    IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
+                    IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
+                    Month => (2, false, Parsed::set_month),
+                    Day => (2, false, Parsed::set_day),
+                    WeekFromSun => (2, false, Parsed::set_week_from_sun),
+                    WeekFromMon => (2, false, Parsed::set_week_from_mon),
+                    IsoWeek => (2, false, Parsed::set_isoweek),
+                    NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
+                    WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
+                    Ordinal => (3, false, Parsed::set_ordinal),
+                    Hour => (2, false, Parsed::set_hour),
+                    Hour12 => (2, false, Parsed::set_hour12),
+                    Minute => (2, false, Parsed::set_minute),
+                    Second => (2, false, Parsed::set_second),
+                    Nanosecond => (9, false, Parsed::set_nanosecond),
+                    Timestamp => (usize::MAX, false, Parsed::set_timestamp),
+
+                    // for the future expansion
+                    Internal(ref int) => match int._dummy {},
+                };
+
+                s = s.trim_left();
+                let v = if signed {
+                    if s.starts_with('-') {
+                        let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
+                        0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))?
+                    } else if s.starts_with('+') {
+                        try_consume!(scan::number(&s[1..], 1, usize::MAX))
+                    } else {
+                        // if there is no explicit sign, we respect the original `width`
+                        try_consume!(scan::number(s, 1, width))
+                    }
+                } else {
+                    try_consume!(scan::number(s, 1, width))
+                };
+                set(parsed, v).map_err(|e| (s, e))?;
+            }
+
+            Item::Fixed(ref spec) => {
+                use super::Fixed::*;
+
+                match spec {
+                    &ShortMonthName => {
+                        let month0 = try_consume!(scan::short_month0(s));
+                        parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
+                    }
+
+                    &LongMonthName => {
+                        let month0 = try_consume!(scan::short_or_long_month0(s));
+                        parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
+                    }
+
+                    &ShortWeekdayName => {
+                        let weekday = try_consume!(scan::short_weekday(s));
+                        parsed.set_weekday(weekday).map_err(|e| (s, e))?;
+                    }
+
+                    &LongWeekdayName => {
+                        let weekday = try_consume!(scan::short_or_long_weekday(s));
+                        parsed.set_weekday(weekday).map_err(|e| (s, e))?;
+                    }
+
+                    &LowerAmPm | &UpperAmPm => {
+                        if s.len() < 2 {
+                            return Err((s, TOO_SHORT));
+                        }
+                        let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
+                            (b'a', b'm') => false,
+                            (b'p', b'm') => true,
+                            _ => return Err((s, INVALID)),
+                        };
+                        parsed.set_ampm(ampm).map_err(|e| (s, e))?;
+                        s = &s[2..];
+                    }
+
+                    &Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
+                        if s.starts_with('.') {
+                            let nano = try_consume!(scan::nanosecond(&s[1..]));
+                            parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
+                        }
+                    }
+
+                    &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
+                        if s.len() < 3 {
+                            return Err((s, TOO_SHORT));
+                        }
+                        let nano = try_consume!(scan::nanosecond_fixed(s, 3));
+                        parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
+                    }
+
+                    &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
+                        if s.len() < 6 {
+                            return Err((s, TOO_SHORT));
+                        }
+                        let nano = try_consume!(scan::nanosecond_fixed(s, 6));
+                        parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
+                    }
+
+                    &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
+                        if s.len() < 9 {
+                            return Err((s, TOO_SHORT));
+                        }
+                        let nano = try_consume!(scan::nanosecond_fixed(s, 9));
+                        parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
+                    }
+
+                    &TimezoneName => {
+                        try_consume!(scan::timezone_name_skip(s));
+                    }
+
+                    &TimezoneOffsetColon
+                    | &TimezoneOffsetDoubleColon
+                    | &TimezoneOffsetTripleColon
+                    | &TimezoneOffset => {
+                        let offset = try_consume!(scan::timezone_offset(
+                            s.trim_left(),
+                            scan::colon_or_space
+                        ));
+                        parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
+                    }
+
+                    &TimezoneOffsetColonZ | &TimezoneOffsetZ => {
+                        let offset = try_consume!(scan::timezone_offset_zulu(
+                            s.trim_left(),
+                            scan::colon_or_space
+                        ));
+                        parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
+                    }
+                    &Internal(InternalFixed {
+                        val: InternalInternal::TimezoneOffsetPermissive,
+                    }) => {
+                        let offset = try_consume!(scan::timezone_offset_permissive(
+                            s.trim_left(),
+                            scan::colon_or_space
+                        ));
+                        parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
+                    }
+
+                    &RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
+                    &RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
+                }
+            }
+
+            Item::Error => {
+                return Err((s, BAD_FORMAT));
+            }
+        }
+    }
+
+    // if there are trailling chars, it is an error
+    if !s.is_empty() {
+        Err((s, TOO_LONG))
+    } else {
+        Ok(s)
+    }
+}
+
+/// 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: &[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),
+        ];
+        const TIME_ITEMS: &[Item<'static>] = &[
+            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(""),
+            Item::Fixed(Fixed::TimezoneOffsetZ),
+            Item::Space(""),
+        ];
+
+        let mut parsed = Parsed::new();
+        match parse_internal(&mut parsed, s, DATE_ITEMS.iter()) {
+            Err((remainder, e)) if e.0 == ParseErrorKind::TooLong => {
+                if remainder.starts_with('T') || remainder.starts_with(' ') {
+                    parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?;
+                } else {
+                    return Err(INVALID);
+                }
+            }
+            Err((_s, e)) => return Err(e),
+            Ok(_) => return Err(NOT_ENOUGH),
+        };
+        parsed.to_datetime()
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn test_parse() {
+    use super::IMPOSSIBLE;
+    use super::*;
+
+    // workaround for Rust issue #22255
+    fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
+        let mut parsed = Parsed::new();
+        parse(&mut parsed, s, items.iter())?;
+        Ok(parsed)
+    }
+
+    macro_rules! check {
+        ($fmt:expr, $items:expr; $err:tt) => (
+            assert_eq!(parse_all($fmt, &$items), Err($err))
+        );
+        ($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
+            let mut expected = Parsed::new();
+            $(expected.$k = Some($v);)*
+            assert_eq!(parse_all($fmt, &$items), Ok(expected))
+        });
+    }
+
+    // empty string
+    check!("",  []; );
+    check!(" ", []; TOO_LONG);
+    check!("a", []; TOO_LONG);
+
+    // whitespaces
+    check!("",          [sp!("")]; );
+    check!(" ",         [sp!("")]; );
+    check!("\t",        [sp!("")]; );
+    check!(" \n\r  \n", [sp!("")]; );
+    check!("a",         [sp!("")]; TOO_LONG);
+
+    // literal
+    check!("",    [lit!("a")]; TOO_SHORT);
+    check!(" ",   [lit!("a")]; INVALID);
+    check!("a",   [lit!("a")]; );
+    check!("aa",  [lit!("a")]; TOO_LONG);
+    check!("A",   [lit!("a")]; INVALID);
+    check!("xy",  [lit!("xy")]; );
+    check!("xy",  [lit!("x"), lit!("y")]; );
+    check!("x y", [lit!("x"), lit!("y")]; INVALID);
+    check!("xy",  [lit!("x"), sp!(""), lit!("y")]; );
+    check!("x y", [lit!("x"), sp!(""), lit!("y")]; );
+
+    // numeric
+    check!("1987",        [num!(Year)]; year: 1987);
+    check!("1987 ",       [num!(Year)]; TOO_LONG);
+    check!("0x12",        [num!(Year)]; TOO_LONG); // `0` is parsed
+    check!("x123",        [num!(Year)]; INVALID);
+    check!("2015",        [num!(Year)]; year: 2015);
+    check!("0000",        [num!(Year)]; year:    0);
+    check!("9999",        [num!(Year)]; year: 9999);
+    check!(" \t987",      [num!(Year)]; year:  987);
+    check!("5",           [num!(Year)]; year:    5);
+    check!("5\0",         [num!(Year)]; TOO_LONG);
+    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);
+    check!("12345",       [num0!(Year), lit!("5")]; year: 1234);
+    check!("12341234",    [num!(Year), num!(Year)]; year: 1234);
+    check!("1234 1234",   [num!(Year), num!(Year)]; year: 1234);
+    check!("1234 1235",   [num!(Year), num!(Year)]; IMPOSSIBLE);
+    check!("1234 1234",   [num!(Year), lit!("x"), num!(Year)]; INVALID);
+    check!("1234x1234",   [num!(Year), lit!("x"), num!(Year)]; year: 1234);
+    check!("1234xx1234",  [num!(Year), lit!("x"), num!(Year)]; INVALID);
+    check!("1234 x 1234", [num!(Year), lit!("x"), num!(Year)]; INVALID);
+
+    // signed numeric
+    check!("-42",         [num!(Year)]; year: -42);
+    check!("+42",         [num!(Year)]; year: 42);
+    check!("-0042",       [num!(Year)]; year: -42);
+    check!("+0042",       [num!(Year)]; year: 42);
+    check!("-42195",      [num!(Year)]; year: -42195);
+    check!("+42195",      [num!(Year)]; year: 42195);
+    check!("  -42195",    [num!(Year)]; year: -42195);
+    check!("  +42195",    [num!(Year)]; year: 42195);
+    check!("  -   42",    [num!(Year)]; INVALID);
+    check!("  +   42",    [num!(Year)]; INVALID);
+    check!("-",           [num!(Year)]; TOO_SHORT);
+    check!("+",           [num!(Year)]; TOO_SHORT);
+
+    // unsigned numeric
+    check!("345",   [num!(Ordinal)]; ordinal: 345);
+    check!("+345",  [num!(Ordinal)]; INVALID);
+    check!("-345",  [num!(Ordinal)]; INVALID);
+    check!(" 345",  [num!(Ordinal)]; ordinal: 345);
+    check!(" +345", [num!(Ordinal)]; INVALID);
+    check!(" -345", [num!(Ordinal)]; INVALID);
+
+    // various numeric fields
+    check!("1234 5678",
+           [num!(Year), num!(IsoYear)];
+           year: 1234, isoyear: 5678);
+    check!("12 34 56 78",
+           [num!(YearDiv100), num!(YearMod100), num!(IsoYearDiv100), num!(IsoYearMod100)];
+           year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78);
+    check!("1 2 3 4 5 6",
+           [num!(Month), num!(Day), num!(WeekFromSun), num!(WeekFromMon), num!(IsoWeek),
+            num!(NumDaysFromSun)];
+           month: 1, day: 2, week_from_sun: 3, week_from_mon: 4, isoweek: 5, weekday: Weekday::Sat);
+    check!("7 89 01",
+           [num!(WeekdayFromMon), num!(Ordinal), num!(Hour12)];
+           weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1);
+    check!("23 45 6 78901234 567890123",
+           [num!(Hour), num!(Minute), num!(Second), num!(Nanosecond), num!(Timestamp)];
+           hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234,
+           timestamp: 567_890_123);
+
+    // fixed: month and weekday names
+    check!("apr",       [fix!(ShortMonthName)]; month: 4);
+    check!("Apr",       [fix!(ShortMonthName)]; month: 4);
+    check!("APR",       [fix!(ShortMonthName)]; month: 4);
+    check!("ApR",       [fix!(ShortMonthName)]; month: 4);
+    check!("April",     [fix!(ShortMonthName)]; TOO_LONG); // `Apr` is parsed
+    check!("A",         [fix!(ShortMonthName)]; TOO_SHORT);
+    check!("Sol",       [fix!(ShortMonthName)]; INVALID);
+    check!("Apr",       [fix!(LongMonthName)]; month: 4);
+    check!("Apri",      [fix!(LongMonthName)]; TOO_LONG); // `Apr` is parsed
+    check!("April",     [fix!(LongMonthName)]; month: 4);
+    check!("Aprill",    [fix!(LongMonthName)]; TOO_LONG);
+    check!("Aprill",    [fix!(LongMonthName), lit!("l")]; month: 4);
+    check!("Aprl",      [fix!(LongMonthName), lit!("l")]; month: 4);
+    check!("April",     [fix!(LongMonthName), lit!("il")]; TOO_SHORT); // do not backtrack
+    check!("thu",       [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
+    check!("Thu",       [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
+    check!("THU",       [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
+    check!("tHu",       [fix!(ShortWeekdayName)]; weekday: Weekday::Thu);
+    check!("Thursday",  [fix!(ShortWeekdayName)]; TOO_LONG); // `Thu` is parsed
+    check!("T",         [fix!(ShortWeekdayName)]; TOO_SHORT);
+    check!("The",       [fix!(ShortWeekdayName)]; INVALID);
+    check!("Nop",       [fix!(ShortWeekdayName)]; INVALID);
+    check!("Thu",       [fix!(LongWeekdayName)]; weekday: Weekday::Thu);
+    check!("Thur",      [fix!(LongWeekdayName)]; TOO_LONG); // `Thu` is parsed
+    check!("Thurs",     [fix!(LongWeekdayName)]; TOO_LONG); // ditto
+    check!("Thursday",  [fix!(LongWeekdayName)]; weekday: Weekday::Thu);
+    check!("Thursdays", [fix!(LongWeekdayName)]; TOO_LONG);
+    check!("Thursdays", [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu);
+    check!("Thus",      [fix!(LongWeekdayName), lit!("s")]; weekday: Weekday::Thu);
+    check!("Thursday",  [fix!(LongWeekdayName), lit!("rsday")]; TOO_SHORT); // do not backtrack
+
+    // fixed: am/pm
+    check!("am",  [fix!(LowerAmPm)]; hour_div_12: 0);
+    check!("pm",  [fix!(LowerAmPm)]; hour_div_12: 1);
+    check!("AM",  [fix!(LowerAmPm)]; hour_div_12: 0);
+    check!("PM",  [fix!(LowerAmPm)]; hour_div_12: 1);
+    check!("am",  [fix!(UpperAmPm)]; hour_div_12: 0);
+    check!("pm",  [fix!(UpperAmPm)]; hour_div_12: 1);
+    check!("AM",  [fix!(UpperAmPm)]; hour_div_12: 0);
+    check!("PM",  [fix!(UpperAmPm)]; hour_div_12: 1);
+    check!("Am",  [fix!(LowerAmPm)]; hour_div_12: 0);
+    check!(" Am", [fix!(LowerAmPm)]; INVALID);
+    check!("ame", [fix!(LowerAmPm)]; TOO_LONG); // `am` is parsed
+    check!("a",   [fix!(LowerAmPm)]; TOO_SHORT);
+    check!("p",   [fix!(LowerAmPm)]; TOO_SHORT);
+    check!("x",   [fix!(LowerAmPm)]; TOO_SHORT);
+    check!("xx",  [fix!(LowerAmPm)]; INVALID);
+    check!("",    [fix!(LowerAmPm)]; TOO_SHORT);
+
+    // fixed: dot plus nanoseconds
+    check!("",              [fix!(Nanosecond)]; ); // no field set, but not an error
+    check!("4",             [fix!(Nanosecond)]; TOO_LONG); // never consumes `4`
+    check!("4",             [fix!(Nanosecond), num!(Second)]; second: 4);
+    check!(".0",            [fix!(Nanosecond)]; nanosecond: 0);
+    check!(".4",            [fix!(Nanosecond)]; nanosecond: 400_000_000);
+    check!(".42",           [fix!(Nanosecond)]; nanosecond: 420_000_000);
+    check!(".421",          [fix!(Nanosecond)]; nanosecond: 421_000_000);
+    check!(".42195",        [fix!(Nanosecond)]; nanosecond: 421_950_000);
+    check!(".421950803",    [fix!(Nanosecond)]; nanosecond: 421_950_803);
+    check!(".421950803547", [fix!(Nanosecond)]; nanosecond: 421_950_803);
+    check!(".000000003547", [fix!(Nanosecond)]; nanosecond: 3);
+    check!(".000000000547", [fix!(Nanosecond)]; nanosecond: 0);
+    check!(".",             [fix!(Nanosecond)]; TOO_SHORT);
+    check!(".4x",           [fix!(Nanosecond)]; TOO_LONG);
+    check!(".  4",          [fix!(Nanosecond)]; INVALID);
+    check!("  .4",          [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming
+
+    // fixed: nanoseconds without the dot
+    check!("",             [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
+    check!("0",            [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
+    check!("4",            [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
+    check!("42",           [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
+    check!("421",          [internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000);
+    check!("42143",        [internal_fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43);
+    check!("42195",        [internal_fix!(Nanosecond3NoDot)]; TOO_LONG);
+    check!("4x",           [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
+    check!("  4",          [internal_fix!(Nanosecond3NoDot)]; INVALID);
+    check!(".421",         [internal_fix!(Nanosecond3NoDot)]; INVALID);
+
+    check!("",             [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
+    check!("0",            [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
+    check!("42195",        [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
+    check!("421950",       [internal_fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000);
+    check!("000003",       [internal_fix!(Nanosecond6NoDot)]; nanosecond: 3000);
+    check!("000000",       [internal_fix!(Nanosecond6NoDot)]; nanosecond: 0);
+    check!("4x",           [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
+    check!("     4",       [internal_fix!(Nanosecond6NoDot)]; INVALID);
+    check!(".42100",       [internal_fix!(Nanosecond6NoDot)]; INVALID);
+
+    check!("",             [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
+    check!("42195",        [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
+    check!("421950803",    [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803);
+    check!("000000003",    [internal_fix!(Nanosecond9NoDot)]; nanosecond: 3);
+    check!("42195080354",  [internal_fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9
+    check!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG);
+    check!("000000000",    [internal_fix!(Nanosecond9NoDot)]; nanosecond: 0);
+    check!("00000000x",    [internal_fix!(Nanosecond9NoDot)]; INVALID);
+    check!("        4",    [internal_fix!(Nanosecond9NoDot)]; INVALID);
+    check!(".42100000",    [internal_fix!(Nanosecond9NoDot)]; INVALID);
+
+    // fixed: timezone offsets
+    check!("+00:00",    [fix!(TimezoneOffset)]; offset: 0);
+    check!("-00:00",    [fix!(TimezoneOffset)]; offset: 0);
+    check!("+00:01",    [fix!(TimezoneOffset)]; offset: 60);
+    check!("-00:01",    [fix!(TimezoneOffset)]; offset: -60);
+    check!("+00:30",    [fix!(TimezoneOffset)]; offset: 30 * 60);
+    check!("-00:30",    [fix!(TimezoneOffset)]; offset: -30 * 60);
+    check!("+04:56",    [fix!(TimezoneOffset)]; offset: 296 * 60);
+    check!("-04:56",    [fix!(TimezoneOffset)]; offset: -296 * 60);
+    check!("+24:00",    [fix!(TimezoneOffset)]; offset: 24 * 60 * 60);
+    check!("-24:00",    [fix!(TimezoneOffset)]; offset: -24 * 60 * 60);
+    check!("+99:59",    [fix!(TimezoneOffset)]; offset: (100 * 60 - 1) * 60);
+    check!("-99:59",    [fix!(TimezoneOffset)]; offset: -(100 * 60 - 1) * 60);
+    check!("+00:59",    [fix!(TimezoneOffset)]; offset: 59 * 60);
+    check!("+00:60",    [fix!(TimezoneOffset)]; OUT_OF_RANGE);
+    check!("+00:99",    [fix!(TimezoneOffset)]; OUT_OF_RANGE);
+    check!("#12:34",    [fix!(TimezoneOffset)]; INVALID);
+    check!("12:34",     [fix!(TimezoneOffset)]; INVALID);
+    check!("+12:34 ",   [fix!(TimezoneOffset)]; TOO_LONG);
+    check!(" +12:34",   [fix!(TimezoneOffset)]; offset: 754 * 60);
+    check!("\t -12:34", [fix!(TimezoneOffset)]; offset: -754 * 60);
+    check!("",          [fix!(TimezoneOffset)]; TOO_SHORT);
+    check!("+",         [fix!(TimezoneOffset)]; TOO_SHORT);
+    check!("+1",        [fix!(TimezoneOffset)]; TOO_SHORT);
+    check!("+12",       [fix!(TimezoneOffset)]; TOO_SHORT);
+    check!("+123",      [fix!(TimezoneOffset)]; TOO_SHORT);
+    check!("+1234",     [fix!(TimezoneOffset)]; offset: 754 * 60);
+    check!("+12345",    [fix!(TimezoneOffset)]; TOO_LONG);
+    check!("+12345",    [fix!(TimezoneOffset), num!(Day)]; offset: 754 * 60, day: 5);
+    check!("Z",         [fix!(TimezoneOffset)]; INVALID);
+    check!("z",         [fix!(TimezoneOffset)]; INVALID);
+    check!("Z",         [fix!(TimezoneOffsetZ)]; offset: 0);
+    check!("z",         [fix!(TimezoneOffsetZ)]; offset: 0);
+    check!("Y",         [fix!(TimezoneOffsetZ)]; INVALID);
+    check!("Zulu",      [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0);
+    check!("zulu",      [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0);
+    check!("+1234ulu",  [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
+    check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
+    check!("Z",         [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
+    check!("z",         [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
+    check!("+12:00",    [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
+    check!("+12",       [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
+    check!("CEST 5",    [fix!(TimezoneName), lit!(" "), num!(Day)]; day: 5);
+
+    // some practical examples
+    check!("2015-02-04T14:37:05+09:00",
+           [num!(Year), lit!("-"), num!(Month), lit!("-"), num!(Day), lit!("T"),
+            num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), fix!(TimezoneOffset)];
+           year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
+           minute: 37, second: 5, offset: 32400);
+    check!("20150204143705567",
+            [num!(Year), num!(Month), num!(Day),
+            num!(Hour), num!(Minute), num!(Second), internal_fix!(Nanosecond3NoDot)];
+            year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
+            minute: 37, second: 5, nanosecond: 567000000);
+    check!("Mon, 10 Jun 2013 09:32:37 GMT",
+           [fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "),
+            fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"),
+            num!(Minute), lit!(":"), num!(Second), sp!(" "), lit!("GMT")];
+           year: 2013, month: 6, day: 10, weekday: Weekday::Mon,
+           hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37);
+    check!("Sun Aug 02 13:39:15 CEST 2020",
+            [fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName), sp!(" "),
+            num!(Day), sp!(" "), num!(Hour), lit!(":"), num!(Minute), lit!(":"),
+            num!(Second), sp!(" "), fix!(TimezoneName), sp!(" "), num!(Year)];
+            year: 2020, month: 8, day: 2, weekday: Weekday::Sun,
+            hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15);
+    check!("20060102150405",
+           [num!(Year), num!(Month), num!(Day), num!(Hour), num!(Minute), num!(Second)];
+           year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5);
+    check!("3:14PM",
+           [num!(Hour12), lit!(":"), num!(Minute), fix!(LowerAmPm)];
+           hour_div_12: 1, hour_mod_12: 3, minute: 14);
+    check!("12345678901234.56789",
+           [num!(Timestamp), lit!("."), num!(Nanosecond)];
+           nanosecond: 56_789, timestamp: 12_345_678_901_234);
+    check!("12345678901234.56789",
+           [num!(Timestamp), fix!(Nanosecond)];
+           nanosecond: 567_890_000, timestamp: 12_345_678_901_234);
+}
+
+#[cfg(test)]
+#[test]
+fn test_rfc2822() {
+    use super::NOT_ENOUGH;
+    use super::*;
+    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
+        ("11 Sep 2001 09:45:00 EST", Ok("Tue, 11 Sep 2001 09:45:00 -0500")),
+        ("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
+        ("Tue, 20 Jan 2015", Err(TOO_SHORT)),              // omitted fields
+        ("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name
+        ("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour
+        ("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)),  // bad # of digits in hour
+        ("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute
+        ("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second
+        ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset
+        ("6 Jun 1944 04:00:00Z", Err(INVALID)),            // bad offset (zulu not allowed)
+        ("Tue, 20 Jan 2015 17:35:20 HAS", Err(NOT_ENOUGH)), // bad named time zone
+    ];
+
+    fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
+        let mut parsed = Parsed::new();
+        parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
+        parsed.to_datetime()
+    }
+
+    fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
+        dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string()
+    }
+
+    // Test against test data above
+    for &(date, checkdate) in testdates.iter() {
+        let d = rfc2822_to_datetime(date); // parse a date
+        let dt = match d {
+            // did we get a value?
+            Ok(dt) => Ok(fmt_rfc2822_datetime(dt)), // yes, go on
+            Err(e) => Err(e),                       // otherwise keep an error for the comparison
+        };
+        if dt != checkdate.map(|s| s.to_string()) {
+            // check for expected result
+            panic!(
+                "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
+                date, dt, checkdate
+            );
+        }
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn parse_rfc850() {
+    use crate::{TimeZone, Utc};
+
+    static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT";
+
+    let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
+    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);
+
+    // Check that it parses correctly
+    assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
+
+    // Check that the rest of the weekdays parse correctly (this test originally failed because
+    // Sunday parsed incorrectly).
+    let testdates = [
+        (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 {
+        assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn test_rfc3339() {
+    use super::*;
+    use crate::offset::FixedOffset;
+    use crate::DateTime;
+
+    // Test data - (input, Ok(expected result after parse and format) or Err(error code))
+    let testdates = [
+        ("2015-01-20T17:35:20-08:00", Ok("2015-01-20T17:35:20-08:00")), // normal case
+        ("1944-06-06T04:04:00Z", Ok("1944-06-06T04:04:00+00:00")),      // D-day
+        ("2001-09-11T09:45:00-08:00", Ok("2001-09-11T09:45:00-08:00")),
+        ("2015-01-20T17:35:20.001-08:00", Ok("2015-01-20T17:35:20.001-08:00")),
+        ("2015-01-20T17:35:20.000031-08:00", Ok("2015-01-20T17:35:20.000031-08:00")),
+        ("2015-01-20T17:35:20.000000004-08:00", Ok("2015-01-20T17:35:20.000000004-08:00")),
+        ("2015-01-20T17:35:20.000000000452-08:00", Ok("2015-01-20T17:35:20-08:00")), // too small
+        ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month
+        ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour
+        ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute
+        ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second
+        ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset
+    ];
+
+    fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
+        let mut parsed = Parsed::new();
+        parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?;
+        parsed.to_datetime()
+    }
+
+    fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
+        dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string()
+    }
+
+    // Test against test data above
+    for &(date, checkdate) in testdates.iter() {
+        let d = rfc3339_to_datetime(date); // parse a date
+        let dt = match d {
+            // did we get a value?
+            Ok(dt) => Ok(fmt_rfc3339_datetime(dt)), // yes, go on
+            Err(e) => Err(e),                       // otherwise keep an error for the comparison
+        };
+        if dt != checkdate.map(|s| s.to_string()) {
+            // check for expected result
+            panic!(
+                "Date conversion failed for {}\nReceived: {:?}\nExpected: {:?}",
+                date, dt, checkdate
+            );
+        }
+    }
+}
diff --git a/src/format/parsed.rs b/src/format/parsed.rs
new file mode 100644 (file)
index 0000000..6cc29e9
--- /dev/null
@@ -0,0 +1,1289 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! 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 super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
+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:
+///
+/// - `set_*` methods try to set given field(s) while checking for the consistency.
+///   It may or may not check for the range constraint immediately (for efficiency reasons).
+///
+/// - `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.
+#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
+pub struct Parsed {
+    /// Year.
+    ///
+    /// This can be negative unlike [`year_div_100`](#structfield.year_div_100)
+    /// and [`year_mod_100`](#structfield.year_mod_100) fields.
+    pub year: Option<i32>,
+
+    /// Year divided by 100. Implies that the year is >= 1 BCE when set.
+    ///
+    /// Due to the common usage, if this field is missing but
+    /// [`year_mod_100`](#structfield.year_mod_100) is present,
+    /// it is inferred to 19 when `year_mod_100 >= 70` and 20 otherwise.
+    pub year_div_100: Option<i32>,
+
+    /// Year modulo 100. Implies that the year is >= 1 BCE when set.
+    pub year_mod_100: Option<i32>,
+
+    /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date).
+    ///
+    /// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and
+    /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
+    pub isoyear: Option<i32>,
+
+    /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), divided by 100.
+    /// Implies that the year is >= 1 BCE when set.
+    ///
+    /// Due to the common usage, if this field is missing but
+    /// [`isoyear_mod_100`](#structfield.isoyear_mod_100) is present,
+    /// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise.
+    pub isoyear_div_100: Option<i32>,
+
+    /// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), modulo 100.
+    /// Implies that the year is >= 1 BCE when set.
+    pub isoyear_mod_100: Option<i32>,
+
+    /// Month (1--12).
+    pub month: Option<u32>,
+
+    /// Week number, where the week 1 starts at the first Sunday of January
+    /// (0--53, 1--53 or 1--52 depending on the year).
+    pub week_from_sun: Option<u32>,
+
+    /// Week number, where the week 1 starts at the first Monday of January
+    /// (0--53, 1--53 or 1--52 depending on the year).
+    pub week_from_mon: Option<u32>,
+
+    /// [ISO week number](../naive/struct.NaiveDate.html#week-date)
+    /// (1--52 or 1--53 depending on the year).
+    pub isoweek: Option<u32>,
+
+    /// Day of the week.
+    pub weekday: Option<Weekday>,
+
+    /// Day of the year (1--365 or 1--366 depending on the year).
+    pub ordinal: Option<u32>,
+
+    /// Day of the month (1--28, 1--29, 1--30 or 1--31 depending on the month).
+    pub day: Option<u32>,
+
+    /// Hour number divided by 12 (0--1). 0 indicates AM and 1 indicates PM.
+    pub hour_div_12: Option<u32>,
+
+    /// Hour number modulo 12 (0--11).
+    pub hour_mod_12: Option<u32>,
+
+    /// Minute number (0--59).
+    pub minute: Option<u32>,
+
+    /// Second number (0--60, accounting for leap seconds).
+    pub second: Option<u32>,
+
+    /// The number of nanoseconds since the whole second (0--999,999,999).
+    pub nanosecond: Option<u32>,
+
+    /// The number of non-leap seconds since the midnight UTC on January 1, 1970.
+    ///
+    /// This can be off by one if [`second`](#structfield.second) is 60 (a leap second).
+    pub timestamp: Option<i64>,
+
+    /// Offset from the local time to UTC, in seconds.
+    pub offset: Option<i32>,
+
+    /// A dummy field to make this type not fully destructible (required for API stability).
+    _dummy: (),
+}
+
+/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"),
+/// and if it is empty, set `old` to `new` as well.
+#[inline]
+fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
+    if let Some(ref old) = *old {
+        if *old == new {
+            Ok(())
+        } else {
+            Err(IMPOSSIBLE)
+        }
+    } else {
+        *old = Some(new);
+        Ok(())
+    }
+}
+
+impl Parsed {
+    /// Returns the initial value of parsed parts.
+    pub fn new() -> Parsed {
+        Parsed::default()
+    }
+
+    /// Tries to set the [`year`](#structfield.year) field from given value.
+    #[inline]
+    pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.year, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
+    #[inline]
+    pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
+        if value < 0 {
+            return Err(OUT_OF_RANGE);
+        }
+        set_if_consistent(&mut self.year_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
+    #[inline]
+    pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
+        if value < 0 {
+            return Err(OUT_OF_RANGE);
+        }
+        set_if_consistent(&mut self.year_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
+    #[inline]
+    pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.isoyear, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
+    #[inline]
+    pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
+        if value < 0 {
+            return Err(OUT_OF_RANGE);
+        }
+        set_if_consistent(&mut self.isoyear_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
+    #[inline]
+    pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
+        if value < 0 {
+            return Err(OUT_OF_RANGE);
+        }
+        set_if_consistent(&mut self.isoyear_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`month`](#structfield.month) field from given value.
+    #[inline]
+    pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.month, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
+    #[inline]
+    pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.week_from_sun, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
+    #[inline]
+    pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.week_from_mon, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
+    #[inline]
+    pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.isoweek, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`weekday`](#structfield.weekday) field from given value.
+    #[inline]
+    pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
+        set_if_consistent(&mut self.weekday, value)
+    }
+
+    /// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
+    #[inline]
+    pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.ordinal, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`day`](#structfield.day) field from given value.
+    #[inline]
+    pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.day, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
+    /// (`false` for AM, `true` for PM)
+    #[inline]
+    pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
+        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 !(1..=12).contains(&value) {
+            return Err(OUT_OF_RANGE);
+        }
+        set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
+    }
+
+    /// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and
+    /// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
+    #[inline]
+    pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
+        let v = value.to_u32().ok_or(OUT_OF_RANGE)?;
+        set_if_consistent(&mut self.hour_div_12, v / 12)?;
+        set_if_consistent(&mut self.hour_mod_12, v % 12)?;
+        Ok(())
+    }
+
+    /// Tries to set the [`minute`](#structfield.minute) field from given value.
+    #[inline]
+    pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.minute, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`second`](#structfield.second) field from given value.
+    #[inline]
+    pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.second, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
+    #[inline]
+    pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.nanosecond, value.to_u32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
+    #[inline]
+    pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.timestamp, value)
+    }
+
+    /// Tries to set the [`offset`](#structfield.offset) field from given value.
+    #[inline]
+    pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
+        set_if_consistent(&mut self.offset, value.to_i32().ok_or(OUT_OF_RANGE)?)
+    }
+
+    /// Returns a parsed naive date out of given fields.
+    ///
+    /// This method is able to determine the date from given subset of fields:
+    ///
+    /// - Year, month, day.
+    /// - Year, day of the year (ordinal).
+    /// - Year, week number counted from Sunday or Monday, day of the week.
+    /// - ISO week date.
+    ///
+    /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted,
+    /// the two-digit year is used to guess the century number then.
+    pub fn to_naive_date(&self) -> ParseResult<NaiveDate> {
+        fn resolve_year(
+            y: Option<i32>,
+            q: Option<i32>,
+            r: Option<i32>,
+        ) -> ParseResult<Option<i32>> {
+            match (y, q, r) {
+                // if there is no further information, simply return the given full year.
+                // this is a common case, so let's avoid division here.
+                (y, None, None) => Ok(y),
+
+                // if there is a full year *and* also quotient and/or modulo,
+                // 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) => {
+                    if y < 0 {
+                        return Err(OUT_OF_RANGE);
+                    }
+                    let (q_, r_) = div_rem(y, 100);
+                    if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ {
+                        Ok(Some(y))
+                    } else {
+                        Err(IMPOSSIBLE)
+                    }
+                }
+
+                // 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)) => {
+                    if q < 0 {
+                        return Err(OUT_OF_RANGE);
+                    }
+                    let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
+                    Ok(Some(y.ok_or(OUT_OF_RANGE)?))
+                }
+
+                // 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 })),
+
+                // otherwise it is an out-of-bound or insufficient condition.
+                (None, Some(_), None) => Err(NOT_ENOUGH),
+                (_, _, Some(_)) => Err(OUT_OF_RANGE),
+            }
+        }
+
+        let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
+        let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
+
+        // verify the normal year-month-day date.
+        let verify_ymd = |date: NaiveDate| {
+            let year = date.year();
+            let (year_div_100, year_mod_100) = if year >= 0 {
+                let (q, r) = div_rem(year, 100);
+                (Some(q), Some(r))
+            } else {
+                (None, None) // they should be empty to be consistent
+            };
+            let month = date.month();
+            let day = date.day();
+            self.year.unwrap_or(year) == year
+                && self.year_div_100.or(year_div_100) == year_div_100
+                && self.year_mod_100.or(year_mod_100) == year_mod_100
+                && self.month.unwrap_or(month) == month
+                && self.day.unwrap_or(day) == day
+        };
+
+        // verify the ISO week date.
+        let verify_isoweekdate = |date: NaiveDate| {
+            let week = date.iso_week();
+            let isoyear = week.year();
+            let isoweek = week.week();
+            let weekday = date.weekday();
+            let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
+                let (q, r) = div_rem(isoyear, 100);
+                (Some(q), Some(r))
+            } else {
+                (None, None) // they should be empty to be consistent
+            };
+            self.isoyear.unwrap_or(isoyear) == isoyear
+                && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100
+                && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100
+                && self.isoweek.unwrap_or(isoweek) == isoweek
+                && self.weekday.unwrap_or(weekday) == weekday
+        };
+
+        // verify the ordinal and other (non-ISO) week dates.
+        let verify_ordinal = |date: NaiveDate| {
+            let ordinal = date.ordinal();
+            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
+        };
+
+        // test several possibilities.
+        // tries to construct a full `NaiveDate` as much as possible, then verifies that
+        // it is consistent with other given fields.
+        let (verified, parsed_date) = match (given_year, given_isoyear, self) {
+            (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
+                // year, month, day
+                let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?;
+                (verify_isoweekdate(date) && verify_ordinal(date), date)
+            }
+
+            (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
+                // year, day of the year
+                let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?;
+                (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
+            }
+
+            (
+                Some(year),
+                _,
+                &Parsed { week_from_sun: Some(week_from_sun), weekday: Some(weekday), .. },
+            ) => {
+                // year, week (starting at 1st Sunday), day of the week
+                let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
+                let firstweek = match newyear.weekday() {
+                    Weekday::Sun => 0,
+                    Weekday::Mon => 6,
+                    Weekday::Tue => 5,
+                    Weekday::Wed => 4,
+                    Weekday::Thu => 3,
+                    Weekday::Fri => 2,
+                    Weekday::Sat => 1,
+                };
+
+                // `firstweek+1`-th day of January is the beginning of the week 1.
+                if week_from_sun > 53 {
+                    return Err(OUT_OF_RANGE);
+                } // can it overflow?
+                let ndays = firstweek
+                    + (week_from_sun as i32 - 1) * 7
+                    + weekday.num_days_from_sunday() as i32;
+                let date = newyear
+                    .checked_add_signed(OldDuration::days(i64::from(ndays)))
+                    .ok_or(OUT_OF_RANGE)?;
+                if date.year() != year {
+                    return Err(OUT_OF_RANGE);
+                } // early exit for correct error
+
+                (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
+            }
+
+            (
+                Some(year),
+                _,
+                &Parsed { week_from_mon: Some(week_from_mon), weekday: Some(weekday), .. },
+            ) => {
+                // year, week (starting at 1st Monday), day of the week
+                let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
+                let firstweek = match newyear.weekday() {
+                    Weekday::Sun => 1,
+                    Weekday::Mon => 0,
+                    Weekday::Tue => 6,
+                    Weekday::Wed => 5,
+                    Weekday::Thu => 4,
+                    Weekday::Fri => 3,
+                    Weekday::Sat => 2,
+                };
+
+                // `firstweek+1`-th day of January is the beginning of the week 1.
+                if week_from_mon > 53 {
+                    return Err(OUT_OF_RANGE);
+                } // can it overflow?
+                let ndays = firstweek
+                    + (week_from_mon as i32 - 1) * 7
+                    + weekday.num_days_from_monday() as i32;
+                let date = newyear
+                    .checked_add_signed(OldDuration::days(i64::from(ndays)))
+                    .ok_or(OUT_OF_RANGE)?;
+                if date.year() != year {
+                    return Err(OUT_OF_RANGE);
+                } // early exit for correct error
+
+                (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
+            }
+
+            (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
+                // ISO year, week, day of the week
+                let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
+                let date = date.ok_or(OUT_OF_RANGE)?;
+                (verify_ymd(date) && verify_ordinal(date), date)
+            }
+
+            (_, _, _) => return Err(NOT_ENOUGH),
+        };
+
+        if verified {
+            Ok(parsed_date)
+        } else {
+            Err(IMPOSSIBLE)
+        }
+    }
+
+    /// Returns a parsed naive time out of given fields.
+    ///
+    /// This method is able to determine the time from given subset of fields:
+    ///
+    /// - Hour, minute. (second and nanosecond assumed to be 0)
+    /// - Hour, minute, second. (nanosecond assumed to be 0)
+    /// - Hour, minute, second, nanosecond.
+    ///
+    /// 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(_) => 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(_) => 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(_) => 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),
+            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(_) => return Err(OUT_OF_RANGE),
+            None => 0,
+        };
+
+        NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE)
+    }
+
+    /// Returns a parsed naive date and time out of given fields,
+    /// except for the [`offset`](#structfield.offset) field (assumed to have a given value).
+    /// This is required for parsing a local time or other known-timezone inputs.
+    ///
+    /// This method is able to determine the combined date and time
+    /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field.
+    /// Either way those fields have to be consistent to each other.
+    pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult<NaiveDateTime> {
+        let date = self.to_naive_date();
+        let time = self.to_naive_time();
+        if let (Ok(date), Ok(time)) = (date, time) {
+            let datetime = date.and_time(time);
+
+            // verify the timestamp field if any
+            // the following is safe, `timestamp` is very limited in range
+            let timestamp = datetime.timestamp() - i64::from(offset);
+            if let Some(given_timestamp) = self.timestamp {
+                // if `datetime` represents a leap second, it might be off by one second.
+                if given_timestamp != timestamp
+                    && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1)
+                {
+                    return Err(IMPOSSIBLE);
+                }
+            }
+
+            Ok(datetime)
+        } else if let Some(timestamp) = self.timestamp {
+            use super::ParseError as PE;
+            use super::ParseErrorKind::{Impossible, OutOfRange};
+
+            // if date and time is problematic already, there is no point proceeding.
+            // we at least try to give a correct error though.
+            match (date, time) {
+                (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE),
+                (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE),
+                (_, _) => {} // one of them is insufficient
+            }
+
+            // reconstruct date and time fields from timestamp
+            let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
+            let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
+            let mut datetime = datetime.ok_or(OUT_OF_RANGE)?;
+
+            // fill year, ordinal, hour, minute and second fields from timestamp.
+            // if existing fields are consistent, this will allow the full date/time reconstruction.
+            let mut parsed = self.clone();
+            if parsed.second == Some(60) {
+                // `datetime.second()` cannot be 60, so this is the only case for a leap second.
+                match datetime.second() {
+                    // it's okay, just do not try to overwrite the existing field.
+                    59 => {}
+                    // `datetime` is known to be off by one second.
+                    0 => {
+                        datetime -= OldDuration::seconds(1);
+                    }
+                    // otherwise it is impossible.
+                    _ => return Err(IMPOSSIBLE),
+                }
+            // ...and we have the correct candidates for other fields.
+            } else {
+                parsed.set_second(i64::from(datetime.second()))?;
+            }
+            parsed.set_year(i64::from(datetime.year()))?;
+            parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
+            parsed.set_hour(i64::from(datetime.hour()))?;
+            parsed.set_minute(i64::from(datetime.minute()))?;
+
+            // validate other fields (e.g. week) and return
+            let date = parsed.to_naive_date()?;
+            let time = parsed.to_naive_time()?;
+            Ok(date.and_time(time))
+        } else {
+            // reproduce the previous error(s)
+            date?;
+            time?;
+            unreachable!()
+        }
+    }
+
+    /// Returns a parsed fixed time zone offset out of given fields.
+    pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
+        self.offset.and_then(FixedOffset::east_opt).ok_or(OUT_OF_RANGE)
+    }
+
+    /// Returns a parsed timezone-aware date and time out of given fields.
+    ///
+    /// This method is able to determine the combined date and time
+    /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field,
+    /// plus a time zone offset.
+    /// Either way those fields have to be consistent to each other.
+    pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
+        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),
+            LocalResult::Ambiguous(..) => Err(NOT_ENOUGH),
+        }
+    }
+
+    /// Returns a parsed timezone-aware date and time out of given fields,
+    /// with an additional `TimeZone` used to interpret and validate the local date.
+    ///
+    /// This method is able to determine the combined date and time
+    /// from date and time fields or a single [`timestamp`](#structfield.timestamp) field,
+    /// plus a time zone offset.
+    /// Either way those fields have to be consistent to each other.
+    /// If parsed fields include an UTC offset, it also has to be consistent to
+    /// [`offset`](#structfield.offset).
+    pub fn to_datetime_with_timezone<Tz: TimeZone>(&self, tz: &Tz) -> ParseResult<DateTime<Tz>> {
+        // if we have `timestamp` specified, guess an offset from that.
+        let mut guessed_offset = 0;
+        if let Some(timestamp) = self.timestamp {
+            // make a naive `DateTime` from given timestamp and (if any) nanosecond.
+            // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
+            let nanosecond = self.nanosecond.unwrap_or(0);
+            let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
+            let dt = dt.ok_or(OUT_OF_RANGE)?;
+            guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
+        }
+
+        // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
+        let check_offset = |dt: &DateTime<Tz>| {
+            if let Some(offset) = self.offset {
+                dt.offset().fix().local_minus_utc() == offset
+            } else {
+                true
+            }
+        };
+
+        // `guessed_offset` should be correct when `self.timestamp` is given.
+        // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
+        let datetime = self.to_naive_datetime_with_offset(guessed_offset)?;
+        match tz.from_local_datetime(&datetime) {
+            LocalResult::None => Err(IMPOSSIBLE),
+            LocalResult::Single(t) => {
+                if check_offset(&t) {
+                    Ok(t)
+                } else {
+                    Err(IMPOSSIBLE)
+                }
+            }
+            LocalResult::Ambiguous(min, max) => {
+                // try to disambiguate two possible local dates by offset.
+                match (check_offset(&min), check_offset(&max)) {
+                    (false, false) => Err(IMPOSSIBLE),
+                    (false, true) => Ok(max),
+                    (true, false) => Ok(min),
+                    (true, true) => Err(NOT_ENOUGH),
+                }
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
+    use super::Parsed;
+    use crate::naive::{NaiveDate, NaiveTime};
+    use crate::offset::{FixedOffset, TimeZone, Utc};
+    use crate::Datelike;
+    use crate::Weekday::*;
+
+    #[test]
+    fn test_parsed_set_fields() {
+        // year*, isoyear*
+        let mut p = Parsed::new();
+        assert_eq!(p.set_year(1987), Ok(()));
+        assert_eq!(p.set_year(1986), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year(1988), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year(1987), Ok(()));
+        assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year`
+        assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto
+        assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE));
+
+        let mut p = Parsed::new();
+        assert_eq!(p.set_year(0), Ok(()));
+        assert_eq!(p.set_year_div_100(0), Ok(()));
+        assert_eq!(p.set_year_mod_100(0), Ok(()));
+
+        let mut p = Parsed::new();
+        assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE));
+        assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE));
+        assert_eq!(p.set_year(-1), Ok(()));
+        assert_eq!(p.set_year(-2), Err(IMPOSSIBLE));
+        assert_eq!(p.set_year(0), Err(IMPOSSIBLE));
+
+        let mut p = Parsed::new();
+        assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
+        assert_eq!(p.set_year_div_100(8), Ok(()));
+        assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE));
+
+        // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset
+        let mut p = Parsed::new();
+        assert_eq!(p.set_month(7), Ok(()));
+        assert_eq!(p.set_month(1), Err(IMPOSSIBLE));
+        assert_eq!(p.set_month(6), Err(IMPOSSIBLE));
+        assert_eq!(p.set_month(8), Err(IMPOSSIBLE));
+        assert_eq!(p.set_month(12), Err(IMPOSSIBLE));
+
+        let mut p = Parsed::new();
+        assert_eq!(p.set_month(8), Ok(()));
+        assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE));
+
+        // hour
+        let mut p = Parsed::new();
+        assert_eq!(p.set_hour(12), Ok(()));
+        assert_eq!(p.set_hour(11), Err(IMPOSSIBLE));
+        assert_eq!(p.set_hour(13), Err(IMPOSSIBLE));
+        assert_eq!(p.set_hour(12), Ok(()));
+        assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE));
+        assert_eq!(p.set_ampm(true), Ok(()));
+        assert_eq!(p.set_hour12(12), Ok(()));
+        assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation
+        assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE));
+        assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE));
+
+        let mut p = Parsed::new();
+        assert_eq!(p.set_ampm(true), Ok(()));
+        assert_eq!(p.set_hour12(7), Ok(()));
+        assert_eq!(p.set_hour(7), Err(IMPOSSIBLE));
+        assert_eq!(p.set_hour(18), Err(IMPOSSIBLE));
+        assert_eq!(p.set_hour(19), Ok(()));
+
+        // timestamp
+        let mut p = Parsed::new();
+        assert_eq!(p.set_timestamp(1_234_567_890), Ok(()));
+        assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE));
+        assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE));
+    }
+
+    #[test]
+    fn test_parsed_to_naive_date() {
+        macro_rules! parse {
+            ($($k:ident: $v:expr),*) => (
+                Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date()
+            )
+        }
+
+        let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap());
+
+        // ymd: omission of fields
+        assert_eq!(parse!(), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2));
+        assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2));
+        assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2));
+        assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2));
+
+        // ymd: out-of-range conditions
+        assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29));
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31),
+            ymd(1983, 12, 31)
+        );
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+        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 = NaiveDate::MAX.year();
+        assert_eq!(
+            parse!(year_div_100: max_year / 100,
+                          year_mod_100: max_year % 100, month: 1, day: 1),
+            ymd(max_year, 1, 1)
+        );
+        assert_eq!(
+            parse!(year_div_100: (max_year + 1) / 100,
+                          year_mod_100: (max_year + 1) % 100, month: 1, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+
+        // ymd: conflicting inputs
+        assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1));
+        assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE));
+        assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1));
+        assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE));
+        assert_eq!(
+            parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1),
+            ymd(1984, 1, 1)
+        );
+        assert_eq!(
+            parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(OUT_OF_RANGE));
+
+        // weekdates
+        assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1));
+        assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1));
+        assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2));
+        assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2));
+        assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3));
+        assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3));
+        assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8));
+        assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8));
+        assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9));
+        assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9));
+        assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10));
+        assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30));
+        assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31));
+        assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1));
+
+        // weekdates: conflicting inputs
+        assert_eq!(
+            parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat),
+            ymd(2000, 1, 8)
+        );
+        assert_eq!(
+            parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun),
+            ymd(2000, 1, 9)
+        );
+        assert_eq!(
+            parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun),
+            Err(IMPOSSIBLE)
+        );
+
+        // ISO weekdates
+        assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH));
+        assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31));
+        assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1));
+        assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3));
+        assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH));
+
+        // year and ordinal
+        assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH));
+        assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1));
+        assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29));
+        assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1));
+        assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31));
+        assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1));
+        assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28));
+        assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1));
+        assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31));
+        assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE));
+
+        // more complex cases
+        assert_eq!(
+            parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1,
+                          week_from_sun: 52, week_from_mon: 52, weekday: Wed),
+            ymd(2014, 12, 31)
+        );
+        assert_eq!(
+            parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1,
+                          week_from_sun: 52, week_from_mon: 52),
+            ymd(2014, 12, 31)
+        );
+        assert_eq!(
+            parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53,
+                          week_from_sun: 52, week_from_mon: 52, weekday: Wed),
+            Err(IMPOSSIBLE)
+        ); // no ISO week date 2014-W53-3
+        assert_eq!(
+            parse!(year: 2012, isoyear: 2015, isoweek: 1,
+                          week_from_sun: 52, week_from_mon: 52),
+            Err(NOT_ENOUGH)
+        ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31)
+        assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH));
+        // technically unique (2014-12-31) but Chrono gives up
+    }
+
+    #[test]
+    fn test_parsed_to_naive_time() {
+        macro_rules! parse {
+            ($($k:ident: $v:expr),*) => (
+                Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time()
+            )
+        }
+
+        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));
+        assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH));
+        assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH));
+        assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0));
+        assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45));
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45,
+                          nanosecond: 678_901_234),
+            hmsn(1, 23, 45, 678_901_234)
+        );
+        assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6));
+        assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH));
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012),
+            Err(NOT_ENOUGH)
+        );
+
+        // out-of-range conditions
+        assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE));
+        assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE));
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61),
+            Err(OUT_OF_RANGE)
+        );
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34,
+                          nanosecond: 1_000_000_000),
+            Err(OUT_OF_RANGE)
+        );
+
+        // leap seconds
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60),
+            hmsn(1, 23, 59, 1_000_000_000)
+        );
+        assert_eq!(
+            parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60,
+                          nanosecond: 999_999_999),
+            hmsn(1, 23, 59, 1_999_999_999)
+        );
+    }
+
+    #[test]
+    fn test_parsed_to_naive_datetime_with_offset() {
+        macro_rules! parse {
+            (offset = $offset:expr; $($k:ident: $v:expr),*) => (
+                Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset)
+            );
+            ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*))
+        }
+
+        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));
+        assert_eq!(
+            parse!(year: 2015, month: 1, day: 30,
+                          hour_div_12: 1, hour_mod_12: 2, minute: 38),
+            ymdhms(2015, 1, 30, 14, 38, 0)
+        );
+        assert_eq!(
+            parse!(year: 1997, month: 1, day: 30,
+                          hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5),
+            ymdhms(1997, 1, 30, 14, 38, 5)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5,
+                          minute: 6, second: 7, nanosecond: 890_123_456),
+            ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456)
+        );
+        assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0));
+        assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1));
+        assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1));
+        assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40));
+        assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44));
+
+        // full fields
+        assert_eq!(
+            parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
+                          ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
+                          isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
+                          hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
+                          nanosecond: 12_345_678, timestamp: 1_420_000_000),
+            ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
+        );
+        assert_eq!(
+            parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
+                          ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
+                          isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
+                          hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
+                          nanosecond: 12_345_678, timestamp: 1_419_999_999),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(offset = 32400;
+                          year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31,
+                          ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15,
+                          isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed,
+                          hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40,
+                          nanosecond: 12_345_678, timestamp: 1_419_967_600),
+            ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678)
+        );
+
+        // more timestamps
+        let max_days_from_year_1970 =
+            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 =
+            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(NaiveDate::MIN.year(), 1, 1, 0, 0, 0)
+        );
+        assert_eq!(
+            parse!(timestamp: year_0_from_year_1970.num_seconds()),
+            ymdhms(0, 1, 1, 0, 0, 0)
+        );
+        assert_eq!(
+            parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
+            ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59)
+        );
+
+        // leap seconds #1: partial fields
+        assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
+        assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59));
+        assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
+        assert_eq!(
+            parse!(second: 60, timestamp: 1_341_100_799),
+            ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
+        );
+        assert_eq!(
+            parse!(second: 60, timestamp: 1_341_100_800),
+            ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
+        );
+        assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0));
+        assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE));
+        assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE));
+
+        // leap seconds #2: full fields
+        // we need to have separate tests for them since it uses another control flow.
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 59, timestamp: 1_341_100_798),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 59, timestamp: 1_341_100_799),
+            ymdhms(2012, 6, 30, 23, 59, 59)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 59, timestamp: 1_341_100_800),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 60, timestamp: 1_341_100_799),
+            ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 60, timestamp: 1_341_100_800),
+            ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
+                          minute: 0, second: 0, timestamp: 1_341_100_800),
+            ymdhms(2012, 7, 1, 0, 0, 0)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0,
+                          minute: 0, second: 1, timestamp: 1_341_100_800),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11,
+                          minute: 59, second: 60, timestamp: 1_341_100_801),
+            Err(IMPOSSIBLE)
+        );
+
+        // error codes
+        assert_eq!(
+            parse!(year: 2015, month: 1, day: 20, weekday: Tue,
+                          hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20),
+            Err(OUT_OF_RANGE)
+        ); // `hour_div_12` is out of range
+    }
+
+    #[test]
+    fn test_parsed_to_datetime() {
+        macro_rules! parse {
+            ($($k:ident: $v:expr),*) => (
+                Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime()
+            )
+        }
+
+        let ymdhmsn = |y, m, d, h, n, s, nano, off| {
+            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));
+        assert_eq!(
+            parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
+                          minute: 26, second: 40, nanosecond: 12_345_678),
+            Err(NOT_ENOUGH)
+        );
+        assert_eq!(
+            parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
+                          minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
+            ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0)
+        );
+        assert_eq!(
+            parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
+                          minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
+            ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400)
+        );
+        assert_eq!(
+            parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1,
+                          minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876),
+            ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876)
+        );
+        assert_eq!(
+            parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4,
+                          minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400),
+            Err(OUT_OF_RANGE)
+        ); // `FixedOffset` does not support such huge offset
+    }
+
+    #[test]
+    fn test_parsed_to_datetime_with_timezone() {
+        macro_rules! parse {
+            ($tz:expr; $($k:ident: $v:expr),*) => (
+                Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz)
+            )
+        }
+
+        // single result from ymdhms
+        assert_eq!(
+            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
+                .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;
+                          year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
+                          minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            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_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_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.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_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0),
+            Err(IMPOSSIBLE)
+        );
+        assert_eq!(
+            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/src/format/scan.rs b/src/format/scan.rs
new file mode 100644 (file)
index 0000000..263fec5
--- /dev/null
@@ -0,0 +1,415 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+/*!
+ * Various scanning routines for the parser.
+ */
+
+#![allow(deprecated)]
+
+use super::{ParseResult, INVALID, OUT_OF_RANGE, TOO_SHORT};
+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,
+        _ => c,
+    });
+    let mut ys = pattern.as_bytes().iter().cloned();
+    loop {
+        match (xs.next(), ys.next()) {
+            (None, None) => return true,
+            (None, _) | (_, None) => return false,
+            (Some(x), Some(y)) if x != y => return false,
+            _ => (),
+        }
+    }
+}
+
+/// Tries to parse the non-negative number from `min` to `max` digits.
+///
+/// The absence of digits at all is an unconditional error.
+/// 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(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
+    // the first non-numeric byte, which may be another ascii character or beginning of multi-byte
+    // UTF-8 character.
+    let bytes = s.as_bytes();
+    if bytes.len() < min {
+        return Err(TOO_SHORT);
+    }
+
+    let mut n = 0i64;
+    for (i, c) in bytes.iter().take(max).cloned().enumerate() {
+        // cloned() = copied()
+        if !(b'0'..=b'9').contains(&c) {
+            if i < min {
+                return Err(INVALID);
+            } else {
+                return Ok((&s[i..], n));
+            }
+        }
+
+        n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) {
+            Some(n) => n,
+            None => return Err(OUT_OF_RANGE),
+        };
+    }
+
+    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(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)?;
+    let consumed = origlen - s.len();
+
+    // scale the number accordingly.
+    static SCALE: [i64; 10] =
+        [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1];
+    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'..='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(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)?;
+
+    // scale the number accordingly.
+    static SCALE: [i64; 10] =
+        [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1];
+    let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?;
+
+    Ok((s, v))
+}
+
+/// Tries to parse the month index (0 through 11) with the first three ASCII letters.
+pub(super) fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
+    if s.len() < 3 {
+        return Err(TOO_SHORT);
+    }
+    let buf = s.as_bytes();
+    let month0 = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) {
+        (b'j', b'a', b'n') => 0,
+        (b'f', b'e', b'b') => 1,
+        (b'm', b'a', b'r') => 2,
+        (b'a', b'p', b'r') => 3,
+        (b'm', b'a', b'y') => 4,
+        (b'j', b'u', b'n') => 5,
+        (b'j', b'u', b'l') => 6,
+        (b'a', b'u', b'g') => 7,
+        (b's', b'e', b'p') => 8,
+        (b'o', b'c', b't') => 9,
+        (b'n', b'o', b'v') => 10,
+        (b'd', b'e', b'c') => 11,
+        _ => return Err(INVALID),
+    };
+    Ok((&s[3..], month0))
+}
+
+/// Tries to parse the weekday with the first three ASCII letters.
+pub(super) fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
+    if s.len() < 3 {
+        return Err(TOO_SHORT);
+    }
+    let buf = s.as_bytes();
+    let weekday = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) {
+        (b'm', b'o', b'n') => Weekday::Mon,
+        (b't', b'u', b'e') => Weekday::Tue,
+        (b'w', b'e', b'd') => Weekday::Wed,
+        (b't', b'h', b'u') => Weekday::Thu,
+        (b'f', b'r', b'i') => Weekday::Fri,
+        (b's', b'a', b't') => Weekday::Sat,
+        (b's', b'u', b'n') => Weekday::Sun,
+        _ => return Err(INVALID),
+    };
+    Ok((&s[3..], 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(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
+    // lowercased month names, minus first three chars
+    static LONG_MONTH_SUFFIXES: [&str; 12] =
+        ["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
+
+    let (mut s, month0) = short_month0(s)?;
+
+    // tries to consume the suffix if possible
+    let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
+    if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
+        s = &s[suffix.len()..];
+    }
+
+    Ok((s, month0))
+}
+
+/// 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(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
+    // lowercased weekday names, minus first three chars
+    static LONG_WEEKDAY_SUFFIXES: [&str; 7] =
+        ["day", "sday", "nesday", "rsday", "day", "urday", "day"];
+
+    let (mut s, weekday) = short_weekday(s)?;
+
+    // tries to consume the suffix if possible
+    let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
+    if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
+        s = &s[suffix.len()..];
+    }
+
+    Ok((s, weekday))
+}
+
+/// Tries to consume exactly one given character.
+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),
+        None => Err(TOO_SHORT),
+    }
+}
+
+/// Tries to consume one or more whitespace.
+pub(super) fn space(s: &str) -> ParseResult<&str> {
+    let s_ = s.trim_left();
+    if s_.len() < s.len() {
+        Ok(s_)
+    } else if s.is_empty() {
+        Err(TOO_SHORT)
+    } else {
+        Err(INVALID)
+    }
+}
+
+/// Consumes any number (including zero) of colon or spaces.
+pub(super) fn colon_or_space(s: &str) -> ParseResult<&str> {
+    Ok(s.trim_left_matches(|c: char| c == ':' || c.is_whitespace()))
+}
+
+/// Tries to parse `[-+]\d\d` continued by `\d\d`. Return an offset in seconds if possible.
+///
+/// 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(super) fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
+where
+    F: FnMut(&str) -> ParseResult<&str>,
+{
+    timezone_offset_internal(s, consume_colon, false)
+}
+
+fn timezone_offset_internal<F>(
+    mut s: &str,
+    mut consume_colon: F,
+    allow_missing_minutes: bool,
+) -> ParseResult<(&str, i32)>
+where
+    F: FnMut(&str) -> ParseResult<&str>,
+{
+    fn digits(s: &str) -> ParseResult<(u8, u8)> {
+        let b = s.as_bytes();
+        if b.len() < 2 {
+            Err(TOO_SHORT)
+        } else {
+            Ok((b[0], b[1]))
+        }
+    }
+    let negative = match s.as_bytes().first() {
+        Some(&b'+') => false,
+        Some(&b'-') => true,
+        Some(_) => return Err(INVALID),
+        None => return Err(TOO_SHORT),
+    };
+    s = &s[1..];
+
+    // 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')),
+        _ => return Err(INVALID),
+    };
+    s = &s[2..];
+
+    // colons (and possibly other separators)
+    s = consume_colon(s)?;
+
+    // minutes (00--59)
+    // 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),
+            _ => return Err(INVALID),
+        }
+    } else if allow_missing_minutes {
+        0
+    } else {
+        return Err(TOO_SHORT);
+    };
+    s = match s.len() {
+        len if len >= 2 => &s[2..],
+        len if len == 0 => s,
+        _ => return Err(TOO_SHORT),
+    };
+
+    let seconds = hours * 3600 + minutes * 60;
+    Ok((s, if negative { -seconds } else { seconds }))
+}
+
+/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
+pub(super) fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
+where
+    F: FnMut(&str) -> ParseResult<&str>,
+{
+    let bytes = s.as_bytes();
+    match bytes.first() {
+        Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
+        Some(&b'u') | Some(&b'U') => {
+            if bytes.len() >= 3 {
+                let (b, c) = (bytes[1], bytes[2]);
+                match (b | 32, c | 32) {
+                    (b't', b'c') => Ok((&s[3..], 0)),
+                    _ => Err(INVALID),
+                }
+            } else {
+                Err(INVALID)
+            }
+        }
+        _ => timezone_offset(s, colon),
+    }
+}
+
+/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
+/// `+00:00`, and allows missing minutes entirely.
+pub(super) fn timezone_offset_permissive<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
+where
+    F: FnMut(&str) -> ParseResult<&str>,
+{
+    match s.as_bytes().first() {
+        Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
+        _ => timezone_offset_internal(s, colon, true),
+    }
+}
+
+/// 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(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,
+            _ => true,
+        })
+        .unwrap_or(s.len());
+    if upto > 0 {
+        let name = &s[..upto];
+        let s = &s[upto..];
+        let offset_hours = |o| Ok((s, Some(o * 3600)));
+        if equals(name, "gmt") || equals(name, "ut") {
+            offset_hours(0)
+        } else if equals(name, "edt") {
+            offset_hours(-4)
+        } else if equals(name, "est") || equals(name, "cdt") {
+            offset_hours(-5)
+        } else if equals(name, "cst") || equals(name, "mdt") {
+            offset_hours(-6)
+        } else if equals(name, "mst") || equals(name, "pdt") {
+            offset_hours(-7)
+        } else if equals(name, "pst") {
+            offset_hours(-8)
+        } else {
+            Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
+        }
+    } else {
+        let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
+        Ok((s_, Some(offset)))
+    }
+}
+
+/// Tries to consume everything until next whitespace-like symbol.
+/// Does not provide any offset information from the consumed data.
+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/src/format/strftime.rs b/src/format/strftime.rs
new file mode 100644 (file)
index 0000000..dcaabe4
--- /dev/null
@@ -0,0 +1,712 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+/*!
+`strftime`/`strptime`-inspired date and time formatting syntax.
+
+## Specifiers
+
+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. 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.                                  |
+| `%B`  | `July`   | Full month name. Also accepts corresponding abbreviation in parsing.       |
+| `%h`  | `Jul`    | Same as `%b`.                                                              |
+|       |          |                                                                            |
+| `%d`  | `08`     | Day number (01--31), zero-padded to 2 digits.                              |
+| `%e`  | ` 8`     | Same as `%d` but space-padded. Same as `%_d`.                              |
+|       |          |                                                                            |
+| `%a`  | `Sun`    | Abbreviated weekday name. Always 3 letters.                                |
+| `%A`  | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in 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. [^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. [^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.                       |
+|       |          |                                                                            |
+| `%D`  | `07/08/01`    | Month-day-year format. Same as `%m/%d/%y`.                            |
+| `%x`  | `07/08/01`    | Locale's date representation (e.g., 12/31/99).                        |
+| `%F`  | `2001-07-08`  | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`.                 |
+| `%v`  | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`.                            |
+|       |          |                                                                            |
+|       |          | **TIME SPECIFIERS:**                                                       |
+| `%H`  | `00`     | Hour number (00--23), zero-padded to 2 digits.                             |
+| `%k`  | ` 0`     | Same as `%H` but space-padded. Same as `%_H`.                              |
+| `%I`  | `12`     | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits.           |
+| `%l`  | `12`     | Same as `%I` but space-padded. Same as `%_I`.                              |
+|       |          |                                                                            |
+| `%P`  | `am`     | `am` or `pm` in 12-hour clocks.                                            |
+| `%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. [^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`.                        |
+| `%X`  | `00:34:60`    | Locale's time representation (e.g., 23:13:48).                        |
+| `%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. [^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. [^5]     |
+|       |               |                                                                       |
+| `%s`  | `994518299`   | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^6]|
+|       |          |                                                                            |
+|       |          | **SPECIAL SPECIFIERS:**                                                    |
+| `%t`  |          | Literal tab (`\t`).                                                        |
+| `%n`  |          | Literal newline (`\n`).                                                    |
+| `%%`  |          | Literal percent sign.                                                      |
+
+It is possible to override the default padding behavior of numeric specifiers `%?`.
+This is not allowed for other specifiers and will result in the `BAD_FORMAT` error.
+
+Modifier | Description
+-------- | -----------
+`%-?`    | Suppresses any padding including spaces and zeroes. (e.g. `%j` = `012`, `%-j` = `12`)
+`%_?`    | Uses spaces as a padding. (e.g. `%j` = `012`, `%_j` = ` 12`)
+`%0?`    | Uses zeroes as a padding. (e.g. `%e` = ` 9`, `%0e` = `09`)
+
+Notes:
+
+[^1]: `%C`, `%y`:
+   This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively.
+
+[^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.
+
+[^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`.
+
+[^4]: `%S`:
+   It accounts for leap seconds, so `60` is possible.
+
+[^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.
+
+[^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.
+
+[^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,
+   so it always counts the number of nanoseconds since the last whole second.
+   E.g. 7ms after the last second will print `007000000`,
+   and parsing `7000000` will yield the same.
+   <br>
+   <br>
+   The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits
+   according to the precision.
+   E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`),
+   and parsing `.07`, `.070000` etc. will yield the same.
+   Note that they can print or read nothing if the fractional part is zero or
+   the next character is not `.`.
+   <br>
+   <br>
+   The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits
+   according to the number preceding `f`.
+   E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`),
+   and parsing `.07`, `.070000` etc. will yield the same.
+   Note that they can read nothing if the fractional part is zero or
+   the next character is not `.` however will print with the specified length.
+   <br>
+   <br>
+   The variant `%3f`, `%6f` and `%9f` are left-aligned and print 3, 6 or 9 fractional digits
+   according to the number preceding `f`, but without the leading dot.
+   E.g. 70ms after the last second under `%3f` will print `070` (note: not `07`),
+   and parsing `07`, `070000` etc. will yield the same.
+   Note that they can read nothing if the fractional part is zero.
+
+[^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.
+   <br>
+   <br>
+   It is not possible to reliably convert from an abbreviation to an offset,
+   for example CDT can mean either Central Daylight Time (North America) or
+   China Daylight Time.
+*/
+
+#[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};
+
+#[cfg(feature = "unstable-locales")]
+type Fmt<'a> = Vec<Item<'a>>;
+#[cfg(not(feature = "unstable-locales"))]
+type Fmt<'a> = &'static [Item<'static>];
+
+static D_FMT: &[Item<'static>] =
+    &[num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)];
+static D_T_FMT: &[Item<'static>] = &[
+    fix!(ShortWeekdayName),
+    sp!(" "),
+    fix!(ShortMonthName),
+    sp!(" "),
+    nums!(Day),
+    sp!(" "),
+    num0!(Hour),
+    lit!(":"),
+    num0!(Minute),
+    lit!(":"),
+    num0!(Second),
+    sp!(" "),
+    num0!(Year),
+];
+static T_FMT: &[Item<'static>] = &[num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)];
+
+/// Parsing iterator for `strftime`-like format strings.
+#[derive(Clone, Debug)]
+pub struct StrftimeItems<'a> {
+    /// Remaining portion of the string.
+    remainder: &'a str,
+    /// If the current specifier is composed of multiple formatting items (e.g. `%+`),
+    /// parser refers to the statically reconstructed slice of them.
+    /// If `recons` is not empty they have to be returned earlier than the `remainder`.
+    recons: Fmt<'a>,
+    /// Date format
+    d_fmt: Fmt<'a>,
+    /// Date and time format
+    d_t_fmt: Fmt<'a>,
+    /// Time format
+    t_fmt: Fmt<'a>,
+}
+
+impl<'a> StrftimeItems<'a> {
+    /// Creates a new parsing iterator from the `strftime`-like format string.
+    pub fn new(s: &'a str) -> StrftimeItems<'a> {
+        Self::with_remainer(s)
+    }
+
+    /// 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_t_fmt, t_fmt }
+    }
+
+    #[cfg(not(feature = "unstable-locales"))]
+    fn with_remainer(s: &'a str) -> StrftimeItems<'a> {
+        static FMT_NONE: &[Item<'static>; 0] = &[];
+
+        StrftimeItems {
+            remainder: s,
+            recons: FMT_NONE,
+            d_fmt: D_FMT,
+            d_t_fmt: D_T_FMT,
+            t_fmt: T_FMT,
+        }
+    }
+
+    #[cfg(feature = "unstable-locales")]
+    fn with_remainer(s: &'a str) -> StrftimeItems<'a> {
+        StrftimeItems {
+            remainder: s,
+            recons: Vec::new(),
+            d_fmt: D_FMT.to_vec(),
+            d_t_fmt: D_T_FMT.to_vec(),
+            t_fmt: T_FMT.to_vec(),
+        }
+    }
+}
+
+const HAVE_ALTERNATES: &str = "z";
+
+impl<'a> Iterator for StrftimeItems<'a> {
+    type Item = Item<'a>;
+
+    fn next(&mut self) -> Option<Item<'a>> {
+        // we have some reconstructed items to return
+        if !self.recons.is_empty() {
+            let item;
+            #[cfg(feature = "unstable-locales")]
+            {
+                item = self.recons.remove(0);
+            }
+            #[cfg(not(feature = "unstable-locales"))]
+            {
+                item = self.recons[0].clone();
+                self.recons = &self.recons[1..];
+            }
+            return Some(item);
+        }
+
+        match self.remainder.chars().next() {
+            // we are done
+            None => None,
+
+            // the next item is a specifier
+            Some('%') => {
+                self.remainder = &self.remainder[1..];
+
+                macro_rules! next {
+                    () => {
+                        match self.remainder.chars().next() {
+                            Some(x) => {
+                                self.remainder = &self.remainder[x.len_utf8()..];
+                                x
+                            }
+                            None => return Some(Item::Error), // premature end of string
+                        }
+                    };
+                }
+
+                let spec = next!();
+                let pad_override = match spec {
+                    '-' => Some(Pad::None),
+                    '0' => Some(Pad::Zero),
+                    '_' => Some(Pad::Space),
+                    _ => None,
+                };
+                let is_alternate = spec == '#';
+                let spec = if pad_override.is_some() || is_alternate { next!() } else { spec };
+                if is_alternate && !HAVE_ALTERNATES.contains(spec) {
+                    return Some(Item::Error);
+                }
+
+                macro_rules! recons {
+                    [$head:expr, $($tail:expr),+ $(,)*] => ({
+                        #[cfg(feature = "unstable-locales")]
+                        {
+                            self.recons.clear();
+                            $(self.recons.push($tail);)+
+                        }
+                        #[cfg(not(feature = "unstable-locales"))]
+                        {
+                            const RECONS: &'static [Item<'static>] = &[$($tail),+];
+                            self.recons = RECONS;
+                        }
+                        $head
+                    })
+                }
+
+                macro_rules! recons_from_slice {
+                    ($slice:expr) => {{
+                        #[cfg(feature = "unstable-locales")]
+                        {
+                            self.recons.clear();
+                            self.recons.extend_from_slice(&$slice[1..]);
+                        }
+                        #[cfg(not(feature = "unstable-locales"))]
+                        {
+                            self.recons = &$slice[1..];
+                        }
+                        $slice[0].clone()
+                    }};
+                }
+
+                let item = match spec {
+                    'A' => fix!(LongWeekdayName),
+                    'B' => fix!(LongMonthName),
+                    'C' => num0!(YearDiv100),
+                    'D' => {
+                        recons![num0!(Month), lit!("/"), num0!(Day), lit!("/"), num0!(YearMod100)]
+                    }
+                    'F' => recons![num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)],
+                    'G' => num0!(IsoYear),
+                    'H' => num0!(Hour),
+                    'I' => num0!(Hour12),
+                    'M' => num0!(Minute),
+                    'P' => fix!(LowerAmPm),
+                    'R' => recons![num0!(Hour), lit!(":"), num0!(Minute)],
+                    'S' => num0!(Second),
+                    'T' => recons![num0!(Hour), lit!(":"), num0!(Minute), lit!(":"), num0!(Second)],
+                    'U' => num0!(WeekFromSun),
+                    'V' => num0!(IsoWeek),
+                    'W' => num0!(WeekFromMon),
+                    'X' => recons_from_slice!(self.t_fmt),
+                    'Y' => num0!(Year),
+                    'Z' => fix!(TimezoneName),
+                    'a' => fix!(ShortWeekdayName),
+                    'b' | 'h' => fix!(ShortMonthName),
+                    'c' => recons_from_slice!(self.d_t_fmt),
+                    'd' => num0!(Day),
+                    'e' => nums!(Day),
+                    'f' => num0!(Nanosecond),
+                    'g' => num0!(IsoYearMod100),
+                    'j' => num0!(Ordinal),
+                    'k' => nums!(Hour),
+                    'l' => nums!(Hour12),
+                    'm' => num0!(Month),
+                    'n' => sp!("\n"),
+                    'p' => fix!(UpperAmPm),
+                    'r' => recons![
+                        num0!(Hour12),
+                        lit!(":"),
+                        num0!(Minute),
+                        lit!(":"),
+                        num0!(Second),
+                        sp!(" "),
+                        fix!(UpperAmPm)
+                    ],
+                    's' => num!(Timestamp),
+                    't' => sp!("\t"),
+                    'u' => num!(WeekdayFromMon),
+                    'v' => {
+                        recons![nums!(Day), lit!("-"), fix!(ShortMonthName), lit!("-"), num0!(Year)]
+                    }
+                    'w' => num!(NumDaysFromSun),
+                    'x' => recons_from_slice!(self.d_fmt),
+                    'y' => num0!(YearMod100),
+                    'z' => {
+                        if is_alternate {
+                            internal_fix!(TimezoneOffsetPermissive)
+                        } else {
+                            fix!(TimezoneOffset)
+                        }
+                    }
+                    '+' => fix!(RFC3339),
+                    ':' => {
+                        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),
+                            _ => Item::Error,
+                        },
+                        '6' => match next!() {
+                            'f' => fix!(Nanosecond6),
+                            _ => Item::Error,
+                        },
+                        '9' => match next!() {
+                            'f' => fix!(Nanosecond9),
+                            _ => Item::Error,
+                        },
+                        'f' => fix!(Nanosecond),
+                        _ => Item::Error,
+                    },
+                    '3' => match next!() {
+                        'f' => internal_fix!(Nanosecond3NoDot),
+                        _ => Item::Error,
+                    },
+                    '6' => match next!() {
+                        'f' => internal_fix!(Nanosecond6NoDot),
+                        _ => Item::Error,
+                    },
+                    '9' => match next!() {
+                        'f' => internal_fix!(Nanosecond9NoDot),
+                        _ => Item::Error,
+                    },
+                    '%' => lit!("%"),
+                    _ => Item::Error, // no such specifier
+                };
+
+                // adjust `item` if we have any padding modifier
+                if let Some(new_pad) = pad_override {
+                    match item {
+                        Item::Numeric(ref kind, _pad) if self.recons.is_empty() => {
+                            Some(Item::Numeric(kind.clone(), new_pad))
+                        }
+                        _ => Some(Item::Error), // no reconstructed or non-numeric item allowed
+                    }
+                } else {
+                    Some(item)
+                }
+            }
+
+            // the next item is space
+            Some(c) if c.is_whitespace() => {
+                // `%` is not a whitespace, so `c != '%'` is redundant
+                let nextspec = self
+                    .remainder
+                    .find(|c: char| !c.is_whitespace())
+                    .unwrap_or(self.remainder.len());
+                assert!(nextspec > 0);
+                let item = sp!(&self.remainder[..nextspec]);
+                self.remainder = &self.remainder[nextspec..];
+                Some(item)
+            }
+
+            // the next item is literal
+            _ => {
+                let nextspec = self
+                    .remainder
+                    .find(|c: char| c.is_whitespace() || c == '%')
+                    .unwrap_or(self.remainder.len());
+                assert!(nextspec > 0);
+                let item = lit!(&self.remainder[..nextspec]);
+                self.remainder = &self.remainder[nextspec..];
+                Some(item)
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+#[test]
+fn test_strftime_items() {
+    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_else(|| vec![Item::Error])
+    }
+
+    assert_eq!(parse_and_collect(""), []);
+    assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]);
+    assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]);
+    assert_eq!(
+        parse_and_collect("a  b\t\nc"),
+        [lit!("a"), sp!("  "), lit!("b"), sp!("\t\n"), lit!("c")]
+    );
+    assert_eq!(parse_and_collect("100%%"), [lit!("100"), lit!("%")]);
+    assert_eq!(parse_and_collect("100%% ok"), [lit!("100"), lit!("%"), sp!(" "), lit!("ok")]);
+    assert_eq!(parse_and_collect("%%PDF-1.0"), [lit!("%"), lit!("PDF-1.0")]);
+    assert_eq!(
+        parse_and_collect("%Y-%m-%d"),
+        [num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)]
+    );
+    assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]"));
+    assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]);
+    assert_eq!(parse_and_collect("%"), [Item::Error]);
+    assert_eq!(parse_and_collect("%%"), [lit!("%")]);
+    assert_eq!(parse_and_collect("%%%"), [Item::Error]);
+    assert_eq!(parse_and_collect("%%%%"), [lit!("%"), lit!("%")]);
+    assert_eq!(parse_and_collect("foo%?"), [Item::Error]);
+    assert_eq!(parse_and_collect("bar%42"), [Item::Error]);
+    assert_eq!(parse_and_collect("quux% +"), [Item::Error]);
+    assert_eq!(parse_and_collect("%.Z"), [Item::Error]);
+    assert_eq!(parse_and_collect("%:Z"), [Item::Error]);
+    assert_eq!(parse_and_collect("%-Z"), [Item::Error]);
+    assert_eq!(parse_and_collect("%0Z"), [Item::Error]);
+    assert_eq!(parse_and_collect("%_Z"), [Item::Error]);
+    assert_eq!(parse_and_collect("%.j"), [Item::Error]);
+    assert_eq!(parse_and_collect("%:j"), [Item::Error]);
+    assert_eq!(parse_and_collect("%-j"), [num!(Ordinal)]);
+    assert_eq!(parse_and_collect("%0j"), [num0!(Ordinal)]);
+    assert_eq!(parse_and_collect("%_j"), [nums!(Ordinal)]);
+    assert_eq!(parse_and_collect("%.e"), [Item::Error]);
+    assert_eq!(parse_and_collect("%:e"), [Item::Error]);
+    assert_eq!(parse_and_collect("%-e"), [num!(Day)]);
+    assert_eq!(parse_and_collect("%0e"), [num0!(Day)]);
+    assert_eq!(parse_and_collect("%_e"), [nums!(Day)]);
+    assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]);
+    assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]);
+    assert_eq!(parse_and_collect("%#m"), [Item::Error]);
+}
+
+#[cfg(test)]
+#[test]
+fn test_strftime_docs() {
+    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");
+    assert_eq!(dt.format("%C").to_string(), "20");
+    assert_eq!(dt.format("%y").to_string(), "01");
+    assert_eq!(dt.format("%m").to_string(), "07");
+    assert_eq!(dt.format("%b").to_string(), "Jul");
+    assert_eq!(dt.format("%B").to_string(), "July");
+    assert_eq!(dt.format("%h").to_string(), "Jul");
+    assert_eq!(dt.format("%d").to_string(), "08");
+    assert_eq!(dt.format("%e").to_string(), " 8");
+    assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string());
+    assert_eq!(dt.format("%a").to_string(), "Sun");
+    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(), "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");
+    assert_eq!(dt.format("%V").to_string(), "27");
+    assert_eq!(dt.format("%j").to_string(), "189");
+    assert_eq!(dt.format("%D").to_string(), "07/08/01");
+    assert_eq!(dt.format("%x").to_string(), "07/08/01");
+    assert_eq!(dt.format("%F").to_string(), "2001-07-08");
+    assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001");
+
+    // time specifiers
+    assert_eq!(dt.format("%H").to_string(), "00");
+    assert_eq!(dt.format("%k").to_string(), " 0");
+    assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string());
+    assert_eq!(dt.format("%I").to_string(), "12");
+    assert_eq!(dt.format("%l").to_string(), "12");
+    assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string());
+    assert_eq!(dt.format("%P").to_string(), "am");
+    assert_eq!(dt.format("%p").to_string(), "AM");
+    assert_eq!(dt.format("%M").to_string(), "34");
+    assert_eq!(dt.format("%S").to_string(), "60");
+    assert_eq!(dt.format("%f").to_string(), "026490708");
+    assert_eq!(dt.format("%.f").to_string(), ".026490708");
+    assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490");
+    assert_eq!(dt.format("%.3f").to_string(), ".026");
+    assert_eq!(dt.format("%.6f").to_string(), ".026490");
+    assert_eq!(dt.format("%.9f").to_string(), ".026490708");
+    assert_eq!(dt.format("%3f").to_string(), "026");
+    assert_eq!(dt.format("%6f").to_string(), "026490");
+    assert_eq!(dt.format("%9f").to_string(), "026490708");
+    assert_eq!(dt.format("%R").to_string(), "00:34");
+    assert_eq!(dt.format("%T").to_string(), "00:34:60");
+    assert_eq!(dt.format("%X").to_string(), "00:34:60");
+    assert_eq!(dt.format("%r").to_string(), "12:34:60 AM");
+
+    // time zone specifiers
+    //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"
+    );
+    assert_eq!(dt.format("%s").to_string(), "994518299");
+
+    // special specifiers
+    assert_eq!(dt.format("%t").to_string(), "\t");
+    assert_eq!(dt.format("%n").to_string(), "\n");
+    assert_eq!(dt.format("%%").to_string(), "%");
+}
+
+#[cfg(feature = "unstable-locales")]
+#[test]
+fn test_strftime_docs_localized() {
+    use crate::{FixedOffset, NaiveDate, TimeZone};
+
+    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");
+    assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet");
+    assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui");
+    assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim");
+    assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche");
+    assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01");
+    assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01");
+    assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08");
+    assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001");
+
+    // time specifiers
+    assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), "");
+    assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), "");
+    assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34");
+    assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60");
+    assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60");
+    assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "12:34:60 ");
+
+    // date & time specifiers
+    assert_eq!(
+        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/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..861ee10
--- /dev/null
@@ -0,0 +1,527 @@
+//! # Chrono: Date and Time for Rust
+//!
+//! It aims to be a feature-complete superset of
+//! the [time](https://github.com/rust-lang-deprecated/time) library.
+//! In particular,
+//!
+//! * Chrono strictly adheres to ISO 8601.
+//! * Chrono is timezone-aware by default, with separate timezone-naive types.
+//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
+//!
+//! There were several previous attempts to bring a good date and time library to Rust,
+//! which Chrono builds upon and should acknowledge:
+//!
+//! * [Initial research on
+//!    the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
+//! * Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
+//! * Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
+//!
+//! ### 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`) that depends on the standard library for
+//! UNIX-like operating systems and the Windows API (`winapi`) for Windows.
+//!
+//! Optional features:
+//!
+//! - [`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`**](./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`**](./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`**](./offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
+//!
+//! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone.
+//!
+//! * [**`FixedOffset`**](./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`](./struct.DateTime.html#method.with_timezone) method.
+//!
+//! You can get the current date and time in the UTC time zone
+//! ([`Utc::now()`](./offset/struct.Utc.html#method.now))
+//! or in the local time zone
+//! ([`Local::now()`](./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`
+//! # let _ = utc; let _ = local;
+//! ```
+//!
+//! 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.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_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_opt(9, 10, 11).unwrap());
+//!
+//! 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.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.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;
+//! ```
+//!
+//! Various properties are available to the date and time, and can be altered individually.
+//! Most of them are defined in the traits [`Datelike`](./trait.Datelike.html) and
+//! [`Timelike`](./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_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));
+//! 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_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
+//! 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.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.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
+//!
+//! Formatting is done via the [`format`](./struct.DateTime.html#method.format) method,
+//! which format is equivalent to the familiar `strftime` format.
+//!
+//! See [`format::strftime`](./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`](./struct.DateTime.html#method.to_rfc2822) and
+//! [`to_rfc3339`](./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`:
+//!
+//! ```toml
+//! chrono = { version = "0.4", features = ["unstable-locales"] }
+//! ```
+//!
+//! The `unstable-locales` feature requires and implies at least the `alloc` feature.
+//!
+//! ```rust
+//! use chrono::prelude::*;
+//!
+//! # #[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.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 = 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:
+//!
+//! 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`](./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`](./struct.DateTime.html#method.parse_from_rfc2822)
+//!    and
+//!    [`DateTime::parse_from_rfc3339`](./struct.DateTime.html#method.parse_from_rfc3339)
+//!    are similar but for well-known formats.
+//!
+//! 3. [`Offset::datetime_from_str`](./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`](./format/index.html) module.
+//!
+//! ```rust
+//! use chrono::prelude::*;
+//!
+//! 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()));
+//! 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`](./format/strftime/index.html#specifiers)
+//! documentation for full syntax and list of specifiers.
+//!
+//! ### Conversion from and to EPOCH timestamps
+//!
+//! Use [`Utc.timestamp(seconds, nanoseconds)`](./offset/trait.TimeZone.html#method.timestamp)
+//! to construct a [`DateTime<Utc>`](./struct.DateTime.html) from a UNIX timestamp
+//! (seconds, nanoseconds that passed since January 1st 1970).
+//!
+//! Use [`DateTime.timestamp`](./struct.DateTime.html#method.timestamp) to get the timestamp (in seconds)
+//! from a [`DateTime`](./struct.DateTime.html). Additionally, you can use
+//! [`DateTime.timestamp_subsec_nanos`](./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`**](./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;
+//!
+//! # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
+//! assert_eq!(Utc::today(), Utc::now().date());
+//! assert_eq!(Local::today(), Local::now().date());
+//!
+//! assert_eq!(Utc.ymd_opt(2014, 11, 28).unwrap().weekday(), Weekday::Fri);
+//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
+//! 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");
+//! ```
+//!
+//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
+//!
+//! `DateTime` has [`date`](./struct.DateTime.html#method.date) method
+//! which returns a `Date` which represents its date component.
+//! There is also a [`time`](./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`**](./naive/struct.NaiveDate.html),
+//! [**`NaiveTime`**](./naive/struct.NaiveTime.html) and
+//! [**`NaiveDateTime`**](./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`](./struct.DateTime.html#method.naive_local) returns
+//! a view to the naive local time,
+//! and [`naive_utc`](./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.
+//! Be very careful if you really have to deal with pre-20C dates, they can be in Julian or others.
+//!
+//! Date types are limited in about +/- 262,000 years from the common epoch.
+//! Time types are limited in the nanosecond accuracy.
+//!
+//! [Leap seconds are supported in the representation but
+//! Chrono doesn't try to make use of them](./naive/struct.NaiveTime.html#leap-second-handling).
+//! (The main reason is that leap seconds are not really predictable.)
+//! Almost *every* operation over the possible leap seconds will ignore them.
+//! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
+//! 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_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)
+//! 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.
+
+#![doc(html_root_url = "https://docs.rs/chrono/latest/")]
+#![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)]
+#![cfg_attr(not(any(feature = "std", test)), no_std)]
+// 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 = "__doctest")]
+#[cfg_attr(feature = "__doctest", cfg(doctest))]
+use doc_comment::doctest;
+
+#[cfg(feature = "__doctest")]
+#[cfg_attr(feature = "__doctest", cfg(doctest))]
+doctest!("../README.md");
+
+/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
+pub mod prelude {
+    #[doc(no_inline)]
+    #[allow(deprecated)]
+    pub use crate::Date;
+    #[cfg(feature = "clock")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "clock")))]
+    #[doc(no_inline)]
+    pub use crate::Local;
+    #[cfg(feature = "unstable-locales")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
+    #[doc(no_inline)]
+    pub use crate::Locale;
+    #[doc(no_inline)]
+    pub use crate::SubsecRound;
+    #[doc(no_inline)]
+    pub use crate::{DateTime, SecondsFormat};
+    #[doc(no_inline)]
+    pub use crate::{Datelike, Month, Timelike, Weekday};
+    #[doc(no_inline)]
+    pub use crate::{FixedOffset, Utc};
+    #[doc(no_inline)]
+    pub use crate::{NaiveDate, NaiveDateTime, NaiveTime};
+    #[doc(no_inline)]
+    pub use crate::{Offset, TimeZone};
+}
+
+mod date;
+#[allow(deprecated)]
+pub use date::{Date, MAX_DATE, MIN_DATE};
+
+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};
+
+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 mod naive;
+#[doc(no_inline)]
+pub use naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime, NaiveWeek};
+
+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};
+
+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 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].
+///
+/// *Available on crate feature 'serde' only.*
+///
+/// [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::*;
+}
+
+/// MSRV 1.42
+#[cfg(test)]
+#[macro_export]
+macro_rules! matches {
+    ($expression:expr, $(|)? $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => {
+        match $expression {
+            $( $pattern )|+ $( if $guard )? => true,
+            _ => false
+        }
+    }
+}
diff --git a/src/month.rs b/src/month.rs
new file mode 100644 (file)
index 0000000..46f09d0
--- /dev/null
@@ -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/src/naive/date.rs b/src/naive/date.rs
new file mode 100644 (file)
index 0000000..64af978
--- /dev/null
@@ -0,0 +1,2997 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! ISO 8601 calendar date without timezone.
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+use core::borrow::Borrow;
+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;
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+/// L10n locales.
+#[cfg(feature = "unstable-locales")]
+use pure_rust_locales::Locale;
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+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;
+
+const MAX_YEAR: i32 = internals::MAX_YEAR;
+const MIN_YEAR: i32 = internals::MIN_YEAR;
+
+//   MAX_YEAR-12-31 minus 0000-01-01
+// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + (0001-01-01 minus 0000-01-01) - 1 day
+// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + 365 days
+// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365 days
+#[cfg(test)] // only used for testing
+const MAX_DAYS_FROM_YEAR_0: i32 =
+    MAX_YEAR * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400 + 365;
+
+//   MIN_YEAR-01-01 minus 0000-01-01
+// = (MIN_YEAR+400n+1)-01-01 minus (400n+1)-01-01
+// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - ((400n+1)-01-01 minus 0001-01-01)
+// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - 146097n days
+//
+// n is set to 1000 for convenience.
+#[cfg(test)] // only used for testing
+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
+    - 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.
+/// Also supports the conversion from ISO 8601 ordinal and week date.
+///
+/// # Calendar Date
+///
+/// The ISO 8601 **calendar date** follows the proleptic Gregorian calendar.
+/// It is like a normal civil calendar but note some slight differences:
+///
+/// * Dates before the Gregorian calendar's inception in 1582 are defined via the extrapolation.
+///   Be careful, as historical dates are often noted in the Julian calendar and others
+///   and the transition to Gregorian may differ across countries (as late as early 20C).
+///
+///   (Some example: Both Shakespeare from Britain and Cervantes from Spain seemingly died
+///   on the same calendar date---April 23, 1616---but in the different calendar.
+///   Britain used the Julian calendar at that time, so Shakespeare's death is later.)
+///
+/// * ISO 8601 calendars has the year 0, which is 1 BCE (a year before 1 CE).
+///   If you need a typical BCE/BC and CE/AD notation for year numbers,
+///   use the [`Datelike::year_ce`](../trait.Datelike.html#method.year_ce) method.
+///
+/// # Week Date
+///
+/// The ISO 8601 **week date** is a triple of year number, week number
+/// and [day of the week](../enum.Weekday.html) with the following rules:
+///
+/// * A week consists of Monday through Sunday, and is always numbered within some year.
+///   The week number ranges from 1 to 52 or 53 depending on the year.
+///
+/// * The week 1 of given year is defined as the first week containing January 4 of that year,
+///   or equivalently, the first week containing four or more days in that year.
+///
+/// * The year number in the week date may *not* correspond to the actual Gregorian year.
+///   For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015.
+///
+/// Chrono's date types default to the ISO 8601 [calendar date](#calendar-date),
+/// but [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) and
+/// [`Datelike::weekday`](../trait.Datelike.html#tymethod.weekday) methods
+/// can be used to get the corresponding week date.
+///
+/// # Ordinal Date
+///
+/// The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal").
+/// The ordinal number ranges from 1 to 365 or 366 depending on the year.
+/// The year number is the same as that of the [calendar date](#calendar-date).
+///
+/// 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).
+#[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).
+#[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 `NaiveDate::MIN` and `NaiveDate::MAX`,
+// we use a separate run-time test.
+#[test]
+fn test_date_bounds() {
+    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!(
+        NaiveDate::MIN == calculated_min,
+        "`NaiveDate::MIN` should have a year flag {:?}",
+        calculated_min.of().flags()
+    );
+    assert!(
+        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 = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds();
+    let maxsecs = maxsecs + 86401; // also take care of DateTime
+    assert!(
+        maxsecs < (1 << MAX_BITS),
+        "The entire `NaiveDate` range somehow exceeds 2^{} seconds",
+        MAX_BITS
+    );
+}
+
+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 (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
+            let Of(of) = of;
+            Some(NaiveDate { ymdf: (year << 13) | (of as DateImpl) })
+        } else {
+            None
+        }
+    }
+
+    /// Makes a new `NaiveDate` from year and packed month-day-flags, with a verification.
+    fn from_mdf(year: i32, mdf: Mdf) -> Option<NaiveDate> {
+        NaiveDate::from_of(year, mdf.to_of())
+    }
+
+    /// Makes a new `NaiveDate` from the [calendar date](#calendar-date)
+    /// (year, month and day).
+    ///
+    /// Panics on the out-of-range date, invalid month and/or day.
+    #[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")
+    }
+
+    /// Makes a new `NaiveDate` from the [calendar date](#calendar-date)
+    /// (year, month and day).
+    ///
+    /// Returns `None` on the out-of-range date, invalid month and/or day.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// let from_ymd_opt = NaiveDate::from_ymd_opt;
+    ///
+    /// assert!(from_ymd_opt(2015, 3, 14).is_some());
+    /// assert!(from_ymd_opt(2015, 0, 14).is_none());
+    /// assert!(from_ymd_opt(2015, 2, 29).is_none());
+    /// 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)?)
+    }
+
+    /// 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.
+    #[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")
+    }
+
+    /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date)
+    /// (year and day of the year).
+    ///
+    /// Returns `None` on the out-of-range date and/or invalid day of year.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// let from_yo_opt = NaiveDate::from_yo_opt;
+    ///
+    /// assert!(from_yo_opt(2015, 100).is_some());
+    /// assert!(from_yo_opt(2015, 0).is_none());
+    /// assert!(from_yo_opt(2015, 365).is_some());
+    /// assert!(from_yo_opt(2015, 366).is_none());
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` from the [ISO week date](#week-date)
+    /// (year, week number and day of the week).
+    /// The resulting `NaiveDate` may have a different year from the input year.
+    ///
+    /// Panics on the out-of-range date and/or invalid week number.
+    #[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")
+    }
+
+    /// Makes a new `NaiveDate` from the [ISO week date](#week-date)
+    /// (year, week number and day of the week).
+    /// The resulting `NaiveDate` may have a different year from the input year.
+    ///
+    /// Returns `None` on the out-of-range date and/or invalid week number.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Weekday};
+    ///
+    /// let from_ymd = NaiveDate::from_ymd;
+    /// let from_isoywd_opt = NaiveDate::from_isoywd_opt;
+    ///
+    /// assert_eq!(from_isoywd_opt(2015, 0, Weekday::Sun), None);
+    /// assert_eq!(from_isoywd_opt(2015, 10, Weekday::Sun), Some(from_ymd(2015, 3, 8)));
+    /// assert_eq!(from_isoywd_opt(2015, 30, Weekday::Mon), Some(from_ymd(2015, 7, 20)));
+    /// assert_eq!(from_isoywd_opt(2015, 60, Weekday::Mon), None);
+    ///
+    /// 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;
+    /// //           Mo Tu We Th Fr Sa Su
+    /// // 2014-W52  22 23 24 25 26 27 28    has 4+ days of new year,
+    /// // 2015-W01  29 30 31  1  2  3  4 <- so this is the first week
+    /// assert_eq!(from_isoywd_opt(2014, 52, Weekday::Sun), Some(from_ymd(2014, 12, 28)));
+    /// assert_eq!(from_isoywd_opt(2014, 53, Weekday::Mon), None);
+    /// assert_eq!(from_isoywd_opt(2015, 1, Weekday::Mon), Some(from_ymd(2014, 12, 29)));
+    ///
+    /// // 2015-W52  21 22 23 24 25 26 27    has 4+ days of old year,
+    /// // 2015-W53  28 29 30 31  1  2  3 <- so this is the last week
+    /// // 2016-W01   4  5  6  7  8  9 10
+    /// assert_eq!(from_isoywd_opt(2015, 52, Weekday::Sun), Some(from_ymd(2015, 12, 27)));
+    /// 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();
+        if 1 <= week && week <= nweeks {
+            // ordinal = week ordinal - delta
+            let weekord = week * 7 + weekday as u32;
+            let delta = flags.isoweek_delta();
+            if weekord <= delta {
+                // ordinal < 1, previous year
+                let prevflags = YearFlags::from_year(year - 1);
+                NaiveDate::from_of(
+                    year - 1,
+                    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)?)
+                } else {
+                    // ordinal > ndays, next year
+                    let nextflags = YearFlags::from_year(year + 1);
+                    NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)?)
+                }
+            }
+        } else {
+            None
+        }
+    }
+
+    /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
+    /// January 1, 1 being day 1.
+    ///
+    /// Panics if the date is out of range.
+    #[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")
+    }
+
+    /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
+    /// January 1, 1 being day 1.
+    ///
+    /// Returns `None` if the date is out of range.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// let from_ndays_opt = NaiveDate::from_num_days_from_ce_opt;
+    /// 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)));
+    /// assert_eq!(from_ndays_opt(0),            Some(from_ymd(0, 12, 31)));
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
+    /// 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)`.
+    ///
+    /// # Panics
+    ///
+    /// The resulting `NaiveDate` is guaranteed to be in `month`.  If `n` is larger than the number
+    /// 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.
+    #[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")
+    }
+
+    /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
+    /// 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`.
+    pub fn from_weekday_of_month_opt(
+        year: i32,
+        month: u32,
+        weekday: Weekday,
+        n: u8,
+    ) -> Option<NaiveDate> {
+        if n == 0 {
+            return None;
+        }
+        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)
+    }
+
+    /// Parses a string with the specified format string and returns a new `NaiveDate`.
+    /// See the [`format::strftime` module](../format/strftime/index.html)
+    /// on the supported escape sequences.
+    ///
+    /// # 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_opt(2015, 9, 5).unwrap()));
+    /// assert_eq!(parse_from_str("5sep2015", "%d%b%Y"),
+    ///            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_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_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 const fn and_time(&self, time: NaiveTime) -> NaiveDateTime {
+        NaiveDateTime::new(*self, time)
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute and second.
+    ///
+    /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here;
+    /// use `NaiveDate::and_hms_*` methods with a subsecond parameter instead.
+    ///
+    /// 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) -> NaiveDateTime {
+        self.and_hms_opt(hour, min, sec).expect("invalid time")
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute and second.
+    ///
+    /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here;
+    /// use `NaiveDate::and_hms_*_opt` methods with a subsecond parameter instead.
+    ///
+    /// Returns `None` on invalid hour, minute and/or second.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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))
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond.
+    ///
+    /// The millisecond part can exceed 1,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// 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) -> NaiveDateTime {
+        self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond.
+    ///
+    /// The millisecond part can exceed 1,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or millisecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        milli: u32,
+    ) -> Option<NaiveDateTime> {
+        NaiveTime::from_hms_milli_opt(hour, min, sec, milli).map(|time| self.and_time(time))
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond.
+    ///
+    /// The microsecond part can exceed 1,000,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// Panics on invalid hour, minute, second and/or microsecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, NaiveDateTime, Datelike, Timelike, Weekday};
+    ///
+    /// 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")
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond.
+    ///
+    /// The microsecond part can exceed 1,000,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or microsecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        micro: u32,
+    ) -> Option<NaiveDateTime> {
+        NaiveTime::from_hms_micro_opt(hour, min, sec, micro).map(|time| self.and_time(time))
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// 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) -> NaiveDateTime {
+        self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
+    }
+
+    /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or nanosecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        nano: u32,
+    ) -> Option<NaiveDateTime> {
+        NaiveTime::from_hms_nano_opt(hour, min, sec, nano).map(|time| self.and_time(time))
+    }
+
+    /// Returns the packed month-day-flags.
+    #[inline]
+    fn mdf(&self) -> Mdf {
+        self.of().to_mdf()
+    }
+
+    /// Returns the packed ordinal-flags.
+    #[inline]
+    const fn of(&self) -> Of {
+        Of((self.ymdf & 0b1_1111_1111_1111) as u32)
+    }
+
+    /// Makes a new `NaiveDate` with the packed month-day-flags changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    #[inline]
+    fn with_mdf(&self, mdf: Mdf) -> Option<NaiveDate> {
+        self.with_of(mdf.to_of())
+    }
+
+    /// Makes a new `NaiveDate` with the packed ordinal-flags changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    #[inline]
+    fn with_of(&self, of: Of) -> Option<NaiveDate> {
+        if of.valid() {
+            let Of(of) = of;
+            Some(NaiveDate { ymdf: (self.ymdf & !0b1_1111_1111_1111) | of as DateImpl })
+        } else {
+            None
+        }
+    }
+
+    /// Makes a new `NaiveDate` for the next calendar date.
+    ///
+    /// Panics when `self` is the last representable date.
+    #[deprecated(since = "0.4.23", note = "use `succ_opt()` instead")]
+    #[inline]
+    pub fn succ(&self) -> NaiveDate {
+        self.succ_opt().expect("out of bound")
+    }
+
+    /// Makes a new `NaiveDate` for the next calendar date.
+    ///
+    /// Returns `None` when `self` is the last representable date.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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))
+    }
+
+    /// Makes a new `NaiveDate` for the previous calendar date.
+    ///
+    /// Panics when `self` is the first representable date.
+    #[deprecated(since = "0.4.23", note = "use `pred_opt()` instead")]
+    #[inline]
+    pub fn pred(&self) -> NaiveDate {
+        self.pred_opt().expect("out of bound")
+    }
+
+    /// Makes a new `NaiveDate` for the previous calendar date.
+    ///
+    /// Returns `None` when `self` is the first representable date.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveDate;
+    ///
+    /// 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))
+    }
+
+    /// Adds the `days` part of given `Duration` to the current date.
+    ///
+    /// Returns `None` when it will result in overflow.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{Duration, NaiveDate};
+    ///
+    /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap();
+    /// assert_eq!(d.checked_add_signed(Duration::days(40)),
+    ///            Some(NaiveDate::from_ymd_opt(2015, 10, 15).unwrap()));
+    /// assert_eq!(d.checked_add_signed(Duration::days(-40)),
+    ///            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!(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 = (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)?)
+    }
+
+    /// Subtracts the `days` part of given `Duration` from the current date.
+    ///
+    /// Returns `None` when it will result in overflow.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{Duration, NaiveDate};
+    ///
+    /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap();
+    /// assert_eq!(d.checked_sub_signed(Duration::days(40)),
+    ///            Some(NaiveDate::from_ymd_opt(2015, 7, 27).unwrap()));
+    /// assert_eq!(d.checked_sub_signed(Duration::days(-40)),
+    ///            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!(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 = (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)?)
+    }
+
+    /// Subtracts another `NaiveDate` from the current date.
+    /// Returns a `Duration` of integral numbers.
+    ///
+    /// This does not overflow or underflow at all,
+    /// as all possible output fits in the range of `Duration`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{Duration, NaiveDate};
+    ///
+    /// let from_ymd = NaiveDate::from_ymd;
+    /// let since = NaiveDate::signed_duration_since;
+    ///
+    /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 1)), Duration::zero());
+    /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 12, 31)), Duration::days(1));
+    /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 2)), Duration::days(-1));
+    /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2013, 9, 23)), Duration::days(100));
+    /// 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();
+        let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400);
+        let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400);
+        let cycle1 = i64::from(internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal()));
+        let cycle2 = i64::from(internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal()));
+        OldDuration::days(
+            (i64::from(year1_div_400) - i64::from(year2_div_400)) * 146_097 + (cycle1 - cycle2),
+        )
+    }
+
+    /// 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.
+    ///
+    /// 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");
+    /// 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_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
+        I: Iterator<Item = B> + Clone,
+        B: Borrow<Item<'a>>,
+    {
+        DelayedFormat::new(Some(*self), None, items)
+    }
+
+    /// Formats the date 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 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_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))
+    }
+
+    /// 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
+    ///
+    /// ```
+    /// # use chrono::NaiveDate;
+    ///
+    /// let expected = [
+    ///     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_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 const fn iter_days(&self) -> NaiveDateDaysIterator {
+        NaiveDateDaysIterator { value: *self }
+    }
+
+    /// Returns an iterator that steps by weeks across all representable dates.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # use chrono::NaiveDate;
+    ///
+    /// let expected = [
+    ///     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_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 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 {
+    /// Returns the year number in the [calendar date](#calendar-date).
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the month number starting from 1.
+    ///
+    /// The return value ranges from 1 to 12.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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()
+    }
+
+    /// Returns the month number starting from 0.
+    ///
+    /// The return value ranges from 0 to 11.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the day of month starting from 1.
+    ///
+    /// The return value ranges from 1 to 31. (The last day of month differs by months.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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_opt(y, m, 1).unwrap();
+    ///
+    ///     // ...is preceded by the last day of the original month
+    ///     d.pred().day()
+    /// }
+    ///
+    /// assert_eq!(ndays_in_month(2015, 8), 31);
+    /// assert_eq!(ndays_in_month(2015, 9), 30);
+    /// 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()
+    }
+
+    /// Returns the day of month starting from 0.
+    ///
+    /// The return value ranges from 0 to 30. (The last day of month differs by months.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the day of year starting from 1.
+    ///
+    /// The return value ranges from 1 to 366. (The last day of year differs by years.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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_opt(year + 1, 1, 1).unwrap();
+    ///
+    ///     // ...is preceded by the last day of the original year
+    ///     d.pred().ordinal()
+    /// }
+    ///
+    /// assert_eq!(ndays_in_year(2015), 365);
+    /// assert_eq!(ndays_in_year(2016), 366);
+    /// 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()
+    }
+
+    /// Returns the day of year starting from 0.
+    ///
+    /// The return value ranges from 0 to 365. (The last day of year differs by years.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the day of week.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike, Weekday};
+    ///
+    /// 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()
+    }
+
+    #[inline]
+    fn iso_week(&self) -> IsoWeek {
+        isoweek::iso_week_from_yof(self.year(), self.of())
+    }
+
+    /// Makes a new `NaiveDate` with the year number changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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_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
+        let mdf = self.mdf();
+
+        // adjust the flags as needed
+        let flags = YearFlags::from_year(year);
+        let mdf = mdf.with_flags(flags);
+
+        NaiveDate::from_mdf(year, mdf)
+    }
+
+    /// Makes a new `NaiveDate` with the month number (starting from 1) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` with the month number (starting from 0) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` with the day of month (starting from 1) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` with the day of month (starting from 0) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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)?)
+    }
+
+    /// Makes a new `NaiveDate` with the day of year (starting from 1) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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_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)?)
+    }
+
+    /// Makes a new `NaiveDate` with the day of year (starting from 0) changed.
+    ///
+    /// Returns `None` when the resulting `NaiveDate` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike};
+    ///
+    /// 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_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)?)
+    }
+}
+
+/// An addition of `Duration` to `NaiveDate` discards the fractional days,
+/// rounding to the closest integral number of days towards `Duration::zero()`.
+///
+/// Panics on underflow or overflow.
+/// Use [`NaiveDate::checked_add_signed`](#method.checked_add_signed) to detect that.
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveDate};
+///
+/// let from_ymd = NaiveDate::from_ymd;
+///
+/// assert_eq!(from_ymd(2014, 1, 1) + Duration::zero(),             from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) + Duration::seconds(86399),     from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) + Duration::seconds(-86399),    from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(1),            from_ymd(2014, 1, 2));
+/// assert_eq!(from_ymd(2014, 1, 1) + Duration::days(-1),           from_ymd(2013, 12, 31));
+/// 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;
+
+    #[inline]
+    fn add(self, rhs: OldDuration) -> NaiveDate {
+        self.checked_add_signed(rhs).expect("`NaiveDate + Duration` overflowed")
+    }
+}
+
+impl AddAssign<OldDuration> for NaiveDate {
+    #[inline]
+    fn add_assign(&mut self, rhs: OldDuration) {
+        *self = self.add(rhs);
+    }
+}
+
+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`.
+///
+/// Panics on underflow or overflow.
+/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that.
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveDate};
+///
+/// let from_ymd = NaiveDate::from_ymd;
+///
+/// assert_eq!(from_ymd(2014, 1, 1) - Duration::zero(),             from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) - Duration::seconds(86399),     from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) - Duration::seconds(-86399),    from_ymd(2014, 1, 1));
+/// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(1),            from_ymd(2013, 12, 31));
+/// assert_eq!(from_ymd(2014, 1, 1) - Duration::days(-1),           from_ymd(2014, 1, 2));
+/// 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;
+
+    #[inline]
+    fn sub(self, rhs: OldDuration) -> NaiveDate {
+        self.checked_sub_signed(rhs).expect("`NaiveDate - Duration` overflowed")
+    }
+}
+
+impl SubAssign<OldDuration> for NaiveDate {
+    #[inline]
+    fn sub_assign(&mut self, rhs: OldDuration) {
+        *self = self.sub(rhs);
+    }
+}
+
+/// Subtracts another `NaiveDate` from the current date.
+/// Returns a `Duration` of integral numbers.
+///
+/// This does not overflow or underflow at all,
+/// as all possible output fits in the range of `Duration`.
+///
+/// The implementation is a wrapper around
+/// [`NaiveDate::signed_duration_since`](#method.signed_duration_since).
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveDate};
+///
+/// let from_ymd = NaiveDate::from_ymd;
+///
+/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 1), Duration::zero());
+/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 12, 31), Duration::days(1));
+/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 2), Duration::days(-1));
+/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 9, 23), Duration::days(100));
+/// 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;
+
+    #[inline]
+    fn sub(self, rhs: NaiveDate) -> OldDuration {
+        self.signed_duration_since(rhs)
+    }
+}
+
+/// Iterator over `NaiveDate` with a step size of one day.
+#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
+pub struct NaiveDateDaysIterator {
+    value: NaiveDate,
+}
+
+impl Iterator for NaiveDateDaysIterator {
+    type Item = NaiveDate;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.value == NaiveDate::MAX {
+            return None;
+        }
+        // current < NaiveDate::MAX from here on:
+        let current = self.value;
+        // 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 = 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,
+}
+
+impl Iterator for NaiveDateWeeksIterator {
+    type Item = NaiveDate;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if NaiveDate::MAX - self.value < OldDuration::weeks(1) {
+            return None;
+        }
+        let current = self.value;
+        self.value = current + OldDuration::weeks(1);
+        Some(current)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        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
+
+/// The `Debug` output of the naive date `d` is the same as
+/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
+///
+/// The string printed can be readily parsed via the `parse` method on `str`.
+///
+/// # Example
+///
+/// ```
+/// use chrono::NaiveDate;
+///
+/// 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_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..=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}", year)?;
+        }
+
+        f.write_char('-')?;
+        write_hundreds(f, mdf.month() as u8)?;
+        f.write_char('-')?;
+        write_hundreds(f, mdf.day() as u8)
+    }
+}
+
+/// The `Display` output of the naive date `d` is the same as
+/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
+///
+/// The string printed can be readily parsed via the `parse` method on `str`.
+///
+/// # Example
+///
+/// ```
+/// use chrono::NaiveDate;
+///
+/// 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_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)
+    }
+}
+
+/// Parsing a `str` into a `NaiveDate` uses the same format,
+/// [`%Y-%m-%d`](../format/strftime/index.html), as in `Debug` and `Display`.
+///
+/// # Example
+///
+/// ```
+/// use chrono::NaiveDate;
+///
+/// let d = NaiveDate::from_ymd_opt(2015, 9, 18).unwrap();
+/// assert_eq!("2015-09-18".parse::<NaiveDate>(), Ok(d));
+///
+/// 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: &[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(""),
+        ];
+
+        let mut parsed = Parsed::new();
+        parse(&mut parsed, s, ITEMS.iter())?;
+        parsed.to_naive_date()
+    }
+}
+
+/// 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_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")))]
+fn test_decodable_json<F, E>(from_str: F)
+where
+    F: Fn(&str) -> Result<NaiveDate, E>,
+    E: ::std::fmt::Debug,
+{
+    use std::{i32, i64};
+
+    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());
+    assert!(from_str(r#""20001231""#).is_err());
+    assert!(from_str(r#""2000-00-00""#).is_err());
+    assert!(from_str(r#""2000-02-30""#).is_err());
+    assert!(from_str(r#""2001-02-29""#).is_err());
+    assert!(from_str(r#""2002-002-28""#).is_err());
+    assert!(from_str(r#""yyyy-mm-dd""#).is_err());
+    assert!(from_str(r#"0"#).is_err());
+    assert!(from_str(r#"20.01"#).is_err());
+    assert!(from_str(&i32::MIN.to_string()).is_err());
+    assert!(from_str(&i32::MAX.to_string()).is_err());
+    assert!(from_str(&i64::MIN.to_string()).is_err());
+    assert!(from_str(&i64::MAX.to_string()).is_err());
+    assert!(from_str(r#"{}"#).is_err());
+    // pre-0.3.0 rustc-serialize format is now invalid
+    assert!(from_str(r#"{"ymdf":20}"#).is_err());
+    assert!(from_str(r#"null"#).is_err());
+}
+
+#[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};
+
+    impl Encodable for NaiveDate {
+        fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+            format!("{:?}", self).encode(s)
+        }
+    }
+
+    impl Decodable for NaiveDate {
+        fn decode<D: Decoder>(d: &mut D) -> Result<NaiveDate, D::Error> {
+            d.read_str()?.parse().map_err(|_| d.error("invalid date"))
+        }
+    }
+
+    #[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")]
+#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
+mod serde {
+    use super::NaiveDate;
+    use core::fmt;
+    use serde::{de, ser};
+
+    // TODO not very optimized for space (binary formats would want something better)
+
+    impl ser::Serialize for NaiveDate {
+        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 NaiveDateVisitor;
+
+    impl<'de> de::Visitor<'de> for NaiveDateVisitor {
+        type Value = NaiveDate;
+
+        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter.write_str("a formatted date string")
+        }
+
+        #[cfg(any(feature = "std", test))]
+        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            value.parse().map_err(E::custom)
+        }
+
+        #[cfg(not(any(feature = "std", test)))]
+        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 NaiveDate {
+        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+        where
+            D: de::Deserializer<'de>,
+        {
+            deserializer.deserialize_str(NaiveDateVisitor)
+        }
+    }
+
+    #[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 d = NaiveDate::from_ymd_opt(2014, 7, 24).unwrap();
+        let encoded = serialize(&d).unwrap();
+        let decoded: NaiveDate = deserialize(&encoded).unwrap();
+        assert_eq!(d, decoded);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    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 = NaiveDate::from_ymd_opt;
+
+        assert!(ymd_opt(2012, 0, 1).is_none());
+        assert!(ymd_opt(2012, 1, 1).is_some());
+        assert!(ymd_opt(2012, 2, 29).is_some());
+        assert!(ymd_opt(2014, 2, 29).is_none());
+        assert!(ymd_opt(2014, 3, 0).is_none());
+        assert!(ymd_opt(2014, 3, 1).is_some());
+        assert!(ymd_opt(2014, 3, 31).is_some());
+        assert!(ymd_opt(2014, 3, 32).is_none());
+        assert!(ymd_opt(2014, 12, 31).is_some());
+        assert!(ymd_opt(2014, 13, 1).is_none());
+    }
+
+    #[test]
+    fn test_date_from_yo() {
+        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)));
+        assert_eq!(yo_opt(2012, 2), Some(ymd(2012, 1, 2)));
+        assert_eq!(yo_opt(2012, 32), Some(ymd(2012, 2, 1)));
+        assert_eq!(yo_opt(2012, 60), Some(ymd(2012, 2, 29)));
+        assert_eq!(yo_opt(2012, 61), Some(ymd(2012, 3, 1)));
+        assert_eq!(yo_opt(2012, 100), Some(ymd(2012, 4, 9)));
+        assert_eq!(yo_opt(2012, 200), Some(ymd(2012, 7, 18)));
+        assert_eq!(yo_opt(2012, 300), Some(ymd(2012, 10, 26)));
+        assert_eq!(yo_opt(2012, 366), Some(ymd(2012, 12, 31)));
+        assert_eq!(yo_opt(2012, 367), None);
+
+        assert_eq!(yo_opt(2014, 0), None);
+        assert_eq!(yo_opt(2014, 1), Some(ymd(2014, 1, 1)));
+        assert_eq!(yo_opt(2014, 2), Some(ymd(2014, 1, 2)));
+        assert_eq!(yo_opt(2014, 32), Some(ymd(2014, 2, 1)));
+        assert_eq!(yo_opt(2014, 59), Some(ymd(2014, 2, 28)));
+        assert_eq!(yo_opt(2014, 60), Some(ymd(2014, 3, 1)));
+        assert_eq!(yo_opt(2014, 100), Some(ymd(2014, 4, 10)));
+        assert_eq!(yo_opt(2014, 200), Some(ymd(2014, 7, 19)));
+        assert_eq!(yo_opt(2014, 300), Some(ymd(2014, 10, 27)));
+        assert_eq!(yo_opt(2014, 365), Some(ymd(2014, 12, 31)));
+        assert_eq!(yo_opt(2014, 366), None);
+    }
+
+    #[test]
+    fn test_date_from_isoywd() {
+        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)));
+        assert_eq!(isoywd_opt(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4)));
+        assert_eq!(isoywd_opt(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5)));
+        assert_eq!(isoywd_opt(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11)));
+        assert_eq!(isoywd_opt(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20)));
+        assert_eq!(isoywd_opt(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26)));
+        assert_eq!(isoywd_opt(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27)));
+        assert_eq!(isoywd_opt(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2)));
+        assert_eq!(isoywd_opt(2004, 54, Weekday::Mon), None);
+
+        assert_eq!(isoywd_opt(2011, 0, Weekday::Sun), None);
+        assert_eq!(isoywd_opt(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3)));
+        assert_eq!(isoywd_opt(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9)));
+        assert_eq!(isoywd_opt(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10)));
+        assert_eq!(isoywd_opt(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16)));
+
+        assert_eq!(isoywd_opt(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17)));
+        assert_eq!(isoywd_opt(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23)));
+        assert_eq!(isoywd_opt(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24)));
+        assert_eq!(isoywd_opt(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30)));
+        assert_eq!(isoywd_opt(2018, 53, Weekday::Mon), None);
+    }
+
+    #[test]
+    fn test_date_from_isoywd_and_iso_week() {
+        for year in 2000..2401 {
+            for week in 1..54 {
+                for &weekday in [
+                    Weekday::Mon,
+                    Weekday::Tue,
+                    Weekday::Wed,
+                    Weekday::Thu,
+                    Weekday::Fri,
+                    Weekday::Sat,
+                    Weekday::Sun,
+                ]
+                .iter()
+                {
+                    let d = NaiveDate::from_isoywd_opt(year, week, weekday);
+                    if let Some(d) = d {
+                        assert_eq!(d.weekday(), weekday);
+                        let w = d.iso_week();
+                        assert_eq!(w.year(), year);
+                        assert_eq!(w.week(), week);
+                    }
+                }
+            }
+        }
+
+        for year in 2000..2401 {
+            for month in 1..13 {
+                for day in 1..32 {
+                    let d = NaiveDate::from_ymd_opt(year, month, day);
+                    if let Some(d) = d {
+                        let w = d.iso_week();
+                        let d_ = NaiveDate::from_isoywd_opt(w.year(), w.week(), d.weekday());
+                        assert_eq!(d, d_.unwrap());
+                    }
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn test_date_from_num_days_from_ce() {
+        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(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 = 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_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_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_opt(year, ordinal).unwrap();
+            assert_eq!(d2.year(), year);
+            assert_eq!(d2.month(), month);
+            assert_eq!(d2.day(), day);
+            assert_eq!(d2.ordinal(), ordinal);
+
+            assert_eq!(d1, d2);
+        }
+
+        check(2012, 1, 1, 1);
+        check(2012, 1, 2, 2);
+        check(2012, 2, 1, 32);
+        check(2012, 2, 29, 60);
+        check(2012, 3, 1, 61);
+        check(2012, 4, 9, 100);
+        check(2012, 7, 18, 200);
+        check(2012, 10, 26, 300);
+        check(2012, 12, 31, 366);
+
+        check(2014, 1, 1, 1);
+        check(2014, 1, 2, 2);
+        check(2014, 2, 1, 32);
+        check(2014, 2, 28, 59);
+        check(2014, 3, 1, 60);
+        check(2014, 4, 10, 100);
+        check(2014, 7, 19, 200);
+        check(2014, 10, 27, 300);
+        check(2014, 12, 31, 365);
+    }
+
+    #[test]
+    fn test_date_weekday() {
+        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_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_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_opt(1600, 2, 29).unwrap()));
+        assert_eq!(d.with_year(1900), None);
+        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_opt(2004, 2, 29).unwrap()));
+        assert_eq!(d.with_year(i32::MAX), None);
+
+        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_opt(2000, 1, 30).unwrap()));
+        assert_eq!(d.with_month(2), None);
+        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_opt(2000, 2, 8).unwrap();
+        assert_eq!(d.with_day(0), None);
+        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_opt(2000, 5, 5).unwrap();
+        assert_eq!(d.with_ordinal(0), None);
+        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_opt(1, 1, 1).unwrap().num_days_from_ce(), 1);
+
+        for year in -9999..10001 {
+            assert_eq!(
+                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_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(NaiveDate::MAX.year(), 12, 31).succ_opt(), None);
+    }
+
+    #[test]
+    fn test_date_pred() {
+        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(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_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);
+        }
+
+        check((2014, 1, 1), Duration::zero(), Some((2014, 1, 1)));
+        check((2014, 1, 1), Duration::seconds(86399), Some((2014, 1, 1)));
+        // always round towards zero
+        check((2014, 1, 1), Duration::seconds(-86399), Some((2014, 1, 1)));
+        check((2014, 1, 1), Duration::days(1), Some((2014, 1, 2)));
+        check((2014, 1, 1), Duration::days(-1), Some((2013, 12, 31)));
+        check((2014, 1, 1), Duration::days(364), Some((2014, 12, 31)));
+        check((2014, 1, 1), Duration::days(365 * 4 + 1), Some((2018, 1, 1)));
+        check((2014, 1, 1), Duration::days(365 * 400 + 97), Some((2414, 1, 1)));
+
+        check((-7, 1, 1), Duration::days(365 * 12 + 3), Some((5, 1, 1)));
+
+        // overflow check
+        check((0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64), Some((MAX_YEAR, 12, 31)));
+        check((0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1), None);
+        check((0, 1, 1), Duration::max_value(), None);
+        check((0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64), Some((MIN_YEAR, 1, 1)));
+        check((0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1), None);
+        check((0, 1, 1), Duration::min_value(), None);
+    }
+
+    #[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_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);
+        }
+
+        check((2014, 1, 1), (2014, 1, 1), Duration::zero());
+        check((2014, 1, 2), (2014, 1, 1), Duration::days(1));
+        check((2014, 12, 31), (2014, 1, 1), Duration::days(364));
+        check((2015, 1, 3), (2014, 1, 1), Duration::days(365 + 2));
+        check((2018, 1, 1), (2014, 1, 1), Duration::days(365 * 4 + 1));
+        check((2414, 1, 1), (2014, 1, 1), Duration::days(365 * 400 + 97));
+
+        check((MAX_YEAR, 12, 31), (0, 1, 1), Duration::days(MAX_DAYS_FROM_YEAR_0 as i64));
+        check((MIN_YEAR, 1, 1), (0, 1, 1), Duration::days(MIN_DAYS_FROM_YEAR_0 as i64));
+    }
+
+    #[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 = |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));
+        date += Duration::days(30);
+        assert_eq!(date, ymd(2016, 11, 10));
+    }
+
+    #[test]
+    fn test_date_subassignment() {
+        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));
+        date -= Duration::days(2);
+        assert_eq!(date, ymd(2016, 9, 29));
+    }
+
+    #[test]
+    fn test_date_fmt() {
+        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_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_opt(1234, 5, 6).unwrap()), "1234-05-06");
+        assert_eq!(
+            format!("{:30?}", NaiveDate::from_ymd_opt(12345, 6, 7).unwrap()),
+            "+12345-06-07"
+        );
+    }
+
+    #[test]
+    fn test_date_from_str() {
+        // valid cases
+        let valid = [
+            "-0000000123456-1-2",
+            "    -123456 - 1 - 2    ",
+            "-12345-1-2",
+            "-1234-12-31",
+            "-7-6-5",
+            "350-2-28",
+            "360-02-29",
+            "0360-02-29",
+            "2015-2 -18",
+            "+70-2-18",
+            "+70000-2-18",
+            "+00007-2-18",
+        ];
+        for &s in &valid {
+            let d = match s.parse::<NaiveDate>() {
+                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::<NaiveDate>() {
+                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::<NaiveDate>().is_err());
+        assert!("x".parse::<NaiveDate>().is_err());
+        assert!("2014".parse::<NaiveDate>().is_err());
+        assert!("2014-01".parse::<NaiveDate>().is_err());
+        assert!("2014-01-00".parse::<NaiveDate>().is_err());
+        assert!("2014-13-57".parse::<NaiveDate>().is_err());
+        assert!("9999999-9-9".parse::<NaiveDate>().is_err()); // out-of-bounds
+    }
+
+    #[test]
+    fn test_date_parse_from_str() {
+        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))
+        ); // ignore time and offset
+        assert_eq!(
+            NaiveDate::parse_from_str("2015-W06-1=2015-033", "%G-W%V-%u = %Y-%j"),
+            Ok(ymd(2015, 2, 2))
+        );
+        assert_eq!(
+            NaiveDate::parse_from_str("Fri, 09 Aug 13", "%a, %d %b %y"),
+            Ok(ymd(2013, 8, 9))
+        );
+        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_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");
+        assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09");
+        assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7");
+        assert_eq!(d.format("%j").to_string(), "064"); // since 2012 is a leap year
+        assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12");
+        assert_eq!(d.format("%F").to_string(), "2012-03-04");
+        assert_eq!(d.format("%v").to_string(), " 4-Mar-2012");
+        assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t");
+
+        // non-four-digit years
+        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_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(),
+            "2008,08,52,53,01"
+        );
+        assert_eq!(
+            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_opt(-262144, 1, 3).unwrap().iter_days().rev().take(4).count(),
+            2
+        );
+    }
+
+    #[test]
+    fn test_week_iterator_limit() {
+        assert_eq!(
+            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/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs
new file mode 100644 (file)
index 0000000..ec0d842
--- /dev/null
@@ -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/src/naive/datetime/rustc_serialize.rs b/src/naive/datetime/rustc_serialize.rs
new file mode 100644 (file)
index 0000000..6e33829
--- /dev/null
@@ -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/src/naive/datetime/serde.rs b/src/naive/datetime/serde.rs
new file mode 100644 (file)
index 0000000..40695fa
--- /dev/null
@@ -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/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs
new file mode 100644 (file)
index 0000000..202bdb3
--- /dev/null
@@ -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/src/naive/internals.rs b/src/naive/internals.rs
new file mode 100644 (file)
index 0000000..05305b5
--- /dev/null
@@ -0,0 +1,816 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! The internal implementation of the calendar and ordinal date.
+//!
+//! The current implementation is optimized for determining year, month, day and day of week.
+//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
+//! which are included in every packed `NaiveDate` instance.
+//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
+//! based on the moderately-sized lookup table (~1.5KB)
+//! and the packed representation is chosen for the efficient lookup.
+//! Every internal data structure does not validate its input,
+//! 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.
+
+#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
+
+use crate::Weekday;
+use core::{fmt, i32};
+use num_integer::{div_rem, mod_floor};
+use num_traits::FromPrimitive;
+
+/// The internal date representation. This also includes the packed `Mdf` value.
+pub(super) type DateImpl = i32;
+
+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).
+///
+/// There are 14 possible classes of year in the Gregorian calendar:
+/// common and leap years starting with Monday through Sunday.
+/// The `YearFlags` stores this information into 4 bits `abbb`,
+/// 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).
+#[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,
+    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, 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, G, F,
+    E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
+    C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, 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, 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, G, F, E, DC, B, A,
+    G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
+    E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, 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, 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, G, F, E, DC, B, A, G, FE, D, C,
+    B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
+    G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, 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, 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, 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, // 400
+];
+
+static YEAR_DELTAS: [u8; 401] = [
+    0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
+    8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14,
+    15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20,
+    21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
+    25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30,
+    30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36,
+    36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42,
+    42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48,
+    48, 49, 49, 49, // 200
+    49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54,
+    54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60,
+    60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66,
+    66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72,
+    72, 73, 73, 73, // 300
+    73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78,
+    78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84,
+    84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90,
+    90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96,
+    96, 97, 97, 97, 97, // 400+1
+];
+
+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 {
+        year_mod_400 -= 1;
+        ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]);
+    } else {
+        ordinal0 -= delta;
+    }
+    (year_mod_400, ordinal0 + 1)
+}
+
+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);
+        YearFlags::from_year_mod_400(year)
+    }
+
+    #[inline]
+    pub(super) fn from_year_mod_400(year: i32) -> YearFlags {
+        YEAR_TO_FLAGS[year as usize]
+    }
+
+    #[inline]
+    pub(super) fn ndays(&self) -> u32 {
+        let YearFlags(flags) = *self;
+        366 - u32::from(flags >> 3)
+    }
+
+    #[inline]
+    pub(super) fn isoweek_delta(&self) -> u32 {
+        let YearFlags(flags) = *self;
+        let mut delta = u32::from(flags) & 0b0111;
+        if delta < 3 {
+            delta += 7;
+        }
+        delta
+    }
+
+    #[inline]
+    pub(super) const fn nisoweeks(&self) -> u32 {
+        let YearFlags(flags) = *self;
+        52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
+    }
+}
+
+impl fmt::Debug for YearFlags {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let YearFlags(flags) = *self;
+        match flags {
+            0o15 => "A".fmt(f),
+            0o05 => "AG".fmt(f),
+            0o14 => "B".fmt(f),
+            0o04 => "BA".fmt(f),
+            0o13 => "C".fmt(f),
+            0o03 => "CB".fmt(f),
+            0o12 => "D".fmt(f),
+            0o02 => "DC".fmt(f),
+            0o11 => "E".fmt(f),
+            0o01 => "ED".fmt(f),
+            0o10 => "F?".fmt(f),
+            0o00 => "FE?".fmt(f), // non-canonical
+            0o17 => "F".fmt(f),
+            0o07 => "FE".fmt(f),
+            0o16 => "G".fmt(f),
+            0o06 => "GF".fmt(f),
+            _ => write!(f, "YearFlags({})", flags),
+        }
+    }
+}
+
+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] = [
+    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
+    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
+    XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
+    XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
+    XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
+    XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
+    72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
+    72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
+    XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
+    74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
+    74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
+    XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
+    78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
+    78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
+    XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
+    80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
+    80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
+    XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
+    84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
+    84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
+    XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
+    86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
+    86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
+    XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
+    88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
+    88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
+    XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
+    92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
+    92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
+    XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
+    94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
+    94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
+    XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
+    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
+    98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
+    100, // 12
+];
+
+static OL_TO_MDL: [u8; MAX_OL as usize + 1] = [
+    0, 0, // 0
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
+    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+    66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+    66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
+    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
+    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72,
+    74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
+    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
+    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74,
+    76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
+    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
+    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78,
+    80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
+    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
+    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80,
+    82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
+    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
+    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84,
+    86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
+    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
+    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86,
+    88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
+    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
+    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88,
+    90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
+    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
+    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92,
+    94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
+    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
+    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94,
+    96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
+    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
+    98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98,
+    100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100,
+    98, // 12
+];
+
+/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
+///
+/// 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(super) struct Of(pub(crate) u32);
+
+impl Of {
+    #[inline]
+    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(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)),
+            None => Of(0),
+        }
+    }
+
+    #[inline]
+    pub(super) fn valid(&self) -> bool {
+        let Of(of) = *self;
+        let ol = of >> 3;
+        (MIN_OL..=MAX_OL).contains(&ol)
+    }
+
+    #[inline]
+    pub(super) const fn ordinal(&self) -> u32 {
+        let Of(of) = *self;
+        of >> 4
+    }
+
+    #[inline]
+    pub(super) fn with_ordinal(&self, ordinal: u32) -> Option<Of> {
+        if ordinal > 366 {
+            return None;
+        }
+
+        let Of(of) = *self;
+        Some(Of((of & 0b1111) | (ordinal << 4)))
+    }
+
+    #[inline]
+    pub(super) const fn flags(&self) -> YearFlags {
+        let Of(of) = *self;
+        YearFlags((of & 0b1111) as u8)
+    }
+
+    #[inline]
+    pub(super) fn weekday(&self) -> Weekday {
+        let Of(of) = *self;
+        Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
+    }
+
+    #[inline]
+    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(super) fn to_mdf(&self) -> Mdf {
+        Mdf::from_of(*self)
+    }
+
+    #[inline]
+    pub(super) const fn succ(&self) -> Of {
+        let Of(of) = *self;
+        Of(of + (1 << 4))
+    }
+
+    #[inline]
+    pub(super) const fn pred(&self) -> Of {
+        let Of(of) = *self;
+        Of(of - (1 << 4))
+    }
+}
+
+impl fmt::Debug for Of {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let Of(of) = *self;
+        write!(
+            f,
+            "Of(({} << 4) | {:#04o} /*{:?}*/)",
+            of >> 4,
+            of & 0b1111,
+            YearFlags((of & 0b1111) as u8)
+        )
+    }
+}
+
+/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
+///
+/// The whole bits except for the least 3 bits are referred as `Mdl`
+/// (month, day of month and leap flag),
+/// which is an index to the `MDL_TO_OL` lookup table.
+#[derive(PartialEq, PartialOrd, Copy, Clone)]
+pub(super) struct Mdf(pub(super) u32);
+
+impl Mdf {
+    #[inline]
+    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(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)),
+            None => Mdf(0),
+        }
+    }
+
+    #[cfg(test)]
+    pub(super) fn valid(&self) -> bool {
+        let Mdf(mdf) = *self;
+        let mdl = mdf >> 3;
+        match MDL_TO_OL.get(mdl as usize) {
+            Some(&v) => v >= 0,
+            None => false,
+        }
+    }
+
+    #[inline]
+    pub(super) const fn month(&self) -> u32 {
+        let Mdf(mdf) = *self;
+        mdf >> 9
+    }
+
+    #[inline]
+    pub(super) fn with_month(&self, month: u32) -> Option<Mdf> {
+        if month > 12 {
+            return None;
+        }
+
+        let Mdf(mdf) = *self;
+        Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
+    }
+
+    #[inline]
+    pub(super) const fn day(&self) -> u32 {
+        let Mdf(mdf) = *self;
+        (mdf >> 4) & 0b1_1111
+    }
+
+    #[inline]
+    pub(super) fn with_day(&self, day: u32) -> Option<Mdf> {
+        if day > 31 {
+            return None;
+        }
+
+        let Mdf(mdf) = *self;
+        Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
+    }
+
+    #[inline]
+    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(super) fn to_of(&self) -> Of {
+        Of::from_mdf(*self)
+    }
+}
+
+impl fmt::Debug for Mdf {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let Mdf(mdf) = *self;
+        write!(
+            f,
+            "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
+            mdf >> 9,
+            (mdf >> 4) & 0b1_1111,
+            mdf & 0b1111,
+            YearFlags((mdf & 0b1111) as u8)
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use num_iter::range_inclusive;
+    use std::u32;
+
+    use super::{Mdf, Of};
+    use super::{YearFlags, A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF};
+    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];
+    const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
+
+    #[test]
+    fn test_year_flags_ndays_from_year() {
+        assert_eq!(YearFlags::from_year(2014).ndays(), 365);
+        assert_eq!(YearFlags::from_year(2012).ndays(), 366);
+        assert_eq!(YearFlags::from_year(2000).ndays(), 366);
+        assert_eq!(YearFlags::from_year(1900).ndays(), 365);
+        assert_eq!(YearFlags::from_year(1600).ndays(), 366);
+        assert_eq!(YearFlags::from_year(1).ndays(), 365);
+        assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian)
+        assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE
+        assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE
+        assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE
+        assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
+        assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
+        assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
+    }
+
+    #[test]
+    fn test_year_flags_nisoweeks() {
+        assert_eq!(A.nisoweeks(), 52);
+        assert_eq!(B.nisoweeks(), 52);
+        assert_eq!(C.nisoweeks(), 52);
+        assert_eq!(D.nisoweeks(), 53);
+        assert_eq!(E.nisoweeks(), 52);
+        assert_eq!(F.nisoweeks(), 52);
+        assert_eq!(G.nisoweeks(), 52);
+        assert_eq!(AG.nisoweeks(), 52);
+        assert_eq!(BA.nisoweeks(), 52);
+        assert_eq!(CB.nisoweeks(), 52);
+        assert_eq!(DC.nisoweeks(), 53);
+        assert_eq!(ED.nisoweeks(), 53);
+        assert_eq!(FE.nisoweeks(), 52);
+        assert_eq!(GF.nisoweeks(), 52);
+    }
+
+    #[test]
+    fn test_of() {
+        fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
+            for ordinal in range_inclusive(ordinal1, ordinal2) {
+                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 {:?}",
+                    ordinal,
+                    of,
+                    if expected { "valid" } else { "invalid" },
+                    flags
+                );
+            }
+        }
+
+        for &flags in NONLEAP_FLAGS.iter() {
+            check(false, flags, 0, 0);
+            check(true, flags, 1, 365);
+            check(false, flags, 366, 1024);
+            check(false, flags, u32::MAX, u32::MAX);
+        }
+
+        for &flags in LEAP_FLAGS.iter() {
+            check(false, flags, 0, 0);
+            check(true, flags, 1, 366);
+            check(false, flags, 367, 1024);
+            check(false, flags, u32::MAX, u32::MAX);
+        }
+    }
+
+    #[test]
+    fn test_mdf_valid() {
+        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 = 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 {:?}",
+                        month,
+                        day,
+                        mdf,
+                        if expected { "valid" } else { "invalid" },
+                        flags
+                    );
+                }
+            }
+        }
+
+        for &flags in NONLEAP_FLAGS.iter() {
+            check(false, flags, 0, 0, 0, 1024);
+            check(false, flags, 0, 0, 16, 0);
+            check(true, flags, 1, 1, 1, 31);
+            check(false, flags, 1, 32, 1, 1024);
+            check(true, flags, 2, 1, 2, 28);
+            check(false, flags, 2, 29, 2, 1024);
+            check(true, flags, 3, 1, 3, 31);
+            check(false, flags, 3, 32, 3, 1024);
+            check(true, flags, 4, 1, 4, 30);
+            check(false, flags, 4, 31, 4, 1024);
+            check(true, flags, 5, 1, 5, 31);
+            check(false, flags, 5, 32, 5, 1024);
+            check(true, flags, 6, 1, 6, 30);
+            check(false, flags, 6, 31, 6, 1024);
+            check(true, flags, 7, 1, 7, 31);
+            check(false, flags, 7, 32, 7, 1024);
+            check(true, flags, 8, 1, 8, 31);
+            check(false, flags, 8, 32, 8, 1024);
+            check(true, flags, 9, 1, 9, 30);
+            check(false, flags, 9, 31, 9, 1024);
+            check(true, flags, 10, 1, 10, 31);
+            check(false, flags, 10, 32, 10, 1024);
+            check(true, flags, 11, 1, 11, 30);
+            check(false, flags, 11, 31, 11, 1024);
+            check(true, flags, 12, 1, 12, 31);
+            check(false, flags, 12, 32, 12, 1024);
+            check(false, flags, 13, 0, 16, 1024);
+            check(false, flags, u32::MAX, 0, u32::MAX, 1024);
+            check(false, flags, 0, u32::MAX, 16, u32::MAX);
+            check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
+        }
+
+        for &flags in LEAP_FLAGS.iter() {
+            check(false, flags, 0, 0, 0, 1024);
+            check(false, flags, 0, 0, 16, 0);
+            check(true, flags, 1, 1, 1, 31);
+            check(false, flags, 1, 32, 1, 1024);
+            check(true, flags, 2, 1, 2, 29);
+            check(false, flags, 2, 30, 2, 1024);
+            check(true, flags, 3, 1, 3, 31);
+            check(false, flags, 3, 32, 3, 1024);
+            check(true, flags, 4, 1, 4, 30);
+            check(false, flags, 4, 31, 4, 1024);
+            check(true, flags, 5, 1, 5, 31);
+            check(false, flags, 5, 32, 5, 1024);
+            check(true, flags, 6, 1, 6, 30);
+            check(false, flags, 6, 31, 6, 1024);
+            check(true, flags, 7, 1, 7, 31);
+            check(false, flags, 7, 32, 7, 1024);
+            check(true, flags, 8, 1, 8, 31);
+            check(false, flags, 8, 32, 8, 1024);
+            check(true, flags, 9, 1, 9, 30);
+            check(false, flags, 9, 31, 9, 1024);
+            check(true, flags, 10, 1, 10, 31);
+            check(false, flags, 10, 32, 10, 1024);
+            check(true, flags, 11, 1, 11, 30);
+            check(false, flags, 11, 31, 11, 1024);
+            check(true, flags, 12, 1, 12, 31);
+            check(false, flags, 12, 32, 12, 1024);
+            check(false, flags, 13, 0, 16, 1024);
+            check(false, flags, u32::MAX, 0, u32::MAX, 1024);
+            check(false, flags, 0, u32::MAX, 16, u32::MAX);
+            check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
+        }
+    }
+
+    #[test]
+    fn test_of_fields() {
+        for &flags in FLAGS.iter() {
+            for ordinal in range_inclusive(1u32, 366) {
+                let of = Of::new(ordinal, flags).unwrap();
+                if of.valid() {
+                    assert_eq!(of.ordinal(), ordinal);
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn test_of_with_fields() {
+        fn check(flags: YearFlags, ordinal: u32) {
+            let of = Of::new(ordinal, flags).unwrap();
+
+            for ordinal in range_inclusive(0u32, 1024) {
+                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);
+                }
+            }
+        }
+
+        for &flags in NONLEAP_FLAGS.iter() {
+            check(flags, 1);
+            check(flags, 365);
+        }
+        for &flags in LEAP_FLAGS.iter() {
+            check(flags, 1);
+            check(flags, 366);
+        }
+    }
+
+    #[test]
+    fn test_of_weekday() {
+        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).unwrap().weekday();
+            for ordinal in range_inclusive(2u32, flags.ndays()) {
+                let of = Of::new(ordinal, flags).unwrap();
+                let expected = prev.succ();
+                assert_eq!(of.weekday(), expected);
+                prev = expected;
+            }
+        }
+    }
+
+    #[test]
+    fn test_mdf_fields() {
+        for &flags in FLAGS.iter() {
+            for month in range_inclusive(1u32, 12) {
+                for day in range_inclusive(1u32, 31) {
+                    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);
+                    }
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn test_mdf_with_fields() {
+        fn check(flags: YearFlags, month: u32, day: u32) {
+            let mdf = Mdf::new(month, day, flags).unwrap();
+
+            for month in range_inclusive(0u32, 16) {
+                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);
+                }
+            }
+
+            for day in range_inclusive(0u32, 1024) {
+                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);
+                }
+            }
+        }
+
+        for &flags in NONLEAP_FLAGS.iter() {
+            check(flags, 1, 1);
+            check(flags, 1, 31);
+            check(flags, 2, 1);
+            check(flags, 2, 28);
+            check(flags, 2, 29);
+            check(flags, 12, 31);
+        }
+        for &flags in LEAP_FLAGS.iter() {
+            check(flags, 1, 1);
+            check(flags, 1, 31);
+            check(flags, 2, 1);
+            check(flags, 2, 29);
+            check(flags, 2, 30);
+            check(flags, 12, 31);
+        }
+    }
+
+    #[test]
+    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).unwrap().isoweekdate_raw();
+            assert_eq!(week, 1);
+        }
+    }
+
+    #[test]
+    fn test_of_to_mdf() {
+        for i in range_inclusive(0u32, 8192) {
+            let of = Of(i);
+            assert_eq!(of.valid(), of.to_mdf().valid());
+        }
+    }
+
+    #[test]
+    fn test_mdf_to_of() {
+        for i in range_inclusive(0u32, 8192) {
+            let mdf = Mdf(i);
+            assert_eq!(mdf.valid(), mdf.to_of().valid());
+        }
+    }
+
+    #[test]
+    fn test_of_to_mdf_to_of() {
+        for i in range_inclusive(0u32, 8192) {
+            let of = Of(i);
+            if of.valid() {
+                assert_eq!(of, of.to_mdf().to_of());
+            }
+        }
+    }
+
+    #[test]
+    fn test_mdf_to_of_to_mdf() {
+        for i in range_inclusive(0u32, 8192) {
+            let mdf = Mdf(i);
+            if mdf.valid() {
+                assert_eq!(mdf, mdf.to_of().to_mdf());
+            }
+        }
+    }
+}
diff --git a/src/naive/isoweek.rs b/src/naive/isoweek.rs
new file mode 100644 (file)
index 0000000..6a4fcfd
--- /dev/null
@@ -0,0 +1,167 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! ISO 8601 week.
+
+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),
+/// 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, 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,
+    // which year number might not match the calendar year number.
+    ywf: DateImpl, // (year << 10) | (week << 4) | flag
+}
+
+/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
+//
+// internal use only. we don't expose the public constructor for `IsoWeek` for now,
+// 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(super) fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
+    let (rawweek, _) = of.isoweekdate_raw();
+    let (year, week) = if rawweek < 1 {
+        // previous year
+        let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
+        (year - 1, prevlastweek)
+    } else {
+        let lastweek = of.flags().nisoweeks();
+        if rawweek > lastweek {
+            // next year
+            (year + 1, 1)
+        } else {
+            (year, rawweek)
+        }
+    };
+    IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | DateImpl::from(of.flags().0) }
+}
+
+impl IsoWeek {
+    /// Returns the year number for this ISO week.
+    ///
+    /// # 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_opt(2014, 12, 29).unwrap());
+    /// ```
+    #[inline]
+    pub const fn year(&self) -> i32 {
+        self.ywf >> 10
+    }
+
+    /// Returns the ISO week number starting from 1.
+    ///
+    /// The return value ranges from 1 to 53. (The last week of year differs by years.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike, Weekday};
+    ///
+    /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
+    /// assert_eq!(d.iso_week().week(), 15);
+    /// ```
+    #[inline]
+    pub const fn week(&self) -> u32 {
+        ((self.ywf >> 4) & 0x3f) as u32
+    }
+
+    /// Returns the ISO week number starting from 0.
+    ///
+    /// The return value ranges from 0 to 52. (The last week of year differs by years.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveDate, Datelike, Weekday};
+    ///
+    /// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
+    /// assert_eq!(d.iso_week().week0(), 14);
+    /// ```
+    #[inline]
+    pub const fn week0(&self) -> u32 {
+        ((self.ywf >> 4) & 0x3f) as u32 - 1
+    }
+}
+
+/// The `Debug` output of the ISO week `w` is the same as
+/// [`d.format("%G-W%V")`](../format/strftime/index.html)
+/// where `d` is any `NaiveDate` value in that week.
+///
+/// # Example
+///
+/// ```
+/// use chrono::{NaiveDate, Datelike};
+///
+/// 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_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..=9999).contains(&year) {
+            write!(f, "{:04}-W{:02}", year, week)
+        } else {
+            // ISO 8601 requires the explicit sign for out-of-range years
+            write!(f, "{:+05}-W{:02}", year, week)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::naive::{internals, NaiveDate};
+    use crate::Datelike;
+
+    #[test]
+    fn test_iso_week_extremes() {
+        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), 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), NaiveDate::MAX.format("%G-W%V").to_string());
+    }
+}
diff --git a/src/naive/mod.rs b/src/naive/mod.rs
new file mode 100644 (file)
index 0000000..c41acba
--- /dev/null
@@ -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/src/naive/time/mod.rs b/src/naive/time/mod.rs
new file mode 100644 (file)
index 0000000..1d36583
--- /dev/null
@@ -0,0 +1,1390 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! ISO 8601 time without timezone.
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+use core::borrow::Borrow;
+use core::ops::{Add, AddAssign, Sub, SubAssign};
+use core::{fmt, str};
+
+use num_integer::div_mod_floor;
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+#[cfg(any(feature = "alloc", feature = "std", test))]
+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;
+
+#[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.
+///
+/// # Leap Second Handling
+///
+/// Since 1960s, the manmade atomic clock has been so accurate that
+/// it is much more accurate than Earth's own motion.
+/// It became desirable to define the civil time in terms of the atomic clock,
+/// but that risks the desynchronization of the civil time from Earth.
+/// To account for this, the designers of the Coordinated Universal Time (UTC)
+/// made that the UTC should be kept within 0.9 seconds of the observed Earth-bound time.
+/// When the mean solar day is longer than the ideal (86,400 seconds),
+/// the error slowly accumulates and it is necessary to add a **leap second**
+/// to slow the UTC down a bit.
+/// (We may also remove a second to speed the UTC up a bit, but it never happened.)
+/// The leap second, if any, follows 23:59:59 of June 30 or December 31 in the UTC.
+///
+/// Fast forward to the 21st century,
+/// we have seen 26 leap seconds from January 1972 to December 2015.
+/// Yes, 26 seconds. Probably you can read this paragraph within 26 seconds.
+/// But those 26 seconds, and possibly more in the future, are never predictable,
+/// and whether to add a leap second or not is known only before 6 months.
+/// Internet-based clocks (via NTP) do account for known leap seconds,
+/// but the system API normally doesn't (and often can't, with no network connection)
+/// and there is no reliable way to retrieve leap second information.
+///
+/// Chrono does not try to accurately implement leap seconds; it is impossible.
+/// Rather, **it allows for leap seconds but behaves as if there are *no other* leap seconds.**
+/// Various operations will ignore any possible leap second(s)
+/// except when any of the operands were actually leap seconds.
+///
+/// If you cannot tolerate this behavior,
+/// you must use a separate `TimeZone` for the International Atomic Time (TAI).
+/// TAI is like UTC but has no leap seconds, and thus slightly differs from UTC.
+/// Chrono does not yet provide such implementation, but it is planned.
+///
+/// ## Representing Leap Seconds
+///
+/// The leap second is indicated via fractional seconds more than 1 second.
+/// This makes possible to treat a leap second as the prior non-leap second
+/// if you don't care about sub-second accuracy.
+/// You should use the proper formatting to get the raw leap second.
+///
+/// All methods accepting fractional seconds will accept such values.
+///
+/// ```
+/// use chrono::{NaiveDate, NaiveTime, Utc, TimeZone};
+///
+/// let t = NaiveTime::from_hms_milli_opt(8, 59, 59, 1_000).unwrap();
+///
+/// let dt1 = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_micro_opt(8, 59, 59, 1_000_000).unwrap();
+///
+/// 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.
+/// Practically speaking, though, 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.
+///
+/// ## Date And Time Arithmetics
+///
+/// As a concrete example, let's assume that `03:00:60` and `04:00:60` are leap seconds.
+/// In reality, of course, leap seconds are separated by at least 6 months.
+/// We will also use some intuitive concise notations for the explanation.
+///
+/// `Time + Duration`
+/// (short for [`NaiveTime::overflowing_add_signed`](#method.overflowing_add_signed)):
+///
+/// - `03:00:00 + 1s = 03:00:01`.
+/// - `03:00:59 + 60s = 03:02:00`.
+/// - `03:00:59 + 1s = 03:01:00`.
+/// - `03:00:60 + 1s = 03:01:00`.
+///   Note that the sum is identical to the previous.
+/// - `03:00:60 + 60s = 03:01:59`.
+/// - `03:00:60 + 61s = 03:02:00`.
+/// - `03:00:60.1 + 0.8s = 03:00:60.9`.
+///
+/// `Time - Duration`
+/// (short for [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed)):
+///
+/// - `03:00:00 - 1s = 02:59:59`.
+/// - `03:01:00 - 1s = 03:00:59`.
+/// - `03:01:00 - 60s = 03:00:00`.
+/// - `03:00:60 - 60s = 03:00:00`.
+///   Note that the result is identical to the previous.
+/// - `03:00:60.7 - 0.4s = 03:00:60.3`.
+/// - `03:00:60.7 - 0.9s = 03:00:59.8`.
+///
+/// `Time - Time`
+/// (short for [`NaiveTime::signed_duration_since`](#method.signed_duration_since)):
+///
+/// - `04:00:00 - 03:00:00 = 3600s`.
+/// - `03:01:00 - 03:00:00 = 60s`.
+/// - `03:00:60 - 03:00:00 = 60s`.
+///   Note that the difference is identical to the previous.
+/// - `03:00:60.6 - 03:00:59.4 = 1.2s`.
+/// - `03:01:00 - 03:00:59.8 = 0.2s`.
+/// - `03:01:00 - 03:00:60.5 = 0.5s`.
+///   Note that the difference is larger than the previous,
+///   even though the leap second clearly follows the previous whole second.
+/// - `04:00:60.9 - 03:00:60.1 =
+///   (04:00:60.9 - 04:00:00) + (04:00:00 - 03:01:00) + (03:01:00 - 03:00:60.1) =
+///   60.9s + 3540s + 0.9s = 3601.8s`.
+///
+/// In general,
+///
+/// - `Time + Duration` unconditionally equals to `Duration + Time`.
+///
+/// - `Time - Duration` unconditionally equals to `Time + (-Duration)`.
+///
+/// - `Time1 - Time2` unconditionally equals to `-(Time2 - Time1)`.
+///
+/// - Associativity does not generally hold, because
+///   `(Time + Duration1) - Duration2` no longer equals to `Time + (Duration1 - Duration2)`
+///   for two positive durations.
+///
+///     - As a special case, `(Time + Duration) - Duration` also does not equal to `Time`.
+///
+///     - If you can assume that all durations have the same sign, however,
+///       then the associativity holds:
+///       `(Time + Duration1) + Duration2` equals to `Time + (Duration1 + Duration2)`
+///       for two positive durations.
+///
+/// ## Reading And Writing Leap Seconds
+///
+/// The "typical" leap seconds on the minute boundary are
+/// correctly handled both in the formatting and parsing.
+/// 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, NaiveDate};
+///
+/// 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.
+/// They are allowed for the sake of completeness and consistency;
+/// there were several "exotic" time zone offsets with fractional minutes prior to UTC after all.
+/// 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, NaiveDate};
+///
+/// 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.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, 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.
+    ///
+    /// No [leap second](#leap-second-handling) is allowed here;
+    /// use `NaiveTime::from_hms_*` methods with a subsecond parameter instead.
+    ///
+    /// Panics on invalid hour, minute and/or second.
+    #[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")
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute and second.
+    ///
+    /// No [leap second](#leap-second-handling) is allowed here;
+    /// use `NaiveTime::from_hms_*_opt` methods with a subsecond parameter instead.
+    ///
+    /// Returns `None` on invalid hour, minute and/or second.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveTime;
+    ///
+    /// let from_hms_opt = NaiveTime::from_hms_opt;
+    ///
+    /// assert!(from_hms_opt(0, 0, 0).is_some());
+    /// assert!(from_hms_opt(23, 59, 59).is_some());
+    /// 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)
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and millisecond.
+    ///
+    /// The millisecond part can exceed 1,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Panics on invalid hour, minute, second and/or millisecond.
+    #[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")
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and millisecond.
+    ///
+    /// The millisecond part can exceed 1,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or millisecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveTime;
+    ///
+    /// let from_hmsm_opt = NaiveTime::from_hms_milli_opt;
+    ///
+    /// assert!(from_hmsm_opt(0, 0, 0, 0).is_some());
+    /// assert!(from_hmsm_opt(23, 59, 59, 999).is_some());
+    /// assert!(from_hmsm_opt(23, 59, 59, 1_999).is_some()); // a leap second after 23:59:59
+    /// assert!(from_hmsm_opt(24, 0, 0, 0).is_none());
+    /// 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
+            .checked_mul(1_000_000)
+            .and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano))
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and microsecond.
+    ///
+    /// The microsecond part can exceed 1,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Panics on invalid hour, minute, second and/or microsecond.
+    #[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")
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and microsecond.
+    ///
+    /// The microsecond part can exceed 1,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or microsecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveTime;
+    ///
+    /// let from_hmsu_opt = NaiveTime::from_hms_micro_opt;
+    ///
+    /// assert!(from_hmsu_opt(0, 0, 0, 0).is_some());
+    /// assert!(from_hmsu_opt(23, 59, 59, 999_999).is_some());
+    /// assert!(from_hmsu_opt(23, 59, 59, 1_999_999).is_some()); // a leap second after 23:59:59
+    /// assert!(from_hmsu_opt(24, 0, 0, 0).is_none());
+    /// 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))
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Panics on invalid hour, minute, second and/or nanosecond.
+    #[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")
+    }
+
+    /// Makes a new `NaiveTime` from hour, minute, second and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Returns `None` on invalid hour, minute, second and/or nanosecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveTime;
+    ///
+    /// let from_hmsn_opt = NaiveTime::from_hms_nano_opt;
+    ///
+    /// assert!(from_hmsn_opt(0, 0, 0, 0).is_some());
+    /// assert!(from_hmsn_opt(23, 59, 59, 999_999_999).is_some());
+    /// assert!(from_hmsn_opt(23, 59, 59, 1_999_999_999).is_some()); // a leap second after 23:59:59
+    /// assert!(from_hmsn_opt(24, 0, 0, 0).is_none());
+    /// 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, frac: nano })
+    }
+
+    /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Panics on invalid number of seconds and/or nanosecond.
+    #[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")
+    }
+
+    /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond.
+    ///
+    /// The nanosecond part can exceed 1,000,000,000
+    /// in order to represent the [leap second](#leap-second-handling).
+    ///
+    /// Returns `None` on invalid number of seconds and/or nanosecond.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::NaiveTime;
+    ///
+    /// let from_nsecs_opt = NaiveTime::from_num_seconds_from_midnight_opt;
+    ///
+    /// assert!(from_nsecs_opt(0, 0).is_some());
+    /// assert!(from_nsecs_opt(86399, 999_999_999).is_some());
+    /// 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, frac: nano })
+    }
+
+    /// Parses a string with the specified format string and returns a new `NaiveTime`.
+    /// See the [`format::strftime` module](../format/strftime/index.html)
+    /// on the supported escape sequences.
+    ///
+    /// # 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_opt(23, 56, 4).unwrap()));
+    /// assert_eq!(parse_from_str("pm012345.6789", "%p%I%M%S%.f"),
+    ///            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_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_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_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))?;
+        parsed.to_naive_time()
+    }
+
+    /// Adds given `Duration` to the current time,
+    /// and also returns the number of *seconds*
+    /// in the integral number of days ignored from the addition.
+    /// (We cannot return `Duration` because it is subject to overflow or underflow.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{Duration, NaiveTime};
+    ///
+    /// let from_hms = NaiveTime::from_hms;
+    ///
+    /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(11)),
+    ///            (from_hms(14, 4, 5), 0));
+    /// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(Duration::hours(23)),
+    ///            (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));
+    /// ```
+    pub fn overflowing_add_signed(&self, mut rhs: OldDuration) -> (NaiveTime, i64) {
+        let mut secs = self.secs;
+        let mut frac = self.frac;
+
+        // check if `self` is a leap second and adding `rhs` would escape that leap second.
+        // if it's the case, update `self` and `rhs` to involve no leap second;
+        // otherwise the addition immediately finishes.
+        if frac >= 1_000_000_000 {
+            let rfrac = 2_000_000_000 - frac;
+            if rhs >= OldDuration::nanoseconds(i64::from(rfrac)) {
+                rhs = rhs - OldDuration::nanoseconds(i64::from(rfrac));
+                secs += 1;
+                frac = 0;
+            } else if rhs < OldDuration::nanoseconds(-i64::from(frac)) {
+                rhs = rhs + OldDuration::nanoseconds(i64::from(frac));
+                frac = 0;
+            } else {
+                frac = (i64::from(frac) + rhs.num_nanoseconds().unwrap()) as u32;
+                debug_assert!(frac < 2_000_000_000);
+                return (NaiveTime { secs, frac }, 0);
+            }
+        }
+        debug_assert!(secs <= 86_400);
+        debug_assert!(frac < 1_000_000_000);
+
+        let rhssecs = rhs.num_seconds();
+        let rhsfrac = (rhs - OldDuration::seconds(rhssecs)).num_nanoseconds().unwrap();
+        debug_assert_eq!(OldDuration::seconds(rhssecs) + OldDuration::nanoseconds(rhsfrac), rhs);
+        let rhssecsinday = rhssecs % 86_400;
+        let mut morerhssecs = rhssecs - rhssecsinday;
+        let rhssecs = rhssecsinday as i32;
+        let rhsfrac = rhsfrac as i32;
+        debug_assert!(-86_400 < rhssecs && rhssecs < 86_400);
+        debug_assert_eq!(morerhssecs % 86_400, 0);
+        debug_assert!(-1_000_000_000 < rhsfrac && rhsfrac < 1_000_000_000);
+
+        let mut secs = secs as i32 + rhssecs;
+        let mut frac = frac as i32 + rhsfrac;
+        debug_assert!(-86_400 < secs && secs < 2 * 86_400);
+        debug_assert!(-1_000_000_000 < frac && frac < 2_000_000_000);
+
+        if frac < 0 {
+            frac += 1_000_000_000;
+            secs -= 1;
+        } else if frac >= 1_000_000_000 {
+            frac -= 1_000_000_000;
+            secs += 1;
+        }
+        debug_assert!((-86_400..2 * 86_400).contains(&secs));
+        debug_assert!((0..1_000_000_000).contains(&frac));
+
+        if secs < 0 {
+            secs += 86_400;
+            morerhssecs -= 86_400;
+        } else if secs >= 86_400 {
+            secs -= 86_400;
+            morerhssecs += 86_400;
+        }
+        debug_assert!((0..86_400).contains(&secs));
+
+        (NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs)
+    }
+
+    /// Subtracts given `Duration` from the current time,
+    /// and also returns the number of *seconds*
+    /// in the integral number of days ignored from the subtraction.
+    /// (We cannot return `Duration` because it is subject to overflow or underflow.)
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{Duration, NaiveTime};
+    ///
+    /// let from_hms = NaiveTime::from_hms;
+    ///
+    /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(2)),
+    ///            (from_hms(1, 4, 5), 0));
+    /// assert_eq!(from_hms(3, 4, 5).overflowing_sub_signed(Duration::hours(17)),
+    ///            (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);
+        (time, -rhs) // safe to negate, rhs is within +/- (2^63 / 1000)
+    }
+
+    /// Subtracts another `NaiveTime` from the current time.
+    /// Returns a `Duration` within +/- 1 day.
+    /// This does not overflow or underflow at all.
+    ///
+    /// As a part of Chrono's [leap second handling](#leap-second-handling),
+    /// the subtraction assumes that **there is no leap second ever**,
+    /// except when any of the `NaiveTime`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, NaiveTime};
+    ///
+    /// let from_hmsm = NaiveTime::from_hms_milli;
+    /// let since = NaiveTime::signed_duration_since;
+    ///
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 900)),
+    ///            Duration::zero());
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 875)),
+    ///            Duration::milliseconds(25));
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 6, 925)),
+    ///            Duration::milliseconds(975));
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 0, 900)),
+    ///            Duration::seconds(7));
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 0, 7, 900)),
+    ///            Duration::seconds(5 * 60));
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(0, 5, 7, 900)),
+    ///            Duration::seconds(3 * 3600));
+    /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(4, 5, 7, 900)),
+    ///            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.
+    ///
+    /// ```
+    /// # use chrono::{Duration, NaiveTime};
+    /// # let from_hmsm = NaiveTime::from_hms_milli;
+    /// # let since = NaiveTime::signed_duration_since;
+    /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 59, 0)),
+    ///            Duration::seconds(1));
+    /// assert_eq!(since(from_hmsm(3, 0, 59, 1_500), from_hmsm(3, 0, 59, 0)),
+    ///            Duration::milliseconds(1500));
+    /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 0, 0)),
+    ///            Duration::seconds(60));
+    /// assert_eq!(since(from_hmsm(3, 0, 0, 0), from_hmsm(2, 59, 59, 1_000)),
+    ///            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|    |
+        //     |    |    :    |    |    |    |    |    |    |    :    |    |
+        // ----+----+-----*---+----+----+----+----+----+----+-------*-+----+----
+        //          |   `rhs` |                             |    `self`
+        //          |======================================>|       |
+        //          |     |  `self.secs - rhs.secs`         |`self.frac`
+        //          |====>|   |                             |======>|
+        //      `rhs.frac`|========================================>|
+        //          |     |   |        `self - rhs`         |       |
+
+        use core::cmp::Ordering;
+
+        let secs = i64::from(self.secs) - i64::from(rhs.secs);
+        let frac = i64::from(self.frac) - i64::from(rhs.frac);
+
+        // `secs` may contain a leap second yet to be counted
+        let adjust = match self.secs.cmp(&rhs.secs) {
+            Ordering::Greater => i64::from(rhs.frac >= 1_000_000_000),
+            Ordering::Equal => 0,
+            Ordering::Less => {
+                if self.frac >= 1_000_000_000 {
+                    -1
+                } else {
+                    0
+                }
+            }
+        };
+
+        OldDuration::seconds(secs + adjust) + OldDuration::nanoseconds(frac)
+    }
+
+    /// Formats the 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::NaiveTime;
+    /// use chrono::format::strftime::StrftimeItems;
+    ///
+    /// let fmt = StrftimeItems::new("%H:%M:%S");
+    /// 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_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
+        I: Iterator<Item = B> + Clone,
+        B: Borrow<Item<'a>>,
+    {
+        DelayedFormat::new(None, Some(*self), items)
+    }
+
+    /// Formats the 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::NaiveTime;
+    ///
+    /// 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_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))
+    }
+
+    /// Returns a triple of the hour, minute and second numbers.
+    fn hms(&self) -> (u32, u32, u32) {
+        let (mins, sec) = div_mod_floor(self.secs, 60);
+        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 {
+    /// Returns the hour number from 0 to 23.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the minute number from 0 to 59.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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
+    }
+
+    /// Returns the second number from 0 to 59.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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_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
+    }
+
+    /// 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](#leap-second-handling).
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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_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
+    }
+
+    /// Makes a new `NaiveTime` with the hour number changed.
+    ///
+    /// Returns `None` when the resulting `NaiveTime` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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, ..*self })
+    }
+
+    /// Makes a new `NaiveTime` with the minute number changed.
+    ///
+    /// Returns `None` when the resulting `NaiveTime` would be invalid.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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, ..*self })
+    }
+
+    /// Makes a new `NaiveTime` with the second number changed.
+    ///
+    /// Returns `None` when the resulting `NaiveTime` would be invalid.
+    /// As with the [`second`](#method.second) method,
+    /// the input range is restricted to 0 through 59.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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, ..*self })
+    }
+
+    /// Makes a new `NaiveTime` with nanoseconds since the whole non-leap second changed.
+    ///
+    /// Returns `None` when the resulting `NaiveTime` would be invalid.
+    /// As with the [`nanosecond`](#method.nanosecond) method,
+    /// the input range can exceed 1,000,000,000 for leap seconds.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// 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_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_opt(23, 56, 4, 12_345_678).unwrap();
+    /// assert_eq!(dt.with_nanosecond(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 {
+            return None;
+        }
+        Some(NaiveTime { frac: nano, ..*self })
+    }
+
+    /// Returns the number of non-leap seconds past the last midnight.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use chrono::{NaiveTime, Timelike};
+    ///
+    /// assert_eq!(NaiveTime::from_hms_opt(1, 2, 3).unwrap().num_seconds_from_midnight(),
+    ///            3723);
+    /// 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_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!
+    }
+}
+
+/// An addition of `Duration` to `NaiveTime` wraps around and never overflows or underflows.
+/// In particular the addition ignores integral number of days.
+///
+/// As a part of Chrono's [leap second handling](#leap-second-handling),
+/// the addition assumes that **there is no leap second ever**,
+/// except when the `NaiveTime` itself represents a leap second
+/// in which case the assumption becomes that **there is exactly a single leap second ever**.
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveTime};
+///
+/// let from_hmsm = NaiveTime::from_hms_milli;
+///
+/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::zero(),                  from_hmsm(3, 5, 7, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(1),              from_hmsm(3, 5, 8, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(-1),             from_hmsm(3, 5, 6, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(60 + 4),         from_hmsm(3, 6, 11, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) + Duration::seconds(7*60*60 - 6*60), from_hmsm(9, 59, 7, 0));
+/// 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.
+///
+/// ```
+/// # 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.
+///
+/// ```
+/// # use chrono::{Duration, NaiveTime};
+/// # let from_hmsm = NaiveTime::from_hms_milli;
+/// let leap = from_hmsm(3, 5, 59, 1_300);
+/// assert_eq!(leap + Duration::zero(),             from_hmsm(3, 5, 59, 1_300));
+/// assert_eq!(leap + Duration::milliseconds(-500), from_hmsm(3, 5, 59, 800));
+/// assert_eq!(leap + Duration::milliseconds(500),  from_hmsm(3, 5, 59, 1_800));
+/// assert_eq!(leap + Duration::milliseconds(800),  from_hmsm(3, 6, 0, 100));
+/// 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;
+
+    #[inline]
+    fn add(self, rhs: OldDuration) -> NaiveTime {
+        self.overflowing_add_signed(rhs).0
+    }
+}
+
+impl AddAssign<OldDuration> for NaiveTime {
+    #[inline]
+    fn add_assign(&mut self, rhs: OldDuration) {
+        *self = self.add(rhs);
+    }
+}
+
+/// A subtraction of `Duration` from `NaiveTime` wraps around and never overflows or underflows.
+/// In particular the addition ignores integral number of days.
+/// It is the same as the addition with a negated `Duration`.
+///
+/// As a part of Chrono's [leap second handling](#leap-second-handling),
+/// the addition assumes that **there is no leap second ever**,
+/// except when the `NaiveTime` itself represents a leap second
+/// in which case the assumption becomes that **there is exactly a single leap second ever**.
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveTime};
+///
+/// let from_hmsm = NaiveTime::from_hms_milli;
+///
+/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::zero(),                  from_hmsm(3, 5, 7, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(1),              from_hmsm(3, 5, 6, 0));
+/// assert_eq!(from_hmsm(3, 5, 7, 0) - Duration::seconds(60 + 5),         from_hmsm(3, 4, 2, 0));
+/// 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.
+///
+/// ```
+/// # 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.
+///
+/// ```
+/// # use chrono::{Duration, NaiveTime};
+/// # let from_hmsm = NaiveTime::from_hms_milli;
+/// let leap = from_hmsm(3, 5, 59, 1_300);
+/// assert_eq!(leap - Duration::zero(),            from_hmsm(3, 5, 59, 1_300));
+/// assert_eq!(leap - Duration::milliseconds(200), from_hmsm(3, 5, 59, 1_100));
+/// 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;
+
+    #[inline]
+    fn sub(self, rhs: OldDuration) -> NaiveTime {
+        self.overflowing_sub_signed(rhs).0
+    }
+}
+
+impl SubAssign<OldDuration> for NaiveTime {
+    #[inline]
+    fn sub_assign(&mut self, rhs: OldDuration) {
+        *self = self.sub(rhs);
+    }
+}
+
+/// Subtracts another `NaiveTime` from the current time.
+/// Returns a `Duration` within +/- 1 day.
+/// This does not overflow or underflow at all.
+///
+/// As a part of Chrono's [leap second handling](#leap-second-handling),
+/// the subtraction assumes that **there is no leap second ever**,
+/// except when any of the `NaiveTime`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
+/// [`NaiveTime::signed_duration_since`](#method.signed_duration_since).
+///
+/// # Example
+///
+/// ```
+/// use chrono::{Duration, NaiveTime};
+///
+/// let from_hmsm = NaiveTime::from_hms_milli;
+///
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 900), Duration::zero());
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 875), Duration::milliseconds(25));
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 6, 925), Duration::milliseconds(975));
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 0, 900), Duration::seconds(7));
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 0, 7, 900), Duration::seconds(5 * 60));
+/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(0, 5, 7, 900), Duration::seconds(3 * 3600));
+/// 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.
+///
+/// ```
+/// # 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));
+/// assert_eq!(from_hmsm(3, 0, 59, 1_500) - from_hmsm(3, 0, 59, 0),
+///            Duration::milliseconds(1500));
+/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 0, 0), Duration::seconds(60));
+/// 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;
+
+    #[inline]
+    fn sub(self, rhs: NaiveTime) -> OldDuration {
+        self.signed_duration_since(rhs)
+    }
+}
+
+/// The `Debug` output of the naive time `t` is the same as
+/// [`t.format("%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::NaiveTime;
+///
+/// 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_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();
+        let (sec, nano) = if self.frac >= 1_000_000_000 {
+            (sec + 1, self.frac - 1_000_000_000)
+        } else {
+            (sec, self.frac)
+        };
+
+        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 {
+            write!(f, ".{:03}", nano / 1_000_000)
+        } else if nano % 1_000 == 0 {
+            write!(f, ".{:06}", nano / 1_000)
+        } else {
+            write!(f, ".{:09}", nano)
+        }
+    }
+}
+
+/// The `Display` output of the naive time `t` is the same as
+/// [`t.format("%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::NaiveTime;
+///
+/// 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_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)
+    }
+}
+
+/// Parsing a `str` into a `NaiveTime` uses the same format,
+/// [`%H:%M:%S%.f`](../format/strftime/index.html), as in `Debug` and `Display`.
+///
+/// # Example
+///
+/// ```
+/// use chrono::NaiveTime;
+///
+/// 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_opt(23, 56, 4, 12_345_678).unwrap();
+/// assert_eq!("23:56:4.012345678".parse::<NaiveTime>(), Ok(t));
+///
+/// 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: &[Item<'static>] = &[
+            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_time()
+    }
+}
+
+/// 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_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_opt(0, 0, 59, 1_000).unwrap()).ok(),
+        Some(r#""00:00:60""#.into())
+    );
+    assert_eq!(
+        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_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_opt(23, 59, 59, 1_999_999_999).unwrap()).ok(),
+        Some(r#""23: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<NaiveTime, E>,
+    E: ::std::fmt::Debug,
+{
+    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_opt(3, 5, 7, 98765432).unwrap())
+    );
+    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_opt(12, 34, 56, 789).unwrap())
+    );
+    assert_eq!(
+        from_str(r#""23:59:60.999999999""#).ok(),
+        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_opt(23, 59, 59, 1_999_999_999).unwrap())
+    );
+
+    // bad formats
+    assert!(from_str(r#""""#).is_err());
+    assert!(from_str(r#""000000""#).is_err());
+    assert!(from_str(r#""00:00:61""#).is_err());
+    assert!(from_str(r#""00:60:00""#).is_err());
+    assert!(from_str(r#""24:00:00""#).is_err());
+    assert!(from_str(r#""23:59:59,1""#).is_err());
+    assert!(from_str(r#""012:34:56""#).is_err());
+    assert!(from_str(r#""hh:mm:ss""#).is_err());
+    assert!(from_str(r#"0"#).is_err());
+    assert!(from_str(r#"86399"#).is_err());
+    assert!(from_str(r#"{}"#).is_err());
+    // pre-0.3.0 rustc-serialize format is now invalid
+    assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err());
+    assert!(from_str(r#"null"#).is_err());
+}
diff --git a/src/naive/time/rustc_serialize.rs b/src/naive/time/rustc_serialize.rs
new file mode 100644 (file)
index 0000000..9eaf682
--- /dev/null
@@ -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/src/naive/time/serde.rs b/src/naive/time/serde.rs
new file mode 100644 (file)
index 0000000..c7394fb
--- /dev/null
@@ -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/src/naive/time/tests.rs b/src/naive/time/tests.rs
new file mode 100644 (file)
index 0000000..62c46a2
--- /dev/null
@@ -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/src/offset/fixed.rs b/src/offset/fixed.rs
new file mode 100644 (file)
index 0000000..0989dfa
--- /dev/null
@@ -0,0 +1,284 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! The time zone which has a fixed offset from UTC.
+
+use core::fmt;
+use core::ops::{Add, Sub};
+
+use num_integer::div_mod_floor;
+#[cfg(feature = "rkyv")]
+use rkyv::{Archive, Deserialize, Serialize};
+
+use super::{LocalResult, Offset, TimeZone};
+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_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,
+}
+
+impl FixedOffset {
+    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
+    /// The negative `secs` means the Western Hemisphere.
+    ///
+    /// Panics on the out-of-bound `secs`.
+    #[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")
+    }
+
+    /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
+    /// 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 })
+        } else {
+            None
+        }
+    }
+
+    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
+    /// The negative `secs` means the Eastern Hemisphere.
+    ///
+    /// Panics on the out-of-bound `secs`.
+    #[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")
+    }
+
+    /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference.
+    /// 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 })
+        } else {
+            None
+        }
+    }
+
+    /// Returns the number of seconds to add to convert from UTC to the local time.
+    #[inline]
+    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 const fn utc_minus_local(&self) -> i32 {
+        -self.local_minus_utc
+    }
+}
+
+impl TimeZone for FixedOffset {
+    type Offset = FixedOffset;
+
+    fn from_offset(offset: &FixedOffset) -> FixedOffset {
+        *offset
+    }
+
+    fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
+        LocalResult::Single(*self)
+    }
+    fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
+        LocalResult::Single(*self)
+    }
+
+    fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset {
+        *self
+    }
+    fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset {
+        *self
+    }
+}
+
+impl Offset for FixedOffset {
+    fn fix(&self) -> FixedOffset {
+        *self
+    }
+}
+
+impl fmt::Debug for FixedOffset {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let offset = self.local_minus_utc;
+        let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) };
+        let (mins, sec) = div_mod_floor(offset, 60);
+        let (hour, min) = div_mod_floor(mins, 60);
+        if sec == 0 {
+            write!(f, "{}{:02}:{:02}", sign, hour, min)
+        } else {
+            write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec)
+        }
+    }
+}
+
+impl fmt::Display for FixedOffset {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self, f)
+    }
+}
+
+#[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.
+// this should be implemented more efficiently, but for the time being, this is generic right now.
+
+fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
+where
+    T: Timelike + Add<OldDuration, Output = T>,
+{
+    // extract and temporarily remove the fractional part and later recover it
+    let nanos = lhs.nanosecond();
+    let lhs = lhs.with_nanosecond(0).unwrap();
+    (lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
+}
+
+impl Add<FixedOffset> for NaiveTime {
+    type Output = NaiveTime;
+
+    #[inline]
+    fn add(self, rhs: FixedOffset) -> NaiveTime {
+        add_with_leapsecond(&self, rhs.local_minus_utc)
+    }
+}
+
+impl Sub<FixedOffset> for NaiveTime {
+    type Output = NaiveTime;
+
+    #[inline]
+    fn sub(self, rhs: FixedOffset) -> NaiveTime {
+        add_with_leapsecond(&self, -rhs.local_minus_utc)
+    }
+}
+
+impl Add<FixedOffset> for NaiveDateTime {
+    type Output = NaiveDateTime;
+
+    #[inline]
+    fn add(self, rhs: FixedOffset) -> NaiveDateTime {
+        add_with_leapsecond(&self, rhs.local_minus_utc)
+    }
+}
+
+impl Sub<FixedOffset> for NaiveDateTime {
+    type Output = NaiveDateTime;
+
+    #[inline]
+    fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
+        add_with_leapsecond(&self, -rhs.local_minus_utc)
+    }
+}
+
+impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
+    type Output = DateTime<Tz>;
+
+    #[inline]
+    fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
+        add_with_leapsecond(&self, rhs.local_minus_utc)
+    }
+}
+
+impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
+    type Output = DateTime<Tz>;
+
+    #[inline]
+    fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
+        add_with_leapsecond(&self, -rhs.local_minus_utc)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::FixedOffset;
+    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_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_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_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_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/src/offset/local/mod.rs b/src/offset/local/mod.rs
new file mode 100644 (file)
index 0000000..e280c78
--- /dev/null
@@ -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/src/offset/local/stub.rs b/src/offset/local/stub.rs
new file mode 100644 (file)
index 0000000..9ececd3
--- /dev/null
@@ -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/src/offset/local/tz_info/mod.rs b/src/offset/local/tz_info/mod.rs
new file mode 100644 (file)
index 0000000..bd2693b
--- /dev/null
@@ -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/src/offset/local/tz_info/parser.rs b/src/offset/local/tz_info/parser.rs
new file mode 100644 (file)
index 0000000..5652a0e
--- /dev/null
@@ -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/src/offset/local/tz_info/rule.rs b/src/offset/local/tz_info/rule.rs
new file mode 100644 (file)
index 0000000..7befddb
--- /dev/null
@@ -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(&current_year_dst_start_unix_time, &current_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/src/offset/local/tz_info/timezone.rs b/src/offset/local/tz_info/timezone.rs
new file mode 100644 (file)
index 0000000..8572825
--- /dev/null
@@ -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/src/offset/local/unix.rs b/src/offset/local/unix.rs
new file mode 100644 (file)
index 0000000..32aa316
--- /dev/null
@@ -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/src/offset/local/windows.rs b/src/offset/local/windows.rs
new file mode 100644 (file)
index 0000000..e641501
--- /dev/null
@@ -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/src/offset/mod.rs b/src/offset/mod.rs
new file mode 100644 (file)
index 0000000..09d0714
--- /dev/null
@@ -0,0 +1,549 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! The time zone, which calculates offsets from the local time to UTC.
+//!
+//! There are four operations provided by the `TimeZone` trait:
+//!
+//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
+//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
+//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
+//! 4. Constructing `DateTime<Tz>` objects from various offsets
+//!
+//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
+//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
+//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
+//! Technically speaking `TimeZone` has a total knowledge about given timescale,
+//! but `Offset` is used as a cache to avoid the repeated conversion
+//! and provides implementations for 1 and 3.
+//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
+
+use core::fmt;
+
+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)]
+pub enum LocalResult<T> {
+    /// Given local time representation is invalid.
+    /// This can occur when, for example, the positive timezone transition.
+    None,
+    /// Given local time representation has a single unique result.
+    Single(T),
+    /// Given local time representation has multiple results and thus ambiguous.
+    /// This can occur when, for example, the negative timezone transition.
+    Ambiguous(T /*min*/, T /*max*/),
+}
+
+impl<T> LocalResult<T> {
+    /// Returns `Some` only when the conversion result is unique, or `None` otherwise.
+    pub fn single(self) -> Option<T> {
+        match self {
+            LocalResult::Single(t) => Some(t),
+            _ => None,
+        }
+    }
+
+    /// Returns `Some` for the earliest possible conversion result, or `None` if none.
+    pub fn earliest(self) -> Option<T> {
+        match self {
+            LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
+            _ => None,
+        }
+    }
+
+    /// Returns `Some` for the latest possible conversion result, or `None` if none.
+    pub fn latest(self) -> Option<T> {
+        match self {
+            LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
+            _ => None,
+        }
+    }
+
+    /// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
+    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
+        match self {
+            LocalResult::None => LocalResult::None,
+            LocalResult::Single(v) => LocalResult::Single(f(v)),
+            LocalResult::Ambiguous(min, max) => LocalResult::Ambiguous(f(min), f(max)),
+        }
+    }
+}
+
+#[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.
+    ///
+    /// Propagates any error. Ambiguous result would be discarded.
+    #[inline]
+    pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
+        match self {
+            LocalResult::Single(d) => {
+                d.and_time(time).map_or(LocalResult::None, LocalResult::Single)
+            }
+            _ => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute and second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Propagates any error. Ambiguous result would be discarded.
+    #[inline]
+    pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
+        match self {
+            LocalResult::Single(d) => {
+                d.and_hms_opt(hour, min, sec).map_or(LocalResult::None, LocalResult::Single)
+            }
+            _ => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
+    /// The millisecond part can exceed 1,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Propagates any error. Ambiguous result would be discarded.
+    #[inline]
+    pub fn and_hms_milli_opt(
+        self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        milli: u32,
+    ) -> LocalResult<DateTime<Tz>> {
+        match self {
+            LocalResult::Single(d) => d
+                .and_hms_milli_opt(hour, min, sec, milli)
+                .map_or(LocalResult::None, LocalResult::Single),
+            _ => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
+    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Propagates any error. Ambiguous result would be discarded.
+    #[inline]
+    pub fn and_hms_micro_opt(
+        self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        micro: u32,
+    ) -> LocalResult<DateTime<Tz>> {
+        match self {
+            LocalResult::Single(d) => d
+                .and_hms_micro_opt(hour, min, sec, micro)
+                .map_or(LocalResult::None, LocalResult::Single),
+            _ => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
+    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
+    /// The offset in the current date is preserved.
+    ///
+    /// Propagates any error. Ambiguous result would be discarded.
+    #[inline]
+    pub fn and_hms_nano_opt(
+        self,
+        hour: u32,
+        min: u32,
+        sec: u32,
+        nano: u32,
+    ) -> LocalResult<DateTime<Tz>> {
+        match self {
+            LocalResult::Single(d) => d
+                .and_hms_nano_opt(hour, min, sec, nano)
+                .map_or(LocalResult::None, LocalResult::Single),
+            _ => LocalResult::None,
+        }
+    }
+}
+
+impl<T: fmt::Debug> LocalResult<T> {
+    /// Returns the single unique conversion result, or panics accordingly.
+    pub fn unwrap(self) -> T {
+        match self {
+            LocalResult::None => panic!("No such local time"),
+            LocalResult::Single(t) => t,
+            LocalResult::Ambiguous(t1, t2) => {
+                panic!("Ambiguous local time, ranging from {:?} to {:?}", t1, t2)
+            }
+        }
+    }
+}
+
+/// The offset from the local time to UTC.
+pub trait Offset: Sized + Clone + fmt::Debug {
+    /// Returns the fixed offset from UTC to the local time stored.
+    fn fix(&self) -> FixedOffset;
+}
+
+/// The time zone.
+///
+/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and
+/// [`DateTime`](../struct.DateTime.html) types.
+pub trait TimeZone: Sized + Clone {
+    /// An associated offset type.
+    /// This type is used to store the actual offset in date and time types.
+    /// 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.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// but it will propagate to the `DateTime` values constructed via this date.
+    ///
+    /// Panics on the out-of-range date, invalid month and/or day.
+    #[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()
+    }
+
+    /// 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.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// 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.
+    #[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),
+            None => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
+    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// but it will propagate to the `DateTime` values constructed via this date.
+    ///
+    /// Panics 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(&self, year: i32, ordinal: u32) -> Date<Self> {
+        self.yo_opt(year, ordinal).unwrap()
+    }
+
+    /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone.
+    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// 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),
+            None => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
+    /// the current time zone.
+    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
+    /// The resulting `Date` may have a different year from the input year.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// but it will propagate to the `DateTime` values constructed via this date.
+    ///
+    /// Panics 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(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
+        self.isoywd_opt(year, week, weekday).unwrap()
+    }
+
+    /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and
+    /// the current time zone.
+    /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
+    /// The resulting `Date` may have a different year from the input year.
+    ///
+    /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24),
+    /// 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),
+            None => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the number of non-leap seconds
+    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
+    /// and the number of nanoseconds since the last whole non-leap second.
+    ///
+    /// Panics on the out-of-range number of seconds and/or invalid nanosecond,
+    /// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
+    #[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()
+    }
+
+    /// Makes a new `DateTime` from the number of non-leap seconds
+    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
+    /// and the number of nanoseconds since the last whole non-leap second.
+    ///
+    /// 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)),
+            None => LocalResult::None,
+        }
+    }
+
+    /// Makes a new `DateTime` from the number of non-leap milliseconds
+    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
+    ///
+    /// Panics on out-of-range number of milliseconds for a non-panicking
+    /// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt).
+    #[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()
+    }
+
+    /// Makes a new `DateTime` from the number of non-leap milliseconds
+    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
+    ///
+    ///
+    /// Returns `LocalResult::None` on out-of-range number of milliseconds
+    /// and/or invalid nanosecond, otherwise always returns
+    /// `LocalResult::Single`.
+    ///
+    /// # 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 {
+            secs -= 1;
+            millis += 1000;
+        }
+        self.timestamp_opt(secs, millis as u32 * 1_000_000)
+    }
+
+    /// Makes a new `DateTime` from the number of non-leap nanoseconds
+    /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
+    ///
+    /// Unlike [`timestamp_millis`](#method.timestamp_millis), this never
+    /// panics.
+    ///
+    /// # 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 {
+            secs -= 1;
+            nanos += 1_000_000_000;
+        }
+        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 [`crate::format::strftime`] module on the
+    /// supported escape sequences.
+    ///
+    /// 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 [`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))?;
+        parsed.to_datetime_with_timezone(self)
+    }
+
+    /// Reconstructs the time zone from the offset.
+    fn from_offset(offset: &Self::Offset) -> Self;
+
+    /// Creates the offset(s) for given local `NaiveDate` if possible.
+    fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<Self::Offset>;
+
+    /// Creates the offset(s) for given local `NaiveDateTime` if possible.
+    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
+            Date::from_utc(*local, offset)
+        })
+    }
+
+    /// 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))
+    }
+
+    /// Creates the offset for given UTC `NaiveDate`. This cannot fail.
+    fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset;
+
+    /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail.
+    fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset;
+
+    /// 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))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_negative_millis() {
+        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_opt(-7000).unwrap();
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC");
+        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_opt(-7003).unwrap();
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC");
+        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_opt(-1).unwrap();
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC");
+        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_opt(-3600000).unwrap();
+        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
+
+        for (millis, expected) in &[
+            (-7000, "1969-12-31 23:59:53 UTC"),
+            (-7001, "1969-12-31 23:59:52.999 UTC"),
+            (-7003, "1969-12-31 23:59:52.997 UTC"),
+        ] {
+            match Utc.timestamp_millis_opt(*millis) {
+                LocalResult::Single(dt) => {
+                    assert_eq!(dt.to_string(), *expected);
+                }
+                e => panic!("Got {:?} instead of an okay answer", e),
+            }
+        }
+    }
+
+    #[test]
+    fn test_negative_nanos() {
+        let dt = Utc.timestamp_nanos(-1_000_000_000);
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC");
+        let dt = Utc.timestamp_nanos(-999_999_999);
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC");
+        let dt = Utc.timestamp_nanos(-1);
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC");
+        let dt = Utc.timestamp_nanos(-60_000_000_000);
+        assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC");
+        let dt = Utc.timestamp_nanos(-3_600_000_000_000);
+        assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC");
+    }
+
+    #[test]
+    fn test_nanos_never_panics() {
+        Utc.timestamp_nanos(i64::max_value());
+        Utc.timestamp_nanos(i64::default());
+        Utc.timestamp_nanos(i64::min_value());
+    }
+}
diff --git a/src/offset/utc.rs b/src/offset/utc.rs
new file mode 100644 (file)
index 0000000..cfed754
--- /dev/null
@@ -0,0 +1,125 @@
+// This is a part of Chrono.
+// See README.md and LICENSE.txt for details.
+
+//! The UTC (Coordinated Universal Time) time zone.
+
+use core::fmt;
+#[cfg(all(
+    feature = "clock",
+    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")]
+#[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).
+///
+/// Using the [`TimeZone`](./trait.TimeZone.html) methods
+/// on the UTC struct is the preferred way to construct `DateTime<Utc>`
+/// instances.
+///
+/// # 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.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 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_opt(now.as_secs() as i64, now.subsec_nanos()).unwrap();
+        DateTime::from_utc(naive, Utc)
+    }
+
+    /// 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)
+    }
+}
+
+impl TimeZone for Utc {
+    type Offset = Utc;
+
+    fn from_offset(_state: &Utc) -> Utc {
+        Utc
+    }
+
+    fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
+        LocalResult::Single(Utc)
+    }
+    fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
+        LocalResult::Single(Utc)
+    }
+
+    fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc {
+        Utc
+    }
+    fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc {
+        Utc
+    }
+}
+
+impl Offset for Utc {
+    fn fix(&self) -> FixedOffset {
+        FixedOffset::east_opt(0).unwrap()
+    }
+}
+
+impl fmt::Debug for Utc {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Z")
+    }
+}
+
+impl fmt::Display for Utc {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "UTC")
+    }
+}
diff --git a/src/oldtime.rs b/src/oldtime.rs
new file mode 100644 (file)
index 0000000..8e2b3d2
--- /dev/null
@@ -0,0 +1,765 @@
+// 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.
+
+//! Temporal quantification
+
+use core::ops::{Add, Div, Mul, Neg, Sub};
+use core::time::Duration as StdDuration;
+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.
+const NANOS_PER_MILLI: i32 = 1000_000;
+/// The number of nanoseconds in seconds.
+const NANOS_PER_SEC: i32 = 1_000_000_000;
+/// The number of microseconds per second.
+const MICROS_PER_SEC: i64 = 1000_000;
+/// The number of milliseconds per second.
+const MILLIS_PER_SEC: i64 = 1000;
+/// The number of seconds in a minute.
+const SECS_PER_MINUTE: i64 = 60;
+/// The number of seconds in an hour.
+const SECS_PER_HOUR: i64 = 3600;
+/// The number of (non-leap) seconds in days.
+const SECS_PER_DAY: i64 = 86400;
+/// The number of (non-leap) seconds in a week.
+const SECS_PER_WEEK: i64 = 604800;
+
+macro_rules! try_opt {
+    ($e:expr) => {
+        match $e {
+            Some(v) => v,
+            None => return None,
+        }
+    };
+}
+
+/// 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, 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(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(crate) const MAX: Duration = Duration {
+    secs: i64::MAX / MILLIS_PER_SEC,
+    nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI,
+};
+
+impl Duration {
+    /// Makes a new `Duration` with given number of weeks.
+    /// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
+    /// Panics when the duration is out of bounds.
+    #[inline]
+    pub fn weeks(weeks: i64) -> Duration {
+        let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
+        Duration::seconds(secs)
+    }
+
+    /// Makes a new `Duration` with given number of days.
+    /// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
+    /// Panics when the duration is out of bounds.
+    #[inline]
+    pub fn days(days: i64) -> Duration {
+        let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
+        Duration::seconds(secs)
+    }
+
+    /// Makes a new `Duration` with given number of hours.
+    /// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
+    /// Panics when the duration is out of bounds.
+    #[inline]
+    pub fn hours(hours: i64) -> Duration {
+        let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
+        Duration::seconds(secs)
+    }
+
+    /// Makes a new `Duration` with given number of minutes.
+    /// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
+    /// Panics when the duration is out of bounds.
+    #[inline]
+    pub fn minutes(minutes: i64) -> Duration {
+        let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
+        Duration::seconds(secs)
+    }
+
+    /// Makes a new `Duration` with given number of seconds.
+    /// Panics when the duration is more than `i64::MAX` seconds
+    /// or less than `i64::MIN` seconds.
+    #[inline]
+    pub fn seconds(seconds: i64) -> Duration {
+        let d = Duration { secs: seconds, nanos: 0 };
+        if d < MIN || d > MAX {
+            panic!("Duration::seconds out of bounds");
+        }
+        d
+    }
+
+    /// Makes a new `Duration` with given number of milliseconds.
+    #[inline]
+    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 }
+    }
+
+    /// Makes a new `Duration` with given number of microseconds.
+    #[inline]
+    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 }
+    }
+
+    /// Makes a new `Duration` with given number of nanoseconds.
+    #[inline]
+    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 const fn num_weeks(&self) -> i64 {
+        self.num_days() / 7
+    }
+
+    /// Returns the total number of whole days in the duration.
+    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 const fn num_hours(&self) -> i64 {
+        self.num_seconds() / SECS_PER_HOUR
+    }
+
+    /// Returns the total number of whole minutes in the duration.
+    #[inline]
+    pub const fn num_minutes(&self) -> i64 {
+        self.num_seconds() / SECS_PER_MINUTE
+    }
+
+    /// Returns the total number of whole seconds in the duration.
+    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
+        } else {
+            self.secs
+        }
+    }
+
+    /// Returns the number of nanoseconds such that
+    /// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
+    /// nanoseconds in the duration.
+    const fn nanos_mod_sec(&self) -> i32 {
+        if self.secs < 0 && self.nanos > 0 {
+            self.nanos - NANOS_PER_SEC
+        } else {
+            self.nanos
+        }
+    }
+
+    /// Returns the total number of whole milliseconds in the duration,
+    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;
+        let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
+        secs_part + nanos_part as i64
+    }
+
+    /// Returns the total number of whole microseconds in the duration,
+    /// or `None` on overflow (exceeding 2^63 microseconds in either direction).
+    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)
+    }
+
+    /// Returns the total number of whole nanoseconds in the duration,
+    /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction).
+    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)
+    }
+
+    /// Add two durations, returning `None` if overflow occurred.
+    pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
+        let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
+        let mut nanos = self.nanos + rhs.nanos;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
+            secs = try_opt!(secs.checked_add(1));
+        }
+        let d = Duration { secs: secs, nanos: nanos };
+        // Even if d is within the bounds of i64 seconds,
+        // it might still overflow i64 milliseconds.
+        if d < MIN || d > MAX {
+            None
+        } else {
+            Some(d)
+        }
+    }
+
+    /// Subtract two durations, returning `None` if overflow occurred.
+    pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
+        let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
+        let mut nanos = self.nanos - rhs.nanos;
+        if nanos < 0 {
+            nanos += NANOS_PER_SEC;
+            secs = try_opt!(secs.checked_sub(1));
+        }
+        let d = Duration { secs: secs, nanos: nanos };
+        // Even if d is within the bounds of i64 seconds,
+        // it might still overflow i64 milliseconds.
+        if d < MIN || d > MAX {
+            None
+        } else {
+            Some(d)
+        }
+    }
+
+    /// Returns the duration as an absolute (non-negative) value.
+    #[inline]
+    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 const fn min_value() -> Duration {
+        MIN
+    }
+
+    /// The maximum possible `Duration`: `i64::MAX` milliseconds.
+    #[inline]
+    pub const fn max_value() -> Duration {
+        MAX
+    }
+
+    /// A duration where the stored seconds and nanoseconds are equal to zero.
+    #[inline]
+    pub const fn zero() -> Duration {
+        Duration { secs: 0, nanos: 0 }
+    }
+
+    /// Returns `true` if the duration equals `Duration::zero()`.
+    #[inline]
+    pub const fn is_zero(&self) -> bool {
+        self.secs == 0 && self.nanos == 0
+    }
+
+    /// Creates a `time::Duration` object from `std::time::Duration`
+    ///
+    /// This function errors when original duration is larger than the maximum
+    /// value supported for this type.
+    pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
+        // We need to check secs as u64 before coercing to i64
+        if duration.as_secs() > MAX.secs as u64 {
+            return Err(OutOfRangeError(()));
+        }
+        let d = Duration { secs: duration.as_secs() as i64, nanos: duration.subsec_nanos() as i32 };
+        if d > MAX {
+            return Err(OutOfRangeError(()));
+        }
+        Ok(d)
+    }
+
+    /// Creates a `std::time::Duration` object from `time::Duration`
+    ///
+    /// This function errors when duration is less than zero. As standard
+    /// library implementation is limited to non-negative values.
+    pub fn to_std(&self) -> Result<StdDuration, OutOfRangeError> {
+        if self.secs < 0 {
+            return Err(OutOfRangeError(()));
+        }
+        Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
+    }
+}
+
+impl Neg for Duration {
+    type Output = Duration;
+
+    #[inline]
+    fn neg(self) -> Duration {
+        if self.nanos == 0 {
+            Duration { secs: -self.secs, nanos: 0 }
+        } else {
+            Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
+        }
+    }
+}
+
+impl Add for Duration {
+    type Output = Duration;
+
+    fn add(self, rhs: Duration) -> Duration {
+        let mut secs = self.secs + rhs.secs;
+        let mut nanos = self.nanos + rhs.nanos;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
+            secs += 1;
+        }
+        Duration { secs: secs, nanos: nanos }
+    }
+}
+
+impl Sub for Duration {
+    type Output = Duration;
+
+    fn sub(self, rhs: Duration) -> Duration {
+        let mut secs = self.secs - rhs.secs;
+        let mut nanos = self.nanos - rhs.nanos;
+        if nanos < 0 {
+            nanos += NANOS_PER_SEC;
+            secs -= 1;
+        }
+        Duration { secs: secs, nanos: nanos }
+    }
+}
+
+impl Mul<i32> for Duration {
+    type Output = Duration;
+
+    fn mul(self, rhs: i32) -> Duration {
+        // Multiply nanoseconds as i64, because it cannot overflow that way.
+        let total_nanos = self.nanos as i64 * rhs as i64;
+        let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
+        let secs = self.secs * rhs as i64 + extra_secs;
+        Duration { secs: secs, nanos: nanos as i32 }
+    }
+}
+
+impl Div<i32> for Duration {
+    type Output = Duration;
+
+    fn div(self, rhs: i32) -> Duration {
+        let mut secs = self.secs / rhs as i64;
+        let carry = self.secs - secs * rhs as i64;
+        let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64;
+        let mut nanos = self.nanos / rhs + extra_nanos as i32;
+        if nanos >= NANOS_PER_SEC {
+            nanos -= NANOS_PER_SEC;
+            secs += 1;
+        }
+        if nanos < 0 {
+            nanos += NANOS_PER_SEC;
+            secs -= 1;
+        }
+        Duration { secs: secs, nanos: nanos }
+    }
+}
+
+#[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.
+        let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
+
+        let days = abs.secs / SECS_PER_DAY;
+        let secs = abs.secs - days * SECS_PER_DAY;
+        let hasdate = days != 0;
+        let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
+
+        write!(f, "{}P", sign)?;
+
+        if hasdate {
+            write!(f, "{}D", days)?;
+        }
+        if hastime {
+            if abs.nanos == 0 {
+                write!(f, "T{}S", secs)?;
+            } else if abs.nanos % NANOS_PER_MILLI == 0 {
+                write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?;
+            } else if abs.nanos % NANOS_PER_MICRO == 0 {
+                write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
+            } else {
+                write!(f, "T{}.{:09}S", secs, abs.nanos)?;
+            }
+        }
+        Ok(())
+    }
+}
+
+/// Represents error when converting `Duration` to/from a standard library
+/// implementation
+///
+/// The `std::time::Duration` supports a range from zero to `u64::MAX`
+/// *seconds*, while this module supports signed range of up to
+/// `i64::MAX` of *milliseconds*.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OutOfRangeError(());
+
+impl fmt::Display for OutOfRangeError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "Source duration value is out of range for the target type")
+    }
+}
+
+#[cfg(any(feature = "std", test))]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl Error for OutOfRangeError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "out of range error"
+    }
+}
+
+// Copied from libnum
+#[inline]
+const fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
+    (div_floor_64(this, other), mod_floor_64(this, other))
+}
+
+#[inline]
+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,
+    }
+}
+
+#[inline]
+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,
+    }
+}
+
+#[inline]
+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};
+    use std::time::Duration as StdDuration;
+    use std::{i32, i64};
+
+    #[test]
+    fn test_duration() {
+        assert!(Duration::seconds(1) != Duration::zero());
+        assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
+        assert_eq!(
+            Duration::seconds(86399) + Duration::seconds(4),
+            Duration::days(1) + Duration::seconds(3)
+        );
+        assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
+        assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
+        assert_eq!(
+            Duration::days(2) + Duration::seconds(86399) + Duration::nanoseconds(1234567890),
+            Duration::days(3) + Duration::nanoseconds(234567890)
+        );
+        assert_eq!(-Duration::days(3), Duration::days(-3));
+        assert_eq!(
+            -(Duration::days(3) + Duration::seconds(70)),
+            Duration::days(-4) + Duration::seconds(86400 - 70)
+        );
+    }
+
+    #[test]
+    fn test_duration_num_days() {
+        assert_eq!(Duration::zero().num_days(), 0);
+        assert_eq!(Duration::days(1).num_days(), 1);
+        assert_eq!(Duration::days(-1).num_days(), -1);
+        assert_eq!(Duration::seconds(86399).num_days(), 0);
+        assert_eq!(Duration::seconds(86401).num_days(), 1);
+        assert_eq!(Duration::seconds(-86399).num_days(), 0);
+        assert_eq!(Duration::seconds(-86401).num_days(), -1);
+        assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
+        assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
+    }
+
+    #[test]
+    fn test_duration_num_seconds() {
+        assert_eq!(Duration::zero().num_seconds(), 0);
+        assert_eq!(Duration::seconds(1).num_seconds(), 1);
+        assert_eq!(Duration::seconds(-1).num_seconds(), -1);
+        assert_eq!(Duration::milliseconds(999).num_seconds(), 0);
+        assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
+        assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
+        assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1);
+    }
+
+    #[test]
+    fn test_duration_num_milliseconds() {
+        assert_eq!(Duration::zero().num_milliseconds(), 0);
+        assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1);
+        assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1);
+        assert_eq!(Duration::microseconds(999).num_milliseconds(), 0);
+        assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
+        assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
+        assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
+        assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX);
+        assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN);
+        assert_eq!(MAX.num_milliseconds(), i64::MAX);
+        assert_eq!(MIN.num_milliseconds(), i64::MIN);
+    }
+
+    #[test]
+    fn test_duration_num_microseconds() {
+        assert_eq!(Duration::zero().num_microseconds(), Some(0));
+        assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1));
+        assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1));
+        assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0));
+        assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
+        assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
+        assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1));
+        assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
+        assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
+        assert_eq!(MAX.num_microseconds(), None);
+        assert_eq!(MIN.num_microseconds(), None);
+
+        // overflow checks
+        const MICROS_PER_DAY: i64 = 86400_000_000;
+        assert_eq!(
+            Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(),
+            Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY)
+        );
+        assert_eq!(
+            Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(),
+            Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY)
+        );
+        assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None);
+        assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None);
+    }
+
+    #[test]
+    fn test_duration_num_nanoseconds() {
+        assert_eq!(Duration::zero().num_nanoseconds(), Some(0));
+        assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1));
+        assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1));
+        assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
+        assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
+        assert_eq!(MAX.num_nanoseconds(), None);
+        assert_eq!(MIN.num_nanoseconds(), None);
+
+        // overflow checks
+        const NANOS_PER_DAY: i64 = 86400_000_000_000;
+        assert_eq!(
+            Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(),
+            Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY)
+        );
+        assert_eq!(
+            Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(),
+            Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY)
+        );
+        assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None);
+        assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None);
+    }
+
+    #[test]
+    fn test_duration_checked_ops() {
+        assert_eq!(
+            Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)),
+            Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999))
+        );
+        assert!(Duration::milliseconds(i64::MAX)
+            .checked_add(&Duration::microseconds(1000))
+            .is_none());
+
+        assert_eq!(
+            Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)),
+            Some(Duration::milliseconds(i64::MIN))
+        );
+        assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1)).is_none());
+    }
+
+    #[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());
+        assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero());
+        assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1));
+        assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1));
+        assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1));
+        assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1));
+        assert_eq!(
+            Duration::nanoseconds(30) * 333_333_333,
+            Duration::seconds(10) - Duration::nanoseconds(10)
+        );
+        assert_eq!(
+            (Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3,
+            Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3)
+        );
+        assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3));
+        assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3));
+    }
+
+    #[test]
+    fn test_duration_div() {
+        assert_eq!(Duration::zero() / i32::MAX, Duration::zero());
+        assert_eq!(Duration::zero() / i32::MIN, Duration::zero());
+        assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789));
+        assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789));
+        assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789));
+        assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789));
+        assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333));
+        assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333));
+        assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500));
+        assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500));
+        assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500));
+        assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333));
+        assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333));
+    }
+
+    #[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");
+        assert_eq!(Duration::days(-42).to_string(), "-P42D");
+        assert_eq!(Duration::seconds(42).to_string(), "PT42S");
+        assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S");
+        assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S");
+        assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S");
+        assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), "P7DT6.543S");
+        assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S");
+        assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S");
+
+        // the format specifier should have no effect on `Duration`
+        assert_eq!(
+            format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)),
+            "P1DT2.345S"
+        );
+    }
+
+    #[test]
+    fn test_to_std() {
+        assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
+        assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0)));
+        assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
+        assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
+        assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
+        assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000)));
+        assert_eq!(Duration::seconds(-1).to_std(), Err(OutOfRangeError(())));
+        assert_eq!(Duration::milliseconds(-1).to_std(), Err(OutOfRangeError(())));
+    }
+
+    #[test]
+    fn test_from_std() {
+        assert_eq!(Ok(Duration::seconds(1)), Duration::from_std(StdDuration::new(1, 0)));
+        assert_eq!(Ok(Duration::seconds(86401)), Duration::from_std(StdDuration::new(86401, 0)));
+        assert_eq!(
+            Ok(Duration::milliseconds(123)),
+            Duration::from_std(StdDuration::new(0, 123000000))
+        );
+        assert_eq!(
+            Ok(Duration::milliseconds(123765)),
+            Duration::from_std(StdDuration::new(123, 765000000))
+        );
+        assert_eq!(Ok(Duration::nanoseconds(777)), Duration::from_std(StdDuration::new(0, 777)));
+        assert_eq!(Ok(MAX), Duration::from_std(StdDuration::new(9223372036854775, 807000000)));
+        assert_eq!(
+            Duration::from_std(StdDuration::new(9223372036854776, 0)),
+            Err(OutOfRangeError(()))
+        );
+        assert_eq!(
+            Duration::from_std(StdDuration::new(9223372036854775, 807000001)),
+            Err(OutOfRangeError(()))
+        );
+    }
+}
diff --git a/src/round.rs b/src/round.rs
new file mode 100644 (file)
index 0000000..b95f245
--- /dev/null
@@ -0,0 +1,763 @@
+// 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};
+
+/// Extension trait for subsecond rounding or truncation to a maximum number
+/// of digits. Rounding can be used to decrease the error variance when
+/// serializing/persisting to lower precision. Truncation is the default
+/// behavior in Chrono display formatting.  Either can be used to guarantee
+/// equality (e.g. for testing) when round-tripping through a lower precision
+/// format.
+pub trait SubsecRound {
+    /// Return a copy rounded to the specified number of subsecond digits. With
+    /// 9 or more digits, self is returned unmodified. Halfway values are
+    /// rounded up (away from zero).
+    ///
+    /// # Example
+    /// ``` rust
+    /// # 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);
+    /// ```
+    fn round_subsecs(self, digits: u16) -> Self;
+
+    /// Return a copy truncated to the specified number of subsecond
+    /// digits. With 9 or more digits, self is returned unmodified.
+    ///
+    /// # Example
+    /// ``` rust
+    /// # 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);
+    /// ```
+    fn trunc_subsecs(self, digits: u16) -> Self;
+}
+
+impl<T> SubsecRound for T
+where
+    T: Timelike + Add<Duration, Output = T> + Sub<Duration, Output = T>,
+{
+    fn round_subsecs(self, digits: u16) -> T {
+        let span = span_for_digits(digits);
+        let delta_down = self.nanosecond() % span;
+        if delta_down > 0 {
+            let delta_up = span - delta_down;
+            if delta_up <= delta_down {
+                self + Duration::nanoseconds(delta_up.into())
+            } else {
+                self - Duration::nanoseconds(delta_down.into())
+            }
+        } else {
+            self // unchanged
+        }
+    }
+
+    fn trunc_subsecs(self, digits: u16) -> T {
+        let span = span_for_digits(digits);
+        let delta_down = self.nanosecond() % span;
+        if delta_down > 0 {
+            self - Duration::nanoseconds(delta_down.into())
+        } else {
+            self // unchanged
+        }
+    }
+}
+
+// Return the maximum span in nanoseconds for the target number of digits.
+fn span_for_digits(digits: u16) -> u32 {
+    // fast lookup form of: 10^(9-min(9,digits))
+    match digits {
+        0 => 1_000_000_000,
+        1 => 100_000_000,
+        2 => 10_000_000,
+        3 => 1_000_000,
+        4 => 100_000,
+        5 => 10_000,
+        6 => 1_000,
+        7 => 100,
+        8 => 10,
+        _ => 1,
+    }
+}
+
+/// Extension trait for rounding or truncating a DateTime by a Duration.
+///
+/// # Limitations
+/// Both rounding and truncating are done via [`Duration::num_nanoseconds`] and
+/// [`DateTime::timestamp_nanos`]. This means that they will fail if either the
+/// `Duration` or the `DateTime` are too big to represented as nanoseconds. They
+/// will also fail if the `Duration` is bigger than the timestamp.
+pub trait DurationRound: Sized {
+    /// Error that can occur in rounding or truncating
+    #[cfg(any(feature = "std", test))]
+    type Err: std::error::Error;
+
+    /// Error that can occur in rounding or truncating
+    #[cfg(not(any(feature = "std", test)))]
+    type Err: fmt::Debug + fmt::Display;
+
+    /// Return a copy rounded by Duration.
+    ///
+    /// # Example
+    /// ``` rust
+    /// # 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"
+    /// );
+    /// assert_eq!(
+    ///     dt.duration_round(Duration::days(1)).unwrap().to_string(),
+    ///     "2018-01-12 00:00:00 UTC"
+    /// );
+    /// ```
+    fn duration_round(self, duration: Duration) -> Result<Self, Self::Err>;
+
+    /// Return a copy truncated by Duration.
+    ///
+    /// # Example
+    /// ``` rust
+    /// # 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"
+    /// );
+    /// assert_eq!(
+    ///     dt.duration_trunc(Duration::days(1)).unwrap().to_string(),
+    ///     "2018-01-11 00:00:00 UTC"
+    /// );
+    /// ```
+    fn duration_trunc(self, duration: Duration) -> Result<Self, Self::Err>;
+}
+
+/// The maximum number of seconds a DateTime can be to be represented as nanoseconds
+const MAX_SECONDS_TIMESTAMP_FOR_NANOS: i64 = 9_223_372_036;
+
+impl<Tz: TimeZone> DurationRound for DateTime<Tz> {
+    type Err = RoundingError;
+
+    fn duration_round(self, duration: Duration) -> Result<Self, Self::Err> {
+        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 {
+                Ok(original - Duration::nanoseconds(delta_down))
+            }
+        }
+    } 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)
+    }
+}
+
+/// An error from rounding by `Duration`
+///
+/// See: [`DurationRound`]
+#[derive(Debug, Clone, PartialEq, Eq, Copy)]
+pub enum RoundingError {
+    /// Error when the Duration exceeds the Duration from or until the Unix epoch.
+    ///
+    /// ``` rust
+    /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc};
+    /// let dt = Utc.with_ymd_and_hms(1970, 12, 12, 0, 0, 0).unwrap();
+    ///
+    /// assert_eq!(
+    ///     dt.duration_round(Duration::days(365)),
+    ///     Err(RoundingError::DurationExceedsTimestamp),
+    /// );
+    /// ```
+    DurationExceedsTimestamp,
+
+    /// Error when `Duration.num_nanoseconds` exceeds the limit.
+    ///
+    /// ``` rust
+    /// # 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)),
+    ///     Err(RoundingError::DurationExceedsLimit)
+    /// );
+    /// ```
+    DurationExceedsLimit,
+
+    /// Error when `DateTime.timestamp_nanos` exceeds the limit.
+    ///
+    /// ``` rust
+    /// # use chrono::{DateTime, DurationRound, Duration, RoundingError, TimeZone, Utc};
+    /// 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),);
+    /// ```
+    TimestampExceedsLimit,
+}
+
+impl fmt::Display for RoundingError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match *self {
+            RoundingError::DurationExceedsTimestamp => {
+                write!(f, "duration in nanoseconds exceeds timestamp")
+            }
+            RoundingError::DurationExceedsLimit => {
+                write!(f, "duration exceeds num_nanoseconds limit")
+            }
+            RoundingError::TimestampExceedsLimit => {
+                write!(f, "timestamp exceeds num_nanoseconds limit")
+            }
+        }
+    }
+}
+
+#[cfg(any(feature = "std", test))]
+#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
+impl std::error::Error for RoundingError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "error from rounding or truncating with DurationRound"
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{Duration, DurationRound, SubsecRound};
+    use crate::offset::{FixedOffset, TimeZone, Utc};
+    use crate::NaiveDate;
+    use crate::Timelike;
+
+    #[test]
+    fn test_round_subsecs() {
+        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(), 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
+            .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);
+        assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
+        assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);
+
+        assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
+        assert_eq!(dt.round_subsecs(0).second(), 28);
+    }
+
+    #[test]
+    fn test_round_leap_nanos() {
+        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);
+        assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
+        assert_eq!(dt.round_subsecs(1).second(), 59);
+
+        assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
+        assert_eq!(dt.round_subsecs(0).second(), 0);
+    }
+
+    #[test]
+    fn test_trunc_subsecs() {
+        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(), 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
+            .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);
+        assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
+        assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);
+
+        assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
+        assert_eq!(dt.trunc_subsecs(0).second(), 27);
+    }
+
+    #[test]
+    fn test_trunc_leap_nanos() {
+        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);
+        assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
+        assert_eq!(dt.trunc_subsecs(1).second(), 59);
+
+        assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
+        assert_eq!(dt.trunc_subsecs(0).second(), 59);
+    }
+
+    #[test]
+    fn test_duration_round() {
+        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(),
+            "2016-12-31 23:59:59.180 UTC"
+        );
+
+        // 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();
+        assert_eq!(
+            dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
+            "2012-12-12 18:25:00 UTC"
+        );
+        // 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();
+        assert_eq!(
+            dt.duration_round(Duration::minutes(5)).unwrap().to_string(),
+            "2012-12-12 18:20:00 UTC"
+        );
+
+        assert_eq!(
+            dt.duration_round(Duration::minutes(10)).unwrap().to_string(),
+            "2012-12-12 18:20:00 UTC"
+        );
+        assert_eq!(
+            dt.duration_round(Duration::minutes(30)).unwrap().to_string(),
+            "2012-12-12 18:30:00 UTC"
+        );
+        assert_eq!(
+            dt.duration_round(Duration::hours(1)).unwrap().to_string(),
+            "2012-12-12 18:00:00 UTC"
+        );
+        assert_eq!(
+            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.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"
+        );
+    }
+
+    #[test]
+    fn test_duration_trunc() {
+        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(),
+            "2016-12-31 23:59:59.170 UTC"
+        );
+
+        // 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();
+        assert_eq!(
+            dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
+            "2012-12-12 18:20:00 UTC"
+        );
+        // 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();
+        assert_eq!(
+            dt.duration_trunc(Duration::minutes(5)).unwrap().to_string(),
+            "2012-12-12 18:20:00 UTC"
+        );
+        assert_eq!(
+            dt.duration_trunc(Duration::minutes(10)).unwrap().to_string(),
+            "2012-12-12 18:20:00 UTC"
+        );
+        assert_eq!(
+            dt.duration_trunc(Duration::minutes(30)).unwrap().to_string(),
+            "2012-12-12 18:00:00 UTC"
+        );
+        assert_eq!(
+            dt.duration_trunc(Duration::hours(1)).unwrap().to_string(),
+            "2012-12-12 18:00:00 UTC"
+        );
+        assert_eq!(
+            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.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/src/traits.rs b/src/traits.rs
new file mode 100644 (file)
index 0000000..1b6af69
--- /dev/null
@@ -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/src/weekday.rs b/src/weekday.rs
new file mode 100644 (file)
index 0000000..72e3846
--- /dev/null
@@ -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/tests/dateutils.rs b/tests/dateutils.rs
new file mode 100644 (file)
index 0000000..dec6bfe
--- /dev/null
@@ -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/tests/wasm.rs b/tests/wasm.rs
new file mode 100644 (file)
index 0000000..f003d4d
--- /dev/null
@@ -0,0 +1,81 @@
+#![cfg(all(
+    target_arch = "wasm32",
+    feature = "wasmbind",
+    not(any(target_os = "emscripten", target_os = "wasi"))
+))]
+
+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();
+
+    // 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);
+
+    // 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();
+
+    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);
+}
+
+#[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);
+
+    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);
+}