Compare commits

..

No commits in common. "master" and "no-std" have entirely different histories.

29 changed files with 616 additions and 1780 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
rustc --version
cargo --version
node --version
banner() {
echo "======================================================================"
echo "$*"
echo "======================================================================"
}
CHANNEL=nightly
if [ "x${CLIPPY}" = xy ] ; then
run_clippy
else
build_and_test
fi
underline() {
echo "$*"
echo "${*//?/^}"
}
CHANNEL=beta
build_and_test
matching_banner() {
if channel_matches || ! is_ci ; then
banner "$*"
echo_versions
fi
}
CHANNEL=stable
build_and_test
echo_versions() {
channel_run rustc --version
channel_run cargo --version
node --version
}
channel() {
channel_run cargo "$@"
}
channel_run() {
if channel_matches ; then
local the_cmd="$ $*"
underline "$the_cmd"
"$@"
elif ! is_ci ; then
local cmd="$1"
shift
if [[ $cmd == cargo || $cmd == rustc ]] ; then
underline "$ $cmd +${CHANNEL} $*"
"$cmd" "+${CHANNEL}" "$@"
else
underline "$ $cmd $*"
"$cmd" "$@"
fi
fi
}
channel_matches() {
if is_ci ; then
if [[ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}"
|| "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]] ; then
return 0
fi
fi
return 1
}
is_ci() {
if [[ -n "$TRAVIS" || -n "$APPVEYOR" ]] ; then
return 0
else
return 1
fi
}
main
CHANNEL=1.13.0
build_only

View File

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

View File

@ -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,
V: fmt::Display,
T: fmt::Display,
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));
}
}

View File

@ -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) {
// 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),
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),
}
} else {
match pad {
Pad::None => write!(result, "{}", v),
Pad::Zero => write!(result, "{:01$}", v, width),
Pad::Space => write!(result, "{:1$}", v, width),
}
}
} else {
match pad {
&Pad::None => write!(result, "{}", v),
&Pad::Zero => write!(result, "{:01$}", v, width),
&Pad::Space => write!(result, "{:1$}", v, width),
}
}?
)
} else {
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())
}

View File

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

View File

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

View File

@ -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))?;
Ok((s_, Some(offset)))
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)))
}
}
}

View File

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

View File

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

View File

@ -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.
@ -1442,7 +1387,7 @@ impl SubAssign<OldDuration> for NaiveDate {
/// Subtracts another `NaiveDate` from the current date.
/// Returns a `Duration` of integral numbers.
///
///
/// This does not overflow or underflow at all,
/// as all possible output fits in the range of `Duration`.
///
@ -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};
@ -1684,23 +1629,15 @@ mod serde {
impl<'de> de::Visitor<'de> for NaiveDateVisitor {
type Value = NaiveDate;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
{
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) {

View File

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

View File

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

View File

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

View File

@ -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};
@ -1437,7 +1431,7 @@ mod serde {
impl<'de> de::Visitor<'de> for NaiveTimeVisitor {
type Value = NaiveTime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result
{
write!(formatter, "a formatted time string")
}
@ -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)))
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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