Merge branch 'master' into naive_assign
This commit is contained in:
commit
8ea2d3f236
|
@ -0,0 +1,62 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This is the script that's executed by travis, you can run it yourself to run
|
||||||
|
# the exact same suite
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
channel() {
|
||||||
|
if [ -n "${TRAVIS}" ]; then
|
||||||
|
if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then
|
||||||
|
pwd
|
||||||
|
(set -x; cargo "$@")
|
||||||
|
fi
|
||||||
|
elif [ -n "${APPVEYOR}" ]; then
|
||||||
|
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
|
||||||
|
pwd
|
||||||
|
(set -x; cargo "$@")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
pwd
|
||||||
|
(set -x; cargo "+${CHANNEL}" "$@")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build_and_test() {
|
||||||
|
# interleave building and testing in hope that it saves time
|
||||||
|
# also vary the local time zone to (hopefully) catch tz-dependent bugs
|
||||||
|
# also avoid doc-testing multiple times---it takes a lot and rarely helps
|
||||||
|
cargo clean
|
||||||
|
channel build -v
|
||||||
|
TZ=ACST-9:30 channel test -v --lib
|
||||||
|
channel build -v --features rustc-serialize
|
||||||
|
TZ=EST4 channel test -v --features rustc-serialize --lib
|
||||||
|
channel build -v --features 'serde bincode'
|
||||||
|
TZ=UTC0 channel test -v --features 'serde bincode'
|
||||||
|
}
|
||||||
|
|
||||||
|
build_only() {
|
||||||
|
# Rust 1.13 doesn't support custom derive, so, to avoid doctests which
|
||||||
|
# validate that, we just build there.
|
||||||
|
cargo clean
|
||||||
|
channel build -v
|
||||||
|
channel build -v --features rustc-serialize
|
||||||
|
channel build -v --features 'serde bincode'
|
||||||
|
}
|
||||||
|
|
||||||
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
|
||||||
|
CHANNEL=nightly
|
||||||
|
build_and_test
|
||||||
|
|
||||||
|
CHANNEL=beta
|
||||||
|
build_and_test
|
||||||
|
|
||||||
|
CHANNEL=stable
|
||||||
|
build_and_test
|
||||||
|
|
||||||
|
CHANNEL=1.13.0
|
||||||
|
build_only
|
15
.travis.yml
15
.travis.yml
|
@ -1,9 +1,8 @@
|
||||||
language: rust
|
language: rust
|
||||||
sudo: false
|
sudo: false
|
||||||
rust:
|
rust:
|
||||||
# 1.8.0 is the earliest known version that Cargo does work for the current crates.io-index repo.
|
# 1.13.0 is the earliest version that Serde 1.0 tests, so we follow suit
|
||||||
# probably older versions work, but we are forced to use this as the minimum for Cargo...
|
- 1.13.0
|
||||||
- 1.8.0
|
|
||||||
- stable
|
- stable
|
||||||
- beta
|
- beta
|
||||||
- nightly
|
- nightly
|
||||||
|
@ -16,15 +15,7 @@ matrix:
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- LD_LIBRARY_PATH: /usr/local/lib
|
- LD_LIBRARY_PATH: /usr/local/lib
|
||||||
script:
|
script: ./.travis.sh
|
||||||
# interleave building and testing in hope that it saves time
|
|
||||||
# also avoid doc-testing multiple times---it takes a lot and rarely helps
|
|
||||||
- cargo build -v
|
|
||||||
- cargo test -v
|
|
||||||
- cargo build -v --features rustc-serialize
|
|
||||||
- cargo test -v --features rustc-serialize --lib
|
|
||||||
- cargo build -v --features serde
|
|
||||||
- cargo test -v --features serde --lib
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
irc:
|
irc:
|
||||||
|
|
13
AUTHORS.txt
13
AUTHORS.txt
|
@ -1,23 +1,36 @@
|
||||||
Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
|
Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
|
||||||
and also the following people (in ascending order):
|
and also the following people (in ascending order):
|
||||||
|
|
||||||
|
Alex Mikhalev <alexmikhalevalex@gmail.com>
|
||||||
Alexander Bulaev <alexbool@yandex-team.ru>
|
Alexander Bulaev <alexbool@yandex-team.ru>
|
||||||
|
Ashley Mannix <ashleymannix@live.com.au>
|
||||||
|
Ben Boeckel <mathstuf@gmail.com>
|
||||||
Ben Eills <ben@beneills.com>
|
Ben Eills <ben@beneills.com>
|
||||||
|
Brandon W Maister <bwm@knewton.com>
|
||||||
|
Brandon W Maister <quodlibetor@gmail.com>
|
||||||
Colin Ray <r.colinray@gmail.com>
|
Colin Ray <r.colinray@gmail.com>
|
||||||
Corey Farwell <coreyf@rwell.org>
|
Corey Farwell <coreyf@rwell.org>
|
||||||
Dan <dan@ebip.co.uk>
|
Dan <dan@ebip.co.uk>
|
||||||
Danilo Bargen <mail@dbrgn.ch>
|
Danilo Bargen <mail@dbrgn.ch>
|
||||||
David Hewson <dev@daveid.co.uk>
|
David Hewson <dev@daveid.co.uk>
|
||||||
David Ross <daboross@daboross.net>
|
David Ross <daboross@daboross.net>
|
||||||
|
David Tolnay <dtolnay@gmail.com>
|
||||||
David Willie <david.willie.1@gmail.com>
|
David Willie <david.willie.1@gmail.com>
|
||||||
|
Eric Findlay <e.findlay@protonmail.ch>
|
||||||
Eunchong Yu <kroisse@gmail.com>
|
Eunchong Yu <kroisse@gmail.com>
|
||||||
|
Frans Skarman <frans.skarman@gmail.com>
|
||||||
Huon Wilson <dbau.pp+github@gmail.com>
|
Huon Wilson <dbau.pp+github@gmail.com>
|
||||||
|
Igor Gnatenko <ignatenko@redhat.com>
|
||||||
|
Jim Turner <jturner314@gmail.com>
|
||||||
Jisoo Park <xxxyel@gmail.com>
|
Jisoo Park <xxxyel@gmail.com>
|
||||||
Joe Wilm <joe@jwilm.com>
|
Joe Wilm <joe@jwilm.com>
|
||||||
John Heitmann <jheitmann@gmail.com>
|
John Heitmann <jheitmann@gmail.com>
|
||||||
John Nagle <nagle@sitetruth.com>
|
John Nagle <nagle@sitetruth.com>
|
||||||
|
Jonas mg <jonasmg@yepmail.net>
|
||||||
|
János Illés <ijanos@gmail.com>
|
||||||
Ken Tossell <ken@tossell.net>
|
Ken Tossell <ken@tossell.net>
|
||||||
Martin Risell Lilja <martin.risell.lilja@gmail.com>
|
Martin Risell Lilja <martin.risell.lilja@gmail.com>
|
||||||
|
Richard Petrie <rap1011@ksu.edu>
|
||||||
Ryan Lewis <ryansname@gmail.com>
|
Ryan Lewis <ryansname@gmail.com>
|
||||||
Sergey V. Galtsev <sergey.v.galtsev@github.com>
|
Sergey V. Galtsev <sergey.v.galtsev@github.com>
|
||||||
Steve Klabnik <steve@steveklabnik.com>
|
Steve Klabnik <steve@steveklabnik.com>
|
||||||
|
|
106
CHANGELOG.md
106
CHANGELOG.md
|
@ -1,15 +1,119 @@
|
||||||
ChangeLog for Chrono
|
ChangeLog for Chrono
|
||||||
====================
|
====================
|
||||||
|
|
||||||
This documents all notable changes to [Chrono](https://github.com/lifthrasiir/rust-chrono).
|
This documents all notable changes to [Chrono](https://github.com/chronotope/chrono).
|
||||||
|
|
||||||
Chrono obeys the principle of [Semantic Versioning](http://semver.org/).
|
Chrono obeys the principle of [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
There were/are numerous minor versions before 1.0 due to the language changes.
|
There were/are numerous minor versions before 1.0 due to the language changes.
|
||||||
Versions with only mechnical changes will be omitted from the following list.
|
Versions with only mechnical changes will be omitted from the following list.
|
||||||
|
|
||||||
|
## 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)
|
## 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,
|
(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.)
|
and replaced by 0.2.25 very shortly. Duh.)
|
||||||
|
|
||||||
|
|
22
Cargo.toml
22
Cargo.toml
|
@ -1,25 +1,31 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.2.25"
|
version = "0.4.0"
|
||||||
authors = ["Kang Seonghoon <public+rust@mearie.org>"]
|
authors = ["Kang Seonghoon <public+rust@mearie.org>"]
|
||||||
|
|
||||||
description = "Date and time library for Rust"
|
description = "Date and time library for Rust"
|
||||||
homepage = "https://github.com/lifthrasiir/rust-chrono"
|
homepage = "https://github.com/chronotope/chrono"
|
||||||
documentation = "https://lifthrasiir.github.io/rust-chrono/"
|
documentation = "https://docs.rs/chrono/"
|
||||||
repository = "https://github.com/lifthrasiir/rust-chrono"
|
repository = "https://github.com/chronotope/chrono"
|
||||||
keywords = ["date", "time", "calendar"]
|
keywords = ["date", "time", "calendar"]
|
||||||
|
categories = ["date-and-time"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
travis-ci = { repository = "chronotope/chrono" }
|
||||||
|
appveyor = { repository = "chronotope/chrono" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
time = "0.1"
|
time = "^0.1.36"
|
||||||
num = { version = "0.1", default-features = false }
|
num = { version = "0.1", default-features = false }
|
||||||
rustc-serialize = { version = "0.3", optional = true }
|
rustc-serialize = { version = "0.3", optional = true }
|
||||||
serde = { version = "<0.9", optional = true }
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = { version = ">=0.7.0" }
|
serde_json = { version = "1" }
|
||||||
bincode = { version = "0.6", features = ["serde"], default-features = false }
|
serde_derive = { version = "1" }
|
||||||
|
bincode = { version = "0.8.0" }
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
Rust-chrono is dual-licensed under The MIT License [1] and
|
Rust-chrono is dual-licensed under The MIT License [1] and
|
||||||
Apache 2.0 License [2]. Copyright (c) 2014, Kang Seonghoon.
|
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.
|
Nota Bene: This is same as the Rust Project's own license.
|
||||||
|
|
||||||
|
|
65
Makefile
65
Makefile
|
@ -17,50 +17,39 @@ readme: README.md
|
||||||
|
|
||||||
README.md: src/lib.rs
|
README.md: src/lib.rs
|
||||||
# really, really sorry for this mess.
|
# really, really sorry for this mess.
|
||||||
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< > $@
|
( \
|
||||||
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< | sed 's/./=/g' >> $@
|
VERSION="$$(cargo pkgid | cut -d: -f3)"; \
|
||||||
echo >> $@
|
awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $<; \
|
||||||
echo '[![Chrono on Travis CI][travis-image]][travis]' >> $@
|
awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $< | sed 's/./=/g'; \
|
||||||
echo '[![Chrono on Appveyor][appveyor-image]][appveyor]' >> $@
|
echo; \
|
||||||
echo '[![Chrono on crates.io][cratesio-image]][cratesio]' >> $@
|
echo '[![Chrono on Travis CI][travis-image]][travis]'; \
|
||||||
echo >> $@
|
echo '[![Chrono on Appveyor][appveyor-image]][appveyor]'; \
|
||||||
echo '[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.svg?branch=master' >> $@
|
echo '[![Chrono on crates.io][cratesio-image]][cratesio]'; \
|
||||||
echo '[travis]: https://travis-ci.org/lifthrasiir/rust-chrono' >> $@
|
echo '[![Chrono on docs.rs][docsrs-image]][docsrs]'; \
|
||||||
echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true' >> $@
|
echo; \
|
||||||
echo '[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master' >> $@
|
echo '[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master'; \
|
||||||
echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg' >> $@
|
echo '[travis]: https://travis-ci.org/chronotope/chrono'; \
|
||||||
echo '[cratesio]: https://crates.io/crates/chrono' >> $@
|
echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true'; \
|
||||||
|
echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono'; \
|
||||||
|
echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg'; \
|
||||||
|
echo '[cratesio]: https://crates.io/crates/chrono'; \
|
||||||
|
echo '[docsrs-image]: https://docs.rs/chrono/badge.svg?version='$$VERSION; \
|
||||||
|
echo '[docsrs]: https://docs.rs/chrono/'$$VERSION'/'; \
|
||||||
|
echo; \
|
||||||
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \
|
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \
|
||||||
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
|
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g'; \
|
||||||
echo '***[Complete Documentation][doc]***' >> $@
|
echo; \
|
||||||
echo >> $@
|
|
||||||
echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@
|
|
||||||
echo >> $@
|
|
||||||
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
|
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
|
||||||
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
|
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g' \
|
||||||
|
) > $@
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
cargo test --features 'serde rustc-serialize'
|
TZ=UTC0 cargo test --features 'serde rustc-serialize bincode' --lib
|
||||||
|
TZ=ACST-9:30 cargo test --features 'serde rustc-serialize bincode' --lib
|
||||||
|
TZ=EST4 cargo test --features 'serde rustc-serialize bincode'
|
||||||
|
|
||||||
.PHONY: doc
|
.PHONY: doc
|
||||||
doc: authors readme
|
doc: authors readme
|
||||||
cargo doc --features 'serde rustc-serialize'
|
cargo doc --features 'serde rustc-serialize bincode'
|
||||||
|
|
||||||
.PHONY: doc-publish
|
|
||||||
doc-publish: doc
|
|
||||||
( \
|
|
||||||
PKGID="$$(cargo pkgid)"; \
|
|
||||||
PKGNAMEVER="$${PKGID#*#}"; \
|
|
||||||
PKGNAME="$${PKGNAMEVER%:*}"; \
|
|
||||||
REMOTE="$$(git config --get remote.origin.url)"; \
|
|
||||||
cd target/doc && \
|
|
||||||
rm -rf .git && \
|
|
||||||
git init && \
|
|
||||||
git checkout --orphan gh-pages && \
|
|
||||||
echo '<!doctype html><html><head><meta http-equiv="refresh" content="0;URL='$$PKGNAME'/index.html"></head><body></body></html>' > index.html && \
|
|
||||||
git add . && \
|
|
||||||
git commit -m 'updated docs.' && \
|
|
||||||
git push "$$REMOTE" gh-pages -f; \
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
224
README.md
224
README.md
|
@ -1,18 +1,22 @@
|
||||||
[Chrono][doc] 0.2.25
|
[Chrono][docsrs] 0.4.0
|
||||||
====================
|
======================
|
||||||
|
|
||||||
[![Chrono on Travis CI][travis-image]][travis]
|
[![Chrono on Travis CI][travis-image]][travis]
|
||||||
[![Chrono on Appveyor][appveyor-image]][appveyor]
|
[![Chrono on Appveyor][appveyor-image]][appveyor]
|
||||||
[![Chrono on crates.io][cratesio-image]][cratesio]
|
[![Chrono on crates.io][cratesio-image]][cratesio]
|
||||||
|
[![Chrono on docs.rs][docsrs-image]][docsrs]
|
||||||
|
|
||||||
[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.svg?branch=master
|
[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master
|
||||||
[travis]: https://travis-ci.org/lifthrasiir/rust-chrono
|
[travis]: https://travis-ci.org/chronotope/chrono
|
||||||
[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true
|
[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true
|
||||||
[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master
|
[appveyor]: https://ci.appveyor.com/project/chronotope/chrono
|
||||||
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
|
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
|
||||||
[cratesio]: https://crates.io/crates/chrono
|
[cratesio]: https://crates.io/crates/chrono
|
||||||
|
[docsrs-image]: https://docs.rs/chrono/badge.svg?version=0.4.0
|
||||||
|
[docsrs]: https://docs.rs/chrono/0.4.0/
|
||||||
|
|
||||||
Date and time handling for Rust. (also known as `rust-chrono`)
|
|
||||||
|
Date and time handling for Rust.
|
||||||
It aims to be a feature-complete superset of
|
It aims to be a feature-complete superset of
|
||||||
the [time](https://github.com/rust-lang-deprecated/time) library.
|
the [time](https://github.com/rust-lang-deprecated/time) library.
|
||||||
In particular,
|
In particular,
|
||||||
|
@ -22,16 +26,13 @@ In particular,
|
||||||
* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
|
* 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,
|
There were several previous attempts to bring a good date and time library to Rust,
|
||||||
which Chrono builts upon and should acknowledge:
|
which Chrono builds upon and should acknowledge:
|
||||||
|
|
||||||
* [Initial research on
|
* [Initial research on
|
||||||
the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
|
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)
|
* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
|
||||||
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
|
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
|
||||||
|
|
||||||
***[Complete Documentation][doc]***
|
|
||||||
|
|
||||||
[doc]: https://lifthrasiir.github.io/rust-chrono/
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ Put this in your `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.2"
|
chrono = "0.4"
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, if you want [Serde](https://github.com/serde-rs/serde) or
|
Or, if you want [Serde](https://github.com/serde-rs/serde) or
|
||||||
|
@ -48,7 +49,7 @@ include the features like this:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
|
chrono = { version = "0.4", features = ["serde", "rustc-serialize"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then put this in your crate root:
|
Then put this in your crate root:
|
||||||
|
@ -57,20 +58,39 @@ Then put this in your crate root:
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Avoid using `use chrono::*;` as Chrono exports several modules other than types.
|
||||||
|
If you prefer the glob imports, use the following instead:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use chrono::prelude::*;
|
||||||
|
```
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
### Duration
|
### Duration
|
||||||
|
|
||||||
[**`Duration`**](https://lifthrasiir.github.io/rust-chrono/chrono/struct.Duration.html)
|
Chrono currently uses
|
||||||
represents the magnitude of a time span. `Duration` used to be provided by Chrono.
|
the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type
|
||||||
It has been moved to the `time` crate as the
|
from the `time` crate to represent the magnitude of a time span.
|
||||||
[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
|
Since this has the same name to the newer, standard type for duration,
|
||||||
still re-exported from Chrono.
|
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.
|
||||||
|
|
||||||
|
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://doc.rust-lang.org/time/time/struct.Duration.html#method.from_std)
|
||||||
|
and
|
||||||
|
[`Duration::to_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.to_std)
|
||||||
|
methods.
|
||||||
|
|
||||||
### Date and Time
|
### Date and Time
|
||||||
|
|
||||||
Chrono provides a
|
Chrono provides a
|
||||||
[**`DateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html)
|
[**`DateTime`**](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html)
|
||||||
type to represent a date and a time in a timezone.
|
type to represent a date and a time in a timezone.
|
||||||
|
|
||||||
For more abstract moment-in-time tracking such as internal timekeeping
|
For more abstract moment-in-time tracking such as internal timekeeping
|
||||||
|
@ -81,15 +101,15 @@ which tracks your system clock, or
|
||||||
is an opaque but monotonically-increasing representation of a moment in time.
|
is an opaque but monotonically-increasing representation of a moment in time.
|
||||||
|
|
||||||
`DateTime` is timezone-aware and must be constructed from
|
`DateTime` is timezone-aware and must be constructed from
|
||||||
the [**`TimeZone`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html) object,
|
the [**`TimeZone`**](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.html) object,
|
||||||
which defines how the local date is converted to and back from the UTC date.
|
which defines how the local date is converted to and back from the UTC date.
|
||||||
There are three well-known `TimeZone` implementations:
|
There are three well-known `TimeZone` implementations:
|
||||||
|
|
||||||
* [**`UTC`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient.
|
* [**`Utc`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
|
||||||
|
|
||||||
* [**`Local`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html) specifies the system local time zone.
|
* [**`Local`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html) specifies the system local time zone.
|
||||||
|
|
||||||
* [**`FixedOffset`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/fixed/struct.FixedOffset.html) specifies
|
* [**`FixedOffset`**](https://docs.rs/chrono/0.4.0/chrono/offset/struct.FixedOffset.html) specifies
|
||||||
an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
|
an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
|
||||||
This often results from the parsed textual date and time.
|
This often results from the parsed textual date and time.
|
||||||
Since it stores the most information and does not depend on the system environment,
|
Since it stores the most information and does not depend on the system environment,
|
||||||
|
@ -97,58 +117,60 @@ There are three well-known `TimeZone` implementations:
|
||||||
|
|
||||||
`DateTime`s with different `TimeZone` types are distinct and do not mix,
|
`DateTime`s with different `TimeZone` types are distinct and do not mix,
|
||||||
but can be converted to each other using
|
but can be converted to each other using
|
||||||
the [`DateTime::with_timezone`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.with_timezone) method.
|
the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.with_timezone) method.
|
||||||
|
|
||||||
You can get the current date and time in the UTC time zone
|
You can get the current date and time in the UTC time zone
|
||||||
([`UTC::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html#method.now))
|
([`Utc::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Utc.html#method.now))
|
||||||
or in the local time zone
|
or in the local time zone
|
||||||
([`Local::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html#method.now)).
|
([`Local::now()`](https://docs.rs/chrono/0.4.0/chrono/offset/struct.Local.html#method.now)).
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
|
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 local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
Alternatively, you can create your own date and time.
|
Alternatively, you can create your own date and time.
|
||||||
This is a bit verbose due to Rust's lack of function and method overloading,
|
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.
|
but in turn we get a rich combination of initialization methods.
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
use chrono::offset::LocalResult;
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
|
let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
|
||||||
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
|
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
|
||||||
assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
|
assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
|
||||||
// July 8 is Tuesday in ISO week 28 of the year 2014.
|
// July 8 is Tuesday in ISO week 28 of the year 2014.
|
||||||
assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
|
assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
|
let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
|
||||||
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
|
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
|
||||||
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
|
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
|
||||||
|
|
||||||
// dynamic verification
|
// dynamic verification
|
||||||
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
|
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
|
||||||
LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
|
LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
|
||||||
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
|
assert_eq!(Utc.ymd_opt(2014, 7, 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);
|
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.
|
// 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.
|
// obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
|
||||||
let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12);
|
let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12);
|
||||||
let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
|
let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
|
||||||
assert_eq!(dt, fixed_dt);
|
assert_eq!(dt, fixed_dt);
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
Various properties are available to the date and time, and can be altered individually.
|
Various properties are available to the date and time, and can be altered individually.
|
||||||
Most of them are defined in the traits [`Datelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Datelike.html) and
|
Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Datelike.html) and
|
||||||
[`Timelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Timelike.html) which you should `use` before.
|
[`Timelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Timelike.html) which you should `use` before.
|
||||||
Addition and subtraction is also supported.
|
Addition and subtraction is also supported.
|
||||||
The following illustrates most supported operations to the date and time:
|
The following illustrates most supported operations to the date and time:
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
use time::Duration;
|
||||||
|
|
||||||
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
||||||
let dt = Local::now();
|
let dt = Local::now();
|
||||||
|
@ -163,9 +185,9 @@ 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
|
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
||||||
|
|
||||||
// time zone accessor and manipulation
|
// time zone accessor and manipulation
|
||||||
assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
|
assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
|
||||||
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
||||||
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
||||||
|
|
||||||
// a sample of property manipulations (validates dynamically)
|
// 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(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
|
||||||
|
@ -173,28 +195,30 @@ assert_eq!(dt.with_day(32), None);
|
||||||
assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
|
assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
|
||||||
|
|
||||||
// arithmetic operations
|
// arithmetic operations
|
||||||
assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
|
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
||||||
Duration::seconds(-2 * 3600 + 2));
|
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
|
||||||
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
|
assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
|
||||||
UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
|
assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
|
||||||
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
|
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
|
||||||
UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
|
Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
|
||||||
~~~~
|
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
|
||||||
|
Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
|
||||||
|
```
|
||||||
|
|
||||||
Formatting is done via the [`format`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.format) method,
|
Formatting is done via the [`format`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.format) method,
|
||||||
which format is equivalent to the familiar `strftime` format.
|
which format is equivalent to the familiar `strftime` format.
|
||||||
(See the [`format::strftime` module documentation](https://lifthrasiir.github.io/rust-chrono/chrono/format/strftime/index.html#specifiers)
|
(See the [`format::strftime` module documentation](https://docs.rs/chrono/0.4.0/chrono/format/strftime/index.html#specifiers)
|
||||||
for full syntax.)
|
for full syntax.)
|
||||||
|
|
||||||
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
||||||
Chrono also provides [`to_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and
|
Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc2822) and
|
||||||
[`to_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods
|
[`to_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc3339) methods
|
||||||
for well-known formats.
|
for well-known formats.
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||||
assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
|
assert_eq!(dt.format("%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("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
|
||||||
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
|
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
|
||||||
|
@ -203,44 +227,44 @@ 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_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
|
||||||
assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
|
assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
|
||||||
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
|
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
Parsing can be done with three methods:
|
Parsing can be done with three methods:
|
||||||
|
|
||||||
1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
|
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
|
(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
|
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
|
||||||
`DateTime<Local>` values. This parses what the `{:?}`
|
`DateTime<Local>` values. This parses what the `{:?}`
|
||||||
([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
|
([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
|
||||||
format specifier prints, and requires the offset to be present.
|
format specifier prints, and requires the offset to be present.
|
||||||
|
|
||||||
2. [`DateTime::parse_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses
|
2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_str) parses
|
||||||
a date and time with offsets and returns `DateTime<FixedOffset>`.
|
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.
|
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.
|
It *cannot* be used when the offset can be missing.
|
||||||
[`DateTime::parse_from_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822)
|
[`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_rfc2822)
|
||||||
and
|
and
|
||||||
[`DateTime::parse_from_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339)
|
[`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.parse_from_rfc3339)
|
||||||
are similar but for well-known formats.
|
are similar but for well-known formats.
|
||||||
|
|
||||||
3. [`Offset::datetime_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is
|
3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4.0/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is
|
||||||
similar but returns `DateTime` of given offset.
|
similar but returns `DateTime` of given offset.
|
||||||
When the explicit offset is missing from the input, it simply uses 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
|
It issues an error when the input contains an explicit offset different
|
||||||
from the current offset.
|
from the current offset.
|
||||||
|
|
||||||
More detailed control over the parsing process is available via
|
More detailed control over the parsing process is available via
|
||||||
[`format`](https://lifthrasiir.github.io/rust-chrono/chrono/format/index.html) module.
|
[`format`](https://docs.rs/chrono/0.4.0/chrono/format/index.html) module.
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||||
let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
|
let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
|
||||||
|
|
||||||
// method 1
|
// method 1
|
||||||
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
|
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<Utc>>(), Ok(dt.clone()));
|
||||||
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
|
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
|
||||||
|
|
||||||
// method 2
|
// method 2
|
||||||
|
@ -251,57 +275,58 @@ assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"),
|
||||||
assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
||||||
|
|
||||||
// method 3
|
// 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("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()));
|
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!
|
// oops, the year is missing!
|
||||||
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
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!
|
// 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());
|
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
|
||||||
// oops, the weekday is incorrect!
|
// oops, the weekday is incorrect!
|
||||||
assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
### Individual date
|
### Individual date
|
||||||
|
|
||||||
Chrono also provides an individual date type ([**`Date`**](https://lifthrasiir.github.io/rust-chrono/chrono/date/struct.Date.html)).
|
Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4.0/chrono/struct.Date.html)).
|
||||||
It also has time zones attached, and have to be constructed via time zones.
|
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.
|
Most operations available to `DateTime` are also available to `Date` whenever appropriate.
|
||||||
|
|
||||||
~~~~ {.rust}
|
```rust
|
||||||
use chrono::*;
|
use chrono::prelude::*;
|
||||||
|
use chrono::offset::LocalResult;
|
||||||
|
|
||||||
assert_eq!(UTC::today(), UTC::now().date());
|
assert_eq!(Utc::today(), Utc::now().date());
|
||||||
assert_eq!(Local::today(), Local::now().date());
|
assert_eq!(Local::today(), Local::now().date());
|
||||||
|
|
||||||
assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
|
assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
|
||||||
assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
|
assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
|
||||||
assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
|
assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
|
||||||
"070809");
|
"070809");
|
||||||
~~~~
|
```
|
||||||
|
|
||||||
There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
|
There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
|
||||||
|
|
||||||
`DateTime` has [`date`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.date) method
|
`DateTime` has [`date`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.date) method
|
||||||
which returns a `Date` which represents its date component.
|
which returns a `Date` which represents its date component.
|
||||||
There is also a [`time`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.time) method,
|
There is also a [`time`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.time) method,
|
||||||
which simply returns a naive local time described below.
|
which simply returns a naive local time described below.
|
||||||
|
|
||||||
### Naive date and time
|
### Naive date and time
|
||||||
|
|
||||||
Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
|
Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
|
||||||
as [**`NaiveDate`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/date/struct.NaiveDate.html),
|
as [**`NaiveDate`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDate.html),
|
||||||
[**`NaiveTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/struct.NaiveTime.html) and
|
[**`NaiveTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveTime.html) and
|
||||||
[**`NaiveDateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/datetime/struct.NaiveDateTime.html) respectively.
|
[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDateTime.html) respectively.
|
||||||
|
|
||||||
They have almost equivalent interfaces as their timezone-aware twins,
|
They have almost equivalent interfaces as their timezone-aware twins,
|
||||||
but are not associated to time zones obviously and can be quite low-level.
|
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.
|
They are mostly useful for building blocks for higher-level types.
|
||||||
|
|
||||||
Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
|
Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
|
||||||
[`naive_local`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_local) returns
|
[`naive_local`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.naive_local) returns
|
||||||
a view to the naive local time,
|
a view to the naive local time,
|
||||||
and [`naive_utc`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_utc) returns
|
and [`naive_utc`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.naive_utc) returns
|
||||||
a view to the naive UTC time.
|
a view to the naive UTC time.
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
@ -313,7 +338,7 @@ Date types are limited in about +/- 262,000 years from the common epoch.
|
||||||
Time types are limited in the nanosecond accuracy.
|
Time types are limited in the nanosecond accuracy.
|
||||||
|
|
||||||
[Leap seconds are supported in the representation but
|
[Leap seconds are supported in the representation but
|
||||||
Chrono doesn't try to make use of them](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/index.html#leap-second-handling).
|
Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveTime.html#leap-second-handling).
|
||||||
(The main reason is that leap seconds are not really predictable.)
|
(The main reason is that leap seconds are not really predictable.)
|
||||||
Almost *every* operation over the possible leap seconds will ignore them.
|
Almost *every* operation over the possible leap seconds will ignore them.
|
||||||
Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
||||||
|
@ -322,7 +347,8 @@ if you want.
|
||||||
Chrono inherently does not support an inaccurate or partial date and time representation.
|
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.
|
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
|
For example, "a month later" of 2014-01-30 is not well-defined
|
||||||
and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
|
and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`.
|
||||||
|
|
||||||
Advanced time zone handling is not yet supported (but is planned in 0.3).
|
Advanced time zone handling is not yet supported.
|
||||||
|
For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
|
||||||
|
|
||||||
|
|
10
appveyor.yml
10
appveyor.yml
|
@ -1,6 +1,6 @@
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- TARGET: 1.8.0-x86_64-pc-windows-gnu
|
- TARGET: 1.13.0-x86_64-pc-windows-gnu
|
||||||
- TARGET: nightly-x86_64-pc-windows-msvc
|
- TARGET: nightly-x86_64-pc-windows-msvc
|
||||||
- TARGET: nightly-i686-pc-windows-msvc
|
- TARGET: nightly-i686-pc-windows-msvc
|
||||||
- TARGET: nightly-x86_64-pc-windows-gnu
|
- TARGET: nightly-x86_64-pc-windows-gnu
|
||||||
|
@ -14,8 +14,8 @@ install:
|
||||||
- ps: $env:PATH="$env:PATH;C:\rust\bin"
|
- ps: $env:PATH="$env:PATH;C:\rust\bin"
|
||||||
- rustc -vV
|
- rustc -vV
|
||||||
- cargo -vV
|
- cargo -vV
|
||||||
build_script:
|
|
||||||
# do not test all combinations, Travis will handle that
|
build: false
|
||||||
- cargo build -v --features "serde rustc-serialize"
|
|
||||||
test_script:
|
test_script:
|
||||||
- cargo test -v --features "serde rustc-serialize"
|
- sh -c 'PATH=`rustc --print sysroot`/bin:$PATH ./.travis.sh'
|
||||||
|
|
181
src/date.rs
181
src/date.rs
|
@ -1,29 +1,24 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014-2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! ISO 8601 calendar date with time zone.
|
||||||
* ISO 8601 calendar date with time zone.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::{fmt, hash};
|
use std::{fmt, hash};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use {Weekday, Datelike};
|
use {Weekday, Datelike};
|
||||||
use duration::Duration;
|
use offset::{TimeZone, Utc};
|
||||||
use offset::{TimeZone, Offset};
|
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
|
||||||
use offset::utc::UTC;
|
use DateTime;
|
||||||
use naive;
|
|
||||||
use naive::date::NaiveDate;
|
|
||||||
use naive::time::NaiveTime;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use format::{Item, DelayedFormat, StrftimeItems};
|
use format::{Item, DelayedFormat, StrftimeItems};
|
||||||
|
|
||||||
/// ISO 8601 calendar date with time zone.
|
/// ISO 8601 calendar date with time zone.
|
||||||
///
|
///
|
||||||
/// This type should be considered ambiguous at best,
|
/// This type should be considered ambiguous at best,
|
||||||
/// due to the inherent lack of precision required for the time zone resolution.
|
/// due to the inherent lack of precision required for the time zone resolution.
|
||||||
|
/// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
|
||||||
/// There are some guarantees on the usage of `Date<Tz>`:
|
/// There are some guarantees on the usage of `Date<Tz>`:
|
||||||
///
|
///
|
||||||
/// - If properly constructed via `TimeZone::ymd` and others without an error,
|
/// - If properly constructed via `TimeZone::ymd` and others without an error,
|
||||||
|
@ -50,9 +45,9 @@ pub struct Date<Tz: TimeZone> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The minimum possible `Date`.
|
/// The minimum possible `Date`.
|
||||||
pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
|
pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc };
|
||||||
/// The maximum possible `Date`.
|
/// The maximum possible `Date`.
|
||||||
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };
|
pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc };
|
||||||
|
|
||||||
impl<Tz: TimeZone> Date<Tz> {
|
impl<Tz: TimeZone> Date<Tz> {
|
||||||
/// Makes a new `Date` with given *UTC* date and offset.
|
/// Makes a new `Date` with given *UTC* date and offset.
|
||||||
|
@ -210,8 +205,8 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn checked_add(self, rhs: Duration) -> Option<Date<Tz>> {
|
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||||
let date = try_opt!(self.date.checked_add(rhs));
|
let date = try_opt!(self.date.checked_add_signed(rhs));
|
||||||
Some(Date { date: date, offset: self.offset })
|
Some(Date { date: date, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,11 +214,21 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn checked_sub(self, rhs: Duration) -> Option<Date<Tz>> {
|
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
|
||||||
let date = try_opt!(self.date.checked_sub(rhs));
|
let date = try_opt!(self.date.checked_sub_signed(rhs));
|
||||||
Some(Date { date: date, offset: self.offset })
|
Some(Date { 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.
|
/// Returns a view to the naive UTC date.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn naive_utc(&self) -> NaiveDate {
|
pub fn naive_utc(&self) -> NaiveDate {
|
||||||
|
@ -231,9 +236,13 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a view to the naive local date.
|
/// Returns a view to the naive local date.
|
||||||
|
///
|
||||||
|
/// This is technically same to [`naive_utc`](#method.naive_utc)
|
||||||
|
/// because the offset is restricted to never exceed one day,
|
||||||
|
/// but provided for the consistency.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn naive_local(&self) -> NaiveDate {
|
pub fn naive_local(&self) -> NaiveDate {
|
||||||
self.date + self.offset.local_minus_utc()
|
self.date
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +261,7 @@ impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the date with the specified format string.
|
/// Formats the date with the specified format string.
|
||||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
|
@ -269,7 +278,7 @@ impl<Tz: TimeZone> Datelike for Date<Tz> {
|
||||||
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
||||||
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
||||||
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
||||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
|
#[inline] fn iso_week(&self) -> IsoWeek { self.naive_local().iso_week() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
|
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
|
||||||
|
@ -332,28 +341,21 @@ impl<Tz: TimeZone> hash::Hash for Date<Tz> {
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.date.hash(state) }
|
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.date.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone> Add<Duration> for Date<Tz> {
|
impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
|
||||||
type Output = Date<Tz>;
|
type Output = Date<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(self, rhs: Duration) -> Date<Tz> {
|
fn add(self, rhs: OldDuration) -> Date<Tz> {
|
||||||
self.checked_add(rhs).expect("`Date + Duration` overflowed")
|
self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone, Tz2: TimeZone> Sub<Date<Tz2>> for Date<Tz> {
|
impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
|
||||||
type Output = Duration;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, rhs: Date<Tz2>) -> Duration { self.date - rhs.date }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> Sub<Duration> for Date<Tz> {
|
|
||||||
type Output = Date<Tz>;
|
type Output = Date<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(self, rhs: Duration) -> Date<Tz> {
|
fn sub(self, rhs: OldDuration) -> Date<Tz> {
|
||||||
self.checked_sub(rhs).expect("`Date - Duration` overflowed")
|
self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -369,114 +371,3 @@ impl<Tz: TimeZone> fmt::Display for Date<Tz> where Tz::Offset: fmt::Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
|
||||||
mod rustc_serialize {
|
|
||||||
use super::Date;
|
|
||||||
use offset::TimeZone;
|
|
||||||
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
|
|
||||||
|
|
||||||
// TODO the current serialization format is NEVER intentionally defined.
|
|
||||||
// in the future it is likely to be redefined to more sane and reasonable format.
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> Encodable for Date<Tz> where Tz::Offset: Encodable {
|
|
||||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
|
||||||
s.emit_struct("Date", 2, |s| {
|
|
||||||
try!(s.emit_struct_field("date", 0, |s| self.date.encode(s)));
|
|
||||||
try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s)));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> Decodable for Date<Tz> where Tz::Offset: Decodable {
|
|
||||||
fn decode<D: Decoder>(d: &mut D) -> Result<Date<Tz>, D::Error> {
|
|
||||||
d.read_struct("Date", 2, |d| {
|
|
||||||
let date = try!(d.read_struct_field("date", 0, Decodable::decode));
|
|
||||||
let offset = try!(d.read_struct_field("offset", 1, Decodable::decode));
|
|
||||||
Ok(Date::from_utc(date, offset))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encodable() {
|
|
||||||
use offset::utc::UTC;
|
|
||||||
use rustc_serialize::json::encode;
|
|
||||||
|
|
||||||
assert_eq!(encode(&UTC.ymd(2014, 7, 24)).ok(),
|
|
||||||
Some(r#"{"date":{"ymdf":16501977},"offset":{}}"#.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_decodable() {
|
|
||||||
use offset::utc::UTC;
|
|
||||||
use rustc_serialize::json;
|
|
||||||
|
|
||||||
let decode = |s: &str| json::decode::<Date<UTC>>(s);
|
|
||||||
|
|
||||||
assert_eq!(decode(r#"{"date":{"ymdf":16501977},"offset":{}}"#).ok(),
|
|
||||||
Some(UTC.ymd(2014, 7, 24)));
|
|
||||||
|
|
||||||
assert!(decode(r#"{"date":{"ymdf":0},"offset":{}}"#).is_err());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use Datelike;
|
|
||||||
use duration::Duration;
|
|
||||||
use naive::date::NaiveDate;
|
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use offset::{TimeZone, Offset, LocalResult};
|
|
||||||
use offset::local::Local;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
struct UTC1y; // same to UTC but with an offset of 365 days
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
struct OneYear;
|
|
||||||
|
|
||||||
impl TimeZone for UTC1y {
|
|
||||||
type Offset = OneYear;
|
|
||||||
|
|
||||||
fn from_offset(_offset: &OneYear) -> UTC1y { UTC1y }
|
|
||||||
|
|
||||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<OneYear> {
|
|
||||||
LocalResult::Single(OneYear)
|
|
||||||
}
|
|
||||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<OneYear> {
|
|
||||||
LocalResult::Single(OneYear)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
|
|
||||||
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Offset for OneYear {
|
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::days(365) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for OneYear {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_date_weird_offset() {
|
|
||||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29)),
|
|
||||||
"2012-02-29+8760:00".to_string());
|
|
||||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
|
||||||
"2012-02-29T05:06:07+8760:00".to_string());
|
|
||||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4)),
|
|
||||||
"2012-03-04+8760:00".to_string());
|
|
||||||
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
|
||||||
"2012-03-04T05:06:07+8760:00".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_local_date_sanity_check() { // issue #27
|
|
||||||
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
651
src/datetime.rs
651
src/datetime.rs
|
@ -1,37 +1,69 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014-2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! ISO 8601 date and time with time zone.
|
||||||
* ISO 8601 date and time with time zone.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::{str, fmt, hash};
|
use std::{str, fmt, hash};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
#[cfg(feature = "rustc-serialize")]
|
||||||
|
use std::ops::Deref;
|
||||||
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use {Weekday, Timelike, Datelike};
|
use {Weekday, Timelike, Datelike};
|
||||||
use offset::{TimeZone, Offset, add_with_leapsecond};
|
use offset::{TimeZone, Offset, Utc, Local, FixedOffset};
|
||||||
use offset::utc::UTC;
|
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
||||||
use offset::local::Local;
|
use Date;
|
||||||
use offset::fixed::FixedOffset;
|
|
||||||
use duration::Duration;
|
|
||||||
use naive::time::NaiveTime;
|
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use date::Date;
|
|
||||||
use format::{Item, Numeric, Pad, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
|
|
||||||
/// ISO 8601 combined date and time with time zone.
|
/// ISO 8601 combined date and time with time zone.
|
||||||
|
///
|
||||||
|
/// There are some constructors implemented here (the `from_*` methods), but
|
||||||
|
/// the general-purpose constructors are all via the methods on the
|
||||||
|
/// [`TimeZone`](./offset/trait.TimeZone.html) implementations.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DateTime<Tz: TimeZone> {
|
pub struct DateTime<Tz: TimeZone> {
|
||||||
datetime: NaiveDateTime,
|
datetime: NaiveDateTime,
|
||||||
offset: Tz::Offset,
|
offset: Tz::Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A DateTime that can be deserialized from a timestamp
|
||||||
|
///
|
||||||
|
/// A timestamp here is seconds since the epoch
|
||||||
|
#[cfg(feature = "rustc-serialize")]
|
||||||
|
pub struct TsSeconds<Tz: TimeZone>(DateTime<Tz>);
|
||||||
|
|
||||||
|
#[cfg(feature = "rustc-serialize")]
|
||||||
|
impl<Tz: TimeZone> From<TsSeconds<Tz>> for DateTime<Tz> {
|
||||||
|
/// Pull the inner DateTime<Tz> out
|
||||||
|
fn from(obj: TsSeconds<Tz>) -> DateTime<Tz> {
|
||||||
|
obj.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rustc-serialize")]
|
||||||
|
impl<Tz: TimeZone> Deref for TsSeconds<Tz> {
|
||||||
|
type Target = DateTime<Tz>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> DateTime<Tz> {
|
impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
/// Makes a new `DateTime` with given *UTC* datetime and offset.
|
/// Makes a new `DateTime` with given *UTC* datetime and offset.
|
||||||
/// The local datetime should be constructed via the `TimeZone` trait.
|
/// The local datetime should be constructed via the `TimeZone` trait.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
|
||||||
|
///
|
||||||
|
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
|
||||||
|
/// assert_eq!(Utc.timestamp(61, 0), dt);
|
||||||
|
/// ~~~~
|
||||||
//
|
//
|
||||||
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
|
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -49,7 +81,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
/// Unlike `date`, this is not associated to the time zone.
|
/// Unlike `date`, this is not associated to the time zone.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn time(&self) -> NaiveTime {
|
pub fn time(&self) -> NaiveTime {
|
||||||
add_with_leapsecond(&self.datetime.time(), &self.offset.local_minus_utc())
|
self.datetime.time() + self.offset.fix()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
|
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
|
||||||
|
@ -89,12 +121,6 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
self.datetime.timestamp_subsec_nanos()
|
self.datetime.timestamp_subsec_nanos()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// *Deprecated*: Same to `DateTime::timestamp`.
|
|
||||||
#[inline]
|
|
||||||
pub fn num_seconds_from_unix_epoch(&self) -> i64 {
|
|
||||||
self.timestamp()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves an associated offset from UTC.
|
/// Retrieves an associated offset from UTC.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
|
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
|
||||||
|
@ -118,8 +144,8 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn checked_add(self, rhs: Duration) -> Option<DateTime<Tz>> {
|
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> {
|
||||||
let datetime = try_opt!(self.datetime.checked_add(rhs));
|
let datetime = try_opt!(self.datetime.checked_add_signed(rhs));
|
||||||
Some(DateTime { datetime: datetime, offset: self.offset })
|
Some(DateTime { datetime: datetime, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +153,18 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
///
|
///
|
||||||
/// Returns `None` when it will result in overflow.
|
/// Returns `None` when it will result in overflow.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn checked_sub(self, rhs: Duration) -> Option<DateTime<Tz>> {
|
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> {
|
||||||
let datetime = try_opt!(self.datetime.checked_sub(rhs));
|
let datetime = try_opt!(self.datetime.checked_sub_signed(rhs));
|
||||||
Some(DateTime { datetime: datetime, offset: self.offset })
|
Some(DateTime { datetime: datetime, offset: self.offset })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subtracts another `DateTime` from the current date and time.
|
||||||
|
/// This does not overflow or underflow at all.
|
||||||
|
#[inline]
|
||||||
|
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> OldDuration {
|
||||||
|
self.datetime.signed_duration_since(rhs.datetime)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a view to the naive UTC datetime.
|
/// Returns a view to the naive UTC datetime.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn naive_utc(&self) -> NaiveDateTime {
|
pub fn naive_utc(&self) -> NaiveDateTime {
|
||||||
|
@ -141,7 +174,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
/// Returns a view to the naive local datetime.
|
/// Returns a view to the naive local datetime.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn naive_local(&self) -> NaiveDateTime {
|
pub fn naive_local(&self) -> NaiveDateTime {
|
||||||
add_with_leapsecond(&self.datetime, &self.offset.local_minus_utc())
|
self.datetime + self.offset.fix()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +208,7 @@ impl DateTime<FixedOffset> {
|
||||||
|
|
||||||
/// Parses a string with the specified format string and
|
/// Parses a string with the specified format string and
|
||||||
/// returns a new `DateTime` with a parsed `FixedOffset`.
|
/// returns a new `DateTime` with a parsed `FixedOffset`.
|
||||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
///
|
///
|
||||||
/// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone.
|
/// See also `Offset::datetime_from_str` which gives a local `DateTime` on specific time zone.
|
||||||
|
@ -208,7 +241,7 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the combined date and time with the specified format string.
|
/// Formats the combined date and time with the specified format string.
|
||||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
|
@ -225,7 +258,7 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
|
||||||
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
|
||||||
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
|
||||||
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
|
||||||
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
|
#[inline] fn iso_week(&self) -> IsoWeek { self.naive_local().iso_week() }
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
|
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
|
||||||
|
@ -315,28 +348,21 @@ impl<Tz: TimeZone> hash::Hash for DateTime<Tz> {
|
||||||
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.datetime.hash(state) }
|
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.datetime.hash(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
|
impl<Tz: TimeZone> Add<OldDuration> for DateTime<Tz> {
|
||||||
type Output = DateTime<Tz>;
|
type Output = DateTime<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add(self, rhs: Duration) -> DateTime<Tz> {
|
fn add(self, rhs: OldDuration) -> DateTime<Tz> {
|
||||||
self.checked_add(rhs).expect("`DateTime + Duration` overflowed")
|
self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone, Tz2: TimeZone> Sub<DateTime<Tz2>> for DateTime<Tz> {
|
impl<Tz: TimeZone> Sub<OldDuration> for DateTime<Tz> {
|
||||||
type Output = Duration;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn sub(self, rhs: DateTime<Tz2>) -> Duration { self.datetime - rhs.datetime }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> Sub<Duration> for DateTime<Tz> {
|
|
||||||
type Output = DateTime<Tz>;
|
type Output = DateTime<Tz>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sub(self, rhs: Duration) -> DateTime<Tz> {
|
fn sub(self, rhs: OldDuration) -> DateTime<Tz> {
|
||||||
self.checked_sub(rhs).expect("`DateTime - Duration` overflowed")
|
self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,11 +405,11 @@ impl str::FromStr for DateTime<FixedOffset> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl str::FromStr for DateTime<UTC> {
|
impl str::FromStr for DateTime<Utc> {
|
||||||
type Err = ParseError;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> ParseResult<DateTime<UTC>> {
|
fn from_str(s: &str) -> ParseResult<DateTime<Utc>> {
|
||||||
s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&UTC))
|
s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&Utc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,122 +421,420 @@ impl str::FromStr for DateTime<Local> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
|
||||||
|
fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed)
|
||||||
|
where FUtc: Fn(&DateTime<Utc>) -> Result<String, E>,
|
||||||
|
FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>,
|
||||||
|
E: ::std::fmt::Debug
|
||||||
|
{
|
||||||
|
assert_eq!(to_string_utc(&Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
||||||
|
Some(r#""2014-07-24T12:34:06Z""#.into()));
|
||||||
|
|
||||||
|
assert_eq!(to_string_fixed(&FixedOffset::east(3660).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
||||||
|
Some(r#""2014-07-24T12:34:06+01:01""#.into()));
|
||||||
|
assert_eq!(to_string_fixed(&FixedOffset::east(3650).ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
||||||
|
Some(r#""2014-07-24T12:34:06+01:00:50""#.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, any(feature = "rustc-serialize", feature = "serde")))]
|
||||||
|
fn test_decodable_json<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
|
||||||
|
fixed_from_str: FFixed,
|
||||||
|
local_from_str: FLocal)
|
||||||
|
where FUtc: Fn(&str) -> Result<DateTime<Utc>, E>,
|
||||||
|
FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>,
|
||||||
|
FLocal: Fn(&str) -> Result<DateTime<Local>, E>,
|
||||||
|
E: ::std::fmt::Debug
|
||||||
|
{
|
||||||
|
// should check against the offset as well (the normal DateTime comparison will ignore them)
|
||||||
|
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
|
||||||
|
dt.as_ref().map(|dt| (dt, dt.offset()))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()),
|
||||||
|
norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6))));
|
||||||
|
assert_eq!(norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()),
|
||||||
|
norm(&Some(Utc.ymd(2014, 7, 24).and_hms(12, 34, 6))));
|
||||||
|
|
||||||
|
assert_eq!(norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()),
|
||||||
|
norm(&Some(FixedOffset::east(0).ymd(2014, 7, 24).and_hms(12, 34, 6))));
|
||||||
|
assert_eq!(norm(&fixed_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()),
|
||||||
|
norm(&Some(FixedOffset::east(60*60 + 23*60).ymd(2014, 7, 24).and_hms(13, 57, 6))));
|
||||||
|
|
||||||
|
// we don't know the exact local offset but we can check that
|
||||||
|
// the conversion didn't change the instant itself
|
||||||
|
assert_eq!(local_from_str(r#""2014-07-24T12:34:06Z""#)
|
||||||
|
.expect("local shouuld parse"),
|
||||||
|
Utc.ymd(2014, 7, 24).and_hms(12, 34, 6));
|
||||||
|
assert_eq!(local_from_str(r#""2014-07-24T13:57:06+01:23""#)
|
||||||
|
.expect("local should parse with offset"),
|
||||||
|
Utc.ymd(2014, 7, 24).and_hms(12, 34, 6));
|
||||||
|
|
||||||
|
assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err());
|
||||||
|
assert!(fixed_from_str(r#""2014-07-32T12:34:06Z""#).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "rustc-serialize"))]
|
||||||
|
fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
|
||||||
|
fixed_from_str: FFixed,
|
||||||
|
local_from_str: FLocal)
|
||||||
|
where FUtc: Fn(&str) -> Result<TsSeconds<Utc>, E>,
|
||||||
|
FFixed: Fn(&str) -> Result<TsSeconds<FixedOffset>, E>,
|
||||||
|
FLocal: Fn(&str) -> Result<TsSeconds<Local>, E>,
|
||||||
|
E: ::std::fmt::Debug
|
||||||
|
{
|
||||||
|
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
|
||||||
|
dt.as_ref().map(|dt| (dt, dt.offset()))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(norm(&utc_from_str("0").ok().map(DateTime::from)),
|
||||||
|
norm(&Some(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0))));
|
||||||
|
assert_eq!(norm(&utc_from_str("-1").ok().map(DateTime::from)),
|
||||||
|
norm(&Some(Utc.ymd(1969, 12, 31).and_hms(23, 59, 59))));
|
||||||
|
|
||||||
|
assert_eq!(norm(&fixed_from_str("0").ok().map(DateTime::from)),
|
||||||
|
norm(&Some(FixedOffset::east(0).ymd(1970, 1, 1).and_hms(0, 0, 0))));
|
||||||
|
assert_eq!(norm(&fixed_from_str("-1").ok().map(DateTime::from)),
|
||||||
|
norm(&Some(FixedOffset::east(0).ymd(1969, 12, 31).and_hms(23, 59, 59))));
|
||||||
|
|
||||||
|
assert_eq!(*fixed_from_str("0").expect("0 timestamp should parse"),
|
||||||
|
Utc.ymd(1970, 1, 1).and_hms(0, 0, 0));
|
||||||
|
assert_eq!(*local_from_str("-1").expect("-1 timestamp should parse"),
|
||||||
|
Utc.ymd(1969, 12, 31).and_hms(23, 59, 59));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
#[cfg(feature = "rustc-serialize")]
|
||||||
mod rustc_serialize {
|
mod rustc_serialize {
|
||||||
use super::DateTime;
|
use std::fmt;
|
||||||
use offset::TimeZone;
|
use super::{DateTime, TsSeconds};
|
||||||
|
use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset};
|
||||||
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
|
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
|
||||||
|
|
||||||
// TODO the current serialization format is NEVER intentionally defined.
|
impl<Tz: TimeZone> Encodable for DateTime<Tz> {
|
||||||
// in the future it is likely to be redefined to more sane and reasonable format.
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> Encodable for DateTime<Tz> where Tz::Offset: Encodable {
|
|
||||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
s.emit_struct("DateTime", 2, |s| {
|
format!("{:?}", self).encode(s)
|
||||||
try!(s.emit_struct_field("datetime", 0, |s| self.datetime.encode(s)));
|
|
||||||
try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s)));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone> Decodable for DateTime<Tz> where Tz::Offset: Decodable {
|
// try!-like function to convert a LocalResult into a serde-ish Result
|
||||||
fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Tz>, D::Error> {
|
fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error>
|
||||||
d.read_struct("DateTime", 2, |d| {
|
where D: Decoder,
|
||||||
let datetime = try!(d.read_struct_field("datetime", 0, Decodable::decode));
|
T: fmt::Display,
|
||||||
let offset = try!(d.read_struct_field("offset", 1, Decodable::decode));
|
{
|
||||||
Ok(DateTime::from_utc(datetime, offset))
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for TsSeconds<FixedOffset> {
|
||||||
|
fn decode<D: Decoder>(d: &mut D) -> Result<TsSeconds<FixedOffset>, D::Error> {
|
||||||
|
from(FixedOffset::east(0).timestamp_opt(d.read_i64()?, 0), d)
|
||||||
|
.map(|dt| TsSeconds(dt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(|dt| TsSeconds(dt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for TsSeconds<Local> {
|
||||||
|
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]
|
#[test]
|
||||||
fn test_encodable() {
|
fn test_encodable() {
|
||||||
use offset::utc::UTC;
|
super::test_encodable_json(json::encode, json::encode);
|
||||||
use rustc_serialize::json::encode;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
encode(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
|
||||||
Some(concat!(r#"{"datetime":{"date":{"ymdf":16501977},"#,
|
|
||||||
r#""time":{"secs":45246,"frac":0}},"#,
|
|
||||||
r#""offset":{}}"#).into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decodable() {
|
fn test_decodable() {
|
||||||
use offset::utc::UTC;
|
super::test_decodable_json(json::decode, json::decode, json::decode);
|
||||||
use rustc_serialize::json;
|
|
||||||
|
|
||||||
let decode = |s: &str| json::decode::<DateTime<UTC>>(s);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
decode(r#"{"datetime":{"date":{"ymdf":16501977},
|
|
||||||
"time":{"secs":45246,"frac":0}},
|
|
||||||
"offset":{}}"#).ok(),
|
|
||||||
Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
decode(r#"{"datetime":{"date":{"ymdf":0},
|
|
||||||
"time":{"secs":0,"frac":0}},
|
|
||||||
"offset":{}}"#).ok(),
|
|
||||||
None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decodable_timestamps() {
|
||||||
|
super::test_decodable_json_timestamps(json::decode, json::decode, json::decode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ser/de helpers
|
||||||
|
///
|
||||||
|
/// The various modules in here are intended to be used with serde's [`with`
|
||||||
|
/// annotation](https://serde.rs/attributes.html#field-attributes).
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde {
|
pub mod serde {
|
||||||
|
use std::fmt;
|
||||||
use super::DateTime;
|
use super::DateTime;
|
||||||
|
use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset};
|
||||||
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
/// Ser/de to/from timestamps in seconds
|
||||||
|
///
|
||||||
|
/// Intended for use with `serde`'s `with` attribute.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # // We mark this ignored so that we can test on 1.13 (which does not
|
||||||
|
/// # // support custom derive), and run tests with --ignored on beta and
|
||||||
|
/// # // nightly to actually trigger these.
|
||||||
|
/// #
|
||||||
|
/// # #[macro_use] extern crate serde_derive;
|
||||||
|
/// # #[macro_use] extern crate serde_json;
|
||||||
|
/// # extern crate chrono;
|
||||||
|
/// # use chrono::{TimeZone, DateTime, Utc};
|
||||||
|
/// use chrono::serde::ts_seconds;
|
||||||
|
/// #[derive(Deserialize, Serialize)]
|
||||||
|
/// struct S {
|
||||||
|
/// #[serde(with = "ts_seconds")]
|
||||||
|
/// time: DateTime<Utc>
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn example() -> Result<S, serde_json::Error> {
|
||||||
|
/// let time = Utc.ymd(2015, 5, 15).and_hms(10, 0, 0);
|
||||||
|
/// let my_s = S {
|
||||||
|
/// time: time.clone(),
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// let as_string = serde_json::to_string(&my_s)?;
|
||||||
|
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||||
|
/// let my_s: S = serde_json::from_str(&as_string)?;
|
||||||
|
/// assert_eq!(my_s.time, time);
|
||||||
|
/// # Ok(my_s)
|
||||||
|
/// # }
|
||||||
|
/// # fn main() { example().unwrap(); }
|
||||||
|
/// ```
|
||||||
|
pub mod ts_seconds {
|
||||||
|
use std::fmt;
|
||||||
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
use {DateTime, Utc, FixedOffset};
|
||||||
use offset::TimeZone;
|
use offset::TimeZone;
|
||||||
use offset::utc::UTC;
|
use super::from;
|
||||||
use offset::local::Local;
|
|
||||||
use offset::fixed::FixedOffset;
|
/// Deserialize a DateTime from a seconds timestamp
|
||||||
use std::fmt::Display;
|
///
|
||||||
use serde::{ser, de};
|
/// Intended for use with `serde`s `deserialize_with` attribute.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # // We mark this ignored so that we can test on 1.13 (which does not
|
||||||
|
/// # // support custom derive), and run tests with --ignored on beta and
|
||||||
|
/// # // nightly to actually trigger these.
|
||||||
|
/// #
|
||||||
|
/// # #[macro_use] extern crate serde_derive;
|
||||||
|
/// # #[macro_use] extern crate serde_json;
|
||||||
|
/// # extern crate chrono;
|
||||||
|
/// # use chrono::{DateTime, Utc};
|
||||||
|
/// use chrono::serde::ts_seconds::deserialize as from_ts;
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct S {
|
||||||
|
/// #[serde(deserialize_with = "from_ts")]
|
||||||
|
/// time: DateTime<Utc>
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn example() -> Result<S, serde_json::Error> {
|
||||||
|
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
|
||||||
|
/// # Ok(my_s)
|
||||||
|
/// # }
|
||||||
|
/// # fn main() { example().unwrap(); }
|
||||||
|
/// ```
|
||||||
|
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||||
|
where D: de::Deserializer<'de>
|
||||||
|
{
|
||||||
|
Ok(try!(d.deserialize_i64(SecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize a UTC datetime into an integer number of seconds since the epoch
|
||||||
|
///
|
||||||
|
/// Intended for use with `serde`s `serialize_with` attribute.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # // We mark this ignored so that we can test on 1.13 (which does not
|
||||||
|
/// # // support custom derive), and run tests with --ignored on beta and
|
||||||
|
/// # // nightly to actually trigger these.
|
||||||
|
/// #
|
||||||
|
/// # #[macro_use] extern crate serde_derive;
|
||||||
|
/// # #[macro_use] extern crate serde_json;
|
||||||
|
/// # extern crate chrono;
|
||||||
|
/// # use chrono::{TimeZone, DateTime, Utc};
|
||||||
|
/// use chrono::serde::ts_seconds::serialize as to_ts;
|
||||||
|
/// #[derive(Serialize)]
|
||||||
|
/// struct S {
|
||||||
|
/// #[serde(serialize_with = "to_ts")]
|
||||||
|
/// time: DateTime<Utc>
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn example() -> Result<String, serde_json::Error> {
|
||||||
|
/// let my_s = S {
|
||||||
|
/// time: Utc.ymd(2015, 5, 15).and_hms(10, 0, 0),
|
||||||
|
/// };
|
||||||
|
/// let as_string = serde_json::to_string(&my_s)?;
|
||||||
|
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||||
|
/// # Ok(as_string)
|
||||||
|
/// # }
|
||||||
|
/// # fn main() { example().unwrap(); }
|
||||||
|
/// ```
|
||||||
|
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: ser::Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_i64(dt.timestamp())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SecondsTimestampVisitor;
|
||||||
|
|
||||||
|
impl<'de> de::Visitor<'de> for SecondsTimestampVisitor {
|
||||||
|
type Value = DateTime<FixedOffset>;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
||||||
|
{
|
||||||
|
write!(formatter, "a unix timestamp in seconds")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a timestamp in seconds since the epoch
|
||||||
|
fn visit_i64<E>(self, value: i64) -> Result<DateTime<FixedOffset>, E>
|
||||||
|
where E: de::Error
|
||||||
|
{
|
||||||
|
from(FixedOffset::east(0).timestamp_opt(value, 0), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a timestamp in seconds since the epoch
|
||||||
|
fn visit_u64<E>(self, value: u64) -> Result<DateTime<FixedOffset>, E>
|
||||||
|
where E: de::Error
|
||||||
|
{
|
||||||
|
from(FixedOffset::east(0).timestamp_opt(value as i64, 0), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// TODO not very optimized for space (binary formats would want something better)
|
// TODO not very optimized for space (binary formats would want something better)
|
||||||
|
|
||||||
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz>
|
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> {
|
||||||
where Tz::Offset: Display
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
{
|
|
||||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
|
||||||
where S: ser::Serializer
|
where S: ser::Serializer
|
||||||
{
|
{
|
||||||
|
struct FormatWrapped<'a, D: 'a> {
|
||||||
|
inner: &'a D
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
self.inner.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug formatting is correct RFC3339, and it allows Zulu.
|
// Debug formatting is correct RFC3339, and it allows Zulu.
|
||||||
serializer.serialize_str(&format!("{:?}", self))
|
serializer.collect_str(&FormatWrapped { inner: &self })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try!-like function to convert a LocalResult into a serde-ish Result
|
||||||
|
fn from<T, E, V>(me: LocalResult<T>, ts: V) -> Result<T, E>
|
||||||
|
where E: de::Error,
|
||||||
|
V: fmt::Display,
|
||||||
|
T: fmt::Display,
|
||||||
|
{
|
||||||
|
match me {
|
||||||
|
LocalResult::None => Err(E::custom(
|
||||||
|
format!("value is not a legal timestamp: {}", ts))),
|
||||||
|
LocalResult::Ambiguous(min, max) => Err(E::custom(
|
||||||
|
format!("value is an ambiguous timestamp: {}, could be either of {}, {}",
|
||||||
|
ts, min, max))),
|
||||||
|
LocalResult::Single(val) => Ok(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DateTimeVisitor;
|
struct DateTimeVisitor;
|
||||||
|
|
||||||
impl de::Visitor for DateTimeVisitor {
|
impl<'de> de::Visitor<'de> for DateTimeVisitor {
|
||||||
type Value = DateTime<FixedOffset>;
|
type Value = DateTime<FixedOffset>;
|
||||||
|
|
||||||
fn visit_str<E>(&mut self, value: &str) -> Result<DateTime<FixedOffset>, E>
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
||||||
|
{
|
||||||
|
write!(formatter, "a formatted date and time string or a unix timestamp")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl de::Deserialize for DateTime<FixedOffset> {
|
/// Deserialize a value that optionally includes a timezone offset in its
|
||||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
/// string representation
|
||||||
where D: de::Deserializer
|
///
|
||||||
|
/// The serialized value can be either a string representation or a unix
|
||||||
|
/// timestamp
|
||||||
|
impl<'de> de::Deserialize<'de> for DateTime<FixedOffset> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
deserializer.deserialize_str(DateTimeVisitor)
|
deserializer.deserialize_str(DateTimeVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl de::Deserialize for DateTime<UTC> {
|
/// Deserialize into a UTC value
|
||||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
///
|
||||||
where D: de::Deserializer
|
/// The serialized value can be either a string representation or a unix
|
||||||
|
/// timestamp
|
||||||
|
impl<'de> de::Deserialize<'de> for DateTime<Utc> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&UTC))
|
deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Utc))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl de::Deserialize for DateTime<Local> {
|
/// Deserialize a value that includes no timezone in its string
|
||||||
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
/// representation
|
||||||
where D: de::Deserializer
|
///
|
||||||
|
/// The serialized value can be either a string representation or a unix
|
||||||
|
/// timestamp
|
||||||
|
impl<'de> de::Deserialize<'de> for DateTime<Local> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local))
|
deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local))
|
||||||
}
|
}
|
||||||
|
@ -521,34 +845,24 @@ mod serde {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_serialize() {
|
fn test_serde_serialize() {
|
||||||
use self::serde_json::to_string;
|
super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string);
|
||||||
|
|
||||||
assert_eq!(to_string(&UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
|
||||||
Some(r#""2014-07-24T12:34:06Z""#.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_deserialize() {
|
fn test_serde_deserialize() {
|
||||||
use self::serde_json;
|
super::test_decodable_json(|input| self::serde_json::from_str(&input), |input| self::serde_json::from_str(&input),
|
||||||
|
|input| self::serde_json::from_str(&input));
|
||||||
let from_str = |s: &str| serde_json::from_str::<DateTime<UTC>>(s);
|
|
||||||
|
|
||||||
assert_eq!(from_str(r#""2014-07-24T12:34:06Z""#).ok(),
|
|
||||||
Some(UTC.ymd(2014, 7, 24).and_hms(12, 34, 6)));
|
|
||||||
|
|
||||||
assert!(from_str(r#""2014-07-32T12:34:06Z""#).is_err());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde_bincode() {
|
fn test_serde_bincode() {
|
||||||
// Bincode is relevant to test separately from JSON because
|
// Bincode is relevant to test separately from JSON because
|
||||||
// it is not self-describing.
|
// it is not self-describing.
|
||||||
use self::bincode::SizeLimit;
|
use self::bincode::{Infinite, serialize, deserialize};
|
||||||
use self::bincode::serde::{serialize, deserialize};
|
|
||||||
|
|
||||||
let dt = UTC.ymd(2014, 7, 24).and_hms(12, 34, 6);
|
let dt = Utc.ymd(2014, 7, 24).and_hms(12, 34, 6);
|
||||||
let encoded = serialize(&dt, SizeLimit::Infinite).unwrap();
|
let encoded = serialize(&dt, Infinite).unwrap();
|
||||||
let decoded: DateTime<UTC> = deserialize(&encoded).unwrap();
|
let decoded: DateTime<Utc> = deserialize(&encoded).unwrap();
|
||||||
assert_eq!(dt, decoded);
|
assert_eq!(dt, decoded);
|
||||||
assert_eq!(dt.offset(), decoded.offset());
|
assert_eq!(dt.offset(), decoded.offset());
|
||||||
}
|
}
|
||||||
|
@ -558,57 +872,53 @@ mod serde {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::DateTime;
|
use super::DateTime;
|
||||||
use Datelike;
|
use Datelike;
|
||||||
use naive::time::NaiveTime;
|
use naive::{NaiveTime, NaiveDate};
|
||||||
use naive::date::NaiveDate;
|
use offset::{TimeZone, Utc, Local, FixedOffset};
|
||||||
use duration::Duration;
|
use oldtime::Duration;
|
||||||
use offset::TimeZone;
|
|
||||||
use offset::utc::UTC;
|
|
||||||
use offset::local::Local;
|
|
||||||
use offset::fixed::FixedOffset;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn test_datetime_offset() {
|
fn test_datetime_offset() {
|
||||||
let EST = FixedOffset::west(5*60*60);
|
let Est = FixedOffset::west(5*60*60);
|
||||||
let EDT = FixedOffset::west(4*60*60);
|
let Edt = FixedOffset::west(4*60*60);
|
||||||
let KST = FixedOffset::east(9*60*60);
|
let Kst = FixedOffset::east(9*60*60);
|
||||||
|
|
||||||
assert_eq!(format!("{}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06 07:08:09 UTC");
|
"2014-05-06 07:08:09 UTC");
|
||||||
assert_eq!(format!("{}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06 07:08:09 -04:00");
|
"2014-05-06 07:08:09 -04:00");
|
||||||
assert_eq!(format!("{}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06 07:08:09 +09:00");
|
"2014-05-06 07:08:09 +09:00");
|
||||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06T07:08:09Z");
|
"2014-05-06T07:08:09Z");
|
||||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06T07:08:09-04:00");
|
"2014-05-06T07:08:09-04:00");
|
||||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)),
|
||||||
"2014-05-06T07:08:09+09:00");
|
"2014-05-06T07:08:09+09:00");
|
||||||
|
|
||||||
// edge cases
|
// edge cases
|
||||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||||
"2014-05-06T00:00:00Z");
|
"2014-05-06T00:00:00Z");
|
||||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||||
"2014-05-06T00:00:00-04:00");
|
"2014-05-06T00:00:00-04:00");
|
||||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(0, 0, 0)),
|
||||||
"2014-05-06T00:00:00+09:00");
|
"2014-05-06T00:00:00+09:00");
|
||||||
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||||
"2014-05-06T23:59:59Z");
|
"2014-05-06T23:59:59Z");
|
||||||
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||||
"2014-05-06T23:59:59-04:00");
|
"2014-05-06T23:59:59-04:00");
|
||||||
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(23, 59, 59)),
|
||||||
"2014-05-06T23:59:59+09:00");
|
"2014-05-06T23:59:59+09:00");
|
||||||
|
|
||||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9), EDT.ymd(2014, 5, 6).and_hms(3, 8, 9));
|
let dt = Utc.ymd(2014, 5, 6).and_hms(7, 8, 9);
|
||||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + Duration::seconds(3600 + 60 + 1),
|
assert_eq!(dt, Edt.ymd(2014, 5, 6).and_hms(3, 8, 9));
|
||||||
UTC.ymd(2014, 5, 6).and_hms(8, 9, 10));
|
assert_eq!(dt + Duration::seconds(3600 + 60 + 1), Utc.ymd(2014, 5, 6).and_hms(8, 9, 10));
|
||||||
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) - EDT.ymd(2014, 5, 6).and_hms(10, 11, 12),
|
assert_eq!(dt.signed_duration_since(Edt.ymd(2014, 5, 6).and_hms(10, 11, 12)),
|
||||||
Duration::seconds(-7*3600 - 3*60 - 3));
|
Duration::seconds(-7*3600 - 3*60 - 3));
|
||||||
|
|
||||||
assert_eq!(*UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), UTC);
|
assert_eq!(*Utc.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Utc);
|
||||||
assert_eq!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), EDT);
|
assert_eq!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Edt);
|
||||||
assert!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != EST);
|
assert!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != Est);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -638,7 +948,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_with_timezone() {
|
fn test_datetime_with_timezone() {
|
||||||
let local_now = Local::now();
|
let local_now = Local::now();
|
||||||
let utc_now = local_now.with_timezone(&UTC);
|
let utc_now = local_now.with_timezone(&Utc);
|
||||||
let local_now2 = utc_now.with_timezone(&Local);
|
let local_now2 = utc_now.with_timezone(&Local);
|
||||||
assert_eq!(local_now, local_now2);
|
assert_eq!(local_now, local_now2);
|
||||||
}
|
}
|
||||||
|
@ -647,9 +957,9 @@ mod tests {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn test_datetime_rfc2822_and_rfc3339() {
|
fn test_datetime_rfc2822_and_rfc3339() {
|
||||||
let EDT = FixedOffset::east(5*60*60);
|
let EDT = FixedOffset::east(5*60*60);
|
||||||
assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(),
|
assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(),
|
||||||
"Wed, 18 Feb 2015 23:16:09 +0000");
|
"Wed, 18 Feb 2015 23:16:09 +0000");
|
||||||
assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(),
|
assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(),
|
||||||
"2015-02-18T23:16:09+00:00");
|
"2015-02-18T23:16:09+00:00");
|
||||||
assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(),
|
assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(),
|
||||||
"Wed, 18 Feb 2015 23:16:09 +0500");
|
"Wed, 18 Feb 2015 23:16:09 +0500");
|
||||||
|
@ -678,11 +988,11 @@ mod tests {
|
||||||
Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150)));
|
Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150)));
|
||||||
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
|
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
|
||||||
|
|
||||||
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<UTC>>(),
|
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
|
||||||
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
||||||
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<UTC>>(),
|
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
|
||||||
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
||||||
assert!("2015-2-18T23:16:9.15".parse::<DateTime<UTC>>().is_err());
|
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
|
||||||
|
|
||||||
// no test for `DateTime<Local>`, we cannot verify that much.
|
// no test for `DateTime<Local>`, we cannot verify that much.
|
||||||
}
|
}
|
||||||
|
@ -695,22 +1005,22 @@ mod tests {
|
||||||
assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no 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",
|
assert!(DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
|
||||||
"%a, %d %b %Y %H:%M:%S GMT").is_err());
|
"%a, %d %b %Y %H:%M:%S GMT").is_err());
|
||||||
assert_eq!(UTC.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
|
assert_eq!(Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
|
||||||
"%a, %d %b %Y %H:%M:%S GMT"),
|
"%a, %d %b %Y %H:%M:%S GMT"),
|
||||||
Ok(UTC.ymd(2013, 8, 9).and_hms(23, 54, 35)));
|
Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_format_with_local() {
|
fn test_datetime_format_with_local() {
|
||||||
// if we are not around the year boundary, local and UTC date should have the same year
|
// 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();
|
let dt = Local::now().with_month(5).unwrap();
|
||||||
assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&UTC).format("%Y").to_string());
|
assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_is_copy() {
|
fn test_datetime_is_copy() {
|
||||||
// UTC is known to be `Copy`.
|
// UTC is known to be `Copy`.
|
||||||
let a = UTC::now();
|
let a = Utc::now();
|
||||||
let b = a;
|
let b = a;
|
||||||
assert_eq!(a, b);
|
assert_eq!(a, b);
|
||||||
}
|
}
|
||||||
|
@ -720,7 +1030,7 @@ mod tests {
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
// UTC is known to be `Send`.
|
// UTC is known to be `Send`.
|
||||||
let a = UTC::now();
|
let a = Utc::now();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let _ = a;
|
let _ = a;
|
||||||
}).join().unwrap();
|
}).join().unwrap();
|
||||||
|
@ -728,11 +1038,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_subsecond_part() {
|
fn test_subsecond_part() {
|
||||||
let datetime = UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 1234567);
|
let datetime = Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 1234567);
|
||||||
|
|
||||||
assert_eq!(1, datetime.timestamp_subsec_millis());
|
assert_eq!(1, datetime.timestamp_subsec_millis());
|
||||||
assert_eq!(1234, datetime.timestamp_subsec_micros());
|
assert_eq!(1234, datetime.timestamp_subsec_micros());
|
||||||
assert_eq!(1234567, datetime.timestamp_subsec_nanos());
|
assert_eq!(1234567, datetime.timestamp_subsec_nanos());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014, Kang Seonghoon.
|
// Portions Copyright 2013-2014 The Rust Project Developers.
|
||||||
// Copyright 2013-2014 The Rust Project Developers.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))
|
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))
|
||||||
|
|
|
@ -1,23 +1,37 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014-2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
//! Formatting utilities for date and time.
|
//! Formatting (and parsing) utilities for date and time.
|
||||||
|
//!
|
||||||
|
//! This module provides the common types and routines to implement,
|
||||||
|
//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or
|
||||||
|
//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods.
|
||||||
|
//! For most cases you should use these high-level interfaces.
|
||||||
|
//!
|
||||||
|
//! Internally the formatting and parsing shares the same abstract **formatting items**,
|
||||||
|
//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of
|
||||||
|
//! the [`Item`](./enum.Item.html) type.
|
||||||
|
//! They are generated from more readable **format strings**;
|
||||||
|
//! currently Chrono supports [one built-in syntax closely resembling
|
||||||
|
//! C's `strftime` format](./strftime/index.html).
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use {Datelike, Timelike};
|
use {Datelike, Timelike, Weekday, ParseWeekdayError};
|
||||||
use div::{div_floor, mod_floor};
|
use div::{div_floor, mod_floor};
|
||||||
use duration::Duration;
|
use offset::{Offset, FixedOffset};
|
||||||
use offset::{Offset, add_with_leapsecond};
|
use naive::{NaiveDate, NaiveTime};
|
||||||
use naive::date::NaiveDate;
|
|
||||||
use naive::time::NaiveTime;
|
|
||||||
|
|
||||||
pub use self::strftime::StrftimeItems;
|
pub use self::strftime::StrftimeItems;
|
||||||
pub use self::parsed::Parsed;
|
pub use self::parsed::Parsed;
|
||||||
pub use self::parse::parse;
|
pub use self::parse::parse;
|
||||||
|
|
||||||
|
/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
enum Void {}
|
||||||
|
|
||||||
/// Padding characters for numeric items.
|
/// Padding characters for numeric items.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Pad {
|
pub enum Pad {
|
||||||
|
@ -42,7 +56,7 @@ pub enum Pad {
|
||||||
/// It also trims the preceding whitespaces if any.
|
/// It also trims the preceding whitespaces if any.
|
||||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||||
/// parsed with the same formatting items.
|
/// parsed with the same formatting items.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Numeric {
|
pub enum Numeric {
|
||||||
/// Full Gregorian year (FW=4, PW=∞).
|
/// Full Gregorian year (FW=4, PW=∞).
|
||||||
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
|
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
|
||||||
|
@ -89,13 +103,45 @@ pub enum Numeric {
|
||||||
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
|
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
|
||||||
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
/// For formatting, it assumes UTC upon the absence of time zone offset.
|
||||||
Timestamp,
|
Timestamp,
|
||||||
|
|
||||||
|
/// Internal uses only.
|
||||||
|
///
|
||||||
|
/// This item exists so that one can add additional internal-only formatting
|
||||||
|
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||||
|
Internal(InternalNumeric),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque type representing numeric item types for internal uses only.
|
||||||
|
pub struct InternalNumeric {
|
||||||
|
_dummy: Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for InternalNumeric {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self._dummy {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for InternalNumeric {
|
||||||
|
fn eq(&self, _other: &InternalNumeric) -> bool {
|
||||||
|
match self._dummy {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for InternalNumeric {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for InternalNumeric {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "<InternalNumeric>")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fixed-format item types.
|
/// Fixed-format item types.
|
||||||
///
|
///
|
||||||
/// They have their own rules of formatting and parsing.
|
/// They have their own rules of formatting and parsing.
|
||||||
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Fixed {
|
pub enum Fixed {
|
||||||
/// Abbreviated month names.
|
/// Abbreviated month names.
|
||||||
///
|
///
|
||||||
|
@ -139,14 +185,14 @@ pub enum Fixed {
|
||||||
///
|
///
|
||||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces.
|
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces.
|
||||||
/// The offset is limited from `-24:00` to `+24:00`,
|
/// The offset is limited from `-24:00` to `+24:00`,
|
||||||
/// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range.
|
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||||
TimezoneOffsetColon,
|
TimezoneOffsetColon,
|
||||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||||
///
|
///
|
||||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces,
|
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces,
|
||||||
/// and `Z` can be either in upper case or in lower case.
|
/// and `Z` can be either in upper case or in lower case.
|
||||||
/// The offset is limited from `-24:00` to `+24:00`,
|
/// The offset is limited from `-24:00` to `+24:00`,
|
||||||
/// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range.
|
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||||
TimezoneOffsetColonZ,
|
TimezoneOffsetColonZ,
|
||||||
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
||||||
/// Parsing allows an optional colon.
|
/// Parsing allows an optional colon.
|
||||||
|
@ -158,15 +204,51 @@ pub enum Fixed {
|
||||||
RFC2822,
|
RFC2822,
|
||||||
/// RFC 3339 & ISO 8601 date and time syntax.
|
/// RFC 3339 & ISO 8601 date and time syntax.
|
||||||
RFC3339,
|
RFC3339,
|
||||||
|
|
||||||
|
/// Internal uses only.
|
||||||
|
///
|
||||||
|
/// This item exists so that one can add additional internal-only formatting
|
||||||
|
/// without breaking major compatibility (as enum variants cannot be selectively private).
|
||||||
|
Internal(InternalFixed),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An opaque type representing fixed-format item types for internal uses only.
|
||||||
|
pub struct InternalFixed {
|
||||||
|
_dummy: Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for InternalFixed {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self._dummy {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for InternalFixed {
|
||||||
|
fn eq(&self, _other: &InternalFixed) -> bool {
|
||||||
|
match self._dummy {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for InternalFixed {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for InternalFixed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "<InternalFixed>")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single formatting item. This is used for both formatting and parsing.
|
/// A single formatting item. This is used for both formatting and parsing.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Item<'a> {
|
pub enum Item<'a> {
|
||||||
/// A literally printed and parsed text.
|
/// A literally printed and parsed text.
|
||||||
Literal(&'a str),
|
Literal(&'a str),
|
||||||
|
/// Same to `Literal` but with the string owned by the item.
|
||||||
|
OwnedLiteral(Box<str>),
|
||||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||||
Space(&'a str),
|
Space(&'a str),
|
||||||
|
/// Same to `Space` but with the string owned by the item.
|
||||||
|
OwnedSpace(Box<str>),
|
||||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||||
/// the parser simply ignores any padded whitespace and zeroes.
|
/// the parser simply ignores any padded whitespace and zeroes.
|
||||||
Numeric(Numeric, Pad),
|
Numeric(Numeric, Pad),
|
||||||
|
@ -184,10 +266,10 @@ macro_rules! nums { ($x:ident) => (Item::Numeric(Numeric::$x, Pad::Space)) }
|
||||||
macro_rules! fix { ($x:ident) => (Item::Fixed(Fixed::$x)) }
|
macro_rules! fix { ($x:ident) => (Item::Fixed(Fixed::$x)) }
|
||||||
|
|
||||||
/// An error from the `parse` function.
|
/// An error from the `parse` function.
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
pub struct ParseError(ParseErrorKind);
|
pub struct ParseError(ParseErrorKind);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
enum ParseErrorKind {
|
enum ParseErrorKind {
|
||||||
/// Given field is out of permitted range.
|
/// Given field is out of permitted range.
|
||||||
OutOfRange,
|
OutOfRange,
|
||||||
|
@ -253,7 +335,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
||||||
/// Tries to format given arguments with given formatting items.
|
/// Tries to format given arguments with given formatting items.
|
||||||
/// Internally used by `DelayedFormat`.
|
/// Internally used by `DelayedFormat`.
|
||||||
pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
|
pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
|
||||||
off: Option<&(String, Duration)>, items: I) -> fmt::Result
|
off: Option<&(String, FixedOffset)>, items: I) -> fmt::Result
|
||||||
where I: Iterator<Item=Item<'a>> {
|
where I: Iterator<Item=Item<'a>> {
|
||||||
// full and abbreviated month and weekday names
|
// full and abbreviated month and weekday names
|
||||||
static SHORT_MONTHS: [&'static str; 12] =
|
static SHORT_MONTHS: [&'static str; 12] =
|
||||||
|
@ -269,6 +351,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
for item in items {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
|
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
|
||||||
|
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => try!(write!(w, "{}", s)),
|
||||||
|
|
||||||
Item::Numeric(spec, pad) => {
|
Item::Numeric(spec, pad) => {
|
||||||
use self::Numeric::*;
|
use self::Numeric::*;
|
||||||
|
@ -282,14 +365,14 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
Year => (4, date.map(|d| d.year() as i64)),
|
Year => (4, date.map(|d| d.year() as i64)),
|
||||||
YearDiv100 => (2, date.map(|d| div_floor(d.year() as i64, 100))),
|
YearDiv100 => (2, date.map(|d| div_floor(d.year() as i64, 100))),
|
||||||
YearMod100 => (2, date.map(|d| mod_floor(d.year() as i64, 100))),
|
YearMod100 => (2, date.map(|d| mod_floor(d.year() as i64, 100))),
|
||||||
IsoYear => (4, date.map(|d| d.isoweekdate().0 as i64)),
|
IsoYear => (4, date.map(|d| d.iso_week().year() as i64)),
|
||||||
IsoYearDiv100 => (2, date.map(|d| div_floor(d.isoweekdate().0 as i64, 100))),
|
IsoYearDiv100 => (2, date.map(|d| div_floor(d.iso_week().year() as i64, 100))),
|
||||||
IsoYearMod100 => (2, date.map(|d| mod_floor(d.isoweekdate().0 as i64, 100))),
|
IsoYearMod100 => (2, date.map(|d| mod_floor(d.iso_week().year() as i64, 100))),
|
||||||
Month => (2, date.map(|d| d.month() as i64)),
|
Month => (2, date.map(|d| d.month() as i64)),
|
||||||
Day => (2, date.map(|d| d.day() as i64)),
|
Day => (2, date.map(|d| d.day() as i64)),
|
||||||
WeekFromSun => (2, date.map(|d| week_from_sun(d) as i64)),
|
WeekFromSun => (2, date.map(|d| week_from_sun(d) as i64)),
|
||||||
WeekFromMon => (2, date.map(|d| week_from_mon(d) as i64)),
|
WeekFromMon => (2, date.map(|d| week_from_mon(d) as i64)),
|
||||||
IsoWeek => (2, date.map(|d| d.isoweekdate().1 as i64)),
|
IsoWeek => (2, date.map(|d| d.iso_week().week() as i64)),
|
||||||
NumDaysFromSun => (1, date.map(|d| d.weekday().num_days_from_sunday() as i64)),
|
NumDaysFromSun => (1, date.map(|d| d.weekday().num_days_from_sunday() as i64)),
|
||||||
WeekdayFromMon => (1, date.map(|d| d.weekday().number_from_monday() as i64)),
|
WeekdayFromMon => (1, date.map(|d| d.weekday().number_from_monday() as i64)),
|
||||||
Ordinal => (3, date.map(|d| d.ordinal() as i64)),
|
Ordinal => (3, date.map(|d| d.ordinal() as i64)),
|
||||||
|
@ -303,9 +386,12 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
(Some(d), Some(t), None) =>
|
(Some(d), Some(t), None) =>
|
||||||
Some(d.and_time(*t).timestamp()),
|
Some(d.and_time(*t).timestamp()),
|
||||||
(Some(d), Some(t), Some(&(_, off))) =>
|
(Some(d), Some(t), Some(&(_, off))) =>
|
||||||
Some(add_with_leapsecond(&d.and_time(*t), &-off).timestamp()),
|
Some((d.and_time(*t) - off).timestamp()),
|
||||||
(_, _, _) => None
|
(_, _, _) => None
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
|
@ -333,15 +419,15 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
|
|
||||||
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
||||||
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
|
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
|
||||||
fn write_local_minus_utc(w: &mut fmt::Formatter, off: Duration,
|
fn write_local_minus_utc(w: &mut fmt::Formatter, off: FixedOffset,
|
||||||
allow_zulu: bool, use_colon: bool) -> fmt::Result {
|
allow_zulu: bool, use_colon: bool) -> fmt::Result {
|
||||||
let off = off.num_minutes();
|
let off = off.local_minus_utc();
|
||||||
if !allow_zulu || off != 0 {
|
if !allow_zulu || off != 0 {
|
||||||
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
|
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
|
||||||
if use_colon {
|
if use_colon {
|
||||||
write!(w, "{}{:02}:{:02}", sign, off / 60, off % 60)
|
write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
|
||||||
} else {
|
} else {
|
||||||
write!(w, "{}{:02}{:02}", sign, off / 60, off % 60)
|
write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
write!(w, "Z")
|
write!(w, "Z")
|
||||||
|
@ -421,6 +507,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
match ret {
|
match ret {
|
||||||
|
@ -436,7 +525,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod parsed;
|
mod parsed;
|
||||||
|
|
||||||
// due to the size of parsing routines, they are in separate modules.
|
// due to the size of parsing routines, they are in separate modules.
|
||||||
mod scan;
|
mod scan;
|
||||||
|
@ -453,7 +542,7 @@ pub struct DelayedFormat<I> {
|
||||||
/// The time view, if any.
|
/// The time view, if any.
|
||||||
time: Option<NaiveTime>,
|
time: Option<NaiveTime>,
|
||||||
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
/// The name and local-to-UTC difference for the offset (timezone), if any.
|
||||||
off: Option<(String, Duration)>,
|
off: Option<(String, FixedOffset)>,
|
||||||
/// An iterator returning formatting items.
|
/// An iterator returning formatting items.
|
||||||
items: I,
|
items: I,
|
||||||
}
|
}
|
||||||
|
@ -468,7 +557,7 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
|
||||||
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
|
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
|
||||||
offset: &Off, items: I) -> DelayedFormat<I>
|
offset: &Off, items: I) -> DelayedFormat<I>
|
||||||
where Off: Offset + fmt::Display {
|
where Off: Offset + fmt::Display {
|
||||||
let name_and_diff = (offset.to_string(), offset.local_minus_utc());
|
let name_and_diff = (offset.to_string(), offset.fix());
|
||||||
DelayedFormat { date: date, time: time, off: Some(name_and_diff), items: items }
|
DelayedFormat { date: date, time: time, off: Some(name_and_diff), items: items }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,3 +568,41 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for DelayedFormat<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this implementation is here only because we need some private code from `scan`
|
||||||
|
|
||||||
|
/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun));
|
||||||
|
/// assert!("any day".parse::<Weekday>().is_err());
|
||||||
|
/// ~~~~
|
||||||
|
///
|
||||||
|
/// The parsing is case-insensitive.
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// # use chrono::Weekday;
|
||||||
|
/// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon));
|
||||||
|
/// ~~~~
|
||||||
|
///
|
||||||
|
/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted.
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// # use chrono::Weekday;
|
||||||
|
/// assert!("thurs".parse::<Weekday>().is_err());
|
||||||
|
/// ~~~~
|
||||||
|
impl FromStr for Weekday {
|
||||||
|
type Err = ParseWeekdayError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
if let Ok(("", w)) = scan::short_or_long_weekday(s) {
|
||||||
|
Ok(w)
|
||||||
|
} else {
|
||||||
|
Err(ParseWeekdayError { _dummy: () })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// Portions copyright (c) 2015, John Nagle.
|
// Portions copyright (c) 2015, John Nagle.
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
|
@ -219,7 +218,13 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
s = &s[prefix.len()..];
|
s = &s[prefix.len()..];
|
||||||
}
|
}
|
||||||
|
|
||||||
Item::Space(_) => {
|
Item::OwnedLiteral(ref prefix) => {
|
||||||
|
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||||
|
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
|
||||||
|
s = &s[prefix.len()..];
|
||||||
|
}
|
||||||
|
|
||||||
|
Item::Space(_) | Item::OwnedSpace(_) => {
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +253,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
Second => (2, false, Parsed::set_second),
|
Second => (2, false, Parsed::set_second),
|
||||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
|
@ -325,6 +333,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
|
||||||
|
|
||||||
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||||
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||||
|
|
||||||
|
// for the future expansion
|
||||||
|
Internal(ref int) => match int._dummy {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,9 +370,11 @@ fn test_parse() {
|
||||||
($fmt:expr, $items:expr; $err:tt) => (
|
($fmt:expr, $items:expr; $err:tt) => (
|
||||||
assert_eq!(parse_all($fmt, &$items), Err($err))
|
assert_eq!(parse_all($fmt, &$items), Err($err))
|
||||||
);
|
);
|
||||||
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (
|
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
|
||||||
assert_eq!(parse_all($fmt, &$items), Ok(Parsed { $($k: Some($v),)* ..Parsed::new() }))
|
let mut expected = Parsed::new();
|
||||||
);
|
$(expected.$k = Some($v);)*
|
||||||
|
assert_eq!(parse_all($fmt, &$items), Ok(expected))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty string
|
// empty string
|
||||||
|
@ -590,8 +603,8 @@ fn test_parse() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rfc2822() {
|
fn test_rfc2822() {
|
||||||
use datetime::DateTime;
|
use DateTime;
|
||||||
use offset::fixed::FixedOffset;
|
use offset::FixedOffset;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::NOT_ENOUGH;
|
use super::NOT_ENOUGH;
|
||||||
|
|
||||||
|
@ -642,40 +655,40 @@ fn test_rfc2822() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_rfc850() {
|
fn parse_rfc850() {
|
||||||
use ::{UTC, TimeZone};
|
use ::{Utc, TimeZone};
|
||||||
|
|
||||||
static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
|
static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
|
||||||
|
|
||||||
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
|
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
|
||||||
let dt = UTC.ymd(1994, 11, 6).and_hms(8, 49, 37);
|
let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
|
||||||
|
|
||||||
// Check that the format is what we expect
|
// Check that the format is what we expect
|
||||||
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
|
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
|
||||||
|
|
||||||
// Check that it parses correctly
|
// Check that it parses correctly
|
||||||
assert_eq!(Ok(dt), UTC.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
|
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
|
// Check that the rest of the weekdays parse correctly (this test originally failed because
|
||||||
// Sunday parsed incorrectly).
|
// Sunday parsed incorrectly).
|
||||||
let testdates = [
|
let testdates = [
|
||||||
(UTC.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
|
||||||
(UTC.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
|
||||||
(UTC.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
|
||||||
(UTC.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
|
||||||
(UTC.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
|
||||||
(UTC.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
|
(Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for val in &testdates {
|
for val in &testdates {
|
||||||
assert_eq!(Ok(val.0), UTC.datetime_from_str(val.1, RFC850_FMT));
|
assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rfc3339() {
|
fn test_rfc3339() {
|
||||||
use datetime::DateTime;
|
use DateTime;
|
||||||
use offset::fixed::FixedOffset;
|
use offset::FixedOffset;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
// Test data - (input, Ok(expected result after parse and format) or Err(error code))
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
//! A collection of parsed date and time items.
|
//! A collection of parsed date and time items.
|
||||||
//! They can be constructed incrementally while being checked for consistency.
|
//! They can be constructed incrementally while being checked for consistency.
|
||||||
|
|
||||||
use num::traits::ToPrimitive;
|
use num::traits::ToPrimitive;
|
||||||
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use {Datelike, Timelike};
|
use {Datelike, Timelike};
|
||||||
use Weekday;
|
use Weekday;
|
||||||
use div::div_rem;
|
use div::div_rem;
|
||||||
use duration::Duration;
|
use offset::{TimeZone, Offset, LocalResult, FixedOffset};
|
||||||
use offset::{TimeZone, Offset, LocalResult};
|
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
|
||||||
use offset::fixed::FixedOffset;
|
use DateTime;
|
||||||
use naive::date::NaiveDate;
|
|
||||||
use naive::time::NaiveTime;
|
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use super::{ParseResult, OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
|
use super::{ParseResult, OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
|
||||||
|
|
||||||
/// Parsed parts of date and time. There are two classes of methods:
|
/// Parsed parts of date and time. There are two classes of methods:
|
||||||
|
@ -45,13 +41,13 @@ pub struct Parsed {
|
||||||
/// Year modulo 100. Implies that the year is >= 1 BCE when set.
|
/// Year modulo 100. Implies that the year is >= 1 BCE when set.
|
||||||
pub year_mod_100: Option<i32>,
|
pub year_mod_100: Option<i32>,
|
||||||
|
|
||||||
/// Year in the [ISO week date](../../naive/date/index.html#week-date).
|
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date).
|
||||||
///
|
///
|
||||||
/// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and
|
/// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and
|
||||||
/// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
|
/// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
|
||||||
pub isoyear: Option<i32>,
|
pub isoyear: Option<i32>,
|
||||||
|
|
||||||
/// Year in the [ISO week date](../../naive/date/index.html#week-date), divided by 100.
|
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), divided by 100.
|
||||||
/// Implies that the year is >= 1 BCE when set.
|
/// Implies that the year is >= 1 BCE when set.
|
||||||
///
|
///
|
||||||
/// Due to the common usage, if this field is missing but
|
/// Due to the common usage, if this field is missing but
|
||||||
|
@ -59,7 +55,7 @@ pub struct Parsed {
|
||||||
/// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise.
|
/// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise.
|
||||||
pub isoyear_div_100: Option<i32>,
|
pub isoyear_div_100: Option<i32>,
|
||||||
|
|
||||||
/// Year in the [ISO week date](../../naive/date/index.html#week-date), modulo 100.
|
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), modulo 100.
|
||||||
/// Implies that the year is >= 1 BCE when set.
|
/// Implies that the year is >= 1 BCE when set.
|
||||||
pub isoyear_mod_100: Option<i32>,
|
pub isoyear_mod_100: Option<i32>,
|
||||||
|
|
||||||
|
@ -74,7 +70,7 @@ pub struct Parsed {
|
||||||
/// (0--53, 1--53 or 1--52 depending on the year).
|
/// (0--53, 1--53 or 1--52 depending on the year).
|
||||||
pub week_from_mon: Option<u32>,
|
pub week_from_mon: Option<u32>,
|
||||||
|
|
||||||
/// [ISO week number](../../naive/date/index.html#week-date)
|
/// [ISO week number](../naive/struct.NaiveDate.html#week-date)
|
||||||
/// (1--52 or 1--53 depending on the year).
|
/// (1--52 or 1--53 depending on the year).
|
||||||
pub isoweek: Option<u32>,
|
pub isoweek: Option<u32>,
|
||||||
|
|
||||||
|
@ -109,6 +105,9 @@ pub struct Parsed {
|
||||||
|
|
||||||
/// Offset from the local time to UTC, in seconds.
|
/// Offset from the local time to UTC, in seconds.
|
||||||
pub offset: Option<i32>,
|
pub offset: Option<i32>,
|
||||||
|
|
||||||
|
/// A dummy field to make this type not fully destructible (required for API stability).
|
||||||
|
_dummy: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
|
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
|
||||||
|
@ -122,14 +121,23 @@ fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parsed {
|
impl Default for Parsed {
|
||||||
/// Returns the initial value of parsed parts.
|
fn default() -> Parsed {
|
||||||
pub fn new() -> Parsed {
|
Parsed {
|
||||||
Parsed { year: None, year_div_100: None, year_mod_100: None, isoyear: None,
|
year: None, year_div_100: None, year_mod_100: None, isoyear: None,
|
||||||
isoyear_div_100: None, isoyear_mod_100: None, month: None,
|
isoyear_div_100: None, isoyear_mod_100: None, month: None,
|
||||||
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
|
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
|
||||||
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
|
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
|
||||||
second: None, nanosecond: None, timestamp: None, offset: None }
|
second: None, nanosecond: None, timestamp: None, offset: None,
|
||||||
|
_dummy: (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parsed {
|
||||||
|
/// Returns the initial value of parsed parts.
|
||||||
|
pub fn new() -> Parsed {
|
||||||
|
Parsed::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`year`](#structfield.year) field from given value.
|
/// Tries to set the [`year`](#structfield.year) field from given value.
|
||||||
|
@ -324,7 +332,10 @@ impl Parsed {
|
||||||
|
|
||||||
// verify the ISO week date.
|
// verify the ISO week date.
|
||||||
let verify_isoweekdate = |date: NaiveDate| {
|
let verify_isoweekdate = |date: NaiveDate| {
|
||||||
let (isoyear, isoweek, weekday) = date.isoweekdate();
|
let week = date.iso_week();
|
||||||
|
let isoyear = week.year();
|
||||||
|
let isoweek = week.week();
|
||||||
|
let weekday = date.weekday();
|
||||||
let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
|
let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
|
||||||
let (q, r) = div_rem(isoyear, 100);
|
let (q, r) = div_rem(isoyear, 100);
|
||||||
(Some(q), Some(r))
|
(Some(q), Some(r))
|
||||||
|
@ -383,7 +394,7 @@ impl Parsed {
|
||||||
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||||
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
|
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
|
||||||
weekday.num_days_from_sunday() as i32;
|
weekday.num_days_from_sunday() as i32;
|
||||||
let date = try!(newyear.checked_add(Duration::days(ndays as i64))
|
let date = try!(newyear.checked_add_signed(OldDuration::days(ndays as i64))
|
||||||
.ok_or(OUT_OF_RANGE));
|
.ok_or(OUT_OF_RANGE));
|
||||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||||
|
|
||||||
|
@ -408,7 +419,7 @@ impl Parsed {
|
||||||
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||||
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
|
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
|
||||||
weekday.num_days_from_monday() as i32;
|
weekday.num_days_from_monday() as i32;
|
||||||
let date = try!(newyear.checked_add(Duration::days(ndays as i64))
|
let date = try!(newyear.checked_add_signed(OldDuration::days(ndays as i64))
|
||||||
.ok_or(OUT_OF_RANGE));
|
.ok_or(OUT_OF_RANGE));
|
||||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||||
|
|
||||||
|
@ -527,7 +538,7 @@ impl Parsed {
|
||||||
// it's okay, just do not try to overwrite the existing field.
|
// it's okay, just do not try to overwrite the existing field.
|
||||||
59 => {}
|
59 => {}
|
||||||
// `datetime` is known to be off by one second.
|
// `datetime` is known to be off by one second.
|
||||||
0 => { datetime = datetime - Duration::seconds(1); }
|
0 => { datetime = datetime - OldDuration::seconds(1); }
|
||||||
// otherwise it is impossible.
|
// otherwise it is impossible.
|
||||||
_ => return Err(IMPOSSIBLE)
|
_ => return Err(IMPOSSIBLE)
|
||||||
}
|
}
|
||||||
|
@ -593,20 +604,13 @@ impl Parsed {
|
||||||
let nanosecond = self.nanosecond.unwrap_or(0);
|
let nanosecond = self.nanosecond.unwrap_or(0);
|
||||||
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
|
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
|
||||||
let dt = try!(dt.ok_or(OUT_OF_RANGE));
|
let dt = try!(dt.ok_or(OUT_OF_RANGE));
|
||||||
|
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
|
||||||
// we cannot handle offsets larger than i32 at all. give up if so.
|
|
||||||
// we can instead make `to_naive_datetime_with_offset` to accept i64, but this makes
|
|
||||||
// the algorithm too complex and tons of edge cases. i32 should be enough for all.
|
|
||||||
let offset = tz.offset_from_utc_datetime(&dt).local_minus_utc().num_seconds();
|
|
||||||
guessed_offset = try!(offset.to_i32().ok_or(OUT_OF_RANGE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
|
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
|
||||||
let check_offset = |dt: &DateTime<Tz>| {
|
let check_offset = |dt: &DateTime<Tz>| {
|
||||||
if let Some(offset) = self.offset {
|
if let Some(offset) = self.offset {
|
||||||
let delta = dt.offset().local_minus_utc().num_seconds();
|
dt.offset().fix().local_minus_utc() == offset
|
||||||
// if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway.
|
|
||||||
delta.to_i32() == Some(offset)
|
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -637,11 +641,8 @@ mod tests {
|
||||||
use super::super::{OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
|
use super::super::{OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
|
||||||
use Datelike;
|
use Datelike;
|
||||||
use Weekday::*;
|
use Weekday::*;
|
||||||
use naive::date::{self, NaiveDate};
|
use naive::{MIN_DATE, MAX_DATE, NaiveDate, NaiveTime};
|
||||||
use naive::time::NaiveTime;
|
use offset::{TimeZone, Utc, FixedOffset};
|
||||||
use offset::TimeZone;
|
|
||||||
use offset::utc::UTC;
|
|
||||||
use offset::fixed::FixedOffset;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parsed_set_fields() {
|
fn test_parsed_set_fields() {
|
||||||
|
@ -760,7 +761,7 @@ mod tests {
|
||||||
ymd(0, 1, 1));
|
ymd(0, 1, 1));
|
||||||
assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1),
|
assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1),
|
||||||
Err(OUT_OF_RANGE));
|
Err(OUT_OF_RANGE));
|
||||||
let max_year = date::MAX.year();
|
let max_year = MAX_DATE.year();
|
||||||
assert_eq!(parse!(year_div_100: max_year / 100,
|
assert_eq!(parse!(year_div_100: max_year / 100,
|
||||||
year_mod_100: max_year % 100, month: 1, day: 1),
|
year_mod_100: max_year % 100, month: 1, day: 1),
|
||||||
ymd(max_year, 1, 1));
|
ymd(max_year, 1, 1));
|
||||||
|
@ -958,15 +959,18 @@ mod tests {
|
||||||
ymdhmsn(2014,12,31, 4,26,40,12_345_678));
|
ymdhmsn(2014,12,31, 4,26,40,12_345_678));
|
||||||
|
|
||||||
// more timestamps
|
// more timestamps
|
||||||
let max_days_from_year_1970 = date::MAX - NaiveDate::from_ymd(1970,1,1);
|
let max_days_from_year_1970 =
|
||||||
let year_0_from_year_1970 = NaiveDate::from_ymd(0,1,1) - NaiveDate::from_ymd(1970,1,1);
|
MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1));
|
||||||
let min_days_from_year_1970 = date::MIN - NaiveDate::from_ymd(1970,1,1);
|
let year_0_from_year_1970 =
|
||||||
|
NaiveDate::from_ymd(0,1,1).signed_duration_since(NaiveDate::from_ymd(1970,1,1));
|
||||||
|
let min_days_from_year_1970 =
|
||||||
|
MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1));
|
||||||
assert_eq!(parse!(timestamp: min_days_from_year_1970.num_seconds()),
|
assert_eq!(parse!(timestamp: min_days_from_year_1970.num_seconds()),
|
||||||
ymdhms(date::MIN.year(),1,1, 0,0,0));
|
ymdhms(MIN_DATE.year(),1,1, 0,0,0));
|
||||||
assert_eq!(parse!(timestamp: year_0_from_year_1970.num_seconds()),
|
assert_eq!(parse!(timestamp: year_0_from_year_1970.num_seconds()),
|
||||||
ymdhms(0,1,1, 0,0,0));
|
ymdhms(0,1,1, 0,0,0));
|
||||||
assert_eq!(parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
|
assert_eq!(parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
|
||||||
ymdhms(date::MAX.year(),12,31, 23,59,59));
|
ymdhms(MAX_DATE.year(),12,31, 23,59,59));
|
||||||
|
|
||||||
// leap seconds #1: partial fields
|
// leap seconds #1: partial fields
|
||||||
assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
|
assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
|
||||||
|
@ -1051,11 +1055,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// single result from ymdhms
|
// single result from ymdhms
|
||||||
assert_eq!(parse!(UTC;
|
assert_eq!(parse!(Utc;
|
||||||
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
|
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
|
||||||
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
|
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
|
||||||
Ok(UTC.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
|
Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
|
||||||
assert_eq!(parse!(UTC;
|
assert_eq!(parse!(Utc;
|
||||||
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
|
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
|
||||||
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
|
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
|
||||||
Err(IMPOSSIBLE));
|
Err(IMPOSSIBLE));
|
||||||
|
@ -1070,9 +1074,9 @@ mod tests {
|
||||||
.and_hms_nano(13, 26, 40, 12_345_678)));
|
.and_hms_nano(13, 26, 40, 12_345_678)));
|
||||||
|
|
||||||
// single result from timestamp
|
// single result from timestamp
|
||||||
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 0),
|
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 0),
|
||||||
Ok(UTC.ymd(2014, 12, 31).and_hms(4, 26, 40)));
|
Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40)));
|
||||||
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 32400),
|
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400),
|
||||||
Err(IMPOSSIBLE));
|
Err(IMPOSSIBLE));
|
||||||
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
|
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
|
||||||
Err(IMPOSSIBLE));
|
Err(IMPOSSIBLE));
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -174,7 +173,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||||
fn next(&mut self) -> Option<Item<'a>> {
|
fn next(&mut self) -> Option<Item<'a>> {
|
||||||
// we have some reconstructed items to return
|
// we have some reconstructed items to return
|
||||||
if !self.recons.is_empty() {
|
if !self.recons.is_empty() {
|
||||||
let item = self.recons[0];
|
let item = self.recons[0].clone();
|
||||||
self.recons = &self.recons[1..];
|
self.recons = &self.recons[1..];
|
||||||
return Some(item);
|
return Some(item);
|
||||||
}
|
}
|
||||||
|
@ -293,8 +292,8 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||||
// adjust `item` if we have any padding modifier
|
// adjust `item` if we have any padding modifier
|
||||||
if let Some(new_pad) = pad_override {
|
if let Some(new_pad) = pad_override {
|
||||||
match item {
|
match item {
|
||||||
Item::Numeric(kind, _pad) if self.recons.is_empty() =>
|
Item::Numeric(ref kind, _pad) if self.recons.is_empty() =>
|
||||||
Some(Item::Numeric(kind, new_pad)),
|
Some(Item::Numeric(kind.clone(), new_pad)),
|
||||||
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
406
src/lib.rs
406
src/lib.rs
|
@ -1,10 +1,9 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014-2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
//! # Chrono 0.2.25
|
//! # Chrono 0.4.0 (not yet released)
|
||||||
//!
|
//!
|
||||||
//! Date and time handling for Rust. (also known as `rust-chrono`)
|
//! Date and time handling for Rust.
|
||||||
//! It aims to be a feature-complete superset of
|
//! It aims to be a feature-complete superset of
|
||||||
//! the [time](https://github.com/rust-lang-deprecated/time) library.
|
//! the [time](https://github.com/rust-lang-deprecated/time) library.
|
||||||
//! In particular,
|
//! In particular,
|
||||||
|
@ -14,7 +13,7 @@
|
||||||
//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
|
//! * 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,
|
//! There were several previous attempts to bring a good date and time library to Rust,
|
||||||
//! which Chrono builts upon and should acknowledge:
|
//! which Chrono builds upon and should acknowledge:
|
||||||
//!
|
//!
|
||||||
//! * [Initial research on
|
//! * [Initial research on
|
||||||
//! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
|
//! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
|
||||||
|
@ -27,7 +26,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! chrono = "0.2"
|
//! chrono = "0.4"
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Or, if you want [Serde](https://github.com/serde-rs/serde) or
|
//! Or, if you want [Serde](https://github.com/serde-rs/serde) or
|
||||||
|
@ -36,7 +35,7 @@
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
|
//! chrono = { version = "0.4", features = ["serde", "rustc-serialize"] }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Then put this in your crate root:
|
//! Then put this in your crate root:
|
||||||
|
@ -45,20 +44,39 @@
|
||||||
//! extern crate chrono;
|
//! extern crate chrono;
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! Avoid using `use chrono::*;` as Chrono exports several modules other than types.
|
||||||
|
//! If you prefer the glob imports, use the following instead:
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use chrono::prelude::*;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! ## Overview
|
//! ## Overview
|
||||||
//!
|
//!
|
||||||
//! ### Duration
|
//! ### Duration
|
||||||
//!
|
//!
|
||||||
//! [**`Duration`**](./struct.Duration.html)
|
//! Chrono currently uses
|
||||||
//! represents the magnitude of a time span. `Duration` used to be provided by Chrono.
|
//! the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type
|
||||||
//! It has been moved to the `time` crate as the
|
//! from the `time` crate to represent the magnitude of a time span.
|
||||||
//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
|
//! Since this has the same name to the newer, standard type for duration,
|
||||||
//! still re-exported from Chrono.
|
//! 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.
|
||||||
|
//!
|
||||||
|
//! 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://doc.rust-lang.org/time/time/struct.Duration.html#method.from_std)
|
||||||
|
//! and
|
||||||
|
//! [`Duration::to_std`](https://doc.rust-lang.org/time/time/struct.Duration.html#method.to_std)
|
||||||
|
//! methods.
|
||||||
//!
|
//!
|
||||||
//! ### Date and Time
|
//! ### Date and Time
|
||||||
//!
|
//!
|
||||||
//! Chrono provides a
|
//! Chrono provides a
|
||||||
//! [**`DateTime`**](./datetime/struct.DateTime.html)
|
//! [**`DateTime`**](./struct.DateTime.html)
|
||||||
//! type to represent a date and a time in a timezone.
|
//! type to represent a date and a time in a timezone.
|
||||||
//!
|
//!
|
||||||
//! For more abstract moment-in-time tracking such as internal timekeeping
|
//! For more abstract moment-in-time tracking such as internal timekeeping
|
||||||
|
@ -73,11 +91,11 @@
|
||||||
//! which defines how the local date is converted to and back from the UTC date.
|
//! which defines how the local date is converted to and back from the UTC date.
|
||||||
//! There are three well-known `TimeZone` implementations:
|
//! There are three well-known `TimeZone` implementations:
|
||||||
//!
|
//!
|
||||||
//! * [**`UTC`**](./offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient.
|
//! * [**`Utc`**](./offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
|
||||||
//!
|
//!
|
||||||
//! * [**`Local`**](./offset/local/struct.Local.html) specifies the system local time zone.
|
//! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone.
|
||||||
//!
|
//!
|
||||||
//! * [**`FixedOffset`**](./offset/fixed/struct.FixedOffset.html) specifies
|
//! * [**`FixedOffset`**](./offset/struct.FixedOffset.html) specifies
|
||||||
//! an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
|
//! an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
|
||||||
//! This often results from the parsed textual date and time.
|
//! This often results from the parsed textual date and time.
|
||||||
//! Since it stores the most information and does not depend on the system environment,
|
//! Since it stores the most information and does not depend on the system environment,
|
||||||
|
@ -85,43 +103,44 @@
|
||||||
//!
|
//!
|
||||||
//! `DateTime`s with different `TimeZone` types are distinct and do not mix,
|
//! `DateTime`s with different `TimeZone` types are distinct and do not mix,
|
||||||
//! but can be converted to each other using
|
//! but can be converted to each other using
|
||||||
//! the [`DateTime::with_timezone`](./datetime/struct.DateTime.html#method.with_timezone) method.
|
//! the [`DateTime::with_timezone`](./struct.DateTime.html#method.with_timezone) method.
|
||||||
//!
|
//!
|
||||||
//! You can get the current date and time in the UTC time zone
|
//! You can get the current date and time in the UTC time zone
|
||||||
//! ([`UTC::now()`](./offset/utc/struct.UTC.html#method.now))
|
//! ([`Utc::now()`](./offset/struct.Utc.html#method.now))
|
||||||
//! or in the local time zone
|
//! or in the local time zone
|
||||||
//! ([`Local::now()`](./offset/local/struct.Local.html#method.now)).
|
//! ([`Local::now()`](./offset/struct.Local.html#method.now)).
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! use chrono::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
|
//! 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 local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
|
||||||
//! # let _ = utc; let _ = local;
|
//! # let _ = utc; let _ = local;
|
||||||
//! ~~~~
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Alternatively, you can create your own date and time.
|
//! Alternatively, you can create your own date and time.
|
||||||
//! This is a bit verbose due to Rust's lack of function and method overloading,
|
//! 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.
|
//! but in turn we get a rich combination of initialization methods.
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! use chrono::prelude::*;
|
||||||
|
//! use chrono::offset::LocalResult;
|
||||||
//!
|
//!
|
||||||
//! let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
|
//! let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
|
||||||
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
|
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
|
||||||
//! assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
|
//! assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
|
||||||
//! // July 8 is Tuesday in ISO week 28 of the year 2014.
|
//! // July 8 is Tuesday in ISO week 28 of the year 2014.
|
||||||
//! assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
|
//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
|
||||||
//!
|
//!
|
||||||
//! let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
|
//! let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
|
||||||
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
|
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
|
||||||
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
|
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
|
||||||
//!
|
//!
|
||||||
//! // dynamic verification
|
//! // dynamic verification
|
||||||
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
|
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
|
||||||
//! LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
|
//! LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
|
||||||
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
|
//! assert_eq!(Utc.ymd_opt(2014, 7, 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);
|
//! 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.
|
//! // 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.
|
//! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
|
||||||
|
@ -129,7 +148,7 @@
|
||||||
//! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
|
//! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
|
||||||
//! assert_eq!(dt, fixed_dt);
|
//! assert_eq!(dt, fixed_dt);
|
||||||
//! # let _ = local_dt;
|
//! # let _ = local_dt;
|
||||||
//! ~~~~
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Various properties are available to the date and time, and can be altered individually.
|
//! 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
|
//! Most of them are defined in the traits [`Datelike`](./trait.Datelike.html) and
|
||||||
|
@ -137,8 +156,10 @@
|
||||||
//! Addition and subtraction is also supported.
|
//! Addition and subtraction is also supported.
|
||||||
//! The following illustrates most supported operations to the date and time:
|
//! The following illustrates most supported operations to the date and time:
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! # extern crate chrono; extern crate time; fn main() {
|
||||||
|
//! use chrono::prelude::*;
|
||||||
|
//! use time::Duration;
|
||||||
//!
|
//!
|
||||||
//! # /* we intentionally fake the datetime...
|
//! # /* we intentionally fake the datetime...
|
||||||
//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`:
|
||||||
|
@ -156,9 +177,9 @@
|
||||||
//! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
//! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
|
||||||
//!
|
//!
|
||||||
//! // time zone accessor and manipulation
|
//! // time zone accessor and manipulation
|
||||||
//! assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
|
//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
|
||||||
//! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
//! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
|
||||||
//! assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
//! assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
|
||||||
//!
|
//!
|
||||||
//! // a sample of property manipulations (validates dynamically)
|
//! // 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(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
|
||||||
|
@ -166,28 +187,31 @@
|
||||||
//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
|
//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
|
||||||
//!
|
//!
|
||||||
//! // arithmetic operations
|
//! // arithmetic operations
|
||||||
//! assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
|
//! let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
|
||||||
//! Duration::seconds(-2 * 3600 + 2));
|
//! let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
|
||||||
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
|
//! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
|
||||||
//! UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
|
//! assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
|
||||||
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
|
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
|
||||||
//! UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
|
//! Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
|
||||||
//! ~~~~
|
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
|
||||||
|
//! Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Formatting is done via the [`format`](./datetime/struct.DateTime.html#method.format) method,
|
//! Formatting is done via the [`format`](./struct.DateTime.html#method.format) method,
|
||||||
//! which format is equivalent to the familiar `strftime` format.
|
//! which format is equivalent to the familiar `strftime` format.
|
||||||
//! (See the [`format::strftime` module documentation](./format/strftime/index.html#specifiers)
|
//! (See the [`format::strftime` module documentation](./format/strftime/index.html#specifiers)
|
||||||
//! for full syntax.)
|
//! for full syntax.)
|
||||||
//!
|
//!
|
||||||
//! The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
//! The default `to_string` method and `{:?}` specifier also give a reasonable representation.
|
||||||
//! Chrono also provides [`to_rfc2822`](./datetime/struct.DateTime.html#method.to_rfc2822) and
|
//! Chrono also provides [`to_rfc2822`](./struct.DateTime.html#method.to_rfc2822) and
|
||||||
//! [`to_rfc3339`](./datetime/struct.DateTime.html#method.to_rfc3339) methods
|
//! [`to_rfc3339`](./struct.DateTime.html#method.to_rfc3339) methods
|
||||||
//! for well-known formats.
|
//! for well-known formats.
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! use chrono::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||||
//! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
|
//! assert_eq!(dt.format("%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("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
|
||||||
//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
|
//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
|
||||||
|
@ -196,24 +220,24 @@
|
||||||
//! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
|
//! 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!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
|
||||||
//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
|
//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
|
||||||
//! ~~~~
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Parsing can be done with three methods:
|
//! Parsing can be done with three methods:
|
||||||
//!
|
//!
|
||||||
//! 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
|
//! 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
|
//! (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
|
//! on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
|
||||||
//! `DateTime<Local>` values. This parses what the `{:?}`
|
//! `DateTime<Local>` values. This parses what the `{:?}`
|
||||||
//! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
|
//! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
|
||||||
//! format specifier prints, and requires the offset to be present.
|
//! format specifier prints, and requires the offset to be present.
|
||||||
//!
|
//!
|
||||||
//! 2. [`DateTime::parse_from_str`](./datetime/struct.DateTime.html#method.parse_from_str) parses
|
//! 2. [`DateTime::parse_from_str`](./struct.DateTime.html#method.parse_from_str) parses
|
||||||
//! a date and time with offsets and returns `DateTime<FixedOffset>`.
|
//! 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.
|
//! 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.
|
//! It *cannot* be used when the offset can be missing.
|
||||||
//! [`DateTime::parse_from_rfc2822`](./datetime/struct.DateTime.html#method.parse_from_rfc2822)
|
//! [`DateTime::parse_from_rfc2822`](./struct.DateTime.html#method.parse_from_rfc2822)
|
||||||
//! and
|
//! and
|
||||||
//! [`DateTime::parse_from_rfc3339`](./datetime/struct.DateTime.html#method.parse_from_rfc3339)
|
//! [`DateTime::parse_from_rfc3339`](./struct.DateTime.html#method.parse_from_rfc3339)
|
||||||
//! are similar but for well-known formats.
|
//! are similar but for well-known formats.
|
||||||
//!
|
//!
|
||||||
//! 3. [`Offset::datetime_from_str`](./offset/trait.TimeZone.html#method.datetime_from_str) is
|
//! 3. [`Offset::datetime_from_str`](./offset/trait.TimeZone.html#method.datetime_from_str) is
|
||||||
|
@ -225,15 +249,15 @@
|
||||||
//! More detailed control over the parsing process is available via
|
//! More detailed control over the parsing process is available via
|
||||||
//! [`format`](./format/index.html) module.
|
//! [`format`](./format/index.html) module.
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! use chrono::prelude::*;
|
||||||
//!
|
//!
|
||||||
//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
|
||||||
//! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
|
//! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
|
||||||
//!
|
//!
|
||||||
//! // method 1
|
//! // method 1
|
||||||
//! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
|
//! 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<Utc>>(), Ok(dt.clone()));
|
||||||
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
|
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
|
||||||
//!
|
//!
|
||||||
//! // method 2
|
//! // method 2
|
||||||
|
@ -244,58 +268,59 @@
|
||||||
//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
||||||
//!
|
//!
|
||||||
//! // method 3
|
//! // 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("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()));
|
//! 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!
|
//! // oops, the year is missing!
|
||||||
//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
//! 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!
|
//! // 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());
|
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
|
||||||
//! // oops, the weekday is incorrect!
|
//! // oops, the weekday is incorrect!
|
||||||
//! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
||||||
//! ~~~~
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### Individual date
|
//! ### Individual date
|
||||||
//!
|
//!
|
||||||
//! Chrono also provides an individual date type ([**`Date`**](./date/struct.Date.html)).
|
//! 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.
|
//! 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.
|
//! Most operations available to `DateTime` are also available to `Date` whenever appropriate.
|
||||||
//!
|
//!
|
||||||
//! ~~~~ {.rust}
|
//! ```rust
|
||||||
//! use chrono::*;
|
//! use chrono::prelude::*;
|
||||||
|
//! use chrono::offset::LocalResult;
|
||||||
//!
|
//!
|
||||||
//! # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
|
//! # // 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!(Utc::today(), Utc::now().date());
|
||||||
//! assert_eq!(Local::today(), Local::now().date());
|
//! assert_eq!(Local::today(), Local::now().date());
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
|
//! assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
|
||||||
//! assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
|
//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
|
||||||
//! assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
|
//! assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
|
||||||
//! "070809");
|
//! "070809");
|
||||||
//! ~~~~
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
|
//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
|
||||||
//!
|
//!
|
||||||
//! `DateTime` has [`date`](./datetime/struct.DateTime.html#method.date) method
|
//! `DateTime` has [`date`](./struct.DateTime.html#method.date) method
|
||||||
//! which returns a `Date` which represents its date component.
|
//! which returns a `Date` which represents its date component.
|
||||||
//! There is also a [`time`](./datetime/struct.DateTime.html#method.time) method,
|
//! There is also a [`time`](./struct.DateTime.html#method.time) method,
|
||||||
//! which simply returns a naive local time described below.
|
//! which simply returns a naive local time described below.
|
||||||
//!
|
//!
|
||||||
//! ### Naive date and time
|
//! ### Naive date and time
|
||||||
//!
|
//!
|
||||||
//! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
|
//! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
|
||||||
//! as [**`NaiveDate`**](./naive/date/struct.NaiveDate.html),
|
//! as [**`NaiveDate`**](./naive/struct.NaiveDate.html),
|
||||||
//! [**`NaiveTime`**](./naive/time/struct.NaiveTime.html) and
|
//! [**`NaiveTime`**](./naive/struct.NaiveTime.html) and
|
||||||
//! [**`NaiveDateTime`**](./naive/datetime/struct.NaiveDateTime.html) respectively.
|
//! [**`NaiveDateTime`**](./naive/struct.NaiveDateTime.html) respectively.
|
||||||
//!
|
//!
|
||||||
//! They have almost equivalent interfaces as their timezone-aware twins,
|
//! They have almost equivalent interfaces as their timezone-aware twins,
|
||||||
//! but are not associated to time zones obviously and can be quite low-level.
|
//! 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.
|
//! They are mostly useful for building blocks for higher-level types.
|
||||||
//!
|
//!
|
||||||
//! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
|
//! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
|
||||||
//! [`naive_local`](./datetime/struct.DateTime.html#method.naive_local) returns
|
//! [`naive_local`](./struct.DateTime.html#method.naive_local) returns
|
||||||
//! a view to the naive local time,
|
//! a view to the naive local time,
|
||||||
//! and [`naive_utc`](./datetime/struct.DateTime.html#method.naive_utc) returns
|
//! and [`naive_utc`](./struct.DateTime.html#method.naive_utc) returns
|
||||||
//! a view to the naive UTC time.
|
//! a view to the naive UTC time.
|
||||||
//!
|
//!
|
||||||
//! ## Limitations
|
//! ## Limitations
|
||||||
|
@ -307,7 +332,7 @@
|
||||||
//! Time types are limited in the nanosecond accuracy.
|
//! Time types are limited in the nanosecond accuracy.
|
||||||
//!
|
//!
|
||||||
//! [Leap seconds are supported in the representation but
|
//! [Leap seconds are supported in the representation but
|
||||||
//! Chrono doesn't try to make use of them](./naive/time/index.html#leap-second-handling).
|
//! 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.)
|
//! (The main reason is that leap seconds are not really predictable.)
|
||||||
//! Almost *every* operation over the possible leap seconds will ignore them.
|
//! Almost *every* operation over the possible leap seconds will ignore them.
|
||||||
//! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
//! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
||||||
|
@ -316,47 +341,49 @@
|
||||||
//! Chrono inherently does not support an inaccurate or partial date and time representation.
|
//! 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.
|
//! 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
|
//! For example, "a month later" of 2014-01-30 is not well-defined
|
||||||
//! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
|
//! and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`.
|
||||||
//!
|
//!
|
||||||
//! Advanced time zone handling is not yet supported (but is planned in 0.3).
|
//! 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://lifthrasiir.github.io/rust-chrono/")]
|
#![doc(html_root_url = "https://docs.rs/chrono/0.4.0/")]
|
||||||
|
|
||||||
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507
|
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
extern crate time as stdtime;
|
extern crate time as oldtime;
|
||||||
extern crate num;
|
extern crate num;
|
||||||
#[cfg(feature = "rustc-serialize")]
|
#[cfg(feature = "rustc-serialize")]
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
extern crate serde;
|
extern crate serde as serdelib;
|
||||||
|
|
||||||
pub use duration::Duration;
|
// this reexport is to aid the transition and should not be in the prelude!
|
||||||
pub use offset::{TimeZone, Offset, LocalResult};
|
pub use oldtime::Duration;
|
||||||
pub use offset::utc::UTC;
|
|
||||||
pub use offset::fixed::FixedOffset;
|
#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset, Local};
|
||||||
pub use offset::local::Local;
|
#[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime};
|
||||||
pub use naive::date::NaiveDate;
|
pub use date::{Date, MIN_DATE, MAX_DATE};
|
||||||
pub use naive::time::NaiveTime;
|
|
||||||
pub use naive::datetime::NaiveDateTime;
|
|
||||||
pub use date::Date;
|
|
||||||
pub use datetime::DateTime;
|
pub use datetime::DateTime;
|
||||||
|
#[cfg(feature = "rustc-serialize")] pub use datetime::TsSeconds;
|
||||||
pub use format::{ParseError, ParseResult};
|
pub use format::{ParseError, ParseResult};
|
||||||
|
|
||||||
|
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
|
||||||
|
pub mod prelude {
|
||||||
|
#[doc(no_inline)] pub use {Datelike, Timelike, Weekday};
|
||||||
|
#[doc(no_inline)] pub use {TimeZone, Offset};
|
||||||
|
#[doc(no_inline)] pub use {Utc, FixedOffset, Local};
|
||||||
|
#[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime};
|
||||||
|
#[doc(no_inline)] pub use Date;
|
||||||
|
#[doc(no_inline)] pub use DateTime;
|
||||||
|
}
|
||||||
|
|
||||||
// useful throughout the codebase
|
// useful throughout the codebase
|
||||||
macro_rules! try_opt {
|
macro_rules! try_opt {
|
||||||
($e:expr) => (match $e { Some(v) => v, None => return None })
|
($e:expr) => (match $e { Some(v) => v, None => return None })
|
||||||
}
|
}
|
||||||
|
|
||||||
mod div;
|
mod div;
|
||||||
pub mod duration {
|
|
||||||
//! ISO 8601 duration.
|
|
||||||
//!
|
|
||||||
//! This used to be a part of rust-chrono,
|
|
||||||
//! but has been subsequently merged into Rust's standard library.
|
|
||||||
pub use stdtime::Duration;
|
|
||||||
}
|
|
||||||
pub mod offset;
|
pub mod offset;
|
||||||
pub mod naive {
|
pub mod naive {
|
||||||
//! Date and time types which do not concern about the timezones.
|
//! Date and time types which do not concern about the timezones.
|
||||||
|
@ -364,20 +391,43 @@ pub mod naive {
|
||||||
//! They are primarily building blocks for other types
|
//! They are primarily building blocks for other types
|
||||||
//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
|
//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
|
||||||
//! but can be also used for the simpler date and time handling.
|
//! but can be also used for the simpler date and time handling.
|
||||||
pub mod date;
|
|
||||||
pub mod time;
|
mod internals;
|
||||||
pub mod datetime;
|
mod date;
|
||||||
|
mod isoweek;
|
||||||
|
mod time;
|
||||||
|
mod datetime;
|
||||||
|
|
||||||
|
pub use self::date::{NaiveDate, MIN_DATE, MAX_DATE};
|
||||||
|
pub use self::isoweek::IsoWeek;
|
||||||
|
pub use self::time::NaiveTime;
|
||||||
|
pub use self::datetime::{NaiveDateTime, TsSeconds};
|
||||||
|
|
||||||
|
/// Tools to help serializing/deserializing naive types.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub mod serde {
|
||||||
|
pub use super::datetime::serde::*;
|
||||||
}
|
}
|
||||||
pub mod date;
|
}
|
||||||
pub mod datetime;
|
mod date;
|
||||||
|
mod datetime;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
|
||||||
|
/// Ser/de helpers
|
||||||
|
///
|
||||||
|
/// The various modules in here are intended to be used with serde's [`with`
|
||||||
|
/// annotation](https://serde.rs/attributes.html#field-attributes).
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub mod serde {
|
||||||
|
pub use super::datetime::serde::*;
|
||||||
|
}
|
||||||
|
|
||||||
/// The day of week.
|
/// The day of week.
|
||||||
///
|
///
|
||||||
/// The order of the days of week depends on the context.
|
/// The order of the days of week depends on the context.
|
||||||
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
|
/// (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.
|
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
|
||||||
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
||||||
pub enum Weekday {
|
pub enum Weekday {
|
||||||
/// Monday.
|
/// Monday.
|
||||||
|
@ -539,10 +589,131 @@ impl num::traits::FromPrimitive for Weekday {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// An error resulting from reading `Weekday` value with `FromStr`.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct ParseWeekdayError {
|
||||||
|
_dummy: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ParseWeekdayError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "ParseWeekdayError {{ .. }}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
mod weekday_serde {
|
||||||
|
use super::Weekday;
|
||||||
|
use std::fmt;
|
||||||
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
impl ser::Serialize for Weekday {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: ser::Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_str(&format!("{:?}", self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WeekdayVisitor;
|
||||||
|
|
||||||
|
impl<'de> de::Visitor<'de> for WeekdayVisitor {
|
||||||
|
type Value = Weekday;
|
||||||
|
|
||||||
|
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "Weekday")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where E: de::Error
|
||||||
|
{
|
||||||
|
value.parse().map_err(|_| E::custom("short or long weekday names expected"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> de::Deserialize<'de> for Weekday {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: de::Deserializer<'de>
|
||||||
|
{
|
||||||
|
deserializer.deserialize_str(WeekdayVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_serialize() {
|
||||||
|
use self::serde_json::to_string;
|
||||||
|
use Weekday::*;
|
||||||
|
|
||||||
|
let cases: Vec<(Weekday, &str)> = vec![
|
||||||
|
(Mon, "\"Mon\""),
|
||||||
|
(Tue, "\"Tue\""),
|
||||||
|
(Wed, "\"Wed\""),
|
||||||
|
(Thu, "\"Thu\""),
|
||||||
|
(Fri, "\"Fri\""),
|
||||||
|
(Sat, "\"Sat\""),
|
||||||
|
(Sun, "\"Sun\""),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (weekday, expected_str) in cases {
|
||||||
|
let string = to_string(&weekday).unwrap();
|
||||||
|
assert_eq!(string, expected_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_deserialize() {
|
||||||
|
use self::serde_json::from_str;
|
||||||
|
use Weekday::*;
|
||||||
|
|
||||||
|
let cases: Vec<(&str, Weekday)> = vec![
|
||||||
|
("\"mon\"", Mon),
|
||||||
|
("\"MONDAY\"", Mon),
|
||||||
|
("\"MonDay\"", Mon),
|
||||||
|
("\"mOn\"", Mon),
|
||||||
|
("\"tue\"", Tue),
|
||||||
|
("\"tuesday\"", Tue),
|
||||||
|
("\"wed\"", Wed),
|
||||||
|
("\"wednesday\"", Wed),
|
||||||
|
("\"thu\"", Thu),
|
||||||
|
("\"thursday\"", Thu),
|
||||||
|
("\"fri\"", Fri),
|
||||||
|
("\"friday\"", Fri),
|
||||||
|
("\"sat\"", Sat),
|
||||||
|
("\"saturday\"", Sat),
|
||||||
|
("\"sun\"", Sun),
|
||||||
|
("\"sunday\"", Sun),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (str, expected_weekday) in cases {
|
||||||
|
let weekday = from_str::<Weekday>(str).unwrap();
|
||||||
|
assert_eq!(weekday, expected_weekday);
|
||||||
|
}
|
||||||
|
|
||||||
|
let errors: Vec<&str> = vec![
|
||||||
|
"\"not a weekday\"",
|
||||||
|
"\"monDAYs\"",
|
||||||
|
"\"mond\"",
|
||||||
|
"mon",
|
||||||
|
"\"thur\"",
|
||||||
|
"\"thurs\"",
|
||||||
|
];
|
||||||
|
|
||||||
|
for str in errors {
|
||||||
|
from_str::<Weekday>(str).unwrap_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The common set of methods for date component.
|
/// The common set of methods for date component.
|
||||||
pub trait Datelike: Sized {
|
pub trait Datelike: Sized {
|
||||||
/// Returns the year number in the [calendar date](./naive/date/index.html#calendar-date).
|
/// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date).
|
||||||
fn year(&self) -> i32;
|
fn year(&self) -> i32;
|
||||||
|
|
||||||
/// Returns the absolute year number starting from 1 with a boolean flag,
|
/// Returns the absolute year number starting from 1 with a boolean flag,
|
||||||
|
@ -590,9 +761,8 @@ pub trait Datelike: Sized {
|
||||||
/// Returns the day of week.
|
/// Returns the day of week.
|
||||||
fn weekday(&self) -> Weekday;
|
fn weekday(&self) -> Weekday;
|
||||||
|
|
||||||
/// Returns the ISO week date: an adjusted year, week number and day of week.
|
/// Returns the ISO week.
|
||||||
/// The adjusted year may differ from that of the calendar date.
|
fn iso_week(&self) -> IsoWeek;
|
||||||
fn isoweekdate(&self) -> (i32, u32, Weekday);
|
|
||||||
|
|
||||||
/// Makes a new value with the year number changed.
|
/// Makes a new value with the year number changed.
|
||||||
///
|
///
|
||||||
|
@ -670,7 +840,7 @@ pub trait Timelike: Sized {
|
||||||
|
|
||||||
/// Returns the number of nanoseconds since the whole non-leap second.
|
/// Returns the number of nanoseconds since the whole non-leap second.
|
||||||
/// The range from 1,000,000,000 to 1,999,999,999 represents
|
/// The range from 1,000,000,000 to 1,999,999,999 represents
|
||||||
/// the [leap second](./naive/time/index.html#leap-second-handling).
|
/// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling).
|
||||||
fn nanosecond(&self) -> u32;
|
fn nanosecond(&self) -> u32;
|
||||||
|
|
||||||
/// Makes a new value with the hour number changed.
|
/// Makes a new value with the hour number changed.
|
||||||
|
@ -708,7 +878,7 @@ pub trait Timelike: Sized {
|
||||||
fn test_readme_doomsday() {
|
fn test_readme_doomsday() {
|
||||||
use num::iter::range_inclusive;
|
use num::iter::range_inclusive;
|
||||||
|
|
||||||
for y in range_inclusive(naive::date::MIN.year(), naive::date::MAX.year()) {
|
for y in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) {
|
||||||
// even months
|
// even months
|
||||||
let d4 = NaiveDate::from_ymd(y, 4, 4);
|
let d4 = NaiveDate::from_ymd(y, 4, 4);
|
||||||
let d6 = NaiveDate::from_ymd(y, 6, 6);
|
let d6 = NaiveDate::from_ymd(y, 6, 6);
|
||||||
|
|
1428
src/naive/date.rs
1428
src/naive/date.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,778 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#![allow(dead_code)] // some internal methods have been left for consistency
|
||||||
|
|
||||||
|
use std::{i32, fmt};
|
||||||
|
use num::traits::FromPrimitive;
|
||||||
|
use Weekday;
|
||||||
|
use div::{div_rem, mod_floor};
|
||||||
|
|
||||||
|
/// The internal date representation. This also includes the packed `Mdf` value.
|
||||||
|
pub type DateImpl = i32;
|
||||||
|
|
||||||
|
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
|
||||||
|
pub 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).
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub struct YearFlags(pub u8);
|
||||||
|
|
||||||
|
pub const A: YearFlags = YearFlags(0o15); pub const AG: YearFlags = YearFlags(0o05);
|
||||||
|
pub const B: YearFlags = YearFlags(0o14); pub const BA: YearFlags = YearFlags(0o04);
|
||||||
|
pub const C: YearFlags = YearFlags(0o13); pub const CB: YearFlags = YearFlags(0o03);
|
||||||
|
pub const D: YearFlags = YearFlags(0o12); pub const DC: YearFlags = YearFlags(0o02);
|
||||||
|
pub const E: YearFlags = YearFlags(0o11); pub const ED: YearFlags = YearFlags(0o01);
|
||||||
|
pub const F: YearFlags = YearFlags(0o17); pub const FE: YearFlags = YearFlags(0o07);
|
||||||
|
pub const G: YearFlags = YearFlags(0o16); pub const GF: YearFlags = YearFlags(0o06);
|
||||||
|
|
||||||
|
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 fn cycle_to_yo(cycle: u32) -> (u32, u32) {
|
||||||
|
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
|
||||||
|
let delta = YEAR_DELTAS[year_mod_400 as usize] as u32;
|
||||||
|
if ordinal0 < delta {
|
||||||
|
year_mod_400 -= 1;
|
||||||
|
ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
|
||||||
|
} else {
|
||||||
|
ordinal0 -= delta;
|
||||||
|
}
|
||||||
|
(year_mod_400, ordinal0 + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
|
||||||
|
year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
impl YearFlags {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_year(year: i32) -> YearFlags {
|
||||||
|
let year = mod_floor(year, 400);
|
||||||
|
YearFlags::from_year_mod_400(year)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_year_mod_400(year: i32) -> YearFlags {
|
||||||
|
YEAR_TO_FLAGS[year as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ndays(&self) -> u32 {
|
||||||
|
let YearFlags(flags) = *self;
|
||||||
|
366 - (flags >> 3) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn isoweek_delta(&self) -> u32 {
|
||||||
|
let YearFlags(flags) = *self;
|
||||||
|
let mut delta = flags as u32 & 0b111;
|
||||||
|
if delta < 3 { delta += 7; }
|
||||||
|
delta
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn nisoweeks(&self) -> u32 {
|
||||||
|
let YearFlags(flags) = *self;
|
||||||
|
52 + ((0b00000100_00000110 >> 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 const MIN_OL: u32 = 1 << 1;
|
||||||
|
pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
|
||||||
|
pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
|
||||||
|
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||||
|
|
||||||
|
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 struct Of(pub u32);
|
||||||
|
|
||||||
|
impl Of {
|
||||||
|
#[inline]
|
||||||
|
fn clamp_ordinal(ordinal: u32) -> u32 {
|
||||||
|
if ordinal > 366 {0} else {ordinal}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
|
||||||
|
let ordinal = Of::clamp_ordinal(ordinal);
|
||||||
|
Of((ordinal << 4) | (flags as u32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub 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((v as i32 as u32 & 0x3ff) << 3)),
|
||||||
|
None => Of(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn valid(&self) -> bool {
|
||||||
|
let Of(of) = *self;
|
||||||
|
let ol = of >> 3;
|
||||||
|
MIN_OL <= ol && ol <= MAX_OL
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn ordinal(&self) -> u32 {
|
||||||
|
let Of(of) = *self;
|
||||||
|
of >> 4
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_ordinal(&self, ordinal: u32) -> Of {
|
||||||
|
let ordinal = Of::clamp_ordinal(ordinal);
|
||||||
|
let Of(of) = *self;
|
||||||
|
Of((of & 0b1111) | (ordinal << 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn flags(&self) -> YearFlags {
|
||||||
|
let Of(of) = *self;
|
||||||
|
YearFlags((of & 0b1111) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
|
||||||
|
let Of(of) = *self;
|
||||||
|
Of((of & !0b1111) | (flags as u32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn weekday(&self) -> Weekday {
|
||||||
|
let Of(of) = *self;
|
||||||
|
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
|
||||||
|
// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_mdf(&self) -> Mdf {
|
||||||
|
Mdf::from_of(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn succ(&self) -> Of {
|
||||||
|
let Of(of) = *self;
|
||||||
|
Of(of + (1 << 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub 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 struct Mdf(pub u32);
|
||||||
|
|
||||||
|
impl Mdf {
|
||||||
|
#[inline]
|
||||||
|
fn clamp_month(month: u32) -> u32 {
|
||||||
|
if month > 12 {0} else {month}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clamp_day(day: u32) -> u32 {
|
||||||
|
if day > 31 {0} else {day}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
|
||||||
|
let month = Mdf::clamp_month(month);
|
||||||
|
let day = Mdf::clamp_day(day);
|
||||||
|
Mdf((month << 9) | (day << 4) | (flags as u32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_of(Of(of): Of) -> Mdf {
|
||||||
|
let ol = of >> 3;
|
||||||
|
match OL_TO_MDL.get(ol as usize) {
|
||||||
|
Some(&v) => Mdf(of + ((v as u32) << 3)),
|
||||||
|
None => Mdf(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub 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 fn month(&self) -> u32 {
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
mdf >> 9
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_month(&self, month: u32) -> Mdf {
|
||||||
|
let month = Mdf::clamp_month(month);
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
Mdf((mdf & 0b11111_1111) | (month << 9))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn day(&self) -> u32 {
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
(mdf >> 4) & 0b11111
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_day(&self, day: u32) -> Mdf {
|
||||||
|
let day = Mdf::clamp_day(day);
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
Mdf((mdf & !0b11111_0000) | (day << 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn flags(&self) -> YearFlags {
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
YearFlags((mdf & 0b1111) as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
|
||||||
|
let Mdf(mdf) = *self;
|
||||||
|
Mdf((mdf & !0b1111) | (flags as u32))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub 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) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[cfg(bench)] extern crate test;
|
||||||
|
|
||||||
|
use Weekday;
|
||||||
|
use super::{Of, Mdf};
|
||||||
|
use super::{YearFlags, A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF};
|
||||||
|
use num::iter::range_inclusive;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(bench)]
|
||||||
|
#[bench]
|
||||||
|
fn bench_year_flags_from_year(bh: &mut test::Bencher) {
|
||||||
|
bh.iter(|| {
|
||||||
|
for year in -999i32..1000 {
|
||||||
|
YearFlags::from_year(year);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_of() {
|
||||||
|
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||||
|
for ordinal in range_inclusive(ordinal1, ordinal2) {
|
||||||
|
let of = Of::new(ordinal, flags);
|
||||||
|
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 = Mdf::new(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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
for ordinal in range_inclusive(0u32, 1024) {
|
||||||
|
let of = of.with_ordinal(ordinal);
|
||||||
|
assert_eq!(of.valid(), Of::new(ordinal, flags).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).weekday(), Weekday::Sun);
|
||||||
|
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
|
||||||
|
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
|
||||||
|
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
|
||||||
|
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
|
||||||
|
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
|
||||||
|
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
|
||||||
|
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
|
||||||
|
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
|
||||||
|
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
|
||||||
|
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
|
||||||
|
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
|
||||||
|
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
|
||||||
|
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
|
||||||
|
|
||||||
|
for &flags in FLAGS.iter() {
|
||||||
|
let mut prev = Of::new(1, flags).weekday();
|
||||||
|
for ordinal in range_inclusive(2u32, flags.ndays()) {
|
||||||
|
let of = Of::new(ordinal, flags);
|
||||||
|
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 = Mdf::new(month, day, flags);
|
||||||
|
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);
|
||||||
|
|
||||||
|
for month in range_inclusive(0u32, 16) {
|
||||||
|
let mdf = mdf.with_month(month);
|
||||||
|
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||||
|
if mdf.valid() {
|
||||||
|
assert_eq!(mdf.month(), month);
|
||||||
|
assert_eq!(mdf.day(), day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for day in range_inclusive(0u32, 1024) {
|
||||||
|
let mdf = mdf.with_day(day);
|
||||||
|
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||||
|
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).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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
// This is a part of Chrono.
|
||||||
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
|
//! ISO 8601 week.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use super::internals::{DateImpl, Of, YearFlags};
|
||||||
|
|
||||||
|
/// ISO 8601 week.
|
||||||
|
///
|
||||||
|
/// This type, combined with [`Weekday`](../enum.Weekday.html),
|
||||||
|
/// constitues 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)]
|
||||||
|
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 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 | of.flags().0 as DateImpl }
|
||||||
|
}
|
||||||
|
|
||||||
|
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(2014, 12, 29));
|
||||||
|
/// ~~~~
|
||||||
|
#[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 same to
|
||||||
|
/// [`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(2015, 9, 5).iso_week()), "2015-W36");
|
||||||
|
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01");
|
||||||
|
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52");
|
||||||
|
/// ~~~~
|
||||||
|
///
|
||||||
|
/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE.
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// # use chrono::{NaiveDate, Datelike};
|
||||||
|
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52");
|
||||||
|
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52");
|
||||||
|
/// ~~~~
|
||||||
|
impl fmt::Debug for IsoWeek {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let year = self.year();
|
||||||
|
let week = self.week();
|
||||||
|
if 0 <= year && year <= 9999 {
|
||||||
|
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 naive::{internals, MIN_DATE, MAX_DATE};
|
||||||
|
use Datelike;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_week_extremes() {
|
||||||
|
let minweek = MIN_DATE.iso_week();
|
||||||
|
let maxweek = MAX_DATE.iso_week();
|
||||||
|
|
||||||
|
assert_eq!(minweek.year(), internals::MIN_YEAR);
|
||||||
|
assert_eq!(minweek.week(), 1);
|
||||||
|
assert_eq!(minweek.week0(), 0);
|
||||||
|
assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string());
|
||||||
|
|
||||||
|
assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
|
||||||
|
assert_eq!(maxweek.week(), 1);
|
||||||
|
assert_eq!(maxweek.week0(), 0);
|
||||||
|
assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string());
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,47 +1,44 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! The time zone which has a fixed offset from UTC.
|
||||||
* The time zone which has a fixed offset from UTC.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
|
use Timelike;
|
||||||
use div::div_mod_floor;
|
use div::div_mod_floor;
|
||||||
use duration::Duration;
|
use naive::{NaiveTime, NaiveDate, NaiveDateTime};
|
||||||
use naive::date::NaiveDate;
|
use DateTime;
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use super::{TimeZone, Offset, LocalResult};
|
use super::{TimeZone, Offset, LocalResult};
|
||||||
|
|
||||||
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
|
/// 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(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct FixedOffset {
|
pub struct FixedOffset {
|
||||||
local_minus_utc: i32,
|
local_minus_utc: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FixedOffset {
|
impl FixedOffset {
|
||||||
/// Makes a new `FixedOffset` from the serialized representation.
|
|
||||||
/// Used for serialization formats.
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
|
||||||
fn from_serialized(secs: i32) -> Option<FixedOffset> {
|
|
||||||
// check if the values are in the range
|
|
||||||
if secs <= -86400 || 86400 <= secs { return None; }
|
|
||||||
|
|
||||||
let offset = FixedOffset { local_minus_utc: secs };
|
|
||||||
Some(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a serialized representation of this `FixedOffset`.
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
|
||||||
fn to_serialized(&self) -> i32 {
|
|
||||||
self.local_minus_utc
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
|
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
|
||||||
/// The negative `secs` means the Western Hemisphere.
|
/// The negative `secs` means the Western Hemisphere.
|
||||||
///
|
///
|
||||||
/// Panics on the out-of-bound `secs`.
|
/// Panics on the out-of-bound `secs`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{FixedOffset, TimeZone};
|
||||||
|
/// let hour = 3600;
|
||||||
|
/// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
|
||||||
|
/// .and_hms(0, 0, 0);
|
||||||
|
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
|
||||||
|
/// ~~~~
|
||||||
pub fn east(secs: i32) -> FixedOffset {
|
pub fn east(secs: i32) -> FixedOffset {
|
||||||
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
|
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
|
||||||
}
|
}
|
||||||
|
@ -62,6 +59,16 @@ impl FixedOffset {
|
||||||
/// The negative `secs` means the Eastern Hemisphere.
|
/// The negative `secs` means the Eastern Hemisphere.
|
||||||
///
|
///
|
||||||
/// Panics on the out-of-bound `secs`.
|
/// Panics on the out-of-bound `secs`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{FixedOffset, TimeZone};
|
||||||
|
/// let hour = 3600;
|
||||||
|
/// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
|
||||||
|
/// .and_hms(0, 0, 0);
|
||||||
|
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
|
||||||
|
/// ~~~~
|
||||||
pub fn west(secs: i32) -> FixedOffset {
|
pub fn west(secs: i32) -> FixedOffset {
|
||||||
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
|
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
|
||||||
}
|
}
|
||||||
|
@ -77,6 +84,16 @@ impl FixedOffset {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of seconds to add to convert from UTC to the local time.
|
||||||
|
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.
|
||||||
|
pub fn utc_minus_local(&self) -> i32 {
|
||||||
|
-self.local_minus_utc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeZone for FixedOffset {
|
impl TimeZone for FixedOffset {
|
||||||
|
@ -96,7 +113,7 @@ impl TimeZone for FixedOffset {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for FixedOffset {
|
impl Offset for FixedOffset {
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) }
|
fn fix(&self) -> FixedOffset { *self }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FixedOffset {
|
impl fmt::Debug for FixedOffset {
|
||||||
|
@ -117,72 +134,91 @@ impl fmt::Display for FixedOffset {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
// addition or subtraction of FixedOffset to/from Timelike values is same to
|
||||||
mod rustc_serialize {
|
// 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(rhs as i64)).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 offset::TimeZone;
|
||||||
use super::FixedOffset;
|
use super::FixedOffset;
|
||||||
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
|
|
||||||
|
|
||||||
// TODO the current serialization format is NEVER intentionally defined.
|
|
||||||
// this basically follows the automatically generated implementation for those traits,
|
|
||||||
// plus manual verification steps for avoiding security problem.
|
|
||||||
// in the future it is likely to be redefined to more sane and reasonable format.
|
|
||||||
|
|
||||||
impl Encodable for FixedOffset {
|
|
||||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
|
||||||
let secs = self.to_serialized();
|
|
||||||
s.emit_struct("FixedOffset", 1, |s| {
|
|
||||||
try!(s.emit_struct_field("local_minus_utc", 0, |s| secs.encode(s)));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Decodable for FixedOffset {
|
|
||||||
fn decode<D: Decoder>(d: &mut D) -> Result<FixedOffset, D::Error> {
|
|
||||||
d.read_struct("FixedOffset", 1, |d| {
|
|
||||||
let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode));
|
|
||||||
FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encodable() {
|
fn test_date_extreme_offset() {
|
||||||
use rustc_serialize::json::encode;
|
// starting from 0.3 we don't have an offset exceeding one day.
|
||||||
|
// this makes everything easier!
|
||||||
assert_eq!(encode(&FixedOffset::east(0)).ok(),
|
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
|
||||||
Some(r#"{"local_minus_utc":0}"#.into()));
|
"2012-02-29+23:59:59".to_string());
|
||||||
assert_eq!(encode(&FixedOffset::east(1234)).ok(),
|
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
|
||||||
Some(r#"{"local_minus_utc":1234}"#.into()));
|
"2012-02-29T05:06:07+23:59:59".to_string());
|
||||||
assert_eq!(encode(&FixedOffset::east(86399)).ok(),
|
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
|
||||||
Some(r#"{"local_minus_utc":86399}"#.into()));
|
"2012-03-04-23:59:59".to_string());
|
||||||
assert_eq!(encode(&FixedOffset::west(1234)).ok(),
|
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
|
||||||
Some(r#"{"local_minus_utc":-1234}"#.into()));
|
"2012-03-04T05:06:07-23:59:59".to_string());
|
||||||
assert_eq!(encode(&FixedOffset::west(86399)).ok(),
|
|
||||||
Some(r#"{"local_minus_utc":-86399}"#.into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_decodable() {
|
|
||||||
use rustc_serialize::json;
|
|
||||||
|
|
||||||
let decode = |s: &str| json::decode::<FixedOffset>(s);
|
|
||||||
|
|
||||||
assert_eq!(decode(r#"{"local_minus_utc":0}"#).ok(), Some(FixedOffset::east(0)));
|
|
||||||
assert_eq!(decode(r#"{"local_minus_utc": 1234}"#).ok(), Some(FixedOffset::east(1234)));
|
|
||||||
assert_eq!(decode(r#"{"local_minus_utc":86399}"#).ok(), Some(FixedOffset::east(86399)));
|
|
||||||
assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234)));
|
|
||||||
assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399)));
|
|
||||||
|
|
||||||
assert!(decode(r#"{"local_minus_utc":86400}"#).is_err());
|
|
||||||
assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err());
|
|
||||||
assert!(decode(r#"{"local_minus_utc":0.1}"#).is_err());
|
|
||||||
assert!(decode(r#"{"local_minus_utc":null}"#).is_err());
|
|
||||||
assert!(decode(r#"{}"#).is_err());
|
|
||||||
assert!(decode(r#"0"#).is_err());
|
|
||||||
assert!(decode(r#"1234"#).is_err());
|
|
||||||
assert!(decode(r#""string""#).is_err());
|
|
||||||
assert!(decode(r#"null"#).is_err());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,39 +1,32 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! The local (system) time zone.
|
||||||
* The local (system) time zone.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use stdtime;
|
use oldtime;
|
||||||
|
|
||||||
use {Datelike, Timelike};
|
use {Datelike, Timelike};
|
||||||
use duration::Duration;
|
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
|
||||||
use naive::date::NaiveDate;
|
use {Date, DateTime};
|
||||||
use naive::time::NaiveTime;
|
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use date::Date;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use super::{TimeZone, LocalResult};
|
use super::{TimeZone, LocalResult};
|
||||||
use super::fixed::FixedOffset;
|
use super::fixed::FixedOffset;
|
||||||
|
|
||||||
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
|
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
|
||||||
/// This assumes that `time` is working correctly, i.e. any error is fatal.
|
/// This assumes that `time` is working correctly, i.e. any error is fatal.
|
||||||
fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
|
fn tm_to_datetime(mut tm: oldtime::Tm) -> DateTime<Local> {
|
||||||
if tm.tm_sec >= 60 {
|
if tm.tm_sec >= 60 {
|
||||||
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
|
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
|
||||||
tm.tm_sec = 59;
|
tm.tm_sec = 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate {
|
fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
|
||||||
// from_yo is more efficient than from_ymd (since it's the internal representation).
|
// from_yo is more efficient than from_ymd (since it's the internal representation).
|
||||||
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
|
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate {
|
fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
|
||||||
// ...but tm_yday is broken in Windows (issue #85)
|
// ...but tm_yday is broken in Windows (issue #85)
|
||||||
NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
|
NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
|
||||||
}
|
}
|
||||||
|
@ -42,17 +35,17 @@ fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
|
||||||
let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32,
|
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);
|
tm.tm_sec as u32, tm.tm_nsec as u32);
|
||||||
let offset = FixedOffset::east(tm.tm_utcoff);
|
let offset = FixedOffset::east(tm.tm_utcoff);
|
||||||
DateTime::from_utc(date.and_time(time) + Duration::seconds(-tm.tm_utcoff as i64), offset)
|
DateTime::from_utc(date.and_time(time) - offset, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
|
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
|
||||||
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec {
|
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec {
|
||||||
// well, this exploits an undocumented `Tm::to_timespec` behavior
|
// well, this exploits an undocumented `Tm::to_timespec` behavior
|
||||||
// to get the exact function we want (either `timegm` or `mktime`).
|
// to get the exact function we want (either `timegm` or `mktime`).
|
||||||
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
|
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
|
||||||
let tm_utcoff = if local {1} else {0};
|
let tm_utcoff = if local {1} else {0};
|
||||||
|
|
||||||
let tm = stdtime::Tm {
|
let tm = oldtime::Tm {
|
||||||
tm_sec: d.second() as i32,
|
tm_sec: d.second() as i32,
|
||||||
tm_min: d.minute() as i32,
|
tm_min: d.minute() as i32,
|
||||||
tm_hour: d.hour() as i32,
|
tm_hour: d.hour() as i32,
|
||||||
|
@ -63,14 +56,28 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec {
|
||||||
tm_yday: 0, // and this
|
tm_yday: 0, // and this
|
||||||
tm_isdst: -1,
|
tm_isdst: -1,
|
||||||
tm_utcoff: tm_utcoff,
|
tm_utcoff: tm_utcoff,
|
||||||
tm_nsec: d.nanosecond() as i32,
|
// do not set this, OS APIs are heavily inconsistent in terms of leap second handling
|
||||||
|
tm_nsec: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
tm.to_timespec()
|
tm.to_timespec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The local timescale. This is implemented via the standard `time` crate.
|
/// 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)]
|
#[derive(Copy, Clone)]
|
||||||
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
|
||||||
pub struct Local;
|
pub struct Local;
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
|
@ -81,7 +88,7 @@ impl Local {
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
pub fn now() -> DateTime<Local> {
|
pub fn now() -> DateTime<Local> {
|
||||||
tm_to_datetime(stdtime::now())
|
tm_to_datetime(oldtime::now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +101,7 @@ impl TimeZone for Local {
|
||||||
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
|
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
|
||||||
self.from_local_date(local).map(|date| *date.offset())
|
self.from_local_date(local).map(|date| *date.offset())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||||
self.from_local_datetime(local).map(|datetime| *datetime.offset())
|
self.from_local_datetime(local).map(|datetime| *datetime.offset())
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,7 @@ impl TimeZone for Local {
|
||||||
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
|
||||||
*self.from_utc_date(utc).offset()
|
*self.from_utc_date(utc).offset()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
|
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
|
||||||
*self.from_utc_datetime(utc).offset()
|
*self.from_utc_datetime(utc).offset()
|
||||||
}
|
}
|
||||||
|
@ -113,18 +122,61 @@ impl TimeZone for Local {
|
||||||
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
|
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
|
||||||
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
|
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
|
||||||
let timespec = datetime_to_timespec(local, true);
|
let timespec = datetime_to_timespec(local, true);
|
||||||
LocalResult::Single(tm_to_datetime(stdtime::at(timespec)))
|
|
||||||
|
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||||
|
let mut tm = oldtime::at(timespec);
|
||||||
|
assert_eq!(tm.tm_nsec, 0);
|
||||||
|
tm.tm_nsec = local.nanosecond() as i32;
|
||||||
|
|
||||||
|
LocalResult::Single(tm_to_datetime(tm))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
|
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
|
||||||
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
|
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
|
||||||
Date::from_utc(*utc, *midnight.offset())
|
Date::from_utc(*utc, *midnight.offset())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
|
||||||
let timespec = datetime_to_timespec(utc, false);
|
let timespec = datetime_to_timespec(utc, false);
|
||||||
tm_to_datetime(stdtime::at(timespec))
|
|
||||||
|
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
|
||||||
|
let mut tm = oldtime::at(timespec);
|
||||||
|
assert_eq!(tm.tm_nsec, 0);
|
||||||
|
tm.tm_nsec = utc.nanosecond() as i32;
|
||||||
|
|
||||||
|
tm_to_datetime(tm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use Datelike;
|
||||||
|
use offset::TimeZone;
|
||||||
|
use super::Local;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_local_date_sanity_check() { // issue #27
|
||||||
|
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_leap_second() { // issue #123
|
||||||
|
let today = Local::today();
|
||||||
|
|
||||||
|
let dt = today.and_hms_milli(1, 2, 59, 1000);
|
||||||
|
let timestr = dt.time().to_string();
|
||||||
|
// the OS API may or may not support the leap second,
|
||||||
|
// but there are only two sensible options.
|
||||||
|
assert!(timestr == "01:02:60" || timestr == "01:03:00",
|
||||||
|
"unexpected timestr {:?}", timestr);
|
||||||
|
|
||||||
|
let dt = today.and_hms_milli(1, 2, 3, 1234);
|
||||||
|
let timestr = dt.time().to_string();
|
||||||
|
assert!(timestr == "01:02:03.234" || timestr == "01:02:04.234",
|
||||||
|
"unexpected timestr {:?}", timestr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,30 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2014-2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! The time zone, which calculates offsets from the local time to UTC.
|
||||||
* The time zone, which calculates offsets from the local time to UTC.
|
//!
|
||||||
*
|
//! There are four operations provided by the `TimeZone` trait:
|
||||||
* There are three operations provided by the `TimeZone` trait:
|
//!
|
||||||
*
|
//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
|
||||||
* 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
|
//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
|
||||||
* 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
|
//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
|
||||||
* 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.
|
//! 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
|
//! 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).
|
//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
|
||||||
* Technically speaking `TimeZone` has a total knowledge about given timescale,
|
//! Technically speaking `TimeZone` has a total knowledge about given timescale,
|
||||||
* but `Offset` is used as a cache to avoid the repeated conversion
|
//! but `Offset` is used as a cache to avoid the repeated conversion
|
||||||
* and provides implementations for 1 and 3.
|
//! and provides implementations for 1 and 3.
|
||||||
* An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
||||||
*/
|
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Add;
|
|
||||||
|
|
||||||
use Weekday;
|
use Weekday;
|
||||||
use Timelike;
|
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
|
||||||
use duration::Duration;
|
use {Date, DateTime};
|
||||||
use naive::date::NaiveDate;
|
|
||||||
use naive::time::NaiveTime;
|
|
||||||
use naive::datetime::NaiveDateTime;
|
|
||||||
use date::Date;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use format::{parse, Parsed, ParseResult, StrftimeItems};
|
use format::{parse, Parsed, ParseResult, StrftimeItems};
|
||||||
|
|
||||||
/// Same to `*lhs + *rhs`, but keeps the leap second information.
|
|
||||||
/// `rhs` should *not* have a fractional second.
|
|
||||||
// TODO this should be replaced by the addition with FixedOffset in 0.3!
|
|
||||||
pub fn add_with_leapsecond<T: Timelike + Add<Duration, Output=T>>(lhs: &T, rhs: &Duration) -> T {
|
|
||||||
debug_assert!(*rhs == Duration::seconds(rhs.num_seconds()));
|
|
||||||
|
|
||||||
// extract and temporarily remove the fractional part and later recover it
|
|
||||||
let nanos = lhs.nanosecond();
|
|
||||||
let lhs = lhs.with_nanosecond(0).unwrap();
|
|
||||||
(lhs + *rhs).with_nanosecond(nanos).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The conversion result from the local time to the timezone-aware datetime types.
|
/// The conversion result from the local time to the timezone-aware datetime types.
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub enum LocalResult<T> {
|
pub enum LocalResult<T> {
|
||||||
|
@ -173,11 +153,14 @@ impl<T: fmt::Debug> LocalResult<T> {
|
||||||
|
|
||||||
/// The offset from the local time to UTC.
|
/// The offset from the local time to UTC.
|
||||||
pub trait Offset: Sized + Clone + fmt::Debug {
|
pub trait Offset: Sized + Clone + fmt::Debug {
|
||||||
/// Returns the offset from UTC to the local time stored.
|
/// Returns the fixed offset from UTC to the local time stored.
|
||||||
fn local_minus_utc(&self) -> Duration;
|
fn fix(&self) -> FixedOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The time zone.
|
/// 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 {
|
pub trait TimeZone: Sized + Clone {
|
||||||
/// An associated offset type.
|
/// An associated offset type.
|
||||||
/// This type is used to store the actual offset in date and time types.
|
/// This type is used to store the actual offset in date and time types.
|
||||||
|
@ -191,6 +174,14 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Panics on the out-of-range date, invalid month and/or day.
|
/// Panics on the out-of-range date, invalid month and/or day.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{Utc, TimeZone};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC");
|
||||||
|
/// ~~~~
|
||||||
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
|
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
|
||||||
self.ymd_opt(year, month, day).unwrap()
|
self.ymd_opt(year, month, day).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -202,6 +193,15 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
/// 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.
|
/// Returns `None` on the out-of-range date, invalid month and/or day.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{Utc, LocalResult, TimeZone};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC");
|
||||||
|
/// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None);
|
||||||
|
/// ~~~~
|
||||||
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
|
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
|
||||||
match NaiveDate::from_ymd_opt(year, month, day) {
|
match NaiveDate::from_ymd_opt(year, month, day) {
|
||||||
Some(d) => self.from_local_date(&d),
|
Some(d) => self.from_local_date(&d),
|
||||||
|
@ -216,6 +216,14 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Panics on the out-of-range date and/or invalid DOY.
|
/// Panics on the out-of-range date and/or invalid DOY.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{Utc, TimeZone};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC");
|
||||||
|
/// ~~~~
|
||||||
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
|
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
|
||||||
self.yo_opt(year, ordinal).unwrap()
|
self.yo_opt(year, ordinal).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -243,6 +251,14 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// but it will propagate to the `DateTime` values constructed via this date.
|
/// but it will propagate to the `DateTime` values constructed via this date.
|
||||||
///
|
///
|
||||||
/// Panics on the out-of-range date and/or invalid week number.
|
/// Panics on the out-of-range date and/or invalid week number.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{Utc, Weekday, TimeZone};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC");
|
||||||
|
/// ~~~~
|
||||||
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
|
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
|
||||||
self.isoywd_opt(year, week, weekday).unwrap()
|
self.isoywd_opt(year, week, weekday).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -268,6 +284,14 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// and the number of nanoseconds since the last whole non-leap second.
|
/// 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.
|
/// Panics on the out-of-range number of seconds and/or invalid nanosecond.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ~~~~
|
||||||
|
/// use chrono::{Utc, TimeZone};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC");
|
||||||
|
/// ~~~~
|
||||||
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
|
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
|
||||||
self.timestamp_opt(secs, nsecs).unwrap()
|
self.timestamp_opt(secs, nsecs).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -286,7 +310,7 @@ pub trait TimeZone: Sized + Clone {
|
||||||
|
|
||||||
/// Parses a string with the specified format string and
|
/// Parses a string with the specified format string and
|
||||||
/// returns a `DateTime` with the current offset.
|
/// returns a `DateTime` with the current offset.
|
||||||
/// See the [`format::strftime` module](../../format/strftime/index.html)
|
/// See the [`format::strftime` module](../format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
///
|
///
|
||||||
/// If the format does not include offsets, the current offset is assumed;
|
/// If the format does not include offsets, the current offset is assumed;
|
||||||
|
@ -312,15 +336,15 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
|
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
|
||||||
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
|
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
|
||||||
self.offset_from_local_date(local).map(|offset| {
|
self.offset_from_local_date(local).map(|offset| {
|
||||||
Date::from_utc(*local - offset.local_minus_utc(), 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.
|
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
|
||||||
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
|
||||||
self.offset_from_local_datetime(local).map(|offset| {
|
self.offset_from_local_datetime(local).map(|offset| {
|
||||||
let utc = add_with_leapsecond(local, &-offset.local_minus_utc());
|
DateTime::from_utc(*local - offset.fix(), offset)
|
||||||
DateTime::from_utc(utc, offset)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +367,11 @@ pub trait TimeZone: Sized + Clone {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod utc;
|
mod utc;
|
||||||
pub mod fixed;
|
mod fixed;
|
||||||
pub mod local;
|
mod local;
|
||||||
|
|
||||||
|
pub use self::utc::Utc;
|
||||||
|
pub use self::fixed::FixedOffset;
|
||||||
|
pub use self::local::Local;
|
||||||
|
|
||||||
|
|
|
@ -1,64 +1,72 @@
|
||||||
// This is a part of rust-chrono.
|
// This is a part of Chrono.
|
||||||
// Copyright (c) 2015, Kang Seonghoon.
|
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
/*!
|
//! The UTC (Coordinated Universal Time) time zone.
|
||||||
* The UTC (Coordinated Universal Time) time zone.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use stdtime;
|
use oldtime;
|
||||||
|
|
||||||
use duration::Duration;
|
use naive::{NaiveDate, NaiveDateTime};
|
||||||
use naive::date::NaiveDate;
|
use {Date, DateTime};
|
||||||
use naive::datetime::NaiveDateTime;
|
use super::{TimeZone, Offset, LocalResult, FixedOffset};
|
||||||
use date::Date;
|
|
||||||
use datetime::DateTime;
|
|
||||||
use super::{TimeZone, Offset, LocalResult};
|
|
||||||
|
|
||||||
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
|
/// 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).
|
/// 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.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
|
||||||
|
/// ~~~~
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
|
pub struct Utc;
|
||||||
pub struct UTC;
|
|
||||||
|
|
||||||
impl UTC {
|
impl Utc {
|
||||||
/// Returns a `Date` which corresponds to the current date.
|
/// Returns a `Date` which corresponds to the current date.
|
||||||
pub fn today() -> Date<UTC> { UTC::now().date() }
|
pub fn today() -> Date<Utc> { Utc::now().date() }
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
pub fn now() -> DateTime<UTC> {
|
pub fn now() -> DateTime<Utc> {
|
||||||
let spec = stdtime::get_time();
|
let spec = oldtime::get_time();
|
||||||
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
|
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
|
||||||
DateTime::from_utc(naive, UTC)
|
DateTime::from_utc(naive, Utc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeZone for UTC {
|
impl TimeZone for Utc {
|
||||||
type Offset = UTC;
|
type Offset = Utc;
|
||||||
|
|
||||||
fn from_offset(_state: &UTC) -> UTC { UTC }
|
fn from_offset(_state: &Utc) -> Utc { Utc }
|
||||||
|
|
||||||
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<UTC> {
|
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
|
||||||
LocalResult::Single(UTC)
|
LocalResult::Single(Utc)
|
||||||
}
|
}
|
||||||
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<UTC> {
|
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
|
||||||
LocalResult::Single(UTC)
|
LocalResult::Single(Utc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC }
|
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc { Utc }
|
||||||
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC}
|
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { Utc }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Offset for UTC {
|
impl Offset for Utc {
|
||||||
fn local_minus_utc(&self) -> Duration { Duration::zero() }
|
fn fix(&self) -> FixedOffset { FixedOffset::east(0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for UTC {
|
impl fmt::Debug for Utc {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for UTC {
|
impl fmt::Display for Utc {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue