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
sudo: false
rust:
# 1.8.0 is the earliest known version that Cargo does work for the current crates.io-index repo.
# probably older versions work, but we are forced to use this as the minimum for Cargo...
- 1.8.0
# 1.13.0 is the earliest version that Serde 1.0 tests, so we follow suit
- 1.13.0
- stable
- beta
- nightly
@ -16,15 +15,7 @@ matrix:
env:
global:
- LD_LIBRARY_PATH: /usr/local/lib
script:
# 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
script: ./.travis.sh
notifications:
email: false
irc:

View File

@ -1,23 +1,36 @@
Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
and also the following people (in ascending order):
Alex Mikhalev <alexmikhalevalex@gmail.com>
Alexander Bulaev <alexbool@yandex-team.ru>
Ashley Mannix <ashleymannix@live.com.au>
Ben Boeckel <mathstuf@gmail.com>
Ben Eills <ben@beneills.com>
Brandon W Maister <bwm@knewton.com>
Brandon W Maister <quodlibetor@gmail.com>
Colin Ray <r.colinray@gmail.com>
Corey Farwell <coreyf@rwell.org>
Dan <dan@ebip.co.uk>
Danilo Bargen <mail@dbrgn.ch>
David Hewson <dev@daveid.co.uk>
David Ross <daboross@daboross.net>
David Tolnay <dtolnay@gmail.com>
David Willie <david.willie.1@gmail.com>
Eric Findlay <e.findlay@protonmail.ch>
Eunchong Yu <kroisse@gmail.com>
Frans Skarman <frans.skarman@gmail.com>
Huon Wilson <dbau.pp+github@gmail.com>
Igor Gnatenko <ignatenko@redhat.com>
Jim Turner <jturner314@gmail.com>
Jisoo Park <xxxyel@gmail.com>
Joe Wilm <joe@jwilm.com>
John Heitmann <jheitmann@gmail.com>
John Nagle <nagle@sitetruth.com>
Jonas mg <jonasmg@yepmail.net>
János Illés <ijanos@gmail.com>
Ken Tossell <ken@tossell.net>
Martin Risell Lilja <martin.risell.lilja@gmail.com>
Richard Petrie <rap1011@ksu.edu>
Ryan Lewis <ryansname@gmail.com>
Sergey V. Galtsev <sergey.v.galtsev@github.com>
Steve Klabnik <steve@steveklabnik.com>

View File

