Compare commits
No commits in common. "master" and "no-std" have entirely different histories.
65
CHANGELOG.md
65
CHANGELOG.md
|
@ -8,61 +8,6 @@ Chrono obeys the principle of [Semantic Versioning](http://semver.org/).
|
||||||
There were/are numerous minor versions before 1.0 due to the language changes.
|
There were/are numerous minor versions before 1.0 due to the language changes.
|
||||||
Versions with only mechanical changes will be omitted from the following list.
|
Versions with only mechanical changes will be omitted from the following list.
|
||||||
|
|
||||||
## 0.4.11
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
* Support a space or `T` in `FromStr` for `DateTime<Tz>`, meaning that e.g.
|
|
||||||
`dt.to_string().parse::<DateTime<Utc>>()` now correctly works on round-trip.
|
|
||||||
(@quodlibetor in #378)
|
|
||||||
* Support "negative UTC" in `parse_from_rfc2822` (@quodlibetor #368 reported in
|
|
||||||
#102)
|
|
||||||
* Support comparisons of DateTimes with different timezones (@dlalic in #375)
|
|
||||||
* Many documentation improvements
|
|
||||||
|
|
||||||
### Bitrot and external integration fixes
|
|
||||||
|
|
||||||
* Don't use wasmbind on wasi (@coolreader18 #365)
|
|
||||||
* Avoid deprecation warnings for `Error::description` (@AnderEnder and
|
|
||||||
@quodlibetor #376)
|
|
||||||
|
|
||||||
### Internal improvements
|
|
||||||
|
|
||||||
* Use Criterion for benchmarks (@quodlibetor)
|
|
||||||
|
|
||||||
## 0.4.10
|
|
||||||
|
|
||||||
### Compatibility notes
|
|
||||||
|
|
||||||
* Putting some functionality behind an `alloc` feature to improve no-std
|
|
||||||
support (in #341) means that if you were relying on chrono with
|
|
||||||
`no-default-features` *and* using any of the functions that require alloc
|
|
||||||
support (i.e. any of the string-generating functions like `to_rfc3339`) you
|
|
||||||
will need to add the `alloc` feature in your Cargo.toml.
|
|
||||||
|
|
||||||
### Improvements
|
|
||||||
|
|
||||||
* `DateTime::parse_from_str` is more than 2x faster in some cases. (@michalsrb
|
|
||||||
#358)
|
|
||||||
* Significant improvements to no-std and alloc support (This should also make
|
|
||||||
many format/serialization operations induce zero unnecessary allocations)
|
|
||||||
(@CryZe #341)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Functions that were accepting `Iterator` of `Item`s (for example
|
|
||||||
`format_with_items`) now accept `Iterator` of `Borrow<Item>`, so one can
|
|
||||||
use values or references. (@michalsrb #358)
|
|
||||||
* Add built-in support for structs with nested `Option<Datetime>` etc fields
|
|
||||||
(@manifest #302)
|
|
||||||
|
|
||||||
### Internal/doc improvements
|
|
||||||
|
|
||||||
* Use markdown footnotes on the `strftime` docs page (@qudlibetor #359)
|
|
||||||
* Migrate from `try!` -> `?` (question mark) because it is now emitting
|
|
||||||
deprecation warnings and has been stable since rustc 1.13.0
|
|
||||||
* Deny dead code
|
|
||||||
|
|
||||||
## 0.4.9
|
## 0.4.9
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
@ -107,12 +52,6 @@ Versions with only mechanical changes will be omitted from the following list.
|
||||||
* Doc improvements -- improve README CI verification, external links
|
* Doc improvements -- improve README CI verification, external links
|
||||||
* winapi upgrade to 0.3
|
* winapi upgrade to 0.3
|
||||||
|
|
||||||
## Unreleased
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Added `NaiveDate::from_weekday_of_month{,_opt}` for getting eg. the 2nd Friday of March 2017.
|
|
||||||
|
|
||||||
## 0.4.5
|
## 0.4.5
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
@ -248,7 +187,7 @@ release due to the compatibility concern raised.
|
||||||
- The major version was made to fix the broken Serde dependency issues. (#146, #156, #158, #159)
|
- The major version was made to fix the broken Serde dependency issues. (#146, #156, #158, #159)
|
||||||
|
|
||||||
The original intention to technically break the dependency was
|
The original intention to technically break the dependency was
|
||||||
to facilitate the use of Serde 1.0 at the expense of temporary breakage.
|
to faciliate the use of Serde 1.0 at the expense of temporary breakage.
|
||||||
Whether this was appropriate or not is quite debatable,
|
Whether this was appropriate or not is quite debatable,
|
||||||
but it became clear that there are several high-profile crates requiring Serde 0.9
|
but it became clear that there are several high-profile crates requiring Serde 0.9
|
||||||
and it is not feasible to force them to use Serde 1.0 anyway.
|
and it is not feasible to force them to use Serde 1.0 anyway.
|
||||||
|
@ -462,7 +401,7 @@ and replaced by 0.2.25 very shortly. Duh.)
|
||||||
They are glibc extensions which seem to be reasonably widespread (e.g. Ruby).
|
They are glibc extensions which seem to be reasonably widespread (e.g. Ruby).
|
||||||
|
|
||||||
- Added `%:z` specifier and corresponding formatting items
|
- Added `%:z` specifier and corresponding formatting items
|
||||||
which is essentially the same as `%z` but with a colon.
|
which is essentially same to `%z` but with a colon.
|
||||||
|
|
||||||
- Added a new specifier `%.f` which precision adapts from the input.
|
- Added a new specifier `%.f` which precision adapts from the input.
|
||||||
This was added as a response to the UX problems in the original nanosecond specifier `%f`.
|
This was added as a response to the UX problems in the original nanosecond specifier `%f`.
|
||||||
|
|
33
Cargo.toml
33
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.11"
|
version = "0.4.9"
|
||||||
authors = [
|
authors = [
|
||||||
"Kang Seonghoon <public+rust@mearie.org>",
|
"Kang Seonghoon <public+rust@mearie.org>",
|
||||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
"Brandon W Maister <quodlibetor@gmail.com>",
|
||||||
|
@ -24,47 +24,34 @@ appveyor = { repository = "chronotope/chrono" }
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["clock", "std"]
|
default = ["clock"]
|
||||||
alloc = []
|
clock = ["time"]
|
||||||
std = []
|
|
||||||
clock = ["time", "std"]
|
|
||||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||||
__internal_bench = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
libc = { version = "0.2", default-features = false }
|
||||||
time = { version = "0.1.39", optional = true }
|
time = { version = "0.1.39", optional = true }
|
||||||
num-integer = { version = "0.1.36", default-features = false }
|
num-integer = { version = "0.1.36", default-features = false }
|
||||||
num-traits = { version = "0.2", default-features = false }
|
num-traits = { version = "0.2", default-features = false }
|
||||||
rustc-serialize = { version = "0.3.20", optional = true }
|
rustc-serialize = { version = "0.3.20", optional = true }
|
||||||
serde = { version = "1.0.99", default-features = false, optional = true }
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies]
|
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
|
||||||
wasm-bindgen = { version = "0.2", optional = true }
|
wasm-bindgen = { version = "0.2", optional = true }
|
||||||
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
|
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
serde_derive = { version = "1", default-features = false }
|
serde_derive = { version = "1" }
|
||||||
bincode = { version = "0.8.0" }
|
bincode = { version = "0.8.0" }
|
||||||
num-iter = { version = "0.1.35", default-features = false }
|
num-iter = { version = "0.1.35", default-features = false }
|
||||||
criterion = { version = "0.3" }
|
|
||||||
doc-comment = "0.3"
|
doc-comment = "0.3"
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
|
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dev-dependencies]
|
||||||
wasm-bindgen-test = "0.2"
|
wasm-bindgen-test = "0.2"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["serde"]
|
all-features = true
|
||||||
|
|
||||||
[package.metadata.playground]
|
[package.metadata.playground]
|
||||||
features = ["serde"]
|
all-features = true
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "chrono"
|
|
||||||
required-features = ["__internal_bench"]
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "serde"
|
|
||||||
harness = false
|
|
||||||
required-features = ["serde"]
|
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -1,8 +1,6 @@
|
||||||
# this Makefile is mostly for the packaging convenience.
|
# this Makefile is mostly for the packaging convenience.
|
||||||
# casual users should use `cargo` to retrieve the appropriate version of Chrono.
|
# casual users should use `cargo` to retrieve the appropriate version of Chrono.
|
||||||
|
|
||||||
CHANNEL=stable
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all:
|
all:
|
||||||
@echo 'Try `cargo build` instead.'
|
@echo 'Try `cargo build` instead.'
|
||||||
|
@ -22,8 +20,11 @@ README.md: src/lib.rs
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
CHANNEL=$(CHANNEL) ./ci/travis.sh
|
TZ=UTC0 cargo test --features 'serde rustc-serialize bincode' --lib
|
||||||
|
TZ=ACST-9:30 cargo test --features 'serde rustc-serialize bincode' --lib
|
||||||
|
TZ=EST4 cargo test --features 'serde rustc-serialize bincode'
|
||||||
|
|
||||||
.PHONY: doc
|
.PHONY: doc
|
||||||
doc: authors readme
|
doc: authors readme
|
||||||
cargo doc --features 'serde rustc-serialize bincode'
|
cargo doc --features 'serde rustc-serialize bincode'
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ use chrono::prelude::*;
|
||||||
Chrono currently uses
|
Chrono currently uses
|
||||||
the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type
|
the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type
|
||||||
from the `time` crate to represent the magnitude of a time span.
|
from the `time` crate to represent the magnitude of a time span.
|
||||||
Since this has the same name as the newer, standard type for duration,
|
Since this has the same name to the newer, standard type for duration,
|
||||||
the reference will refer this type as `OldDuration`.
|
the reference will refer this type as `OldDuration`.
|
||||||
Note that this is an "accurate" duration represented as seconds and
|
Note that this is an "accurate" duration represented as seconds and
|
||||||
nanoseconds and does not represent "nominal" components such as days or
|
nanoseconds and does not represent "nominal" components such as days or
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
//! Benchmarks for chrono that just depend on std
|
|
||||||
|
|
||||||
extern crate chrono;
|
|
||||||
extern crate criterion;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
|
|
||||||
|
|
||||||
use chrono::prelude::*;
|
|
||||||
use chrono::{Utc, FixedOffset, DateTime, __BenchYearFlags};
|
|
||||||
|
|
||||||
fn bench_datetime_parse_from_rfc2822(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_datetime_parse_from_rfc2822", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let str = black_box("Wed, 18 Feb 2015 23:16:09 +0000");
|
|
||||||
DateTime::parse_from_rfc2822(str).unwrap()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_datetime_parse_from_rfc3339(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_datetime_parse_from_rfc3339", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let str = black_box("2015-02-18T23:59:60.234567+05:00");
|
|
||||||
DateTime::parse_from_rfc3339(str).unwrap()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_datetime_from_str(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_datetime_from_str", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
use std::str::FromStr;
|
|
||||||
let str = black_box("2019-03-30T18:46:57.193Z");
|
|
||||||
DateTime::<Utc>::from_str(str).unwrap()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_datetime_to_rfc2822(c: &mut Criterion) {
|
|
||||||
let pst = FixedOffset::east(8 * 60 * 60);
|
|
||||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
|
|
||||||
c.bench_function("bench_datetime_to_rfc2822", |b| {
|
|
||||||
b.iter(|| black_box(dt).to_rfc2822())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_datetime_to_rfc3339(c: &mut Criterion) {
|
|
||||||
let pst = FixedOffset::east(8 * 60 * 60);
|
|
||||||
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_000);
|
|
||||||
c.bench_function("bench_datetime_to_rfc3339", |b| {
|
|
||||||
b.iter(|| black_box(dt).to_rfc3339())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_year_flags_from_year(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_year_flags_from_year", |b|
|
|
||||||
b.iter(|| {
|
|
||||||
for year in -999i32..1000 {
|
|
||||||
__BenchYearFlags::from_year(year);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
/// Returns the number of multiples of `div` in the range `start..end`.
|
|
||||||
///
|
|
||||||
/// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
|
|
||||||
/// behaviour is defined by the following equation:
|
|
||||||
/// `in_between(start, end, div) == - in_between(end, start, div)`.
|
|
||||||
///
|
|
||||||
/// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `div` is not positive.
|
|
||||||
fn in_between(start: i32, end: i32, div: i32) -> i32 {
|
|
||||||
assert!(div > 0, "in_between: nonpositive div = {}", div);
|
|
||||||
let start = (start.div_euclid(div), start.rem_euclid(div));
|
|
||||||
let end = (end.div_euclid(div), end.rem_euclid(div));
|
|
||||||
// The lowest multiple of `div` greater than or equal to `start`, divided.
|
|
||||||
let start = start.0 + (start.1 != 0) as i32;
|
|
||||||
// The lowest multiple of `div` greater than or equal to `end`, divided.
|
|
||||||
let end = end.0 + (end.1 != 0) as i32;
|
|
||||||
end - start
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alternative implementation to `Datelike::num_days_from_ce`
|
|
||||||
fn num_days_from_ce_alt<Date: Datelike>(date: &Date) -> i32 {
|
|
||||||
let year = date.year();
|
|
||||||
let diff = move |div| in_between(1, year, div);
|
|
||||||
// 365 days a year, one more in leap years. In the gregorian calendar, leap years are all
|
|
||||||
// the multiples of 4 except multiples of 100 but including multiples of 400.
|
|
||||||
date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_num_days_from_ce(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("num_days_from_ce");
|
|
||||||
for year in &[1, 500, 2000, 2019] {
|
|
||||||
let d = NaiveDate::from_ymd(*year, 1, 1);
|
|
||||||
group.bench_with_input(
|
|
||||||
BenchmarkId::new("new", year),
|
|
||||||
&d,
|
|
||||||
|b, y| b.iter(|| num_days_from_ce_alt(y)),
|
|
||||||
);
|
|
||||||
group.bench_with_input(
|
|
||||||
BenchmarkId::new("classic", year),
|
|
||||||
&d,
|
|
||||||
|b, y| b.iter(|| y.num_days_from_ce()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(
|
|
||||||
benches,
|
|
||||||
bench_datetime_parse_from_rfc2822,
|
|
||||||
bench_datetime_parse_from_rfc3339,
|
|
||||||
bench_datetime_from_str,
|
|
||||||
bench_datetime_to_rfc2822,
|
|
||||||
bench_datetime_to_rfc3339,
|
|
||||||
bench_year_flags_from_year,
|
|
||||||
bench_num_days_from_ce,
|
|
||||||
);
|
|
||||||
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,30 +0,0 @@
|
||||||
extern crate chrono;
|
|
||||||
extern crate criterion;
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
|
||||||
|
|
||||||
fn bench_ser_naivedatetime_string(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_ser_naivedatetime_string", |b| {
|
|
||||||
let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
|
|
||||||
b.iter(|| {
|
|
||||||
black_box(serde_json::to_string(&dt)).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_ser_naivedatetime_writer(c: &mut Criterion) {
|
|
||||||
c.bench_function("bench_ser_naivedatetime_writer", |b| {
|
|
||||||
let mut s: Vec<u8> = Vec::with_capacity(20);
|
|
||||||
let dt: NaiveDateTime = "2000-01-01T00:00:00".parse().unwrap();
|
|
||||||
b.iter(|| {
|
|
||||||
let s = &mut s;
|
|
||||||
s.clear();
|
|
||||||
black_box(serde_json::to_writer(s, &dt)).unwrap();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, bench_ser_naivedatetime_writer, bench_ser_naivedatetime_string);
|
|
||||||
criterion_main!(benches);
|
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "core-test"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = [
|
|
||||||
"Kang Seonghoon <public+rust@mearie.org>",
|
|
||||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
|
||||||
]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
chrono = { path = "../..", default-features = false, features = ["serde"] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
alloc = ["chrono/alloc"]
|
|
|
@ -1,7 +0,0 @@
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
use chrono::{TimeZone, Utc};
|
|
||||||
|
|
||||||
pub fn create_time() {
|
|
||||||
let _ = Utc.ymd(2019, 1, 1).and_hms(0, 0, 0);
|
|
||||||
}
|
|
157
ci/travis.sh
157
ci/travis.sh
|
@ -2,50 +2,25 @@
|
||||||
|
|
||||||
# This is the script that's executed by travis, you can run it yourself to run
|
# This is the script that's executed by travis, you can run it yourself to run
|
||||||
# the exact same suite
|
# the exact same suite
|
||||||
#
|
|
||||||
# When running it locally the most important thing to set is the CHANNEL env
|
|
||||||
# var, otherwise it will run tests against every version of rust that it knows
|
|
||||||
# about (nightly, beta, stable, 1.13.0):
|
|
||||||
#
|
|
||||||
# $ CHANNEL=stable ./ci/travis.sh
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
channel() {
|
||||||
main() {
|
if [ -n "${TRAVIS}" ]; then
|
||||||
if [[ -n "$CHANNEL" ]] ; then
|
if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then
|
||||||
if [[ "$CHANNEL" == 1.13.0 ]]; then
|
pwd
|
||||||
banner "Building $CHANNEL"
|
(set -x; cargo "$@")
|
||||||
build_only
|
fi
|
||||||
else
|
elif [ -n "${APPVEYOR}" ]; then
|
||||||
banner "Building/testing $CHANNEL"
|
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
|
||||||
build_and_test
|
pwd
|
||||||
banner "Testing Core $CHANNEL"
|
(set -x; cargo "$@")
|
||||||
build_core_test
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
CHANNEL=nightly
|
pwd
|
||||||
matching_banner "Test $CHANNEL"
|
(set -x; cargo "+${CHANNEL}" "$@")
|
||||||
if [[ "${CLIPPY}" = y ]] ; then
|
|
||||||
run_clippy
|
|
||||||
else
|
|
||||||
build_and_test
|
|
||||||
fi
|
|
||||||
|
|
||||||
CHANNEL=beta
|
|
||||||
matching_banner "Test $CHANNEL"
|
|
||||||
build_and_test
|
|
||||||
|
|
||||||
CHANNEL=stable
|
|
||||||
matching_banner "Test $CHANNEL"
|
|
||||||
build_and_test
|
|
||||||
build_core_test
|
|
||||||
|
|
||||||
CHANNEL=1.13.0
|
|
||||||
matching_banner "Test $CHANNEL"
|
|
||||||
build_only
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,20 +54,14 @@ build_and_test_nonwasm() {
|
||||||
TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize
|
TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize
|
||||||
|
|
||||||
# without default "clock" feature
|
# without default "clock" feature
|
||||||
channel build -v --no-default-features --features std
|
channel build -v --no-default-features
|
||||||
TZ=ACST-9:30 channel test -v --no-default-features --lib
|
TZ=ACST-9:30 channel test -v --no-default-features --lib
|
||||||
channel build -v --no-default-features --features std,rustc-serialize
|
channel build -v --no-default-features --features rustc-serialize
|
||||||
TZ=EST4 channel test -v --no-default-features --features rustc-serialize --lib
|
TZ=EST4 channel test -v --no-default-features --features rustc-serialize --lib
|
||||||
channel build -v --no-default-features --features std,serde
|
channel build -v --no-default-features --features serde
|
||||||
TZ=UTC0 channel test -v --no-default-features --features serde --lib
|
TZ=UTC0 channel test -v --no-default-features --features serde --lib
|
||||||
channel build -v --no-default-features --features std,serde,rustc-serialize
|
channel build -v --no-default-features --features serde,rustc-serialize
|
||||||
TZ=Asia/Katmandu channel test -v --no-default-features --features std,serde,rustc-serialize --lib
|
TZ=Asia/Katmandu channel test -v --no-default-features --features serde,rustc-serialize --lib
|
||||||
|
|
||||||
channel build -v --no-default-features --features 'serde'
|
|
||||||
TZ=UTC0 channel test -v --no-default-features --features 'serde' --lib
|
|
||||||
|
|
||||||
channel build -v --no-default-features --features 'alloc serde'
|
|
||||||
TZ=UTC0 channel test -v --no-default-features --features 'alloc serde' --lib
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build_and_test_wasm() {
|
build_and_test_wasm() {
|
||||||
|
@ -112,16 +81,8 @@ build_only() {
|
||||||
cargo clean
|
cargo clean
|
||||||
channel build -v
|
channel build -v
|
||||||
channel build -v --features rustc-serialize
|
channel build -v --features rustc-serialize
|
||||||
channel build -v --features serde
|
channel build -v --features 'serde bincode'
|
||||||
channel build -v --no-default-features --features std
|
channel build -v --no-default-features
|
||||||
}
|
|
||||||
|
|
||||||
build_core_test() {
|
|
||||||
channel_run rustup target add thumbv6m-none-eabi --toolchain "$CHANNEL"
|
|
||||||
(
|
|
||||||
cd ci/core-test
|
|
||||||
channel build -v --target thumbv6m-none-eabi
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_clippy() {
|
run_clippy() {
|
||||||
|
@ -131,7 +92,7 @@ run_clippy() {
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cargo clippy --features 'serde rustc-serialize' -- -Dclippy
|
cargo clippy --features 'serde bincode rustc-serialize' -- -Dclippy
|
||||||
}
|
}
|
||||||
|
|
||||||
check_readme() {
|
check_readme() {
|
||||||
|
@ -139,70 +100,22 @@ check_readme() {
|
||||||
(set -x; git diff --exit-code -- README.md) ; echo $?
|
(set -x; git diff --exit-code -- README.md) ; echo $?
|
||||||
}
|
}
|
||||||
|
|
||||||
# script helpers
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
node --version
|
||||||
|
|
||||||
banner() {
|
CHANNEL=nightly
|
||||||
echo "======================================================================"
|
if [ "x${CLIPPY}" = xy ] ; then
|
||||||
echo "$*"
|
run_clippy
|
||||||
echo "======================================================================"
|
else
|
||||||
}
|
build_and_test
|
||||||
|
fi
|
||||||
|
|
||||||
underline() {
|
CHANNEL=beta
|
||||||
echo "$*"
|
build_and_test
|
||||||
echo "${*//?/^}"
|
|
||||||
}
|
|
||||||
|
|
||||||
matching_banner() {
|
CHANNEL=stable
|
||||||
if channel_matches || ! is_ci ; then
|
build_and_test
|
||||||
banner "$*"
|
|
||||||
echo_versions
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
echo_versions() {
|
CHANNEL=1.13.0
|
||||||
channel_run rustc --version
|
build_only
|
||||||
channel_run cargo --version
|
|
||||||
node --version
|
|
||||||
}
|
|
||||||
|
|
||||||
channel() {
|
|
||||||
channel_run cargo "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
channel_run() {
|
|
||||||
if channel_matches ; then
|
|
||||||
local the_cmd="$ $*"
|
|
||||||
underline "$the_cmd"
|
|
||||||
"$@"
|
|
||||||
elif ! is_ci ; then
|
|
||||||
local cmd="$1"
|
|
||||||
shift
|
|
||||||
if [[ $cmd == cargo || $cmd == rustc ]] ; then
|
|
||||||
underline "$ $cmd +${CHANNEL} $*"
|
|
||||||
"$cmd" "+${CHANNEL}" "$@"
|
|
||||||
else
|
|
||||||
underline "$ $cmd $*"
|
|
||||||
"$cmd" "$@"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
channel_matches() {
|
|
||||||
if is_ci ; then
|
|
||||||
if [[ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}"
|
|
||||||
|| "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]] ; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
is_ci() {
|
|
||||||
if [[ -n "$TRAVIS" || -n "$APPVEYOR" ]] ; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
20
src/date.rs
20
src/date.rs
|
@ -3,18 +3,16 @@
|
||||||
|
|
||||||
//! ISO 8601 calendar date with time zone.
|
//! ISO 8601 calendar date with time zone.
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
use std::{fmt, hash};
|
||||||
use core::{fmt, hash};
|
use std::cmp::Ordering;
|
||||||
use core::cmp::Ordering;
|
use std::ops::{Add, Sub};
|
||||||
use core::ops::{Add, Sub};
|
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use {Weekday, Datelike};
|
use {Weekday, Datelike};
|
||||||
use offset::{TimeZone, Utc};
|
use offset::{TimeZone, Utc};
|
||||||
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
|
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
|
||||||
use DateTime;
|
use DateTime;
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
use format::{Item, DelayedFormat, StrftimeItems};
|
||||||
use format::{DelayedFormat, Item, StrftimeItems};
|
|
||||||
|
|
||||||
/// ISO 8601 calendar date with time zone.
|
/// ISO 8601 calendar date with time zone.
|
||||||
///
|
///
|
||||||
|
@ -55,7 +53,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||||
/// Makes a new `Date` with given *UTC* date and offset.
|
/// Makes a new `Date` with given *UTC* date and offset.
|
||||||
/// The local date should be constructed via the `TimeZone` trait.
|
/// The local date should be constructed via the `TimeZone` trait.
|
||||||
//
|
//
|
||||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
||||||
Date { date: date, offset: offset }
|
Date { date: date, offset: offset }
|
||||||
|
@ -240,7 +238,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||||
|
|
||||||
/// Returns a view to the naive local date.
|
/// Returns a view to the naive local date.
|
||||||
///
|
///
|
||||||
/// This is technically the same as [`naive_utc`](#method.naive_utc)
|
/// This is technically same to [`naive_utc`](#method.naive_utc)
|
||||||
/// because the offset is restricted to never exceed one day,
|
/// because the offset is restricted to never exceed one day,
|
||||||
/// but provided for the consistency.
|
/// but provided for the consistency.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -257,17 +255,15 @@ fn map_local<Tz: TimeZone, F>(d: &Date<Tz>, mut f: F) -> Option<Date<Tz>>
|
||||||
|
|
||||||
impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
|
impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
|
||||||
/// Formats the date with the specified formatting items.
|
/// Formats the date with the specified formatting items.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> + Clone {
|
||||||
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
|
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the date with the specified format string.
|
/// Formats the date with the specified format string.
|
||||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
self.format_with_items(StrftimeItems::new(fmt))
|
self.format_with_items(StrftimeItems::new(fmt))
|
||||||
|
|
646
src/datetime.rs
646
src/datetime.rs
|
@ -3,29 +3,20 @@
|
||||||
|
|
||||||
//! ISO 8601 date and time with time zone.
|
//! ISO 8601 date and time with time zone.
|
||||||
|
|
||||||
use core::{str, fmt, hash};
|
use std::{str, fmt, hash};
|
||||||
use core::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use core::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::string::ToString;
|
|
||||||
|
|
||||||
use {Weekday, Timelike, Datelike};
|
use {Weekday, Timelike, Datelike};
|
||||||
#[cfg(feature="clock")]
|
#[cfg(feature="clock")]
|
||||||
use offset::Local;
|
use offset::Local;
|
||||||
use offset::{TimeZone, Offset, Utc, FixedOffset};
|
use offset::{TimeZone, Offset, Utc, FixedOffset};
|
||||||
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
||||||
use Date;
|
use Date;
|
||||||
use format::{Item, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use format::DelayedFormat;
|
|
||||||
use core::borrow::Borrow;
|
|
||||||
|
|
||||||
/// Specific formatting options for seconds. This may be extended in the
|
/// Specific formatting options for seconds. This may be extended in the
|
||||||
/// future, so exhaustive matching in external code is not recommended.
|
/// future, so exhaustive matching in external code is not recommended.
|
||||||
|
@ -82,7 +73,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
/// assert_eq!(Utc.timestamp(61, 0), dt);
|
/// assert_eq!(Utc.timestamp(61, 0), dt);
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
//
|
//
|
||||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
|
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
|
||||||
DateTime { datetime: datetime, offset: offset }
|
DateTime { datetime: datetime, offset: offset }
|
||||||
|
@ -137,7 +128,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||||
/// Note that this does reduce the number of years that can be represented
|
/// Note that this does reduce the number of years that can be represented
|
||||||
/// from ~584 Billion to ~584. (If this is a problem, please file
|
/// from ~584 Billion to ~584. (If this is a problem, please file
|
||||||
/// an issue to let me know what domain needs nanosecond precision over
|
/// an issue to let me know what domain needs nanosecond precision over
|
||||||
/// millennia, I'm curious.)
|
/// millenia, I'm curious.)
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -324,21 +315,10 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
|
||||||
impl DateTime<FixedOffset> {
|
impl DateTime<FixedOffset> {
|
||||||
/// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`,
|
/// Parses an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`,
|
||||||
/// then returns a new `DateTime` with a parsed `FixedOffset`.
|
/// then returns a new `DateTime` with a parsed `FixedOffset`.
|
||||||
///
|
|
||||||
/// RFC 2822 is the internet message standard that specifices the
|
|
||||||
/// representation of times in HTTP and email headers.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use chrono::{DateTime, FixedOffset, TimeZone};
|
|
||||||
/// assert_eq!(
|
|
||||||
/// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(),
|
|
||||||
/// FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, ITEMS.iter())?;
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
parsed.to_datetime()
|
parsed.to_datetime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +330,7 @@ impl DateTime<FixedOffset> {
|
||||||
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
|
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, ITEMS.iter())?;
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
parsed.to_datetime()
|
parsed.to_datetime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,24 +356,22 @@ impl DateTime<FixedOffset> {
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
|
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||||
parsed.to_datetime()
|
parsed.to_datetime()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
||||||
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
|
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
pub fn to_rfc2822(&self) -> String {
|
pub fn to_rfc2822(&self) -> String {
|
||||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
||||||
self.format_with_items(ITEMS.iter()).to_string()
|
self.format_with_items(ITEMS.iter().cloned()).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`.
|
/// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
pub fn to_rfc3339(&self) -> String {
|
pub fn to_rfc3339(&self) -> String {
|
||||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
|
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
|
||||||
self.format_with_items(ITEMS.iter()).to_string()
|
self.format_with_items(ITEMS.iter().cloned()).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an RFC 3339 and ISO 8601 date and time string with subseconds
|
/// Return an RFC 3339 and ISO 8601 date and time string with subseconds
|
||||||
|
@ -420,7 +398,6 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
||||||
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true),
|
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||||
/// "2018-01-26T10:30:09+08:00");
|
/// "2018-01-26T10:30:09+08:00");
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String {
|
pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String {
|
||||||
use format::Numeric::*;
|
use format::Numeric::*;
|
||||||
use format::Pad::Zero;
|
use format::Pad::Zero;
|
||||||
|
@ -462,20 +439,19 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
||||||
match ssitem {
|
match ssitem {
|
||||||
None =>
|
None =>
|
||||||
self.format_with_items(
|
self.format_with_items(
|
||||||
PREFIX.iter().chain([tzitem].iter())
|
PREFIX.iter().chain([tzitem].iter()).cloned()
|
||||||
).to_string(),
|
).to_string(),
|
||||||
Some(s) =>
|
Some(s) =>
|
||||||
self.format_with_items(
|
self.format_with_items(
|
||||||
PREFIX.iter().chain([s, tzitem].iter())
|
PREFIX.iter().chain([s, tzitem].iter()).cloned()
|
||||||
).to_string(),
|
).to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the combined date and time with the specified formatting items.
|
/// Formats the combined date and time with the specified formatting items.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> + Clone {
|
||||||
let local = self.naive_local();
|
let local = self.naive_local();
|
||||||
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
|
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
|
||||||
}
|
}
|
||||||
|
@ -483,7 +459,6 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
||||||
/// Formats the combined date and time with the specified format string.
|
/// Formats the combined date and time with the specified format string.
|
||||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
self.format_with_items(StrftimeItems::new(fmt))
|
self.format_with_items(StrftimeItems::new(fmt))
|
||||||
|
@ -575,23 +550,8 @@ impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
|
||||||
impl<Tz: TimeZone> Eq for DateTime<Tz> {
|
impl<Tz: TimeZone> Eq for DateTime<Tz> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Tz: TimeZone, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> {
|
impl<Tz: TimeZone> PartialOrd for DateTime<Tz> {
|
||||||
/// Compare two DateTimes based on their true time, ignoring time zones
|
fn partial_cmp(&self, other: &DateTime<Tz>) -> Option<Ordering> {
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use chrono::prelude::*;
|
|
||||||
///
|
|
||||||
/// let earlier = Utc.ymd(2015, 5, 15).and_hms(2, 0, 0).with_timezone(&FixedOffset::west(1 * 3600));
|
|
||||||
/// let later = Utc.ymd(2015, 5, 15).and_hms(3, 0, 0).with_timezone(&FixedOffset::west(5 * 3600));
|
|
||||||
///
|
|
||||||
/// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00");
|
|
||||||
/// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00");
|
|
||||||
///
|
|
||||||
/// assert!(later > earlier);
|
|
||||||
/// ```
|
|
||||||
fn partial_cmp(&self, other: &DateTime<Tz2>) -> Option<Ordering> {
|
|
||||||
self.datetime.partial_cmp(&other.datetime)
|
self.datetime.partial_cmp(&other.datetime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,6 +603,33 @@ impl<Tz: TimeZone> fmt::Display for DateTime<Tz> where Tz::Offset: fmt::Display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl str::FromStr for DateTime<FixedOffset> {
|
||||||
|
type Err = ParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
|
const ITEMS: &'static [Item<'static>] = &[
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
|
||||||
|
Item::Space(""), Item::Literal("-"),
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
|
||||||
|
Item::Space(""), Item::Literal("-"),
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
|
||||||
|
Item::Space(""), Item::Literal("T"), // XXX shouldn't this be case-insensitive?
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||||
|
Item::Space(""), Item::Literal(":"),
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||||
|
Item::Space(""), Item::Literal(":"),
|
||||||
|
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
|
||||||
|
Item::Fixed(Fixed::Nanosecond),
|
||||||
|
Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ),
|
||||||
|
Item::Space(""),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut parsed = Parsed::new();
|
||||||
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
|
parsed.to_datetime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl str::FromStr for DateTime<Utc> {
|
impl str::FromStr for DateTime<Utc> {
|
||||||
type Err = ParseError;
|
type Err = ParseError;
|
||||||
|
|
||||||
|
@ -660,7 +647,6 @@ impl str::FromStr for DateTime<Local> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl From<SystemTime> for DateTime<Utc> {
|
impl From<SystemTime> for DateTime<Utc> {
|
||||||
fn from(t: SystemTime) -> DateTime<Utc> {
|
fn from(t: SystemTime) -> DateTime<Utc> {
|
||||||
let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
|
let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
|
||||||
|
@ -686,7 +672,6 @@ impl From<SystemTime> for DateTime<Local> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime {
|
impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime {
|
||||||
fn from(dt: DateTime<Tz>) -> SystemTime {
|
fn from(dt: DateTime<Tz>) -> SystemTime {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -714,7 +699,7 @@ fn test_auto_conversion() {
|
||||||
fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed)
|
fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed)
|
||||||
where FUtc: Fn(&DateTime<Utc>) -> Result<String, E>,
|
where FUtc: Fn(&DateTime<Utc>) -> Result<String, E>,
|
||||||
FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>,
|
FFixed: Fn(&DateTime<FixedOffset>) -> Result<String, E>,
|
||||||
E: ::core::fmt::Debug
|
E: ::std::fmt::Debug
|
||||||
{
|
{
|
||||||
assert_eq!(to_string_utc(&Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
assert_eq!(to_string_utc(&Utc.ymd(2014, 7, 24).and_hms(12, 34, 6)).ok(),
|
||||||
Some(r#""2014-07-24T12:34:06Z""#.into()));
|
Some(r#""2014-07-24T12:34:06Z""#.into()));
|
||||||
|
@ -732,7 +717,7 @@ fn test_decodable_json<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
|
||||||
where FUtc: Fn(&str) -> Result<DateTime<Utc>, E>,
|
where FUtc: Fn(&str) -> Result<DateTime<Utc>, E>,
|
||||||
FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>,
|
FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, E>,
|
||||||
FLocal: Fn(&str) -> Result<DateTime<Local>, E>,
|
FLocal: Fn(&str) -> Result<DateTime<Local>, E>,
|
||||||
E: ::core::fmt::Debug
|
E: ::std::fmt::Debug
|
||||||
{
|
{
|
||||||
// should check against the offset as well (the normal DateTime comparison will ignore them)
|
// 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)> {
|
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
|
||||||
|
@ -769,7 +754,7 @@ fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
|
||||||
where FUtc: Fn(&str) -> Result<rustc_serialize::TsSeconds<Utc>, E>,
|
where FUtc: Fn(&str) -> Result<rustc_serialize::TsSeconds<Utc>, E>,
|
||||||
FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, E>,
|
FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, E>,
|
||||||
FLocal: Fn(&str) -> Result<rustc_serialize::TsSeconds<Local>, E>,
|
FLocal: Fn(&str) -> Result<rustc_serialize::TsSeconds<Local>, E>,
|
||||||
E: ::core::fmt::Debug
|
E: ::std::fmt::Debug
|
||||||
{
|
{
|
||||||
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
|
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
|
||||||
dt.as_ref().map(|dt| (dt, dt.offset()))
|
dt.as_ref().map(|dt| (dt, dt.offset()))
|
||||||
|
@ -793,8 +778,8 @@ fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
|
||||||
|
|
||||||
#[cfg(feature = "rustc-serialize")]
|
#[cfg(feature = "rustc-serialize")]
|
||||||
pub mod rustc_serialize {
|
pub mod rustc_serialize {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use core::ops::Deref;
|
use std::ops::Deref;
|
||||||
use super::DateTime;
|
use super::DateTime;
|
||||||
#[cfg(feature="clock")]
|
#[cfg(feature="clock")]
|
||||||
use offset::Local;
|
use offset::Local;
|
||||||
|
@ -807,7 +792,7 @@ pub mod rustc_serialize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lik? function to convert a LocalResult into a serde-ish Result
|
// 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>
|
fn from<T, D>(me: LocalResult<T>, d: &mut D) -> Result<T, D::Error>
|
||||||
where D: Decoder,
|
where D: Decoder,
|
||||||
T: fmt::Display,
|
T: fmt::Display,
|
||||||
|
@ -922,38 +907,25 @@ pub mod rustc_serialize {
|
||||||
/// documented at re-export site
|
/// documented at re-export site
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub mod serde {
|
pub mod serde {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use super::DateTime;
|
use super::DateTime;
|
||||||
#[cfg(feature="clock")]
|
#[cfg(feature="clock")]
|
||||||
use offset::Local;
|
use offset::Local;
|
||||||
use offset::{LocalResult, TimeZone, Utc, FixedOffset};
|
use offset::{LocalResult, TimeZone, Utc, FixedOffset};
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
use {SerdeError, ne_timestamp};
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
// try!-like function to convert a LocalResult into a serde-ish Result
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SecondsTimestampVisitor;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct NanoSecondsTimestampVisitor;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MilliSecondsTimestampVisitor;
|
|
||||||
|
|
||||||
// lik? function to convert a LocalResult into a serde-ish Result
|
|
||||||
fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E>
|
fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E>
|
||||||
where
|
where E: de::Error,
|
||||||
E: de::Error,
|
V: fmt::Display,
|
||||||
V: fmt::Display,
|
T: fmt::Display,
|
||||||
T: fmt::Display,
|
|
||||||
{
|
{
|
||||||
match me {
|
match me {
|
||||||
LocalResult::None => Err(E::custom(
|
LocalResult::None => Err(E::custom(
|
||||||
ne_timestamp(ts))),
|
format!("value is not a legal timestamp: {}", ts))),
|
||||||
LocalResult::Ambiguous(min, max) => Err(E::custom(
|
LocalResult::Ambiguous(min, max) => Err(E::custom(
|
||||||
SerdeError::Ambiguous { timestamp: ts, min: min, max: max })),
|
format!("value is an ambiguous timestamp: {}, could be either of {}, {}",
|
||||||
|
ts, min, max))),
|
||||||
LocalResult::Single(val) => Ok(val)
|
LocalResult::Single(val) => Ok(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -995,13 +967,13 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_nanoseconds {
|
pub mod ts_nanoseconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {DateTime, Utc};
|
use {DateTime, Utc};
|
||||||
use offset::TimeZone;
|
use offset::TimeZone;
|
||||||
|
|
||||||
use super::{serde_from, NanoSecondsTimestampVisitor};
|
use super::serde_from;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
|
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -1072,15 +1044,17 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(NanoSecondsTimestampVisitor)?)
|
Ok(try!(d.deserialize_i64(NanoSecondsTimestampVisitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NanoSecondsTimestampVisitor;
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor {
|
impl<'de> de::Visitor<'de> for NanoSecondsTimestampVisitor {
|
||||||
type Value = DateTime<Utc>;
|
type Value = DateTime<Utc>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
||||||
{
|
{
|
||||||
write!(formatter, "a unix timestamp in nanoseconds")
|
write!(formatter, "a unix timestamp in seconds")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize a timestamp in nanoseconds since the epoch
|
/// Deserialize a timestamp in nanoseconds since the epoch
|
||||||
|
@ -1103,152 +1077,6 @@ pub mod serde {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ser/de to/from optional timestamps in nanoseconds
|
|
||||||
///
|
|
||||||
/// 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_nanoseconds_option;
|
|
||||||
/// #[derive(Deserialize, Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(with = "ts_nanoseconds_option")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<S, serde_json::Error> {
|
|
||||||
/// let time = Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733));
|
|
||||||
/// let my_s = S {
|
|
||||||
/// time: time.clone(),
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let as_string = serde_json::to_string(&my_s)?;
|
|
||||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
|
||||||
/// 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_nanoseconds_option {
|
|
||||||
use core::fmt;
|
|
||||||
use serdelib::{ser, de};
|
|
||||||
|
|
||||||
use {DateTime, Utc};
|
|
||||||
|
|
||||||
use super::{ts_nanoseconds, NanoSecondsTimestampVisitor};
|
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none
|
|
||||||
///
|
|
||||||
/// 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_nanoseconds_option::serialize as to_nano_tsopt;
|
|
||||||
/// #[derive(Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(serialize_with = "to_nano_tsopt")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<String, serde_json::Error> {
|
|
||||||
/// let my_s = S {
|
|
||||||
/// time: Some(Utc.ymd(2018, 5, 17).and_hms_nano(02, 04, 59, 918355733)),
|
|
||||||
/// };
|
|
||||||
/// let as_string = serde_json::to_string(&my_s)?;
|
|
||||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
|
||||||
/// # Ok(as_string)
|
|
||||||
/// # }
|
|
||||||
/// # fn main() { example().unwrap(); }
|
|
||||||
/// ```
|
|
||||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where S: ser::Serializer
|
|
||||||
{
|
|
||||||
match *opt {
|
|
||||||
Some(ref dt) => ts_nanoseconds::serialize(dt, serializer),
|
|
||||||
None => serializer.serialize_none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a `DateTime` from a nanosecond timestamp or none
|
|
||||||
///
|
|
||||||
/// 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_nanoseconds_option::deserialize as from_nano_tsopt;
|
|
||||||
/// #[derive(Deserialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(deserialize_with = "from_nano_tsopt")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<S, serde_json::Error> {
|
|
||||||
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?;
|
|
||||||
/// # Ok(my_s)
|
|
||||||
/// # }
|
|
||||||
/// # fn main() { example().unwrap(); }
|
|
||||||
/// ```
|
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where D: de::Deserializer<'de>
|
|
||||||
{
|
|
||||||
Ok(d.deserialize_option(OptionNanoSecondsTimestampVisitor)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OptionNanoSecondsTimestampVisitor;
|
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor {
|
|
||||||
type Value = Option<DateTime<Utc>>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
|
||||||
{
|
|
||||||
formatter.write_str("a unix timestamp in nanoseconds or none")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where
|
|
||||||
D: de::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
d.deserialize_i64(NanoSecondsTimestampVisitor).map(|val| Some(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
|
|
||||||
where E: de::Error
|
|
||||||
{
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ser/de to/from timestamps in milliseconds
|
/// Ser/de to/from timestamps in milliseconds
|
||||||
///
|
///
|
||||||
/// Intended for use with `serde`s `with` attribute.
|
/// Intended for use with `serde`s `with` attribute.
|
||||||
|
@ -1286,13 +1114,13 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_milliseconds {
|
pub mod ts_milliseconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {DateTime, Utc};
|
use {DateTime, Utc};
|
||||||
use offset::TimeZone;
|
use offset::TimeZone;
|
||||||
|
|
||||||
use super::{serde_from, MilliSecondsTimestampVisitor};
|
use super::serde_from;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
|
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -1363,9 +1191,11 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))?)
|
Ok(try!(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct MilliSecondsTimestampVisitor;
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor {
|
impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor {
|
||||||
type Value = DateTime<Utc>;
|
type Value = DateTime<Utc>;
|
||||||
|
|
||||||
|
@ -1394,152 +1224,6 @@ pub mod serde {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ser/de to/from optional timestamps in milliseconds
|
|
||||||
///
|
|
||||||
/// 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_milliseconds_option;
|
|
||||||
/// #[derive(Deserialize, Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(with = "ts_milliseconds_option")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<S, serde_json::Error> {
|
|
||||||
/// let time = Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918));
|
|
||||||
/// let my_s = S {
|
|
||||||
/// time: time.clone(),
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let as_string = serde_json::to_string(&my_s)?;
|
|
||||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
|
||||||
/// 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_milliseconds_option {
|
|
||||||
use core::fmt;
|
|
||||||
use serdelib::{ser, de};
|
|
||||||
|
|
||||||
use {DateTime, Utc};
|
|
||||||
|
|
||||||
use super::{ts_milliseconds, MilliSecondsTimestampVisitor};
|
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none
|
|
||||||
///
|
|
||||||
/// 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_milliseconds_option::serialize as to_milli_tsopt;
|
|
||||||
/// #[derive(Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(serialize_with = "to_milli_tsopt")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<String, serde_json::Error> {
|
|
||||||
/// let my_s = S {
|
|
||||||
/// time: Some(Utc.ymd(2018, 5, 17).and_hms_milli(02, 04, 59, 918)),
|
|
||||||
/// };
|
|
||||||
/// let as_string = serde_json::to_string(&my_s)?;
|
|
||||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
|
||||||
/// # Ok(as_string)
|
|
||||||
/// # }
|
|
||||||
/// # fn main() { example().unwrap(); }
|
|
||||||
/// ```
|
|
||||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where S: ser::Serializer
|
|
||||||
{
|
|
||||||
match *opt {
|
|
||||||
Some(ref dt) => ts_milliseconds::serialize(dt, serializer),
|
|
||||||
None => serializer.serialize_none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a `DateTime` from a millisecond timestamp or none
|
|
||||||
///
|
|
||||||
/// 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_milliseconds_option::deserialize as from_milli_tsopt;
|
|
||||||
/// #[derive(Deserialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(deserialize_with = "from_milli_tsopt")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<S, serde_json::Error> {
|
|
||||||
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?;
|
|
||||||
/// # Ok(my_s)
|
|
||||||
/// # }
|
|
||||||
/// # fn main() { example().unwrap(); }
|
|
||||||
/// ```
|
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where D: de::Deserializer<'de>
|
|
||||||
{
|
|
||||||
Ok(d.deserialize_option(OptionMilliSecondsTimestampVisitor).map(|opt| opt.map(|dt| dt.with_timezone(&Utc)))?)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OptionMilliSecondsTimestampVisitor;
|
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor {
|
|
||||||
type Value = Option<DateTime<Utc>>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
|
||||||
{
|
|
||||||
formatter.write_str("a unix timestamp in milliseconds or none")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where
|
|
||||||
D: de::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
d.deserialize_i64(MilliSecondsTimestampVisitor).map(|val| Some(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
|
|
||||||
where E: de::Error
|
|
||||||
{
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ser/de to/from timestamps in seconds
|
/// Ser/de to/from timestamps in seconds
|
||||||
///
|
///
|
||||||
/// Intended for use with `serde`'s `with` attribute.
|
/// Intended for use with `serde`'s `with` attribute.
|
||||||
|
@ -1577,13 +1261,13 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_seconds {
|
pub mod ts_seconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {DateTime, Utc};
|
use {DateTime, Utc};
|
||||||
use offset::TimeZone;
|
use offset::TimeZone;
|
||||||
|
|
||||||
use super::{serde_from, SecondsTimestampVisitor};
|
use super::serde_from;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of seconds since the epoch
|
/// Serialize a UTC datetime into an integer number of seconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -1654,9 +1338,11 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(SecondsTimestampVisitor)?)
|
Ok(try!(d.deserialize_i64(SecondsTimestampVisitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SecondsTimestampVisitor;
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for SecondsTimestampVisitor {
|
impl<'de> de::Visitor<'de> for SecondsTimestampVisitor {
|
||||||
type Value = DateTime<Utc>;
|
type Value = DateTime<Utc>;
|
||||||
|
|
||||||
|
@ -1681,152 +1367,6 @@ pub mod serde {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ser/de to/from optional 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_option;
|
|
||||||
/// #[derive(Deserialize, Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(with = "ts_seconds_option")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<S, serde_json::Error> {
|
|
||||||
/// let time = Some(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_option {
|
|
||||||
use core::fmt;
|
|
||||||
use serdelib::{ser, de};
|
|
||||||
|
|
||||||
use {DateTime, Utc};
|
|
||||||
|
|
||||||
use super::{ts_seconds, SecondsTimestampVisitor};
|
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of seconds since the epoch or none
|
|
||||||
///
|
|
||||||
/// 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_option::serialize as to_tsopt;
|
|
||||||
/// #[derive(Serialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(serialize_with = "to_tsopt")]
|
|
||||||
/// time: Option<DateTime<Utc>>
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn example() -> Result<String, serde_json::Error> {
|
|
||||||
/// let my_s = S {
|
|
||||||
/// time: Some(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>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where S: ser::Serializer
|
|
||||||
{
|
|
||||||
match *opt {
|
|
||||||
Some(ref dt) => ts_seconds::serialize(dt, serializer),
|
|
||||||
None => serializer.serialize_none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a `DateTime` from a seconds timestamp or none
|
|
||||||
///
|
|
||||||
/// 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_option::deserialize as from_tsopt;
|
|
||||||
/// #[derive(Deserialize)]
|
|
||||||
/// struct S {
|
|
||||||
/// #[serde(deserialize_with = "from_tsopt")]
|
|
||||||
/// time: Option<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<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where D: de::Deserializer<'de>
|
|
||||||
{
|
|
||||||
Ok(d.deserialize_option(OptionSecondsTimestampVisitor)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OptionSecondsTimestampVisitor;
|
|
||||||
|
|
||||||
impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor {
|
|
||||||
type Value = Option<DateTime<Utc>>;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
|
|
||||||
{
|
|
||||||
formatter.write_str("a unix timestamp in seconds or none")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_some<D>(self, d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
|
||||||
where
|
|
||||||
D: de::Deserializer<'de>,
|
|
||||||
{
|
|
||||||
d.deserialize_i64(SecondsTimestampVisitor).map(|val| Some(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deserialize a timestamp in seconds since the epoch
|
|
||||||
fn visit_none<E>(self) -> Result<Option<DateTime<Utc>>, E>
|
|
||||||
where E: de::Error
|
|
||||||
{
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> {
|
impl<Tz: TimeZone> ser::Serialize for DateTime<Tz> {
|
||||||
/// Serialize into a rfc3339 time string
|
/// Serialize into a rfc3339 time string
|
||||||
///
|
///
|
||||||
|
@ -1863,7 +1403,7 @@ pub mod serde {
|
||||||
fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E>
|
fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
value.parse().map_err(|err: ::format::ParseError| E::custom(err))
|
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2020,9 +1560,6 @@ mod tests {
|
||||||
assert_eq!(d.date(), tz.ymd(2017, 8, 9));
|
assert_eq!(d.date(), tz.ymd(2017, 8, 9));
|
||||||
assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2017, 8, 9));
|
assert_eq!(d.date().naive_local(), NaiveDate::from_ymd(2017, 8, 9));
|
||||||
assert_eq!(d.date().and_time(d.time()), Some(d));
|
assert_eq!(d.date().and_time(d.time()), Some(d));
|
||||||
|
|
||||||
let utc_d = Utc.ymd(2017, 8, 9).and_hms(12, 34, 56);
|
|
||||||
assert!(utc_d < d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2053,8 +1590,6 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"),
|
assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"),
|
||||||
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)));
|
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)));
|
||||||
assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"),
|
|
||||||
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)));
|
|
||||||
assert_eq!(DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"),
|
assert_eq!(DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"),
|
||||||
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)));
|
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms(23, 16, 9)));
|
||||||
assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"),
|
assert_eq!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"),
|
||||||
|
@ -2095,15 +1630,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_from_str() {
|
fn test_datetime_from_str() {
|
||||||
assert_eq!("2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
|
|
||||||
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
|
||||||
assert_eq!("2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
|
|
||||||
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
|
||||||
assert_eq!("2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(),
|
|
||||||
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
|
||||||
assert_eq!("2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(),
|
|
||||||
Ok(Utc.ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
|
||||||
|
|
||||||
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
|
assert_eq!("2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
|
||||||
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
Ok(FixedOffset::east(0).ymd(2015, 2, 18).and_hms_milli(23, 16, 9, 150)));
|
||||||
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
|
assert_eq!("2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
|
||||||
|
@ -2132,25 +1658,6 @@ mod tests {
|
||||||
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_to_string_round_trip() {
|
|
||||||
let dt = Utc.ymd(2000, 1, 1).and_hms(0, 0, 0);
|
|
||||||
let _dt: DateTime<Utc> = dt.to_string().parse().unwrap();
|
|
||||||
|
|
||||||
let ndt_fixed = dt.with_timezone(&FixedOffset::east(3600));
|
|
||||||
let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap();
|
|
||||||
|
|
||||||
let ndt_fixed = dt.with_timezone(&FixedOffset::east(0));
|
|
||||||
let _dt: DateTime<FixedOffset> = ndt_fixed.to_string().parse().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(feature="clock")]
|
|
||||||
fn test_to_string_round_trip_with_local() {
|
|
||||||
let ndt = Local::now();
|
|
||||||
let _dt: DateTime<FixedOffset> = ndt.to_string().parse().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature="clock")]
|
#[cfg(feature="clock")]
|
||||||
fn test_datetime_format_with_local() {
|
fn test_datetime_format_with_local() {
|
||||||
|
@ -2246,5 +1753,4 @@ mod tests {
|
||||||
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
|
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
|
||||||
assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd));
|
assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,31 +17,20 @@
|
||||||
|
|
||||||
#![allow(ellipsis_inclusive_range_patterns)]
|
#![allow(ellipsis_inclusive_range_patterns)]
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
use std::fmt;
|
||||||
use core::fmt;
|
use std::str::FromStr;
|
||||||
use core::str::FromStr;
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
use {Datelike, Timelike, Weekday, ParseWeekdayError};
|
||||||
use {Datelike, Timelike};
|
|
||||||
use {Weekday, ParseWeekdayError};
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use div::{div_floor, mod_floor};
|
use div::{div_floor, mod_floor};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use offset::{Offset, FixedOffset};
|
use offset::{Offset, FixedOffset};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use naive::{NaiveDate, NaiveTime};
|
use naive::{NaiveDate, NaiveTime};
|
||||||
|
|
||||||
pub use self::strftime::StrftimeItems;
|
pub use self::strftime::StrftimeItems;
|
||||||
pub use self::parsed::Parsed;
|
pub use self::parsed::Parsed;
|
||||||
pub use self::parse::parse;
|
pub use self::parse::parse;
|
||||||
|
|
||||||
/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below.
|
/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
enum Void {}
|
enum Void {}
|
||||||
|
|
||||||
|
@ -66,7 +55,7 @@ pub enum Pad {
|
||||||
///
|
///
|
||||||
/// The **parsing width** is the maximal width to be scanned.
|
/// The **parsing width** is the maximal width to be scanned.
|
||||||
/// The parser only tries to consume from one to given number of digits (greedily).
|
/// The parser only tries to consume from one to given number of digits (greedily).
|
||||||
/// It also trims the preceding whitespace if any.
|
/// It also trims the preceding whitespaces if any.
|
||||||
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
/// It cannot parse the negative number, so some date and time cannot be formatted then
|
||||||
/// parsed with the same formatting items.
|
/// parsed with the same formatting items.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
@ -184,11 +173,11 @@ pub enum Fixed {
|
||||||
/// May print nothing, 3, 6 or 9 digits according to the available accuracy.
|
/// May print nothing, 3, 6 or 9 digits according to the available accuracy.
|
||||||
/// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond).
|
/// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond).
|
||||||
Nanosecond,
|
Nanosecond,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
|
||||||
Nanosecond3,
|
Nanosecond3,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
|
||||||
Nanosecond6,
|
Nanosecond6,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
|
||||||
Nanosecond9,
|
Nanosecond9,
|
||||||
/// Timezone name.
|
/// Timezone name.
|
||||||
///
|
///
|
||||||
|
@ -196,21 +185,21 @@ pub enum Fixed {
|
||||||
TimezoneName,
|
TimezoneName,
|
||||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`).
|
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`).
|
||||||
///
|
///
|
||||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
|
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces.
|
||||||
/// The offset is limited from `-24:00` to `+24:00`,
|
/// The offset is limited from `-24:00` to `+24:00`,
|
||||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||||
TimezoneOffsetColon,
|
TimezoneOffsetColon,
|
||||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||||
///
|
///
|
||||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
|
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces,
|
||||||
/// and `Z` can be either in upper case or in lower case.
|
/// and `Z` can be either in upper case or in lower case.
|
||||||
/// The offset is limited from `-24:00` to `+24:00`,
|
/// The offset is limited from `-24:00` to `+24:00`,
|
||||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||||
TimezoneOffsetColonZ,
|
TimezoneOffsetColonZ,
|
||||||
/// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
||||||
/// Parsing allows an optional colon.
|
/// Parsing allows an optional colon.
|
||||||
TimezoneOffset,
|
TimezoneOffset,
|
||||||
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
|
/// Same to [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
|
||||||
/// Parsing allows an optional colon.
|
/// Parsing allows an optional colon.
|
||||||
TimezoneOffsetZ,
|
TimezoneOffsetZ,
|
||||||
/// RFC 2822 date and time syntax. Commonly used for email and MIME date and time.
|
/// RFC 2822 date and time syntax. Commonly used for email and MIME date and time.
|
||||||
|
@ -242,11 +231,11 @@ enum InternalInternal {
|
||||||
///
|
///
|
||||||
/// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
|
/// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
|
||||||
TimezoneOffsetPermissive,
|
TimezoneOffsetPermissive,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
|
||||||
Nanosecond3NoDot,
|
Nanosecond3NoDot,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
|
||||||
Nanosecond6NoDot,
|
Nanosecond6NoDot,
|
||||||
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
|
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
|
||||||
Nanosecond9NoDot,
|
Nanosecond9NoDot,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,13 +244,11 @@ enum InternalInternal {
|
||||||
pub enum Item<'a> {
|
pub enum Item<'a> {
|
||||||
/// A literally printed and parsed text.
|
/// A literally printed and parsed text.
|
||||||
Literal(&'a str),
|
Literal(&'a str),
|
||||||
/// Same as `Literal` but with the string owned by the item.
|
/// Same to `Literal` but with the string owned by the item.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
OwnedLiteral(Box<str>),
|
OwnedLiteral(Box<str>),
|
||||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||||
Space(&'a str),
|
Space(&'a str),
|
||||||
/// Same as `Space` but with the string owned by the item.
|
/// Same to `Space` but with the string owned by the item.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
OwnedSpace(Box<str>),
|
OwnedSpace(Box<str>),
|
||||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||||
/// the parser simply ignores any padded whitespace and zeroes.
|
/// the parser simply ignores any padded whitespace and zeroes.
|
||||||
|
@ -284,7 +271,6 @@ macro_rules! internal_fix { ($x:ident) => (Item::Fixed(Fixed::Internal(InternalF
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
pub struct ParseError(ParseErrorKind);
|
pub struct ParseError(ParseErrorKind);
|
||||||
|
|
||||||
/// The category of parse error
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
enum ParseErrorKind {
|
enum ParseErrorKind {
|
||||||
/// Given field is out of permitted range.
|
/// Given field is out of permitted range.
|
||||||
|
@ -316,28 +302,26 @@ enum ParseErrorKind {
|
||||||
BadFormat,
|
BadFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `Result<T, ParseError>`.
|
/// Same to `Result<T, ParseError>`.
|
||||||
pub type ParseResult<T> = Result<T, ParseError>;
|
pub type ParseResult<T> = Result<T, ParseError>;
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.0 {
|
self.description().fmt(f)
|
||||||
ParseErrorKind::OutOfRange => write!(f, "input is out of range"),
|
|
||||||
ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"),
|
|
||||||
ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"),
|
|
||||||
ParseErrorKind::Invalid => write!(f, "input contains invalid characters"),
|
|
||||||
ParseErrorKind::TooShort => write!(f, "premature end of input"),
|
|
||||||
ParseErrorKind::TooLong => write!(f, "trailing input"),
|
|
||||||
ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl Error for ParseError {
|
impl Error for ParseError {
|
||||||
#[allow(deprecated)]
|
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
"parser error, see to_string() for details"
|
match self.0 {
|
||||||
|
ParseErrorKind::OutOfRange => "input is out of range",
|
||||||
|
ParseErrorKind::Impossible => "no possible date and time matching input",
|
||||||
|
ParseErrorKind::NotEnough => "input is not enough for unique date and time",
|
||||||
|
ParseErrorKind::Invalid => "input contains invalid characters",
|
||||||
|
ParseErrorKind::TooShort => "premature end of input",
|
||||||
|
ParseErrorKind::TooLong => "trailing input",
|
||||||
|
ParseErrorKind::BadFormat => "bad or unsupported format string",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,15 +336,14 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
||||||
|
|
||||||
/// Tries to format given arguments with given formatting items.
|
/// Tries to format given arguments with given formatting items.
|
||||||
/// Internally used by `DelayedFormat`.
|
/// Internally used by `DelayedFormat`.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
pub fn format<'a, I>(
|
||||||
pub fn format<'a, I, B>(
|
|
||||||
w: &mut fmt::Formatter,
|
w: &mut fmt::Formatter,
|
||||||
date: Option<&NaiveDate>,
|
date: Option<&NaiveDate>,
|
||||||
time: Option<&NaiveTime>,
|
time: Option<&NaiveTime>,
|
||||||
off: Option<&(String, FixedOffset)>,
|
off: Option<&(String, FixedOffset)>,
|
||||||
items: I,
|
items: I,
|
||||||
) -> fmt::Result
|
) -> fmt::Result
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>
|
where I: Iterator<Item=Item<'a>>
|
||||||
{
|
{
|
||||||
// full and abbreviated month and weekday names
|
// full and abbreviated month and weekday names
|
||||||
static SHORT_MONTHS: [&'static str; 12] =
|
static SHORT_MONTHS: [&'static str; 12] =
|
||||||
|
@ -373,16 +356,15 @@ pub fn format<'a, I, B>(
|
||||||
static LONG_WEEKDAYS: [&'static str; 7] =
|
static LONG_WEEKDAYS: [&'static str; 7] =
|
||||||
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||||
|
|
||||||
use core::fmt::Write;
|
use std::fmt::Write;
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
match item.borrow() {
|
match item {
|
||||||
&Item::Literal(s) | &Item::Space(s) => result.push_str(s),
|
Item::Literal(s) | Item::Space(s) => result.push_str(s),
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
|
||||||
&Item::OwnedLiteral(ref s) | &Item::OwnedSpace(ref s) => result.push_str(s),
|
|
||||||
|
|
||||||
&Item::Numeric(ref spec, ref pad) => {
|
Item::Numeric(spec, pad) => {
|
||||||
use self::Numeric::*;
|
use self::Numeric::*;
|
||||||
|
|
||||||
let week_from_sun = |d: &NaiveDate|
|
let week_from_sun = |d: &NaiveDate|
|
||||||
|
@ -391,31 +373,31 @@ pub fn format<'a, I, B>(
|
||||||
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7;
|
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7;
|
||||||
|
|
||||||
let (width, v) = match spec {
|
let (width, v) = match spec {
|
||||||
&Year => (4, date.map(|d| i64::from(d.year()))),
|
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||||
&YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
|
YearDiv100 => (2, date.map(|d| div_floor(i64::from(d.year()), 100))),
|
||||||
&YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
|
YearMod100 => (2, date.map(|d| mod_floor(i64::from(d.year()), 100))),
|
||||||
&IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
|
IsoYear => (4, date.map(|d| i64::from(d.iso_week().year()))),
|
||||||
&IsoYearDiv100 => (2, date.map(|d| div_floor(
|
IsoYearDiv100 => (2, date.map(|d| div_floor(
|
||||||
i64::from(d.iso_week().year()), 100))),
|
i64::from(d.iso_week().year()), 100))),
|
||||||
&IsoYearMod100 => (2, date.map(|d| mod_floor(
|
IsoYearMod100 => (2, date.map(|d| mod_floor(
|
||||||
i64::from(d.iso_week().year()), 100))),
|
i64::from(d.iso_week().year()), 100))),
|
||||||
&Month => (2, date.map(|d| i64::from(d.month()))),
|
Month => (2, date.map(|d| i64::from(d.month()))),
|
||||||
&Day => (2, date.map(|d| i64::from(d.day()))),
|
Day => (2, date.map(|d| i64::from(d.day()))),
|
||||||
&WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||||
&WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||||
&IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||||
&NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
||||||
.num_days_from_sunday()))),
|
.num_days_from_sunday()))),
|
||||||
&WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday()
|
WeekdayFromMon => (1, date.map(|d| i64::from(d.weekday()
|
||||||
.number_from_monday()))),
|
.number_from_monday()))),
|
||||||
&Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||||
&Hour => (2, time.map(|t| i64::from(t.hour()))),
|
Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||||
&Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||||
&Minute => (2, time.map(|t| i64::from(t.minute()))),
|
Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||||
&Second => (2, time.map(|t| i64::from(t.second() +
|
Second => (2, time.map(|t| i64::from(t.second() +
|
||||||
t.nanosecond() / 1_000_000_000))),
|
t.nanosecond() / 1_000_000_000))),
|
||||||
&Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
||||||
&Timestamp => (1, match (date, time, off) {
|
Timestamp => (1, match (date, time, off) {
|
||||||
(Some(d), Some(t), None) =>
|
(Some(d), Some(t), None) =>
|
||||||
Some(d.and_time(*t).timestamp()),
|
Some(d.and_time(*t).timestamp()),
|
||||||
(Some(d), Some(t), Some(&(_, off))) =>
|
(Some(d), Some(t), Some(&(_, off))) =>
|
||||||
|
@ -424,31 +406,33 @@ pub fn format<'a, I, B>(
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// for the future expansion
|
// for the future expansion
|
||||||
&Internal(ref int) => match int._dummy {},
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if let Some(v) = v {
|
if let Some(v) = v {
|
||||||
if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
|
try!(
|
||||||
// non-four-digit years require an explicit sign as per ISO 8601
|
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10_000) {
|
||||||
match pad {
|
// non-four-digit years require an explicit sign as per ISO 8601
|
||||||
&Pad::None => write!(result, "{:+}", v),
|
match pad {
|
||||||
&Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
Pad::None => write!(result, "{:+}", v),
|
||||||
&Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||||
|
Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match pad {
|
||||||
|
Pad::None => write!(result, "{}", v),
|
||||||
|
Pad::Zero => write!(result, "{:01$}", v, width),
|
||||||
|
Pad::Space => write!(result, "{:1$}", v, width),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
)
|
||||||
match pad {
|
|
||||||
&Pad::None => write!(result, "{}", v),
|
|
||||||
&Pad::Zero => write!(result, "{:01$}", v, width),
|
|
||||||
&Pad::Space => write!(result, "{:1$}", v, width),
|
|
||||||
}
|
|
||||||
}?
|
|
||||||
} else {
|
} else {
|
||||||
return Err(fmt::Error) // insufficient arguments for given format
|
return Err(fmt::Error) // insufficient arguments for given format
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
&Item::Fixed(ref spec) => {
|
Item::Fixed(spec) => {
|
||||||
use self::Fixed::*;
|
use self::Fixed::*;
|
||||||
|
|
||||||
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
|
||||||
|
@ -474,41 +458,41 @@ pub fn format<'a, I, B>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = match spec {
|
let ret = match spec {
|
||||||
&ShortMonthName =>
|
ShortMonthName =>
|
||||||
date.map(|d| {
|
date.map(|d| {
|
||||||
result.push_str(SHORT_MONTHS[d.month0() as usize]);
|
result.push_str(SHORT_MONTHS[d.month0() as usize]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&LongMonthName =>
|
LongMonthName =>
|
||||||
date.map(|d| {
|
date.map(|d| {
|
||||||
result.push_str(LONG_MONTHS[d.month0() as usize]);
|
result.push_str(LONG_MONTHS[d.month0() as usize]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&ShortWeekdayName =>
|
ShortWeekdayName =>
|
||||||
date.map(|d| {
|
date.map(|d| {
|
||||||
result.push_str(
|
result.push_str(
|
||||||
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&LongWeekdayName =>
|
LongWeekdayName =>
|
||||||
date.map(|d| {
|
date.map(|d| {
|
||||||
result.push_str(
|
result.push_str(
|
||||||
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&LowerAmPm =>
|
LowerAmPm =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
result.push_str(if t.hour12().0 {"pm"} else {"am"});
|
result.push_str(if t.hour12().0 {"pm"} else {"am"});
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&UpperAmPm =>
|
UpperAmPm =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
result.push_str(if t.hour12().0 {"PM"} else {"AM"});
|
result.push_str(if t.hour12().0 {"PM"} else {"AM"});
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&Nanosecond =>
|
Nanosecond =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
if nano == 0 {
|
if nano == 0 {
|
||||||
|
@ -521,70 +505,70 @@ pub fn format<'a, I, B>(
|
||||||
write!(result, ".{:09}", nano)
|
write!(result, ".{:09}", nano)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
&Nanosecond3 =>
|
Nanosecond3 =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, ".{:03}", nano / 1_000_000)
|
write!(result, ".{:03}", nano / 1_000_000)
|
||||||
}),
|
}),
|
||||||
&Nanosecond6 =>
|
Nanosecond6 =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, ".{:06}", nano / 1_000)
|
write!(result, ".{:06}", nano / 1_000)
|
||||||
}),
|
}),
|
||||||
&Nanosecond9 =>
|
Nanosecond9 =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, ".{:09}", nano)
|
write!(result, ".{:09}", nano)
|
||||||
}),
|
}),
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, "{:03}", nano / 1_000_000)
|
write!(result, "{:03}", nano / 1_000_000)
|
||||||
}),
|
}),
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, "{:06}", nano / 1_000)
|
write!(result, "{:06}", nano / 1_000)
|
||||||
}),
|
}),
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
||||||
time.map(|t| {
|
time.map(|t| {
|
||||||
let nano = t.nanosecond() % 1_000_000_000;
|
let nano = t.nanosecond() % 1_000_000_000;
|
||||||
write!(result, "{:09}", nano)
|
write!(result, "{:09}", nano)
|
||||||
}),
|
}),
|
||||||
&TimezoneName =>
|
TimezoneName =>
|
||||||
off.map(|&(ref name, _)| {
|
off.map(|&(ref name, _)| {
|
||||||
result.push_str(name);
|
result.push_str(name);
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
&TimezoneOffsetColon =>
|
TimezoneOffsetColon =>
|
||||||
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, true)),
|
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, true)),
|
||||||
&TimezoneOffsetColonZ =>
|
TimezoneOffsetColonZ =>
|
||||||
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, true)),
|
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, true)),
|
||||||
&TimezoneOffset =>
|
TimezoneOffset =>
|
||||||
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, false)),
|
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, false, false)),
|
||||||
&TimezoneOffsetZ =>
|
TimezoneOffsetZ =>
|
||||||
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, false)),
|
off.map(|&(_, off)| write_local_minus_utc(&mut result, off, true, false)),
|
||||||
&Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
||||||
panic!("Do not try to write %#z it is undefined"),
|
panic!("Do not try to write %#z it is undefined"),
|
||||||
&RFC2822 => // same as `%a, %e %b %Y %H:%M:%S %z`
|
RFC2822 => // same to `%a, %e %b %Y %H:%M:%S %z`
|
||||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||||
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
||||||
write!(
|
try!(write!(
|
||||||
result,
|
result,
|
||||||
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
|
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
|
||||||
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
|
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
|
||||||
d.day(), SHORT_MONTHS[d.month0() as usize], d.year(),
|
d.day(), SHORT_MONTHS[d.month0() as usize], d.year(),
|
||||||
t.hour(), t.minute(), sec
|
t.hour(), t.minute(), sec
|
||||||
)?;
|
));
|
||||||
Some(write_local_minus_utc(&mut result, off, false, false))
|
Some(write_local_minus_utc(&mut result, off, false, false))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
&RFC3339 => // same as `%Y-%m-%dT%H:%M:%S%.f%:z`
|
RFC3339 => // same to `%Y-%m-%dT%H:%M:%S%.f%:z`
|
||||||
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
|
||||||
// reuse `Debug` impls which already print ISO 8601 format.
|
// reuse `Debug` impls which already print ISO 8601 format.
|
||||||
// this is faster in this way.
|
// this is faster in this way.
|
||||||
write!(result, "{:?}T{:?}", d, t)?;
|
try!(write!(result, "{:?}T{:?}", d, t));
|
||||||
Some(write_local_minus_utc(&mut result, off, false, true))
|
Some(write_local_minus_utc(&mut result, off, false, true))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -592,12 +576,12 @@ pub fn format<'a, I, B>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match ret {
|
match ret {
|
||||||
Some(ret) => ret?,
|
Some(ret) => try!(ret),
|
||||||
None => return Err(fmt::Error), // insufficient arguments for given format
|
None => return Err(fmt::Error), // insufficient arguments for given format
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
&Item::Error => return Err(fmt::Error),
|
Item::Error => return Err(fmt::Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +598,6 @@ pub mod strftime;
|
||||||
|
|
||||||
/// A *temporary* object which can be used as an argument to `format!` or others.
|
/// A *temporary* object which can be used as an argument to `format!` or others.
|
||||||
/// This is normally constructed via `format` methods of each date and time type.
|
/// This is normally constructed via `format` methods of each date and time type.
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DelayedFormat<I> {
|
pub struct DelayedFormat<I> {
|
||||||
/// The date view, if any.
|
/// The date view, if any.
|
||||||
|
@ -627,8 +610,7 @@ pub struct DelayedFormat<I> {
|
||||||
items: I,
|
items: I,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
|
||||||
impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
|
||||||
/// Makes a new `DelayedFormat` value out of local date and time.
|
/// Makes a new `DelayedFormat` value out of local date and time.
|
||||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||||
DelayedFormat { date: date, time: time, off: None, items: items }
|
DelayedFormat { date: date, time: time, off: None, items: items }
|
||||||
|
@ -643,8 +625,7 @@ impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for DelayedFormat<I> {
|
||||||
impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
|
format(f, self.date.as_ref(), self.time.as_ref(), self.off.as_ref(), self.items.clone())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,13 @@
|
||||||
|
|
||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use core::borrow::Borrow;
|
use std::usize;
|
||||||
use core::usize;
|
|
||||||
use core::str;
|
use Weekday;
|
||||||
|
|
||||||
use {DateTime, FixedOffset, Weekday};
|
|
||||||
use super::scan;
|
use super::scan;
|
||||||
use super::{Parsed, Numeric, Pad, Fixed, Item, InternalFixed, InternalInternal};
|
use super::{Parsed, ParseResult, Item, InternalFixed, InternalInternal};
|
||||||
use super::{ParseResult, ParseError, ParseErrorKind};
|
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT};
|
||||||
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT, NOT_ENOUGH};
|
|
||||||
|
|
||||||
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
|
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
|
||||||
p.set_weekday(match v {
|
p.set_weekday(match v {
|
||||||
|
@ -34,7 +32,7 @@ fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()
|
||||||
|
|
||||||
fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
||||||
macro_rules! try_consume {
|
macro_rules! try_consume {
|
||||||
($e:expr) => ({ let (s_, v) = $e?; s = s_; v })
|
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
|
||||||
}
|
}
|
||||||
|
|
||||||
// an adapted RFC 2822 syntax from Section 3.3 and 4.3:
|
// an adapted RFC 2822 syntax from Section 3.3 and 4.3:
|
||||||
|
@ -54,10 +52,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
// minute = *S 2DIGIT *S
|
// minute = *S 2DIGIT *S
|
||||||
// second = *S 2DIGIT *S
|
// second = *S 2DIGIT *S
|
||||||
// zone = ( "+" / "-" ) 4DIGIT /
|
// zone = ( "+" / "-" ) 4DIGIT /
|
||||||
// "UT" / "GMT" / ; same as +0000
|
// "UT" / "GMT" / ; same to +0000
|
||||||
// "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
|
// "EST" / "CST" / "MST" / "PST" / ; same to -0500 to -0800
|
||||||
// "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
|
// "EDT" / "CDT" / "MDT" / "PDT" / ; same to -0400 to -0700
|
||||||
// 1*(%d65-90 / %d97-122) ; same as -0000
|
// 1*(%d65-90 / %d97-122) ; same to -0000
|
||||||
//
|
//
|
||||||
// some notes:
|
// some notes:
|
||||||
//
|
//
|
||||||
|
@ -74,6 +72,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
// by adding 1900. note that four-or-more-digit years less than 1000
|
// by adding 1900. note that four-or-more-digit years less than 1000
|
||||||
// are *never* affected by this rule.
|
// are *never* affected by this rule.
|
||||||
//
|
//
|
||||||
|
// - zone of `-0000` and any unrecognized legacy time zones (including
|
||||||
|
// *every* one-letter military time zones) are considered "missing",
|
||||||
|
// in such that we don't actually know what time zone is being used.
|
||||||
|
//
|
||||||
// - mismatching day-of-week is always an error, which is consistent to
|
// - mismatching day-of-week is always an error, which is consistent to
|
||||||
// Chrono's own rules.
|
// Chrono's own rules.
|
||||||
//
|
//
|
||||||
|
@ -87,14 +89,14 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
if let Ok((s_, weekday)) = scan::short_weekday(s) {
|
if let Ok((s_, weekday)) = scan::short_weekday(s) {
|
||||||
if !s_.starts_with(',') { return Err(INVALID); }
|
if !s_.starts_with(',') { return Err(INVALID); }
|
||||||
s = &s_[1..];
|
s = &s_[1..];
|
||||||
parsed.set_weekday(weekday)?;
|
try!(parsed.set_weekday(weekday));
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
|
try!(parsed.set_day(try_consume!(scan::number(s, 1, 2))));
|
||||||
s = scan::space(s)?; // mandatory
|
s = try!(scan::space(s)); // mandatory
|
||||||
parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
|
try!(parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s)))));
|
||||||
s = scan::space(s)?; // mandatory
|
s = try!(scan::space(s)); // mandatory
|
||||||
|
|
||||||
// distinguish two- and three-digit years from four-digit years
|
// distinguish two- and three-digit years from four-digit years
|
||||||
let prevlen = s.len();
|
let prevlen = s.len();
|
||||||
|
@ -106,20 +108,20 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
(3, _) => { year += 1900; } // 112 -> 2012, 009 -> 1909
|
(3, _) => { year += 1900; } // 112 -> 2012, 009 -> 1909
|
||||||
(_, _) => {} // 1987 -> 1987, 0654 -> 0654
|
(_, _) => {} // 1987 -> 1987, 0654 -> 0654
|
||||||
}
|
}
|
||||||
parsed.set_year(year)?;
|
try!(parsed.set_year(year));
|
||||||
|
|
||||||
s = scan::space(s)?; // mandatory
|
s = try!(scan::space(s)); // mandatory
|
||||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_hour(try_consume!(scan::number(s, 2, 2))));
|
||||||
s = scan::char(s.trim_left(), b':')?.trim_left(); // *S ":" *S
|
s = try!(scan::char(s.trim_left(), b':')).trim_left(); // *S ":" *S
|
||||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_minute(try_consume!(scan::number(s, 2, 2))));
|
||||||
if let Ok(s_) = scan::char(s.trim_left(), b':') { // [ ":" *S 2DIGIT ]
|
if let Ok(s_) = scan::char(s.trim_left(), b':') { // [ ":" *S 2DIGIT ]
|
||||||
parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
|
try!(parsed.set_second(try_consume!(scan::number(s_, 2, 2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
s = scan::space(s)?; // mandatory
|
s = try!(scan::space(s)); // mandatory
|
||||||
if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) {
|
if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) {
|
||||||
// only set the offset when it is definitely known (i.e. not `-0000`)
|
// only set the offset when it is definitely known (i.e. not `-0000`)
|
||||||
parsed.set_offset(i64::from(offset))?;
|
try!(parsed.set_offset(i64::from(offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((s, ()))
|
Ok((s, ()))
|
||||||
|
@ -127,7 +129,7 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
|
|
||||||
fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> {
|
||||||
macro_rules! try_consume {
|
macro_rules! try_consume {
|
||||||
($e:expr) => ({ let (s_, v) = $e?; s = s_; v })
|
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
|
||||||
}
|
}
|
||||||
|
|
||||||
// an adapted RFC 3339 syntax from Section 5.6:
|
// an adapted RFC 3339 syntax from Section 5.6:
|
||||||
|
@ -157,11 +159,11 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
// note that this restriction is unique to RFC 3339 and not ISO 8601.
|
// note that this restriction is unique to RFC 3339 and not ISO 8601.
|
||||||
// since this is not a typical Chrono behavior, we check it earlier.
|
// since this is not a typical Chrono behavior, we check it earlier.
|
||||||
|
|
||||||
parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
|
try!(parsed.set_year(try_consume!(scan::number(s, 4, 4))));
|
||||||
s = scan::char(s, b'-')?;
|
s = try!(scan::char(s, b'-'));
|
||||||
parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_month(try_consume!(scan::number(s, 2, 2))));
|
||||||
s = scan::char(s, b'-')?;
|
s = try!(scan::char(s, b'-'));
|
||||||
parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_day(try_consume!(scan::number(s, 2, 2))));
|
||||||
|
|
||||||
s = match s.as_bytes().first() {
|
s = match s.as_bytes().first() {
|
||||||
Some(&b't') | Some(&b'T') => &s[1..],
|
Some(&b't') | Some(&b'T') => &s[1..],
|
||||||
|
@ -169,19 +171,19 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
None => return Err(TOO_SHORT),
|
None => return Err(TOO_SHORT),
|
||||||
};
|
};
|
||||||
|
|
||||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_hour(try_consume!(scan::number(s, 2, 2))));
|
||||||
s = scan::char(s, b':')?;
|
s = try!(scan::char(s, b':'));
|
||||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_minute(try_consume!(scan::number(s, 2, 2))));
|
||||||
s = scan::char(s, b':')?;
|
s = try!(scan::char(s, b':'));
|
||||||
parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
|
try!(parsed.set_second(try_consume!(scan::number(s, 2, 2))));
|
||||||
if s.starts_with('.') {
|
if s.starts_with('.') {
|
||||||
let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
|
let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
|
||||||
parsed.set_nanosecond(nanosecond)?;
|
try!(parsed.set_nanosecond(nanosecond));
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':')));
|
let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':')));
|
||||||
if offset <= -86_400 || offset >= 86_400 { return Err(OUT_OF_RANGE); }
|
if offset <= -86_400 || offset >= 86_400 { return Err(OUT_OF_RANGE); }
|
||||||
parsed.set_offset(i64::from(offset))?;
|
try!(parsed.set_offset(i64::from(offset)));
|
||||||
|
|
||||||
Ok((s, ()))
|
Ok((s, ()))
|
||||||
}
|
}
|
||||||
|
@ -202,86 +204,65 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
||||||
/// so one can prepend any number of whitespace then any number of zeroes before numbers.
|
/// so one can prepend any number of whitespace then any number of zeroes before numbers.
|
||||||
///
|
///
|
||||||
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
|
/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`.
|
||||||
pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()>
|
pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()>
|
||||||
where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> {
|
||||||
parse_internal(parsed, s, items).map(|_| ()).map_err(|(_s, e)| e)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_internal<'a, 'b, I, B>(
|
|
||||||
parsed: &mut Parsed, mut s: &'b str, items: I
|
|
||||||
) -> Result<&'b str, (&'b str, ParseError)>
|
|
||||||
where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
|
|
||||||
macro_rules! try_consume {
|
macro_rules! try_consume {
|
||||||
($e:expr) => ({
|
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
|
||||||
match $e {
|
|
||||||
Ok((s_, v)) => {
|
|
||||||
s = s_;
|
|
||||||
v
|
|
||||||
}
|
|
||||||
Err(e) => return Err((s, e))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
match item.borrow() {
|
match item {
|
||||||
&Item::Literal(prefix) => {
|
Item::Literal(prefix) => {
|
||||||
if s.len() < prefix.len() { return Err((s, TOO_SHORT)); }
|
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||||
if !s.starts_with(prefix) { return Err((s, INVALID)); }
|
if !s.starts_with(prefix) { return Err(INVALID); }
|
||||||
s = &s[prefix.len()..];
|
s = &s[prefix.len()..];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
Item::OwnedLiteral(ref prefix) => {
|
||||||
&Item::OwnedLiteral(ref prefix) => {
|
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||||
if s.len() < prefix.len() { return Err((s, TOO_SHORT)); }
|
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
|
||||||
if !s.starts_with(&prefix[..]) { return Err((s, INVALID)); }
|
|
||||||
s = &s[prefix.len()..];
|
s = &s[prefix.len()..];
|
||||||
}
|
}
|
||||||
|
|
||||||
&Item::Space(_) => {
|
Item::Space(_) | Item::OwnedSpace(_) => {
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
Item::Numeric(spec, _pad) => {
|
||||||
&Item::OwnedSpace(_) => {
|
|
||||||
s = s.trim_left();
|
|
||||||
}
|
|
||||||
|
|
||||||
&Item::Numeric(ref spec, ref _pad) => {
|
|
||||||
use super::Numeric::*;
|
use super::Numeric::*;
|
||||||
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
|
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
|
||||||
|
|
||||||
let (width, signed, set): (usize, bool, Setter) = match spec {
|
let (width, signed, set): (usize, bool, Setter) = match spec {
|
||||||
&Year => (4, true, Parsed::set_year),
|
Year => (4, true, Parsed::set_year),
|
||||||
&YearDiv100 => (2, false, Parsed::set_year_div_100),
|
YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||||
&YearMod100 => (2, false, Parsed::set_year_mod_100),
|
YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||||
&IsoYear => (4, true, Parsed::set_isoyear),
|
IsoYear => (4, true, Parsed::set_isoyear),
|
||||||
&IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||||
&IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||||
&Month => (2, false, Parsed::set_month),
|
Month => (2, false, Parsed::set_month),
|
||||||
&Day => (2, false, Parsed::set_day),
|
Day => (2, false, Parsed::set_day),
|
||||||
&WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||||
&WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||||
&IsoWeek => (2, false, Parsed::set_isoweek),
|
IsoWeek => (2, false, Parsed::set_isoweek),
|
||||||
&NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||||
&WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||||
&Ordinal => (3, false, Parsed::set_ordinal),
|
Ordinal => (3, false, Parsed::set_ordinal),
|
||||||
&Hour => (2, false, Parsed::set_hour),
|
Hour => (2, false, Parsed::set_hour),
|
||||||
&Hour12 => (2, false, Parsed::set_hour12),
|
Hour12 => (2, false, Parsed::set_hour12),
|
||||||
&Minute => (2, false, Parsed::set_minute),
|
Minute => (2, false, Parsed::set_minute),
|
||||||
&Second => (2, false, Parsed::set_second),
|
Second => (2, false, Parsed::set_second),
|
||||||
&Nanosecond => (9, false, Parsed::set_nanosecond),
|
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||||
&Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||||
|
|
||||||
// for the future expansion
|
// for the future expansion
|
||||||
&Internal(ref int) => match int._dummy {},
|
Internal(ref int) => match int._dummy {},
|
||||||
};
|
};
|
||||||
|
|
||||||
s = s.trim_left();
|
s = s.trim_left();
|
||||||
let v = if signed {
|
let v = if signed {
|
||||||
if s.starts_with('-') {
|
if s.starts_with('-') {
|
||||||
let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
|
let v = try_consume!(scan::number(&s[1..], 1, usize::MAX));
|
||||||
0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))?
|
try!(0i64.checked_sub(v).ok_or(OUT_OF_RANGE))
|
||||||
} else if s.starts_with('+') {
|
} else if s.starts_with('+') {
|
||||||
try_consume!(scan::number(&s[1..], 1, usize::MAX))
|
try_consume!(scan::number(&s[1..], 1, usize::MAX))
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,142 +272,104 @@ where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
|
||||||
} else {
|
} else {
|
||||||
try_consume!(scan::number(s, 1, width))
|
try_consume!(scan::number(s, 1, width))
|
||||||
};
|
};
|
||||||
set(parsed, v).map_err(|e| (s, e))?;
|
try!(set(parsed, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
&Item::Fixed(ref spec) => {
|
Item::Fixed(spec) => {
|
||||||
use super::Fixed::*;
|
use super::Fixed::*;
|
||||||
|
|
||||||
match spec {
|
match spec {
|
||||||
&ShortMonthName => {
|
ShortMonthName => {
|
||||||
let month0 = try_consume!(scan::short_month0(s));
|
let month0 = try_consume!(scan::short_month0(s));
|
||||||
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
|
try!(parsed.set_month(i64::from(month0) + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
&LongMonthName => {
|
LongMonthName => {
|
||||||
let month0 = try_consume!(scan::short_or_long_month0(s));
|
let month0 = try_consume!(scan::short_or_long_month0(s));
|
||||||
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
|
try!(parsed.set_month(i64::from(month0) + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
&ShortWeekdayName => {
|
ShortWeekdayName => {
|
||||||
let weekday = try_consume!(scan::short_weekday(s));
|
let weekday = try_consume!(scan::short_weekday(s));
|
||||||
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
|
try!(parsed.set_weekday(weekday));
|
||||||
}
|
}
|
||||||
|
|
||||||
&LongWeekdayName => {
|
LongWeekdayName => {
|
||||||
let weekday = try_consume!(scan::short_or_long_weekday(s));
|
let weekday = try_consume!(scan::short_or_long_weekday(s));
|
||||||
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
|
try!(parsed.set_weekday(weekday));
|
||||||
}
|
}
|
||||||
|
|
||||||
&LowerAmPm | &UpperAmPm => {
|
LowerAmPm | UpperAmPm => {
|
||||||
if s.len() < 2 { return Err((s, TOO_SHORT)); }
|
if s.len() < 2 { return Err(TOO_SHORT); }
|
||||||
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
|
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
|
||||||
(b'a',b'm') => false,
|
(b'a',b'm') => false,
|
||||||
(b'p',b'm') => true,
|
(b'p',b'm') => true,
|
||||||
_ => return Err((s, INVALID))
|
_ => return Err(INVALID)
|
||||||
};
|
};
|
||||||
parsed.set_ampm(ampm).map_err(|e| (s, e))?;
|
try!(parsed.set_ampm(ampm));
|
||||||
s = &s[2..];
|
s = &s[2..];
|
||||||
}
|
}
|
||||||
|
|
||||||
&Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
|
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => {
|
||||||
if s.starts_with('.') {
|
if s.starts_with('.') {
|
||||||
let nano = try_consume!(scan::nanosecond(&s[1..]));
|
let nano = try_consume!(scan::nanosecond(&s[1..]));
|
||||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
try!(parsed.set_nanosecond(nano));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
||||||
if s.len() < 3 { return Err((s, TOO_SHORT)); }
|
if s.len() < 3 { return Err(TOO_SHORT); }
|
||||||
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
|
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
|
||||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
try!(parsed.set_nanosecond(nano));
|
||||||
}
|
}
|
||||||
|
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
||||||
if s.len() < 6 { return Err((s, TOO_SHORT)); }
|
if s.len() < 6 { return Err(TOO_SHORT); }
|
||||||
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
|
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
|
||||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
try!(parsed.set_nanosecond(nano));
|
||||||
}
|
}
|
||||||
|
|
||||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
||||||
if s.len() < 9 { return Err((s, TOO_SHORT)); }
|
if s.len() < 9 { return Err(TOO_SHORT); }
|
||||||
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
|
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
|
||||||
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
|
try!(parsed.set_nanosecond(nano));
|
||||||
}
|
}
|
||||||
|
|
||||||
&TimezoneName => return Err((s, BAD_FORMAT)),
|
TimezoneName => return Err(BAD_FORMAT),
|
||||||
|
|
||||||
&TimezoneOffsetColon | &TimezoneOffset => {
|
TimezoneOffsetColon | TimezoneOffset => {
|
||||||
let offset = try_consume!(scan::timezone_offset(s.trim_left(),
|
let offset = try_consume!(scan::timezone_offset(s.trim_left(),
|
||||||
scan::colon_or_space));
|
scan::colon_or_space));
|
||||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
try!(parsed.set_offset(i64::from(offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
&TimezoneOffsetColonZ | &TimezoneOffsetZ => {
|
TimezoneOffsetColonZ | TimezoneOffsetZ => {
|
||||||
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
|
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
|
||||||
scan::colon_or_space));
|
scan::colon_or_space));
|
||||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
try!(parsed.set_offset(i64::from(offset)));
|
||||||
}
|
}
|
||||||
&Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
|
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) => {
|
||||||
let offset = try_consume!(scan::timezone_offset_permissive(
|
let offset = try_consume!(scan::timezone_offset_permissive(
|
||||||
s.trim_left(), scan::colon_or_space));
|
s.trim_left(), scan::colon_or_space));
|
||||||
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
|
try!(parsed.set_offset(i64::from(offset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
&RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||||
&RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&Item::Error => {
|
Item::Error => {
|
||||||
return Err((s, BAD_FORMAT));
|
return Err(BAD_FORMAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are trailling chars, it is an error
|
// if there are trailling chars, it is an error
|
||||||
if !s.is_empty() {
|
if !s.is_empty() {
|
||||||
Err((s, TOO_LONG))
|
Err(TOO_LONG)
|
||||||
} else {
|
} else {
|
||||||
Ok(s)
|
Ok(())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl str::FromStr for DateTime<FixedOffset> {
|
|
||||||
type Err = ParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
|
||||||
const DATE_ITEMS: &'static [Item<'static>] = &[
|
|
||||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
|
||||||
Item::Space(""), Item::Literal("-"),
|
|
||||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
|
||||||
Item::Space(""), Item::Literal("-"),
|
|
||||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
|
||||||
];
|
|
||||||
const TIME_ITEMS: &'static [Item<'static>] = &[
|
|
||||||
Item::Numeric(Numeric::Hour, Pad::Zero),
|
|
||||||
Item::Space(""), Item::Literal(":"),
|
|
||||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
|
||||||
Item::Space(""), Item::Literal(":"),
|
|
||||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
|
||||||
Item::Fixed(Fixed::Nanosecond),
|
|
||||||
Item::Space(""), Item::Fixed(Fixed::TimezoneOffsetZ),
|
|
||||||
Item::Space(""),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut parsed = Parsed::new();
|
|
||||||
match parse_internal(&mut parsed, s, DATE_ITEMS.iter()) {
|
|
||||||
Err((remainder, e)) if e.0 == ParseErrorKind::TooLong =>{
|
|
||||||
if remainder.starts_with('T') || remainder.starts_with(' ') {
|
|
||||||
parse(&mut parsed, &remainder[1..], TIME_ITEMS.iter())?;
|
|
||||||
} else {
|
|
||||||
Err(INVALID)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err((_s, e)) => Err(e)?,
|
|
||||||
Ok(_) => Err(NOT_ENOUGH)?,
|
|
||||||
};
|
|
||||||
parsed.to_datetime()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +382,7 @@ fn test_parse() {
|
||||||
// workaround for Rust issue #22255
|
// workaround for Rust issue #22255
|
||||||
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
|
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, items.iter())?;
|
try!(parse(&mut parsed, s, items.iter().cloned()));
|
||||||
Ok(parsed)
|
Ok(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,12 +693,12 @@ fn test_rfc2822() {
|
||||||
|
|
||||||
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
|
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter().cloned()));
|
||||||
parsed.to_datetime()
|
parsed.to_datetime()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
|
fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
|
||||||
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string()
|
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter().cloned()).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test against test data above
|
// Test against test data above
|
||||||
|
@ -831,12 +774,12 @@ fn test_rfc3339() {
|
||||||
|
|
||||||
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?;
|
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter().cloned()));
|
||||||
parsed.to_datetime()
|
parsed.to_datetime()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
|
fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
|
||||||
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string()
|
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter().cloned()).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test against test data above
|
// Test against test data above
|
||||||
|
|
|
@ -110,9 +110,8 @@ pub struct Parsed {
|
||||||
_dummy: (),
|
_dummy: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"),
|
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
|
||||||
/// and if it is empty, set `old` to `new` as well.
|
/// and if it is empty, set `old` to `new` as well.
|
||||||
#[inline]
|
|
||||||
fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
|
fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
|
||||||
if let Some(ref old) = *old {
|
if let Some(ref old) = *old {
|
||||||
if *old == new {Ok(())} else {Err(IMPOSSIBLE)}
|
if *old == new {Ok(())} else {Err(IMPOSSIBLE)}
|
||||||
|
@ -142,97 +141,82 @@ impl Parsed {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`year`](#structfield.year) field from given value.
|
/// Tries to set the [`year`](#structfield.year) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_year(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.year, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.year, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
|
/// Tries to set the [`year_div_100`](#structfield.year_div_100) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||||
if value < 0 { return Err(OUT_OF_RANGE); }
|
if value < 0 { return Err(OUT_OF_RANGE); }
|
||||||
set_if_consistent(&mut self.year_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.year_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
|
/// Tries to set the [`year_mod_100`](#structfield.year_mod_100) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
||||||
if value < 0 { return Err(OUT_OF_RANGE); }
|
if value < 0 { return Err(OUT_OF_RANGE); }
|
||||||
set_if_consistent(&mut self.year_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.year_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
|
/// Tries to set the [`isoyear`](#structfield.isoyear) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.isoyear, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.isoyear, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
|
/// Tries to set the [`isoyear_div_100`](#structfield.isoyear_div_100) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||||
if value < 0 { return Err(OUT_OF_RANGE); }
|
if value < 0 { return Err(OUT_OF_RANGE); }
|
||||||
set_if_consistent(&mut self.isoyear_div_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.isoyear_div_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
|
/// Tries to set the [`isoyear_mod_100`](#structfield.isoyear_mod_100) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
||||||
if value < 0 { return Err(OUT_OF_RANGE); }
|
if value < 0 { return Err(OUT_OF_RANGE); }
|
||||||
set_if_consistent(&mut self.isoyear_mod_100, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.isoyear_mod_100, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`month`](#structfield.month) field from given value.
|
/// Tries to set the [`month`](#structfield.month) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_month(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.month, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.month, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
|
/// Tries to set the [`week_from_sun`](#structfield.week_from_sun) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.week_from_sun, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.week_from_sun, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
|
/// Tries to set the [`week_from_mon`](#structfield.week_from_mon) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.week_from_mon, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.week_from_mon, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
|
/// Tries to set the [`isoweek`](#structfield.isoweek) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.isoweek, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.isoweek, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`weekday`](#structfield.weekday) field from given value.
|
/// Tries to set the [`weekday`](#structfield.weekday) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
|
pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.weekday, value)
|
set_if_consistent(&mut self.weekday, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
|
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.ordinal, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.ordinal, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`day`](#structfield.day) field from given value.
|
/// Tries to set the [`day`](#structfield.day) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_day(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.day, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.day, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
|
/// Tries to set the [`hour_div_12`](#structfield.hour_div_12) field from given value.
|
||||||
/// (`false` for AM, `true` for PM)
|
/// (`false` for AM, `true` for PM)
|
||||||
#[inline]
|
|
||||||
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
|
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.hour_div_12, if value {1} else {0})
|
set_if_consistent(&mut self.hour_div_12, if value {1} else {0})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
|
/// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
|
||||||
/// given hour number in 12-hour clocks.
|
/// given hour number in 12-hour clocks.
|
||||||
#[inline]
|
|
||||||
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
|
||||||
if value < 1 || value > 12 { return Err(OUT_OF_RANGE); }
|
if value < 1 || value > 12 { return Err(OUT_OF_RANGE); }
|
||||||
set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
|
set_if_consistent(&mut self.hour_mod_12, value as u32 % 12)
|
||||||
|
@ -240,42 +224,36 @@ impl Parsed {
|
||||||
|
|
||||||
/// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and
|
/// Tries to set both [`hour_div_12`](#structfield.hour_div_12) and
|
||||||
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
|
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
|
||||||
let v = value.to_u32().ok_or(OUT_OF_RANGE)?;
|
let v = try!(value.to_u32().ok_or(OUT_OF_RANGE));
|
||||||
set_if_consistent(&mut self.hour_div_12, v / 12)?;
|
try!(set_if_consistent(&mut self.hour_div_12, v / 12));
|
||||||
set_if_consistent(&mut self.hour_mod_12, v % 12)?;
|
try!(set_if_consistent(&mut self.hour_mod_12, v % 12));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`minute`](#structfield.minute) field from given value.
|
/// Tries to set the [`minute`](#structfield.minute) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_minute(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.minute, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.minute, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`second`](#structfield.second) field from given value.
|
/// Tries to set the [`second`](#structfield.second) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_second(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.second, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.second, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
|
/// Tries to set the [`nanosecond`](#structfield.nanosecond) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.nanosecond, value.to_u32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.nanosecond, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
|
/// Tries to set the [`timestamp`](#structfield.timestamp) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.timestamp, value)
|
set_if_consistent(&mut self.timestamp, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to set the [`offset`](#structfield.offset) field from given value.
|
/// Tries to set the [`offset`](#structfield.offset) field from given value.
|
||||||
#[inline]
|
|
||||||
pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
|
pub fn set_offset(&mut self, value: i64) -> ParseResult<()> {
|
||||||
set_if_consistent(&mut self.offset, value.to_i32().ok_or(OUT_OF_RANGE)?)
|
set_if_consistent(&mut self.offset, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parsed naive date out of given fields.
|
/// Returns a parsed naive date out of given fields.
|
||||||
|
@ -316,7 +294,7 @@ impl Parsed {
|
||||||
(None, Some(q), Some(r @ 0...99)) => {
|
(None, Some(q), Some(r @ 0...99)) => {
|
||||||
if q < 0 { return Err(OUT_OF_RANGE); }
|
if q < 0 { return Err(OUT_OF_RANGE); }
|
||||||
let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
|
let y = q.checked_mul(100).and_then(|v| v.checked_add(r));
|
||||||
Ok(Some(y.ok_or(OUT_OF_RANGE)?))
|
Ok(Some(try!(y.ok_or(OUT_OF_RANGE))))
|
||||||
},
|
},
|
||||||
|
|
||||||
// we only have modulo. try to interpret a modulo as a conventional two-digit year.
|
// we only have modulo. try to interpret a modulo as a conventional two-digit year.
|
||||||
|
@ -330,9 +308,9 @@ impl Parsed {
|
||||||
}
|
}
|
||||||
|
|
||||||
let given_year =
|
let given_year =
|
||||||
resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
|
try!(resolve_year(self.year, self.year_div_100, self.year_mod_100));
|
||||||
let given_isoyear =
|
let given_isoyear =
|
||||||
resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
|
try!(resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100));
|
||||||
|
|
||||||
// verify the normal year-month-day date.
|
// verify the normal year-month-day date.
|
||||||
let verify_ymd = |date: NaiveDate| {
|
let verify_ymd = |date: NaiveDate| {
|
||||||
|
@ -388,20 +366,20 @@ impl Parsed {
|
||||||
let (verified, parsed_date) = match (given_year, given_isoyear, self) {
|
let (verified, parsed_date) = match (given_year, given_isoyear, self) {
|
||||||
(Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
|
(Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => {
|
||||||
// year, month, day
|
// year, month, day
|
||||||
let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?;
|
let date = try!(NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE));
|
||||||
(verify_isoweekdate(date) && verify_ordinal(date), date)
|
(verify_isoweekdate(date) && verify_ordinal(date), date)
|
||||||
},
|
},
|
||||||
|
|
||||||
(Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
|
(Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
|
||||||
// year, day of the year
|
// year, day of the year
|
||||||
let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?;
|
let date = try!(NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE));
|
||||||
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
||||||
},
|
},
|
||||||
|
|
||||||
(Some(year), _, &Parsed { week_from_sun: Some(week_from_sun),
|
(Some(year), _, &Parsed { week_from_sun: Some(week_from_sun),
|
||||||
weekday: Some(weekday), .. }) => {
|
weekday: Some(weekday), .. }) => {
|
||||||
// year, week (starting at 1st Sunday), day of the week
|
// year, week (starting at 1st Sunday), day of the week
|
||||||
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
|
let newyear = try!(NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE));
|
||||||
let firstweek = match newyear.weekday() {
|
let firstweek = match newyear.weekday() {
|
||||||
Weekday::Sun => 0,
|
Weekday::Sun => 0,
|
||||||
Weekday::Mon => 6,
|
Weekday::Mon => 6,
|
||||||
|
@ -416,8 +394,8 @@ impl Parsed {
|
||||||
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||||
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
|
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
|
||||||
weekday.num_days_from_sunday() as i32;
|
weekday.num_days_from_sunday() as i32;
|
||||||
let date = newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||||
.ok_or(OUT_OF_RANGE)?;
|
.ok_or(OUT_OF_RANGE));
|
||||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||||
|
|
||||||
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
||||||
|
@ -426,7 +404,7 @@ impl Parsed {
|
||||||
(Some(year), _, &Parsed { week_from_mon: Some(week_from_mon),
|
(Some(year), _, &Parsed { week_from_mon: Some(week_from_mon),
|
||||||
weekday: Some(weekday), .. }) => {
|
weekday: Some(weekday), .. }) => {
|
||||||
// year, week (starting at 1st Monday), day of the week
|
// year, week (starting at 1st Monday), day of the week
|
||||||
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
|
let newyear = try!(NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE));
|
||||||
let firstweek = match newyear.weekday() {
|
let firstweek = match newyear.weekday() {
|
||||||
Weekday::Sun => 1,
|
Weekday::Sun => 1,
|
||||||
Weekday::Mon => 0,
|
Weekday::Mon => 0,
|
||||||
|
@ -441,8 +419,8 @@ impl Parsed {
|
||||||
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||||
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
|
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
|
||||||
weekday.num_days_from_monday() as i32;
|
weekday.num_days_from_monday() as i32;
|
||||||
let date = newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||||
.ok_or(OUT_OF_RANGE)?;
|
.ok_or(OUT_OF_RANGE));
|
||||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||||
|
|
||||||
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
(verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date)
|
||||||
|
@ -451,7 +429,7 @@ impl Parsed {
|
||||||
(_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
|
(_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => {
|
||||||
// ISO year, week, day of the week
|
// ISO year, week, day of the week
|
||||||
let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
|
let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday);
|
||||||
let date = date.ok_or(OUT_OF_RANGE)?;
|
let date = try!(date.ok_or(OUT_OF_RANGE));
|
||||||
(verify_ymd(date) && verify_ordinal(date), date)
|
(verify_ymd(date) && verify_ordinal(date), date)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -547,9 +525,9 @@ impl Parsed {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconstruct date and time fields from timestamp
|
// reconstruct date and time fields from timestamp
|
||||||
let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
|
let ts = try!(timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE));
|
||||||
let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
|
let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
|
||||||
let mut datetime = datetime.ok_or(OUT_OF_RANGE)?;
|
let mut datetime = try!(datetime.ok_or(OUT_OF_RANGE));
|
||||||
|
|
||||||
// fill year, ordinal, hour, minute and second fields from timestamp.
|
// fill year, ordinal, hour, minute and second fields from timestamp.
|
||||||
// if existing fields are consistent, this will allow the full date/time reconstruction.
|
// if existing fields are consistent, this will allow the full date/time reconstruction.
|
||||||
|
@ -566,21 +544,21 @@ impl Parsed {
|
||||||
}
|
}
|
||||||
// ...and we have the correct candidates for other fields.
|
// ...and we have the correct candidates for other fields.
|
||||||
} else {
|
} else {
|
||||||
parsed.set_second(i64::from(datetime.second()))?;
|
try!(parsed.set_second(i64::from(datetime.second())));
|
||||||
}
|
}
|
||||||
parsed.set_year (i64::from(datetime.year()))?;
|
try!(parsed.set_year (i64::from(datetime.year())));
|
||||||
parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
|
try!(parsed.set_ordinal(i64::from(datetime.ordinal()))); // more efficient than ymd
|
||||||
parsed.set_hour (i64::from(datetime.hour()))?;
|
try!(parsed.set_hour (i64::from(datetime.hour())));
|
||||||
parsed.set_minute (i64::from(datetime.minute()))?;
|
try!(parsed.set_minute (i64::from(datetime.minute())));
|
||||||
|
|
||||||
// validate other fields (e.g. week) and return
|
// validate other fields (e.g. week) and return
|
||||||
let date = parsed.to_naive_date()?;
|
let date = try!(parsed.to_naive_date());
|
||||||
let time = parsed.to_naive_time()?;
|
let time = try!(parsed.to_naive_time());
|
||||||
Ok(date.and_time(time))
|
Ok(date.and_time(time))
|
||||||
} else {
|
} else {
|
||||||
// reproduce the previous error(s)
|
// reproduce the previous error(s)
|
||||||
date?;
|
try!(date);
|
||||||
time?;
|
try!(time);
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -597,9 +575,9 @@ impl Parsed {
|
||||||
/// plus a time zone offset.
|
/// plus a time zone offset.
|
||||||
/// Either way those fields have to be consistent to each other.
|
/// Either way those fields have to be consistent to each other.
|
||||||
pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
|
pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
let offset = self.offset.ok_or(NOT_ENOUGH)?;
|
let offset = try!(self.offset.ok_or(NOT_ENOUGH));
|
||||||
let datetime = self.to_naive_datetime_with_offset(offset)?;
|
let datetime = try!(self.to_naive_datetime_with_offset(offset));
|
||||||
let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
|
let offset = try!(FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE));
|
||||||
match offset.from_local_datetime(&datetime) {
|
match offset.from_local_datetime(&datetime) {
|
||||||
LocalResult::None => Err(IMPOSSIBLE),
|
LocalResult::None => Err(IMPOSSIBLE),
|
||||||
LocalResult::Single(t) => Ok(t),
|
LocalResult::Single(t) => Ok(t),
|
||||||
|
@ -624,7 +602,7 @@ impl Parsed {
|
||||||
// an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
|
// an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
|
||||||
let nanosecond = self.nanosecond.unwrap_or(0);
|
let nanosecond = self.nanosecond.unwrap_or(0);
|
||||||
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
|
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
|
||||||
let dt = dt.ok_or(OUT_OF_RANGE)?;
|
let dt = try!(dt.ok_or(OUT_OF_RANGE));
|
||||||
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
|
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,7 +617,7 @@ impl Parsed {
|
||||||
|
|
||||||
// `guessed_offset` should be correct when `self.timestamp` is given.
|
// `guessed_offset` should be correct when `self.timestamp` is given.
|
||||||
// it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
|
// it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case.
|
||||||
let datetime = self.to_naive_datetime_with_offset(guessed_offset)?;
|
let datetime = try!(self.to_naive_datetime_with_offset(guessed_offset));
|
||||||
match tz.from_local_datetime(&datetime) {
|
match tz.from_local_datetime(&datetime) {
|
||||||
LocalResult::None => Err(IMPOSSIBLE),
|
LocalResult::None => Err(IMPOSSIBLE),
|
||||||
LocalResult::Single(t) => if check_offset(&t) {Ok(t)} else {Err(IMPOSSIBLE)},
|
LocalResult::Single(t) => if check_offset(&t) {Ok(t)} else {Err(IMPOSSIBLE)},
|
||||||
|
|
|
@ -30,35 +30,23 @@ fn equals(s: &str, pattern: &str) -> bool {
|
||||||
/// The absence of digits at all is an unconditional error.
|
/// The absence of digits at all is an unconditional error.
|
||||||
/// More than `max` digits are consumed up to the first `max` digits.
|
/// More than `max` digits are consumed up to the first `max` digits.
|
||||||
/// Any number that does not fit in `i64` is an error.
|
/// Any number that does not fit in `i64` is an error.
|
||||||
#[inline]
|
|
||||||
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||||
assert!(min <= max);
|
assert!(min <= max);
|
||||||
|
|
||||||
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
|
// limit `s` to given number of digits
|
||||||
// the first non-numeric byte, which may be another ascii character or beginning of multi-byte
|
let mut window = s.as_bytes();
|
||||||
// UTF-8 character.
|
if window.len() > max { window = &window[..max]; }
|
||||||
let bytes = s.as_bytes();
|
|
||||||
if bytes.len() < min {
|
// scan digits
|
||||||
return Err(TOO_SHORT);
|
let upto = window.iter().position(|&c| c < b'0' || b'9' < c)
|
||||||
|
.unwrap_or_else(|| window.len());
|
||||||
|
if upto < min {
|
||||||
|
return Err(if window.is_empty() {TOO_SHORT} else {INVALID});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut n = 0i64;
|
// we can overflow here, which is the only possible cause of error from `parse`.
|
||||||
for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied()
|
let v: i64 = try!(s[..upto].parse().map_err(|_| OUT_OF_RANGE));
|
||||||
if c < b'0' || b'9' < c {
|
Ok((&s[upto..], v))
|
||||||
if i < min {
|
|
||||||
return Err(INVALID);
|
|
||||||
} else {
|
|
||||||
return Ok((&s[i..], n));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) {
|
|
||||||
Some(n) => n,
|
|
||||||
None => return Err(OUT_OF_RANGE),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((&s[::core::cmp::min(max, bytes.len())..], n))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to consume at least one digits as a fractional second.
|
/// Tries to consume at least one digits as a fractional second.
|
||||||
|
@ -66,13 +54,13 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||||
pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||||
// record the number of digits consumed for later scaling.
|
// record the number of digits consumed for later scaling.
|
||||||
let origlen = s.len();
|
let origlen = s.len();
|
||||||
let (s, v) = number(s, 1, 9)?;
|
let (s, v) = try!(number(s, 1, 9));
|
||||||
let consumed = origlen - s.len();
|
let consumed = origlen - s.len();
|
||||||
|
|
||||||
// scale the number accordingly.
|
// scale the number accordingly.
|
||||||
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
||||||
1_000, 100, 10, 1];
|
1_000, 100, 10, 1];
|
||||||
let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?;
|
let v = try!(v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE));
|
||||||
|
|
||||||
// if there are more than 9 digits, skip next digits.
|
// if there are more than 9 digits, skip next digits.
|
||||||
let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9');
|
let s = s.trim_left_matches(|c: char| '0' <= c && c <= '9');
|
||||||
|
@ -84,12 +72,12 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
|
||||||
/// Returns the number of whole nanoseconds (0--999,999,999).
|
/// Returns the number of whole nanoseconds (0--999,999,999).
|
||||||
pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||||
// record the number of digits consumed for later scaling.
|
// record the number of digits consumed for later scaling.
|
||||||
let (s, v) = number(s, digits, digits)?;
|
let (s, v) = try!(number(s, digits, digits));
|
||||||
|
|
||||||
// scale the number accordingly.
|
// scale the number accordingly.
|
||||||
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
||||||
1_000, 100, 10, 1];
|
1_000, 100, 10, 1];
|
||||||
let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?;
|
let v = try!(v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE));
|
||||||
|
|
||||||
Ok((s, v))
|
Ok((s, v))
|
||||||
}
|
}
|
||||||
|
@ -140,7 +128,7 @@ pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
||||||
static LONG_MONTH_SUFFIXES: [&'static str; 12] =
|
static LONG_MONTH_SUFFIXES: [&'static str; 12] =
|
||||||
["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
|
["uary", "ruary", "ch", "il", "", "e", "y", "ust", "tember", "ober", "ember", "ember"];
|
||||||
|
|
||||||
let (mut s, month0) = short_month0(s)?;
|
let (mut s, month0) = try!(short_month0(s));
|
||||||
|
|
||||||
// tries to consume the suffix if possible
|
// tries to consume the suffix if possible
|
||||||
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
|
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
|
||||||
|
@ -158,7 +146,7 @@ pub fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
|
||||||
static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] =
|
static LONG_WEEKDAY_SUFFIXES: [&'static str; 7] =
|
||||||
["day", "sday", "nesday", "rsday", "day", "urday", "day"];
|
["day", "sday", "nesday", "rsday", "day", "urday", "day"];
|
||||||
|
|
||||||
let (mut s, weekday) = short_weekday(s)?;
|
let (mut s, weekday) = try!(short_weekday(s));
|
||||||
|
|
||||||
// tries to consume the suffix if possible
|
// tries to consume the suffix if possible
|
||||||
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
|
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
|
||||||
|
@ -225,14 +213,14 @@ fn timezone_offset_internal<F>(mut s: &str, mut consume_colon: F, allow_missing_
|
||||||
s = &s[1..];
|
s = &s[1..];
|
||||||
|
|
||||||
// hours (00--99)
|
// hours (00--99)
|
||||||
let hours = match digits(s)? {
|
let hours = match try!(digits(s)) {
|
||||||
(h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
(h1 @ b'0'...b'9', h2 @ b'0'...b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')),
|
||||||
_ => return Err(INVALID),
|
_ => return Err(INVALID),
|
||||||
};
|
};
|
||||||
s = &s[2..];
|
s = &s[2..];
|
||||||
|
|
||||||
// colons (and possibly other separators)
|
// colons (and possibly other separators)
|
||||||
s = consume_colon(s)?;
|
s = try!(consume_colon(s));
|
||||||
|
|
||||||
// minutes (00--59)
|
// minutes (00--59)
|
||||||
// if the next two items are digits then we have to add minutes
|
// if the next two items are digits then we have to add minutes
|
||||||
|
@ -257,30 +245,18 @@ fn timezone_offset_internal<F>(mut s: &str, mut consume_colon: F, allow_missing_
|
||||||
Ok((s, if negative {-seconds} else {seconds}))
|
Ok((s, if negative {-seconds} else {seconds}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
|
/// Same to `timezone_offset` but also allows for `z`/`Z` which is same to `+00:00`.
|
||||||
pub fn timezone_offset_zulu<F>(s: &str, colon: F)
|
pub fn timezone_offset_zulu<F>(s: &str, colon: F)
|
||||||
-> ParseResult<(&str, i32)>
|
-> ParseResult<(&str, i32)>
|
||||||
where F: FnMut(&str) -> ParseResult<&str>
|
where F: FnMut(&str) -> ParseResult<&str>
|
||||||
{
|
{
|
||||||
let bytes = s.as_bytes();
|
match s.as_bytes().first() {
|
||||||
match bytes.first() {
|
|
||||||
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
|
||||||
Some(&b'u') | Some(&b'U') => {
|
|
||||||
if bytes.len() >= 3 {
|
|
||||||
let (b, c) = (bytes[1], bytes[2]);
|
|
||||||
match (b | 32, c | 32) {
|
|
||||||
(b't', b'c') => Ok((&s[3..], 0)),
|
|
||||||
_ => Err(INVALID),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(INVALID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => timezone_offset(s, colon),
|
_ => timezone_offset(s, colon),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
|
/// Same to `timezone_offset` but also allows for `z`/`Z` which is same to
|
||||||
/// `+00:00`, and allows missing minutes entirely.
|
/// `+00:00`, and allows missing minutes entirely.
|
||||||
pub fn timezone_offset_permissive<F>(s: &str, colon: F)
|
pub fn timezone_offset_permissive<F>(s: &str, colon: F)
|
||||||
-> ParseResult<(&str, i32)>
|
-> ParseResult<(&str, i32)>
|
||||||
|
@ -292,7 +268,7 @@ pub fn timezone_offset_permissive<F>(s: &str, colon: F)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones.
|
/// Same to `timezone_offset` but also allows for RFC 2822 legacy timezones.
|
||||||
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
|
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
|
||||||
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
||||||
// tries to parse legacy time zone names
|
// tries to parse legacy time zone names
|
||||||
|
@ -319,8 +295,12 @@ pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
||||||
Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
|
Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
|
let (s_, offset) = try!(timezone_offset(s, |s| Ok(s)));
|
||||||
Ok((s_, Some(offset)))
|
if offset == 0 && s.starts_with('-') { // -0000 is not same to +0000
|
||||||
|
Ok((s_, None))
|
||||||
|
} else {
|
||||||
|
Ok((s_, Some(offset)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,73 +11,73 @@ The following specifiers are available both to formatting and parsing.
|
||||||
| Spec. | Example | Description |
|
| Spec. | Example | Description |
|
||||||
|-------|----------|----------------------------------------------------------------------------|
|
|-------|----------|----------------------------------------------------------------------------|
|
||||||
| | | **DATE SPECIFIERS:** |
|
| | | **DATE SPECIFIERS:** |
|
||||||
| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [^1] |
|
| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. [1] |
|
||||||
| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^2] |
|
| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [2] |
|
||||||
| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] |
|
| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [2] |
|
||||||
| | | |
|
| | | |
|
||||||
| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. |
|
| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. |
|
||||||
| `%b` | `Jul` | Abbreviated month name. Always 3 letters. |
|
| `%b` | `Jul` | Abbreviated month name. Always 3 letters. |
|
||||||
| `%B` | `July` | Full month name. Also accepts corresponding abbreviation in parsing. |
|
| `%B` | `July` | Full month name. Also accepts corresponding abbreviation in parsing. |
|
||||||
| `%h` | `Jul` | Same as `%b`. |
|
| `%h` | `Jul` | Same to `%b`. |
|
||||||
| | | |
|
| | | |
|
||||||
| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. |
|
| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. |
|
||||||
| `%e` | ` 8` | Same as `%d` but space-padded. Same as `%_d`. |
|
| `%e` | ` 8` | Same to `%d` but space-padded. Same to `%_d`. |
|
||||||
| | | |
|
| | | |
|
||||||
| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. |
|
| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. |
|
||||||
| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. |
|
| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. |
|
||||||
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
|
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
|
||||||
| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) |
|
| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) |
|
||||||
| | | |
|
| | | |
|
||||||
| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^3] |
|
| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [3] |
|
||||||
| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.|
|
| `%W` | `27` | Same to `%U`, but week 1 starts with the first Monday in that year instead.|
|
||||||
| | | |
|
| | | |
|
||||||
| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^4] |
|
| `%G` | `2001` | Same to `%Y` but uses the year number in ISO 8601 week date. [4] |
|
||||||
| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^4] |
|
| `%g` | `01` | Same to `%y` but uses the year number in ISO 8601 week date. [4] |
|
||||||
| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^4] |
|
| `%V` | `27` | Same to `%U` but uses the week number in ISO 8601 week date (01--53). [4] |
|
||||||
| | | |
|
| | | |
|
||||||
| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. |
|
| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. |
|
||||||
| | | |
|
| | | |
|
||||||
| `%D` | `07/08/01` | Month-day-year format. Same as `%m/%d/%y`. |
|
| `%D` | `07/08/01` | Month-day-year format. Same to `%m/%d/%y`. |
|
||||||
| `%x` | `07/08/01` | Same as `%D`. |
|
| `%x` | `07/08/01` | Same to `%D`. |
|
||||||
| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`. |
|
| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same to `%Y-%m-%d`. |
|
||||||
| `%v` | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`. |
|
| `%v` | ` 8-Jul-2001` | Day-month-year format. Same to `%e-%b-%Y`. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | **TIME SPECIFIERS:** |
|
| | | **TIME SPECIFIERS:** |
|
||||||
| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. |
|
| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. |
|
||||||
| `%k` | ` 0` | Same as `%H` but space-padded. Same as `%_H`. |
|
| `%k` | ` 0` | Same to `%H` but space-padded. Same to `%_H`. |
|
||||||
| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. |
|
| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. |
|
||||||
| `%l` | `12` | Same as `%I` but space-padded. Same as `%_I`. |
|
| `%l` | `12` | Same to `%I` but space-padded. Same to `%_I`. |
|
||||||
| | | |
|
| | | |
|
||||||
| `%P` | `am` | `am` or `pm` in 12-hour clocks. |
|
| `%P` | `am` | `am` or `pm` in 12-hour clocks. |
|
||||||
| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. |
|
| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. |
|
||||||
| | | |
|
| | | |
|
||||||
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
|
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
|
||||||
| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^5] |
|
| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [5] |
|
||||||
| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [^8] |
|
| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [8] |
|
||||||
| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [^8] |
|
| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [8] |
|
||||||
| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [^8] |
|
| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [8] |
|
||||||
| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [^8] |
|
| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [8] |
|
||||||
| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [^8] |
|
| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [8] |
|
||||||
| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [^8] |
|
| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [8] |
|
||||||
| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [^8] |
|
| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [8] |
|
||||||
| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [^8] |
|
| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [8] |
|
||||||
| | | |
|
| | | |
|
||||||
| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. |
|
| `%R` | `00:34` | Hour-minute format. Same to `%H:%M`. |
|
||||||
| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. |
|
| `%T` | `00:34:60` | Hour-minute-second format. Same to `%H:%M:%S`. |
|
||||||
| `%X` | `00:34:60` | Same as `%T`. |
|
| `%X` | `00:34:60` | Same to `%T`. |
|
||||||
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. |
|
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same to `%I:%M:%S %p`. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | **TIME ZONE SPECIFIERS:** |
|
| | | **TIME ZONE SPECIFIERS:** |
|
||||||
| `%Z` | `ACST` | *Formatting only:* Local time zone name. |
|
| `%Z` | `ACST` | *Formatting only:* Local time zone name. |
|
||||||
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
|
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
|
||||||
| `%:z` | `+09:30` | Same as `%z` but with a colon. |
|
| `%:z` | `+09:30` | Same to `%z` but with a colon. |
|
||||||
| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. |
|
| `%#z` | `+09` | *Parsing only:* Same to `%z` but allows minutes to be missing or present. |
|
||||||
| | | |
|
| | | |
|
||||||
| | | **DATE & TIME SPECIFIERS:** |
|
| | | **DATE & TIME SPECIFIERS:** |
|
||||||
|`%c`|`Sun Jul 8 00:34:60 2001`|`ctime` date & time format. Same as `%a %b %e %T %Y` sans `\n`.|
|
|`%c`|`Sun Jul 8 00:34:60 2001`|`ctime` date & time format. Same to `%a %b %e %T %Y` sans `\n`.|
|
||||||
| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] |
|
| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [6] |
|
||||||
| | | |
|
| | | |
|
||||||
| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^7]|
|
| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [7] |
|
||||||
| | | |
|
| | | |
|
||||||
| | | **SPECIAL SPECIFIERS:** |
|
| | | **SPECIAL SPECIFIERS:** |
|
||||||
| `%t` | | Literal tab (`\t`). |
|
| `%t` | | Literal tab (`\t`). |
|
||||||
|
@ -95,62 +95,59 @@ Modifier | Description
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
[^1]: `%Y`:
|
1. `%Y`:
|
||||||
Negative years are allowed in formatting but not in parsing.
|
Negative years are allowed in formatting but not in parsing.
|
||||||
|
|
||||||
[^2]: `%C`, `%y`:
|
2. `%C`, `%y`:
|
||||||
This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively.
|
This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively.
|
||||||
|
|
||||||
[^3]: `%U`:
|
3. `%U`:
|
||||||
Week 1 starts with the first Sunday in that year.
|
Week 1 starts with the first Sunday in that year.
|
||||||
It is possible to have week 0 for days before the first Sunday.
|
It is possible to have week 0 for days before the first Sunday.
|
||||||
|
|
||||||
[^4]: `%G`, `%g`, `%V`:
|
4. `%G`, `%g`, `%V`:
|
||||||
Week 1 is the first week with at least 4 days in that year.
|
Week 1 is the first week with at least 4 days in that year.
|
||||||
Week 0 does not exist, so this should be used with `%G` or `%g`.
|
Week 0 does not exist, so this should be used with `%G` or `%g`.
|
||||||
|
|
||||||
[^5]: `%S`:
|
5. `%S`:
|
||||||
It accounts for leap seconds, so `60` is possible.
|
It accounts for leap seconds, so `60` is possible.
|
||||||
|
|
||||||
[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional
|
6. `%+`:
|
||||||
digits for seconds and colons in the time zone offset.
|
Same to `%Y-%m-%dT%H:%M:%S%.f%:z`,
|
||||||
<br>
|
i.e. 0, 3, 6 or 9 fractional digits for seconds and colons in the time zone offset.
|
||||||
<br>
|
|
||||||
The typical `strftime` implementations have different (and locale-dependent)
|
|
||||||
formats for this specifier. While Chrono's format for `%+` is far more
|
|
||||||
stable, it is best to avoid this specifier if you want to control the exact
|
|
||||||
output.
|
|
||||||
|
|
||||||
[^7]: `%s`:
|
The typical `strftime` implementations have
|
||||||
|
different (and locale-dependent) formats for this specifier.
|
||||||
|
While Chrono's format for `%+` is far more stable,
|
||||||
|
it is best to avoid this specifier if you want to control the exact output.
|
||||||
|
|
||||||
|
7. `%s`:
|
||||||
This is not padded and can be negative.
|
This is not padded and can be negative.
|
||||||
For the purpose of Chrono, it only accounts for non-leap seconds
|
For the purpose of Chrono, it only accounts for non-leap seconds
|
||||||
so it slightly differs from ISO C `strftime` behavior.
|
so it slightly differs from ISO C `strftime` behavior.
|
||||||
|
|
||||||
[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
|
8. `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
|
||||||
<br>
|
|
||||||
The default `%f` is right-aligned and always zero-padded to 9 digits
|
The default `%f` is right-aligned and always zero-padded to 9 digits
|
||||||
for the compatibility with glibc and others,
|
for the compatibility with glibc and others,
|
||||||
so it always counts the number of nanoseconds since the last whole second.
|
so it always counts the number of nanoseconds since the last whole second.
|
||||||
E.g. 7ms after the last second will print `007000000`,
|
E.g. 7ms after the last second will print `007000000`,
|
||||||
and parsing `7000000` will yield the same.
|
and parsing `7000000` will yield the same.
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits
|
The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits
|
||||||
according to the precision.
|
according to the precision.
|
||||||
E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`),
|
E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`),
|
||||||
and parsing `.07`, `.070000` etc. will yield the same.
|
and parsing `.07`, `.070000` etc. will yield the same.
|
||||||
Note that they can print or read nothing if the fractional part is zero or
|
Note that they can print or read nothing if the fractional part is zero or
|
||||||
the next character is not `.`.
|
the next character is not `.`.
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits
|
The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits
|
||||||
according to the number preceding `f`.
|
according to the number preceding `f`.
|
||||||
E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`),
|
E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`),
|
||||||
and parsing `.07`, `.070000` etc. will yield the same.
|
and parsing `.07`, `.070000` etc. will yield the same.
|
||||||
Note that they can read nothing if the fractional part is zero or
|
Note that they can read nothing if the fractional part is zero or
|
||||||
the next character is not `.` however will print with the specified length.
|
the next character is not `.` however will print with the specified length.
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
The variant `%3f`, `%6f` and `%9f` are left-aligned and print 3, 6 or 9 fractional digits
|
The variant `%3f`, `%6f` and `%9f` are left-aligned and print 3, 6 or 9 fractional digits
|
||||||
according to the number preceding `f`, but without the leading dot.
|
according to the number preceding `f`, but without the leading dot.
|
||||||
E.g. 70ms after the last second under `%3f` will print `070` (note: not `07`),
|
E.g. 70ms after the last second under `%3f` will print `070` (note: not `07`),
|
||||||
|
|
149
src/lib.rs
149
src/lib.rs
|
@ -59,7 +59,7 @@
|
||||||
//! Chrono currently uses
|
//! Chrono currently uses
|
||||||
//! the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type
|
//! the [`time::Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type
|
||||||
//! from the `time` crate to represent the magnitude of a time span.
|
//! from the `time` crate to represent the magnitude of a time span.
|
||||||
//! Since this has the same name as the newer, standard type for duration,
|
//! Since this has the same name to the newer, standard type for duration,
|
||||||
//! the reference will refer this type as `OldDuration`.
|
//! the reference will refer this type as `OldDuration`.
|
||||||
//! Note that this is an "accurate" duration represented as seconds and
|
//! Note that this is an "accurate" duration represented as seconds and
|
||||||
//! nanoseconds and does not represent "nominal" components such as days or
|
//! nanoseconds and does not represent "nominal" components such as days or
|
||||||
|
@ -383,12 +383,9 @@
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/chrono/latest/")]
|
#![doc(html_root_url = "https://docs.rs/chrono/latest/")]
|
||||||
|
|
||||||
#![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507
|
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
#![deny(missing_debug_implementations)]
|
#![deny(missing_debug_implementations)]
|
||||||
#![deny(dead_code)]
|
|
||||||
|
|
||||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
|
||||||
|
|
||||||
// The explicit 'static lifetimes are still needed for rustc 1.13-16
|
// The explicit 'static lifetimes are still needed for rustc 1.13-16
|
||||||
// backward compatibility, and this appeases clippy. If minimum rustc
|
// backward compatibility, and this appeases clippy. If minimum rustc
|
||||||
|
@ -406,13 +403,6 @@
|
||||||
trivially_copy_pass_by_ref,
|
trivially_copy_pass_by_ref,
|
||||||
))]
|
))]
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
extern crate alloc;
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
extern crate std as core;
|
|
||||||
#[cfg(all(feature = "std", not(feature="alloc")))]
|
|
||||||
extern crate std as alloc;
|
|
||||||
|
|
||||||
#[cfg(feature="clock")]
|
#[cfg(feature="clock")]
|
||||||
extern crate time as oldtime;
|
extern crate time as oldtime;
|
||||||
extern crate num_integer;
|
extern crate num_integer;
|
||||||
|
@ -424,12 +414,10 @@ extern crate serde as serdelib;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate doc_comment;
|
extern crate doc_comment;
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
|
||||||
extern crate wasm_bindgen;
|
extern crate wasm_bindgen;
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
|
||||||
extern crate js_sys;
|
extern crate js_sys;
|
||||||
#[cfg(feature = "bench")]
|
|
||||||
extern crate test;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
doctest!("../README.md");
|
doctest!("../README.md");
|
||||||
|
@ -491,10 +479,6 @@ pub mod naive {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub use self::datetime::rustc_serialize::TsSeconds;
|
pub use self::datetime::rustc_serialize::TsSeconds;
|
||||||
|
|
||||||
#[cfg(feature = "__internal_bench")]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use self::internals::YearFlags as __BenchYearFlags;
|
|
||||||
|
|
||||||
|
|
||||||
/// Serialization/Deserialization of naive types in alternate formats
|
/// Serialization/Deserialization of naive types in alternate formats
|
||||||
///
|
///
|
||||||
|
@ -514,10 +498,6 @@ mod datetime;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
mod round;
|
mod round;
|
||||||
|
|
||||||
#[cfg(feature = "__internal_bench")]
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use naive::__BenchYearFlags;
|
|
||||||
|
|
||||||
/// Serialization/Deserialization in alternate formats
|
/// Serialization/Deserialization in alternate formats
|
||||||
///
|
///
|
||||||
/// The various modules in here are intended to be used with serde's [`with`
|
/// The various modules in here are intended to be used with serde's [`with`
|
||||||
|
@ -531,41 +511,6 @@ pub mod serde {
|
||||||
pub use super::datetime::serde::*;
|
pub use super::datetime::serde::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Until rust 1.18 there is no "pub(crate)" so to share this we need it in the root
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
enum SerdeError<V: fmt::Display, D: fmt::Display> {
|
|
||||||
NonExistent { timestamp: V },
|
|
||||||
Ambiguous { timestamp: V, min: D, max: D },
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct a [`SerdeError::NonExistent`]
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
fn ne_timestamp<T: fmt::Display>(ts: T) -> SerdeError<T, u8> {
|
|
||||||
SerdeError::NonExistent::<T, u8> { timestamp: ts }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<V: fmt::Display, D: fmt::Display> fmt::Debug for SerdeError<V, D> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "ChronoSerdeError({})", self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl<V: fmt::Display, D: fmt::Debug> core::error::Error for SerdeError<V, D> {}
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<V: fmt::Display, D: fmt::Display> fmt::Display for SerdeError<V, D> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
&SerdeError::NonExistent { ref timestamp } => write!(
|
|
||||||
f, "value is not a legal timestamp: {}", timestamp),
|
|
||||||
&SerdeError::Ambiguous { ref timestamp, ref min, ref max } => write!(
|
|
||||||
f, "value is an ambiguous timestamp: {}, could be either of {}, {}",
|
|
||||||
timestamp, min, max),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The day of week.
|
/// The day of week.
|
||||||
///
|
///
|
||||||
/// The order of the days of week depends on the context.
|
/// The order of the days of week depends on the context.
|
||||||
|
@ -700,20 +645,6 @@ impl Weekday {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Weekday {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str(match *self {
|
|
||||||
Weekday::Mon => "Mon",
|
|
||||||
Weekday::Tue => "Tue",
|
|
||||||
Weekday::Wed => "Wed",
|
|
||||||
Weekday::Thu => "Thu",
|
|
||||||
Weekday::Fri => "Fri",
|
|
||||||
Weekday::Sat => "Sat",
|
|
||||||
Weekday::Sun => "Sun",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Any weekday can be represented as an integer from 0 to 6, which equals to
|
/// Any weekday can be represented as an integer from 0 to 6, which equals to
|
||||||
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
|
/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation.
|
||||||
/// Do not heavily depend on this though; use explicit methods whenever possible.
|
/// Do not heavily depend on this though; use explicit methods whenever possible.
|
||||||
|
@ -747,7 +678,7 @@ impl num_traits::FromPrimitive for Weekday {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// An error resulting from reading `Weekday` value with `FromStr`.
|
/// An error resulting from reading `Weekday` value with `FromStr`.
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
|
@ -766,14 +697,14 @@ impl fmt::Debug for ParseWeekdayError {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod weekday_serde {
|
mod weekday_serde {
|
||||||
use super::Weekday;
|
use super::Weekday;
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
impl ser::Serialize for Weekday {
|
impl ser::Serialize for Weekday {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: ser::Serializer
|
where S: ser::Serializer
|
||||||
{
|
{
|
||||||
serializer.collect_str(&self)
|
serializer.serialize_str(&format!("{:?}", self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,22 +888,17 @@ pub trait Datelike: Sized {
|
||||||
/// Returns `None` when the resulting value would be invalid.
|
/// Returns `None` when the resulting value would be invalid.
|
||||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>;
|
fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>;
|
||||||
|
|
||||||
/// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1.
|
/// Returns the number of days since January 1, Year 1 (aka Day 1) in the
|
||||||
|
/// proleptic Gregorian calendar.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Example:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ~~~
|
||||||
/// use chrono::{NaiveDate, Datelike};
|
/// use chrono::{NaiveDate, Datelike};
|
||||||
///
|
/// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719163);
|
||||||
/// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719_163);
|
|
||||||
/// assert_eq!(NaiveDate::from_ymd(2, 1, 1).num_days_from_ce(), 366);
|
|
||||||
/// assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1);
|
|
||||||
/// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365);
|
/// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365);
|
||||||
/// ```
|
/// ~~~
|
||||||
fn num_days_from_ce(&self) -> i32 {
|
fn num_days_from_ce(&self) -> i32 {
|
||||||
// See test_num_days_from_ce_against_alternative_impl below for a more straightforward
|
|
||||||
// implementation.
|
|
||||||
|
|
||||||
// we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range.
|
// we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range.
|
||||||
let mut year = self.year() - 1;
|
let mut year = self.year() - 1;
|
||||||
let mut ndays = 0;
|
let mut ndays = 0;
|
||||||
|
@ -1074,52 +1000,3 @@ fn test_readme_doomsday() {
|
||||||
assert!(other_dates.iter().all(|d| d.weekday() == weekday));
|
assert!(other_dates.iter().all(|d| d.weekday() == weekday));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests `Datelike::num_days_from_ce` against an alternative implementation.
|
|
||||||
///
|
|
||||||
/// The alternative implementation is not as short as the current one but it is simpler to
|
|
||||||
/// understand, with less unexplained magic constants.
|
|
||||||
#[test]
|
|
||||||
fn test_num_days_from_ce_against_alternative_impl() {
|
|
||||||
/// Returns the number of multiples of `div` in the range `start..end`.
|
|
||||||
///
|
|
||||||
/// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
|
|
||||||
/// behaviour is defined by the following equation:
|
|
||||||
/// `in_between(start, end, div) == - in_between(end, start, div)`.
|
|
||||||
///
|
|
||||||
/// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `div` is not positive.
|
|
||||||
fn in_between(start: i32, end: i32, div: i32) -> i32 {
|
|
||||||
assert!(div > 0, "in_between: nonpositive div = {}", div);
|
|
||||||
let start = (start.div_euclid(div), start.rem_euclid(div));
|
|
||||||
let end = ( end.div_euclid(div), end.rem_euclid(div));
|
|
||||||
// The lowest multiple of `div` greater than or equal to `start`, divided.
|
|
||||||
let start = start.0 + (start.1 != 0) as i32;
|
|
||||||
// The lowest multiple of `div` greater than or equal to `end`, divided.
|
|
||||||
let end = end.0 + ( end.1 != 0) as i32;
|
|
||||||
end - start
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Alternative implementation to `Datelike::num_days_from_ce`
|
|
||||||
fn num_days_from_ce<Date: Datelike>(date: &Date) -> i32 {
|
|
||||||
let year = date.year();
|
|
||||||
let diff = move |div| in_between(1, year, div);
|
|
||||||
// 365 days a year, one more in leap years. In the gregorian calendar, leap years are all
|
|
||||||
// the multiples of 4 except multiples of 100 but including multiples of 400.
|
|
||||||
date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
|
|
||||||
}
|
|
||||||
|
|
||||||
use num_iter::range_inclusive;
|
|
||||||
|
|
||||||
for year in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) {
|
|
||||||
let jan1_year = NaiveDate::from_ymd(year, 1, 1);
|
|
||||||
assert_eq!(jan1_year.num_days_from_ce(), num_days_from_ce(&jan1_year),
|
|
||||||
"on {:?}", jan1_year);
|
|
||||||
let mid_year = jan1_year + Duration::days(133);
|
|
||||||
assert_eq!(mid_year.num_days_from_ce(), num_days_from_ce(&mid_year),
|
|
||||||
"on {:?}", mid_year);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
|
|
||||||
//! ISO 8601 calendar date without timezone.
|
//! ISO 8601 calendar date without timezone.
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
use std::{str, fmt};
|
||||||
use core::borrow::Borrow;
|
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||||
use core::{str, fmt};
|
|
||||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
|
@ -14,9 +12,7 @@ use {Weekday, Datelike};
|
||||||
use div::div_mod_floor;
|
use div::div_mod_floor;
|
||||||
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
||||||
use format::{Item, Numeric, Pad};
|
use format::{Item, Numeric, Pad};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use format::DelayedFormat;
|
|
||||||
|
|
||||||
use super::isoweek;
|
use super::isoweek;
|
||||||
use super::internals::{self, DateImpl, Of, Mdf, YearFlags};
|
use super::internals::{self, DateImpl, Of, Mdf, YearFlags};
|
||||||
|
@ -94,7 +90,7 @@ const MAX_BITS: usize = 44;
|
||||||
///
|
///
|
||||||
/// The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal").
|
/// The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal").
|
||||||
/// The ordinal number ranges from 1 to 365 or 366 depending on the year.
|
/// The ordinal number ranges from 1 to 365 or 366 depending on the year.
|
||||||
/// The year number is the same as that of the [calendar date](#calendar-date).
|
/// The year number is same to that of the [calendar date](#calendar-date).
|
||||||
///
|
///
|
||||||
/// This is currently the internal format of Chrono's date types.
|
/// This is currently the internal format of Chrono's date types.
|
||||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
|
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
|
||||||
|
@ -334,10 +330,10 @@ impl NaiveDate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
|
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (Day 1)
|
||||||
/// January 1, 1 being day 1.
|
/// in the proleptic Gregorian calendar.
|
||||||
///
|
///
|
||||||
/// Panics if the date is out of range.
|
/// Panics on the out-of-range date.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -382,10 +378,10 @@ impl NaiveDate {
|
||||||
NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date")
|
NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
|
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (Day 1)
|
||||||
/// January 1, 1 being day 1.
|
/// in the proleptic Gregorian calendar.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the date is out of range.
|
/// Returns `None` on the out-of-range date.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -411,55 +407,6 @@ impl NaiveDate {
|
||||||
Of::new(ordinal, flags))
|
Of::new(ordinal, flags))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
|
|
||||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
|
||||||
/// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// The resulting `NaiveDate` is guaranteed to be in `month`. If `n` is larger than the number
|
|
||||||
/// of `weekday` in `month` (eg. the 6th Friday of March 2017) then this function will panic.
|
|
||||||
///
|
|
||||||
/// `n` is 1-indexed. Passing `n=0` will cause a panic.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ~~~~
|
|
||||||
/// use chrono::{NaiveDate, Weekday};
|
|
||||||
///
|
|
||||||
/// let from_weekday_of_month = NaiveDate::from_weekday_of_month;
|
|
||||||
/// let from_ymd = NaiveDate::from_ymd;
|
|
||||||
///
|
|
||||||
/// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Wed, 1), from_ymd(2018, 8, 1));
|
|
||||||
/// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 1), from_ymd(2018, 8, 3));
|
|
||||||
/// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Tue, 2), from_ymd(2018, 8, 14));
|
|
||||||
/// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 4), from_ymd(2018, 8, 24));
|
|
||||||
/// assert_eq!(from_weekday_of_month(2018, 8, Weekday::Fri, 5), from_ymd(2018, 8, 31));
|
|
||||||
/// ~~~~
|
|
||||||
pub fn from_weekday_of_month(year: i32, month: u32, weekday: Weekday, n: u8) -> NaiveDate {
|
|
||||||
NaiveDate::from_weekday_of_month_opt(year, month, weekday, n).expect("out-of-range date")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
|
|
||||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
|
||||||
/// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. `n` is 1-indexed.
|
|
||||||
///
|
|
||||||
/// ~~~~
|
|
||||||
/// use chrono::{NaiveDate, Weekday};
|
|
||||||
/// assert_eq!(NaiveDate::from_weekday_of_month_opt(2017, 3, Weekday::Fri, 2),
|
|
||||||
/// NaiveDate::from_ymd_opt(2017, 3, 10))
|
|
||||||
/// ~~~~
|
|
||||||
///
|
|
||||||
/// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in
|
|
||||||
/// `month` (eg. the 6th Friday of March 2017), or if `n == 0`.
|
|
||||||
pub fn from_weekday_of_month_opt(year: i32, month: u32, weekday: Weekday, n: u8) -> Option<NaiveDate> {
|
|
||||||
if n == 0 { return None; }
|
|
||||||
let first = NaiveDate::from_ymd(year, month, 1).weekday();
|
|
||||||
let first_to_dow = (7 + weekday.number_from_monday() - first.number_from_monday()) % 7;
|
|
||||||
let day = (u32::from(n) - 1) * 7 + first_to_dow + 1;
|
|
||||||
NaiveDate::from_ymd_opt(year, month, day)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses a string with the specified format string and returns a new `NaiveDate`.
|
/// Parses a string with the specified format string and returns a new `NaiveDate`.
|
||||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
/// See the [`format::strftime` module](../format/strftime/index.html)
|
||||||
/// on the supported escape sequences.
|
/// on the supported escape sequences.
|
||||||
|
@ -504,7 +451,7 @@ impl NaiveDate {
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
|
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||||
parsed.to_naive_date()
|
parsed.to_naive_date()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,7 +890,7 @@ impl NaiveDate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the date with the specified formatting items.
|
/// Formats the date with the specified formatting items.
|
||||||
/// Otherwise it is the same as the ordinary `format` method.
|
/// Otherwise it is same to the ordinary `format` method.
|
||||||
///
|
///
|
||||||
/// The `Iterator` of items should be `Clone`able,
|
/// The `Iterator` of items should be `Clone`able,
|
||||||
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
||||||
|
@ -969,10 +916,9 @@ impl NaiveDate {
|
||||||
/// # let d = NaiveDate::from_ymd(2015, 9, 5);
|
/// # let d = NaiveDate::from_ymd(2015, 9, 5);
|
||||||
/// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05");
|
/// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> + Clone {
|
||||||
DelayedFormat::new(Some(*self), None, items)
|
DelayedFormat::new(Some(*self), None, items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1008,7 +954,6 @@ impl NaiveDate {
|
||||||
/// assert_eq!(format!("{}", d.format("%Y-%m-%d")), "2015-09-05");
|
/// assert_eq!(format!("{}", d.format("%Y-%m-%d")), "2015-09-05");
|
||||||
/// assert_eq!(format!("{}", d.format("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015");
|
/// assert_eq!(format!("{}", d.format("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
self.format_with_items(StrftimeItems::new(fmt))
|
self.format_with_items(StrftimeItems::new(fmt))
|
||||||
|
@ -1400,7 +1345,7 @@ impl AddAssign<OldDuration> for NaiveDate {
|
||||||
|
|
||||||
/// A subtraction of `Duration` from `NaiveDate` discards the fractional days,
|
/// A subtraction of `Duration` from `NaiveDate` discards the fractional days,
|
||||||
/// rounding to the closest integral number of days towards `Duration::zero()`.
|
/// rounding to the closest integral number of days towards `Duration::zero()`.
|
||||||
/// It is the same as the addition with a negated `Duration`.
|
/// It is same to the addition with a negated `Duration`.
|
||||||
///
|
///
|
||||||
/// Panics on underflow or overflow.
|
/// Panics on underflow or overflow.
|
||||||
/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that.
|
/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that.
|
||||||
|
@ -1476,7 +1421,7 @@ impl Sub<NaiveDate> for NaiveDate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Debug` output of the naive date `d` is the same as
|
/// The `Debug` output of the naive date `d` is same to
|
||||||
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
|
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||||
|
@ -1511,7 +1456,7 @@ impl fmt::Debug for NaiveDate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Display` output of the naive date `d` is the same as
|
/// The `Display` output of the naive date `d` is same to
|
||||||
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
|
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||||
|
@ -1558,16 +1503,16 @@ impl str::FromStr for NaiveDate {
|
||||||
|
|
||||||
fn from_str(s: &str) -> ParseResult<NaiveDate> {
|
fn from_str(s: &str) -> ParseResult<NaiveDate> {
|
||||||
const ITEMS: &'static [Item<'static>] = &[
|
const ITEMS: &'static [Item<'static>] = &[
|
||||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal("-"),
|
Item::Space(""), Item::Literal("-"),
|
||||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal("-"),
|
Item::Space(""), Item::Literal("-"),
|
||||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
|
||||||
Item::Space(""),
|
Item::Space(""),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, ITEMS.iter())?;
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
parsed.to_naive_date()
|
parsed.to_naive_date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1655,7 +1600,7 @@ mod rustc_serialize {
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde {
|
mod serde {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use super::NaiveDate;
|
use super::NaiveDate;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
@ -1689,18 +1634,10 @@ mod serde {
|
||||||
write!(formatter, "a formatted date string")
|
write!(formatter, "a formatted date string")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E>
|
fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
value.parse().map_err(E::custom)
|
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "std", test)))]
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E>
|
|
||||||
where E: de::Error
|
|
||||||
{
|
|
||||||
value.parse().map_err(E::custom)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1886,24 +1823,6 @@ mod tests {
|
||||||
assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce() + 1), None);
|
assert_eq!(from_ndays_from_ce(MAX_DATE.num_days_from_ce() + 1), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_date_from_weekday_of_month_opt() {
|
|
||||||
let ymwd = |y,m,w,n| NaiveDate::from_weekday_of_month_opt(y,m,w,n);
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None);
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd(2018, 8, 1)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd(2018, 8, 2)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd(2018, 8, 5)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd(2018, 8, 6)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd(2018, 8, 7)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd(2018, 8, 8)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd(2018, 8, 12)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd(2018, 8, 16)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd(2018, 8, 23)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd(2018, 8, 30)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd(2018, 8, 31)));
|
|
||||||
assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_date_fields() {
|
fn test_date_fields() {
|
||||||
fn check(year: i32, month: u32, day: u32, ordinal: u32) {
|
fn check(year: i32, month: u32, day: u32, ordinal: u32) {
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
|
|
||||||
//! ISO 8601 date and time without timezone.
|
//! ISO 8601 date and time without timezone.
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
use std::{str, fmt, hash};
|
||||||
use core::borrow::Borrow;
|
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||||
use core::{str, fmt, hash};
|
|
||||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
|
@ -14,9 +12,7 @@ use {Weekday, Timelike, Datelike};
|
||||||
use div::div_mod_floor;
|
use div::div_mod_floor;
|
||||||
use naive::{NaiveTime, NaiveDate, IsoWeek};
|
use naive::{NaiveTime, NaiveDate, IsoWeek};
|
||||||
use format::{Item, Numeric, Pad, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use format::DelayedFormat;
|
|
||||||
|
|
||||||
/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS`
|
/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS`
|
||||||
/// will always overflow the addition with any date and time type.
|
/// will always overflow the addition with any date and time type.
|
||||||
|
@ -210,7 +206,7 @@ impl NaiveDateTime {
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
|
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||||
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
|
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,7 +313,7 @@ impl NaiveDateTime {
|
||||||
/// 2262-04-11T23:47:16.854775804.
|
/// 2262-04-11T23:47:16.854775804.
|
||||||
///
|
///
|
||||||
/// (If this is a problem, please file an issue to let me know what domain
|
/// (If this is a problem, please file an issue to let me know what domain
|
||||||
/// needs nanosecond precision over millennia, I'm curious.)
|
/// needs nanosecond precision over millenia, I'm curious.)
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -623,7 +619,7 @@ impl NaiveDateTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the combined date and time with the specified formatting items.
|
/// Formats the combined date and time with the specified formatting items.
|
||||||
/// Otherwise it is the same as the ordinary [`format`](#method.format) method.
|
/// Otherwise it is same to the ordinary [`format`](#method.format) method.
|
||||||
///
|
///
|
||||||
/// The `Iterator` of items should be `Clone`able,
|
/// The `Iterator` of items should be `Clone`able,
|
||||||
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
||||||
|
@ -649,10 +645,9 @@ impl NaiveDateTime {
|
||||||
/// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4);
|
/// # let dt = NaiveDate::from_ymd(2015, 9, 5).and_hms(23, 56, 4);
|
||||||
/// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04");
|
/// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> + Clone {
|
||||||
DelayedFormat::new(Some(self.date), Some(self.time), items)
|
DelayedFormat::new(Some(self.date), Some(self.time), items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,7 +683,6 @@ impl NaiveDateTime {
|
||||||
/// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04");
|
/// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04");
|
||||||
/// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5");
|
/// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
self.format_with_items(StrftimeItems::new(fmt))
|
self.format_with_items(StrftimeItems::new(fmt))
|
||||||
|
@ -1265,7 +1259,7 @@ impl AddAssign<OldDuration> for NaiveDateTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`.
|
/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`.
|
||||||
/// It is the same as the addition with a negated `Duration`.
|
/// It is same to the addition with a negated `Duration`.
|
||||||
///
|
///
|
||||||
/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling),
|
/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling),
|
||||||
/// the addition assumes that **there is no leap second ever**,
|
/// the addition assumes that **there is no leap second ever**,
|
||||||
|
@ -1389,7 +1383,7 @@ impl Sub<NaiveDateTime> for NaiveDateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Debug` output of the naive date and time `dt` is the same as
|
/// The `Debug` output of the naive date and time `dt` is same to
|
||||||
/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../format/strftime/index.html).
|
/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||||
|
@ -1422,7 +1416,7 @@ impl fmt::Debug for NaiveDateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Debug` output of the naive date and time `dt` is the same as
|
/// The `Debug` output of the naive date and time `dt` is same to
|
||||||
/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../format/strftime/index.html).
|
/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// It should be noted that, for leap seconds not on the minute boundary,
|
/// It should be noted that, for leap seconds not on the minute boundary,
|
||||||
|
@ -1474,22 +1468,22 @@ impl str::FromStr for NaiveDateTime {
|
||||||
|
|
||||||
fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
|
fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
|
||||||
const ITEMS: &'static [Item<'static>] = &[
|
const ITEMS: &'static [Item<'static>] = &[
|
||||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal("-"),
|
Item::Space(""), Item::Literal("-"),
|
||||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal("-"),
|
Item::Space(""), Item::Literal("-"),
|
||||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal("T"), // XXX shouldn't this be case-insensitive?
|
Item::Space(""), Item::Literal("T"), // XXX shouldn't this be case-insensitive?
|
||||||
Item::Numeric(Numeric::Hour, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal(":"),
|
Item::Space(""), Item::Literal(":"),
|
||||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal(":"),
|
Item::Space(""), Item::Literal(":"),
|
||||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
|
||||||
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, ITEMS.iter())?;
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
parsed.to_naive_datetime_with_offset(0)
|
parsed.to_naive_datetime_with_offset(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1669,7 +1663,7 @@ pub mod rustc_serialize {
|
||||||
/// Tools to help serializing/deserializing `NaiveDateTime`s
|
/// Tools to help serializing/deserializing `NaiveDateTime`s
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
pub mod serde {
|
pub mod serde {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use super::{NaiveDateTime};
|
use super::{NaiveDateTime};
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
@ -1708,7 +1702,7 @@ pub mod serde {
|
||||||
fn visit_str<E>(self, value: &str) -> Result<NaiveDateTime, E>
|
fn visit_str<E>(self, value: &str) -> Result<NaiveDateTime, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
value.parse().map_err(E::custom)
|
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1756,10 +1750,10 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_nanoseconds {
|
pub mod ts_nanoseconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {NaiveDateTime, ne_timestamp};
|
use NaiveDateTime;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
|
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -1834,7 +1828,7 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)?)
|
Ok(try!(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NaiveDateTimeFromNanoSecondsVisitor;
|
struct NaiveDateTimeFromNanoSecondsVisitor;
|
||||||
|
@ -1852,7 +1846,7 @@ pub mod serde {
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt(value / 1_000_000_000,
|
NaiveDateTime::from_timestamp_opt(value / 1_000_000_000,
|
||||||
(value % 1_000_000_000) as u32)
|
(value % 1_000_000_000) as u32)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
||||||
|
@ -1860,7 +1854,7 @@ pub mod serde {
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt(value as i64 / 1_000_000_000,
|
NaiveDateTime::from_timestamp_opt(value as i64 / 1_000_000_000,
|
||||||
(value as i64 % 1_000_000_000) as u32)
|
(value as i64 % 1_000_000_000) as u32)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1901,10 +1895,10 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_milliseconds {
|
pub mod ts_milliseconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {NaiveDateTime, ne_timestamp};
|
use NaiveDateTime;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
|
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -1979,7 +1973,7 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)?)
|
Ok(try!(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NaiveDateTimeFromMilliSecondsVisitor;
|
struct NaiveDateTimeFromMilliSecondsVisitor;
|
||||||
|
@ -1997,7 +1991,7 @@ pub mod serde {
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt(value / 1000,
|
NaiveDateTime::from_timestamp_opt(value / 1000,
|
||||||
((value % 1000) * 1_000_000) as u32)
|
((value % 1000) * 1_000_000) as u32)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
||||||
|
@ -2005,7 +1999,7 @@ pub mod serde {
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt((value / 1000) as i64,
|
NaiveDateTime::from_timestamp_opt((value / 1000) as i64,
|
||||||
((value % 1000) * 1_000_000) as u32)
|
((value % 1000) * 1_000_000) as u32)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2046,10 +2040,10 @@ pub mod serde {
|
||||||
/// # fn main() { example().unwrap(); }
|
/// # fn main() { example().unwrap(); }
|
||||||
/// ```
|
/// ```
|
||||||
pub mod ts_seconds {
|
pub mod ts_seconds {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
use {NaiveDateTime, ne_timestamp};
|
use NaiveDateTime;
|
||||||
|
|
||||||
/// Serialize a UTC datetime into an integer number of seconds since the epoch
|
/// Serialize a UTC datetime into an integer number of seconds since the epoch
|
||||||
///
|
///
|
||||||
|
@ -2124,7 +2118,7 @@ pub mod serde {
|
||||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||||
where D: de::Deserializer<'de>
|
where D: de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
Ok(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)?)
|
Ok(try!(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NaiveDateTimeFromSecondsVisitor;
|
struct NaiveDateTimeFromSecondsVisitor;
|
||||||
|
@ -2141,14 +2135,14 @@ pub mod serde {
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt(value, 0)
|
NaiveDateTime::from_timestamp_opt(value, 0)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
NaiveDateTime::from_timestamp_opt(value as i64, 0)
|
NaiveDateTime::from_timestamp_opt(value as i64, 0)
|
||||||
.ok_or_else(|| E::custom(ne_timestamp(value)))
|
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,8 @@
|
||||||
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
|
//! 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
|
#![allow(dead_code)] // some internal methods have been left for consistency
|
||||||
#![cfg_attr(feature = "__internal_bench", allow(missing_docs))]
|
|
||||||
|
|
||||||
use core::{i32, fmt};
|
use std::{i32, fmt};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use Weekday;
|
use Weekday;
|
||||||
use div::{div_rem, mod_floor};
|
use div::{div_rem, mod_floor};
|
||||||
|
@ -471,6 +470,7 @@ impl fmt::Debug for Mdf {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[cfg(test)] extern crate num_iter;
|
#[cfg(test)] extern crate num_iter;
|
||||||
|
#[cfg(bench)] extern crate test;
|
||||||
|
|
||||||
use Weekday;
|
use Weekday;
|
||||||
use super::{Of, Mdf};
|
use super::{Of, Mdf};
|
||||||
|
@ -517,6 +517,16 @@ mod tests {
|
||||||
assert_eq!(GF.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]
|
#[test]
|
||||||
fn test_of() {
|
fn test_of() {
|
||||||
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
//! ISO 8601 week.
|
//! ISO 8601 week.
|
||||||
|
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use super::internals::{DateImpl, Of, YearFlags};
|
use super::internals::{DateImpl, Of, YearFlags};
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ impl IsoWeek {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Debug` output of the ISO week `w` is the same as
|
/// The `Debug` output of the ISO week `w` is same to
|
||||||
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
|
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
|
||||||
/// where `d` is any `NaiveDate` value in that week.
|
/// where `d` is any `NaiveDate` value in that week.
|
||||||
///
|
///
|
||||||
|
|
|
@ -3,18 +3,14 @@
|
||||||
|
|
||||||
//! ISO 8601 time without timezone.
|
//! ISO 8601 time without timezone.
|
||||||
|
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
use std::{str, fmt, hash};
|
||||||
use core::borrow::Borrow;
|
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||||
use core::{str, fmt, hash};
|
|
||||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
use div::div_mod_floor;
|
use div::div_mod_floor;
|
||||||
use format::{Item, Numeric, Pad, Fixed};
|
use format::{Item, Numeric, Pad, Fixed};
|
||||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
use format::DelayedFormat;
|
|
||||||
|
|
||||||
/// ISO 8601 time without timezone.
|
/// ISO 8601 time without timezone.
|
||||||
/// Allows for the nanosecond precision and optional leap second representation.
|
/// Allows for the nanosecond precision and optional leap second representation.
|
||||||
|
@ -496,7 +492,7 @@ impl NaiveTime {
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
|
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||||
parsed.to_naive_time()
|
parsed.to_naive_time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -685,7 +681,7 @@ impl NaiveTime {
|
||||||
// `rhs.frac`|========================================>|
|
// `rhs.frac`|========================================>|
|
||||||
// | | | `self - rhs` | |
|
// | | | `self - rhs` | |
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
let secs = i64::from(self.secs) - i64::from(rhs.secs);
|
let secs = i64::from(self.secs) - i64::from(rhs.secs);
|
||||||
let frac = i64::from(self.frac) - i64::from(rhs.frac);
|
let frac = i64::from(self.frac) - i64::from(rhs.frac);
|
||||||
|
@ -701,7 +697,7 @@ impl NaiveTime {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the time with the specified formatting items.
|
/// Formats the time with the specified formatting items.
|
||||||
/// Otherwise it is the same as the ordinary [`format`](#method.format) method.
|
/// Otherwise it is same to the ordinary [`format`](#method.format) method.
|
||||||
///
|
///
|
||||||
/// The `Iterator` of items should be `Clone`able,
|
/// The `Iterator` of items should be `Clone`able,
|
||||||
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
||||||
|
@ -727,10 +723,9 @@ impl NaiveTime {
|
||||||
/// # let t = NaiveTime::from_hms(23, 56, 4);
|
/// # let t = NaiveTime::from_hms(23, 56, 4);
|
||||||
/// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04");
|
/// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
where I: Iterator<Item=Item<'a>> + Clone {
|
||||||
DelayedFormat::new(None, Some(*self), items)
|
DelayedFormat::new(None, Some(*self), items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -768,7 +763,6 @@ impl NaiveTime {
|
||||||
/// assert_eq!(format!("{}", t.format("%H:%M:%S%.6f")), "23:56:04.012345");
|
/// assert_eq!(format!("{}", t.format("%H:%M:%S%.6f")), "23:56:04.012345");
|
||||||
/// assert_eq!(format!("{}", t.format("%-I:%M %p")), "11:56 PM");
|
/// assert_eq!(format!("{}", t.format("%-I:%M %p")), "11:56 PM");
|
||||||
/// ~~~~
|
/// ~~~~
|
||||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||||
self.format_with_items(StrftimeItems::new(fmt))
|
self.format_with_items(StrftimeItems::new(fmt))
|
||||||
|
@ -1071,7 +1065,7 @@ impl AddAssign<OldDuration> for NaiveTime {
|
||||||
|
|
||||||
/// A subtraction of `Duration` from `NaiveTime` wraps around and never overflows or underflows.
|
/// A subtraction of `Duration` from `NaiveTime` wraps around and never overflows or underflows.
|
||||||
/// In particular the addition ignores integral number of days.
|
/// In particular the addition ignores integral number of days.
|
||||||
/// It is the same as the addition with a negated `Duration`.
|
/// It is same to the addition with a negated `Duration`.
|
||||||
///
|
///
|
||||||
/// As a part of Chrono's [leap second handling](#leap-second-handling),
|
/// As a part of Chrono's [leap second handling](#leap-second-handling),
|
||||||
/// the addition assumes that **there is no leap second ever**,
|
/// the addition assumes that **there is no leap second ever**,
|
||||||
|
@ -1199,7 +1193,7 @@ impl Sub<NaiveTime> for NaiveTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Debug` output of the naive time `t` is the same as
|
/// The `Debug` output of the naive time `t` is same to
|
||||||
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
|
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||||
|
@ -1236,7 +1230,7 @@ impl fmt::Debug for NaiveTime {
|
||||||
(sec, self.frac)
|
(sec, self.frac)
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?;
|
try!(write!(f, "{:02}:{:02}:{:02}", hour, min, sec));
|
||||||
if nano == 0 {
|
if nano == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if nano % 1_000_000 == 0 {
|
} else if nano % 1_000_000 == 0 {
|
||||||
|
@ -1249,7 +1243,7 @@ impl fmt::Debug for NaiveTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Display` output of the naive time `t` is the same as
|
/// The `Display` output of the naive time `t` is same to
|
||||||
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
|
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// The string printed can be readily parsed via the `parse` method on `str`.
|
/// The string printed can be readily parsed via the `parse` method on `str`.
|
||||||
|
@ -1305,16 +1299,16 @@ impl str::FromStr for NaiveTime {
|
||||||
|
|
||||||
fn from_str(s: &str) -> ParseResult<NaiveTime> {
|
fn from_str(s: &str) -> ParseResult<NaiveTime> {
|
||||||
const ITEMS: &'static [Item<'static>] = &[
|
const ITEMS: &'static [Item<'static>] = &[
|
||||||
Item::Numeric(Numeric::Hour, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal(":"),
|
Item::Space(""), Item::Literal(":"),
|
||||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||||
Item::Space(""), Item::Literal(":"),
|
Item::Space(""), Item::Literal(":"),
|
||||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
|
||||||
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, ITEMS.iter())?;
|
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||||
parsed.to_naive_time()
|
parsed.to_naive_time()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1417,7 +1411,7 @@ mod rustc_serialize {
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod serde {
|
mod serde {
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use super::NaiveTime;
|
use super::NaiveTime;
|
||||||
use serdelib::{ser, de};
|
use serdelib::{ser, de};
|
||||||
|
|
||||||
|
@ -1445,7 +1439,7 @@ mod serde {
|
||||||
fn visit_str<E>(self, value: &str) -> Result<NaiveTime, E>
|
fn visit_str<E>(self, value: &str) -> Result<NaiveTime, E>
|
||||||
where E: de::Error
|
where E: de::Error
|
||||||
{
|
{
|
||||||
value.parse().map_err(E::custom)
|
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
//! The time zone which has a fixed offset from UTC.
|
//! The time zone which has a fixed offset from UTC.
|
||||||
|
|
||||||
use core::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use oldtime::Duration as OldDuration;
|
use oldtime::Duration as OldDuration;
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
|
@ -136,7 +136,7 @@ impl fmt::Display for FixedOffset {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// addition or subtraction of FixedOffset to/from Timelike values is the same as
|
// addition or subtraction of FixedOffset to/from Timelike values is same to
|
||||||
// adding or subtracting the offset's local_minus_utc value
|
// adding or subtracting the offset's local_minus_utc value
|
||||||
// but keep keeps the leap second information.
|
// but keep keeps the leap second information.
|
||||||
// this should be implemented more efficiently, but for the time being, this is generic right now.
|
// this should be implemented more efficiently, but for the time being, this is generic right now.
|
||||||
|
|
|
@ -87,13 +87,13 @@ impl Local {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
#[cfg(not(all(target_arch = "wasm32", feature = "wasmbind")))]
|
||||||
pub fn now() -> DateTime<Local> {
|
pub fn now() -> DateTime<Local> {
|
||||||
tm_to_datetime(oldtime::now())
|
tm_to_datetime(oldtime::now())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
#[cfg(all(target_arch = "wasm32", feature = "wasmbind"))]
|
||||||
pub fn now() -> DateTime<Local> {
|
pub fn now() -> DateTime<Local> {
|
||||||
use super::Utc;
|
use super::Utc;
|
||||||
let now: DateTime<Utc> = super::Utc::now();
|
let now: DateTime<Utc> = super::Utc::now();
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
//! and provides implementations for 1 and 3.
|
//! and provides implementations for 1 and 3.
|
||||||
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
||||||
|
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use format::{parse, ParseResult, Parsed, StrftimeItems};
|
use format::{parse, ParseResult, Parsed, StrftimeItems};
|
||||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||||
|
@ -415,7 +415,7 @@ pub trait TimeZone: Sized + Clone {
|
||||||
/// with parsed `FixedOffset`.
|
/// with parsed `FixedOffset`.
|
||||||
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||||
parsed.to_datetime_with_timezone(self)
|
parsed.to_datetime_with_timezone(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,8 @@
|
||||||
|
|
||||||
//! The UTC (Coordinated Universal Time) time zone.
|
//! The UTC (Coordinated Universal Time) time zone.
|
||||||
|
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
#[cfg(all(
|
#[cfg(all(feature="clock", not(all(target_arch = "wasm32", feature = "wasmbind"))))]
|
||||||
feature = "clock",
|
|
||||||
not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))
|
|
||||||
))]
|
|
||||||
use oldtime;
|
use oldtime;
|
||||||
|
|
||||||
use naive::{NaiveDate, NaiveDateTime};
|
use naive::{NaiveDate, NaiveDateTime};
|
||||||
|
@ -41,7 +38,7 @@ impl Utc {
|
||||||
pub fn today() -> Date<Utc> { Utc::now().date() }
|
pub fn today() -> Date<Utc> { Utc::now().date() }
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
|
#[cfg(not(all(target_arch = "wasm32", feature = "wasmbind")))]
|
||||||
pub fn now() -> DateTime<Utc> {
|
pub fn now() -> DateTime<Utc> {
|
||||||
let spec = oldtime::get_time();
|
let spec = oldtime::get_time();
|
||||||
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
|
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
|
||||||
|
@ -49,7 +46,7 @@ impl Utc {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `DateTime` which corresponds to the current date.
|
/// Returns a `DateTime` which corresponds to the current date.
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
#[cfg(all(target_arch = "wasm32", feature = "wasmbind"))]
|
||||||
pub fn now() -> DateTime<Utc> {
|
pub fn now() -> DateTime<Utc> {
|
||||||
let now = js_sys::Date::new_0();
|
let now = js_sys::Date::new_0();
|
||||||
let millisecs_since_unix_epoch: u64 = now.get_time() as u64;
|
let millisecs_since_unix_epoch: u64 = now.get_time() as u64;
|
||||||
|
|
|
@ -10,11 +10,10 @@
|
||||||
|
|
||||||
//! Temporal quantification
|
//! Temporal quantification
|
||||||
|
|
||||||
use core::{fmt, i64};
|
use std::{fmt, i64};
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use core::ops::{Add, Sub, Mul, Div, Neg};
|
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||||
use core::time::Duration as StdDuration;
|
use std::time::Duration as StdDuration;
|
||||||
|
|
||||||
/// The number of nanoseconds in a microsecond.
|
/// The number of nanoseconds in a microsecond.
|
||||||
const NANOS_PER_MICRO: i32 = 1000;
|
const NANOS_PER_MICRO: i32 = 1000;
|
||||||
|
@ -364,20 +363,20 @@ impl fmt::Display for Duration {
|
||||||
let hasdate = days != 0;
|
let hasdate = days != 0;
|
||||||
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
|
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
|
||||||
|
|
||||||
write!(f, "{}P", sign)?;
|
try!(write!(f, "{}P", sign));
|
||||||
|
|
||||||
if hasdate {
|
if hasdate {
|
||||||
write!(f, "{}D", days)?;
|
try!(write!(f, "{}D", days));
|
||||||
}
|
}
|
||||||
if hastime {
|
if hastime {
|
||||||
if abs.nanos == 0 {
|
if abs.nanos == 0 {
|
||||||
write!(f, "T{}S", secs)?;
|
try!(write!(f, "T{}S", secs));
|
||||||
} else if abs.nanos % NANOS_PER_MILLI == 0 {
|
} else if abs.nanos % NANOS_PER_MILLI == 0 {
|
||||||
write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?;
|
try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI));
|
||||||
} else if abs.nanos % NANOS_PER_MICRO == 0 {
|
} else if abs.nanos % NANOS_PER_MICRO == 0 {
|
||||||
write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
|
try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO));
|
||||||
} else {
|
} else {
|
||||||
write!(f, "T{}.{:09}S", secs, abs.nanos)?;
|
try!(write!(f, "T{}.{:09}S", secs, abs.nanos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -395,15 +394,13 @@ pub struct OutOfRangeError(());
|
||||||
|
|
||||||
impl fmt::Display for OutOfRangeError {
|
impl fmt::Display for OutOfRangeError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Source duration value is out of range for the target type")
|
write!(f, "{}", self.description())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl Error for OutOfRangeError {
|
impl Error for OutOfRangeError {
|
||||||
#[allow(deprecated)]
|
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
"out of range error"
|
"Source duration value is out of range for the target type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// See README.md and LICENSE.txt for details.
|
// See README.md and LICENSE.txt for details.
|
||||||
|
|
||||||
use Timelike;
|
use Timelike;
|
||||||
use core::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use oldtime::Duration;
|
use oldtime::Duration;
|
||||||
|
|
||||||
/// Extension trait for subsecond rounding or truncation to a maximum number
|
/// Extension trait for subsecond rounding or truncation to a maximum number
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
#[cfg(all(target_arch = "wasm32", feature = "wasmbind"))]
|
||||||
mod test {
|
mod test {
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate wasm_bindgen_test;
|
extern crate wasm_bindgen_test;
|
||||||
|
|
Loading…
Reference in New Issue