feat: patch chrono
This commit is contained in:
@@ -10,9 +10,11 @@ crate-type = ['cdylib']
|
||||
|
||||
[dependencies]
|
||||
boa_engine = { version = "0.16.0", path = "external/boa/boa_engine"}
|
||||
getrandom = { version = "0.2.8", features = ["js"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[patch.crates-io]
|
||||
iana-time-zone = { path = "external/iana-time-zone" }
|
||||
getrandom = { path = "external/getrandom" }
|
||||
chrono = { path = "external/chrono" }
|
||||
|
||||
1
javascript-engine/external/chrono/.git-ignore-revs
vendored
Normal file
1
javascript-engine/external/chrono/.git-ignore-revs
vendored
Normal file
@@ -0,0 +1 @@
|
||||
febb8dc168325ac471b54591c925c48b6a485962 # cargo fmt
|
||||
3
javascript-engine/external/chrono/.github/pull_request_template.md
vendored
Normal file
3
javascript-engine/external/chrono/.github/pull_request_template.md
vendored
Normal file
@@ -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.
|
||||
31
javascript-engine/external/chrono/.github/workflows/lint.yml
vendored
Normal file
31
javascript-engine/external/chrono/.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, 0.4.x]
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.rs"
|
||||
- .github/**
|
||||
- .ci/**
|
||||
- Cargo.toml
|
||||
- deny.toml
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- run: cargo fmt -- --check --color=always
|
||||
- run: cargo clippy --color=always -- -D warnings -A clippy::manual-non-exhaustive
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
155
javascript-engine/external/chrono/.github/workflows/test.yml
vendored
Normal file
155
javascript-engine/external/chrono/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
name: All Tests and Builds
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, 0.4.x]
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.rs"
|
||||
- .github/**
|
||||
- .ci/**
|
||||
- Cargo.toml
|
||||
|
||||
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@v1
|
||||
- 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@v1
|
||||
- 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@v1
|
||||
# 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@v1
|
||||
# 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@v1
|
||||
- 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@v1
|
||||
- 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@v1
|
||||
- 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@v1
|
||||
- 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@v1
|
||||
- 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"
|
||||
6
javascript-engine/external/chrono/.gitignore
vendored
Normal file
6
javascript-engine/external/chrono/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
target
|
||||
Cargo.lock
|
||||
.tool-versions
|
||||
|
||||
# for jetbrains users
|
||||
.idea/
|
||||
43
javascript-engine/external/chrono/AUTHORS.txt
vendored
Normal file
43
javascript-engine/external/chrono/AUTHORS.txt
vendored
Normal file
@@ -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>
|
||||
731
javascript-engine/external/chrono/CHANGELOG.md
vendored
Normal file
731
javascript-engine/external/chrono/CHANGELOG.md
vendored
Normal file
@@ -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`.
|
||||
76
javascript-engine/external/chrono/Cargo.toml
vendored
Normal file
76
javascript-engine/external/chrono/Cargo.toml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
[package]
|
||||
name = "chrono"
|
||||
version = "0.4.23"
|
||||
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"]
|
||||
default = ["clock", "std", "oldtime"]
|
||||
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}
|
||||
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }
|
||||
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 }
|
||||
|
||||
[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
|
||||
240
javascript-engine/external/chrono/LICENSE.txt
vendored
Normal file
240
javascript-engine/external/chrono/LICENSE.txt
vendored
Normal file
@@ -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.
|
||||
~~~~
|
||||
|
||||
26
javascript-engine/external/chrono/Makefile
vendored
Normal file
26
javascript-engine/external/chrono/Makefile
vendored
Normal file
@@ -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'
|
||||
59
javascript-engine/external/chrono/README.md
vendored
Normal file
59
javascript-engine/external/chrono/README.md
vendored
Normal file
@@ -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.
|
||||
12
javascript-engine/external/chrono/appveyor.yml
vendored
Normal file
12
javascript-engine/external/chrono/appveyor.yml
vendored
Normal file
@@ -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"
|
||||
137
javascript-engine/external/chrono/benches/chrono.rs
vendored
Normal file
137
javascript-engine/external/chrono/benches/chrono.rs
vendored
Normal file
@@ -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);
|
||||
29
javascript-engine/external/chrono/benches/serde.rs
vendored
Normal file
29
javascript-engine/external/chrono/benches/serde.rs
vendored
Normal file
@@ -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);
|
||||
14
javascript-engine/external/chrono/ci/core-test/Cargo.toml
vendored
Normal file
14
javascript-engine/external/chrono/ci/core-test/Cargo.toml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "core-test"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Kang Seonghoon <public+rust@mearie.org>",
|
||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
chrono = { path = "../..", default-features = false, features = ["serde"] }
|
||||
|
||||
[features]
|
||||
alloc = ["chrono/alloc"]
|
||||
7
javascript-engine/external/chrono/ci/core-test/src/lib.rs
vendored
Normal file
7
javascript-engine/external/chrono/ci/core-test/src/lib.rs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#![no_std]
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
pub fn create_time() {
|
||||
let _ = Utc.with_ymd_and_hms(2019, 1, 1, 0, 0, 0).unwrap();
|
||||
}
|
||||
1
javascript-engine/external/chrono/clippy.toml
vendored
Normal file
1
javascript-engine/external/chrono/clippy.toml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
msrv = "1.38"
|
||||
13
javascript-engine/external/chrono/deny.toml
vendored
Normal file
13
javascript-engine/external/chrono/deny.toml
vendored
Normal file
@@ -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"
|
||||
4
javascript-engine/external/chrono/fuzz/.gitignore
vendored
Normal file
4
javascript-engine/external/chrono/fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
target
|
||||
corpus
|
||||
artifacts
|
||||
24
javascript-engine/external/chrono/fuzz/Cargo.toml
vendored
Normal file
24
javascript-engine/external/chrono/fuzz/Cargo.toml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
[package]
|
||||
name = "chrono-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["David Korczynski <david@adalogics.com>"]
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.3"
|
||||
|
||||
[dependencies.chrono]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_reader"
|
||||
path = "fuzz_targets/fuzz_reader.rs"
|
||||
12
javascript-engine/external/chrono/fuzz/README.md
vendored
Normal file
12
javascript-engine/external/chrono/fuzz/README.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Fuzzing Chrono
|
||||
To fuzz Chrono we rely on the [Cargo-fuzz](https://rust-fuzz.github.io/) project.
|
||||
|
||||
To install cargo-fuzz:
|
||||
```
|
||||
cargo install cargo-fuzz
|
||||
```
|
||||
|
||||
To run the Chrono fuzzer, navigate to the top directory of chrono and issue the following command:
|
||||
```
|
||||
cargo-fuzz run fuzz_reader
|
||||
```
|
||||
10
javascript-engine/external/chrono/fuzz/fuzz_targets/fuzz_reader.rs
vendored
Normal file
10
javascript-engine/external/chrono/fuzz/fuzz_targets/fuzz_reader.rs
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
use chrono::prelude::*;
|
||||
if let Ok(data) = std::str::from_utf8(data) {
|
||||
let _ = DateTime::parse_from_rfc2822(data);
|
||||
let _ = DateTime::parse_from_rfc3339(data);
|
||||
}
|
||||
});
|
||||
1
javascript-engine/external/chrono/rustfmt.toml
vendored
Normal file
1
javascript-engine/external/chrono/rustfmt.toml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
use_small_heuristics = "Max"
|
||||
645
javascript-engine/external/chrono/src/date.rs
vendored
Normal file
645
javascript-engine/external/chrono/src/date.rs
vendored
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
1322
javascript-engine/external/chrono/src/datetime/mod.rs
vendored
Normal file
1322
javascript-engine/external/chrono/src/datetime/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
123
javascript-engine/external/chrono/src/datetime/rustc_serialize.rs
vendored
Normal file
123
javascript-engine/external/chrono/src/datetime/rustc_serialize.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
1150
javascript-engine/external/chrono/src/datetime/serde.rs
vendored
Normal file
1150
javascript-engine/external/chrono/src/datetime/serde.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
952
javascript-engine/external/chrono/src/datetime/tests.rs
vendored
Normal file
952
javascript-engine/external/chrono/src/datetime/tests.rs
vendored
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
33
javascript-engine/external/chrono/src/format/locales.rs
vendored
Normal file
33
javascript-engine/external/chrono/src/format/locales.rs
vendored
Normal file
@@ -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)
|
||||
}
|
||||
1096
javascript-engine/external/chrono/src/format/mod.rs
vendored
Normal file
1096
javascript-engine/external/chrono/src/format/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
977
javascript-engine/external/chrono/src/format/parse.rs
vendored
Normal file
977
javascript-engine/external/chrono/src/format/parse.rs
vendored
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
1290
javascript-engine/external/chrono/src/format/parsed.rs
vendored
Normal file
1290
javascript-engine/external/chrono/src/format/parsed.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
415
javascript-engine/external/chrono/src/format/scan.rs
vendored
Normal file
415
javascript-engine/external/chrono/src/format/scan.rs
vendored
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
712
javascript-engine/external/chrono/src/format/strftime.rs
vendored
Normal file
712
javascript-engine/external/chrono/src/format/strftime.rs
vendored
Normal file
@@ -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. |
|
||||
| `%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(), "28");
|
||||
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");
|
||||
}
|
||||
527
javascript-engine/external/chrono/src/lib.rs
vendored
Normal file
527
javascript-engine/external/chrono/src/lib.rs
vendored
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
355
javascript-engine/external/chrono/src/month.rs
vendored
Normal file
355
javascript-engine/external/chrono/src/month.rs
vendored
Normal file
@@ -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 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);
|
||||
}
|
||||
}
|
||||
2909
javascript-engine/external/chrono/src/naive/date.rs
vendored
Normal file
2909
javascript-engine/external/chrono/src/naive/date.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1936
javascript-engine/external/chrono/src/naive/datetime/mod.rs
vendored
Normal file
1936
javascript-engine/external/chrono/src/naive/datetime/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
73
javascript-engine/external/chrono/src/naive/datetime/rustc_serialize.rs
vendored
Normal file
73
javascript-engine/external/chrono/src/naive/datetime/rustc_serialize.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
1133
javascript-engine/external/chrono/src/naive/datetime/serde.rs
vendored
Normal file
1133
javascript-engine/external/chrono/src/naive/datetime/serde.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
343
javascript-engine/external/chrono/src/naive/datetime/tests.rs
vendored
Normal file
343
javascript-engine/external/chrono/src/naive/datetime/tests.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
816
javascript-engine/external/chrono/src/naive/internals.rs
vendored
Normal file
816
javascript-engine/external/chrono/src/naive/internals.rs
vendored
Normal file
@@ -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)]
|
||||
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) 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) 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) 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) fn succ(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of + (1 << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) 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) 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) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
167
javascript-engine/external/chrono/src/naive/isoweek.rs
vendored
Normal file
167
javascript-engine/external/chrono/src/naive/isoweek.rs
vendored
Normal file
@@ -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)]
|
||||
#[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 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 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 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());
|
||||
}
|
||||
}
|
||||
39
javascript-engine/external/chrono/src/naive/mod.rs
vendored
Normal file
39
javascript-engine/external/chrono/src/naive/mod.rs
vendored
Normal file
@@ -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::*;
|
||||
}
|
||||
1390
javascript-engine/external/chrono/src/naive/time/mod.rs
vendored
Normal file
1390
javascript-engine/external/chrono/src/naive/time/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
javascript-engine/external/chrono/src/naive/time/rustc_serialize.rs
vendored
Normal file
29
javascript-engine/external/chrono/src/naive/time/rustc_serialize.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
65
javascript-engine/external/chrono/src/naive/time/serde.rs
vendored
Normal file
65
javascript-engine/external/chrono/src/naive/time/serde.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
317
javascript-engine/external/chrono/src/naive/time/tests.rs
vendored
Normal file
317
javascript-engine/external/chrono/src/naive/time/tests.rs
vendored
Normal file
@@ -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"
|
||||
);
|
||||
}
|
||||
284
javascript-engine/external/chrono/src/offset/fixed.rs
vendored
Normal file
284
javascript-engine/external/chrono/src/offset/fixed.rs
vendored
Normal file
@@ -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`](#method.east) and
|
||||
/// [`west`](#method.west) 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 fn local_minus_utc(&self) -> i32 {
|
||||
self.local_minus_utc
|
||||
}
|
||||
|
||||
/// Returns the number of seconds to add to convert from the local time to UTC.
|
||||
#[inline]
|
||||
pub fn utc_minus_local(&self) -> i32 {
|
||||
-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()
|
||||
);
|
||||
}
|
||||
}
|
||||
260
javascript-engine/external/chrono/src/offset/local/mod.rs
vendored
Normal file
260
javascript-engine/external/chrono/src/offset/local/mod.rs
vendored
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
237
javascript-engine/external/chrono/src/offset/local/stub.rs
vendored
Normal file
237
javascript-engine/external/chrono/src/offset/local/stub.rs
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
// 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_opt(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
|
||||
.expect("invalid or out-of-range date");
|
||||
let time = NaiveTime::from_hms_nano_opt(
|
||||
tm.tm_hour as u32,
|
||||
tm.tm_min as u32,
|
||||
tm.tm_sec as u32,
|
||||
tm.tm_nsec as u32,
|
||||
).expect("invalid time");
|
||||
|
||||
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)
|
||||
}
|
||||
131
javascript-engine/external/chrono/src/offset/local/tz_info/mod.rs
vendored
Normal file
131
javascript-engine/external/chrono/src/offset/local/tz_info/mod.rs
vendored
Normal file
@@ -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];
|
||||
334
javascript-engine/external/chrono/src/offset/local/tz_info/parser.rs
vendored
Normal file
334
javascript-engine/external/chrono/src/offset/local/tz_info/parser.rs
vendored
Normal file
@@ -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) 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) 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,
|
||||
}
|
||||
1046
javascript-engine/external/chrono/src/offset/local/tz_info/rule.rs
vendored
Normal file
1046
javascript-engine/external/chrono/src/offset/local/tz_info/rule.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
904
javascript-engine/external/chrono/src/offset/local/tz_info/timezone.rs
vendored
Normal file
904
javascript-engine/external/chrono/src/offset/local/tz_info/timezone.rs
vendored
Normal file
@@ -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) fn new(unix_leap_time: i64, local_time_type_index: usize) -> Self {
|
||||
Self { unix_leap_time, local_time_type_index }
|
||||
}
|
||||
|
||||
/// Returns Unix leap time
|
||||
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) fn new(unix_leap_time: i64, correction: i32) -> Self {
|
||||
Self { unix_leap_time, correction }
|
||||
}
|
||||
|
||||
/// Returns Unix leap time
|
||||
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) fn offset(&self) -> i32 {
|
||||
self.ut_offset
|
||||
}
|
||||
|
||||
/// Returns daylight saving time indicator
|
||||
pub(super) 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(())
|
||||
}
|
||||
}
|
||||
185
javascript-engine/external/chrono/src/offset/local/unix.rs
vendored
Normal file
185
javascript-engine/external/chrono/src/offset/local/unix.rs
vendored
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
278
javascript-engine/external/chrono/src/offset/local/windows.rs
vendored
Normal file
278
javascript-engine/external/chrono/src/offset/local/windows.rs
vendored
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
549
javascript-engine/external/chrono/src/offset/mod.rs
vendored
Normal file
549
javascript-engine/external/chrono/src/offset/mod.rs
vendored
Normal file
@@ -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());
|
||||
}
|
||||
}
|
||||
125
javascript-engine/external/chrono/src/offset/utc.rs
vendored
Normal file
125
javascript-engine/external/chrono/src/offset/utc.rs
vendored
Normal file
@@ -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)]
|
||||
#[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")
|
||||
}
|
||||
}
|
||||
765
javascript-engine/external/chrono/src/oldtime.rs
vendored
Normal file
765
javascript-engine/external/chrono/src/oldtime.rs
vendored
Normal file
@@ -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)]
|
||||
#[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 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 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 fn nanoseconds(nanos: i64) -> Duration {
|
||||
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
|
||||
Duration { secs: secs, nanos: nanos as i32 }
|
||||
}
|
||||
|
||||
/// Returns the total number of whole weeks in the duration.
|
||||
#[inline]
|
||||
pub fn num_weeks(&self) -> i64 {
|
||||
self.num_days() / 7
|
||||
}
|
||||
|
||||
/// Returns the total number of whole days in the duration.
|
||||
pub fn num_days(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_DAY
|
||||
}
|
||||
|
||||
/// Returns the total number of whole hours in the duration.
|
||||
#[inline]
|
||||
pub fn num_hours(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_HOUR
|
||||
}
|
||||
|
||||
/// Returns the total number of whole minutes in the duration.
|
||||
#[inline]
|
||||
pub fn num_minutes(&self) -> i64 {
|
||||
self.num_seconds() / SECS_PER_MINUTE
|
||||
}
|
||||
|
||||
/// Returns the total number of whole seconds in the duration.
|
||||
pub fn num_seconds(&self) -> i64 {
|
||||
// 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.
|
||||
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 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 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 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 fn abs(&self) -> Duration {
|
||||
if self.secs < 0 && self.nanos != 0 {
|
||||
Duration { secs: (self.secs + 1).abs(), nanos: NANOS_PER_SEC - self.nanos }
|
||||
} else {
|
||||
Duration { secs: self.secs.abs(), nanos: self.nanos }
|
||||
}
|
||||
}
|
||||
|
||||
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
|
||||
#[inline]
|
||||
pub fn min_value() -> Duration {
|
||||
MIN
|
||||
}
|
||||
|
||||
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
|
||||
#[inline]
|
||||
pub fn max_value() -> Duration {
|
||||
MAX
|
||||
}
|
||||
|
||||
/// A duration where the stored seconds and nanoseconds are equal to zero.
|
||||
#[inline]
|
||||
pub fn zero() -> Duration {
|
||||
Duration { secs: 0, nanos: 0 }
|
||||
}
|
||||
|
||||
/// Returns `true` if the duration equals `Duration::zero()`.
|
||||
#[inline]
|
||||
pub 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]
|
||||
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
|
||||
(div_floor_64(this, other), mod_floor_64(this, other))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_floor_64(this: i64, other: i64) -> i64 {
|
||||
match div_rem_64(this, other) {
|
||||
(d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
|
||||
(d, _) => d,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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]
|
||||
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(()))
|
||||
);
|
||||
}
|
||||
}
|
||||
763
javascript-engine/external/chrono/src/round.rs
vendored
Normal file
763
javascript-engine/external/chrono/src/round.rs
vendored
Normal file
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
241
javascript-engine/external/chrono/src/traits.rs
vendored
Normal file
241
javascript-engine/external/chrono/src/traits.rs
vendored
Normal file
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
312
javascript-engine/external/chrono/src/weekday.rs
vendored
Normal file
312
javascript-engine/external/chrono/src/weekday.rs
vendored
Normal file
@@ -0,0 +1,312 @@
|
||||
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 fn number_from_monday(&self) -> u32 {
|
||||
match *self {
|
||||
Weekday::Mon => 1,
|
||||
Weekday::Tue => 2,
|
||||
Weekday::Wed => 3,
|
||||
Weekday::Thu => 4,
|
||||
Weekday::Fri => 5,
|
||||
Weekday::Sat => 6,
|
||||
Weekday::Sun => 7,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Sunday = 1.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1
|
||||
#[inline]
|
||||
pub fn number_from_sunday(&self) -> u32 {
|
||||
match *self {
|
||||
Weekday::Mon => 2,
|
||||
Weekday::Tue => 3,
|
||||
Weekday::Wed => 4,
|
||||
Weekday::Thu => 5,
|
||||
Weekday::Fri => 6,
|
||||
Weekday::Sat => 7,
|
||||
Weekday::Sun => 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Monday = 0.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||
#[inline]
|
||||
pub fn num_days_from_monday(&self) -> u32 {
|
||||
match *self {
|
||||
Weekday::Mon => 0,
|
||||
Weekday::Tue => 1,
|
||||
Weekday::Wed => 2,
|
||||
Weekday::Thu => 3,
|
||||
Weekday::Fri => 4,
|
||||
Weekday::Sat => 5,
|
||||
Weekday::Sun => 6,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a day-of-week number starting from Sunday = 0.
|
||||
///
|
||||
/// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun`
|
||||
/// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
|
||||
/// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0
|
||||
#[inline]
|
||||
pub fn num_days_from_sunday(&self) -> u32 {
|
||||
match *self {
|
||||
Weekday::Mon => 1,
|
||||
Weekday::Tue => 2,
|
||||
Weekday::Wed => 3,
|
||||
Weekday::Thu => 4,
|
||||
Weekday::Fri => 5,
|
||||
Weekday::Sat => 6,
|
||||
Weekday::Sun => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Weekday {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
Weekday::Mon => "Mon",
|
||||
Weekday::Tue => "Tue",
|
||||
Weekday::Wed => "Wed",
|
||||
Weekday::Thu => "Thu",
|
||||
Weekday::Fri => "Fri",
|
||||
Weekday::Sat => "Sat",
|
||||
Weekday::Sun => "Sun",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Any weekday can be represented as an integer from 0 to 6, which equals to
|
||||
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
|
||||
/// Do not heavily depend on this though; use explicit methods whenever possible.
|
||||
impl num_traits::FromPrimitive for Weekday {
|
||||
#[inline]
|
||||
fn from_i64(n: i64) -> Option<Weekday> {
|
||||
match n {
|
||||
0 => Some(Weekday::Mon),
|
||||
1 => Some(Weekday::Tue),
|
||||
2 => Some(Weekday::Wed),
|
||||
3 => Some(Weekday::Thu),
|
||||
4 => Some(Weekday::Fri),
|
||||
5 => Some(Weekday::Sat),
|
||||
6 => Some(Weekday::Sun),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_u64(n: u64) -> Option<Weekday> {
|
||||
match n {
|
||||
0 => Some(Weekday::Mon),
|
||||
1 => Some(Weekday::Tue),
|
||||
2 => Some(Weekday::Wed),
|
||||
3 => Some(Weekday::Thu),
|
||||
4 => Some(Weekday::Fri),
|
||||
5 => Some(Weekday::Sat),
|
||||
6 => Some(Weekday::Sun),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {{ .. }}")
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
javascript-engine/external/chrono/tests/dateutils.rs
vendored
Normal file
76
javascript-engine/external/chrono/tests/dateutils.rs
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
81
javascript-engine/external/chrono/tests/wasm.rs
vendored
Normal file
81
javascript-engine/external/chrono/tests/wasm.rs
vendored
Normal file
@@ -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);
|
||||
}
|
||||
@@ -24,10 +24,10 @@ libc = { version = "0.2.139", default-features = false }
|
||||
wasi = { version = "0.11", default-features = false }
|
||||
|
||||
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies]
|
||||
wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
#wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
|
||||
#js-sys = { version = "0.3", optional = true }
|
||||
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.18"
|
||||
#wasm-bindgen-test = "0.3.18"
|
||||
|
||||
[features]
|
||||
# Implement std-only traits for getrandom::Error
|
||||
@@ -35,7 +35,8 @@ std = []
|
||||
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
|
||||
rdrand = []
|
||||
# Feature to enable JavaScript bindings on wasm*-unknown-unknown
|
||||
js = ["wasm-bindgen", "js-sys"]
|
||||
#js = ["wasm-bindgen", "js-sys"]
|
||||
js = []
|
||||
# Feature to enable custom RNG implementations
|
||||
custom = []
|
||||
# Unstable feature to support being a libstd dependency
|
||||
|
||||
306
javascript-engine/external/getrandom/src/js.rs
vendored
306
javascript-engine/external/getrandom/src/js.rs
vendored
@@ -5,157 +5,157 @@
|
||||
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
use crate::Error;
|
||||
// use crate::Error;
|
||||
//
|
||||
// extern crate std;
|
||||
// use std::{mem::MaybeUninit, thread_local};
|
||||
//
|
||||
// // use js_sys::{global, Function, Uint8Array};
|
||||
// // use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
||||
//
|
||||
// // Size of our temporary Uint8Array buffer used with WebCrypto methods
|
||||
// // Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
|
||||
// const WEB_CRYPTO_BUFFER_SIZE: usize = 256;
|
||||
// // Node.js's crypto.randomFillSync requires the size to be less than 2**31.
|
||||
// const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1;
|
||||
//
|
||||
// enum RngSource {
|
||||
// Node(NodeCrypto),
|
||||
// Web(WebCrypto, Uint8Array),
|
||||
// }
|
||||
//
|
||||
// // JsValues are always per-thread, so we initialize RngSource for each thread.
|
||||
// // See: https://github.com/rustwasm/wasm-bindgen/pull/955
|
||||
// thread_local!(
|
||||
// static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
|
||||
// );
|
||||
//
|
||||
// pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
// RNG_SOURCE.with(|result| {
|
||||
// let source = result.as_ref().map_err(|&e| e)?;
|
||||
//
|
||||
// match source {
|
||||
// RngSource::Node(n) => {
|
||||
// for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) {
|
||||
// // SAFETY: chunk is never used directly, the memory is only
|
||||
// // modified via the Uint8Array view, which is passed
|
||||
// // directly to JavaScript. Also, crypto.randomFillSync does
|
||||
// // not resize the buffer. We know the length is less than
|
||||
// // u32::MAX because of the chunking above.
|
||||
// // Note that this uses the fact that JavaScript doesn't
|
||||
// // have a notion of "uninitialized memory", this is purely
|
||||
// // a Rust/C/C++ concept.
|
||||
// let res = n.random_fill_sync(unsafe {
|
||||
// Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len())
|
||||
// });
|
||||
// if res.is_err() {
|
||||
// return Err(Error::NODE_RANDOM_FILL_SYNC);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// RngSource::Web(crypto, buf) => {
|
||||
// // getRandomValues does not work with all types of WASM memory,
|
||||
// // so we initially write to browser memory to avoid exceptions.
|
||||
// for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE) {
|
||||
// // The chunk can be smaller than buf's length, so we call to
|
||||
// // JS to create a smaller view of buf without allocation.
|
||||
// let sub_buf = buf.subarray(0, chunk.len() as u32);
|
||||
//
|
||||
// if crypto.get_random_values(&sub_buf).is_err() {
|
||||
// return Err(Error::WEB_GET_RANDOM_VALUES);
|
||||
// }
|
||||
//
|
||||
// // SAFETY: `sub_buf`'s length is the same length as `chunk`
|
||||
// unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// Ok(())
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// fn getrandom_init() -> Result<RngSource, Error> {
|
||||
// let global: Global = global().unchecked_into();
|
||||
//
|
||||
// // Get the Web Crypto interface if we are in a browser, Web Worker, Deno,
|
||||
// // or another environment that supports the Web Cryptography API. This
|
||||
// // also allows for user-provided polyfills in unsupported environments.
|
||||
// let crypto = match global.crypto() {
|
||||
// // Standard Web Crypto interface
|
||||
// c if c.is_object() => c,
|
||||
// // Node.js CommonJS Crypto module
|
||||
// _ if is_node(&global) => {
|
||||
// // If module.require isn't a valid function, we are in an ES module.
|
||||
// match Module::require_fn().and_then(JsCast::dyn_into::<Function>) {
|
||||
// Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) {
|
||||
// Ok(n) => return Ok(RngSource::Node(n.unchecked_into())),
|
||||
// Err(_) => return Err(Error::NODE_CRYPTO),
|
||||
// },
|
||||
// Err(_) => return Err(Error::NODE_ES_MODULE),
|
||||
// }
|
||||
// }
|
||||
// // IE 11 Workaround
|
||||
// _ => match global.ms_crypto() {
|
||||
// c if c.is_object() => c,
|
||||
// _ => return Err(Error::WEB_CRYPTO),
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32);
|
||||
// Ok(RngSource::Web(crypto, buf))
|
||||
// }
|
||||
//
|
||||
// // Taken from https://www.npmjs.com/package/browser-or-node
|
||||
// fn is_node(global: &Global) -> bool {
|
||||
// let process = global.process();
|
||||
// if process.is_object() {
|
||||
// let versions = process.versions();
|
||||
// if versions.is_object() {
|
||||
// return versions.node().is_string();
|
||||
// }
|
||||
// }
|
||||
// false
|
||||
// }
|
||||
|
||||
extern crate std;
|
||||
use std::{mem::MaybeUninit, thread_local};
|
||||
|
||||
use js_sys::{global, Function, Uint8Array};
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
||||
|
||||
// Size of our temporary Uint8Array buffer used with WebCrypto methods
|
||||
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
|
||||
const WEB_CRYPTO_BUFFER_SIZE: usize = 256;
|
||||
// Node.js's crypto.randomFillSync requires the size to be less than 2**31.
|
||||
const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1;
|
||||
|
||||
enum RngSource {
|
||||
Node(NodeCrypto),
|
||||
Web(WebCrypto, Uint8Array),
|
||||
}
|
||||
|
||||
// JsValues are always per-thread, so we initialize RngSource for each thread.
|
||||
// See: https://github.com/rustwasm/wasm-bindgen/pull/955
|
||||
thread_local!(
|
||||
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
|
||||
);
|
||||
|
||||
pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
RNG_SOURCE.with(|result| {
|
||||
let source = result.as_ref().map_err(|&e| e)?;
|
||||
|
||||
match source {
|
||||
RngSource::Node(n) => {
|
||||
for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) {
|
||||
// SAFETY: chunk is never used directly, the memory is only
|
||||
// modified via the Uint8Array view, which is passed
|
||||
// directly to JavaScript. Also, crypto.randomFillSync does
|
||||
// not resize the buffer. We know the length is less than
|
||||
// u32::MAX because of the chunking above.
|
||||
// Note that this uses the fact that JavaScript doesn't
|
||||
// have a notion of "uninitialized memory", this is purely
|
||||
// a Rust/C/C++ concept.
|
||||
let res = n.random_fill_sync(unsafe {
|
||||
Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len())
|
||||
});
|
||||
if res.is_err() {
|
||||
return Err(Error::NODE_RANDOM_FILL_SYNC);
|
||||
}
|
||||
}
|
||||
}
|
||||
RngSource::Web(crypto, buf) => {
|
||||
// getRandomValues does not work with all types of WASM memory,
|
||||
// so we initially write to browser memory to avoid exceptions.
|
||||
for chunk in dest.chunks_mut(WEB_CRYPTO_BUFFER_SIZE) {
|
||||
// The chunk can be smaller than buf's length, so we call to
|
||||
// JS to create a smaller view of buf without allocation.
|
||||
let sub_buf = buf.subarray(0, chunk.len() as u32);
|
||||
|
||||
if crypto.get_random_values(&sub_buf).is_err() {
|
||||
return Err(Error::WEB_GET_RANDOM_VALUES);
|
||||
}
|
||||
|
||||
// SAFETY: `sub_buf`'s length is the same length as `chunk`
|
||||
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn getrandom_init() -> Result<RngSource, Error> {
|
||||
let global: Global = global().unchecked_into();
|
||||
|
||||
// Get the Web Crypto interface if we are in a browser, Web Worker, Deno,
|
||||
// or another environment that supports the Web Cryptography API. This
|
||||
// also allows for user-provided polyfills in unsupported environments.
|
||||
let crypto = match global.crypto() {
|
||||
// Standard Web Crypto interface
|
||||
c if c.is_object() => c,
|
||||
// Node.js CommonJS Crypto module
|
||||
_ if is_node(&global) => {
|
||||
// If module.require isn't a valid function, we are in an ES module.
|
||||
match Module::require_fn().and_then(JsCast::dyn_into::<Function>) {
|
||||
Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) {
|
||||
Ok(n) => return Ok(RngSource::Node(n.unchecked_into())),
|
||||
Err(_) => return Err(Error::NODE_CRYPTO),
|
||||
},
|
||||
Err(_) => return Err(Error::NODE_ES_MODULE),
|
||||
}
|
||||
}
|
||||
// IE 11 Workaround
|
||||
_ => match global.ms_crypto() {
|
||||
c if c.is_object() => c,
|
||||
_ => return Err(Error::WEB_CRYPTO),
|
||||
},
|
||||
};
|
||||
|
||||
let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32);
|
||||
Ok(RngSource::Web(crypto, buf))
|
||||
}
|
||||
|
||||
// Taken from https://www.npmjs.com/package/browser-or-node
|
||||
fn is_node(global: &Global) -> bool {
|
||||
let process = global.process();
|
||||
if process.is_object() {
|
||||
let versions = process.versions();
|
||||
if versions.is_object() {
|
||||
return versions.node().is_string();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
// Return type of js_sys::global()
|
||||
type Global;
|
||||
|
||||
// Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
|
||||
type WebCrypto;
|
||||
// Getters for the WebCrypto API
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn crypto(this: &Global) -> WebCrypto;
|
||||
#[wasm_bindgen(method, getter, js_name = msCrypto)]
|
||||
fn ms_crypto(this: &Global) -> WebCrypto;
|
||||
// Crypto.getRandomValues()
|
||||
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
|
||||
fn get_random_values(this: &WebCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
|
||||
|
||||
// Node JS crypto module (https://nodejs.org/api/crypto.html)
|
||||
type NodeCrypto;
|
||||
// crypto.randomFillSync()
|
||||
#[wasm_bindgen(method, js_name = randomFillSync, catch)]
|
||||
fn random_fill_sync(this: &NodeCrypto, buf: Uint8Array) -> Result<(), JsValue>;
|
||||
|
||||
// Ideally, we would just use `fn require(s: &str)` here. However, doing
|
||||
// this causes a Webpack warning. So we instead return the function itself
|
||||
// and manually invoke it using call1. This also lets us to check that the
|
||||
// function actually exists, allowing for better error messages. See:
|
||||
// https://github.com/rust-random/getrandom/issues/224
|
||||
// https://github.com/rust-random/getrandom/issues/256
|
||||
type Module;
|
||||
#[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)]
|
||||
fn require_fn() -> Result<JsValue, JsValue>;
|
||||
|
||||
// Node JS process Object (https://nodejs.org/api/process.html)
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn process(this: &Global) -> Process;
|
||||
type Process;
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn versions(this: &Process) -> Versions;
|
||||
type Versions;
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn node(this: &Versions) -> JsValue;
|
||||
}
|
||||
// #[wasm_bindgen]
|
||||
// extern "C" {
|
||||
// // Return type of js_sys::global()
|
||||
// type Global;
|
||||
//
|
||||
// // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
|
||||
// type WebCrypto;
|
||||
// // Getters for the WebCrypto API
|
||||
// #[wasm_bindgen(method, getter)]
|
||||
// fn crypto(this: &Global) -> WebCrypto;
|
||||
// #[wasm_bindgen(method, getter, js_name = msCrypto)]
|
||||
// fn ms_crypto(this: &Global) -> WebCrypto;
|
||||
// // Crypto.getRandomValues()
|
||||
// #[wasm_bindgen(method, js_name = getRandomValues, catch)]
|
||||
// fn get_random_values(this: &WebCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
|
||||
//
|
||||
// // Node JS crypto module (https://nodejs.org/api/crypto.html)
|
||||
// type NodeCrypto;
|
||||
// // crypto.randomFillSync()
|
||||
// #[wasm_bindgen(method, js_name = randomFillSync, catch)]
|
||||
// fn random_fill_sync(this: &NodeCrypto, buf: Uint8Array) -> Result<(), JsValue>;
|
||||
//
|
||||
// // Ideally, we would just use `fn require(s: &str)` here. However, doing
|
||||
// // this causes a Webpack warning. So we instead return the function itself
|
||||
// // and manually invoke it using call1. This also lets us to check that the
|
||||
// // function actually exists, allowing for better error messages. See:
|
||||
// // https://github.com/rust-random/getrandom/issues/224
|
||||
// // https://github.com/rust-random/getrandom/issues/256
|
||||
// type Module;
|
||||
// #[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)]
|
||||
// fn require_fn() -> Result<JsValue, JsValue>;
|
||||
//
|
||||
// // Node JS process Object (https://nodejs.org/api/process.html)
|
||||
// #[wasm_bindgen(method, getter)]
|
||||
// fn process(this: &Global) -> Process;
|
||||
// type Process;
|
||||
// #[wasm_bindgen(method, getter)]
|
||||
// fn versions(this: &Process) -> Versions;
|
||||
// type Versions;
|
||||
// #[wasm_bindgen(method, getter)]
|
||||
// fn node(this: &Versions) -> JsValue;
|
||||
// }
|
||||
|
||||
@@ -332,7 +332,13 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
|
||||
#[inline]
|
||||
pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
|
||||
if !dest.is_empty() {
|
||||
imp::getrandom_inner(dest)?;
|
||||
// imp::getrandom_inner(dest)?;
|
||||
// just init with XXX...XXX
|
||||
let len = dest.len();
|
||||
let ptr = dest.as_mut_ptr() as *mut u8;
|
||||
for i in 0..len {
|
||||
unsafe { *ptr.add(i) = 'X' as u8 };
|
||||
}
|
||||
}
|
||||
// SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
|
||||
// since it returned `Ok`.
|
||||
|
||||
@@ -29,11 +29,11 @@ core-foundation-sys = "0.8.3"
|
||||
windows-sys = { version = "0.42.0", features = ["Win32_Globalization", "Win32_System_Com", "Win32_System_WinRT"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
js-sys = "0.3.50"
|
||||
wasm-bindgen = "0.2.70"
|
||||
#js-sys = "0.3.50"
|
||||
#wasm-bindgen = "0.2.70"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
wasm-bindgen-test = "0.3"
|
||||
#wasm-bindgen-test = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "haiku")'.dependencies]
|
||||
iana-time-zone-haiku = { version = "0.1.1", path = "haiku" }
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
use js_sys::{Array, Intl, Object, Reflect};
|
||||
use wasm_bindgen::JsValue;
|
||||
// use js_sys::{Array, Intl, Object, Reflect};
|
||||
// use wasm_bindgen::JsValue;
|
||||
|
||||
pub(crate) fn get_timezone_inner() -> Result<String, crate::GetTimezoneError> {
|
||||
let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options();
|
||||
Reflect::get(&intl, &JsValue::from_str("timeZone"))
|
||||
.ok()
|
||||
.and_then(|tz| tz.as_string())
|
||||
.ok_or(crate::GetTimezoneError::OsError)
|
||||
// let intl = Intl::DateTimeFormat::new(&Array::new(), &Object::new()).resolved_options();
|
||||
// Reflect::get(&intl, &JsValue::from_str("timeZone"))
|
||||
// .ok()
|
||||
// .and_then(|tz| tz.as_string())
|
||||
// .ok_or(crate::GetTimezoneError::OsError)
|
||||
Ok("Europe/London".into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn pass() {
|
||||
let tz = super::get_timezone_inner().unwrap();
|
||||
console_log!("tz={:?}", tz);
|
||||
}
|
||||
}
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use wasm_bindgen_test::*;
|
||||
//
|
||||
// #[wasm_bindgen_test]
|
||||
// fn pass() {
|
||||
// let tz = super::get_timezone_inner().unwrap();
|
||||
// console_log!("tz={:?}", tz);
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user