@ -1,15 +1,119 @@
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/).
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.
## 0.3.1 (2017-05-02)
### Added
- `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113)
The syntax is identical to `%A`, i.e. either the shortest or the longest form of English names.
### Changed
- Serde 1.0 is now supported. (#142)
This is technically a breaking change because Serde 0.9 and 1.0 are not compatible,
but this time we decided not to issue a minor version because
we have already seen Serde 0.8 and 0.9 compatibility problems even after 0.3.0 and
a new minor version turned out to be not very helpful for this kind of issues.
### Fixed
- Fixed a bug that the leap second can be mapped wrongly in the local time zone.
Only occurs when the local time zone is behind UTC. (#130)
## 0.3.0 (2017-02-07)
The project has moved to the [Chronotope](https://github.com/chronotope/) organization.
### Added
- `chrono::prelude` module has been added. All other glob imports are now discouraged.
- `FixedOffset` can be added to or subtracted from any timelike types.
- `FixedOffset::local_minus_utc` and `FixedOffset::utc_minus_local` methods have been added.
Note that the old `Offset::local_minus_utc` method is gone; see below.
- Serde support for non-self-describing formats like Bincode is added. (#89)
- Added `Item::Owned{Literal,Space}` variants for owned formatting items. (#76)
- Formatting items and the `Parsed` type have been slightly adjusted so that
they can be internally extended without breaking any compatibility.
- `Weekday` is now `Hash`able. (#109)
- `ParseError` now implements `Eq` as well as `PartialEq`. (#114)
- More documentation improvements. (#101, #108, #112)
### Changed
- Chrono now only supports Rust 1.13.0 or later (previously: Rust 1.8.0 or later).
- Serde 0.9 is now supported.
Due to the API difference, support for 0.8 or older is discontinued. (#122)
- Rustc-serialize implementations are now on par with corresponding Serde implementations.
They both standardize on the `std::fmt::Debug` textual output.
**This is a silent breaking change (hopefully the last though).**
You should be prepared for the format change if you depended on rustc-serialize.
- `Offset::local_minus_utc` is now `Offset::fix`, and returns `FixedOffset` instead of a duration.
This makes every time zone operation operate within a bias less than one day,
and vastly simplifies many logics.
- `chrono::format::format` now receives `FixedOffset` instead of `time::Duration`.
- The following methods and implementations have been renamed and older names have been *removed*.
The older names will be reused for the same methods with `std::time::Duration` in the future.
- `checked_*``checked_*_signed` in `Date`, `DateTime`, `NaiveDate` and `NaiveDateTime` types
- `overflowing_*``overflowing_*_signed` in the `NaiveTime` type
- All subtraction implementations between two time instants have been moved to
`signed_duration_since`, following the naming in `std::time`.
### Fixed
- Fixed a panic when the `Local` offset receives a leap second. (#123)
### Removed
- Rustc-serialize support for `Date<Tz>` types and all offset types has been dropped.
These implementations were automatically derived and never had been in a good shape.
Moreover there are no corresponding Serde implementations, limiting their usefulness.
In the future they may be revived with more complete implementations.
- The following method aliases deprecated in the 0.2 branch have been removed.
- `DateTime::num_seconds_from_unix_epoch` (→ `DateTime::timestamp`)
- `NaiveDateTime::from_num_seconds_from_unix_epoch` (→ `NaiveDateTime::from_timestamp`)
- `NaiveDateTime::from_num_seconds_from_unix_epoch_opt` (→ `NaiveDateTime::from_timestamp_opt`)
- `NaiveDateTime::num_seconds_unix_epoch` (→ `NaiveDateTime::timestamp`)
- Formatting items are no longer `Copy`, except for `chrono::format::Pad`.
- `chrono::offset::add_with_leapsecond` has been removed.
Use a direct addition with `FixedOffset` instead.
## 0.2.25 (2016-08-04)
This is the last version officially supports Rust 1.12.0 or older.
(0.2.24 was accidentally uploaded without a proper check for warnings in the default state,
and replaced by 0.2.25 very shortly. Duh.)

View File

@ -1,25 +1,31 @@
[package]
name = "chrono"
version = "0.2.25"
version = "0.4.0"
authors = ["Kang Seonghoon <public+rust@mearie.org>"]
description = "Date and time library for Rust"
homepage = "https://github.com/lifthrasiir/rust-chrono"
documentation = "https://lifthrasiir.github.io/rust-chrono/"
repository = "https://github.com/lifthrasiir/rust-chrono"
homepage = "https://github.com/chronotope/chrono"
documentation = "https://docs.rs/chrono/"
repository = "https://github.com/chronotope/chrono"
keywords = ["date", "time", "calendar"]
categories = ["date-and-time"]
readme = "README.md"
license = "MIT/Apache-2.0"
[badges]
travis-ci = { repository = "chronotope/chrono" }
appveyor = { repository = "chronotope/chrono" }
[lib]
name = "chrono"
[dependencies]
time = "0.1"
time = "^0.1.36"
num = { version = "0.1", default-features = false }
rustc-serialize = { version = "0.3", optional = true }
serde = { version = "<0.9", optional = true }
serde = { version = "1", optional = true }
[dev-dependencies]
serde_json = { version = ">=0.7.0" }
bincode = { version = "0.6", features = ["serde"], default-features = false }
serde_json = { version = "1" }
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
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.

View File

@ -17,50 +17,39 @@ readme: README.md
README.md: src/lib.rs
# really, really sorry for this mess.
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< > $@
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< | sed 's/./=/g' >> $@
echo >> $@
echo '[![Chrono on Travis CI][travis-image]][travis]' >> $@
echo '[![Chrono on Appveyor][appveyor-image]][appveyor]' >> $@
echo '[![Chrono on crates.io][cratesio-image]][cratesio]' >> $@
echo >> $@
echo '[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.svg?branch=master' >> $@
echo '[travis]: https://travis-ci.org/lifthrasiir/rust-chrono' >> $@
echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true' >> $@
echo '[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master' >> $@
echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg' >> $@
echo '[cratesio]: https://crates.io/crates/chrono' >> $@
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
echo '***[Complete Documentation][doc]***' >> $@
echo >> $@
echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@
echo >> $@
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
( \
VERSION="$$(cargo pkgid | cut -d: -f3)"; \
awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $<; \
awk '/^\/\/! # Chrono /{print "[Chrono][docsrs]",$$4}' $< | sed 's/./=/g'; \
echo; \
echo '[![Chrono on Travis CI][travis-image]][travis]'; \
echo '[![Chrono on Appveyor][appveyor-image]][appveyor]'; \
echo '[![Chrono on crates.io][cratesio-image]][cratesio]'; \
echo '[![Chrono on docs.rs][docsrs-image]][docsrs]'; \
echo; \
echo '[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master'; \
echo '[travis]: https://travis-ci.org/chronotope/chrono'; \
echo '[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true'; \
echo '[appveyor]: https://ci.appveyor.com/project/chronotope/chrono'; \
echo '[cratesio-image]: https://img.shields.io/crates/v/chrono.svg'; \
echo '[cratesio]: https://crates.io/crates/chrono'; \
echo '[docsrs-image]: https://docs.rs/chrono/badge.svg?version='$$VERSION; \
echo '[docsrs]: https://docs.rs/chrono/'$$VERSION'/'; \
echo; \
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \
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
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
doc: authors readme
cargo doc --features 'serde rustc-serialize'
.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; \
)
cargo doc --features 'serde rustc-serialize bincode'

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 Appveyor][appveyor-image]][appveyor]
[![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]: https://travis-ci.org/lifthrasiir/rust-chrono
[appveyor-image]: https://ci.appveyor.com/api/projects/status/o83jn08389si56fy/branch/master?svg=true
[appveyor]: https://ci.appveyor.com/project/lifthrasiir/rust-chrono/branch/master
[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master
[travis]: https://travis-ci.org/chronotope/chrono
[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true
[appveyor]: https://ci.appveyor.com/project/chronotope/chrono
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
[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
the [time](https://github.com/rust-lang-deprecated/time) library.
In particular,
@ -22,16 +26,13 @@ In particular,
* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
There were several previous attempts to bring a good date and time library to Rust,
which Chrono builts upon and should acknowledge:
which Chrono builds upon and should acknowledge:
* [Initial research on
the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
***[Complete Documentation][doc]***
[doc]: https://lifthrasiir.github.io/rust-chrono/
## Usage
@ -39,7 +40,7 @@ Put this in your `Cargo.toml`:
```toml
[dependencies]
chrono = "0.2"
chrono = "0.4"
```
Or, if you want [Serde](https://github.com/serde-rs/serde) or
@ -48,7 +49,7 @@ include the features like this:
```toml
[dependencies]
chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
chrono = { version = "0.4", features = ["serde", "rustc-serialize"] }
```
Then put this in your crate root:
@ -57,20 +58,39 @@ Then put this in your crate root:
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
### Duration
[**`Duration`**](https://lifthrasiir.github.io/rust-chrono/chrono/struct.Duration.html)
represents the magnitude of a time span. `Duration` used to be provided by Chrono.
It has been moved to the `time` crate as the
[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
still re-exported from Chrono.
Chrono currently uses
the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type
from the `time` crate to represent the magnitude of a time span.
Since this has the same name to the newer, standard type for duration,
the reference will refer this type as `OldDuration`.
Note that this is an "accurate" duration represented as seconds and
nanoseconds and does not represent "nominal" components such as days or
months.
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
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.
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.
`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.
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.
This often results from the parsed textual date and time.
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,
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
([`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
([`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}
use chrono::*;
```rust
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`
~~~~
```
Alternatively, you can create your own date and time.
This is a bit verbose due to Rust's lack of function and method overloading,
but in turn we get a rich combination of initialization methods.
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
use chrono::offset::LocalResult;
let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
// July 8 is Tuesday in ISO week 28 of the year 2014.
assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
// dynamic verification
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
// other time zone objects can be used to construct a local datetime.
// obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12);
let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
assert_eq!(dt, fixed_dt);
~~~~
```
Various properties are available to the date and time, and can be altered individually.
Most of them are defined in the traits [`Datelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Datelike.html) and
[`Timelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Timelike.html) which you should `use` before.
Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Datelike.html) and
[`Timelike`](https://docs.rs/chrono/0.4.0/chrono/trait.Timelike.html) which you should `use` before.
Addition and subtraction is also supported.
The following illustrates most supported operations to the date and time:
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
use time::Duration;
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
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
// 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.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)
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
// arithmetic operations
assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
Duration::seconds(-2 * 3600 + 2));
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
~~~~
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
```
Formatting 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.
(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.)
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
[`to_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods
Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc2822) and
[`to_rfc3339`](https://docs.rs/chrono/0.4.0/chrono/struct.DateTime.html#method.to_rfc3339) methods
for well-known formats.
~~~~ {.rust}
use chrono::*;
```rust
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("%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());
@ -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_rfc3339(), "2014-11-28T12:00:09+00:00");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~
```
Parsing can be done with three methods:
1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
(and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<UTC>` and
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
`DateTime<Local>` values. This parses what the `{:?}`
([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
format specifier prints, and requires the offset to be present.
2. [`DateTime::parse_from_str`](https://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>`.
This should be used when the offset is a part of input and the caller cannot guess that.
It *cannot* be used when the offset can be missing.
[`DateTime::parse_from_rfc2822`](https://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
[`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.
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.
When the explicit offset is missing from the input, it simply uses given offset.
It issues an error when the input contains an explicit offset different
from the current offset.
More detailed control over the parsing process is available via
[`format`](https://lifthrasiir.github.io/rust-chrono/chrono/format/index.html) module.
[`format`](https://docs.rs/chrono/0.4.0/chrono/format/index.html) module.
~~~~ {.rust}
use chrono::*;
```rust
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));
// method 1
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
// method 2
@ -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()));
// method 3
assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
// oops, the year is missing!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
// oops, the format string does not include the year at all!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
// oops, the weekday is incorrect!
assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
~~~~
assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
```
### 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.
Most operations available to `DateTime` are also available to `Date` whenever appropriate.
~~~~ {.rust}
use chrono::*;
```rust
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!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
"070809");
~~~~
```
There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
`DateTime` has [`date`](https://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.
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.
### Naive date and time
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),
[**`NaiveTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/struct.NaiveTime.html) and
[**`NaiveDateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/datetime/struct.NaiveDateTime.html) respectively.
as [**`NaiveDate`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDate.html),
[**`NaiveTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveTime.html) and
[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.0/chrono/naive/struct.NaiveDateTime.html) respectively.
They have almost equivalent interfaces as their timezone-aware twins,
but are not associated to time zones obviously and can be quite low-level.
They are mostly useful for building blocks for higher-level types.
Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
[`naive_local`](https://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,
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.
## 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.
[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.)
Almost *every* operation over the possible leap seconds will ignore them.
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.
Any operation that can be ambiguous will return `None` in such cases.
For example, "a month later" of 2014-01-30 is not well-defined
and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
and consequently `Utc.ymd(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:
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-i686-pc-windows-msvc
- TARGET: nightly-x86_64-pc-windows-gnu
@ -14,8 +14,8 @@ install:
- ps: $env:PATH="$env:PATH;C:\rust\bin"
- rustc -vV
- cargo -vV
build_script:
# do not test all combinations, Travis will handle that
- cargo build -v --features "serde rustc-serialize"
build: false
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.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// 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::cmp::Ordering;
use std::ops::{Add, Sub};
use oldtime::Duration as OldDuration;
use {Weekday, Datelike};
use duration::Duration;
use offset::{TimeZone, Offset};
use offset::utc::UTC;
use naive;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use datetime::DateTime;
use offset::{TimeZone, Utc};
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
use DateTime;
use format::{Item, DelayedFormat, StrftimeItems};
/// ISO 8601 calendar date with time zone.
///
/// This type should be considered ambiguous at best,
/// due to the inherent lack of precision required for the time zone resolution.
/// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
/// There are some guarantees on the usage of `Date<Tz>`:
///
/// - If properly constructed via `TimeZone::ymd` and others without an error,
@ -50,9 +45,9 @@ pub struct Date<Tz: TimeZone> {
}
/// 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`.
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> {
/// 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.
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_add(rhs));
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_add_signed(rhs));
Some(Date { date: date, offset: self.offset })
}
@ -219,11 +214,21 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_sub(rhs));
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_sub_signed(rhs));
Some(Date { date: date, offset: self.offset })
}
/// Subtracts another `Date` from the current date.
/// Returns a `Duration` of integral numbers.
///
/// This does not overflow or underflow at all,
/// as all possible output fits in the range of `Duration`.
#[inline]
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
self.date.signed_duration_since(rhs.date)
}
/// Returns a view to the naive UTC date.
#[inline]
pub fn naive_utc(&self) -> NaiveDate {
@ -231,9 +236,13 @@ impl<Tz: TimeZone> Date<Tz> {
}
/// 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]
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.
/// See the [`format::strftime` module](../format/strftime/index.html)
/// See the [`format::strftime` module](./format/strftime/index.html)
/// on the supported escape sequences.
#[inline]
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 ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
#[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]
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) }
}
impl<Tz: TimeZone> Add<Duration> for Date<Tz> {
impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn add(self, rhs: Duration) -> Date<Tz> {
self.checked_add(rhs).expect("`Date + Duration` overflowed")
fn add(self, rhs: OldDuration) -> Date<Tz> {
self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
}
}
impl<Tz: TimeZone, Tz2: TimeZone> Sub<Date<Tz2>> 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> {
impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> Date<Tz> {
self.checked_sub(rhs).expect("`Date - Duration` overflowed")
fn sub(self, rhs: OldDuration) -> Date<Tz> {
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.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// 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::cmp::Ordering;
use std::ops::{Add, Sub};
#[cfg(feature = "rustc-serialize")]
use std::ops::Deref;
use oldtime::Duration as OldDuration;
use {Weekday, Timelike, Datelike};
use offset::{TimeZone, Offset, add_with_leapsecond};
use offset::utc::UTC;
use offset::local::Local;
use offset::fixed::FixedOffset;
use duration::Duration;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use offset::{TimeZone, Offset, Utc, Local, FixedOffset};
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
use Date;
use format::{Item, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
/// ISO 8601 combined date and time with time zone.
///
/// There are some constructors implemented here (the `from_*` methods), but
/// the general-purpose constructors are all via the methods on the
/// [`TimeZone`](./offset/trait.TimeZone.html) implementations.
#[derive(Clone)]
pub struct DateTime<Tz: TimeZone> {
datetime: NaiveDateTime,
offset: Tz::Offset,
}
/// 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> {
/// Makes a new `DateTime` with given *UTC* datetime and offset.
/// The local datetime should be constructed via the `TimeZone` trait.
///
/// # Example
///
/// ~~~~
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
///
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
/// assert_eq!(Utc.timestamp(61, 0), dt);
/// ~~~~
//
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
#[inline]
@ -49,7 +81,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Unlike `date`, this is not associated to the time zone.
#[inline]
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
@ -89,12 +121,6 @@ impl<Tz: TimeZone> DateTime<Tz> {
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.
#[inline]
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.
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_add(rhs));
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_add_signed(rhs));
Some(DateTime { datetime: datetime, offset: self.offset })
}
@ -127,11 +153,18 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_sub(rhs));
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<DateTime<Tz>> {
let datetime = try_opt!(self.datetime.checked_sub_signed(rhs));
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.
#[inline]
pub fn naive_utc(&self) -> NaiveDateTime {
@ -141,7 +174,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Returns a view to the naive local datetime.
#[inline]
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
/// 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.
///
/// 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.
/// See the [`format::strftime` module](../format/strftime/index.html)
/// See the [`format::strftime` module](./format/strftime/index.html)
/// on the supported escape sequences.
#[inline]
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 ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
#[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]
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) }
}
impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
impl<Tz: TimeZone> Add<OldDuration> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn add(self, rhs: Duration) -> DateTime<Tz> {
self.checked_add(rhs).expect("`DateTime + Duration` overflowed")
fn add(self, rhs: OldDuration) -> DateTime<Tz> {
self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed")
}
}
impl<Tz: TimeZone, Tz2: TimeZone> Sub<DateTime<Tz2>> 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> {
impl<Tz: TimeZone> Sub<OldDuration> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> DateTime<Tz> {
self.checked_sub(rhs).expect("`DateTime - Duration` overflowed")
fn sub(self, rhs: OldDuration) -> DateTime<Tz> {
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;
fn from_str(s: &str) -> ParseResult<DateTime<UTC>> {
s.parse::<DateTime<FixedOffset>>().map(|dt| dt.with_timezone(&UTC))
fn from_str(s: &str) -> ParseResult<DateTime<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")]
mod rustc_serialize {
use super::DateTime;
use offset::TimeZone;
use std::fmt;
use super::{DateTime, TsSeconds};
use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset};
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 DateTime<Tz> where Tz::Offset: Encodable {
impl<Tz: TimeZone> Encodable for DateTime<Tz> {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("DateTime", 2, |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(())
})
format!("{:?}", self).encode(s)
}
}
impl<Tz: TimeZone> Decodable for DateTime<Tz> where Tz::Offset: Decodable {
fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<Tz>, D::Error> {
d.read_struct("DateTime", 2, |d| {
let datetime = try!(d.read_struct_field("datetime", 0, Decodable::decode));
let offset = try!(d.read_struct_field("offset", 1, Decodable::decode));
Ok(DateTime::from_utc(datetime, offset))
})
// try!-like function to convert a LocalResult into a serde-ish Result
fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error>
where D: Decoder,
T: fmt::Display,
{
match me {
LocalResult::None => Err(d.error(
"value is not a legal timestamp")),
LocalResult::Ambiguous(..) => Err(d.error(
"value is an ambiguous timestamp")),
LocalResult::Single(val) => Ok(val)
}
}
impl Decodable for DateTime<FixedOffset> {
fn decode<D: Decoder>(d: &mut D) -> Result<DateTime<FixedOffset>, D::Error> {
d.read_str()?.parse::<DateTime<FixedOffset>>()
.map_err(|_| d.error("invalid date and time"))
}
}
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]
fn test_encodable() {
use offset::utc::UTC;
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()));
super::test_encodable_json(json::encode, json::encode);
}
#[test]
fn test_decodable() {
use offset::utc::UTC;
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);
super::test_decodable_json(json::decode, json::decode, json::decode);
}
#[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")]
mod serde {
pub mod serde {
use std::fmt;
use super::DateTime;
use offset::TimeZone;
use offset::utc::UTC;
use offset::local::Local;
use offset::fixed::FixedOffset;
use std::fmt::Display;
use serde::{ser, de};
use offset::{TimeZone, LocalResult, Utc, Local, FixedOffset};
use serdelib::{ser, de};
/// Ser/de to/from timestamps in seconds
///
/// Intended for use with `serde`'s `with` attribute.
///
/// # Example:
///
/// ```rust
/// # // We mark this ignored so that we can test on 1.13 (which does not
/// # // support custom derive), and run tests with --ignored on beta and
/// # // nightly to actually trigger these.
/// #
/// # #[macro_use] extern crate serde_derive;
/// # #[macro_use] extern crate serde_json;
/// # extern crate chrono;
/// # use chrono::{TimeZone, DateTime, Utc};
/// use chrono::serde::ts_seconds;
/// #[derive(Deserialize, Serialize)]
/// struct S {
/// #[serde(with = "ts_seconds")]
/// time: DateTime<Utc>
/// }
///
/// # fn example() -> Result<S, serde_json::Error> {
/// let time = Utc.ymd(2015, 5, 15).and_hms(10, 0, 0);
/// let my_s = S {
/// time: time.clone(),
/// };
///
/// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
/// let my_s: S = serde_json::from_str(&as_string)?;
/// assert_eq!(my_s.time, time);
/// # Ok(my_s)
/// # }
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_seconds {
use std::fmt;
use serdelib::{ser, de};
use {DateTime, Utc, FixedOffset};
use offset::TimeZone;
use 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)
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz>
where Tz::Offset: Display
{
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer
{
struct FormatWrapped<'a, D: 'a> {
inner: &'a D
}
impl<'a, D: fmt::Debug> fmt::Display for FormatWrapped<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner.fmt(f)
}
}
// Debug formatting is correct RFC3339, and it allows Zulu.
serializer.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;
impl de::Visitor for DateTimeVisitor {
impl<'de> de::Visitor<'de> for DateTimeVisitor {
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
{
value.parse().map_err(|err| E::custom(format!("{}", err)))
}
}
impl de::Deserialize for DateTime<FixedOffset> {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: de::Deserializer
/// Deserialize a value that optionally includes a timezone offset in its
/// string representation
///
/// 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)
}
}
impl de::Deserialize for DateTime<UTC> {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: de::Deserializer
/// Deserialize into a UTC value
///
/// 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> {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: de::Deserializer
/// Deserialize a value that includes no timezone in its string
/// representation
///
/// 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))
}
@ -521,34 +845,24 @@ mod serde {
#[test]
fn test_serde_serialize() {
use 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()));
super::test_encodable_json(self::serde_json::to_string, self::serde_json::to_string);
}
#[test]
fn test_serde_deserialize() {
use self::serde_json;
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());
super::test_decodable_json(|input| self::serde_json::from_str(&input), |input| self::serde_json::from_str(&input),
|input| self::serde_json::from_str(&input));
}
#[test]
fn test_serde_bincode() {
// Bincode is relevant to test separately from JSON because
// it is not self-describing.
use self::bincode::SizeLimit;
use self::bincode::serde::{serialize, deserialize};
use self::bincode::{Infinite, serialize, deserialize};
let dt = UTC.ymd(2014, 7, 24).and_hms(12, 34, 6);
let encoded = serialize(&dt, SizeLimit::Infinite).unwrap();
let decoded: DateTime<UTC> = deserialize(&encoded).unwrap();
let dt = Utc.ymd(2014, 7, 24).and_hms(12, 34, 6);
let encoded = serialize(&dt, Infinite).unwrap();
let decoded: DateTime<Utc> = deserialize(&encoded).unwrap();
assert_eq!(dt, decoded);
assert_eq!(dt.offset(), decoded.offset());
}
@ -558,57 +872,53 @@ mod serde {
mod tests {
use super::DateTime;
use Datelike;
use naive::time::NaiveTime;
use naive::date::NaiveDate;
use duration::Duration;
use offset::TimeZone;
use offset::utc::UTC;
use offset::local::Local;
use offset::fixed::FixedOffset;
use naive::{NaiveTime, NaiveDate};
use offset::{TimeZone, Utc, Local, FixedOffset};
use oldtime::Duration;
#[test]
#[allow(non_snake_case)]
fn test_datetime_offset() {
let EST = FixedOffset::west(5*60*60);
let EDT = FixedOffset::west(4*60*60);
let KST = FixedOffset::east(9*60*60);
let Est = FixedOffset::west(5*60*60);
let Edt = FixedOffset::west(4*60*60);
let Kst = FixedOffset::east(9*60*60);
assert_eq!(format!("{}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06 07:08:09 UTC");
assert_eq!(format!("{}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06 07:08:09 -04:00");
assert_eq!(format!("{}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06 07:08:09 +09:00");
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09Z");
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09-04:00");
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(7, 8, 9)),
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(7, 8, 9)),
"2014-05-06T07:08:09+09:00");
// edge cases
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(0, 0, 0)),
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(0, 0, 0)),
"2014-05-06T00:00:00Z");
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(0, 0, 0)),
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(0, 0, 0)),
"2014-05-06T00:00:00-04:00");
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(0, 0, 0)),
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(0, 0, 0)),
"2014-05-06T00:00:00+09:00");
assert_eq!(format!("{:?}", UTC.ymd(2014, 5, 6).and_hms(23, 59, 59)),
assert_eq!(format!("{:?}", Utc.ymd(2014, 5, 6).and_hms(23, 59, 59)),
"2014-05-06T23:59:59Z");
assert_eq!(format!("{:?}", EDT.ymd(2014, 5, 6).and_hms(23, 59, 59)),
assert_eq!(format!("{:?}", Edt.ymd(2014, 5, 6).and_hms(23, 59, 59)),
"2014-05-06T23:59:59-04:00");
assert_eq!(format!("{:?}", KST.ymd(2014, 5, 6).and_hms(23, 59, 59)),
assert_eq!(format!("{:?}", Kst.ymd(2014, 5, 6).and_hms(23, 59, 59)),
"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));
assert_eq!(UTC.ymd(2014, 5, 6).and_hms(7, 8, 9) + 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),
let dt = Utc.ymd(2014, 5, 6).and_hms(7, 8, 9);
assert_eq!(dt, Edt.ymd(2014, 5, 6).and_hms(3, 8, 9));
assert_eq!(dt + Duration::seconds(3600 + 60 + 1), Utc.ymd(2014, 5, 6).and_hms(8, 9, 10));
assert_eq!(dt.signed_duration_since(Edt.ymd(2014, 5, 6).and_hms(10, 11, 12)),
Duration::seconds(-7*3600 - 3*60 - 3));
assert_eq!(*UTC.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), UTC);
assert_eq!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), EDT);
assert!(*EDT.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != EST);
assert_eq!(*Utc.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Utc);
assert_eq!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset(), Edt);
assert!(*Edt.ymd(2014, 5, 6).and_hms(7, 8, 9).offset() != Est);
}
#[test]
@ -638,7 +948,7 @@ mod tests {
#[test]
fn test_datetime_with_timezone() {
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);
assert_eq!(local_now, local_now2);
}
@ -647,9 +957,9 @@ mod tests {
#[allow(non_snake_case)]
fn test_datetime_rfc2822_and_rfc3339() {
let EDT = FixedOffset::east(5*60*60);
assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(),
assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc2822(),
"Wed, 18 Feb 2015 23:16:09 +0000");
assert_eq!(UTC.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(),
assert_eq!(Utc.ymd(2015, 2, 18).and_hms(23, 16, 9).to_rfc3339(),
"2015-02-18T23:16:09+00:00");
assert_eq!(EDT.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150).to_rfc2822(),
"Wed, 18 Feb 2015 23:16:09 +0500");
@ -678,11 +988,11 @@ mod tests {
Ok(FixedOffset::west(10 * 3600).ymd(2015, 2, 18).and_hms_milli(13, 16, 9, 150)));
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<UTC>>(),
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<UTC>>(),
Ok(UTC.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert!("2015-2-18T23:16:9.15".parse::<DateTime<UTC>>().is_err());
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
// no test for `DateTime<Local>`, we cannot verify that much.
}
@ -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("Fri, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT").is_err());
assert_eq!(UTC.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
assert_eq!(Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT",
"%a, %d %b %Y %H:%M:%S GMT"),
Ok(UTC.ymd(2013, 8, 9).and_hms(23, 54, 35)));
Ok(Utc.ymd(2013, 8, 9).and_hms(23, 54, 35)));
}
#[test]
fn test_datetime_format_with_local() {
// if we are not around the year boundary, local and UTC date should have the same year
let dt = Local::now().with_month(5).unwrap();
assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&UTC).format("%Y").to_string());
assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string());
}
#[test]
fn test_datetime_is_copy() {
// UTC is known to be `Copy`.
let a = UTC::now();
let a = Utc::now();
let b = a;
assert_eq!(a, b);
}
@ -720,7 +1030,7 @@ mod tests {
use std::thread;
// UTC is known to be `Send`.
let a = UTC::now();
let a = Utc::now();
thread::spawn(move || {
let _ = a;
}).join().unwrap();
@ -728,11 +1038,10 @@ mod tests {
#[test]
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!(1234, datetime.timestamp_subsec_micros());
assert_eq!(1234567, datetime.timestamp_subsec_nanos());
}
}

