Merge branch 'master' into naive_assign
This commit is contained in:
commit
8ea2d3f236
|
@ -0,0 +1,62 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This is the script that's executed by travis, you can run it yourself to run
|
||||
# the exact same suite
|
||||
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
channel() {
|
||||
if [ -n "${TRAVIS}" ]; then
|
||||
if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then
|
||||
pwd
|
||||
(set -x; cargo "$@")
|
||||
fi
|
||||
elif [ -n "${APPVEYOR}" ]; then
|
||||
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
|
||||
pwd
|
||||
(set -x; cargo "$@")
|
||||
fi
|
||||
else
|
||||
pwd
|
||||
(set -x; cargo "+${CHANNEL}" "$@")
|
||||
fi
|
||||
}
|
||||
|
||||
build_and_test() {
|
||||
# interleave building and testing in hope that it saves time
|
||||
# also vary the local time zone to (hopefully) catch tz-dependent bugs
|
||||
# also avoid doc-testing multiple times---it takes a lot and rarely helps
|
||||
cargo clean
|
||||
channel build -v
|
||||
TZ=ACST-9:30 channel test -v --lib
|
||||
channel build -v --features rustc-serialize
|
||||
TZ=EST4 channel test -v --features rustc-serialize --lib
|
||||
channel build -v --features 'serde bincode'
|
||||
TZ=UTC0 channel test -v --features 'serde bincode'
|
||||
}
|
||||
|
||||
build_only() {
|
||||
# Rust 1.13 doesn't support custom derive, so, to avoid doctests which
|
||||
# validate that, we just build there.
|
||||
cargo clean
|
||||
channel build -v
|
||||
channel build -v --features rustc-serialize
|
||||
channel build -v --features 'serde bincode'
|
||||
}
|
||||
|
||||
rustc --version
|
||||
cargo --version
|
||||
|
||||
CHANNEL=nightly
|
||||
build_and_test
|
||||
|
||||
CHANNEL=beta
|
||||
build_and_test
|
||||
|
||||
CHANNEL=stable
|
||||
build_and_test
|
||||
|
||||
CHANNEL=1.13.0
|
||||
build_only
|
15
.travis.yml
15
.travis.yml
|
@ -1,9 +1,8 @@
|
|||
language: rust
|
||||
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:
|
||||
|
|
13
AUTHORS.txt
13
AUTHORS.txt
|
@ -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>
|
||||
|
|
106
CHANGELOG.md
106
CHANGELOG.md
|
@ -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.)
|
||||
|
||||
|
|
22
Cargo.toml
22
Cargo.toml
|
@ -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" }
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
65
Makefile
65
Makefile
|
@ -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' >> $@
|
||||
( \
|
||||
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:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
|
||||
echo '***[Complete Documentation][doc]***' >> $@
|
||||
echo >> $@
|
||||
echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@
|
||||
echo >> $@
|
||||
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g'; \
|
||||
echo; \
|
||||
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
|
||||
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
|
||||
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$$VERSION'\/chrono\//g' \
|
||||
) > $@
|
||||
|
||||
.PHONY: test
|
||||
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
224
README.md
|
@ -1,18 +1,22 @@
|
|||
[Chrono][doc] 0.2.25
|
||||
====================
|
||||
[Chrono][docsrs] 0.4.0
|
||||
======================
|
||||
|
||||
[![Chrono on Travis CI][travis-image]][travis]
|
||||
[![Chrono on 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.
|
||||
|
||||
|
|
10
appveyor.yml
10
appveyor.yml
|
@ -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'
|
||||
|
|
181
src/date.rs
181
src/date.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
649
src/datetime.rs
649
src/datetime.rs
|
@ -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, LocalResult, Utc, Local, FixedOffset};
|
||||
use serdelib::{ser, de};
|
||||
|
||||
/// Ser/de to/from timestamps in seconds
|
||||
///
|
||||
/// Intended for use with `serde`'s `with` attribute.
|
||||
///
|
||||
/// # Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// # // We mark this ignored so that we can test on 1.13 (which does not
|
||||
/// # // support custom derive), and run tests with --ignored on beta and
|
||||
/// # // nightly to actually trigger these.
|
||||
/// #
|
||||
/// # #[macro_use] extern crate serde_derive;
|
||||
/// # #[macro_use] extern crate serde_json;
|
||||
/// # extern crate chrono;
|
||||
/// # use chrono::{TimeZone, DateTime, Utc};
|
||||
/// use chrono::serde::ts_seconds;
|
||||
/// #[derive(Deserialize, Serialize)]
|
||||
/// struct S {
|
||||
/// #[serde(with = "ts_seconds")]
|
||||
/// time: DateTime<Utc>
|
||||
/// }
|
||||
///
|
||||
/// # fn example() -> Result<S, serde_json::Error> {
|
||||
/// let time = Utc.ymd(2015, 5, 15).and_hms(10, 0, 0);
|
||||
/// let my_s = S {
|
||||
/// time: time.clone(),
|
||||
/// };
|
||||
///
|
||||
/// let as_string = serde_json::to_string(&my_s)?;
|
||||
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||
/// let my_s: S = serde_json::from_str(&as_string)?;
|
||||
/// assert_eq!(my_s.time, time);
|
||||
/// # Ok(my_s)
|
||||
/// # }
|
||||
/// # fn main() { example().unwrap(); }
|
||||
/// ```
|
||||
pub mod ts_seconds {
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {DateTime, Utc, FixedOffset};
|
||||
use offset::TimeZone;
|
||||
use offset::utc::UTC;
|
||||
use offset::local::Local;
|
||||
use offset::fixed::FixedOffset;
|
||||
use std::fmt::Display;
|
||||
use serde::{ser, de};
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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/))
|
||||
|
|
|
@ -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: () })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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 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,
|
||||
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 }
|
||||
second: None, nanosecond: None, timestamp: None, offset: None,
|
||||
_dummy: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parsed {
|
||||
/// Returns the initial value of parsed parts.
|
||||
pub fn new() -> Parsed {
|
||||
Parsed::default()
|
||||
}
|
||||
|
||||
/// Tries to set the [`year`](#structfield.year) field from given value.
|
||||
|
@ -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));
|
||||
|
|
|
@ -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.
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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 {
|
||||
|
|
406
src/lib.rs
406
src/lib.rs
|
@ -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);
|
||||
|
|
1432
src/naive/date.rs
1432
src/naive/date.rs
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,778 @@
|
|||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! The internal implementation of the calendar and ordinal date.
|
||||
//!
|
||||
//! The current implementation is optimized for determining year, month, day and day of week.
|
||||
//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
|
||||
//! which are included in every packed `NaiveDate` instance.
|
||||
//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
|
||||
//! based on the moderately-sized lookup table (~1.5KB)
|
||||
//! and the packed representation is chosen for the efficient lookup.
|
||||
//! Every internal data structure does not validate its input,
|
||||
//! but the conversion keeps the valid value valid and the invalid value invalid
|
||||
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
|
||||
|
||||
#![allow(dead_code)] // some internal methods have been left for consistency
|
||||
|
||||
use std::{i32, fmt};
|
||||
use num::traits::FromPrimitive;
|
||||
use Weekday;
|
||||
use div::{div_rem, mod_floor};
|
||||
|
||||
/// The internal date representation. This also includes the packed `Mdf` value.
|
||||
pub type DateImpl = i32;
|
||||
|
||||
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
|
||||
pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
|
||||
|
||||
/// The year flags (aka the dominical letter).
|
||||
///
|
||||
/// There are 14 possible classes of year in the Gregorian calendar:
|
||||
/// common and leap years starting with Monday through Sunday.
|
||||
/// The `YearFlags` stores this information into 4 bits `abbb`,
|
||||
/// where `a` is `1` for the common year (simplifies the `Of` validation)
|
||||
/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
|
||||
/// (simplifies the day of week calculation from the 1-based ordinal).
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
pub struct YearFlags(pub u8);
|
||||
|
||||
pub const A: YearFlags = YearFlags(0o15); pub const AG: YearFlags = YearFlags(0o05);
|
||||
pub const B: YearFlags = YearFlags(0o14); pub const BA: YearFlags = YearFlags(0o04);
|
||||
pub const C: YearFlags = YearFlags(0o13); pub const CB: YearFlags = YearFlags(0o03);
|
||||
pub const D: YearFlags = YearFlags(0o12); pub const DC: YearFlags = YearFlags(0o02);
|
||||
pub const E: YearFlags = YearFlags(0o11); pub const ED: YearFlags = YearFlags(0o01);
|
||||
pub const F: YearFlags = YearFlags(0o17); pub const FE: YearFlags = YearFlags(0o07);
|
||||
pub const G: YearFlags = YearFlags(0o16); pub const GF: YearFlags = YearFlags(0o06);
|
||||
|
||||
static YEAR_TO_FLAGS: [YearFlags; 400] = [
|
||||
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F,
|
||||
ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B,
|
||||
AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
|
||||
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
|
||||
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
|
||||
C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
|
||||
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D,
|
||||
CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G,
|
||||
FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C,
|
||||
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
|
||||
E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C,
|
||||
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F,
|
||||
ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B,
|
||||
AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
|
||||
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
|
||||
G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
|
||||
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
|
||||
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D,
|
||||
CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G,
|
||||
FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
|
||||
];
|
||||
|
||||
static YEAR_DELTAS: [u8; 401] = [
|
||||
0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
|
||||
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10,
|
||||
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15,
|
||||
15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20,
|
||||
20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
|
||||
25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29,
|
||||
29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34,
|
||||
34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39,
|
||||
39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44,
|
||||
44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, // 200
|
||||
49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53,
|
||||
53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58,
|
||||
58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63,
|
||||
63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68,
|
||||
68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, // 300
|
||||
73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77,
|
||||
77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82,
|
||||
82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87,
|
||||
87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92,
|
||||
92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97 // 400+1
|
||||
];
|
||||
|
||||
pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
|
||||
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
|
||||
let delta = YEAR_DELTAS[year_mod_400 as usize] as u32;
|
||||
if ordinal0 < delta {
|
||||
year_mod_400 -= 1;
|
||||
ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32;
|
||||
} else {
|
||||
ordinal0 -= delta;
|
||||
}
|
||||
(year_mod_400, ordinal0 + 1)
|
||||
}
|
||||
|
||||
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
|
||||
year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1
|
||||
}
|
||||
|
||||
impl YearFlags {
|
||||
#[inline]
|
||||
pub fn from_year(year: i32) -> YearFlags {
|
||||
let year = mod_floor(year, 400);
|
||||
YearFlags::from_year_mod_400(year)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_year_mod_400(year: i32) -> YearFlags {
|
||||
YEAR_TO_FLAGS[year as usize]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ndays(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
366 - (flags >> 3) as u32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweek_delta(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
let mut delta = flags as u32 & 0b111;
|
||||
if delta < 3 { delta += 7; }
|
||||
delta
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn nisoweeks(&self) -> u32 {
|
||||
let YearFlags(flags) = *self;
|
||||
52 + ((0b00000100_00000110 >> flags as usize) & 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for YearFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let YearFlags(flags) = *self;
|
||||
match flags {
|
||||
0o15 => "A".fmt(f), 0o05 => "AG".fmt(f),
|
||||
0o14 => "B".fmt(f), 0o04 => "BA".fmt(f),
|
||||
0o13 => "C".fmt(f), 0o03 => "CB".fmt(f),
|
||||
0o12 => "D".fmt(f), 0o02 => "DC".fmt(f),
|
||||
0o11 => "E".fmt(f), 0o01 => "ED".fmt(f),
|
||||
0o10 => "F?".fmt(f), 0o00 => "FE?".fmt(f), // non-canonical
|
||||
0o17 => "F".fmt(f), 0o07 => "FE".fmt(f),
|
||||
0o16 => "G".fmt(f), 0o06 => "GF".fmt(f),
|
||||
_ => write!(f, "YearFlags({})", flags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MIN_OL: u32 = 1 << 1;
|
||||
pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
|
||||
pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
|
||||
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
|
||||
|
||||
const XX: i8 = -128;
|
||||
static MDL_TO_OL: [i8; (MAX_MDL as usize + 1)] = [
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
|
||||
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
|
||||
XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
|
||||
XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
|
||||
XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
|
||||
XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
|
||||
XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
|
||||
XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
|
||||
XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
|
||||
XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
|
||||
XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
|
||||
XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
|
||||
XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
|
||||
XX, XX, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12
|
||||
];
|
||||
|
||||
static OL_TO_MDL: [u8; (MAX_OL as usize + 1)] = [
|
||||
0, 0, // 0
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
|
||||
66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
|
||||
74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
|
||||
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
|
||||
76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
|
||||
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
|
||||
80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
|
||||
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
|
||||
82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
|
||||
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
|
||||
86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
|
||||
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
|
||||
88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
|
||||
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
|
||||
90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
|
||||
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
|
||||
94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
|
||||
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
|
||||
96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
|
||||
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
|
||||
100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
|
||||
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98, // 12
|
||||
];
|
||||
|
||||
/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
|
||||
/// which is an index to the `OL_TO_MDL` lookup table.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Of(pub u32);
|
||||
|
||||
impl Of {
|
||||
#[inline]
|
||||
fn clamp_ordinal(ordinal: u32) -> u32 {
|
||||
if ordinal > 366 {0} else {ordinal}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
Of((ordinal << 4) | (flags as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => Of(mdf.wrapping_sub((v as i32 as u32 & 0x3ff) << 3)),
|
||||
None => Of(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Of(of) = *self;
|
||||
let ol = of >> 3;
|
||||
MIN_OL <= ol && ol <= MAX_OL
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ordinal(&self) -> u32 {
|
||||
let Of(of) = *self;
|
||||
of >> 4
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_ordinal(&self, ordinal: u32) -> Of {
|
||||
let ordinal = Of::clamp_ordinal(ordinal);
|
||||
let Of(of) = *self;
|
||||
Of((of & 0b1111) | (ordinal << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
let Of(of) = *self;
|
||||
YearFlags((of & 0b1111) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of((of & !0b1111) | (flags as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn weekday(&self) -> Weekday {
|
||||
let Of(of) = *self;
|
||||
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
|
||||
// week ordinal = ordinal + delta
|
||||
let Of(of) = *self;
|
||||
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
|
||||
(weekord / 7, Weekday::from_u32(weekord % 7).unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_mdf(&self) -> Mdf {
|
||||
Mdf::from_of(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn succ(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of + (1 << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pred(&self) -> Of {
|
||||
let Of(of) = *self;
|
||||
Of(of - (1 << 4))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Of {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Of(of) = *self;
|
||||
write!(f, "Of(({} << 4) | {:#04o} /*{:?}*/)",
|
||||
of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8))
|
||||
}
|
||||
}
|
||||
|
||||
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
|
||||
///
|
||||
/// The whole bits except for the least 3 bits are referred as `Mdl`
|
||||
/// (month, day of month and leap flag),
|
||||
/// which is an index to the `MDL_TO_OL` lookup table.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct Mdf(pub u32);
|
||||
|
||||
impl Mdf {
|
||||
#[inline]
|
||||
fn clamp_month(month: u32) -> u32 {
|
||||
if month > 12 {0} else {month}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn clamp_day(day: u32) -> u32 {
|
||||
if day > 31 {0} else {day}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
let day = Mdf::clamp_day(day);
|
||||
Mdf((month << 9) | (day << 4) | (flags as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_of(Of(of): Of) -> Mdf {
|
||||
let ol = of >> 3;
|
||||
match OL_TO_MDL.get(ol as usize) {
|
||||
Some(&v) => Mdf(of + ((v as u32) << 3)),
|
||||
None => Mdf(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn valid(&self) -> bool {
|
||||
let Mdf(mdf) = *self;
|
||||
let mdl = mdf >> 3;
|
||||
match MDL_TO_OL.get(mdl as usize) {
|
||||
Some(&v) => v >= 0,
|
||||
None => false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn month(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
mdf >> 9
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_month(&self, month: u32) -> Mdf {
|
||||
let month = Mdf::clamp_month(month);
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & 0b11111_1111) | (month << 9))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn day(&self) -> u32 {
|
||||
let Mdf(mdf) = *self;
|
||||
(mdf >> 4) & 0b11111
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_day(&self, day: u32) -> Mdf {
|
||||
let day = Mdf::clamp_day(day);
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b11111_0000) | (day << 4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&self) -> YearFlags {
|
||||
let Mdf(mdf) = *self;
|
||||
YearFlags((mdf & 0b1111) as u8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
|
||||
let Mdf(mdf) = *self;
|
||||
Mdf((mdf & !0b1111) | (flags as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_of(&self) -> Of {
|
||||
Of::from_mdf(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mdf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let Mdf(mdf) = *self;
|
||||
write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
|
||||
mdf >> 9, (mdf >> 4) & 0b11111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(bench)] extern crate test;
|
||||
|
||||
use Weekday;
|
||||
use super::{Of, Mdf};
|
||||
use super::{YearFlags, A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF};
|
||||
use num::iter::range_inclusive;
|
||||
use std::u32;
|
||||
|
||||
const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
|
||||
const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
|
||||
const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
|
||||
|
||||
#[test]
|
||||
fn test_year_flags_ndays_from_year() {
|
||||
assert_eq!(YearFlags::from_year(2014).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year(2012).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year(2000).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year(1900).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year(1600).ndays(), 366);
|
||||
assert_eq!(YearFlags::from_year( 1).ndays(), 365);
|
||||
assert_eq!(YearFlags::from_year( 0).ndays(), 366); // 1 BCE (proleptic Gregorian)
|
||||
assert_eq!(YearFlags::from_year( -1).ndays(), 365); // 2 BCE
|
||||
assert_eq!(YearFlags::from_year( -4).ndays(), 366); // 5 BCE
|
||||
assert_eq!(YearFlags::from_year( -99).ndays(), 365); // 100 BCE
|
||||
assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
|
||||
assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
|
||||
assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_year_flags_nisoweeks() {
|
||||
assert_eq!(A.nisoweeks(), 52);
|
||||
assert_eq!(B.nisoweeks(), 52);
|
||||
assert_eq!(C.nisoweeks(), 52);
|
||||
assert_eq!(D.nisoweeks(), 53);
|
||||
assert_eq!(E.nisoweeks(), 52);
|
||||
assert_eq!(F.nisoweeks(), 52);
|
||||
assert_eq!(G.nisoweeks(), 52);
|
||||
assert_eq!(AG.nisoweeks(), 52);
|
||||
assert_eq!(BA.nisoweeks(), 52);
|
||||
assert_eq!(CB.nisoweeks(), 52);
|
||||
assert_eq!(DC.nisoweeks(), 53);
|
||||
assert_eq!(ED.nisoweeks(), 53);
|
||||
assert_eq!(FE.nisoweeks(), 52);
|
||||
assert_eq!(GF.nisoweeks(), 52);
|
||||
}
|
||||
|
||||
#[cfg(bench)]
|
||||
#[bench]
|
||||
fn bench_year_flags_from_year(bh: &mut test::Bencher) {
|
||||
bh.iter(|| {
|
||||
for year in -999i32..1000 {
|
||||
YearFlags::from_year(year);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of() {
|
||||
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||
for ordinal in range_inclusive(ordinal1, ordinal2) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
assert!(of.valid() == expected,
|
||||
"ordinal {} = {:?} should be {} for dominical year {:?}",
|
||||
ordinal, of, if expected {"valid"} else {"invalid"}, flags);
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 365);
|
||||
check(false, flags, 366, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0);
|
||||
check(true, flags, 1, 366);
|
||||
check(false, flags, 367, 1024);
|
||||
check(false, flags, u32::MAX, u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_valid() {
|
||||
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32,
|
||||
month2: u32, day2: u32) {
|
||||
for month in range_inclusive(month1, month2) {
|
||||
for day in range_inclusive(day1, day2) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
assert!(mdf.valid() == expected,
|
||||
"month {} day {} = {:?} should be {} for dominical year {:?}",
|
||||
month, day, mdf, if expected {"valid"} else {"invalid"}, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0, 0, 1024);
|
||||
check(false, flags, 0, 0, 16, 0);
|
||||
check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024);
|
||||
check(true, flags, 2, 1, 2, 28); check(false, flags, 2, 29, 2, 1024);
|
||||
check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024);
|
||||
check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024);
|
||||
check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024);
|
||||
check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024);
|
||||
check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024);
|
||||
check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024);
|
||||
check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024);
|
||||
check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024);
|
||||
check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024);
|
||||
check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024);
|
||||
check(false, flags, 13, 0, 16, 1024);
|
||||
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
|
||||
check(false, flags, 0, u32::MAX, 16, u32::MAX);
|
||||
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
|
||||
}
|
||||
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(false, flags, 0, 0, 0, 1024);
|
||||
check(false, flags, 0, 0, 16, 0);
|
||||
check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024);
|
||||
check(true, flags, 2, 1, 2, 29); check(false, flags, 2, 30, 2, 1024);
|
||||
check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024);
|
||||
check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024);
|
||||
check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024);
|
||||
check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024);
|
||||
check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024);
|
||||
check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024);
|
||||
check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024);
|
||||
check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024);
|
||||
check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024);
|
||||
check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024);
|
||||
check(false, flags, 13, 0, 16, 1024);
|
||||
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
|
||||
check(false, flags, 0, u32::MAX, 16, u32::MAX);
|
||||
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for ordinal in range_inclusive(1u32, 366) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_with_fields() {
|
||||
fn check(flags: YearFlags, ordinal: u32) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
|
||||
for ordinal in range_inclusive(0u32, 1024) {
|
||||
let of = of.with_ordinal(ordinal);
|
||||
assert_eq!(of.valid(), Of::new(ordinal, flags).valid());
|
||||
if of.valid() {
|
||||
assert_eq!(of.ordinal(), ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 365);
|
||||
}
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(flags, 1);
|
||||
check(flags, 366);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_weekday() {
|
||||
assert_eq!(Of::new(1, A).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
|
||||
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
|
||||
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
|
||||
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
|
||||
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
|
||||
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
|
||||
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
|
||||
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
|
||||
|
||||
for &flags in FLAGS.iter() {
|
||||
let mut prev = Of::new(1, flags).weekday();
|
||||
for ordinal in range_inclusive(2u32, flags.ndays()) {
|
||||
let of = Of::new(ordinal, flags);
|
||||
let expected = prev.succ();
|
||||
assert_eq!(of.weekday(), expected);
|
||||
prev = expected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_fields() {
|
||||
for &flags in FLAGS.iter() {
|
||||
for month in range_inclusive(1u32, 12) {
|
||||
for day in range_inclusive(1u32, 31) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_with_fields() {
|
||||
fn check(flags: YearFlags, month: u32, day: u32) {
|
||||
let mdf = Mdf::new(month, day, flags);
|
||||
|
||||
for month in range_inclusive(0u32, 16) {
|
||||
let mdf = mdf.with_month(month);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
|
||||
for day in range_inclusive(0u32, 1024) {
|
||||
let mdf = mdf.with_day(day);
|
||||
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf.month(), month);
|
||||
assert_eq!(mdf.day(), day);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for &flags in NONLEAP_FLAGS.iter() {
|
||||
check(flags, 1, 1);
|
||||
check(flags, 1, 31);
|
||||
check(flags, 2, 1);
|
||||
check(flags, 2, 28);
|
||||
check(flags, 2, 29);
|
||||
check(flags, 12, 31);
|
||||
}
|
||||
for &flags in LEAP_FLAGS.iter() {
|
||||
check(flags, 1, 1);
|
||||
check(flags, 1, 31);
|
||||
check(flags, 2, 1);
|
||||
check(flags, 2, 29);
|
||||
check(flags, 2, 30);
|
||||
check(flags, 12, 31);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_isoweekdate_raw() {
|
||||
for &flags in FLAGS.iter() {
|
||||
// January 4 should be in the first week
|
||||
let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw();
|
||||
assert_eq!(week, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
assert_eq!(of.valid(), of.to_mdf().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
assert_eq!(mdf.valid(), mdf.to_of().valid());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of_to_mdf_to_of() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let of = Of(i);
|
||||
if of.valid() {
|
||||
assert_eq!(of, of.to_mdf().to_of());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mdf_to_of_to_mdf() {
|
||||
for i in range_inclusive(0u32, 8192) {
|
||||
let mdf = Mdf(i);
|
||||
if mdf.valid() {
|
||||
assert_eq!(mdf, mdf.to_of().to_mdf());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
// This is a part of Chrono.
|
||||
// See README.md and LICENSE.txt for details.
|
||||
|
||||
//! ISO 8601 week.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::internals::{DateImpl, Of, YearFlags};
|
||||
|
||||
/// ISO 8601 week.
|
||||
///
|
||||
/// This type, combined with [`Weekday`](../enum.Weekday.html),
|
||||
/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date).
|
||||
/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types
|
||||
/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
|
||||
pub struct IsoWeek {
|
||||
// note that this allows for larger year range than `NaiveDate`.
|
||||
// this is crucial because we have an edge case for the first and last week supported,
|
||||
// which year number might not match the calendar year number.
|
||||
ywf: DateImpl, // (year << 10) | (week << 4) | flag
|
||||
}
|
||||
|
||||
/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
|
||||
//
|
||||
// internal use only. we don't expose the public constructor for `IsoWeek` for now,
|
||||
// because the year range for the week date and the calendar date do not match and
|
||||
// it is confusing to have a date that is out of range in one and not in another.
|
||||
// currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`.
|
||||
pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
|
||||
let (rawweek, _) = of.isoweekdate_raw();
|
||||
let (year, week) = if rawweek < 1 { // previous year
|
||||
let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
|
||||
(year - 1, prevlastweek)
|
||||
} else {
|
||||
let lastweek = of.flags().nisoweeks();
|
||||
if rawweek > lastweek { // next year
|
||||
(year + 1, 1)
|
||||
} else {
|
||||
(year, rawweek)
|
||||
}
|
||||
};
|
||||
IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | of.flags().0 as DateImpl }
|
||||
}
|
||||
|
||||
impl IsoWeek {
|
||||
/// Returns the year number for this ISO week.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().year(), 2015);
|
||||
/// ~~~~
|
||||
///
|
||||
/// This year number might not match the calendar year number.
|
||||
/// Continuing the example...
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Datelike, Weekday};
|
||||
/// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
|
||||
/// assert_eq!(d.year(), 2014);
|
||||
/// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29));
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn year(&self) -> i32 {
|
||||
self.ywf >> 10
|
||||
}
|
||||
|
||||
/// Returns the ISO week number starting from 1.
|
||||
///
|
||||
/// The return value ranges from 1 to 53. (The last week of year differs by years.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().week(), 15);
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn week(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32
|
||||
}
|
||||
|
||||
/// Returns the ISO week number starting from 0.
|
||||
///
|
||||
/// The return value ranges from 0 to 52. (The last week of year differs by years.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike, Weekday};
|
||||
///
|
||||
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
|
||||
/// assert_eq!(d.iso_week().week0(), 14);
|
||||
/// ~~~~
|
||||
#[inline]
|
||||
pub fn week0(&self) -> u32 {
|
||||
((self.ywf >> 4) & 0x3f) as u32 - 1
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Debug` output of the ISO week `w` is same to
|
||||
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
|
||||
/// where `d` is any `NaiveDate` value in that week.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~~
|
||||
/// use chrono::{NaiveDate, Datelike};
|
||||
///
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52");
|
||||
/// ~~~~
|
||||
///
|
||||
/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE.
|
||||
///
|
||||
/// ~~~~
|
||||
/// # use chrono::{NaiveDate, Datelike};
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52");
|
||||
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52");
|
||||
/// ~~~~
|
||||
impl fmt::Debug for IsoWeek {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let year = self.year();
|
||||
let week = self.week();
|
||||
if 0 <= year && year <= 9999 {
|
||||
write!(f, "{:04}-W{:02}", year, week)
|
||||
} else {
|
||||
// ISO 8601 requires the explicit sign for out-of-range years
|
||||
write!(f, "{:+05}-W{:02}", year, week)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use naive::{internals, MIN_DATE, MAX_DATE};
|
||||
use Datelike;
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_extremes() {
|
||||
let minweek = MIN_DATE.iso_week();
|
||||
let maxweek = MAX_DATE.iso_week();
|
||||
|
||||
assert_eq!(minweek.year(), internals::MIN_YEAR);
|
||||
assert_eq!(minweek.week(), 1);
|
||||
assert_eq!(minweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string());
|
||||
|
||||
assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
|
||||
assert_eq!(maxweek.week(), 1);
|
||||
assert_eq!(maxweek.week0(), 0);
|
||||
assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,47 +1,44 @@
|
|||
// This is a part of rust-chrono.
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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") }
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue