Compare commits

...

82 Commits

Author SHA1 Message Date
Brandon W Maister e493218d68
Update changelog for 0.4.11 (#399) 2020-03-07 16:16:02 -05:00
Brandon W Maister 725d88e0dc
Merge pull request #395 from vallentin/master
Fixed typos
2020-03-07 15:34:35 -05:00
Brandon W Maister 9041ce5b05
Merge pull request #396 from ignatenkobrain/fix-cargo-test
Fix cargo test with latest serde_json
2020-03-07 15:33:44 -05:00
Igor Gnatenko 7d37413665
Fix cargo test with latest serde_json
```rust
[  117s] error: expected item, found `"serde_json requires that either `std` (default) or `alloc` feature is enabled"`
[  117s]  --> /usr/share/cargo/registry/serde_json-1.0.48/src/features_check/error.rs:1:1
[  117s]   |
[  117s] 1 | "serde_json requires that either `std` (default) or `alloc` feature is enabled"
[  117s]   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected item
```
2020-02-18 16:57:21 +01:00
vallentin 89da02ebd5 Fixed typos 2020-02-11 21:08:21 +01:00
Brandon W Maister 4837b568c2
Merge pull request #381 from waldyrious/same-to_same-as
Reword 'same to' → 'same as'
2019-12-30 17:17:46 -05:00
Brandon W Maister 9397ab2b47
Merge pull request #375 from dlalic/354
Support comparison between dts with different timezones. Fixes #354
2019-12-30 17:16:06 -05:00
Brandon W Maister 4ad95ee602
Merge pull request #378 from quodlibetor/round-trip-display
support round tripping display <-> datetime
2019-12-30 17:15:51 -05:00
Waldir Pimenta 176dfeff87 Reword 'same to' --> 'same as' 2019-12-29 22:27:59 +00:00
Brandon W Maister b9c967b2ac support round tripping display <-> datetime
This extends `FromStr` to allow either a `T` or a ` ` (space) as the delimiter
between the date and the time, and, because of the fact that the `Z`
parser-specifier is shared with the Fixed notation, extends the fixed notation
to support `UTC` in addition to `Z` as the zero-offset.

IMO this Fixes #147
2019-12-27 13:57:02 -05:00
Brandon W Maister 3b295ccb8f Add doctest for comparison between timestamps with different time_zones 2019-12-27 13:49:53 -05:00
Dunja Lalic ad8644ea57 Fixes #354 2019-12-26 20:32:45 -05:00
Brandon W Maister b9cd0ce803
Merge pull request #270 from asayers/from_ymwd
Construct a NaiveDate by specifying eg. "the 2nd Friday of March 2017"
2019-12-26 20:17:17 -05:00
Brandon W Maister 5d16648df4 rename from_ymwd -> from_weekday_of_month 2019-12-23 17:33:44 -05:00
Brandon W Maister b5015c634b
Merge pull request #348 from gThorondorsen/test-num_days_from_ce
Test `Datelike::num_days_from_ce` against an alternative implementation
2019-12-23 17:19:43 -05:00
Brandon W Maister d9d0728f51
Merge pull request #374 from quodlibetor/fix-playground-and-docs
Change docs and playground features so that it compiles
2019-12-23 14:16:02 -05:00
Brandon W Maister 6daed98412
Merge pull request #376 from AnderEnder/remove-deprecated-error-description
Allow deprecated Error::description and replace it with a stub implementation.
2019-12-23 13:48:56 -05:00
Brandon W Maister 536ac86511 Add bench for new implementation
Unfortunately it seems to be slightly more than twice as slow. On the plus
side, that is some justification for the existing optimizations.
2019-12-23 13:46:45 -05:00
Gwaihir Thorondorsen 3e592d4565 Test `Datelike::num_days_from_ce` against an alternative implementation 2019-12-23 13:28:46 -05:00
Brandon W Maister 5ca6ff5afc Make it harder to accidentally use private features
In particular if built with `--all-features` or `features = "all"` we don't
want to accidentally make it look like `YearFlags` are something that people
should use. So: hide it from docs and put it behind a dunder.
2019-12-23 13:26:05 -05:00
Brandon W Maister 4f73f9423b Make criterion a dev dependency
It's much less risky than making it an optional dependency, even if it does
mean that everyone working on chrono has to compile it.
2019-12-23 12:51:11 -05:00
Brandon W Maister 977ad60391 allow_deprecated on stub Error::description for rust 1.13 2019-12-23 12:38:25 -05:00
Andrii Radyk a42c5b22cd remove deprecated Error::description 2019-12-17 09:42:32 +01:00
Brandon W Maister a5e2796931 Change docs and playground features so that it compiles
All features includes some things that are nightly only.

Fixes #373
2019-12-13 11:10:49 -08:00
Brandon W Maister df0e6ae8f6
Merge pull request #370 from quodlibetor/rfc2822-is-http-and-email
Document that rfc2822 is what is used for http and email headers
2019-11-30 19:46:05 -05:00
Brandon W Maister 495243ae3c
Merge pull request #369 from quodlibetor/finish-criterion
Finish moving all benchmarks to criterion
2019-11-30 19:45:37 -05:00
Brandon W Maister d0dbc61b69 Make criterion an optional dependency
So that it doesn't slow down every user's builds and and CI
2019-11-30 18:40:28 -05:00
Brandon W Maister 7a1cd09b86 Document that rfc2822 is what is used for http and email headers
Closes #328
2019-11-30 18:25:36 -05:00
Brandon W Maister 4f1c35827f Finish moving all benchmarks to criterion
I didn't delete some, apparently, and an internal struct had to be made public
to get its benchmark to work.
2019-11-30 18:11:03 -05:00
Brandon W Maister b10e430b1f
Merge pull request #368 from quodlibetor/support-negative-utc-in-rfc2822
Support -0000 as a valid UTC tz in rfc2822
2019-11-30 17:59:22 -05:00
Brandon W Maister b553798f86 Support -0000 as a valid UTC tz in rfc2822
This is a time that is commonly set in some environments, and RFC 5322
explicitly clarifies that we should treat -0000 as UTC[1][2] when interpretting
rfc2822.

Fixes #102

[1]: https://github.com/chronotope/chrono/issues/102#issuecomment-557846931
[2]: https://tools.ietf.org/html/rfc5322#section-3.3
2019-11-30 16:48:35 -05:00
Brandon W Maister 19dd051d22
Merge pull request #365 from coolreader18/fix-wasi
Add support for compilation on wasm32-wasi
2019-11-29 15:03:27 -05:00
Brandon W Maister 00e6edd457
Merge pull request #367 from quodlibetor/0_4_10-compat-notes
Add compatility notes about the new alloc feature in 0.4.10
2019-11-29 15:01:00 -05:00
Brandon W Maister 3532e33ac2 Add compatility notes about the new alloc feature in 0.4.10
Closes #364
2019-11-29 14:19:22 -05:00
Brandon W Maister 596aa19104
Merge pull request #363 from quodlibetor/use-criterion-benchmarks
switch to using criterion for benchmarks
2019-11-29 14:06:43 -05:00
coolreader18 c5973277e7
Don't do wasmbind on wasi 2019-11-26 23:57:51 -06:00
Brandon W Maister 80da04381c add serialization benchmark 2019-11-24 15:05:47 -05:00
Brandon W Maister 8de2cc375f switch to using criterion for benchmarks
It has a ton of great features[1], including stronger statistical signifance
tests, making comparisons to previous or baseline runs, nice plots, and being
able to be run on stable.

1: https://bheisler.github.io/criterion.rs/book/
2019-11-24 13:51:27 -05:00
Brandon W Maister 59059352c1
Merge pull request #361 from quodlibetor/deny-dead-code
Remove unused constant, deny(dead_code)
2019-11-24 12:15:55 -05:00
Brandon W Maister 41700d101e Remove unused constant, deny(dead_code) 2019-11-23 19:58:20 -05:00
Brandon W Maister 5b4fc23bcb
Merge pull request #360 from quodlibetor/chrono-0_4_10
Prepare for and Bump Chrono version to 0.4.10
2019-11-23 19:53:58 -05:00
Brandon W Maister 288f1dd76f
Merge pull request #356 from quodlibetor/num_days_from_epoch-docs
Remove num_days_from_epoch
2019-11-23 19:29:23 -05:00
Brandon W Maister 670561b4c1 Prepare for and Bump Chrono version to 0.4.10 2019-11-23 19:19:04 -05:00
Brandon W Maister 6622bdcae7 Replace all uses of `try!` with question mark
The `?` operator was stabilized in 1.13, this gets rid of a few hundred
deprecation warnings.

Fixes #357
2019-11-23 19:18:08 -05:00
Brandon W Maister 46f8267c61
Merge pull request #358 from michalsrb/optimize-parsing
Optimize parsing
2019-11-23 19:09:12 -05:00
Brandon W Maister b717e0442c Handle some semantic merge conflicts
Something that wasn't part of this PR: the work to support nested
`Option<[ChronoType]>` was merged without being adjusted for the no-std
support

And now there are some unused import warnings because they need to get
configged out.
2019-11-23 18:38:38 -05:00
Brandon W Maister 27f5e5e9b3 Temporarily globally allow deprecated
The question mark will be fixed in a pending commit
2019-11-23 18:22:40 -05:00
Michal Srb 0b8c248791 Fix build with rust 1.13.0 2019-11-23 23:34:59 +01:00
Michal Srb a716b48e9d Inline some parse related functions
Speedups:
 datetime::tests::bench_datetime_from_str             365                         337                       -28   -7.67%   x 1.08
 datetime::tests::bench_datetime_parse_from_rfc2822   195                         181                       -14   -7.18%   x 1.08
 datetime::tests::bench_datetime_parse_from_rfc3339   166                         142                       -24  -14.46%   x 1.17
2019-11-23 23:34:59 +01:00
Michal Srb 6da5359d39 Reimplement scan::number
The original would first check that there is right amount of numeric
characters and then parsed them using the std::str::parse, which
internally checks the characters again and also checks for -/+ prefix,
which is not necessary in this case.

Since we are already going over the characters, we may as well do the
parsing ourselves. The length of the function is roughly the same and
it is faster:

 name                                                 simplify-from-str ns/iter  reimplement-number ns/iter  diff ns/iter   diff %  speedup
 datetime::tests::bench_datetime_from_str             448                        365                                  -83  -18.53%   x 1.23
 datetime::tests::bench_datetime_parse_from_rfc2822   242                        195                                  -47  -19.42%   x 1.24
 datetime::tests::bench_datetime_parse_from_rfc3339   234                        166                                  -68  -29.06%   x 1.41
2019-11-23 23:34:59 +01:00
Michal Srb f7318277e2 Simplify ITEMS in FromStr implementations
The Item::Space calls str::trim_left and Item::Numeric also calls
str::trim_left before doing anything else, so there is no need to
have Item::Space before Item::Numeric.

Speeds up parsing:
 name                                                 remove-cloned ns/iter  simplify-from-str ns/iter  diff ns/iter   diff %  speedup
 datetime::tests::bench_datetime_from_str             582                    448                                -134  -23.02%   x 1.30
 datetime::tests::bench_datetime_parse_from_rfc2822   244                    242                                  -2   -0.82%   x 1.01
 datetime::tests::bench_datetime_parse_from_rfc3339   239                    234                                  -5   -2.09%   x 1.02
2019-11-23 23:34:59 +01:00
Michal Srb 05acc869b9 Accept Borrow<Item> as items
The parse::parse and format::format functions accepted Iterator of owned
Items. While it is sometimes convenient to pass in the owned values,
neither of the functions really need to own them, so references would
be enough. The Borrow trait allows us to pass in Iterator over values,
references, boxes, etc.

According to RFC 1105 this is a minor change, because it shouldn't break
any existing code. And chrono is in pre-1.0 version anyway.

This allows us to remove multiple cloned() calls which speeds up parsing
and formating:

 name                                                 control ns/iter  remove-cloned ns/iter  diff ns/iter   diff %  speedup
 datetime::tests::bench_datetime_from_str             712              582                            -130  -18.26%   x 1.22
 datetime::tests::bench_datetime_parse_from_rfc2822   252              244                              -8   -3.17%   x 1.03
 datetime::tests::bench_datetime_parse_from_rfc3339   242              239                              -3   -1.24%   x 1.01
2019-11-23 23:34:59 +01:00
Michal Srb 53ef941c3a Add benchmarks for DateTime parsing and formatting 2019-11-23 23:34:59 +01:00
Michal Srb db8784f97c Fix existing benchmarks
The #[cfg(bench)] attribute does not exist and is always false. Lets
define a feature "bench" which can be used to enable benchmarks when
building with nightly.
2019-11-23 23:34:59 +01:00
Brandon W Maister d9929a60b4
Merge pull request #341 from CryZe/no-std
Implement Support for no_std
2019-11-22 16:53:09 -05:00
Brandon W Maister 3d385fe2ce
Merge pull request #302 from manifest/feature/option
Add Serialize/Deserialize for Option<DateTime>
2019-11-22 16:07:09 -05:00
Brandon W Maister 7dd7f0c6e7
Merge pull request #359 from quodlibetor/markdown-footnotes
Use markdown footnotes in strftime docs
2019-11-22 15:43:39 -05:00
Brandon W Maister 64a28d6812 Remove core_only, cfg-out the `format::Item::Owned*` variants
This means that a few more features of formatting items don't compile in
non-alloc environments, but they wouldn't have worked correctly anyway.
2019-11-22 15:27:10 -05:00
Brandon W Maister 6a2adc45b7 Use markdown footnotes in strftime docs 2019-11-22 11:30:10 -05:00
Brandon W Maister a74a9744f1 Remove num_days_from_epoch
Deciding between zero-indexed (slightly more reasonable, more compatible with
the outside world) and one-indexed (same indexing as `num_days_from_ce`) seems
not worth it. It's trivial to build `num_days_from_epoch` based on the docs in
`num_days_from_ce`, which punts on the decision and is therefor probably the
right decision.
2019-11-16 16:48:47 -05:00
Brandon W Maister 8e6bc299f8
Merge pull request #349 from gThorondorsen/fix/347
Improve documentation of `num_days_from_ce` methods
2019-11-16 15:17:01 -05:00
Gwaihir Thorondorsen bfdef11d0b Clarify documentation of `num_days_from_ce` methods
The new wording tries to make clearer that those methods use a 1-based
numbering scheme.

This commit also includes a couple of drive-by cosmetic changes.
2019-10-08 19:04:43 +02:00
Brandon W Maister 918cff1f72 Remove some pwds from the CI script
They just add noise, now.
2019-09-20 13:49:30 -04:00
Brandon W Maister 6499c5b1c9 Using core-only on 1.13 doesn't seem to work the same as stable 2019-09-20 13:49:01 -04:00
Brandon W Maister 911dc57402 support rust v1.13.0 struct initialization syntax 2019-09-20 11:41:23 -04:00
Brandon W Maister 9e50bfe034
Merge pull request #344 from quodlibetor/extract-num-days-from-epoch
Add Datelike::num_days_from_epoch
2019-09-20 11:22:58 -04:00
Brandon W Maister 9a3e48931b Make ci script more understandable and robust, hopefully 2019-09-20 11:19:43 -04:00
Brandon W Maister ffcd0f9c19 put bincode back to the version used for testing 2019-09-20 11:02:00 -04:00
Brandon W Maister a09f9ba2a8 Test against serde with no features in both std and no-std 2019-09-20 10:50:28 -04:00
Brandon W Maister 505db4504d Add Datelike::num_days_from_epoch 2019-09-18 17:48:39 -04:00
Brandon W Maister e5bbc94c3b First pass at making "alloc" its own feature
Part of this means that we don't actually need to allocate to write to Serde
any more, which is a win in its own right.
2019-09-15 20:45:49 -04:00
Brandon W Maister 5b72ef3ed6 Make travis.sh and Makefile "nicer"
Slightly easier to reason about the code via some code movement, printing some
banners to make it more obvious when cargo is being run since it is run so many
times.
2019-09-15 20:44:37 -04:00
Brandon W Maister e8c708d81b Make CI able to run non-cargo commands in `channel` 2019-09-13 15:50:20 -04:00
Brandon W Maister 5e1e2d5633 Add a trivial function in core test
To ensure that we don't accidentaly not verify that chrono compiles for core.
2019-09-13 14:50:14 -04:00
Brandon W Maister 4027bbb66d Rename serde-1 back to serde 2019-09-13 14:49:41 -04:00
Christopher Serr e62a054cd9 Introduce an `alloc` feature 2019-09-09 13:51:10 +02:00
Christopher Serr 5ccec02e54 Use std in all other tests on CI 2019-09-07 12:24:11 +02:00
Christopher Serr 9dc91f78ed Implement Support for no_std
This adds a new `std` feature to chrono that is enabled by default. By
deactivating this feature via `default-features = false` you can now use
chrono in applications that don't use the standard library. The `serde`
feature is supported as well.

Resolves #336
2019-09-07 12:12:49 +02:00
Brandon W Maister 97cc89e1fa Merge remote-tracking branch 'origin/master' into feature/option 2019-04-07 17:54:08 -04:00
Andrei Nesterov 662c76294b
Add Serialize/Deserialize for Option<DateTime> 2019-01-27 09:17:42 -08:00
Alex Sayers 892bbf3f3a Add NaiveDate::from_ymwd_opt, implement from_ymwd in terms of it 2018-08-13 11:29:02 +09:00
Alex Sayers 4ffd411654 Add NaiveDate::from_ymwd
This contructor allows you to make a NaiveDate by specifying eg. "the
2nd Friday of March 2017".  It contains a couple of panics, but these
are consistent with the behaviour of the other NaiveDate constructors.
2018-08-13 11:29:02 +09:00
29 changed files with 1780 additions and 616 deletions

View File

@ -8,6 +8,61 @@ 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
@ -52,6 +107,12 @@ 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
@ -187,7 +248,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 faciliate the use of Serde 1.0 at the expense of temporary breakage.
to facilitate 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.
@ -401,7 +462,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 same to `%z` but with a colon.
which is essentially the same as `%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.9"
version = "0.4.11"
authors = [
"Kang Seonghoon <public+rust@mearie.org>",
"Brandon W Maister <quodlibetor@gmail.com>",
@ -24,34 +24,47 @@ appveyor = { repository = "chronotope/chrono" }
name = "chrono"
[features]
default = ["clock"]
clock = ["time"]
default = ["clock", "std"]
alloc = []
std = []
clock = ["time", "std"]
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", optional = true }
serde = { version = "1.0.99", default-features = false, optional = true }
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.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" }
serde_derive = { version = "1", default-features = false }
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(target_os = "emscripten")))'.dev-dependencies]
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
wasm-bindgen-test = "0.2"
[package.metadata.docs.rs]
all-features = true
features = ["serde"]
[package.metadata.playground]
all-features = true
features = ["serde"]
[[bench]]
name = "chrono"
required-features = ["__internal_bench"]
harness = false
[[bench]]
name = "serde"
harness = false
required-features = ["serde"]

View File

@ -1,6 +1,8 @@
# 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.'
@ -20,11 +22,8 @@ README.md: src/lib.rs
.PHONY: test
test:
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'
CHANNEL=$(CHANNEL) ./ci/travis.sh
.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 to the newer, standard type for duration,
Since this has the same name as 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

122
benches/chrono.rs Normal file
View File

@ -0,0 +1,122 @@
//! 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);

30
benches/serde.rs Normal file
View File

@ -0,0 +1,30 @@
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);

14
ci/core-test/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[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"]

7
ci/core-test/src/lib.rs Normal file
View File

@ -0,0 +1,7 @@
#![no_std]
use chrono::{TimeZone, Utc};
pub fn create_time() {
let _ = Utc.ymd(2019, 1, 1).and_hms(0, 0, 0);
}

View File

@ -2,25 +2,50 @@
# 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)"
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 "$@")
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
fi
else
pwd
(set -x; cargo "+${CHANNEL}" "$@")
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
fi
}
@ -54,14 +79,20 @@ build_and_test_nonwasm() {
TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize
# without default "clock" feature
channel build -v --no-default-features
channel build -v --no-default-features --features std
TZ=ACST-9:30 channel test -v --no-default-features --lib
channel build -v --no-default-features --features rustc-serialize
channel build -v --no-default-features --features std,rustc-serialize
TZ=EST4 channel test -v --no-default-features --features rustc-serialize --lib
channel build -v --no-default-features --features serde
channel build -v --no-default-features --features std,serde
TZ=UTC0 channel test -v --no-default-features --features 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
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
}
build_and_test_wasm() {
@ -81,8 +112,16 @@ build_only() {
cargo clean
channel build -v
channel build -v --features rustc-serialize
channel build -v --features 'serde bincode'
channel build -v --no-default-features
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
)
}
run_clippy() {
@ -92,7 +131,7 @@ run_clippy() {
exit
fi
cargo clippy --features 'serde bincode rustc-serialize' -- -Dclippy
cargo clippy --features 'serde rustc-serialize' -- -Dclippy
}
check_readme() {
@ -100,22 +139,70 @@ check_readme() {
(set -x; git diff --exit-code -- README.md) ; echo $?
}
rustc --version
cargo --version
node --version
# script helpers
CHANNEL=nightly
if [ "x${CLIPPY}" = xy ] ; then
run_clippy
else
build_and_test
fi
banner() {
echo "======================================================================"
echo "$*"
echo "======================================================================"
}
CHANNEL=beta
build_and_test
underline() {
echo "$*"
echo "${*//?/^}"
}
CHANNEL=stable
build_and_test
matching_banner() {
if channel_matches || ! is_ci ; then
banner "$*"
echo_versions
fi
}
CHANNEL=1.13.0
build_only
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

View File

@ -3,16 +3,18 @@
//! ISO 8601 calendar date with time zone.
use std::{fmt, hash};
use std::cmp::Ordering;
use std::ops::{Add, Sub};
use core::borrow::Borrow;
use core::{fmt, hash};
use core::cmp::Ordering;
use core::ops::{Add, Sub};
use oldtime::Duration as OldDuration;
use {Weekday, Datelike};
use offset::{TimeZone, Utc};
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
use DateTime;
use format::{Item, DelayedFormat, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))]
use format::{DelayedFormat, Item, StrftimeItems};
/// ISO 8601 calendar date with time zone.
///
@ -53,7 +55,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 purposedly not named to `new` to discourage the direct usage.
// note: this constructor is purposely 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 }
@ -238,7 +240,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Returns a view to the naive local date.
///
/// This is technically same to [`naive_utc`](#method.naive_utc)
/// This is technically the same as [`naive_utc`](#method.naive_utc)
/// because the offset is restricted to never exceed one day,
/// but provided for the consistency.
#[inline]
@ -255,15 +257,17 @@ 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>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
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,20 +3,29 @@
//! ISO 8601 date and time with time zone.
use std::{str, fmt, hash};
use std::cmp::Ordering;
use std::ops::{Add, Sub};
use core::{str, fmt, hash};
use core::cmp::Ordering;
use core::ops::{Add, Sub};
#[cfg(any(feature = "std", test))]
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, Numeric, Pad, Fixed};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
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;
/// Specific formatting options for seconds. This may be extended in the
/// future, so exhaustive matching in external code is not recommended.
@ -73,7 +82,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(Utc.timestamp(61, 0), dt);
/// ~~~~
//
// note: this constructor is purposedly not named to `new` to discourage the direct usage.
// note: this constructor is purposely 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 }
@ -128,7 +137,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
/// millenia, I'm curious.)
/// millennia, I'm curious.)
///
/// # Example
///
@ -315,10 +324,21 @@ 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();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_datetime()
}
@ -330,7 +350,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();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_datetime()
}
@ -356,22 +376,24 @@ impl DateTime<FixedOffset> {
/// ```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
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().cloned()).to_string()
self.format_with_items(ITEMS.iter()).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().cloned()).to_string()
self.format_with_items(ITEMS.iter()).to_string()
}
/// Return an RFC 3339 and ISO 8601 date and time string with subseconds
@ -398,6 +420,7 @@ 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;
@ -439,19 +462,20 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
match ssitem {
None =>
self.format_with_items(
PREFIX.iter().chain([tzitem].iter()).cloned()
PREFIX.iter().chain([tzitem].iter())
).to_string(),
Some(s) =>
self.format_with_items(
PREFIX.iter().chain([s, tzitem].iter()).cloned()
PREFIX.iter().chain([s, tzitem].iter())
).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>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
let local = self.naive_local();
DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items)
}
@ -459,6 +483,7 @@ impl<Tz: TimeZone> DateTime<Tz> where Tz::Offset: fmt::Display {
/// Formats the combined date and time with the specified format string.
/// See the [`format::strftime` module](./format/strftime/index.html)
/// 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))
@ -550,8 +575,23 @@ impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
impl<Tz: TimeZone> Eq for DateTime<Tz> {
}
impl<Tz: TimeZone> PartialOrd for DateTime<Tz> {
fn partial_cmp(&self, other: &DateTime<Tz>) -> Option<Ordering> {
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> {
self.datetime.partial_cmp(&other.datetime)
}
}
@ -603,33 +643,6 @@ 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;
@ -647,6 +660,7 @@ 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) {
@ -672,6 +686,7 @@ 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;
@ -699,7 +714,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: ::std::fmt::Debug
E: ::core::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()));
@ -717,7 +732,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: ::std::fmt::Debug
E: ::core::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)> {
@ -754,7 +769,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: ::std::fmt::Debug
E: ::core::fmt::Debug
{
fn norm<Tz: TimeZone>(dt: &Option<DateTime<Tz>>) -> Option<(&DateTime<Tz>, &Tz::Offset)> {
dt.as_ref().map(|dt| (dt, dt.offset()))
@ -778,8 +793,8 @@ fn test_decodable_json_timestamps<FUtc, FFixed, FLocal, E>(utc_from_str: FUtc,
#[cfg(feature = "rustc-serialize")]
pub mod rustc_serialize {
use std::fmt;
use std::ops::Deref;
use core::fmt;
use core::ops::Deref;
use super::DateTime;
#[cfg(feature="clock")]
use offset::Local;
@ -792,7 +807,7 @@ pub mod rustc_serialize {
}
}
// try!-like function to convert a LocalResult into a serde-ish Result
// lik? 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,
@ -907,25 +922,38 @@ pub mod rustc_serialize {
/// documented at re-export site
#[cfg(feature = "serde")]
pub mod serde {
use std::fmt;
use core::fmt;
use super::DateTime;
#[cfg(feature="clock")]
use offset::Local;
use offset::{LocalResult, TimeZone, Utc, FixedOffset};
use serdelib::{ser, de};
use {SerdeError, ne_timestamp};
// try!-like function to convert a LocalResult into a serde-ish Result
#[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
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(
format!("value is not a legal timestamp: {}", ts))),
ne_timestamp(ts))),
LocalResult::Ambiguous(min, max) => Err(E::custom(
format!("value is an ambiguous timestamp: {}, could be either of {}, {}",
ts, min, max))),
SerdeError::Ambiguous { timestamp: ts, min: min, max: max })),
LocalResult::Single(val) => Ok(val)
}
}
@ -967,13 +995,13 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_nanoseconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use {DateTime, Utc};
use offset::TimeZone;
use super::serde_from;
use super::{serde_from, NanoSecondsTimestampVisitor};
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
///
@ -1044,17 +1072,15 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(NanoSecondsTimestampVisitor)))
Ok(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 seconds")
write!(formatter, "a unix timestamp in nanoseconds")
}
/// Deserialize a timestamp in nanoseconds since the epoch
@ -1077,6 +1103,152 @@ 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.
@ -1114,13 +1286,13 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_milliseconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use {DateTime, Utc};
use offset::TimeZone;
use super::serde_from;
use super::{serde_from, MilliSecondsTimestampVisitor};
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
///
@ -1191,11 +1363,9 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))))
Ok(d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc))?)
}
struct MilliSecondsTimestampVisitor;
impl<'de> de::Visitor<'de> for MilliSecondsTimestampVisitor {
type Value = DateTime<Utc>;
@ -1224,6 +1394,152 @@ 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.
@ -1261,13 +1577,13 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_seconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use {DateTime, Utc};
use offset::TimeZone;
use super::serde_from;
use super::{serde_from, SecondsTimestampVisitor};
/// Serialize a UTC datetime into an integer number of seconds since the epoch
///
@ -1338,11 +1654,9 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(SecondsTimestampVisitor)))
Ok(d.deserialize_i64(SecondsTimestampVisitor)?)
}
struct SecondsTimestampVisitor;
impl<'de> de::Visitor<'de> for SecondsTimestampVisitor {
type Value = DateTime<Utc>;
@ -1367,6 +1681,152 @@ 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
///
@ -1403,7 +1863,7 @@ pub mod serde {
fn visit_str<E>(self, value: &str) -> Result<DateTime<FixedOffset>, E>
where E: de::Error
{
value.parse().map_err(|err| E::custom(format!("{}", err)))
value.parse().map_err(|err: ::format::ParseError| E::custom(err))
}
}
@ -1560,6 +2020,9 @@ 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]
@ -1590,6 +2053,8 @@ 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"),
@ -1630,6 +2095,15 @@ 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>>(),
@ -1658,6 +2132,25 @@ 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() {
@ -1753,4 +2246,5 @@ mod tests {
assert_eq!(format!("{} ", ymd_formatted), format!("{:<17}", ymd));
assert_eq!(format!(" {} ", ymd_formatted), format!("{:^17}", ymd));
}
}

View File

@ -17,20 +17,31 @@
#![allow(ellipsis_inclusive_range_patterns)]
use std::fmt;
use std::str::FromStr;
use core::borrow::Borrow;
use core::fmt;
use core::str::FromStr;
#[cfg(any(feature = "std", test))]
use std::error::Error;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::string::{String, ToString};
use {Datelike, Timelike, Weekday, ParseWeekdayError};
#[cfg(any(feature = "alloc", feature = "std", test))]
use {Datelike, Timelike};
use {Weekday, ParseWeekdayError};
#[cfg(any(feature = "alloc", feature = "std", test))]
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 unhabitated type used for `InternalNumeric` and `InternalFixed` below.
/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below.
#[derive(Clone, PartialEq, Eq)]
enum Void {}
@ -55,7 +66,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 whitespaces if any.
/// It also trims the preceding whitespace 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)]
@ -173,11 +184,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 to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3.
Nanosecond3,
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6.
Nanosecond6,
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9.
Nanosecond9,
/// Timezone name.
///
@ -185,21 +196,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 whitespaces.
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace.
/// The offset is limited from `-24:00` to `+24:00`,
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColon,
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces,
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace,
/// and `Z` can be either in upper case or in lower case.
/// The offset is limited from `-24:00` to `+24:00`,
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
/// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColonZ,
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
/// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
/// Parsing allows an optional colon.
TimezoneOffset,
/// Same to [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon.
/// Same as [`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.
@ -231,11 +242,11 @@ enum InternalInternal {
///
/// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC
TimezoneOffsetPermissive,
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot.
Nanosecond3NoDot,
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot.
Nanosecond6NoDot,
/// Same to [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
/// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot.
Nanosecond9NoDot,
}
@ -244,11 +255,13 @@ enum InternalInternal {
pub enum Item<'a> {
/// A literally printed and parsed text.
Literal(&'a str),
/// Same to `Literal` but with the string owned by the item.
/// Same as `Literal` but with the string owned by the item.
#[cfg(any(feature = "alloc", feature = "std", test))]
OwnedLiteral(Box<str>),
/// Whitespace. Prints literally but reads zero or more whitespace.
Space(&'a str),
/// Same to `Space` but with the string owned by the item.
/// Same as `Space` but with the string owned by the item.
#[cfg(any(feature = "alloc", feature = "std", test))]
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.
@ -271,6 +284,7 @@ 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.
@ -302,26 +316,28 @@ enum ParseErrorKind {
BadFormat,
}
/// Same to `Result<T, ParseError>`.
/// Same as `Result<T, ParseError>`.
pub type ParseResult<T> = Result<T, ParseError>;
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
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"),
}
}
}
#[cfg(any(feature = "std", test))]
impl Error for ParseError {
#[allow(deprecated)]
fn description(&self) -> &str {
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",
}
"parser error, see to_string() for details"
}
}
@ -336,14 +352,15 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
/// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`.
pub fn format<'a, I>(
#[cfg(any(feature = "alloc", feature = "std", test))]
pub fn format<'a, I, B>(
w: &mut fmt::Formatter,
date: Option<&NaiveDate>,
time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>,
items: I,
) -> fmt::Result
where I: Iterator<Item=Item<'a>>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>
{
// full and abbreviated month and weekday names
static SHORT_MONTHS: [&'static str; 12] =
@ -356,15 +373,16 @@ pub fn format<'a, I>(
static LONG_WEEKDAYS: [&'static str; 7] =
["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
use std::fmt::Write;
use core::fmt::Write;
let mut result = String::new();
for item in items {
match item {
Item::Literal(s) | Item::Space(s) => result.push_str(s),
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => result.push_str(s),
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),
Item::Numeric(spec, pad) => {
&Item::Numeric(ref spec, ref pad) => {
use self::Numeric::*;
let week_from_sun = |d: &NaiveDate|
@ -373,31 +391,31 @@ pub fn format<'a, I>(
(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))) =>
@ -406,33 +424,31 @@ pub fn format<'a, I>(
}),
// for the future expansion
Internal(ref int) => match int._dummy {},
&Internal(ref int) => match int._dummy {},
};
if let Some(v) = v {
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),
}
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 {
return Err(fmt::Error) // insufficient arguments for given format
}
},
Item::Fixed(spec) => {
&Item::Fixed(ref spec) => {
use self::Fixed::*;
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
@ -458,41 +474,41 @@ pub fn format<'a, I>(
}
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 {
@ -505,70 +521,70 @@ pub fn format<'a, I>(
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 to `%a, %e %b %Y %H:%M:%S %z`
&RFC2822 => // same as `%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;
try!(write!(
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 to `%Y-%m-%dT%H:%M:%S%.f%:z`
&RFC3339 => // same as `%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.
try!(write!(result, "{:?}T{:?}", d, t));
write!(result, "{:?}T{:?}", d, t)?;
Some(write_local_minus_utc(&mut result, off, false, true))
} else {
None
@ -576,12 +592,12 @@ pub fn format<'a, I>(
};
match ret {
Some(ret) => try!(ret),
Some(ret) => ret?,
None => return Err(fmt::Error), // insufficient arguments for given format
}
},
Item::Error => return Err(fmt::Error),
&Item::Error => return Err(fmt::Error),
}
}
@ -598,6 +614,7 @@ 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.
@ -610,7 +627,8 @@ pub struct DelayedFormat<I> {
items: I,
}
impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
#[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a, I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>>> 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 }
@ -625,7 +643,8 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
}
}
impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for 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> {
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,13 +6,15 @@
#![allow(deprecated)]
use std::usize;
use Weekday;
use core::borrow::Borrow;
use core::usize;
use core::str;
use {DateTime, FixedOffset, Weekday};
use super::scan;
use super::{Parsed, ParseResult, Item, InternalFixed, InternalInternal};
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT};
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};
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
p.set_weekday(match v {
@ -32,7 +34,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) = try!($e); s = s_; v })
($e:expr) => ({ let (s_, v) = $e?; s = s_; v })
}
// an adapted RFC 2822 syntax from Section 3.3 and 4.3:
@ -52,10 +54,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 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
// "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
//
// some notes:
//
@ -72,10 +74,6 @@ 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.
//
@ -89,14 +87,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..];
try!(parsed.set_weekday(weekday));
parsed.set_weekday(weekday)?;
}
s = s.trim_left();
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
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
// distinguish two- and three-digit years from four-digit years
let prevlen = s.len();
@ -108,20 +106,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
}
try!(parsed.set_year(year));
parsed.set_year(year)?;
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))));
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)))?;
if let Ok(s_) = scan::char(s.trim_left(), b':') { // [ ":" *S 2DIGIT ]
try!(parsed.set_second(try_consume!(scan::number(s_, 2, 2))));
parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?;
}
s = try!(scan::space(s)); // mandatory
s = 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`)
try!(parsed.set_offset(i64::from(offset)));
parsed.set_offset(i64::from(offset))?;
}
Ok((s, ()))
@ -129,7 +127,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) = try!($e); s = s_; v })
($e:expr) => ({ let (s_, v) = $e?; s = s_; v })
}
// an adapted RFC 3339 syntax from Section 5.6:
@ -159,11 +157,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.
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))));
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)))?;
s = match s.as_bytes().first() {
Some(&b't') | Some(&b'T') => &s[1..],
@ -171,19 +169,19 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
None => return Err(TOO_SHORT),
};
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))));
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)))?;
if s.starts_with('.') {
let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
try!(parsed.set_nanosecond(nanosecond));
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); }
try!(parsed.set_offset(i64::from(offset)));
parsed.set_offset(i64::from(offset))?;
Ok((s, ()))
}
@ -204,65 +202,86 @@ 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>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<()>
where I: Iterator<Item=Item<'a>> {
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>> {
macro_rules! try_consume {
($e:expr) => ({ let (s_, v) = try!($e); s = s_; v })
($e:expr) => ({
match $e {
Ok((s_, v)) => {
s = s_;
v
}
Err(e) => return Err((s, e))
}
})
}
for item in items {
match item {
Item::Literal(prefix) => {
if s.len() < prefix.len() { return Err(TOO_SHORT); }
if !s.starts_with(prefix) { return Err(INVALID); }
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)); }
s = &s[prefix.len()..];
}
Item::OwnedLiteral(ref prefix) => {
if s.len() < prefix.len() { return Err(TOO_SHORT); }
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
#[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)); }
s = &s[prefix.len()..];
}
Item::Space(_) | Item::OwnedSpace(_) => {
&Item::Space(_) => {
s = s.trim_left();
}
Item::Numeric(spec, _pad) => {
#[cfg(any(feature = "alloc", feature = "std", test))]
&Item::OwnedSpace(_) => {
s = s.trim_left();
}
&Item::Numeric(ref spec, ref _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));
try!(0i64.checked_sub(v).ok_or(OUT_OF_RANGE))
0i64.checked_sub(v).ok_or((s, OUT_OF_RANGE))?
} else if s.starts_with('+') {
try_consume!(scan::number(&s[1..], 1, usize::MAX))
} else {
@ -272,104 +291,142 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
} else {
try_consume!(scan::number(s, 1, width))
};
try!(set(parsed, v));
set(parsed, v).map_err(|e| (s, e))?;
}
Item::Fixed(spec) => {
&Item::Fixed(ref spec) => {
use super::Fixed::*;
match spec {
ShortMonthName => {
&ShortMonthName => {
let month0 = try_consume!(scan::short_month0(s));
try!(parsed.set_month(i64::from(month0) + 1));
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
}
LongMonthName => {
&LongMonthName => {
let month0 = try_consume!(scan::short_or_long_month0(s));
try!(parsed.set_month(i64::from(month0) + 1));
parsed.set_month(i64::from(month0) + 1).map_err(|e| (s, e))?;
}
ShortWeekdayName => {
&ShortWeekdayName => {
let weekday = try_consume!(scan::short_weekday(s));
try!(parsed.set_weekday(weekday));
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
}
LongWeekdayName => {
&LongWeekdayName => {
let weekday = try_consume!(scan::short_or_long_weekday(s));
try!(parsed.set_weekday(weekday));
parsed.set_weekday(weekday).map_err(|e| (s, e))?;
}
LowerAmPm | UpperAmPm => {
if s.len() < 2 { return Err(TOO_SHORT); }
&LowerAmPm | &UpperAmPm => {
if s.len() < 2 { return Err((s, 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(INVALID)
_ => return Err((s, INVALID))
};
try!(parsed.set_ampm(ampm));
parsed.set_ampm(ampm).map_err(|e| (s, e))?;
s = &s[2..];
}
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => {
&Nanosecond | &Nanosecond3 | &Nanosecond6 | &Nanosecond9 => {
if s.starts_with('.') {
let nano = try_consume!(scan::nanosecond(&s[1..]));
try!(parsed.set_nanosecond(nano));
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
}
}
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
if s.len() < 3 { return Err(TOO_SHORT); }
&Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
if s.len() < 3 { return Err((s, TOO_SHORT)); }
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
try!(parsed.set_nanosecond(nano));
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
}
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
if s.len() < 6 { return Err(TOO_SHORT); }
&Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
if s.len() < 6 { return Err((s, TOO_SHORT)); }
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
try!(parsed.set_nanosecond(nano));
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
}
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
if s.len() < 9 { return Err(TOO_SHORT); }
&Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
if s.len() < 9 { return Err((s, TOO_SHORT)); }
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
try!(parsed.set_nanosecond(nano));
parsed.set_nanosecond(nano).map_err(|e| (s, e))?;
}
TimezoneName => return Err(BAD_FORMAT),
&TimezoneName => return Err((s, BAD_FORMAT)),
TimezoneOffsetColon | TimezoneOffset => {
&TimezoneOffsetColon | &TimezoneOffset => {
let offset = try_consume!(scan::timezone_offset(s.trim_left(),
scan::colon_or_space));
try!(parsed.set_offset(i64::from(offset)));
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
}
TimezoneOffsetColonZ | TimezoneOffsetZ => {
&TimezoneOffsetColonZ | &TimezoneOffsetZ => {
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
scan::colon_or_space));
try!(parsed.set_offset(i64::from(offset)));
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
}
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));
try!(parsed.set_offset(i64::from(offset)));
parsed.set_offset(i64::from(offset)).map_err(|e| (s, e))?;
}
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(BAD_FORMAT);
&Item::Error => {
return Err((s, BAD_FORMAT));
}
}
}
// if there are trailling chars, it is an error
if !s.is_empty() {
Err(TOO_LONG)
Err((s, TOO_LONG))
} else {
Ok(())
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()
}
}
@ -382,7 +439,7 @@ fn test_parse() {
// workaround for Rust issue #22255
fn parse_all(s: &str, items: &[Item]) -> ParseResult<Parsed> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, items.iter().cloned()));
parse(&mut parsed, s, items.iter())?;
Ok(parsed)
}
@ -693,12 +750,12 @@ fn test_rfc2822() {
fn rfc2822_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter().cloned()));
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?;
parsed.to_datetime()
}
fn fmt_rfc2822_datetime(dt: DateTime<FixedOffset>) -> String {
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter().cloned()).to_string()
dt.format_with_items([Item::Fixed(Fixed::RFC2822)].iter()).to_string()
}
// Test against test data above
@ -774,12 +831,12 @@ fn test_rfc3339() {
fn rfc3339_to_datetime(date: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter().cloned()));
parse(&mut parsed, date, [Item::Fixed(Fixed::RFC3339)].iter())?;
parsed.to_datetime()
}
fn fmt_rfc3339_datetime(dt: DateTime<FixedOffset>) -> String {
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter().cloned()).to_string()
dt.format_with_items([Item::Fixed(Fixed::RFC3339)].iter()).to_string()
}
// Test against test data above

View File

@ -110,8 +110,9 @@ pub struct Parsed {
_dummy: (),
}
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
/// Checks if `old` is either empty or has the same value as `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)}
@ -141,82 +142,97 @@ 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.year, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.year_div_100, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.year_mod_100, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.isoyear, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.isoyear_div_100, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.isoyear_mod_100, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.month, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.week_from_sun, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.week_from_mon, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.isoweek, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.ordinal, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.day, 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)
@ -224,36 +240,42 @@ 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 = 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));
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)?;
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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.minute, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.second, 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, try!(value.to_u32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.nanosecond, 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, try!(value.to_i32().ok_or(OUT_OF_RANGE)))
set_if_consistent(&mut self.offset, value.to_i32().ok_or(OUT_OF_RANGE)?)
}
/// Returns a parsed naive date out of given fields.
@ -294,7 +316,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(try!(y.ok_or(OUT_OF_RANGE))))
Ok(Some(y.ok_or(OUT_OF_RANGE)?))
},
// we only have modulo. try to interpret a modulo as a conventional two-digit year.
@ -308,9 +330,9 @@ impl Parsed {
}
let given_year =
try!(resolve_year(self.year, self.year_div_100, self.year_mod_100));
resolve_year(self.year, self.year_div_100, self.year_mod_100)?;
let given_isoyear =
try!(resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100));
resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?;
// verify the normal year-month-day date.
let verify_ymd = |date: NaiveDate| {
@ -366,20 +388,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 = try!(NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE));
let date = 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 = try!(NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE));
let date = 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 = try!(NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE));
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 0,
Weekday::Mon => 6,
@ -394,8 +416,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 = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE));
let date = 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)
@ -404,7 +426,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 = try!(NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE));
let newyear = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?;
let firstweek = match newyear.weekday() {
Weekday::Sun => 1,
Weekday::Mon => 0,
@ -419,8 +441,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 = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE));
let date = 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)
@ -429,7 +451,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 = try!(date.ok_or(OUT_OF_RANGE));
let date = date.ok_or(OUT_OF_RANGE)?;
(verify_ymd(date) && verify_ordinal(date), date)
},
@ -525,9 +547,9 @@ impl Parsed {
}
// reconstruct date and time fields from timestamp
let ts = try!(timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE));
let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?;
let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
let mut datetime = try!(datetime.ok_or(OUT_OF_RANGE));
let mut datetime = 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.
@ -544,21 +566,21 @@ impl Parsed {
}
// ...and we have the correct candidates for other fields.
} else {
try!(parsed.set_second(i64::from(datetime.second())));
parsed.set_second(i64::from(datetime.second()))?;
}
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())));
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()))?;
// validate other fields (e.g. week) and return
let date = try!(parsed.to_naive_date());
let time = try!(parsed.to_naive_time());
let date = parsed.to_naive_date()?;
let time = parsed.to_naive_time()?;
Ok(date.and_time(time))
} else {
// reproduce the previous error(s)
try!(date);
try!(time);
date?;
time?;
unreachable!()
}
}
@ -575,9 +597,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 = 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));
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)?;
match offset.from_local_datetime(&datetime) {
LocalResult::None => Err(IMPOSSIBLE),
LocalResult::Single(t) => Ok(t),
@ -602,7 +624,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 = try!(dt.ok_or(OUT_OF_RANGE));
let dt = dt.ok_or(OUT_OF_RANGE)?;
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
}
@ -617,7 +639,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 = try!(self.to_naive_datetime_with_offset(guessed_offset));
let datetime = 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,23 +30,35 @@ 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);
// 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});
// 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);
}
// 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))
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))
}
/// Tries to consume at least one digits as a fractional second.
@ -54,13 +66,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) = try!(number(s, 1, 9));
let (s, v) = 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 = try!(v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE));
let v = 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');
@ -72,12 +84,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) = try!(number(s, digits, digits));
let (s, v) = 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 = try!(v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE));
let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?;
Ok((s, v))
}
@ -128,7 +140,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) = try!(short_month0(s));
let (mut s, month0) = short_month0(s)?;
// tries to consume the suffix if possible
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
@ -146,7 +158,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) = try!(short_weekday(s));
let (mut s, weekday) = short_weekday(s)?;
// tries to consume the suffix if possible
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
@ -213,14 +225,14 @@ fn timezone_offset_internal<F>(mut s: &str, mut consume_colon: F, allow_missing_
s = &s[1..];
// hours (00--99)
let hours = match try!(digits(s)) {
let hours = match 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 = try!(consume_colon(s));
s = consume_colon(s)?;
// minutes (00--59)
// if the next two items are digits then we have to add minutes
@ -245,18 +257,30 @@ fn timezone_offset_internal<F>(mut s: &str, mut consume_colon: F, allow_missing_
Ok((s, if negative {-seconds} else {seconds}))
}
/// Same to `timezone_offset` but also allows for `z`/`Z` which is same to `+00:00`.
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as `+00:00`.
pub fn timezone_offset_zulu<F>(s: &str, colon: F)
-> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str>
{
match s.as_bytes().first() {
let bytes = s.as_bytes();
match 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 to `timezone_offset` but also allows for `z`/`Z` which is same to
/// Same as `timezone_offset` but also allows for `z`/`Z` which is the same as
/// `+00:00`, and allows missing minutes entirely.
pub fn timezone_offset_permissive<F>(s: &str, colon: F)
-> ParseResult<(&str, i32)>
@ -268,7 +292,7 @@ pub fn timezone_offset_permissive<F>(s: &str, colon: F)
}
}
/// Same to `timezone_offset` but also allows for RFC 2822 legacy timezones.
/// Same as `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
@ -295,12 +319,8 @@ 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) = 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)))
}
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;
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 to `%b`. |
| `%h` | `Jul` | Same as `%b`. |
| | | |
| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. |
| `%e` | ` 8` | Same to `%d` but space-padded. Same to `%_d`. |
| `%e` | ` 8` | Same as `%d` but space-padded. Same as `%_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 to `%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 as `%U`, but week 1 starts with the first Monday in that year instead.|
| | | |
| `%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] |
| `%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] |
| | | |
| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. |
| | | |
| `%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`. |
| `%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`. |
| | | |
| | | **TIME SPECIFIERS:** |
| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. |
| `%k` | ` 0` | Same to `%H` but space-padded. Same to `%_H`. |
| `%k` | ` 0` | Same as `%H` but space-padded. Same as `%_H`. |
| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. |
| `%l` | `12` | Same to `%I` but space-padded. Same to `%_I`. |
| `%l` | `12` | Same as `%I` but space-padded. Same as `%_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 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`. |
| `%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`. |
| | | |
| | | **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 to `%z` but with a colon. |
| `%#z` | `+09` | *Parsing only:* Same to `%z` but allows minutes to be missing or present. |
| `%: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. |
| | | |
| | | **DATE & TIME SPECIFIERS:** |
|`%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] |
|`%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] |
| | | |
| `%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,59 +95,62 @@ 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 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.
[^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.
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`:
[^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`:
[^8]: `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
<br>
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 to the newer, standard type for duration,
//! Since this has the same name as 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,9 +383,12 @@
#![doc(html_root_url = "https://docs.rs/chrono/latest/")]
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507
#![cfg_attr(feature = "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
@ -403,6 +406,13 @@
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;
@ -414,10 +424,12 @@ extern crate serde as serdelib;
#[cfg(test)]
#[macro_use]
extern crate doc_comment;
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
extern crate wasm_bindgen;
#[cfg(all(target_arch = "wasm32", feature="wasmbind"))]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
extern crate js_sys;
#[cfg(feature = "bench")]
extern crate test;
#[cfg(test)]
doctest!("../README.md");
@ -479,6 +491,10 @@ 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
///
@ -498,6 +514,10 @@ 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`
@ -511,6 +531,41 @@ 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.
@ -645,6 +700,20 @@ 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.
@ -678,7 +747,7 @@ impl num_traits::FromPrimitive for Weekday {
}
}
use std::fmt;
use core::fmt;
/// An error resulting from reading `Weekday` value with `FromStr`.
#[derive(Clone, PartialEq)]
@ -697,14 +766,14 @@ impl fmt::Debug for ParseWeekdayError {
#[cfg(feature = "serde")]
mod weekday_serde {
use super::Weekday;
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
impl ser::Serialize for Weekday {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer
{
serializer.serialize_str(&format!("{:?}", self))
serializer.collect_str(&self)
}
}
@ -888,17 +957,22 @@ pub trait Datelike: Sized {
/// Returns `None` when the resulting value would be invalid.
fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>;
/// Returns the number of days since January 1, Year 1 (aka Day 1) in the
/// proleptic Gregorian calendar.
/// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1.
///
/// # Example:
/// # Examples
///
/// ~~~
/// ```
/// use chrono::{NaiveDate, Datelike};
/// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719163);
///
/// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719_163);
/// assert_eq!(NaiveDate::from_ymd(2, 1, 1).num_days_from_ce(), 366);
/// assert_eq!(NaiveDate::from_ymd(1, 1, 1).num_days_from_ce(), 1);
/// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365);
/// ~~~
/// ```
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;
@ -1000,3 +1074,52 @@ 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,8 +3,10 @@
//! ISO 8601 calendar date without timezone.
use std::{str, fmt};
use std::ops::{Add, Sub, AddAssign, SubAssign};
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt};
use core::ops::{Add, Sub, AddAssign, SubAssign};
use num_traits::ToPrimitive;
use oldtime::Duration as OldDuration;
@ -12,7 +14,9 @@ use {Weekday, Datelike};
use div::div_mod_floor;
use naive::{NaiveTime, NaiveDateTime, IsoWeek};
use format::{Item, Numeric, Pad};
use format::{parse, Parsed, ParseError, ParseResult, DelayedFormat, StrftimeItems};
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))]
use format::DelayedFormat;
use super::isoweek;
use super::internals::{self, DateImpl, Of, Mdf, YearFlags};
@ -90,7 +94,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 same to that of the [calendar date](#calendar-date).
/// The year number is the same as 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)]
@ -330,10 +334,10 @@ impl NaiveDate {
}
}
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (Day 1)
/// in the proleptic Gregorian calendar.
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
/// January 1, 1 being day 1.
///
/// Panics on the out-of-range date.
/// Panics if the date is out of range.
///
/// # Example
///
@ -378,10 +382,10 @@ impl NaiveDate {
NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date")
}
/// Makes a new `NaiveDate` from the number of days since January 1, 1 (Day 1)
/// in the proleptic Gregorian calendar.
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
/// January 1, 1 being day 1.
///
/// Returns `None` on the out-of-range date.
/// Returns `None` if the date is out of range.
///
/// # Example
///
@ -407,6 +411,55 @@ 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.
@ -451,7 +504,7 @@ impl NaiveDate {
/// ~~~~
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDate> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_date()
}
@ -890,7 +943,7 @@ impl NaiveDate {
}
/// Formats the date with the specified formatting items.
/// Otherwise it is same to the ordinary `format` method.
/// Otherwise it is the same as the ordinary `format` method.
///
/// The `Iterator` of items should be `Clone`able,
/// since the resulting `DelayedFormat` value may be formatted multiple times.
@ -916,9 +969,10 @@ 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>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(Some(*self), None, items)
}
@ -954,6 +1008,7 @@ 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))
@ -1345,7 +1400,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 same to the addition with a negated `Duration`.
/// It is the same as the addition with a negated `Duration`.
///
/// Panics on underflow or overflow.
/// Use [`NaiveDate::checked_sub_signed`](#method.checked_sub_signed) to detect that.
@ -1387,7 +1442,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`.
///
@ -1421,7 +1476,7 @@ impl Sub<NaiveDate> for NaiveDate {
}
}
/// The `Debug` output of the naive date `d` is same to
/// The `Debug` output of the naive date `d` is the same as
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
///
/// The string printed can be readily parsed via the `parse` method on `str`.
@ -1456,7 +1511,7 @@ impl fmt::Debug for NaiveDate {
}
}
/// The `Display` output of the naive date `d` is same to
/// The `Display` output of the naive date `d` is the same as
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
///
/// The string printed can be readily parsed via the `parse` method on `str`.
@ -1503,16 +1558,16 @@ impl str::FromStr for NaiveDate {
fn from_str(s: &str) -> ParseResult<NaiveDate> {
const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
Item::Numeric(Numeric::Day, Pad::Zero),
Item::Space(""),
];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_naive_date()
}
}
@ -1600,7 +1655,7 @@ mod rustc_serialize {
#[cfg(feature = "serde")]
mod serde {
use std::fmt;
use core::fmt;
use super::NaiveDate;
use serdelib::{ser, de};
@ -1629,15 +1684,23 @@ 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(|err| E::custom(format!("{}", err)))
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)
}
}
@ -1823,6 +1886,24 @@ 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,8 +3,10 @@
//! ISO 8601 date and time without timezone.
use std::{str, fmt, hash};
use std::ops::{Add, Sub, AddAssign, SubAssign};
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt, hash};
use core::ops::{Add, Sub, AddAssign, SubAssign};
use num_traits::ToPrimitive;
use oldtime::Duration as OldDuration;
@ -12,7 +14,9 @@ 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, DelayedFormat, StrftimeItems};
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))]
use format::DelayedFormat;
/// The tight upper bound guarantees that a duration with `|Duration| >= 2^MAX_SECS_BITS`
/// will always overflow the addition with any date and time type.
@ -206,7 +210,7 @@ impl NaiveDateTime {
/// ~~~~
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_datetime_with_offset(0) // no offset adjustment
}
@ -313,7 +317,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 millenia, I'm curious.)
/// needs nanosecond precision over millennia, I'm curious.)
///
/// # Example
///
@ -619,7 +623,7 @@ impl NaiveDateTime {
}
/// Formats the combined date and time with the specified formatting items.
/// Otherwise it is same to the ordinary [`format`](#method.format) method.
/// Otherwise it is the same as the ordinary [`format`](#method.format) method.
///
/// The `Iterator` of items should be `Clone`able,
/// since the resulting `DelayedFormat` value may be formatted multiple times.
@ -645,9 +649,10 @@ 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>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(Some(self.date), Some(self.time), items)
}
@ -683,6 +688,7 @@ 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))
@ -1259,7 +1265,7 @@ impl AddAssign<OldDuration> for NaiveDateTime {
}
/// A subtraction of `Duration` from `NaiveDateTime` yields another `NaiveDateTime`.
/// It is same to the addition with a negated `Duration`.
/// It is the same as 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**,
@ -1383,7 +1389,7 @@ impl Sub<NaiveDateTime> for NaiveDateTime {
}
}
/// The `Debug` output of the naive date and time `dt` is same to
/// The `Debug` output of the naive date and time `dt` is the same as
/// [`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`.
@ -1416,7 +1422,7 @@ impl fmt::Debug for NaiveDateTime {
}
}
/// The `Debug` output of the naive date and time `dt` is same to
/// The `Debug` output of the naive date and time `dt` is the same as
/// [`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,
@ -1468,22 +1474,22 @@ impl str::FromStr for NaiveDateTime {
fn from_str(s: &str) -> ParseResult<NaiveDateTime> {
const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Year, Pad::Zero),
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Month, Pad::Zero),
Item::Numeric(Numeric::Month, Pad::Zero),
Item::Space(""), Item::Literal("-"),
Item::Space(""), Item::Numeric(Numeric::Day, Pad::Zero),
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::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_naive_datetime_with_offset(0)
}
}
@ -1663,7 +1669,7 @@ pub mod rustc_serialize {
/// Tools to help serializing/deserializing `NaiveDateTime`s
#[cfg(feature = "serde")]
pub mod serde {
use std::fmt;
use core::fmt;
use super::{NaiveDateTime};
use serdelib::{ser, de};
@ -1702,7 +1708,7 @@ pub mod serde {
fn visit_str<E>(self, value: &str) -> Result<NaiveDateTime, E>
where E: de::Error
{
value.parse().map_err(|err| E::custom(format!("{}", err)))
value.parse().map_err(E::custom)
}
}
@ -1750,10 +1756,10 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_nanoseconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use NaiveDateTime;
use {NaiveDateTime, ne_timestamp};
/// Serialize a UTC datetime into an integer number of nanoseconds since the epoch
///
@ -1828,7 +1834,7 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)))
Ok(d.deserialize_i64(NaiveDateTimeFromNanoSecondsVisitor)?)
}
struct NaiveDateTimeFromNanoSecondsVisitor;
@ -1846,7 +1852,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(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_timestamp(value)))
}
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
@ -1854,7 +1860,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(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_timestamp(value)))
}
}
}
@ -1895,10 +1901,10 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_milliseconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use NaiveDateTime;
use {NaiveDateTime, ne_timestamp};
/// Serialize a UTC datetime into an integer number of milliseconds since the epoch
///
@ -1973,7 +1979,7 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)))
Ok(d.deserialize_i64(NaiveDateTimeFromMilliSecondsVisitor)?)
}
struct NaiveDateTimeFromMilliSecondsVisitor;
@ -1991,7 +1997,7 @@ pub mod serde {
{
NaiveDateTime::from_timestamp_opt(value / 1000,
((value % 1000) * 1_000_000) as u32)
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_timestamp(value)))
}
fn visit_u64<E>(self, value: u64) -> Result<NaiveDateTime, E>
@ -1999,7 +2005,7 @@ pub mod serde {
{
NaiveDateTime::from_timestamp_opt((value / 1000) as i64,
((value % 1000) * 1_000_000) as u32)
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_timestamp(value)))
}
}
}
@ -2040,10 +2046,10 @@ pub mod serde {
/// # fn main() { example().unwrap(); }
/// ```
pub mod ts_seconds {
use std::fmt;
use core::fmt;
use serdelib::{ser, de};
use NaiveDateTime;
use {NaiveDateTime, ne_timestamp};
/// Serialize a UTC datetime into an integer number of seconds since the epoch
///
@ -2118,7 +2124,7 @@ pub mod serde {
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where D: de::Deserializer<'de>
{
Ok(try!(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)))
Ok(d.deserialize_i64(NaiveDateTimeFromSecondsVisitor)?)
}
struct NaiveDateTimeFromSecondsVisitor;
@ -2135,14 +2141,14 @@ pub mod serde {
where E: de::Error
{
NaiveDateTime::from_timestamp_opt(value, 0)
.ok_or_else(|| E::custom(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_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(format!("value is not a legal timestamp: {}", value)))
.ok_or_else(|| E::custom(ne_timestamp(value)))
}
}
}

View File

@ -14,8 +14,9 @@
//! 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 std::{i32, fmt};
use core::{i32, fmt};
use num_traits::FromPrimitive;
use Weekday;
use div::{div_rem, mod_floor};
@ -470,7 +471,6 @@ 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,16 +517,6 @@ 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 std::fmt;
use core::fmt;
use super::internals::{DateImpl, Of, YearFlags};
@ -104,7 +104,7 @@ impl IsoWeek {
}
}
/// The `Debug` output of the ISO week `w` is same to
/// The `Debug` output of the ISO week `w` is the same as
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
/// where `d` is any `NaiveDate` value in that week.
///

View File

@ -3,14 +3,18 @@
//! ISO 8601 time without timezone.
use std::{str, fmt, hash};
use std::ops::{Add, Sub, AddAssign, SubAssign};
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::{str, fmt, hash};
use core::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, DelayedFormat, StrftimeItems};
use format::{parse, Parsed, ParseError, ParseResult, StrftimeItems};
#[cfg(any(feature = "alloc", feature = "std", test))]
use format::DelayedFormat;
/// ISO 8601 time without timezone.
/// Allows for the nanosecond precision and optional leap second representation.
@ -492,7 +496,7 @@ impl NaiveTime {
/// ~~~~
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveTime> {
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_naive_time()
}
@ -681,7 +685,7 @@ impl NaiveTime {
// `rhs.frac`|========================================>|
// | | | `self - rhs` | |
use std::cmp::Ordering;
use core::cmp::Ordering;
let secs = i64::from(self.secs) - i64::from(rhs.secs);
let frac = i64::from(self.frac) - i64::from(rhs.frac);
@ -697,7 +701,7 @@ impl NaiveTime {
}
/// Formats the time with the specified formatting items.
/// Otherwise it is same to the ordinary [`format`](#method.format) method.
/// Otherwise it is the same as the ordinary [`format`](#method.format) method.
///
/// The `Iterator` of items should be `Clone`able,
/// since the resulting `DelayedFormat` value may be formatted multiple times.
@ -723,9 +727,10 @@ 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>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=Item<'a>> + Clone {
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where I: Iterator<Item=B> + Clone, B: Borrow<Item<'a>> {
DelayedFormat::new(None, Some(*self), items)
}
@ -763,6 +768,7 @@ 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))
@ -1065,7 +1071,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 same to the addition with a negated `Duration`.
/// It is the same as 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**,
@ -1193,7 +1199,7 @@ impl Sub<NaiveTime> for NaiveTime {
}
}
/// The `Debug` output of the naive time `t` is same to
/// The `Debug` output of the naive time `t` is the same as
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
///
/// The string printed can be readily parsed via the `parse` method on `str`.
@ -1230,7 +1236,7 @@ impl fmt::Debug for NaiveTime {
(sec, self.frac)
};
try!(write!(f, "{:02}:{:02}:{:02}", hour, min, sec));
write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?;
if nano == 0 {
Ok(())
} else if nano % 1_000_000 == 0 {
@ -1243,7 +1249,7 @@ impl fmt::Debug for NaiveTime {
}
}
/// The `Display` output of the naive time `t` is same to
/// The `Display` output of the naive time `t` is the same as
/// [`t.format("%H:%M:%S%.f")`](../format/strftime/index.html).
///
/// The string printed can be readily parsed via the `parse` method on `str`.
@ -1299,16 +1305,16 @@ impl str::FromStr for NaiveTime {
fn from_str(s: &str) -> ParseResult<NaiveTime> {
const ITEMS: &'static [Item<'static>] = &[
Item::Space(""), Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Space(""), Item::Literal(":"),
Item::Space(""), Item::Numeric(Numeric::Second, Pad::Zero),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Fixed(Fixed::Nanosecond), Item::Space(""),
];
let mut parsed = Parsed::new();
try!(parse(&mut parsed, s, ITEMS.iter().cloned()));
parse(&mut parsed, s, ITEMS.iter())?;
parsed.to_naive_time()
}
}
@ -1411,7 +1417,7 @@ mod rustc_serialize {
#[cfg(feature = "serde")]
mod serde {
use std::fmt;
use core::fmt;
use super::NaiveTime;
use serdelib::{ser, de};
@ -1431,7 +1437,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")
}
@ -1439,7 +1445,7 @@ mod serde {
fn visit_str<E>(self, value: &str) -> Result<NaiveTime, E>
where E: de::Error
{
value.parse().map_err(|err| E::custom(format!("{}", err)))
value.parse().map_err(E::custom)
}
}

View File

@ -3,8 +3,8 @@
//! The time zone which has a fixed offset from UTC.
use std::ops::{Add, Sub};
use std::fmt;
use core::ops::{Add, Sub};
use core::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 same to
// addition or subtraction of FixedOffset to/from Timelike values is the same as
// 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", feature = "wasmbind")))]
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), 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", feature = "wasmbind"))]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), 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 std::fmt;
use core::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();
try!(parse(&mut parsed, s, StrftimeItems::new(fmt)));
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
parsed.to_datetime_with_timezone(self)
}

View File

@ -3,8 +3,11 @@
//! The UTC (Coordinated Universal Time) time zone.
use std::fmt;
#[cfg(all(feature="clock", not(all(target_arch = "wasm32", feature = "wasmbind"))))]
use core::fmt;
#[cfg(all(
feature = "clock",
not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))
))]
use oldtime;
use naive::{NaiveDate, NaiveDateTime};
@ -38,7 +41,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", feature = "wasmbind")))]
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
pub fn now() -> DateTime<Utc> {
let spec = oldtime::get_time();
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
@ -46,7 +49,7 @@ impl Utc {
}
/// Returns a `DateTime` which corresponds to the current date.
#[cfg(all(target_arch = "wasm32", feature = "wasmbind"))]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), 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,10 +10,11 @@
//! Temporal quantification
use std::{fmt, i64};
use core::{fmt, i64};
#[cfg(any(feature = "std", test))]
use std::error::Error;
use std::ops::{Add, Sub, Mul, Div, Neg};
use std::time::Duration as StdDuration;
use core::ops::{Add, Sub, Mul, Div, Neg};
use core::time::Duration as StdDuration;
/// The number of nanoseconds in a microsecond.
const NANOS_PER_MICRO: i32 = 1000;
@ -363,20 +364,20 @@ impl fmt::Display for Duration {
let hasdate = days != 0;
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
try!(write!(f, "{}P", sign));
write!(f, "{}P", sign)?;
if hasdate {
try!(write!(f, "{}D", days));
write!(f, "{}D", days)?;
}
if hastime {
if abs.nanos == 0 {
try!(write!(f, "T{}S", secs));
write!(f, "T{}S", secs)?;
} else if abs.nanos % NANOS_PER_MILLI == 0 {
try!(write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI));
write!(f, "T{}.{:03}S", secs, abs.nanos / NANOS_PER_MILLI)?;
} else if abs.nanos % NANOS_PER_MICRO == 0 {
try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO));
write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO)?;
} else {
try!(write!(f, "T{}.{:09}S", secs, abs.nanos));
write!(f, "T{}.{:09}S", secs, abs.nanos)?;
}
}
Ok(())
@ -394,13 +395,15 @@ pub struct OutOfRangeError(());
impl fmt::Display for OutOfRangeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
write!(f, "Source duration value is out of range for the target type")
}
}
#[cfg(any(feature = "std", test))]
impl Error for OutOfRangeError {
#[allow(deprecated)]
fn description(&self) -> &str {
"Source duration value is out of range for the target type"
"out of range error"
}
}

View File

@ -2,7 +2,7 @@
// See README.md and LICENSE.txt for details.
use Timelike;
use std::ops::{Add, Sub};
use core::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", feature = "wasmbind"))]
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
mod test {
extern crate chrono;
extern crate wasm_bindgen_test;