View File

@ -1,6 +1,5 @@
// This is a part of rust-chrono.
// Copyright (c) 2014, Kang Seonghoon.
// Copyright 2013-2014 The Rust Project Developers.
// This is a part of Chrono.
// Portions Copyright 2013-2014 The Rust Project Developers.
// See README.md and LICENSE.txt for details.
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))

View File

@ -1,23 +1,37 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// 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::str::FromStr;
use std::error::Error;
use {Datelike, Timelike};
use {Datelike, Timelike, Weekday, ParseWeekdayError};
use div::{div_floor, mod_floor};
use duration::Duration;
use offset::{Offset, add_with_leapsecond};
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use offset::{Offset, FixedOffset};
use naive::{NaiveDate, NaiveTime};
pub use self::strftime::StrftimeItems;
pub use self::parsed::Parsed;
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.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Pad {
@ -42,7 +56,7 @@ pub enum Pad {
/// It also trims the preceding whitespaces if any.
/// It cannot parse the negative number, so some date and time cannot be formatted then
/// parsed with the same formatting items.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Numeric {
/// Full Gregorian year (FW=4, PW=∞).
/// 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=∞).
/// For formatting, it assumes UTC upon the absence of time zone offset.
Timestamp,
/// Internal uses only.
///
/// This item exists so that one can add additional internal-only formatting
/// without breaking major compatibility (as enum variants cannot be selectively private).
Internal(InternalNumeric),
}
/// An opaque type representing numeric item types for internal uses only.
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.
///
/// They have their own rules of formatting and parsing.
/// 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 {
/// 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.
/// 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,
/// 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,
/// and `Z` can be either in upper case or in lower case.
/// 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,
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
/// Parsing allows an optional colon.
@ -158,15 +204,51 @@ pub enum Fixed {
RFC2822,
/// RFC 3339 & ISO 8601 date and time syntax.
RFC3339,
/// Internal uses only.
///
/// This item exists so that one can add additional internal-only formatting
/// without breaking major compatibility (as enum variants cannot be selectively private).
Internal(InternalFixed),
}
/// An opaque type representing fixed-format item types for internal uses only.
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.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Item<'a> {
/// A literally printed and parsed text.
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.
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;
/// the parser simply ignores any padded whitespace and zeroes.
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)) }
/// An error from the `parse` function.
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct ParseError(ParseErrorKind);
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
enum ParseErrorKind {
/// Given field is out of permitted range.
OutOfRange,
@ -253,7 +335,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
/// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`.
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>> {
// full and abbreviated month and weekday names
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 {
match item {
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) => {
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)),
YearDiv100 => (2, date.map(|d| div_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)),
IsoYearDiv100 => (2, date.map(|d| div_floor(d.isoweekdate().0 as i64, 100))),
IsoYearMod100 => (2, date.map(|d| mod_floor(d.isoweekdate().0 as i64, 100))),
IsoYear => (4, date.map(|d| d.iso_week().year() as i64)),
IsoYearDiv100 => (2, date.map(|d| div_floor(d.iso_week().year() 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)),
Day => (2, date.map(|d| d.day() as i64)),
WeekFromSun => (2, date.map(|d| week_from_sun(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)),
WeekdayFromMon => (1, date.map(|d| d.weekday().number_from_monday() 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.and_time(*t).timestamp()),
(Some(d), Some(t), Some(&(_, off))) =>
Some(add_with_leapsecond(&d.and_time(*t), &-off).timestamp()),
Some((d.and_time(*t) - off).timestamp()),
(_, _, _) => None
}),
// for the future expansion
Internal(ref int) => match int._dummy {},
};
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`.
/// `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 {
let off = off.num_minutes();
let off = off.local_minus_utc();
if !allow_zulu || off != 0 {
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
if use_colon {
write!(w, "{}{:02}:{:02}", sign, off / 60, off % 60)
write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
} else {
write!(w, "{}{:02}{:02}", sign, off / 60, off % 60)
write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
}
} else {
write!(w, "Z")
@ -421,6 +507,9 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
} else {
None
},
// for the future expansion
Internal(ref int) => match int._dummy {},
};
match ret {
@ -436,7 +525,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
Ok(())
}
pub mod parsed;
mod parsed;
// due to the size of parsing routines, they are in separate modules.
mod scan;
@ -453,7 +542,7 @@ pub struct DelayedFormat<I> {
/// The time view, if any.
time: Option<NaiveTime>,
/// 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.
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>,
offset: &Off, items: I) -> DelayedFormat<I>
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 }
}
}
@ -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.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// Portions copyright (c) 2015, John Nagle.
// 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()..];
}
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();
}
@ -248,6 +253,9 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
Second => (2, false, Parsed::set_second),
Nanosecond => (9, false, Parsed::set_nanosecond),
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
// for the future expansion
Internal(ref int) => match int._dummy {},
};
s = s.trim_left();
@ -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)),
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) => (
assert_eq!(parse_all($fmt, &$items), Err($err))
);
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (
assert_eq!(parse_all($fmt, &$items), Ok(Parsed { $($k: Some($v),)* ..Parsed::new() }))
);
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
let mut expected = Parsed::new();
$(expected.$k = Some($v);)*
assert_eq!(parse_all($fmt, &$items), Ok(expected))
});
}
// empty string
@ -590,8 +603,8 @@ fn test_parse() {
#[cfg(test)]
#[test]
fn test_rfc2822() {
use datetime::DateTime;
use offset::fixed::FixedOffset;
use DateTime;
use offset::FixedOffset;
use super::*;
use super::NOT_ENOUGH;
@ -642,40 +655,40 @@ fn test_rfc2822() {
#[cfg(test)]
#[test]
fn parse_rfc850() {
use ::{UTC, TimeZone};
use ::{Utc, TimeZone};
static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
let dt = UTC.ymd(1994, 11, 6).and_hms(8, 49, 37);
let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
// Check that the format is what we expect
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
// Check that it parses correctly
assert_eq!(Ok(dt), UTC.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
// Check that the rest of the weekdays parse correctly (this test originally failed because
// Sunday parsed incorrectly).
let testdates = [
(UTC.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
];
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)]
#[test]
fn test_rfc3339() {
use datetime::DateTime;
use offset::fixed::FixedOffset;
use DateTime;
use offset::FixedOffset;
use super::*;
// 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.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! A collection of parsed date and time items.
//! They can be constructed incrementally while being checked for consistency.
use num::traits::ToPrimitive;
use oldtime::Duration as OldDuration;
use {Datelike, Timelike};
use Weekday;
use div::div_rem;
use duration::Duration;
use offset::{TimeZone, Offset, LocalResult};
use offset::fixed::FixedOffset;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use datetime::DateTime;
use offset::{TimeZone, Offset, LocalResult, FixedOffset};
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use DateTime;
use super::{ParseResult, OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
/// 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.
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
/// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
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.
///
/// 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.
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.
pub isoyear_mod_100: Option<i32>,
@ -74,7 +70,7 @@ pub struct Parsed {
/// (0--53, 1--53 or 1--52 depending on the year).
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).
pub isoweek: Option<u32>,
@ -109,6 +105,9 @@ pub struct Parsed {
/// Offset from the local time to UTC, in seconds.
pub offset: Option<i32>,
/// A dummy field to make this type not fully destructible (required for API stability).
_dummy: (),
}
/// Checks if `old` is either empty or has the same value 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 {
/// Returns the initial value of parsed parts.
pub fn new() -> 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 }
Parsed::default()
}
/// Tries to set the [`year`](#structfield.year) field from given value.
@ -324,7 +332,10 @@ impl Parsed {
// verify the ISO week date.
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 (q, r) = div_rem(isoyear, 100);
(Some(q), Some(r))
@ -383,7 +394,7 @@ impl Parsed {
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
weekday.num_days_from_sunday() as i32;
let date = 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));
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?
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
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));
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.
59 => {}
// `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.
_ => return Err(IMPOSSIBLE)
}
@ -593,20 +604,13 @@ impl Parsed {
let nanosecond = self.nanosecond.unwrap_or(0);
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
let dt = try!(dt.ok_or(OUT_OF_RANGE));
// 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));
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
}
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
let check_offset = |dt: &DateTime<Tz>| {
if let Some(offset) = self.offset {
let delta = dt.offset().local_minus_utc().num_seconds();
// if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway.
delta.to_i32() == Some(offset)
dt.offset().fix().local_minus_utc() == offset
} else {
true
}
@ -637,11 +641,8 @@ mod tests {
use super::super::{OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
use Datelike;
use Weekday::*;
use naive::date::{self, NaiveDate};
use naive::time::NaiveTime;
use offset::TimeZone;
use offset::utc::UTC;
use offset::fixed::FixedOffset;
use naive::{MIN_DATE, MAX_DATE, NaiveDate, NaiveTime};
use offset::{TimeZone, Utc, FixedOffset};
#[test]
fn test_parsed_set_fields() {
@ -760,7 +761,7 @@ mod tests {
ymd(0, 1, 1));
assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1),
Err(OUT_OF_RANGE));
let max_year = date::MAX.year();
let max_year = MAX_DATE.year();
assert_eq!(parse!(year_div_100: max_year / 100,
year_mod_100: max_year % 100, month: 1, day: 1),
ymd(max_year, 1, 1));
@ -958,15 +959,18 @@ mod tests {
ymdhmsn(2014,12,31, 4,26,40,12_345_678));
// more timestamps
let max_days_from_year_1970 = date::MAX - NaiveDate::from_ymd(1970,1,1);
let year_0_from_year_1970 = NaiveDate::from_ymd(0,1,1) - NaiveDate::from_ymd(1970,1,1);
let min_days_from_year_1970 = date::MIN - NaiveDate::from_ymd(1970,1,1);
let max_days_from_year_1970 =
MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1));
let year_0_from_year_1970 =
NaiveDate::from_ymd(0,1,1).signed_duration_since(NaiveDate::from_ymd(1970,1,1));
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()),
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()),
ymdhms(0,1,1, 0,0,0));
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
assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
@ -1051,11 +1055,11 @@ mod tests {
}
// single result from ymdhms
assert_eq!(parse!(UTC;
assert_eq!(parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
assert_eq!(parse!(UTC;
Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
assert_eq!(parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
Err(IMPOSSIBLE));
@ -1070,9 +1074,9 @@ mod tests {
.and_hms_nano(13, 26, 40, 12_345_678)));
// single result from timestamp
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms(4, 26, 40)));
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 32400),
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 0),
Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40)));
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
Err(IMPOSSIBLE));

