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.
|
||||
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
|
||||
|
||||
### 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
|
||||
* 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
|
||||
|
||||
### 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 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,
|
||||
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.
|
||||
|
@ -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).
|
||||
|
||||
- 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.
|
||||
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]
|
||||
name = "chrono"
|
||||
version = "0.4.11"
|
||||
version = "0.4.9"
|
||||
authors = [
|
||||
"Kang Seonghoon <public+rust@mearie.org>",
|
||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
||||
|
@ -24,47 +24,34 @@ appveyor = { repository = "chronotope/chrono" }
|
|||
name = "chrono"
|
||||
|
||||
[features]
|
||||
default = ["clock", "std"]
|
||||
alloc = []
|
||||
std = []
|
||||
clock = ["time", "std"]
|
||||
default = ["clock"]
|
||||
clock = ["time"]
|
||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||
__internal_bench = []
|
||||
|
||||
[dependencies]
|
||||
libc = { version = "0.2", default-features = false }
|
||||
time = { version = "0.1.39", optional = true }
|
||||
num-integer = { version = "0.1.36", default-features = false }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
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 }
|
||||
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { version = "1" }
|
||||
serde_derive = { version = "1", default-features = false }
|
||||
serde_derive = { version = "1" }
|
||||
bincode = { version = "0.8.0" }
|
||||
num-iter = { version = "0.1.35", default-features = false }
|
||||
criterion = { version = "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"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
all-features = true
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["serde"]
|
||||
|
||||
[[bench]]
|
||||
name = "chrono"
|
||||
required-features = ["__internal_bench"]
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "serde"
|
||||
harness = false
|
||||
required-features = ["serde"]
|
||||
all-features = true
|
||||
|
|
7
Makefile
7
Makefile
|
@ -1,8 +1,6 @@
|
|||
# this Makefile is mostly for the packaging convenience.
|
||||
# casual users should use `cargo` to retrieve the appropriate version of Chrono.
|
||||
|
||||
CHANNEL=stable
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
@echo 'Try `cargo build` instead.'
|
||||
|
@ -22,8 +20,11 @@ README.md: src/lib.rs
|
|||
|
||||
.PHONY: 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
|
||||
doc: authors readme
|
||||
cargo doc --features 'serde rustc-serialize bincode'
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ use chrono::prelude::*;
|
|||
Chrono currently uses
|
||||
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.
|
||||
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`.
|
||||
Note that this is an "accurate" duration represented as seconds and
|
||||
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);
|
||||
}
|
151
ci/travis.sh
151
ci/travis.sh
|
@ -2,50 +2,25 @@
|
|||
|
||||
# This is the script that's executed by travis, you can run it yourself to run
|
||||
# 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
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
|
||||
main() {
|
||||
if [[ -n "$CHANNEL" ]] ; then
|
||||
if [[ "$CHANNEL" == 1.13.0 ]]; then
|
||||
banner "Building $CHANNEL"
|
||||
build_only
|
||||
else
|
||||
banner "Building/testing $CHANNEL"
|
||||
build_and_test
|
||||
banner "Testing Core $CHANNEL"
|
||||
build_core_test
|
||||
channel() {
|
||||
if [ -n "${TRAVIS}" ]; then
|
||||
if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then
|
||||
pwd
|
||||
(set -x; cargo "$@")
|
||||
fi
|
||||
elif [ -n "${APPVEYOR}" ]; then
|
||||
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
|
||||
pwd
|
||||
(set -x; cargo "$@")
|
||||
fi
|
||||
else
|
||||
CHANNEL=nightly
|
||||
matching_banner "Test $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
|
||||
pwd
|
||||
(set -x; cargo "+${CHANNEL}" "$@")
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -79,20 +54,14 @@ build_and_test_nonwasm() {
|
|||
TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize
|
||||
|
||||
# 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
|
||||
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
|
||||
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
|
||||
channel build -v --no-default-features --features std,serde,rustc-serialize
|
||||
TZ=Asia/Katmandu channel test -v --no-default-features --features std,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
|
||||
channel build -v --no-default-features --features serde,rustc-serialize
|
||||
TZ=Asia/Katmandu channel test -v --no-default-features --features serde,rustc-serialize --lib
|
||||
}
|
||||
|
||||
build_and_test_wasm() {
|
||||
|
@ -112,16 +81,8 @@ build_only() {
|
|||
cargo clean
|
||||
channel build -v
|
||||
channel build -v --features rustc-serialize
|
||||
channel build -v --features serde
|
||||
channel build -v --no-default-features --features std
|
||||
}
|
||||
|
||||
build_core_test() {
|
||||
channel_run rustup target add thumbv6m-none-eabi --toolchain "$CHANNEL"
|
||||
(
|
||||
cd ci/core-test
|
||||
channel build -v --target thumbv6m-none-eabi
|
||||
)
|
||||
channel build -v --features 'serde bincode'
|
||||
channel build -v --no-default-features
|
||||
}
|
||||
|
||||
run_clippy() {
|
||||
|
@ -131,7 +92,7 @@ run_clippy() {
|
|||
exit
|
||||
fi
|
||||
|
||||
cargo clippy --features 'serde rustc-serialize' -- -Dclippy
|
||||
cargo clippy --features 'serde bincode rustc-serialize' -- -Dclippy
|
||||
}
|
||||
|
||||
check_readme() {
|
||||
|
@ -139,70 +100,22 @@ check_readme() {
|
|||
(set -x; git diff --exit-code -- README.md) ; echo $?
|
||||
}
|
||||
|
||||
# script helpers
|
||||
|
||||
banner() {
|
||||
echo "======================================================================"
|
||||
echo "$*"
|
||||
echo "======================================================================"
|
||||
}
|
||||
|
||||
underline() {
|
||||
echo "$*"
|
||||
echo "${*//?/^}"
|
||||
}
|
||||
|
||||
matching_banner() {
|
||||
if channel_matches || ! is_ci ; then
|
||||
banner "$*"
|
||||
echo_versions
|
||||
fi
|
||||
}
|
||||
|
||||
echo_versions() {
|
||||
channel_run rustc --version
|
||||
channel_run cargo --version
|
||||
rustc --version
|
||||
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}" "$@"
|
||||
CHANNEL=nightly
|
||||
if [ "x${CLIPPY}" = xy ] ; then
|
||||
run_clippy
|
||||
else
|
||||
underline "$ $cmd $*"
|
||||
"$cmd" "$@"
|
||||
build_and_test
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
channel_matches() {
|
||||
if is_ci ; then
|
||||
if [[ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}"
|
||||
|| "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]] ; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
CHANNEL=beta
|
||||
build_and_test
|
||||
|
||||
is_ci() {
|
||||
if [[ -n "$TRAVIS" || -n "$APPVEYOR" ]] ; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
CHANNEL=stable
|
||||
build_and_test
|
||||
|
||||
main
|
||||
CHANNEL=1.13.0
|
||||
build_only
|
||||
|
|
20
src/date.rs
20
src/date.rs
|
@ -3,18 +3,16 @@
|
|||
|
||||
//! ISO 8601 calendar date with time zone.
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::{fmt, hash};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Add, Sub};
|
||||
use std::{fmt, hash};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use {Weekday, Datelike};
|
||||
use offset::{TimeZone, Utc};
|
||||
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
|
||||
use DateTime;
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::{DelayedFormat, Item, StrftimeItems};
|
||||
use format::{Item, DelayedFormat, StrftimeItems};
|
||||
|
||||
/// 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.
|
||||
/// 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]
|
||||
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
||||
Date { date: date, offset: offset }
|
||||
|
@ -240,7 +238,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
|||
|
||||
/// 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,
|
||||
/// but provided for the consistency.
|
||||
#[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 {
|
||||
/// Formats the date with the specified formatting items.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
||||
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=Item<'a>> + Clone {
|
||||
DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items)
|
||||
}
|
||||
|
||||
/// Formats the date with the specified format string.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
self.format_with_items(StrftimeItems::new(fmt))
|
||||
|
|
642
src/datetime.rs
642
src/datetime.rs
|
@ -3,29 +3,20 @@
|
|||
|
||||
//! ISO 8601 date and time with time zone.
|
||||
|
||||
use core::{str, fmt, hash};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops::{Add, Sub};
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::{str, fmt, hash};
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
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};
|
||||
#[cfg(feature="clock")]
|
||||
use offset::Local;
|
||||
use offset::{TimeZone, Offset, Utc, FixedOffset};
|
||||
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
||||
use Date;
|
||||
use format::{Item, Fixed};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::DelayedFormat;
|
||||
use core::borrow::Borrow;
|
||||
use format::{Item, Numeric, Pad, Fixed};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||
|
||||
/// Specific formatting options for seconds. This may be extended in the
|
||||
/// 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);
|
||||
/// ~~~~
|
||||
//
|
||||
// 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]
|
||||
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
|
||||
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
|
||||
/// from ~584 Billion to ~584. (If this is a problem, please file
|
||||
/// an issue to let me know what domain needs nanosecond precision over
|
||||
/// millennia, I'm curious.)
|
||||
/// millenia, I'm curious.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -324,21 +315,10 @@ fn map_local<Tz: TimeZone, F>(dt: &DateTime<Tz>, mut f: F) -> Option<DateTime<Tz
|
|||
impl DateTime<FixedOffset> {
|
||||
/// 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`.
|
||||
///
|
||||
/// 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>> {
|
||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, ITEMS.iter())?;
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
parsed.to_datetime()
|
||||
}
|
||||
|
||||
|
@ -350,7 +330,7 @@ impl DateTime<FixedOffset> {
|
|||
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
const ITEMS: &'static [Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, ITEMS.iter())?;
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
parsed.to_datetime()
|
||||
}
|
||||
|
||||
|
@ -376,24 +356,22 @@ impl DateTime<FixedOffset> {
|
|||
/// ```
|
||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||
parsed.to_datetime()
|
||||
}
|
||||
}
|
||||
|
||||
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`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn to_rfc2822(&self) -> String {
|
||||
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`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn to_rfc3339(&self) -> String {
|
||||
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
|
||||
|
@ -420,7 +398,6 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
|||
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true),
|
||||
/// "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 {
|
||||
use format::Numeric::*;
|
||||
use format::Pad::Zero;
|
||||
|
@ -462,20 +439,19 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
|
|||
match ssitem {
|
||||
None =>
|
||||
self.format_with_items(
|
||||
PREFIX.iter().chain([tzitem].iter())
|
||||
PREFIX.iter().chain([tzitem].iter()).cloned()
|
||||
).to_string(),
|
||||
Some(s) =>
|
||||
self.format_with_items(
|
||||
PREFIX.iter().chain([s, tzitem].iter())
|
||||
PREFIX.iter().chain([s, tzitem].iter()).cloned()
|
||||
).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the combined date and time with the specified formatting items.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
||||
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=Item<'a>> + Clone {
|
||||
let local = self.naive_local();
|
||||
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.
|
||||
/// See the [`format::strftime` module](./format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
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, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> {
|
||||
/// Compare two DateTimes based on their true time, ignoring time zones
|
||||
///
|
||||
/// # 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> {
|
||||
impl<Tz: TimeZone> PartialOrd for DateTime<Tz> {
|
||||
fn partial_cmp(&self, other: &DateTime<Tz>) -> Option<Ordering> {
|
||||
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> {
|
||||
type Err = ParseError;
|
||||
|
||||
|
@ -660,7 +647,6 @@ impl str::FromStr for DateTime<Local> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl From<SystemTime> for DateTime<Utc> {
|
||||
fn from(t: SystemTime) -> DateTime<Utc> {
|
||||
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 {
|
||||
fn from(dt: DateTime<Tz>) -> SystemTime {
|
||||
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)
|
||||
where FUtc: Fn(&DateTime<Utc>) -> 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(),
|
||||
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>,
|
||||
FFixed: Fn(&str) -> Result<DateTime<FixedOffset>, 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)
|
||||
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>,
|
||||
FFixed: Fn(&str) -> Result<rustc_serialize::TsSeconds<FixedOffset>, 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)> {
|
||||
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")]
|
||||
pub mod rustc_serialize {
|
||||
use core::fmt;
|
||||
use core::ops::Deref;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use super::DateTime;
|
||||
#[cfg(feature="clock")]
|
||||
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>
|
||||
where D: Decoder,
|
||||
T: fmt::Display,
|
||||
|
@ -922,38 +907,25 @@ pub mod rustc_serialize {
|
|||
/// documented at re-export site
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use super::DateTime;
|
||||
#[cfg(feature="clock")]
|
||||
use offset::Local;
|
||||
use offset::{LocalResult, TimeZone, Utc, FixedOffset};
|
||||
use serdelib::{ser, de};
|
||||
use {SerdeError, ne_timestamp};
|
||||
|
||||
#[doc(hidden)]
|
||||
#[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
|
||||
// try!-like function to convert a LocalResult into a serde-ish Result
|
||||
fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E>
|
||||
where
|
||||
E: de::Error,
|
||||
where E: de::Error,
|
||||
V: fmt::Display,
|
||||
T: fmt::Display,
|
||||
{
|
||||
match me {
|
||||
LocalResult::None => Err(E::custom(
|
||||
ne_timestamp(ts))),
|
||||
format!("value is not a legal timestamp: {}", ts))),
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -995,13 +967,13 @@ pub mod serde {
|
|||
/// # fn main() { example().unwrap(); }
|
||||
/// ```
|
||||
pub mod ts_nanoseconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {DateTime, Utc};
|
||||
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
|
||||
///
|
||||
|
@ -1072,15 +1044,17 @@ pub mod serde {
|
|||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
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 {
|
||||
type Value = DateTime<Utc>;
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
///
|
||||
/// Intended for use with `serde`s `with` attribute.
|
||||
|
@ -1286,13 +1114,13 @@ pub mod serde {
|
|||
/// # fn main() { example().unwrap(); }
|
||||
/// ```
|
||||
pub mod ts_milliseconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {DateTime, Utc};
|
||||
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
|
||||
///
|
||||
|
@ -1363,9 +1191,11 @@ pub mod serde {
|
|||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
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 {
|
||||
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
|
||||
///
|
||||
/// Intended for use with `serde`'s `with` attribute.
|
||||
|
@ -1577,13 +1261,13 @@ pub mod serde {
|
|||
/// # fn main() { example().unwrap(); }
|
||||
/// ```
|
||||
pub mod ts_seconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {DateTime, Utc};
|
||||
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
|
||||
///
|
||||
|
@ -1654,9 +1338,11 @@ pub mod serde {
|
|||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
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 {
|
||||
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> {
|
||||
/// Serialize into a rfc3339 time string
|
||||
///
|
||||
|
@ -1863,7 +1403,7 @@ pub mod serde {
|
|||
fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E>
|
||||
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().naive_local(), NaiveDate::from_ymd(2017, 8, 9));
|
||||
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]
|
||||
|
@ -2053,8 +1590,6 @@ mod tests {
|
|||
|
||||
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_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"),
|
||||
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"),
|
||||
|
@ -2095,15 +1630,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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>>(),
|
||||
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>>(),
|
||||
|
@ -2132,25 +1658,6 @@ mod tests {
|
|||
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]
|
||||
#[cfg(feature="clock")]
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,31 +17,20 @@
|
|||
|
||||
#![allow(ellipsis_inclusive_range_patterns)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
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};
|
||||
use {Weekday, ParseWeekdayError};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use {Datelike, Timelike, Weekday, ParseWeekdayError};
|
||||
use div::{div_floor, mod_floor};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use offset::{Offset, FixedOffset};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use naive::{NaiveDate, NaiveTime};
|
||||
|
||||
pub use self::strftime::StrftimeItems;
|
||||
pub use self::parsed::Parsed;
|
||||
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)]
|
||||
enum Void {}
|
||||
|
||||
|
@ -66,7 +55,7 @@ pub enum Pad {
|
|||
///
|
||||
/// The **parsing width** is the maximal width to be scanned.
|
||||
/// 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
|
||||
/// parsed with the same formatting items.
|
||||
#[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.
|
||||
/// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.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,
|
||||
/// 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,
|
||||
/// 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,
|
||||
/// Timezone name.
|
||||
///
|
||||
|
@ -196,21 +185,21 @@ pub enum Fixed {
|
|||
TimezoneName,
|
||||
/// 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`,
|
||||
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
|
||||
TimezoneOffsetColon,
|
||||
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
|
||||
///
|
||||
/// In the parser, the colon can be omitted and/or surrounded with any amount of 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.
|
||||
/// 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,
|
||||
/// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
||||
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
|
||||
/// Parsing allows an optional colon.
|
||||
TimezoneOffset,
|
||||
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
|
||||
/// Same to [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
|
||||
/// Parsing allows an optional colon.
|
||||
TimezoneOffsetZ,
|
||||
/// 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
|
||||
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,
|
||||
/// 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,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
|
@ -255,13 +244,11 @@ enum InternalInternal {
|
|||
pub enum Item<'a> {
|
||||
/// A literally printed and parsed text.
|
||||
Literal(&'a str),
|
||||
/// Same as `Literal` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
/// Same to `Literal` but with the string owned by the item.
|
||||
OwnedLiteral(Box<str>),
|
||||
/// Whitespace. Prints literally but reads zero or more whitespace.
|
||||
Space(&'a str),
|
||||
/// Same as `Space` but with the string owned by the item.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
/// Same to `Space` but with the string owned by the item.
|
||||
OwnedSpace(Box<str>),
|
||||
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
|
||||
/// the parser simply ignores any padded whitespace and zeroes.
|
||||
|
@ -284,7 +271,6 @@ macro_rules! internal_fix { ($x:ident) => (Item::Fixed(Fixed::Internal(InternalF
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
pub struct ParseError(ParseErrorKind);
|
||||
|
||||
/// The category of parse error
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||
enum ParseErrorKind {
|
||||
/// Given field is out of permitted range.
|
||||
|
@ -316,28 +302,26 @@ enum ParseErrorKind {
|
|||
BadFormat,
|
||||
}
|
||||
|
||||
/// Same as `Result<T, ParseError>`.
|
||||
/// Same to `Result<T, ParseError>`.
|
||||
pub type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
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"),
|
||||
}
|
||||
self.description().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
impl Error for ParseError {
|
||||
#[allow(deprecated)]
|
||||
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.
|
||||
/// Internally used by `DelayedFormat`.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
pub fn format<'a, I, B>(
|
||||
pub fn format<'a, I>(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
items: I,
|
||||
) -> 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
|
||||
static SHORT_MONTHS: [&'static str; 12] =
|
||||
|
@ -373,16 +356,15 @@ pub fn format<'a, I, B>(
|
|||
static LONG_WEEKDAYS: [&'static str; 7] =
|
||||
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
|
||||
|
||||
use core::fmt::Write;
|
||||
use std::fmt::Write;
|
||||
let mut result = String::new();
|
||||
|
||||
for item in items {
|
||||
match item.borrow() {
|
||||
&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),
|
||||
match item {
|
||||
Item::Literal(s) | Item::Space(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::*;
|
||||
|
||||
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;
|
||||
|
||||
let (width, v) = match spec {
|
||||
&Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
&YearDiv100 => (2, date.map(|d| div_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()))),
|
||||
&IsoYearDiv100 => (2, date.map(|d| div_floor(
|
||||
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||
YearDiv100 => (2, date.map(|d| div_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()))),
|
||||
IsoYearDiv100 => (2, date.map(|d| div_floor(
|
||||
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))),
|
||||
&Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
&Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
&WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
&WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
&IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
&NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
||||
Month => (2, date.map(|d| i64::from(d.month()))),
|
||||
Day => (2, date.map(|d| i64::from(d.day()))),
|
||||
WeekFromSun => (2, date.map(|d| i64::from(week_from_sun(d)))),
|
||||
WeekFromMon => (2, date.map(|d| i64::from(week_from_mon(d)))),
|
||||
IsoWeek => (2, date.map(|d| i64::from(d.iso_week().week()))),
|
||||
NumDaysFromSun => (1, date.map(|d| i64::from(d.weekday()
|
||||
.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()))),
|
||||
&Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
&Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
&Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
&Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
&Second => (2, time.map(|t| i64::from(t.second() +
|
||||
Ordinal => (3, date.map(|d| i64::from(d.ordinal()))),
|
||||
Hour => (2, time.map(|t| i64::from(t.hour()))),
|
||||
Hour12 => (2, time.map(|t| i64::from(t.hour12().1))),
|
||||
Minute => (2, time.map(|t| i64::from(t.minute()))),
|
||||
Second => (2, time.map(|t| i64::from(t.second() +
|
||||
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) {
|
||||
Nanosecond => (9, time.map(|t| i64::from(t.nanosecond() % 1_000_000_000))),
|
||||
Timestamp => (1, match (date, time, off) {
|
||||
(Some(d), Some(t), None) =>
|
||||
Some(d.and_time(*t).timestamp()),
|
||||
(Some(d), Some(t), Some(&(_, off))) =>
|
||||
|
@ -424,31 +406,33 @@ pub fn format<'a, I, B>(
|
|||
}),
|
||||
|
||||
// for the future expansion
|
||||
&Internal(ref int) => match int._dummy {},
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
|
||||
if let Some(v) = v {
|
||||
if (spec == &Year || spec == &IsoYear) && !(0 <= v && v < 10_000) {
|
||||
try!(
|
||||
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10_000) {
|
||||
// non-four-digit years require an explicit sign as per ISO 8601
|
||||
match pad {
|
||||
&Pad::None => write!(result, "{:+}", v),
|
||||
&Pad::Zero => write!(result, "{:+01$}", v, width + 1),
|
||||
&Pad::Space => write!(result, "{:+1$}", v, width + 1),
|
||||
Pad::None => write!(result, "{:+}", v),
|
||||
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),
|
||||
Pad::None => write!(result, "{}", v),
|
||||
Pad::Zero => write!(result, "{:01$}", v, width),
|
||||
Pad::Space => write!(result, "{:1$}", v, width),
|
||||
}
|
||||
}?
|
||||
}
|
||||
)
|
||||
} else {
|
||||
return Err(fmt::Error) // insufficient arguments for given format
|
||||
}
|
||||
},
|
||||
|
||||
&Item::Fixed(ref spec) => {
|
||||
Item::Fixed(spec) => {
|
||||
use self::Fixed::*;
|
||||
|
||||
/// 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 {
|
||||
&ShortMonthName =>
|
||||
ShortMonthName =>
|
||||
date.map(|d| {
|
||||
result.push_str(SHORT_MONTHS[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
&LongMonthName =>
|
||||
LongMonthName =>
|
||||
date.map(|d| {
|
||||
result.push_str(LONG_MONTHS[d.month0() as usize]);
|
||||
Ok(())
|
||||
}),
|
||||
&ShortWeekdayName =>
|
||||
ShortWeekdayName =>
|
||||
date.map(|d| {
|
||||
result.push_str(
|
||||
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||
);
|
||||
Ok(())
|
||||
}),
|
||||
&LongWeekdayName =>
|
||||
LongWeekdayName =>
|
||||
date.map(|d| {
|
||||
result.push_str(
|
||||
LONG_WEEKDAYS[d.weekday().num_days_from_monday() as usize]
|
||||
);
|
||||
Ok(())
|
||||
}),
|
||||
&LowerAmPm =>
|
||||
LowerAmPm =>
|
||||
time.map(|t| {
|
||||
result.push_str(if t.hour12().0 {"pm"} else {"am"});
|
||||
Ok(())
|
||||
}),
|
||||
&UpperAmPm =>
|
||||
UpperAmPm =>
|
||||
time.map(|t| {
|
||||
result.push_str(if t.hour12().0 {"PM"} else {"AM"});
|
||||
Ok(())
|
||||
}),
|
||||
&Nanosecond =>
|
||||
Nanosecond =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
if nano == 0 {
|
||||
|
@ -521,70 +505,70 @@ pub fn format<'a, I, B>(
|
|||
write!(result, ".{:09}", nano)
|
||||
}
|
||||
}),
|
||||
&Nanosecond3 =>
|
||||
Nanosecond3 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:03}", nano / 1_000_000)
|
||||
}),
|
||||
&Nanosecond6 =>
|
||||
Nanosecond6 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:06}", nano / 1_000)
|
||||
}),
|
||||
&Nanosecond9 =>
|
||||
Nanosecond9 =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, ".{:09}", nano)
|
||||
}),
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:03}", nano / 1_000_000)
|
||||
}),
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:06}", nano / 1_000)
|
||||
}),
|
||||
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
|
||||
time.map(|t| {
|
||||
let nano = t.nanosecond() % 1_000_000_000;
|
||||
write!(result, "{:09}", nano)
|
||||
}),
|
||||
&TimezoneName =>
|
||||
TimezoneName =>
|
||||
off.map(|&(ref name, _)| {
|
||||
result.push_str(name);
|
||||
Ok(())
|
||||
}),
|
||||
&TimezoneOffsetColon =>
|
||||
TimezoneOffsetColon =>
|
||||
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)),
|
||||
&TimezoneOffset =>
|
||||
TimezoneOffset =>
|
||||
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)),
|
||||
&Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
||||
Internal(InternalFixed { val: InternalInternal::TimezoneOffsetPermissive }) =>
|
||||
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) {
|
||||
let sec = t.second() + t.nanosecond() / 1_000_000_000;
|
||||
write!(
|
||||
try!(write!(
|
||||
result,
|
||||
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
|
||||
SHORT_WEEKDAYS[d.weekday().num_days_from_monday() as usize],
|
||||
d.day(), SHORT_MONTHS[d.month0() as usize], d.year(),
|
||||
t.hour(), t.minute(), sec
|
||||
)?;
|
||||
));
|
||||
Some(write_local_minus_utc(&mut result, off, false, false))
|
||||
} else {
|
||||
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) {
|
||||
// reuse `Debug` impls which already print ISO 8601 format.
|
||||
// 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))
|
||||
} else {
|
||||
None
|
||||
|
@ -592,12 +576,12 @@ pub fn format<'a, I, B>(
|
|||
};
|
||||
|
||||
match ret {
|
||||
Some(ret) => ret?,
|
||||
Some(ret) => try!(ret),
|
||||
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.
|
||||
/// This is normally constructed via `format` methods of each date and time type.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[derive(Debug)]
|
||||
pub struct DelayedFormat<I> {
|
||||
/// The date view, if any.
|
||||
|
@ -627,8 +610,7 @@ pub struct DelayedFormat<I> {
|
|||
items: I,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
|
||||
impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
|
||||
/// Makes a new `DelayedFormat` value out of local date and time.
|
||||
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
|
||||
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=B> + Clone, B: Borrow<Item<'a>>> fmt::Display for DelayedFormat<I> {
|
||||
impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for DelayedFormat<I> {
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -6,15 +6,13 @@
|
|||
|
||||
#![allow(deprecated)]
|
||||
|
||||
use core::borrow::Borrow;
|
||||
use core::usize;
|
||||
use core::str;
|
||||
use std::usize;
|
||||
|
||||
use Weekday;
|
||||
|
||||
use {DateTime, FixedOffset, Weekday};
|
||||
use super::scan;
|
||||
use super::{Parsed, Numeric, Pad, Fixed, Item, InternalFixed, InternalInternal};
|
||||
use super::{ParseResult, ParseError, ParseErrorKind};
|
||||
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT, NOT_ENOUGH};
|
||||
use super::{Parsed, ParseResult, Item, InternalFixed, InternalInternal};
|
||||
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT};
|
||||
|
||||
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
|
||||
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, ())> {
|
||||
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:
|
||||
|
@ -54,10 +52,10 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
|
|||
// minute = *S 2DIGIT *S
|
||||
// second = *S 2DIGIT *S
|
||||
// zone = ( "+" / "-" ) 4DIGIT /
|
||||
// "UT" / "GMT" / ; same as +0000
|
||||
// "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800
|
||||
// "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700
|
||||
// 1*(%d65-90 / %d97-122) ; same as -0000
|
||||
// "UT" / "GMT" / ; same to +0000
|
||||
// "EST" / "CST" / "MST" / "PST" / ; same to -0500 to -0800
|
||||
// "EDT" / "CDT" / "MDT" / "PDT" / ; same to -0400 to -0700
|
||||
// 1*(%d65-90 / %d97-122) ; same to -0000
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
// 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 !s_.starts_with(',') { return Err(INVALID); }
|
||||
s = &s_[1..];
|
||||
parsed.set_weekday(weekday)?;
|
||||
try!(parsed.set_weekday(weekday));
|
||||
}
|
||||
|
||||
s = s.trim_left();
|
||||
parsed.set_day(try_consume!(scan::number(s, 1, 2)))?;
|
||||
s = scan::space(s)?; // mandatory
|
||||
parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?;
|
||||
s = scan::space(s)?; // mandatory
|
||||
try!(parsed.set_day(try_consume!(scan::number(s, 1, 2))));
|
||||
s = try!(scan::space(s)); // mandatory
|
||||
try!(parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s)))));
|
||||
s = try!(scan::space(s)); // mandatory
|
||||
|
||||
// distinguish two- and three-digit years from four-digit years
|
||||
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
|
||||
(_, _) => {} // 1987 -> 1987, 0654 -> 0654
|
||||
}
|
||||
parsed.set_year(year)?;
|
||||
try!(parsed.set_year(year));
|
||||
|
||||
s = scan::space(s)?; // mandatory
|
||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s.trim_left(), b':')?.trim_left(); // *S ":" *S
|
||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = try!(scan::space(s)); // mandatory
|
||||
try!(parsed.set_hour(try_consume!(scan::number(s, 2, 2))));
|
||||
s = try!(scan::char(s.trim_left(), b':')).trim_left(); // *S ":" *S
|
||||
try!(parsed.set_minute(try_consume!(scan::number(s, 2, 2))));
|
||||
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)) {
|
||||
// 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, ()))
|
||||
|
@ -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, ())> {
|
||||
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:
|
||||
|
@ -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.
|
||||
// since this is not a typical Chrono behavior, we check it earlier.
|
||||
|
||||
parsed.set_year(try_consume!(scan::number(s, 4, 4)))?;
|
||||
s = scan::char(s, b'-')?;
|
||||
parsed.set_month(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b'-')?;
|
||||
parsed.set_day(try_consume!(scan::number(s, 2, 2)))?;
|
||||
try!(parsed.set_year(try_consume!(scan::number(s, 4, 4))));
|
||||
s = try!(scan::char(s, b'-'));
|
||||
try!(parsed.set_month(try_consume!(scan::number(s, 2, 2))));
|
||||
s = try!(scan::char(s, b'-'));
|
||||
try!(parsed.set_day(try_consume!(scan::number(s, 2, 2))));
|
||||
|
||||
s = match s.as_bytes().first() {
|
||||
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),
|
||||
};
|
||||
|
||||
parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b':')?;
|
||||
parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?;
|
||||
s = scan::char(s, b':')?;
|
||||
parsed.set_second(try_consume!(scan::number(s, 2, 2)))?;
|
||||
try!(parsed.set_hour(try_consume!(scan::number(s, 2, 2))));
|
||||
s = try!(scan::char(s, b':'));
|
||||
try!(parsed.set_minute(try_consume!(scan::number(s, 2, 2))));
|
||||
s = try!(scan::char(s, b':'));
|
||||
try!(parsed.set_second(try_consume!(scan::number(s, 2, 2))));
|
||||
if s.starts_with('.') {
|
||||
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':')));
|
||||
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, ()))
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
/// - (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<()>
|
||||
where I: Iterator<Item=B>, B: Borrow<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>> {
|
||||
pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()>
|
||||
where I: Iterator<Item=Item<'a>> {
|
||||
macro_rules! try_consume {
|
||||
($e:expr) => ({
|
||||
match $e {
|
||||
Ok((s_, v)) => {
|
||||
s = s_;
|
||||
v
|
||||
}
|
||||
Err(e) => return Err((s, e))
|
||||
}
|
||||
})
|
||||
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
|
||||
}
|
||||
|
||||
for item in items {
|
||||
match item.borrow() {
|
||||
&Item::Literal(prefix) => {
|
||||
if s.len() < prefix.len() { return Err((s, TOO_SHORT)); }
|
||||
if !s.starts_with(prefix) { return Err((s, INVALID)); }
|
||||
match item {
|
||||
Item::Literal(prefix) => {
|
||||
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||
if !s.starts_with(prefix) { return Err(INVALID); }
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
&Item::OwnedLiteral(ref prefix) => {
|
||||
if s.len() < prefix.len() { return Err((s, TOO_SHORT)); }
|
||||
if !s.starts_with(&prefix[..]) { return Err((s, INVALID)); }
|
||||
Item::OwnedLiteral(ref prefix) => {
|
||||
if s.len() < prefix.len() { return Err(TOO_SHORT); }
|
||||
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
|
||||
s = &s[prefix.len()..];
|
||||
}
|
||||
|
||||
&Item::Space(_) => {
|
||||
Item::Space(_) | Item::OwnedSpace(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
&Item::OwnedSpace(_) => {
|
||||
s = s.trim_left();
|
||||
}
|
||||
|
||||
&Item::Numeric(ref spec, ref _pad) => {
|
||||
Item::Numeric(spec, _pad) => {
|
||||
use super::Numeric::*;
|
||||
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
|
||||
|
||||
let (width, signed, set): (usize, bool, Setter) = match spec {
|
||||
&Year => (4, true, Parsed::set_year),
|
||||
&YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||
&YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||
&IsoYear => (4, true, Parsed::set_isoyear),
|
||||
&IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||
&IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||
&Month => (2, false, Parsed::set_month),
|
||||
&Day => (2, false, Parsed::set_day),
|
||||
&WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||
&WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||
&IsoWeek => (2, false, Parsed::set_isoweek),
|
||||
&NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||
&WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||
&Ordinal => (3, false, Parsed::set_ordinal),
|
||||
&Hour => (2, false, Parsed::set_hour),
|
||||
&Hour12 => (2, false, Parsed::set_hour12),
|
||||
&Minute => (2, false, Parsed::set_minute),
|
||||
&Second => (2, false, Parsed::set_second),
|
||||
&Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
&Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
Year => (4, true, Parsed::set_year),
|
||||
YearDiv100 => (2, false, Parsed::set_year_div_100),
|
||||
YearMod100 => (2, false, Parsed::set_year_mod_100),
|
||||
IsoYear => (4, true, Parsed::set_isoyear),
|
||||
IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100),
|
||||
IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100),
|
||||
Month => (2, false, Parsed::set_month),
|
||||
Day => (2, false, Parsed::set_day),
|
||||
WeekFromSun => (2, false, Parsed::set_week_from_sun),
|
||||
WeekFromMon => (2, false, Parsed::set_week_from_mon),
|
||||
IsoWeek => (2, false, Parsed::set_isoweek),
|
||||
NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday),
|
||||
WeekdayFromMon => (1, false, set_weekday_with_number_from_monday),
|
||||
Ordinal => (3, false, Parsed::set_ordinal),
|
||||
Hour => (2, false, Parsed::set_hour),
|
||||
Hour12 => (2, false, Parsed::set_hour12),
|
||||
Minute => (2, false, Parsed::set_minute),
|
||||
Second => (2, false, Parsed::set_second),
|
||||
Nanosecond => (9, false, Parsed::set_nanosecond),
|
||||
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
|
||||
|
||||
// for the future expansion
|
||||
&Internal(ref int) => match int._dummy {},
|
||||
Internal(ref int) => match int._dummy {},
|
||||
};
|
||||
|
||||
s = s.trim_left();
|
||||
let v = if signed {
|
||||
if s.starts_with('-') {
|
||||
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('+') {
|
||||
try_consume!(scan::number(&s[1..], 1, usize::MAX))
|
||||
} else {
|
||||
|
@ -291,142 +272,104 @@ where I: Iterator<Item=B>, B: Borrow<Item<'a>> {
|
|||
} else {
|
||||
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::*;
|
||||
|
||||
match spec {
|
||||
&ShortMonthName => {
|
||||
ShortMonthName => {
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
|
||||
try!(parsed.set_weekday(weekday));
|
||||
}
|
||||
|
||||
&LowerAmPm | &UpperAmPm => {
|
||||
if s.len() < 2 { return Err((s, TOO_SHORT)); }
|
||||
LowerAmPm | UpperAmPm => {
|
||||
if s.len() < 2 { return Err(TOO_SHORT); }
|
||||
let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) {
|
||||
(b'a',b'm') => false,
|
||||
(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..];
|
||||
}
|
||||
|
||||
&Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
|
||||
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => {
|
||||
if s.starts_with('.') {
|
||||
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 }) => {
|
||||
if s.len() < 3 { return Err((s, TOO_SHORT)); }
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
|
||||
if s.len() < 3 { return Err(TOO_SHORT); }
|
||||
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 }) => {
|
||||
if s.len() < 6 { return Err((s, TOO_SHORT)); }
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
|
||||
if s.len() < 6 { return Err(TOO_SHORT); }
|
||||
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 }) => {
|
||||
if s.len() < 9 { return Err((s, TOO_SHORT)); }
|
||||
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
|
||||
if s.len() < 9 { return Err(TOO_SHORT); }
|
||||
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(),
|
||||
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(),
|
||||
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(
|
||||
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)),
|
||||
&RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
|
||||
RFC3339 => try_consume!(parse_rfc3339(parsed, s)),
|
||||
}
|
||||
}
|
||||
|
||||
&Item::Error => {
|
||||
return Err((s, BAD_FORMAT));
|
||||
Item::Error => {
|
||||
return Err(BAD_FORMAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if there are trailling chars, it is an error
|
||||
if !s.is_empty() {
|
||||
Err((s, TOO_LONG))
|
||||
Err(TOO_LONG)
|
||||
} else {
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,7 +382,7 @@ fn test_parse() {
|
|||
// workaround for Rust issue #22255
|
||||
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, items.iter())?;
|
||||
try!(parse(&mut parsed, s, items.iter().cloned()));
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
|
@ -750,12 +693,12 @@ fn test_rfc2822() {
|
|||
|
||||
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -831,12 +774,12 @@ fn test_rfc3339() {
|
|||
|
||||
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -110,9 +110,8 @@ pub struct Parsed {
|
|||
_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.
|
||||
#[inline]
|
||||
fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<()> {
|
||||
if let Some(ref old) = *old {
|
||||
if *old == new {Ok(())} else {Err(IMPOSSIBLE)}
|
||||
|
@ -142,97 +141,82 @@ impl Parsed {
|
|||
}
|
||||
|
||||
/// Tries to set the [`year`](#structfield.year) field from given value.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> {
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> {
|
||||
set_if_consistent(&mut self.weekday, value)
|
||||
}
|
||||
|
||||
/// Tries to set the [`ordinal`](#structfield.ordinal) field from given value.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
/// (`false` for AM, `true` for PM)
|
||||
#[inline]
|
||||
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
|
||||
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
|
||||
/// given hour number in 12-hour clocks.
|
||||
#[inline]
|
||||
pub fn set_hour12(&mut self, value: i64) -> ParseResult<()> {
|
||||
if value < 1 || value > 12 { return Err(OUT_OF_RANGE); }
|
||||
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
|
||||
/// [`hour_mod_12`](#structfield.hour_mod_12) fields from given value.
|
||||
#[inline]
|
||||
pub fn set_hour(&mut self, value: i64) -> ParseResult<()> {
|
||||
let v = value.to_u32().ok_or(OUT_OF_RANGE)?;
|
||||
set_if_consistent(&mut self.hour_div_12, v / 12)?;
|
||||
set_if_consistent(&mut self.hour_mod_12, v % 12)?;
|
||||
let v = try!(value.to_u32().ok_or(OUT_OF_RANGE));
|
||||
try!(set_if_consistent(&mut self.hour_div_12, v / 12));
|
||||
try!(set_if_consistent(&mut self.hour_mod_12, v % 12));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to set the [`minute`](#structfield.minute) field from given value.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> {
|
||||
set_if_consistent(&mut self.timestamp, value)
|
||||
}
|
||||
|
||||
/// Tries to set the [`offset`](#structfield.offset) field from given value.
|
||||
#[inline]
|
||||
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.
|
||||
|
@ -316,7 +294,7 @@ impl Parsed {
|
|||
(None, Some(q), Some(r @ 0...99)) => {
|
||||
if q < 0 { return Err(OUT_OF_RANGE); }
|
||||
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.
|
||||
|
@ -330,9 +308,9 @@ impl Parsed {
|
|||
}
|
||||
|
||||
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 =
|
||||
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.
|
||||
let verify_ymd = |date: NaiveDate| {
|
||||
|
@ -388,20 +366,20 @@ impl Parsed {
|
|||
let (verified, parsed_date) = match (given_year, given_isoyear, self) {
|
||||
(Some(year), _, &Parsed { month: Some(month), day: Some(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)
|
||||
},
|
||||
|
||||
(Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => {
|
||||
// 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)
|
||||
},
|
||||
|
||||
(Some(year), _, &Parsed { week_from_sun: Some(week_from_sun),
|
||||
weekday: Some(weekday), .. }) => {
|
||||
// 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() {
|
||||
Weekday::Sun => 0,
|
||||
Weekday::Mon => 6,
|
||||
|
@ -416,8 +394,8 @@ impl Parsed {
|
|||
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
|
||||
weekday.num_days_from_sunday() as i32;
|
||||
let date = newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||
.ok_or(OUT_OF_RANGE)?;
|
||||
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||
.ok_or(OUT_OF_RANGE));
|
||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||
|
||||
(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),
|
||||
weekday: Some(weekday), .. }) => {
|
||||
// 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() {
|
||||
Weekday::Sun => 1,
|
||||
Weekday::Mon => 0,
|
||||
|
@ -441,8 +419,8 @@ impl Parsed {
|
|||
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
|
||||
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
|
||||
weekday.num_days_from_monday() as i32;
|
||||
let date = newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||
.ok_or(OUT_OF_RANGE)?;
|
||||
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
|
||||
.ok_or(OUT_OF_RANGE));
|
||||
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
|
||||
|
||||
(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), .. }) => {
|
||||
// ISO year, week, day of the week
|
||||
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)
|
||||
},
|
||||
|
||||
|
@ -547,9 +525,9 @@ impl Parsed {
|
|||
}
|
||||
|
||||
// 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 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.
|
||||
// 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.
|
||||
} else {
|
||||
parsed.set_second(i64::from(datetime.second()))?;
|
||||
try!(parsed.set_second(i64::from(datetime.second())));
|
||||
}
|
||||
parsed.set_year (i64::from(datetime.year()))?;
|
||||
parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd
|
||||
parsed.set_hour (i64::from(datetime.hour()))?;
|
||||
parsed.set_minute (i64::from(datetime.minute()))?;
|
||||
try!(parsed.set_year (i64::from(datetime.year())));
|
||||
try!(parsed.set_ordinal(i64::from(datetime.ordinal()))); // more efficient than ymd
|
||||
try!(parsed.set_hour (i64::from(datetime.hour())));
|
||||
try!(parsed.set_minute (i64::from(datetime.minute())));
|
||||
|
||||
// validate other fields (e.g. week) and return
|
||||
let date = parsed.to_naive_date()?;
|
||||
let time = parsed.to_naive_time()?;
|
||||
let date = try!(parsed.to_naive_date());
|
||||
let time = try!(parsed.to_naive_time());
|
||||
Ok(date.and_time(time))
|
||||
} else {
|
||||
// reproduce the previous error(s)
|
||||
date?;
|
||||
time?;
|
||||
try!(date);
|
||||
try!(time);
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -597,9 +575,9 @@ impl Parsed {
|
|||
/// plus a time zone offset.
|
||||
/// Either way those fields have to be consistent to each other.
|
||||
pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
|
||||
let offset = self.offset.ok_or(NOT_ENOUGH)?;
|
||||
let datetime = self.to_naive_datetime_with_offset(offset)?;
|
||||
let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
|
||||
let offset = try!(self.offset.ok_or(NOT_ENOUGH));
|
||||
let datetime = try!(self.to_naive_datetime_with_offset(offset));
|
||||
let offset = try!(FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE));
|
||||
match offset.from_local_datetime(&datetime) {
|
||||
LocalResult::None => Err(IMPOSSIBLE),
|
||||
LocalResult::Single(t) => Ok(t),
|
||||
|
@ -624,7 +602,7 @@ impl Parsed {
|
|||
// an empty `nanosecond` is always equal to zero, so missing nanosecond is fine.
|
||||
let nanosecond = self.nanosecond.unwrap_or(0);
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -639,7 +617,7 @@ impl Parsed {
|
|||
|
||||
// `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.
|
||||
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) {
|
||||
LocalResult::None => 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.
|
||||
/// More than `max` digits are consumed up to the first `max` digits.
|
||||
/// Any number that does not fit in `i64` is an error.
|
||||
#[inline]
|
||||
pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
|
||||
assert!(min <= max);
|
||||
|
||||
// We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on
|
||||
// the first non-numeric byte, which may be another ascii character or beginning of multi-byte
|
||||
// UTF-8 character.
|
||||
let bytes = s.as_bytes();
|
||||
if bytes.len() < min {
|
||||
return Err(TOO_SHORT);
|
||||
// limit `s` to given number of digits
|
||||
let mut window = s.as_bytes();
|
||||
if window.len() > max { window = &window[..max]; }
|
||||
|
||||
// scan digits
|
||||
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;
|
||||
for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied()
|
||||
if c < b'0' || b'9' < c {
|
||||
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))
|
||||
// we can overflow here, which is the only possible cause of error from `parse`.
|
||||
let v: i64 = try!(s[..upto].parse().map_err(|_| OUT_OF_RANGE));
|
||||
Ok((&s[upto..], v))
|
||||
}
|
||||
|
||||
/// 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)> {
|
||||
// record the number of digits consumed for later scaling.
|
||||
let origlen = s.len();
|
||||
let (s, v) = number(s, 1, 9)?;
|
||||
let (s, v) = try!(number(s, 1, 9));
|
||||
let consumed = origlen - s.len();
|
||||
|
||||
// scale the number accordingly.
|
||||
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
||||
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.
|
||||
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).
|
||||
pub fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> {
|
||||
// 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.
|
||||
static SCALE: [i64; 10] = [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000,
|
||||
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))
|
||||
}
|
||||
|
@ -140,7 +128,7 @@ pub fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
|
|||
static LONG_MONTH_SUFFIXES: [&'static str; 12] =
|
||||
["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
|
||||
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] =
|
||||
["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
|
||||
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..];
|
||||
|
||||
// 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')),
|
||||
_ => return Err(INVALID),
|
||||
};
|
||||
s = &s[2..];
|
||||
|
||||
// colons (and possibly other separators)
|
||||
s = consume_colon(s)?;
|
||||
s = try!(consume_colon(s));
|
||||
|
||||
// minutes (00--59)
|
||||
// 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}))
|
||||
}
|
||||
|
||||
/// 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)
|
||||
-> ParseResult<(&str, i32)>
|
||||
where F: FnMut(&str) -> ParseResult<&str>
|
||||
{
|
||||
let bytes = s.as_bytes();
|
||||
match bytes.first() {
|
||||
match s.as_bytes().first() {
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn timezone_offset_permissive<F>(s: &str, colon: F)
|
||||
-> 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`).
|
||||
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
|
||||
let (s_, offset) = try!(timezone_offset(s, |s| Ok(s)));
|
||||
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 |
|
||||
|-------|----------|----------------------------------------------------------------------------|
|
||||
| | | **DATE SPECIFIERS:** |
|
||||
| `%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] |
|
||||
| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^2] |
|
||||
| `%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] |
|
||||
| `%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. |
|
||||
| `%b` | `Jul` | Abbreviated month name. Always 3 letters. |
|
||||
| `%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. |
|
||||
| `%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` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. |
|
||||
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
|
||||
| `%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] |
|
||||
| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.|
|
||||
| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [3] |
|
||||
| `%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` | `01` | Same as `%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] |
|
||||
| `%G` | `2001` | Same to `%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 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. |
|
||||
| | | |
|
||||
| `%D` | `07/08/01` | Month-day-year format. Same as `%m/%d/%y`. |
|
||||
| `%x` | `07/08/01` | Same as `%D`. |
|
||||
| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`. |
|
||||
| `%v` | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`. |
|
||||
| `%D` | `07/08/01` | Month-day-year format. Same to `%m/%d/%y`. |
|
||||
| `%x` | `07/08/01` | Same to `%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 to `%e-%b-%Y`. |
|
||||
| | | |
|
||||
| | | **TIME SPECIFIERS:** |
|
||||
| `%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. |
|
||||
| `%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. |
|
||||
| | | |
|
||||
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
|
||||
| `%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` | `.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] |
|
||||
| `%.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] |
|
||||
| `%3f` | `026` | Similar to `%.3f` 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] |
|
||||
| `%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` | `.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] |
|
||||
| `%.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] |
|
||||
| `%3f` | `026` | Similar to `%.3f` 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] |
|
||||
| | | |
|
||||
| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. |
|
||||
| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. |
|
||||
| `%X` | `00:34:60` | Same as `%T`. |
|
||||
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. |
|
||||
| `%R` | `00:34` | Hour-minute format. Same to `%H:%M`. |
|
||||
| `%T` | `00:34:60` | Hour-minute-second format. Same to `%H:%M:%S`. |
|
||||
| `%X` | `00:34:60` | Same to `%T`. |
|
||||
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same to `%I:%M:%S %p`. |
|
||||
| | | |
|
||||
| | | **TIME ZONE SPECIFIERS:** |
|
||||
| `%Z` | `ACST` | *Formatting only:* Local time zone name. |
|
||||
| `%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` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. |
|
||||
| `%:z` | `+09:30` | Same to `%z` but with a colon. |
|
||||
| `%#z` | `+09` | *Parsing only:* Same to `%z` but allows minutes to be missing or present. |
|
||||
| | | |
|
||||
| | | **DATE & TIME SPECIFIERS:** |
|
||||
|`%c`|`Sun Jul 8 00:34:60 2001`|`ctime` date & time format. Same as `%a %b %e %T %Y` sans `\n`.|
|
||||
| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^6] |
|
||||
|`%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] |
|
||||
| | | |
|
||||
| `%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:** |
|
||||
| `%t` | | Literal tab (`\t`). |
|
||||
|
@ -95,62 +95,59 @@ Modifier | Description
|
|||
|
||||
Notes:
|
||||
|
||||
[^1]: `%Y`:
|
||||
1. `%Y`:
|
||||
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.
|
||||
|
||||
[^3]: `%U`:
|
||||
3. `%U`:
|
||||
Week 1 starts with the first Sunday in that year.
|
||||
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 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.
|
||||
|
||||
[^6]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional
|
||||
digits for seconds and colons in the time zone offset.
|
||||
<br>
|
||||
<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.
|
||||
6. `%+`:
|
||||
Same to `%Y-%m-%dT%H:%M:%S%.f%:z`,
|
||||
i.e. 0, 3, 6 or 9 fractional digits for seconds and colons in the time zone offset.
|
||||
|
||||
[^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.
|
||||
For the purpose of Chrono, it only accounts for non-leap seconds
|
||||
so it slightly differs from ISO C `strftime` behavior.
|
||||
|
||||
[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
|
||||
<br>
|
||||
8. `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
|
||||
|
||||
The default `%f` is right-aligned and always zero-padded to 9 digits
|
||||
for the compatibility with glibc and others,
|
||||
so it always counts the number of nanoseconds since the last whole second.
|
||||
E.g. 7ms after the last second will print `007000000`,
|
||||
and parsing `7000000` will yield the same.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
The variant `%.f` is left-aligned and print 0, 3, 6 or 9 fractional digits
|
||||
according to the precision.
|
||||
E.g. 70ms after the last second under `%.f` will print `.070` (note: not `.07`),
|
||||
and parsing `.07`, `.070000` etc. will yield the same.
|
||||
Note that they can print or read nothing if the fractional part is zero or
|
||||
the next character is not `.`.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
The variant `%.3f`, `%.6f` and `%.9f` are left-aligned and print 3, 6 or 9 fractional digits
|
||||
according to the number preceding `f`.
|
||||
E.g. 70ms after the last second under `%.3f` will print `.070` (note: not `.07`),
|
||||
and parsing `.07`, `.070000` etc. will yield the same.
|
||||
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.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
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.
|
||||
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
|
||||
//! 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.
|
||||
//! 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`.
|
||||
//! Note that this is an "accurate" duration represented as seconds and
|
||||
//! nanoseconds and does not represent "nominal" components such as days or
|
||||
|
@ -383,12 +383,9 @@
|
|||
|
||||
#![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_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
|
||||
// backward compatibility, and this appeases clippy. If minimum rustc
|
||||
|
@ -406,13 +403,6 @@
|
|||
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")]
|
||||
extern crate time as oldtime;
|
||||
extern crate num_integer;
|
||||
|
@ -424,12 +414,10 @@ extern crate serde as serdelib;
|
|||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
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;
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
|
||||
extern crate js_sys;
|
||||
#[cfg(feature = "bench")]
|
||||
extern crate test;
|
||||
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
@ -491,10 +479,6 @@ pub mod naive {
|
|||
#[allow(deprecated)]
|
||||
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
|
||||
///
|
||||
|
@ -514,10 +498,6 @@ mod datetime;
|
|||
pub mod format;
|
||||
mod round;
|
||||
|
||||
#[cfg(feature = "__internal_bench")]
|
||||
#[doc(hidden)]
|
||||
pub use naive::__BenchYearFlags;
|
||||
|
||||
/// Serialization/Deserialization in alternate formats
|
||||
///
|
||||
/// 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::*;
|
||||
}
|
||||
|
||||
// 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 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
|
||||
/// [`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.
|
||||
|
@ -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`.
|
||||
#[derive(Clone, PartialEq)]
|
||||
|
@ -766,14 +697,14 @@ impl fmt::Debug for ParseWeekdayError {
|
|||
#[cfg(feature = "serde")]
|
||||
mod weekday_serde {
|
||||
use super::Weekday;
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
impl ser::Serialize for Weekday {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
{
|
||||
serializer.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.
|
||||
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};
|
||||
///
|
||||
/// 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(1970, 1, 1).num_days_from_ce(), 719163);
|
||||
/// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365);
|
||||
/// ```
|
||||
/// ~~~
|
||||
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.
|
||||
let mut year = self.year() - 1;
|
||||
let mut ndays = 0;
|
||||
|
@ -1074,52 +1000,3 @@ fn test_readme_doomsday() {
|
|||
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.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::{str, fmt};
|
||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use std::{str, fmt};
|
||||
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use num_traits::ToPrimitive;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
|
@ -14,9 +12,7 @@ use {Weekday, Datelike};
|
|||
use div::div_mod_floor;
|
||||
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
|
||||
use format::{Item, Numeric, Pad};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::DelayedFormat;
|
||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||
|
||||
use super::isoweek;
|
||||
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 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.
|
||||
#[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
|
||||
/// January 1, 1 being day 1.
|
||||
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (Day 1)
|
||||
/// in the proleptic Gregorian calendar.
|
||||
///
|
||||
/// Panics if the date is out of range.
|
||||
/// Panics on the out-of-range date.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -382,10 +378,10 @@ impl NaiveDate {
|
|||
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
|
||||
/// January 1, 1 being day 1.
|
||||
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (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
|
||||
///
|
||||
|
@ -411,55 +407,6 @@ impl NaiveDate {
|
|||
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`.
|
||||
/// See the [`format::strftime` module](../format/strftime/index.html)
|
||||
/// on the supported escape sequences.
|
||||
|
@ -504,7 +451,7 @@ impl NaiveDate {
|
|||
/// ~~~~
|
||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||
parsed.to_naive_date()
|
||||
}
|
||||
|
||||
|
@ -943,7 +890,7 @@ impl NaiveDate {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
||||
|
@ -969,10 +916,9 @@ impl NaiveDate {
|
|||
/// # let d = NaiveDate::from_ymd(2015, 9, 5);
|
||||
/// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
||||
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=Item<'a>> + Clone {
|
||||
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("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
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,
|
||||
/// 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.
|
||||
/// 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).
|
||||
///
|
||||
/// 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).
|
||||
///
|
||||
/// 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> {
|
||||
const ITEMS: &'static [Item<'static>] = &[
|
||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
|
||||
Item::Space(""), Item::Literal("-"),
|
||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
|
||||
Item::Space(""), Item::Literal("-"),
|
||||
Item::Numeric(Numeric::Day, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
|
||||
Item::Space(""),
|
||||
];
|
||||
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, ITEMS.iter())?;
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
parsed.to_naive_date()
|
||||
}
|
||||
}
|
||||
|
@ -1655,7 +1600,7 @@ mod rustc_serialize {
|
|||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use super::NaiveDate;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
|
@ -1689,18 +1634,10 @@ mod serde {
|
|||
write!(formatter, "a formatted date string")
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
fn visit_str<E>(self, value: &str) -> Result<NaiveDate, E>
|
||||
where E: de::Error
|
||||
{
|
||||
value.parse().map_err(E::custom)
|
||||
}
|
||||
|
||||
#[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)
|
||||
value.parse().map_err(|err| E::custom(format!("{}", err)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1886,24 +1823,6 @@ mod tests {
|
|||
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]
|
||||
fn test_date_fields() {
|
||||
fn check(year: i32, month: u32, day: u32, ordinal: u32) {
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
|
||||
//! ISO 8601 date and time without timezone.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::{str, fmt, hash};
|
||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use std::{str, fmt, hash};
|
||||
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use num_traits::ToPrimitive;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
|
@ -14,9 +12,7 @@ use {Weekday, Timelike, Datelike};
|
|||
use div::div_mod_floor;
|
||||
use naive::{NaiveTime, NaiveDate, IsoWeek};
|
||||
use format::{Item, Numeric, Pad, Fixed};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::DelayedFormat;
|
||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||
|
||||
/// 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.
|
||||
|
@ -210,7 +206,7 @@ impl NaiveDateTime {
|
|||
/// ~~~~
|
||||
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -317,7 +313,7 @@ impl NaiveDateTime {
|
|||
/// 2262-04-11T23:47:16.854775804.
|
||||
///
|
||||
/// (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
|
||||
///
|
||||
|
@ -623,7 +619,7 @@ impl NaiveDateTime {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
/// 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);
|
||||
/// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
||||
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=Item<'a>> + Clone {
|
||||
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("around %l %p on %b %-d")), "around 11 PM on Sep 5");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
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`.
|
||||
/// 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),
|
||||
/// 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).
|
||||
///
|
||||
/// 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).
|
||||
///
|
||||
/// 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> {
|
||||
const ITEMS: &'static [Item<'static>] = &[
|
||||
Item::Numeric(Numeric::Year, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
|
||||
Item::Space(""), Item::Literal("-"),
|
||||
Item::Numeric(Numeric::Month, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
|
||||
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::Numeric(Numeric::Hour, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||
Item::Space(""), Item::Literal(":"),
|
||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||
Item::Space(""), Item::Literal(":"),
|
||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
|
||||
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
||||
];
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1669,7 +1663,7 @@ pub mod rustc_serialize {
|
|||
/// Tools to help serializing/deserializing `NaiveDateTime`s
|
||||
#[cfg(feature = "serde")]
|
||||
pub mod serde {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use super::{NaiveDateTime};
|
||||
use serdelib::{ser, de};
|
||||
|
||||
|
@ -1708,7 +1702,7 @@ pub mod serde {
|
|||
fn visit_str<E>(self, value: &str) -> Result<NaiveDateTime, E>
|
||||
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(); }
|
||||
/// ```
|
||||
pub mod ts_nanoseconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {NaiveDateTime, ne_timestamp};
|
||||
use NaiveDateTime;
|
||||
|
||||
/// 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>
|
||||
where D: de::Deserializer<'de>
|
||||
{
|
||||
Ok(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)?)
|
||||
Ok(try!(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)))
|
||||
}
|
||||
|
||||
struct NaiveDateTimeFromNanoSecondsVisitor;
|
||||
|
@ -1852,7 +1846,7 @@ pub mod serde {
|
|||
{
|
||||
NaiveDateTime::from_timestamp_opt(value / 1_000_000_000,
|
||||
(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>
|
||||
|
@ -1860,7 +1854,7 @@ pub mod serde {
|
|||
{
|
||||
NaiveDateTime::from_timestamp_opt(value as i64 / 1_000_000_000,
|
||||
(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(); }
|
||||
/// ```
|
||||
pub mod ts_milliseconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {NaiveDateTime, ne_timestamp};
|
||||
use NaiveDateTime;
|
||||
|
||||
/// 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>
|
||||
where D: de::Deserializer<'de>
|
||||
{
|
||||
Ok(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)?)
|
||||
Ok(try!(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)))
|
||||
}
|
||||
|
||||
struct NaiveDateTimeFromMilliSecondsVisitor;
|
||||
|
@ -1997,7 +1991,7 @@ pub mod serde {
|
|||
{
|
||||
NaiveDateTime::from_timestamp_opt(value / 1000,
|
||||
((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>
|
||||
|
@ -2005,7 +1999,7 @@ pub mod serde {
|
|||
{
|
||||
NaiveDateTime::from_timestamp_opt((value / 1000) as i64,
|
||||
((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(); }
|
||||
/// ```
|
||||
pub mod ts_seconds {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
use {NaiveDateTime, ne_timestamp};
|
||||
use NaiveDateTime;
|
||||
|
||||
/// 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>
|
||||
where D: de::Deserializer<'de>
|
||||
{
|
||||
Ok(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)?)
|
||||
Ok(try!(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)))
|
||||
}
|
||||
|
||||
struct NaiveDateTimeFromSecondsVisitor;
|
||||
|
@ -2141,14 +2135,14 @@ pub mod serde {
|
|||
where E: de::Error
|
||||
{
|
||||
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>
|
||||
where E: de::Error
|
||||
{
|
||||
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.
|
||||
|
||||
#![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 Weekday;
|
||||
use div::{div_rem, mod_floor};
|
||||
|
@ -471,6 +470,7 @@ impl fmt::Debug for Mdf {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(test)] extern crate num_iter;
|
||||
#[cfg(bench)] extern crate test;
|
||||
|
||||
use Weekday;
|
||||
use super::{Of, Mdf};
|
||||
|
@ -517,6 +517,16 @@ mod tests {
|
|||
assert_eq!(GF.nisoweeks(), 52);
|
||||
}
|
||||
|
||||
#[cfg(bench)]
|
||||
#[bench]
|
||||
fn bench_year_flags_from_year(bh: &mut test::Bencher) {
|
||||
bh.iter(|| {
|
||||
for year in -999i32..1000 {
|
||||
YearFlags::from_year(year);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_of() {
|
||||
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
//! ISO 8601 week.
|
||||
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
|
||||
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)
|
||||
/// where `d` is any `NaiveDate` value in that week.
|
||||
///
|
||||
|
|
|
@ -3,18 +3,14 @@
|
|||
|
||||
//! ISO 8601 time without timezone.
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::{str, fmt, hash};
|
||||
use core::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use std::{str, fmt, hash};
|
||||
use std::ops::{Add, Sub, AddAssign, SubAssign};
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
use Timelike;
|
||||
use div::div_mod_floor;
|
||||
use format::{Item, Numeric, Pad, Fixed};
|
||||
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use format::DelayedFormat;
|
||||
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
|
||||
|
||||
/// ISO 8601 time without timezone.
|
||||
/// 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> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
|
||||
parsed.to_naive_time()
|
||||
}
|
||||
|
||||
|
@ -685,7 +681,7 @@ impl NaiveTime {
|
|||
// `rhs.frac`|========================================>|
|
||||
// | | | `self - rhs` | |
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let secs = i64::from(self.secs) - i64::from(rhs.secs);
|
||||
let frac = i64::from(self.frac) - i64::from(rhs.frac);
|
||||
|
@ -701,7 +697,7 @@ impl NaiveTime {
|
|||
}
|
||||
|
||||
/// 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,
|
||||
/// since the resulting `DelayedFormat` value may be formatted multiple times.
|
||||
|
@ -727,10 +723,9 @@ impl NaiveTime {
|
|||
/// # let t = NaiveTime::from_hms(23, 56, 4);
|
||||
/// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
|
||||
pub fn format_with_items<'a, I>(&self, items: I) -> DelayedFormat<I>
|
||||
where I: Iterator<Item=Item<'a>> + Clone {
|
||||
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("%-I:%M %p")), "11:56 PM");
|
||||
/// ~~~~
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[inline]
|
||||
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
|
||||
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.
|
||||
/// 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),
|
||||
/// 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).
|
||||
///
|
||||
/// 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)
|
||||
};
|
||||
|
||||
write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?;
|
||||
try!(write!(f, "{:02}:{:02}:{:02}", hour, min, sec));
|
||||
if nano == 0 {
|
||||
Ok(())
|
||||
} 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).
|
||||
///
|
||||
/// 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> {
|
||||
const ITEMS: &'static [Item<'static>] = &[
|
||||
Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
|
||||
Item::Space(""), Item::Literal(":"),
|
||||
Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
|
||||
Item::Space(""), Item::Literal(":"),
|
||||
Item::Numeric(Numeric::Second, Pad::Zero),
|
||||
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
|
||||
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
|
||||
];
|
||||
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, ITEMS.iter())?;
|
||||
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
|
||||
parsed.to_naive_time()
|
||||
}
|
||||
}
|
||||
|
@ -1417,7 +1411,7 @@ mod rustc_serialize {
|
|||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde {
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
use super::NaiveTime;
|
||||
use serdelib::{ser, de};
|
||||
|
||||
|
@ -1445,7 +1439,7 @@ mod serde {
|
|||
fn visit_str<E>(self, value: &str) -> Result<NaiveTime, E>
|
||||
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.
|
||||
|
||||
use core::ops::{Add, Sub};
|
||||
use core::fmt;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::fmt;
|
||||
use oldtime::Duration as OldDuration;
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
// 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
|
||||
// but keep keeps the leap second information.
|
||||
// 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.
|
||||
#[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> {
|
||||
tm_to_datetime(oldtime::now())
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
use super::Utc;
|
||||
let now: DateTime<Utc> = super::Utc::now();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
//! and provides implementations for 1 and 3.
|
||||
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
|
||||
|
||||
use core::fmt;
|
||||
use std::fmt;
|
||||
|
||||
use format::{parse, ParseResult, Parsed, StrftimeItems};
|
||||
use naive::{NaiveDate, NaiveDateTime, NaiveTime};
|
||||
|
@ -415,7 +415,7 @@ pub trait TimeZone: Sized + Clone {
|
|||
/// with parsed `FixedOffset`.
|
||||
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,8 @@
|
|||
|
||||
//! The UTC (Coordinated Universal Time) time zone.
|
||||
|
||||
use core::fmt;
|
||||
#[cfg(all(
|
||||
feature = "clock",
|
||||
not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))
|
||||
))]
|
||||
use std::fmt;
|
||||
#[cfg(all(feature="clock", not(all(target_arch = "wasm32", feature = "wasmbind"))))]
|
||||
use oldtime;
|
||||
|
||||
use naive::{NaiveDate, NaiveDateTime};
|
||||
|
@ -41,7 +38,7 @@ impl Utc {
|
|||
pub fn today() -> Date<Utc> { Utc::now().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> {
|
||||
let spec = oldtime::get_time();
|
||||
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.
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
|
||||
#[cfg(all(target_arch = "wasm32", feature = "wasmbind"))]
|
||||
pub fn now() -> DateTime<Utc> {
|
||||
let now = js_sys::Date::new_0();
|
||||
let millisecs_since_unix_epoch: u64 = now.get_time() as u64;
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
|
||||
//! Temporal quantification
|
||||
|
||||
use core::{fmt, i64};
|
||||
#[cfg(any(feature = "std", test))]
|
||||
use std::{fmt, i64};
|
||||
use std::error::Error;
|
||||
use core::ops::{Add, Sub, Mul, Div, Neg};
|
||||
use core::time::Duration as StdDuration;
|
||||
use std::ops::{Add, Sub, Mul, Div, Neg};
|
||||
use std::time::Duration as StdDuration;
|
||||
|
||||
/// The number of nanoseconds in a microsecond.
|
||||
const NANOS_PER_MICRO: i32 = 1000;
|
||||
|
@ -364,20 +363,20 @@ impl fmt::Display for Duration {
|
|||
let hasdate = days != 0;
|
||||
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
|
||||
|
||||
write!(f, "{}P", sign)?;
|
||||
try!(write!(f, "{}P", sign));
|
||||
|
||||
if hasdate {
|
||||
write!(f, "{}D", days)?;
|
||||
try!(write!(f, "{}D", days));
|
||||
}
|
||||
if hastime {
|
||||
if abs.nanos == 0 {
|
||||
write!(f, "T{}S", secs)?;
|
||||
try!(write!(f, "T{}S", secs));
|
||||
} 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 {
|
||||
write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
|
||||
try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO));
|
||||
} else {
|
||||
write!(f, "T{}.{:09}S", secs, abs.nanos)?;
|
||||
try!(write!(f, "T{}.{:09}S", secs, abs.nanos));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -395,15 +394,13 @@ pub struct OutOfRangeError(());
|
|||
|
||||
impl fmt::Display for OutOfRangeError {
|
||||
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 {
|
||||
#[allow(deprecated)]
|
||||
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.
|
||||
|
||||
use Timelike;
|
||||
use core::ops::{Add, Sub};
|
||||
use std::ops::{Add, Sub};
|
||||
use oldtime::Duration;
|
||||
|
||||
/// 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 {
|
||||
extern crate chrono;
|
||||
extern crate wasm_bindgen_test;
|
||||
|
|
Loading…
Reference in New Issue