Merge branch 'master' into naive_assign

This commit is contained in:
Kang Seonghoon 2017-06-22 01:03:06 +09:00
commit 8ea2d3f236
No known key found for this signature in database
GPG Key ID: 82440FABA6709020
27 changed files with 3918 additions and 2794 deletions

62
.travis.sh Executable file
View File

@ -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

View File

@ -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:

View File

@ -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>

View File

@ -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.)

View File

@ -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" }

View File

@ -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.

View File

@ -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'; \
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \ echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono'; \
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@ echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg'; \
echo '***[Complete Documentation][doc]***' >> $@ echo '[cratesio]: https://crates.io/crates/chrono'; \
echo >> $@ echo '[docsrs-image]: https://docs.rs/chrono/badge.svg?version='$$VERSION; \
echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@ echo '[docsrs]: https://docs.rs/chrono/'$$VERSION'/'; \
echo >> $@ echo; \
awk '/^\/\/! ## /,!/^\/\/!/' $< | 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; \
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
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
View File

@ -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.

View File

@ -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'

View File

@ -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);
}
}

View File

@ -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; use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset};
use offset::utc::UTC; use serdelib::{ser, de};
use offset::local::Local;
use offset::fixed::FixedOffset; /// Ser/de to/from timestamps in seconds
use std::fmt::Display; ///
use serde::{ser, de}; /// 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 super::from;
/// Deserialize a DateTime from a seconds timestamp
///
/// Intended for use with `serde`s `deserialize_with` attribute.
///
/// # Example:
///
/// ```rust
/// # // We mark this ignored so that we can test on 1.13 (which does not
/// # // support custom derive), and run tests with --ignored on beta and
/// # // nightly to actually trigger these.
/// #
/// # #[macro_use] extern crate serde_derive;
/// # #[macro_use] extern crate serde_json;
/// # extern crate chrono;
/// # use chrono::{DateTime, Utc};
/// use chrono::serde::ts_seconds::deserialize as from_ts;
/// #[derive(Deserialize)]
/// struct S {
/// #[serde(deserialize_with = "from_ts")]
/// time: DateTime<Utc>
/// }
///
/// # fn example() -> Result<S, serde_json::Error> {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// # Ok(my_s)
/// # }
/// # fn main() { example().unwrap(); }
/// ```
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where D: de::Deserializer<'de>
{
Ok(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());
} }
} }

View File

@ -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/))

View File

@ -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: () })
}
}
}

View File

@ -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))

View File

@ -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 Default for Parsed {
fn default() -> Parsed {
Parsed {
year: None, year_div_100: None, year_mod_100: None, isoyear: None,
isoyear_div_100: None, isoyear_mod_100: None, month: None,
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
second: None, nanosecond: None, timestamp: None, offset: None,
_dummy: (),
}
}
}
impl Parsed { impl Parsed {
/// Returns the initial value of parsed parts. /// Returns the initial value of parsed parts.
pub fn new() -> Parsed { pub fn new() -> Parsed {
Parsed { year: None, year_div_100: None, year_mod_100: None, isoyear: None, Parsed::default()
isoyear_div_100: None, isoyear_mod_100: None, month: None,
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
second: None, nanosecond: None, timestamp: None, offset: None }
} }
/// 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));

View File

@ -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.
/*! /*!

View File

@ -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 {

View File

@ -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; mod date;
pub mod datetime; 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);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

778
src/naive/internals.rs Normal file
View File

@ -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());
}
}
}
}

161
src/naive/isoweek.rs Normal file
View File

@ -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

View File

@ -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
use super::FixedOffset; // but keep keeps the leap second information.
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; // this should be implemented more efficiently, but for the time being, this is generic right now.
// TODO the current serialization format is NEVER intentionally defined. fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
// this basically follows the automatically generated implementation for those traits, where T: Timelike + Add<OldDuration, Output=T>
// plus manual verification steps for avoiding security problem. {
// in the future it is likely to be redefined to more sane and reasonable format. // 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 Encodable for FixedOffset { impl Add<FixedOffset> for NaiveTime {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { type Output = NaiveTime;
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 { #[inline]
fn decode<D: Decoder>(d: &mut D) -> Result<FixedOffset, D::Error> { fn add(self, rhs: FixedOffset) -> NaiveTime {
d.read_struct("FixedOffset", 1, |d| { add_with_leapsecond(&self, rhs.local_minus_utc)
let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode)); }
FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset")) }
})
} impl Sub<FixedOffset> for NaiveTime {
} type Output = NaiveTime;
#[test] #[inline]
fn test_encodable() { fn sub(self, rhs: FixedOffset) -> NaiveTime {
use rustc_serialize::json::encode; add_with_leapsecond(&self, -rhs.local_minus_utc)
}
assert_eq!(encode(&FixedOffset::east(0)).ok(), }
Some(r#"{"local_minus_utc":0}"#.into()));
assert_eq!(encode(&FixedOffset::east(1234)).ok(), impl Add<FixedOffset> for NaiveDateTime {
Some(r#"{"local_minus_utc":1234}"#.into())); type Output = NaiveDateTime;
assert_eq!(encode(&FixedOffset::east(86399)).ok(),
Some(r#"{"local_minus_utc":86399}"#.into())); #[inline]
assert_eq!(encode(&FixedOffset::west(1234)).ok(), fn add(self, rhs: FixedOffset) -> NaiveDateTime {
Some(r#"{"local_minus_utc":-1234}"#.into())); add_with_leapsecond(&self, rhs.local_minus_utc)
assert_eq!(encode(&FixedOffset::west(86399)).ok(), }
Some(r#"{"local_minus_utc":-86399}"#.into())); }
}
impl Sub<FixedOffset> for NaiveDateTime {
#[test] type Output = NaiveDateTime;
fn test_decodable() {
use rustc_serialize::json; #[inline]
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
let decode = |s: &str| json::decode::<FixedOffset>(s); add_with_leapsecond(&self, -rhs.local_minus_utc)
}
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))); impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234))); type Output = DateTime<Tz>;
assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399)));
#[inline]
assert!(decode(r#"{"local_minus_utc":86400}"#).is_err()); fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err()); add_with_leapsecond(&self, rhs.local_minus_utc)
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()); impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
assert!(decode(r#"1234"#).is_err()); type Output = DateTime<Tz>;
assert!(decode(r#""string""#).is_err());
assert!(decode(r#"null"#).is_err()); #[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;
#[test]
fn test_date_extreme_offset() {
// starting from 0.3 we don't have an offset exceeding one day.
// this makes everything easier!
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
"2012-02-29+23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
"2012-02-29T05:06:07+23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
"2012-03-04-23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
"2012-03-04T05:06:07-23:59:59".to_string());
} }
} }

View File

@ -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);
} }
} }

View File

@ -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;

View File

@ -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") }
} }