View File

@ -1,5 +1,4 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!

View File

@ -1,5 +1,4 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// 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>> {
// we have some reconstructed items to return
if !self.recons.is_empty() {
let item = self.recons[0];
let item = self.recons[0].clone();
self.recons = &self.recons[1..];
return Some(item);
}
@ -293,8 +292,8 @@ impl<'a> Iterator for StrftimeItems<'a> {
// adjust `item` if we have any padding modifier
if let Some(new_pad) = pad_override {
match item {
Item::Numeric(kind, _pad) if self.recons.is_empty() =>
Some(Item::Numeric(kind, new_pad)),
Item::Numeric(ref kind, _pad) if self.recons.is_empty() =>
Some(Item::Numeric(kind.clone(), new_pad)),
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
}
} else {

View File

@ -1,10 +1,9 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// 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
//! the [time](https://github.com/rust-lang-deprecated/time) library.
//! In particular,
@ -14,7 +13,7 @@
//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
//!
//! There were several previous attempts to bring a good date and time library to Rust,
//! which Chrono builts upon and should acknowledge:
//! which Chrono builds upon and should acknowledge:
//!
//! * [Initial research on
//! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
@ -27,7 +26,7 @@
//!
//! ```toml
//! [dependencies]
//! chrono = "0.2"
//! chrono = "0.4"
//! ```
//!
//! Or, if you want [Serde](https://github.com/serde-rs/serde) or
@ -36,7 +35,7 @@
//!
//! ```toml
//! [dependencies]
//! chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
//! chrono = { version = "0.4", features = ["serde", "rustc-serialize"] }
//! ```
//!
//! Then put this in your crate root:
@ -45,20 +44,39 @@
//! 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
//!
//! ### Duration
//!
//! [**`Duration`**](./struct.Duration.html)
//! represents the magnitude of a time span. `Duration` used to be provided by Chrono.
//! It has been moved to the `time` crate as the
//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
//! still re-exported from Chrono.
//! Chrono currently uses
//! the [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type
//! from the `time` crate to represent the magnitude of a time span.
//! Since this has the same name to the newer, standard type for duration,
//! the reference will refer this type as `OldDuration`.
//! Note that this is an "accurate" duration represented as seconds and
//! nanoseconds and does not represent "nominal" components such as days or
//! months.
//!
//! 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
//!
//! Chrono provides a
//! [**`DateTime`**](./datetime/struct.DateTime.html)
//! [**`DateTime`**](./struct.DateTime.html)
//! type to represent a date and a time in a timezone.
//!
//! For more abstract moment-in-time tracking such as internal timekeeping
@ -73,11 +91,11 @@
//! which defines how the local date is converted to and back from the UTC date.
//! 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.
//! This often results from the parsed textual date and time.
//! 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,
//! 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
//! ([`UTC::now()`](./offset/utc/struct.UTC.html#method.now))
//! ([`Utc::now()`](./offset/struct.Utc.html#method.now))
//! or in the local time zone
//! ([`Local::now()`](./offset/local/struct.Local.html#method.now)).
//! ([`Local::now()`](./offset/struct.Local.html#method.now)).
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! 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 _ = utc; let _ = local;
//! ~~~~
//! ```
//!
//! Alternatively, you can create your own date and time.
//! This is a bit verbose due to Rust's lack of function and method overloading,
//! but in turn we get a rich combination of initialization methods.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//! use chrono::offset::LocalResult;
//!
//! let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
//! let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
//! assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
//! assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
//! // July 8 is Tuesday in ISO week 28 of the year 2014.
//! assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
//!
//! let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
//! let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
//!
//! // dynamic verification
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
//! LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
//! assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
//! LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
//!
//! // other time zone objects can be used to construct a local datetime.
//! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
@ -129,7 +148,7 @@
//! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
//! assert_eq!(dt, fixed_dt);
//! # let _ = local_dt;
//! ~~~~
//! ```
//!
//! Various properties are available to the date and time, and can be altered individually.
//! Most of them are defined in the traits [`Datelike`](./trait.Datelike.html) and
@ -137,8 +156,10 @@
//! Addition and subtraction is also supported.
//! The following illustrates most supported operations to the date and time:
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! # extern crate chrono; extern crate time; fn main() {
//! use chrono::prelude::*;
//! use time::Duration;
//!
//! # /* we intentionally fake the datetime...
//! // 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
//!
//! // 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.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)
//! 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
//!
//! // arithmetic operations
//! assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
//! Duration::seconds(-2 * 3600 + 2));
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
//! UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
//! UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
//! ~~~~
//! let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
//! let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
//! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
//! assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
//! Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
//! Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
//! # }
//! ```
//!
//! Formatting 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.
//! (See the [`format::strftime` module documentation](./format/strftime/index.html#specifiers)
//! for full syntax.)
//!
//! 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
//! [`to_rfc3339`](./datetime/struct.DateTime.html#method.to_rfc3339) methods
//! Chrono also provides [`to_rfc2822`](./struct.DateTime.html#method.to_rfc2822) and
//! [`to_rfc3339`](./struct.DateTime.html#method.to_rfc3339) methods
//! for well-known formats.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! 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("%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());
@ -196,24 +220,24 @@
//! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
//! assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
//! ~~~~
//! ```
//!
//! Parsing can be done with three methods:
//!
//! 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
//! (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
//! on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<UTC>` and
//! on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
//! `DateTime<Local>` values. This parses what the `{:?}`
//! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
//! format specifier prints, and requires the offset to be present.
//!
//! 2. [`DateTime::parse_from_str`](./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>`.
//! This should be used when the offset is a part of input and the caller cannot guess that.
//! It *cannot* be used when the offset can be missing.
//! [`DateTime::parse_from_rfc2822`](./datetime/struct.DateTime.html#method.parse_from_rfc2822)
//! [`DateTime::parse_from_rfc2822`](./struct.DateTime.html#method.parse_from_rfc2822)
//! 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.
//!
//! 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
//! [`format`](./format/index.html) module.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! 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));
//!
//! // method 1
//! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
//!
//! // method 2
@ -244,58 +268,59 @@
//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
//!
//! // method 3
//! assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
//! assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
//! assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
//! assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
//!
//! // oops, the year is missing!
//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
//! // oops, the format string does not include the year at all!
//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
//! // oops, the weekday is incorrect!
//! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
//! ~~~~
//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
//! ```
//!
//! ### 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.
//! Most operations available to `DateTime` are also available to `Date` whenever appropriate.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//! use chrono::offset::LocalResult;
//!
//! # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
//! assert_eq!(UTC::today(), UTC::now().date());
//! assert_eq!(Utc::today(), Utc::now().date());
//! assert_eq!(Local::today(), Local::now().date());
//!
//! assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
//! assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
//! assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
//! assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
//! assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
//! "070809");
//! ~~~~
//! ```
//!
//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
//!
//! `DateTime` has [`date`](./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.
//! 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.
//!
//! ### Naive date and time
//!
//! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
//! as [**`NaiveDate`**](./naive/date/struct.NaiveDate.html),
//! [**`NaiveTime`**](./naive/time/struct.NaiveTime.html) and
//! [**`NaiveDateTime`**](./naive/datetime/struct.NaiveDateTime.html) respectively.
//! as [**`NaiveDate`**](./naive/struct.NaiveDate.html),
//! [**`NaiveTime`**](./naive/struct.NaiveTime.html) and
//! [**`NaiveDateTime`**](./naive/struct.NaiveDateTime.html) respectively.
//!
//! They have almost equivalent interfaces as their timezone-aware twins,
//! but are not associated to time zones obviously and can be quite low-level.
//! They are mostly useful for building blocks for higher-level types.
//!
//! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
//! [`naive_local`](./datetime/struct.DateTime.html#method.naive_local) returns
//! [`naive_local`](./struct.DateTime.html#method.naive_local) returns
//! 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.
//!
//! ## Limitations
@ -307,7 +332,7 @@
//! Time types are limited in the nanosecond accuracy.
//!
//! [Leap seconds are supported in the representation but
//! Chrono doesn't try to make use of them](./naive/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.)
//! Almost *every* operation over the possible leap seconds will ignore them.
//! 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.
//! Any operation that can be ambiguous will return `None` in such cases.
//! For example, "a month later" of 2014-01-30 is not well-defined
//! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
//! and consequently `Utc.ymd(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
#![deny(missing_docs)]
extern crate time as stdtime;
extern crate time as oldtime;
extern crate num;
#[cfg(feature = "rustc-serialize")]
extern crate rustc_serialize;
#[cfg(feature = "serde")]
extern crate serde;
extern crate serde as serdelib;
pub use duration::Duration;
pub use offset::{TimeZone, Offset, LocalResult};
pub use offset::utc::UTC;
pub use offset::fixed::FixedOffset;
pub use offset::local::Local;
pub use naive::date::NaiveDate;
pub use naive::time::NaiveTime;
pub use naive::datetime::NaiveDateTime;
pub use date::Date;
// this reexport is to aid the transition and should not be in the prelude!
pub use oldtime::Duration;
#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset, Local};
#[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime};
pub use date::{Date, MIN_DATE, MAX_DATE};
pub use datetime::DateTime;
#[cfg(feature = "rustc-serialize")] pub use datetime::TsSeconds;
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
macro_rules! try_opt {
($e:expr) => (match $e { Some(v) => v, None => return None })
}
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 naive {
//! 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
//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
//! but can be also used for the simpler date and time handling.
pub mod date;
pub mod time;
pub mod datetime;
mod internals;
mod date;
mod isoweek;
mod time;
mod datetime;
pub use self::date::{NaiveDate, MIN_DATE, MAX_DATE};
pub use self::isoweek::IsoWeek;
pub use self::time::NaiveTime;
pub use self::datetime::{NaiveDateTime, TsSeconds};
/// Tools to help serializing/deserializing naive types.
#[cfg(feature = "serde")]
pub mod serde {
pub use super::datetime::serde::*;
}
}
pub mod date;
pub mod datetime;
mod date;
mod datetime;
pub mod format;
/// 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 order of the days of week depends on the context.
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub enum Weekday {
/// 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.
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;
/// 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.
fn weekday(&self) -> Weekday;
/// Returns the ISO week date: an adjusted year, week number and day of week.
/// The adjusted year may differ from that of the calendar date.
fn isoweekdate(&self) -> (i32, u32, Weekday);
/// Returns the ISO week.
fn iso_week(&self) -> IsoWeek;
/// 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.
/// 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;
/// Makes a new value with the hour number changed.
@ -708,7 +878,7 @@ pub trait Timelike: Sized {
fn test_readme_doomsday() {
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
let d4 = NaiveDate::from_ymd(y, 4, 4);
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.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// 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 oldtime::Duration as OldDuration;
use Timelike;
use div::div_mod_floor;
use duration::Duration;
use naive::date::NaiveDate;
use naive::datetime::NaiveDateTime;
use naive::{NaiveTime, NaiveDate, NaiveDateTime};
use DateTime;
use super::{TimeZone, Offset, LocalResult};
/// 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)]
pub struct FixedOffset {
local_minus_utc: i32,
}
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.
/// The negative `secs` means the Western Hemisphere.
///
/// Panics on the out-of-bound `secs`.
///
/// # Example
///
/// ~~~~
/// use chrono::{FixedOffset, TimeZone};
/// let hour = 3600;
/// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
/// .and_hms(0, 0, 0);
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
/// ~~~~
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
@ -62,6 +59,16 @@ impl FixedOffset {
/// The negative `secs` means the Eastern Hemisphere.
///
/// Panics on the out-of-bound `secs`.
///
/// # Example
///
/// ~~~~
/// use chrono::{FixedOffset, TimeZone};
/// let hour = 3600;
/// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
/// .and_hms(0, 0, 0);
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
/// ~~~~
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
@ -77,6 +84,16 @@ impl FixedOffset {
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 {
@ -96,7 +113,7 @@ impl TimeZone 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 {
@ -117,72 +134,91 @@ impl fmt::Display for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}
#[cfg(feature = "rustc-serialize")]
mod rustc_serialize {
use super::FixedOffset;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
// addition or subtraction of FixedOffset to/from Timelike values is same to
// adding or subtracting the offset's local_minus_utc value
// but keep keeps the leap second information.
// this should be implemented more efficiently, but for the time being, this is generic right now.
// TODO the current serialization format is NEVER intentionally defined.
// this basically follows the automatically generated implementation for those traits,
// plus manual verification steps for avoiding security problem.
// in the future it is likely to be redefined to more sane and reasonable format.
fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
where T: Timelike + Add<OldDuration, Output=T>
{
// extract and temporarily remove the fractional part and later recover it
let nanos = lhs.nanosecond();
let lhs = lhs.with_nanosecond(0).unwrap();
(lhs + OldDuration::seconds(rhs as i64)).with_nanosecond(nanos).unwrap()
}
impl Encodable for FixedOffset {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
let secs = self.to_serialized();
s.emit_struct("FixedOffset", 1, |s| {
try!(s.emit_struct_field("local_minus_utc", 0, |s| secs.encode(s)));
Ok(())
})
}
}
impl Add<FixedOffset> for NaiveTime {
type Output = NaiveTime;
impl Decodable for FixedOffset {
fn decode<D: Decoder>(d: &mut D) -> Result<FixedOffset, D::Error> {
d.read_struct("FixedOffset", 1, |d| {
let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode));
FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset"))
})
}
}
#[test]
fn test_encodable() {
use rustc_serialize::json::encode;
assert_eq!(encode(&FixedOffset::east(0)).ok(),
Some(r#"{"local_minus_utc":0}"#.into()));
assert_eq!(encode(&FixedOffset::east(1234)).ok(),
Some(r#"{"local_minus_utc":1234}"#.into()));
assert_eq!(encode(&FixedOffset::east(86399)).ok(),
Some(r#"{"local_minus_utc":86399}"#.into()));
assert_eq!(encode(&FixedOffset::west(1234)).ok(),
Some(r#"{"local_minus_utc":-1234}"#.into()));
assert_eq!(encode(&FixedOffset::west(86399)).ok(),
Some(r#"{"local_minus_utc":-86399}"#.into()));
}
#[test]
fn test_decodable() {
use rustc_serialize::json;
let decode = |s: &str| json::decode::<FixedOffset>(s);
assert_eq!(decode(r#"{"local_minus_utc":0}"#).ok(), Some(FixedOffset::east(0)));
assert_eq!(decode(r#"{"local_minus_utc": 1234}"#).ok(), Some(FixedOffset::east(1234)));
assert_eq!(decode(r#"{"local_minus_utc":86399}"#).ok(), Some(FixedOffset::east(86399)));
assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234)));
assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399)));
assert!(decode(r#"{"local_minus_utc":86400}"#).is_err());
assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err());
assert!(decode(r#"{"local_minus_utc":0.1}"#).is_err());
assert!(decode(r#"{"local_minus_utc":null}"#).is_err());
assert!(decode(r#"{}"#).is_err());
assert!(decode(r#"0"#).is_err());
assert!(decode(r#"1234"#).is_err());
assert!(decode(r#""string""#).is_err());
assert!(decode(r#"null"#).is_err());
#[inline]
fn add(self, rhs: FixedOffset) -> NaiveTime {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl Sub<FixedOffset> for NaiveTime {
type Output = NaiveTime;
#[inline]
fn sub(self, rhs: FixedOffset) -> NaiveTime {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
impl Add<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl Sub<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
#[cfg(test)]
mod tests {
use offset::TimeZone;
use super::FixedOffset;
#[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.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// 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 duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use {Date, DateTime};
use super::{TimeZone, LocalResult};
use super::fixed::FixedOffset;
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
/// 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 {
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
tm.tm_sec = 59;
}
#[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).
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
}
#[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)
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,
tm.tm_sec as u32, tm.tm_nsec as u32);
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`.
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
// to get the exact function we want (either `timegm` or `mktime`).
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
let tm_utcoff = if local {1} else {0};
let tm = stdtime::Tm {
let tm = oldtime::Tm {
tm_sec: d.second() as i32,
tm_min: d.minute() 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_isdst: -1,
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()
}
/// 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)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub struct Local;
impl Local {
@ -81,7 +88,7 @@ impl Local {
/// Returns a `DateTime` which corresponds to the current date.
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> {
self.from_local_date(local).map(|date| *date.offset())
}
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
self.from_local_datetime(local).map(|datetime| *datetime.offset())
}
@ -101,6 +109,7 @@ impl TimeZone for Local {
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
*self.from_utc_date(utc).offset()
}
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
*self.from_utc_datetime(utc).offset()
}
@ -113,18 +122,61 @@ impl TimeZone for Local {
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
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> {
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
Date::from_utc(*utc, *midnight.offset())
}
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
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.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* The time zone, which calculates offsets from the local time to UTC.
*
* There are three operations provided by the `TimeZone` trait:
*
* 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
* 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
* 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
*
* 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
* 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
* which implements `Offset` (which then passed to `TimeZone` for actual implementations).
* Technically speaking `TimeZone` has a total knowledge about given timescale,
* but `Offset` is used as a cache to avoid the repeated conversion
* and provides implementations for 1 and 3.
* An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
*/
//! The time zone, which calculates offsets from the local time to UTC.
//!
//! There are four operations provided by the `TimeZone` trait:
//!
//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
//! 4. Constructing `DateTime<Tz>` objects from various offsets
//!
//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
//! Technically speaking `TimeZone` has a total knowledge about given timescale,
//! but `Offset` is used as a cache to avoid the repeated conversion
//! and provides implementations for 1 and 3.
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
use std::fmt;
use std::ops::Add;
use Weekday;
use Timelike;
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use {Date, DateTime};
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.
#[derive(Clone, PartialEq, Debug)]
pub enum LocalResult<T> {
@ -173,11 +153,14 @@ impl<T: fmt::Debug> LocalResult<T> {
/// The offset from the local time to UTC.
pub trait Offset: Sized + Clone + fmt::Debug {
/// Returns the offset from UTC to the local time stored.
fn local_minus_utc(&self) -> Duration;
/// Returns the fixed offset from UTC to the local time stored.
fn fix(&self) -> FixedOffset;
}
/// The time zone.
///
/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and
/// [`DateTime`](../struct.DateTime.html) types.
pub trait TimeZone: Sized + Clone {
/// An associated offset type.
/// This type is used to store the actual offset in date and time types.
@ -191,6 +174,14 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Panics on the out-of-range date, invalid month and/or day.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone};
///
/// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC");
/// ~~~~
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
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.
///
/// 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>> {
match NaiveDate::from_ymd_opt(year, month, day) {
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.
///
/// 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> {
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.
///
/// 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> {
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.
///
/// 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> {
self.timestamp_opt(secs, nsecs).unwrap()
}
@ -286,7 +310,7 @@ pub trait TimeZone: Sized + Clone {
/// Parses a string with the specified format string and
/// returns a `DateTime` with the current offset.
/// See the [`format::strftime` module](../../format/strftime/index.html)
/// See the [`format::strftime` module](../format/strftime/index.html)
/// on the supported escape sequences.
///
/// 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.
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
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.
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
self.offset_from_local_datetime(local).map(|offset| {
let utc = add_with_leapsecond(local, &-offset.local_minus_utc());
DateTime::from_utc(utc, offset)
DateTime::from_utc(*local - offset.fix(), offset)
})
}
@ -343,7 +367,11 @@ pub trait TimeZone: Sized + Clone {
}
}
pub mod utc;
pub mod fixed;
pub mod local;
mod utc;
mod fixed;
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.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// 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 stdtime;
use oldtime;
use duration::Duration;
use naive::date::NaiveDate;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use super::{TimeZone, Offset, LocalResult};
use naive::{NaiveDate, NaiveDateTime};
use {Date, DateTime};
use super::{TimeZone, Offset, LocalResult, FixedOffset};
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
/// It is also used as an offset (which is also a dummy type).
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on the UTC struct is the preferred way to construct `DateTime<Utc>`
/// instances.
///
/// # Example
///
/// ~~~~
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
///
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
///
/// assert_eq!(Utc.timestamp(61, 0), dt);
/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
/// ~~~~
#[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.
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.
pub fn now() -> DateTime<UTC> {
let spec = stdtime::get_time();
pub fn now() -> DateTime<Utc> {
let spec = oldtime::get_time();
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 {
type Offset = UTC;
impl TimeZone for 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> {
LocalResult::Single(UTC)
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
LocalResult::Single(Utc)
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<UTC> {
LocalResult::Single(UTC)
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
LocalResult::Single(Utc)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc { Utc }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { Utc }
}
impl Offset for UTC {
fn local_minus_utc(&self) -> Duration { Duration::zero() }
impl Offset for Utc {
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") }
}
impl fmt::Display for UTC {
impl fmt::Display for Utc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
}