Bug 1492001 - Revendor Rust dependencies. r=jgraham

Depends on D6168

Differential Revision: https://phabricator.services.mozilla.com/D6169

--HG--
rename : third_party/rust/num/bors.toml => third_party/rust/num-integer/bors.toml
extra : moz-landing-system : lando
This commit is contained in:
Matt Brubeck 2018-09-18 16:35:08 +00:00
parent 0aca2432c6
commit 03519bf6d8
101 changed files with 12368 additions and 5605 deletions

View File

@ -1 +1 @@
{"files":{".travis.yml":"e4721fe8d991d6b9f58da6fba573906a759b58b49ba58cb15b0ca279b3bc53cb","AUTHORS.txt":"4ba13d189cb419382ef9837e74de67c2dba98ff1b378816de2a3f59922da598a","CHANGELOG.md":"5dcce1ee25d1e37a0fa3ce162568061787a13870297d5f95bffa44156efe8701","Cargo.toml":"d7703847fb88c9edcd69a7ce84a4098d21a6dfa2159454067cf9bd56577073ac","LICENSE.txt":"37064a3e3254d5a0c7963d476125e25762e86adadc71959549767486e37c8038","Makefile":"6901ba54f43a90422482457a9237edbee41490b394c09fe5a7d40129db7be5b0","README.md":"d674954a5135c85e2af5e6efa3480b64f16f79dcfface35b01dd837c3b942af6","src/date.rs":"54ccfd7964c1d0ef601c8818bd59c88bf2fb61b51ea78336f190f5e793a47b8d","src/datetime.rs":"400cf1666cfc7224b2e38fbab31236a07f9d75418c9c7b3962d9871e4bda51af","src/div.rs":"bf03964177e2960b0c4726f4856f12b597a59b051241e2a0121501b78825dce8","src/format/mod.rs":"ff50334f39ce537e4745424c8e1a7632a8ec5179fd9047fa0d6cf622d6ce182a","src/format/parse.rs":"0b3ac142ac27b7a32618684b18453e2fd43c7d7d7ddc9b3adbf395c5934e0f1c","src/format/parsed.rs":"6ce9196fa34f29e64a1bc14e76a35b76f4ad5bf72711df8eba2b196aad5ec811","src/format/scan.rs":"ea5ebe5ab966c70f18605edce9a55098ee5f661da1a02b0710559d76067bab79","src/format/strftime.rs":"35ee925171f8c02e876a9b4f515d6ba7eadf3fc8cc914759bee382d5821270e7","src/lib.rs":"1e88f2bdc97130bd6ec3f87bfec4d671167cd66e9daa953b7ce11ceb5ea62928","src/naive/date.rs":"ad4e6b0a4ad939afd79981015d4b2004a33f66abd3c0a3d18a0765d6b87900a1","src/naive/datetime.rs":"317ab30a8648aef7440da5a813a55a7346c24ff13953436bcae7f6888ed0a0c6","src/naive/time.rs":"dab2c7a6cbd8943a1a775c6c8a9a042fed0dacca623c741871d3969a592d733f","src/offset/fixed.rs":"9f103b5abb3927a19bfeb533db5a695451a5e474ed645c7cf1ac52649bc5fe8a","src/offset/local.rs":"c29fdd66a0dd39f32ded7834479dd8755022a791fb13be1ae5027999a86e4a9e","src/offset/mod.rs":"3e732d056a29585d3eecd74ccdbb38c050d08def9d10f0000a7328761e6c77e6","src/offset/utc.rs":"072b460f6b726877344207e68edc00507e08d8a9168f9f571b6631a0c73ea7be"},"package":"9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"}
{"files":{".travis.yml":"40caf89b122270034144d7875fcb27cf7ca9b0e150949e1a908932c4e491956f","AUTHORS.txt":"80aa54d9642f63fc62f20f60e0550f3e596de6ea69883769313c7f07a4be8f4c","CHANGELOG.md":"b9d2b2edfb98954c22e3a34c044bbd2f542cae703d06e5cf15245a1e26b32f76","Cargo.toml":"95d58b3f9a862b6bfd497e8aa87cc14ba6a43e7f6d1818094073e611db14ce43","LICENSE.txt":"46610329ff0b38effb9cb05979ff1ef761e465fed96b2eaca39e439d00129fd7","Makefile":"d76b0b1a44e90b31f2a6f97f662d65df585b1dc88253c30c01ea38d9a097a83e","README.md":"8df7579a4ce5ed034b85b91f6f3106573443138dcc568fd76063016ad2d5cc38","appveyor.yml":"b10751e92a0299968ac5cfd65e918d99e680b6ac679362655b92a393cd22c212","ci/fix-readme.sh":"750d262640a6fdc846623a569f37954bfe7604f9bcbc8f7475db38192e1da0fb","ci/travis.sh":"48eb316d163a9c5b37e4b1d4773e2f9934359a3a1dbddc3b6ae6a58ef15856b1","src/date.rs":"c8d61716eaecf8d0e1a887a0ac9bc06d2e5bf0b47eccc61e5683bdeb0f886ff8","src/datetime.rs":"34e71d822cfa70bb6d1041e3d865fcf629ffb2e29021713bd6aee8a3a6d1410f","src/div.rs":"02e6ce9c4fcafcc7931574108dd7ec0cd28b137edb52eaea654a09ab05fbaf90","src/format/mod.rs":"e36b2bee352df7f0addec7365edfd73607ebaa903d3ddb9249f5fe3c11d9da7a","src/format/parse.rs":"8d5b39483c48771932fd75a9d9892967bd7ef6f0c88d55be6a2d35d35ba21f52","src/format/parsed.rs":"a65cbc0ba13190028ca7c4de4a830b8a64acaf375285cae3a1da1bfd6e5d32f8","src/format/scan.rs":"9f8e4ce8001caf9ec76b3eddf7aa9cc5a68606165e3bb53977350c0a03536b79","src/format/strftime.rs":"532f88654cc1531e6ebdea89039bcf2c364e97478c83824f97f1c38277f3c827","src/lib.rs":"1dae4eb3a73db8dc8fd4f5d3e431fc773104a35c9efaa7a301d73f7b898fc464","src/naive/date.rs":"2fbd7069fb576416f2111298cdd59c729e70736abe53d6e69313a4e45f8a6e3d","src/naive/datetime.rs":"5ae4ed07dc199f7f4be27ef18130de385b56dceb01cefafe5e0f0eb9ed39ce7b","src/naive/internals.rs":"db79eda586b7daad5a2645d21bda80ae92f9bee9870d93d2209a7d228e4286c7","src/naive/isoweek.rs":"75101e996e0eccc6f9b2147095d82050e6dac94a741db60f654f4267bbe96fed","src/naive/time.rs":"cfa4936b341246eb0692e0a071d93707f3545825c74aee67749442ecd2aba655","src/offset/fixed.rs":"e0e41c7081e908a8ada1c1bb67fd003f8a36510c542c5088756214e276407cb9","src/offset/local.rs":"c63a88b8ab4af289cef15d04189f9656c8dfcff77fe8417bbd1182b75184f4e6","src/offset/mod.rs":"2aeeb0fa4c657e810f78ff239d7c52f07c33a2b7bdfc8b3765f4339dcafa0088","src/offset/utc.rs":"630f9513f88353892c9f554eed35d5ec204da9b3d65e7b3c44998836ba3d2d9b","src/oldtime.rs":"42f09a5679c8326ba8f0fe068b35ed1066801903c44b2abfd93f00ef5ec62dbc","src/round.rs":"f7ef334fe4d3014b8a6421202b4a50d316d74199ac154ff553548e8c2c58aa80"},"package":"45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"}

View File

@ -1,18 +1,32 @@
language: rust
sudo: false
rust:
# 1.13.0 is the earliest version that Serde 1.0 tests, so we follow suit
- 1.13.0
- stable
- beta
- nightly
os:
- linux
- osx
matrix:
allow_failures:
- rust: nightly
env: CLIPPY=n
include:
- rust: nightly
env: CLIPPY=y
env:
global:
- LD_LIBRARY_PATH: /usr/local/lib
- secure: i8Ijk6g4/26e3e7+r2OeGAPSP8G8O9P50JibW1omJ0j0ixXhyhPoY2bch3CGhnOu44dI5O31IIbjJJ+iEMp29xQBvkv9YpxAI+hIzOP+XAH6GCYxUDiBVcDoWrXTj+wU6/veuvjLCunu4eRHlskrgJbZXhUVODYzJuLgsN8Ou0w=
script:
- cargo build -v
- cargo build -v --features rustc-serialize
- cargo build -v --features serde
- cargo test -v
- cargo test -v --features rustc-serialize
- cargo test -v --features serde
- cargo doc
after_script:
- cd target && curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh
- CLIPPY: n
script: ./ci/travis.sh
notifications:
email: false
irc:
channels:
- "irc.mozilla.org#chronotope"
template:
- "%{repository_slug}/%{branch} (%{commit} - %{author}): %{message}"
skip_join: true

View File

@ -1,23 +1,36 @@
Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,
and also the following people (in ascending order):
Alex Mikhalev <alexmikhalevalex@gmail.com>
Alexander Bulaev <alexbool@yandex-team.ru>
Ashley Mannix <ashleymannix@live.com.au>
Ben Boeckel <mathstuf@gmail.com>
Ben Eills <ben@beneills.com>
Brandon W Maister <bwm@knewton.com>
Brandon W Maister <quodlibetor@gmail.com>
Colin Ray <r.colinray@gmail.com>
Corey Farwell <coreyf@rwell.org>
Dan <dan@ebip.co.uk>
Danilo Bargen <mail@dbrgn.ch>
David Hewson <dev@daveid.co.uk>
David Ross <daboross@daboross.net>
David Tolnay <dtolnay@gmail.com>
David Willie <david.willie.1@gmail.com>
Eric Findlay <e.findlay@protonmail.ch>
Eunchong Yu <kroisse@gmail.com>
Frans Skarman <frans.skarman@gmail.com>
Huon Wilson <dbau.pp+github@gmail.com>
Igor Gnatenko <ignatenko@redhat.com>
Jim Turner <jturner314@gmail.com>
Jisoo Park <xxxyel@gmail.com>
Joe Wilm <joe@jwilm.com>
John Heitmann <jheitmann@gmail.com>
John Nagle <nagle@sitetruth.com>
Jonas mg <jonasmg@yepmail.net>
János Illés <ijanos@gmail.com>
Ken Tossell <ken@tossell.net>
Martin Risell Lilja <martin.risell.lilja@gmail.com>
Richard Petrie <rap1011@ksu.edu>
Ryan Lewis <ryansname@gmail.com>
Sergey V. Galtsev <sergey.v.galtsev@github.com>
Steve Klabnik <steve@steveklabnik.com>

View File

@ -1,15 +1,264 @@
ChangeLog for Chrono
====================
This documents all notable changes to [Chrono](https://github.com/lifthrasiir/rust-chrono).
This documents all notable changes to [Chrono](https://github.com/chronotope/chrono).
Chrono obeys the principle of [Semantic Versioning](http://semver.org/).
There were/are numerous minor versions before 1.0 due to the language changes.
Versions with only mechnical changes will be omitted from the following list.
## 0.4.5
### Features
* Added several more serde deserialization helpers (@novacrazy #258)
* Enabled all features on the playground (@davidtwco #267)
* Derive `Hash` on `FixedOffset` (@LuoZijun #254)
* Improved docs (@storyfeet #261, @quodlibetor #252)
## 0.4.4
### Features
* Added support for parsing nanoseconds without the leading dot (@emschwartz #251)
## 0.4.3
### Features
* Added methods to DateTime/NaiveDateTime to present the stored value as a number
of nanoseconds since the UNIX epoch (@harkonenbade #247)
* Added a serde serialise/deserialise module for nanosecond timestamps. (@harkonenbade #247)
* Added "Permissive" timezone parsing which allows a numeric timezone to
be specified without minutes. (@quodlibetor #242)
## 0.4.2
### Deprecations
* More strongly deprecate RustcSerialize: remove it from documentation unless
the feature is enabled, issue a deprecation warning if the rustc-serialize
feature is enabled (@quodlibetor #174)
### Features
* Move all uses of the system clock behind a `clock` feature, for use in
environments where we don't have access to the current time. (@jethrogb #236)
* Implement subtraction of two `Date`s, `Time`s, or `DateTime`s, returning a
`Duration` (@tobz1000 #237)
## 0.4.1
### Bug Fixes
* Allow parsing timestamps with subsecond precision (@jonasbb)
* RFC2822 allows times to not include the second (@upsuper)
### Features
* New `timestamp_millis` method on `DateTime` and `NaiveDateTim` that returns
number of milliseconds since the epoch. (@quodlibetor)
* Support exact decimal width on subsecond display for RFC3339 via a new
`to_rfc3339_opts` method on `DateTime` (@dekellum)
* Use no_std-compatible num dependencies (@cuviper)
* Add `SubsecRound` trait that allows rounding to the nearest second
(@dekellum)
### Code Hygiene and Docs
* Docs! (@alatiera @kosta @quodlibetor @kennytm)
* Run clippy and various fixes (@quodlibetor)
## 0.4.0 (2017-06-22)
This was originally planned as a minor release but was pushed to a major
release due to the compatibility concern raised.
### Added
- `IsoWeek` has been added for the ISO week without time zone.
- The `+=` and `-=` operators against `time::Duration` are now supported for
`NaiveDate`, `NaiveTime` and `NaiveDateTime`. (#99)
(Note that this does not invalidate the eventual deprecation of `time::Duration`.)
- `SystemTime` and `DateTime<Tz>` types can be now converted to each other via `From`.
Due to the obvious lack of time zone information in `SystemTime`,
the forward direction is limited to `DateTime<Utc>` and `DateTime<Local>` only.
### Changed
- Intermediate implementation modules have been flattened (#161),
and `UTC` has been renamed to `Utc` in accordance with the current convention (#148).
The full list of changes is as follows:
Before | After
---------------------------------------- | ----------------------------
`chrono::date::Date` | `chrono::Date`
`chrono::date::MIN` | `chrono::MIN_DATE`
`chrono::date::MAX` | `chrono::MAX_DATE`
`chrono::datetime::DateTime` | `chrono::DateTime`
`chrono::naive::time::NaiveTime` | `chrono::naive::NaiveTime`
`chrono::naive::date::NaiveDate` | `chrono::naive::NaiveDate`
`chrono::naive::date::MIN` | `chrono::naive::MIN_DATE`
`chrono::naive::date::MAX` | `chrono::naive::MAX_DATE`
`chrono::naive::datetime::NaiveDateTime` | `chrono::naive::NaiveDateTime`
`chrono::offset::utc::UTC` | `chrono::offset::Utc`
`chrono::offset::fixed::FixedOffset` | `chrono::offset::FixedOffset`
`chrono::offset::local::Local` | `chrono::offset::Local`
`chrono::format::parsed::Parsed` | `chrono::format::Parsed`
With an exception of `Utc`, this change does not affect any direct usage of
`chrono::*` or `chrono::prelude::*` types.
- `Datelike::isoweekdate` is replaced by `Datelike::iso_week` which only returns the ISO week.
The original method used to return a tuple of year number, week number and day of the week,
but this duplicated the `Datelike::weekday` method and it had been hard to deal with
the raw year and week number for the ISO week date.
This change isolates any logic and API for the week date into a separate type.
- `NaiveDateTime` and `DateTime` can now be deserialized from an integral UNIX timestamp. (#125)
This turns out to be very common input for web-related usages.
The existing string representation is still supported as well.
- `chrono::serde` and `chrono::naive::serde` modules have been added
for the serialization utilities. (#125)
Currently they contain the `ts_seconds` modules that can be used to
serialize `NaiveDateTime` and `DateTime` values into an integral UNIX timestamp.
This can be combined with Serde's `[de]serialize_with` attributes
to fully support the (de)serialization to/from the timestamp.
For rustc-serialize, there are separate `chrono::TsSeconds` and `chrono::naive::TsSeconds` types
that are newtype wrappers implementing different (de)serialization logics.
This is a suboptimal API, however, and it is strongly recommended to migrate to Serde.
### Fixed
- 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.
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.
To the end, the new major release was made with some known lower-priority breaking changes.
0.3.1 is now yanked and any remaining 0.3 users can safely roll back to 0.3.0.
- Various documentation fixes and goodies. (#92, #131, #136)
## 0.3.1 (2017-05-02)
### Added
- `Weekday` now implements `FromStr`, `Serialize` and `Deserialize`. (#113)
The syntax is identical to `%A`, i.e. either the shortest or the longest form of English names.
### Changed
- Serde 1.0 is now supported. (#142)
This is technically a breaking change because Serde 0.9 and 1.0 are not compatible,
but this time we decided not to issue a minor version because
we have already seen Serde 0.8 and 0.9 compatibility problems even after 0.3.0 and
a new minor version turned out to be not very helpful for this kind of issues.
### Fixed
- Fixed a bug that the leap second can be mapped wrongly in the local time zone.
Only occurs when the local time zone is behind UTC. (#130)
## 0.3.0 (2017-02-07)
The project has moved to the [Chronotope](https://github.com/chronotope/) organization.
### Added
- `chrono::prelude` module has been added. All other glob imports are now discouraged.
- `FixedOffset` can be added to or subtracted from any timelike types.
- `FixedOffset::local_minus_utc` and `FixedOffset::utc_minus_local` methods have been added.
Note that the old `Offset::local_minus_utc` method is gone; see below.
- Serde support for non-self-describing formats like Bincode is added. (#89)
- Added `Item::Owned{Literal,Space}` variants for owned formatting items. (#76)
- Formatting items and the `Parsed` type have been slightly adjusted so that
they can be internally extended without breaking any compatibility.
- `Weekday` is now `Hash`able. (#109)
- `ParseError` now implements `Eq` as well as `PartialEq`. (#114)
- More documentation improvements. (#101, #108, #112)
### Changed
- Chrono now only supports Rust 1.13.0 or later (previously: Rust 1.8.0 or later).
- Serde 0.9 is now supported.
Due to the API difference, support for 0.8 or older is discontinued. (#122)
- Rustc-serialize implementations are now on par with corresponding Serde implementations.
They both standardize on the `std::fmt::Debug` textual output.
**This is a silent breaking change (hopefully the last though).**
You should be prepared for the format change if you depended on rustc-serialize.
- `Offset::local_minus_utc` is now `Offset::fix`, and returns `FixedOffset` instead of a duration.
This makes every time zone operation operate within a bias less than one day,
and vastly simplifies many logics.
- `chrono::format::format` now receives `FixedOffset` instead of `time::Duration`.
- The following methods and implementations have been renamed and older names have been *removed*.
The older names will be reused for the same methods with `std::time::Duration` in the future.
- `checked_*``checked_*_signed` in `Date`, `DateTime`, `NaiveDate` and `NaiveDateTime` types
- `overflowing_*``overflowing_*_signed` in the `NaiveTime` type
- All subtraction implementations between two time instants have been moved to
`signed_duration_since`, following the naming in `std::time`.
### Fixed
- Fixed a panic when the `Local` offset receives a leap second. (#123)
### Removed
- Rustc-serialize support for `Date<Tz>` types and all offset types has been dropped.
These implementations were automatically derived and never had been in a good shape.
Moreover there are no corresponding Serde implementations, limiting their usefulness.
In the future they may be revived with more complete implementations.
- The following method aliases deprecated in the 0.2 branch have been removed.
- `DateTime::num_seconds_from_unix_epoch` (→ `DateTime::timestamp`)
- `NaiveDateTime::from_num_seconds_from_unix_epoch` (→ `NaiveDateTime::from_timestamp`)
- `NaiveDateTime::from_num_seconds_from_unix_epoch_opt` (→ `NaiveDateTime::from_timestamp_opt`)
- `NaiveDateTime::num_seconds_unix_epoch` (→ `NaiveDateTime::timestamp`)
- Formatting items are no longer `Copy`, except for `chrono::format::Pad`.
- `chrono::offset::add_with_leapsecond` has been removed.
Use a direct addition with `FixedOffset` instead.
## 0.2.25 (2016-08-04)
This is the last version officially supports Rust 1.12.0 or older.
(0.2.24 was accidentally uploaded without a proper check for warnings in the default state,
and replaced by 0.2.25 very shortly. Duh.)

View File

@ -1,24 +1,72 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "chrono"
version = "0.2.25"
authors = ["Kang Seonghoon <public+rust@mearie.org>"]
version = "0.4.6"
authors = ["Kang Seonghoon <public+rust@mearie.org>", "Brandon W Maister <quodlibetor@gmail.com>"]
description = "Date and time library for Rust"
homepage = "https://github.com/lifthrasiir/rust-chrono"
documentation = "https://lifthrasiir.github.io/rust-chrono/"
repository = "https://github.com/lifthrasiir/rust-chrono"
keywords = ["date", "time", "calendar"]
homepage = "https://github.com/chronotope/chrono"
documentation = "https://docs.rs/chrono/"
readme = "README.md"
keywords = ["date", "time", "calendar"]
categories = ["date-and-time"]
license = "MIT/Apache-2.0"
repository = "https://github.com/chronotope/chrono"
[package.metadata.docs.rs]
all-features = true
[package.metadata.playground]
all-features = true
[lib]
name = "chrono"
[dependencies.num-integer]
version = "0.1.36"
default-features = false
[dependencies]
time = "0.1"
num = { version = "0.1", default-features = false }
rustc-serialize = { version = "0.3", optional = true }
serde = { version = "<0.9", optional = true }
[dependencies.num-traits]
version = "0.2"
default-features = false
[dev-dependencies]
serde_json = { version = ">=0.7.0" }
[dependencies.rustc-serialize]
version = "0.3.20"
optional = true
[dependencies.serde]
version = "1"
optional = true
[dependencies.time]
version = "0.1.39"
optional = true
[dev-dependencies.bincode]
version = "0.8.0"
[dev-dependencies.num-iter]
version = "0.1.35"
default-features = false
[dev-dependencies.serde_derive]
version = "1"
[dev-dependencies.serde_json]
version = "1"
[features]
clock = ["time"]
default = ["clock"]
[badges.appveyor]
repository = "chronotope/chrono"
[badges.travis-ci]
repository = "chronotope/chrono"

View File

@ -1,5 +1,6 @@
Rust-chrono is dual-licensed under The MIT License [1] and
Apache 2.0 License [2]. Copyright (c) 2014, Kang Seonghoon.
Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and
contributors.
Nota Bene: This is same as the Rust Project's own license.

View File

@ -12,49 +12,19 @@ authors:
echo >> AUTHORS.txt
git log --format='%aN <%aE>' | grep -v 'Kang Seonghoon' | sort -u >> AUTHORS.txt
.PHONY: readme
.PHONY: readme README.md
readme: README.md
README.md: src/lib.rs
# really, really sorry for this mess.
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< > $@
awk '/^\/\/! # Chrono /{print "[Chrono][doc]",$$4}' $< | sed 's/./=/g' >> $@
echo >> $@
echo '[![Chrono on Travis CI][travis-image]][travis]' >> $@
echo >> $@
echo '[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.png' >> $@
echo '[travis]: https://travis-ci.org/lifthrasiir/rust-chrono' >> $@
awk '/^\/\/! # Chrono /,/^\/\/! ## /' $< | cut -b 5- | grep -v '^#' | \
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
echo '[Complete Documentation][doc]' >> $@
echo >> $@
echo '[doc]: https://lifthrasiir.github.io/rust-chrono/' >> $@
echo >> $@
awk '/^\/\/! ## /,!/^\/\/!/' $< | cut -b 5- | grep -v '^# ' | \
sed 's/](\.\//](https:\/\/lifthrasiir.github.io\/rust-chrono\/chrono\//g' >> $@
( ./ci/fix-readme.sh $< ) > $@
.PHONY: test
test:
cargo test --features 'serde rustc-serialize'
TZ=UTC0 cargo test --features 'serde rustc-serialize bincode' --lib
TZ=ACST-9:30 cargo test --features 'serde rustc-serialize bincode' --lib
TZ=EST4 cargo test --features 'serde rustc-serialize bincode'
.PHONY: doc
doc: authors readme
cargo doc --features 'serde rustc-serialize'
.PHONY: doc-publish
doc-publish: doc
( \
PKGID="$$(cargo pkgid)"; \
PKGNAMEVER="$${PKGID#*#}"; \
PKGNAME="$${PKGNAMEVER%:*}"; \
REMOTE="$$(git config --get remote.origin.url)"; \
cd target/doc && \
rm -rf .git && \
git init && \
git checkout --orphan gh-pages && \
echo '<!doctype html><html><head><meta http-equiv="refresh" content="0;URL='$$PKGNAME'/index.html"></head><body></body></html>' > index.html && \
git add . && \
git commit -m 'updated docs.' && \
git push "$$REMOTE" gh-pages -f; \
)
cargo doc --features 'serde rustc-serialize bincode'

View File

@ -1,12 +1,23 @@
[Chrono][doc] 0.2.25
====================
[Chrono][docsrs]: Date and Time for Rust
========================================
[![Chrono on Travis CI][travis-image]][travis]
[![Chrono on Appveyor][appveyor-image]][appveyor]
[![Chrono on crates.io][cratesio-image]][cratesio]
[![Chrono on docs.rs][docsrs-image]][docsrs]
[![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter]
[travis-image]: https://travis-ci.org/lifthrasiir/rust-chrono.png
[travis]: https://travis-ci.org/lifthrasiir/rust-chrono
[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master
[travis]: https://travis-ci.org/chronotope/chrono
[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true
[appveyor]: https://ci.appveyor.com/project/chronotope/chrono
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
[cratesio]: https://crates.io/crates/chrono
[docsrs-image]: https://docs.rs/chrono/badge.svg
[docsrs]: https://docs.rs/chrono
[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
[gitter]: https://gitter.im/chrono-rs/chrono
Date and time handling for Rust. (also known as `rust-chrono`)
It aims to be a feature-complete superset of
the [time](https://github.com/rust-lang-deprecated/time) library.
In particular,
@ -16,16 +27,16 @@ In particular,
* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
There were several previous attempts to bring a good date and time library to Rust,
which Chrono builts upon and should acknowledge:
which Chrono builds upon and should acknowledge:
* [Initial research on
the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
[Complete Documentation][doc]
Any significant changes to Chrono are documented in
the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/master/CHANGELOG.md) file.
[doc]: https://lifthrasiir.github.io/rust-chrono/
## Usage
@ -33,16 +44,15 @@ Put this in your `Cargo.toml`:
```toml
[dependencies]
chrono = "0.2"
chrono = "0.4"
```
Or, if you want [Serde](https://github.com/serde-rs/serde) or
[rustc-serialize](https://github.com/rust-lang-nursery/rustc-serialize) support,
include the features like this:
Or, if you want [Serde](https://github.com/serde-rs/serde) include the
feature like this:
```toml
[dependencies]
chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
chrono = { version = "0.4", features = ["serde"] }
```
Then put this in your crate root:
@ -51,20 +61,39 @@ Then put this in your crate root:
extern crate chrono;
```
Avoid using `use chrono::*;` as Chrono exports several modules other than types.
If you prefer the glob imports, use the following instead:
```rust
use chrono::prelude::*;
```
## Overview
### Duration
[**`Duration`**](https://lifthrasiir.github.io/rust-chrono/chrono/struct.Duration.html)
represents the magnitude of a time span. `Duration` used to be provided by Chrono.
It has been moved to the `time` crate as the
[`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
still re-exported from Chrono.
Chrono currently uses
the [`time::Duration`](https://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,
the reference will refer this type as `OldDuration`.
Note that this is an "accurate" duration represented as seconds and
nanoseconds and does not represent "nominal" components such as days or
months.
Chrono does not yet natively support
the standard [`Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type,
but it will be supported in the future.
Meanwhile you can convert between two types with
[`Duration::from_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.from_std)
and
[`Duration::to_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.to_std)
methods.
### Date and Time
Chrono provides a
[**`DateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html)
[**`DateTime`**](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html)
type to represent a date and a time in a timezone.
For more abstract moment-in-time tracking such as internal timekeeping
@ -75,15 +104,15 @@ which tracks your system clock, or
is an opaque but monotonically-increasing representation of a moment in time.
`DateTime` is timezone-aware and must be constructed from
the [**`TimeZone`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html) object,
the [**`TimeZone`**](https://docs.rs/chrono/0.4.6/chrono/offset/trait.TimeZone.html) object,
which defines how the local date is converted to and back from the UTC date.
There are three well-known `TimeZone` implementations:
* [**`UTC`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient.
* [**`Utc`**](https://docs.rs/chrono/0.4.6/chrono/offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
* [**`Local`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html) specifies the system local time zone.
* [**`Local`**](https://docs.rs/chrono/0.4.6/chrono/offset/struct.Local.html) specifies the system local time zone.
* [**`FixedOffset`**](https://lifthrasiir.github.io/rust-chrono/chrono/offset/fixed/struct.FixedOffset.html) specifies
* [**`FixedOffset`**](https://docs.rs/chrono/0.4.6/chrono/offset/struct.FixedOffset.html) specifies
an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
This often results from the parsed textual date and time.
Since it stores the most information and does not depend on the system environment,
@ -91,58 +120,60 @@ There are three well-known `TimeZone` implementations:
`DateTime`s with different `TimeZone` types are distinct and do not mix,
but can be converted to each other using
the [`DateTime::with_timezone`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.with_timezone) method.
the [`DateTime::with_timezone`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.with_timezone) method.
You can get the current date and time in the UTC time zone
([`UTC::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/utc/struct.UTC.html#method.now))
([`Utc::now()`](https://docs.rs/chrono/0.4.6/chrono/offset/struct.Utc.html#method.now))
or in the local time zone
([`Local::now()`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/local/struct.Local.html#method.now)).
([`Local::now()`](https://docs.rs/chrono/0.4.6/chrono/offset/struct.Local.html#method.now)).
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
let utc: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
~~~~
```
Alternatively, you can create your own date and time.
This is a bit verbose due to Rust's lack of function and method overloading,
but in turn we get a rich combination of initialization methods.
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
use chrono::offset::LocalResult;
let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
// July 8 is 188th day of the year 2014 (`o` for "ordinal")
assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
// July 8 is Tuesday in ISO week 28 of the year 2014.
assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
// dynamic verification
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
// other time zone objects can be used to construct a local datetime.
// obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
let local_dt = Local.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12);
let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
assert_eq!(dt, fixed_dt);
~~~~
```
Various properties are available to the date and time, and can be altered individually.
Most of them are defined in the traits [`Datelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Datelike.html) and
[`Timelike`](https://lifthrasiir.github.io/rust-chrono/chrono/trait.Timelike.html) which you should `use` before.
Most of them are defined in the traits [`Datelike`](https://docs.rs/chrono/0.4.6/chrono/trait.Datelike.html) and
[`Timelike`](https://docs.rs/chrono/0.4.6/chrono/trait.Timelike.html) which you should `use` before.
Addition and subtraction is also supported.
The following illustrates most supported operations to the date and time:
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
use time::Duration;
// assume this returned `2014-11-28T21:45:59.324310806+09:00`:
let dt = Local::now();
@ -157,9 +188,9 @@ assert_eq!(dt.ordinal(), 332); // the day of year
assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
// time zone accessor and manipulation
assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
// a sample of property manipulations (validates dynamically)
assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
@ -167,28 +198,33 @@ assert_eq!(dt.with_day(32), None);
assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
// arithmetic operations
assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
Duration::seconds(-2 * 3600 + 2));
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
~~~~
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
```
Formatting is done via the [`format`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.format) method,
### Formatting and Parsing
Formatting is done via the [`format`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.format) method,
which format is equivalent to the familiar `strftime` format.
(See the [`format::strftime` module documentation](https://lifthrasiir.github.io/rust-chrono/chrono/format/strftime/index.html#specifiers)
for full syntax.)
See [`format::strftime`](https://docs.rs/chrono/0.4.6/chrono/format/strftime/index.html#specifiers)
documentation for full syntax and list of specifiers.
The default `to_string` method and `{:?}` specifier also give a reasonable representation.
Chrono also provides [`to_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc2822) and
[`to_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.to_rfc3339) methods
Chrono also provides [`to_rfc2822`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.to_rfc2822) and
[`to_rfc3339`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.to_rfc3339) methods
for well-known formats.
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
@ -197,44 +233,48 @@ assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC");
assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
~~~~
// Note that milli/nanoseconds are only printed if they are non-zero
let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1);
assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z");
```
Parsing can be done with three methods:
1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
(and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<UTC>` and
on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
`DateTime<Local>` values. This parses what the `{:?}`
([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
format specifier prints, and requires the offset to be present.
2. [`DateTime::parse_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_str) parses
2. [`DateTime::parse_from_str`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.parse_from_str) parses
a date and time with offsets and returns `DateTime<FixedOffset>`.
This should be used when the offset is a part of input and the caller cannot guess that.
It *cannot* be used when the offset can be missing.
[`DateTime::parse_from_rfc2822`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc2822)
[`DateTime::parse_from_rfc2822`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.parse_from_rfc2822)
and
[`DateTime::parse_from_rfc3339`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.parse_from_rfc3339)
[`DateTime::parse_from_rfc3339`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.parse_from_rfc3339)
are similar but for well-known formats.
3. [`Offset::datetime_from_str`](https://lifthrasiir.github.io/rust-chrono/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is
3. [`Offset::datetime_from_str`](https://docs.rs/chrono/0.4.6/chrono/offset/trait.TimeZone.html#method.datetime_from_str) is
similar but returns `DateTime` of given offset.
When the explicit offset is missing from the input, it simply uses given offset.
It issues an error when the input contains an explicit offset different
from the current offset.
More detailed control over the parsing process is available via
[`format`](https://lifthrasiir.github.io/rust-chrono/chrono/format/index.html) module.
[`format`](https://docs.rs/chrono/0.4.6/chrono/format/index.html) module.
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
// method 1
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone()));
assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
// method 2
@ -245,57 +285,85 @@ assert_eq!(DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"),
assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
// method 3
assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
// oops, the year is missing!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
// oops, the format string does not include the year at all!
assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
// oops, the weekday is incorrect!
assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
~~~~
assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
```
Again : See [`format::strftime`](https://docs.rs/chrono/0.4.6/chrono/format/strftime/index.html#specifiers)
documentation for full syntax and list of specifiers.
### Conversion from and to EPOCH timestamps
Use [`Utc.timestamp(seconds, nanoseconds)`](https://docs.rs/chrono/0.4.6/chrono/offset/trait.TimeZone.html#method.timestamp)
to construct a [`DateTime<Utc>`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html) from a UNIX timestamp
(seconds, nanoseconds that passed since January 1st 1970).
Use [`DateTime.timestamp`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.timestamp) to get the timestamp (in seconds)
from a [`DateTime`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html). Additionally, you can use
[`DateTime.timestamp_subsec_nanos`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.timestamp_subsec_nanos)
to get the number of additional number of nanoseconds.
```rust
// We need the trait in scope to use Utc::timestamp().
use chrono::TimeZone;
// Construct a datetime from epoch:
let dt = Utc.timestamp(1_500_000_000, 0);
assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000");
// Get epoch value from a datetime:
let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap();
assert_eq!(dt.timestamp(), 1_500_000_000);
```
### Individual date
Chrono also provides an individual date type ([**`Date`**](https://lifthrasiir.github.io/rust-chrono/chrono/date/struct.Date.html)).
Chrono also provides an individual date type ([**`Date`**](https://docs.rs/chrono/0.4.6/chrono/struct.Date.html)).
It also has time zones attached, and have to be constructed via time zones.
Most operations available to `DateTime` are also available to `Date` whenever appropriate.
~~~~ {.rust}
use chrono::*;
```rust
use chrono::prelude::*;
use chrono::offset::LocalResult;
assert_eq!(UTC::today(), UTC::now().date());
assert_eq!(Utc::today(), Utc::now().date());
assert_eq!(Local::today(), Local::now().date());
assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
"070809");
~~~~
```
There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
`DateTime` has [`date`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.date) method
`DateTime` has [`date`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.date) method
which returns a `Date` which represents its date component.
There is also a [`time`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.time) method,
There is also a [`time`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.time) method,
which simply returns a naive local time described below.
### Naive date and time
Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
as [**`NaiveDate`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/date/struct.NaiveDate.html),
[**`NaiveTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/struct.NaiveTime.html) and
[**`NaiveDateTime`**](https://lifthrasiir.github.io/rust-chrono/chrono/naive/datetime/struct.NaiveDateTime.html) respectively.
as [**`NaiveDate`**](https://docs.rs/chrono/0.4.6/chrono/naive/struct.NaiveDate.html),
[**`NaiveTime`**](https://docs.rs/chrono/0.4.6/chrono/naive/struct.NaiveTime.html) and
[**`NaiveDateTime`**](https://docs.rs/chrono/0.4.6/chrono/naive/struct.NaiveDateTime.html) respectively.
They have almost equivalent interfaces as their timezone-aware twins,
but are not associated to time zones obviously and can be quite low-level.
They are mostly useful for building blocks for higher-level types.
Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
[`naive_local`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_local) returns
[`naive_local`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.naive_local) returns
a view to the naive local time,
and [`naive_utc`](https://lifthrasiir.github.io/rust-chrono/chrono/datetime/struct.DateTime.html#method.naive_utc) returns
and [`naive_utc`](https://docs.rs/chrono/0.4.6/chrono/struct.DateTime.html#method.naive_utc) returns
a view to the naive UTC time.
## Limitations
@ -307,7 +375,7 @@ Date types are limited in about +/- 262,000 years from the common epoch.
Time types are limited in the nanosecond accuracy.
[Leap seconds are supported in the representation but
Chrono doesn't try to make use of them](https://lifthrasiir.github.io/rust-chrono/chrono/naive/time/index.html#leap-second-handling).
Chrono doesn't try to make use of them](https://docs.rs/chrono/0.4.6/chrono/naive/struct.NaiveTime.html#leap-second-handling).
(The main reason is that leap seconds are not really predictable.)
Almost *every* operation over the possible leap seconds will ignore them.
Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
@ -316,7 +384,8 @@ if you want.
Chrono inherently does not support an inaccurate or partial date and time representation.
Any operation that can be ambiguous will return `None` in such cases.
For example, "a month later" of 2014-01-30 is not well-defined
and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`.
Advanced time zone handling is not yet supported (but is planned in 0.3).
Advanced time zone handling is not yet supported.
For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.

21
third_party/rust/chrono/appveyor.yml vendored Normal file
View File

@ -0,0 +1,21 @@
environment:
matrix:
- TARGET: 1.13.0-x86_64-pc-windows-gnu
- TARGET: nightly-x86_64-pc-windows-msvc
- TARGET: nightly-i686-pc-windows-msvc
- TARGET: nightly-x86_64-pc-windows-gnu
- TARGET: nightly-i686-pc-windows-gnu
matrix:
allow_failures:
- channel: nightly
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" -FileName "rust-install.exe"
- ps: .\rust-install.exe /VERYSILENT /NORESTART /DIR="C:\rust" | Out-Null
- ps: $env:PATH="$env:PATH;C:\rust\bin"
- rustc -vV
- cargo -vV
build: false
test_script:
- sh -c 'PATH=`rustc --print sysroot`/bin:$PATH ./ci/travis.sh'

35
third_party/rust/chrono/ci/fix-readme.sh vendored Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
VERSION="$( cargo read-manifest | python -c 'import json, sys; print(json.load(sys.stdin)["version"])')"
LIB="$1"
# Make the Chrono in the header a link to the docs
awk '/^\/\/! # Chrono: / { print "[Chrono][docsrs]:", substr($0, index($0, $4))}' "$LIB"
awk '/^\/\/! # Chrono: / { print "[Chrono][docsrs]:", substr($0, index($0, $4))}' "$LIB" | sed 's/./=/g'
# Add all the badges
echo '
[![Chrono on Travis CI][travis-image]][travis]
[![Chrono on Appveyor][appveyor-image]][appveyor]
[![Chrono on crates.io][cratesio-image]][cratesio]
[![Chrono on docs.rs][docsrs-image]][docsrs]
[![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter]
[travis-image]: https://travis-ci.org/chronotope/chrono.svg?branch=master
[travis]: https://travis-ci.org/chronotope/chrono
[appveyor-image]: https://ci.appveyor.com/api/projects/status/2ia91ofww4w31m2w/branch/master?svg=true
[appveyor]: https://ci.appveyor.com/project/chronotope/chrono
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
[cratesio]: https://crates.io/crates/chrono
[docsrs-image]: https://docs.rs/chrono/badge.svg
[docsrs]: https://docs.rs/chrono
[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
[gitter]: https://gitter.im/chrono-rs/chrono'
# print the section between the header and the usage
awk '/^\/\/! # Chrono:/,/^\/\/! ## /' "$LIB" | cut -b 5- | grep -v '^#' | \
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$VERSION'\/chrono\//g'
echo
# Replace relative doc links with links to this exact version of docs on
# docs.rs
awk '/^\/\/! ## /,!/^\/\/!/' "$LIB" | cut -b 5- | grep -v '^# ' | \
sed 's/](\.\//](https:\/\/docs.rs\/chrono\/'$VERSION'\/chrono\//g' \

100
third_party/rust/chrono/ci/travis.sh vendored Executable file
View File

@ -0,0 +1,100 @@
#!/bin/bash
# This is the script that's executed by travis, you can run it yourself to run
# the exact same suite
set -e
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
channel() {
if [ -n "${TRAVIS}" ]; then
if [ "${TRAVIS_RUST_VERSION}" = "${CHANNEL}" ]; then
pwd
(set -x; cargo "$@")
fi
elif [ -n "${APPVEYOR}" ]; then
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
pwd
(set -x; cargo "$@")
fi
else
pwd
(set -x; cargo "+${CHANNEL}" "$@")
fi
}
build_and_test() {
# interleave building and testing in hope that it saves time
# also vary the local time zone to (hopefully) catch tz-dependent bugs
# also avoid doc-testing multiple times---it takes a lot and rarely helps
cargo clean
channel build -v
TZ=ACST-9:30 channel test -v --lib
channel build -v --features rustc-serialize
TZ=EST4 channel test -v --features rustc-serialize --lib
channel build -v --features serde
TZ=UTC0 channel test -v --features serde --lib
channel build -v --features serde,rustc-serialize
TZ=Asia/Katmandu channel test -v --features serde,rustc-serialize
# without default "clock" feature
channel build -v --no-default-features
TZ=ACST-9:30 channel test -v --no-default-features --lib
channel build -v --no-default-features --features rustc-serialize
TZ=EST4 channel test -v --no-default-features --features rustc-serialize --lib
channel build -v --no-default-features --features 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
if [[ "$CHANNEL" == stable ]]; then
if [[ -n "$TRAVIS" ]] ; then
check_readme
fi
fi
}
build_only() {
# Rust 1.13 doesn't support custom derive, so, to avoid doctests which
# validate that, we just build there.
cargo clean
channel build -v
channel build -v --features rustc-serialize
channel build -v --features 'serde bincode'
channel build -v --no-default-features
}
run_clippy() {
# cached installation will not work on a later nightly
if [ -n "${TRAVIS}" ] && ! cargo install clippy --debug --force; then
echo "COULD NOT COMPILE CLIPPY, IGNORING CLIPPY TESTS"
exit
fi
cargo clippy --features 'serde bincode rustc-serialize' -- -Dclippy
}
check_readme() {
make readme
(set -x; git diff --exit-code -- README.md) ; echo $?
}
rustc --version
cargo --version
CHANNEL=nightly
if [ "x${CLIPPY}" = xy ] ; then
run_clippy
else
build_and_test
fi
CHANNEL=beta
build_and_test
CHANNEL=stable
build_and_test
CHANNEL=1.13.0
build_only

View File

@ -1,29 +1,24 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* ISO 8601 calendar date with time zone.
*/
//! ISO 8601 calendar date with time zone.
use std::{fmt, hash};
use std::cmp::Ordering;
use std::ops::{Add, Sub};
use oldtime::Duration as OldDuration;
use {Weekday, Datelike};
use duration::Duration;
use offset::{TimeZone, Offset};
use offset::utc::UTC;
use naive;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use datetime::DateTime;
use offset::{TimeZone, Utc};
use naive::{self, NaiveDate, NaiveTime, IsoWeek};
use DateTime;
use format::{Item, DelayedFormat, StrftimeItems};
/// ISO 8601 calendar date with time zone.
///
/// This type should be considered ambiguous at best,
/// due to the inherent lack of precision required for the time zone resolution.
/// For serialization and deserialization uses, it is best to use `NaiveDate` instead.
/// There are some guarantees on the usage of `Date<Tz>`:
///
/// - If properly constructed via `TimeZone::ymd` and others without an error,
@ -50,9 +45,9 @@ pub struct Date<Tz: TimeZone> {
}
/// The minimum possible `Date`.
pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
pub const MIN_DATE: Date<Utc> = Date { date: naive::MIN_DATE, offset: Utc };
/// The maximum possible `Date`.
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };
pub const MAX_DATE: Date<Utc> = Date { date: naive::MAX_DATE, offset: Utc };
impl<Tz: TimeZone> Date<Tz> {
/// Makes a new `Date` with given *UTC* date and offset.
@ -189,7 +184,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Retrieves an associated offset from UTC.
#[inline]
pub fn offset<'a>(&'a self) -> &'a Tz::Offset {
pub fn offset(&self) -> &Tz::Offset {
&self.offset
}
@ -210,8 +205,8 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_add(rhs));
pub fn checked_add_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_add_signed(rhs));
Some(Date { date: date, offset: self.offset })
}
@ -219,11 +214,22 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_sub(rhs));
pub fn checked_sub_signed(self, rhs: OldDuration) -> Option<Date<Tz>> {
let date = try_opt!(self.date.checked_sub_signed(rhs));
Some(Date { date: date, offset: self.offset })
}
/// Subtracts another `Date` from the current date.
/// Returns a `Duration` of integral numbers.
///
/// This does not overflow or underflow at all,
/// as all possible output fits in the range of `Duration`.
#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
#[inline]
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> OldDuration {
self.date.signed_duration_since(rhs.date)
}
/// Returns a view to the naive UTC date.
#[inline]
pub fn naive_utc(&self) -> NaiveDate {
@ -231,9 +237,13 @@ impl<Tz: TimeZone> Date<Tz> {
}
/// Returns a view to the naive local date.
///
/// This is technically same to [`naive_utc`](#method.naive_utc)
/// because the offset is restricted to never exceed one day,
/// but provided for the consistency.
#[inline]
pub fn naive_local(&self) -> NaiveDate {
self.date + self.offset.local_minus_utc()
self.date
}
}
@ -252,7 +262,7 @@ impl<Tz: TimeZone> Date<Tz> where Tz::Offset: fmt::Display {
}
/// Formats the date with the specified format string.
/// See the [`format::strftime` module](../format/strftime/index.html)
/// See the [`format::strftime` module](./format/strftime/index.html)
/// on the supported escape sequences.
#[inline]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
@ -269,7 +279,7 @@ impl<Tz: TimeZone> Datelike for Date<Tz> {
#[inline] fn ordinal(&self) -> u32 { self.naive_local().ordinal() }
#[inline] fn ordinal0(&self) -> u32 { self.naive_local().ordinal0() }
#[inline] fn weekday(&self) -> Weekday { self.naive_local().weekday() }
#[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.naive_local().isoweekdate() }
#[inline] fn iso_week(&self) -> IsoWeek { self.naive_local().iso_week() }
#[inline]
fn with_year(&self, year: i32) -> Option<Date<Tz>> {
@ -332,28 +342,30 @@ impl<Tz: TimeZone> hash::Hash for Date<Tz> {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.date.hash(state) }
}
impl<Tz: TimeZone> Add<Duration> for Date<Tz> {
impl<Tz: TimeZone> Add<OldDuration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn add(self, rhs: Duration) -> Date<Tz> {
self.checked_add(rhs).expect("`Date + Duration` overflowed")
fn add(self, rhs: OldDuration) -> Date<Tz> {
self.checked_add_signed(rhs).expect("`Date + Duration` overflowed")
}
}
impl<Tz: TimeZone, Tz2: TimeZone> Sub<Date<Tz2>> for Date<Tz> {
type Output = Duration;
#[inline]
fn sub(self, rhs: Date<Tz2>) -> Duration { self.date - rhs.date }
}
impl<Tz: TimeZone> Sub<Duration> for Date<Tz> {
impl<Tz: TimeZone> Sub<OldDuration> for Date<Tz> {
type Output = Date<Tz>;
#[inline]
fn sub(self, rhs: Duration) -> Date<Tz> {
self.checked_sub(rhs).expect("`Date - Duration` overflowed")
fn sub(self, rhs: OldDuration) -> Date<Tz> {
self.checked_sub_signed(rhs).expect("`Date - Duration` overflowed")
}
}
impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> {
type Output = OldDuration;
#[inline]
fn sub(self, rhs: Date<Tz>) -> OldDuration {
self.signed_duration_since(rhs)
}
}
@ -369,114 +381,3 @@ impl<Tz: TimeZone> fmt::Display for Date<Tz> where Tz::Offset: fmt::Display {
}
}
#[cfg(feature = "rustc-serialize")]
mod rustc_serialize {
use super::Date;
use offset::TimeZone;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
// TODO the current serialization format is NEVER intentionally defined.
// in the future it is likely to be redefined to more sane and reasonable format.
impl<Tz: TimeZone> Encodable for Date<Tz> where Tz::Offset: Encodable {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("Date", 2, |s| {
try!(s.emit_struct_field("date", 0, |s| self.date.encode(s)));
try!(s.emit_struct_field("offset", 1, |s| self.offset.encode(s)));
Ok(())
})
}
}
impl<Tz: TimeZone> Decodable for Date<Tz> where Tz::Offset: Decodable {
fn decode<D: Decoder>(d: &mut D) -> Result<Date<Tz>, D::Error> {
d.read_struct("Date", 2, |d| {
let date = try!(d.read_struct_field("date", 0, Decodable::decode));
let offset = try!(d.read_struct_field("offset", 1, Decodable::decode));
Ok(Date::from_utc(date, offset))
})
}
}
#[test]
fn test_encodable() {
use offset::utc::UTC;
use rustc_serialize::json::encode;
assert_eq!(encode(&UTC.ymd(2014, 7, 24)).ok(),
Some(r#"{"date":{"ymdf":16501977},"offset":{}}"#.into()));
}
#[test]
fn test_decodable() {
use offset::utc::UTC;
use rustc_serialize::json;
let decode = |s: &str| json::decode::<Date<UTC>>(s);
assert_eq!(decode(r#"{"date":{"ymdf":16501977},"offset":{}}"#).ok(),
Some(UTC.ymd(2014, 7, 24)));
assert!(decode(r#"{"date":{"ymdf":0},"offset":{}}"#).is_err());
}
}
#[cfg(test)]
mod tests {
use std::fmt;
use Datelike;
use duration::Duration;
use naive::date::NaiveDate;
use naive::datetime::NaiveDateTime;
use offset::{TimeZone, Offset, LocalResult};
use offset::local::Local;
#[derive(Copy, Clone, PartialEq, Eq)]
struct UTC1y; // same to UTC but with an offset of 365 days
#[derive(Copy, Clone, PartialEq, Eq)]
struct OneYear;
impl TimeZone for UTC1y {
type Offset = OneYear;
fn from_offset(_offset: &OneYear) -> UTC1y { UTC1y }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<OneYear> {
LocalResult::Single(OneYear)
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<OneYear> {
LocalResult::Single(OneYear)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> OneYear { OneYear }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> OneYear { OneYear }
}
impl Offset for OneYear {
fn local_minus_utc(&self) -> Duration { Duration::days(365) }
}
impl fmt::Debug for OneYear {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
}
#[test]
fn test_date_weird_offset() {
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29)),
"2012-02-29+8760:00".to_string());
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7)),
"2012-02-29T05:06:07+8760:00".to_string());
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4)),
"2012-03-04+8760:00".to_string());
assert_eq!(format!("{:?}", UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7)),
"2012-03-04T05:06:07+8760:00".to_string());
}
#[test]
fn test_local_date_sanity_check() { // issue #27
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
// This is a part of rust-chrono.
// Copyright (c) 2014, Kang Seonghoon.
// Copyright 2013-2014 The Rust Project Developers.
// This is a part of Chrono.
// Portions Copyright 2013-2014 The Rust Project Developers.
// See README.md and LICENSE.txt for details.
//! Integer division utilities. (Shamelessly copied from [num](https://github.com/rust-lang/num/))
@ -8,7 +7,7 @@
// Algorithm from [Daan Leijen. _Division and Modulus for Computer Scientists_,
// December 2001](http://research.microsoft.com/pubs/151917/divmodnote-letter.pdf)
pub use num::integer::{div_rem, div_floor, mod_floor, div_mod_floor};
pub use num_integer::{div_rem, div_floor, mod_floor, div_mod_floor};
#[cfg(test)]
mod tests {

View File

@ -1,23 +1,37 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! Formatting utilities for date and time.
//! Formatting (and parsing) utilities for date and time.
//!
//! This module provides the common types and routines to implement,
//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or
//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods.
//! For most cases you should use these high-level interfaces.
//!
//! Internally the formatting and parsing shares the same abstract **formatting items**,
//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of
//! the [`Item`](./enum.Item.html) type.
//! They are generated from more readable **format strings**;
//! currently Chrono supports [one built-in syntax closely resembling
//! C's `strftime` format](./strftime/index.html).
use std::fmt;
use std::str::FromStr;
use std::error::Error;
use {Datelike, Timelike};
use {Datelike, Timelike, Weekday, ParseWeekdayError};
use div::{div_floor, mod_floor};
use duration::Duration;
use offset::Offset;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use offset::{Offset, FixedOffset};
use naive::{NaiveDate, NaiveTime};
pub use self::strftime::StrftimeItems;
pub use self::parsed::Parsed;
pub use self::parse::parse;
/// An unhabitated type used for `InternalNumeric` and `InternalFixed` below.
#[derive(Clone, PartialEq, Eq)]
enum Void {}
/// Padding characters for numeric items.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Pad {
@ -38,11 +52,11 @@ pub enum Pad {
/// If the number is too long or (in some cases) negative, it is printed as is.
///
/// The **parsing width** is the maximal width to be scanned.
/// The parser only tries to consume from one to given number of digits (greedily).
/// The parser only tries to consume from one to given number of digits (greedily).
/// It also trims the preceding whitespaces if any.
/// It cannot parse the negative number, so some date and time cannot be formatted then
/// parsed with the same formatting items.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Numeric {
/// Full Gregorian year (FW=4, PW=∞).
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
@ -89,13 +103,45 @@ pub enum Numeric {
/// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞).
/// For formatting, it assumes UTC upon the absence of time zone offset.
Timestamp,
/// Internal uses only.
///
/// This item exists so that one can add additional internal-only formatting
/// without breaking major compatibility (as enum variants cannot be selectively private).
Internal(InternalNumeric),
}
/// An opaque type representing numeric item types for internal uses only.
pub struct InternalNumeric {
_dummy: Void,
}
impl Clone for InternalNumeric {
fn clone(&self) -> Self {
match self._dummy {}
}
}
impl PartialEq for InternalNumeric {
fn eq(&self, _other: &InternalNumeric) -> bool {
match self._dummy {}
}
}
impl Eq for InternalNumeric {
}
impl fmt::Debug for InternalNumeric {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "<InternalNumeric>")
}
}
/// Fixed-format item types.
///
/// They have their own rules of formatting and parsing.
/// Otherwise noted, they print in the specified cases but parse case-insensitively.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Fixed {
/// Abbreviated month names.
///
@ -139,14 +185,14 @@ pub enum Fixed {
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces.
/// The offset is limited from `-24:00` to `+24:00`,
/// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range.
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColon,
/// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`).
///
/// In the parser, the colon can be omitted and/or surrounded with any amount of whitespaces,
/// and `Z` can be either in upper case or in lower case.
/// The offset is limited from `-24:00` to `+24:00`,
/// which is same to [`FixedOffset`](../offset/fixed/struct.FixedOffset.html)'s range.
/// which is same to [`FixedOffset`](../offset/struct.FixedOffset.html)'s range.
TimezoneOffsetColonZ,
/// Same to [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon.
/// Parsing allows an optional colon.
@ -158,15 +204,50 @@ pub enum Fixed {
RFC2822,
/// RFC 3339 & ISO 8601 date and time syntax.
RFC3339,
/// Internal uses only.
///
/// This item exists so that one can add additional internal-only formatting
/// without breaking major compatibility (as enum variants cannot be selectively private).
Internal(InternalFixed),
}
/// An opaque type representing fixed-format item types for internal uses only.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InternalFixed {
val: InternalInternal,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum InternalInternal {
/// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but
/// allows missing minutes (per [ISO 8601][iso8601]).
///
/// # Panics
///
/// If you try to use this for printing.
///
/// [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.
Nanosecond3NoDot,
/// Same to [`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.
Nanosecond9NoDot,
}
/// A single formatting item. This is used for both formatting and parsing.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Item<'a> {
/// A literally printed and parsed text.
Literal(&'a str),
/// Same to `Literal` but with the string owned by the item.
OwnedLiteral(Box<str>),
/// Whitespace. Prints literally but reads zero or more whitespace.
Space(&'a str),
/// Same to `Space` but with the string owned by the item.
OwnedSpace(Box<str>),
/// Numeric item. Can be optionally padded to the maximal length (if any) when formatting;
/// the parser simply ignores any padded whitespace and zeroes.
Numeric(Numeric, Pad),
@ -182,12 +263,13 @@ macro_rules! num { ($x:ident) => (Item::Numeric(Numeric::$x, Pad::None)) }
macro_rules! num0 { ($x:ident) => (Item::Numeric(Numeric::$x, Pad::Zero)) }
macro_rules! nums { ($x:ident) => (Item::Numeric(Numeric::$x, Pad::Space)) }
macro_rules! fix { ($x:ident) => (Item::Fixed(Fixed::$x)) }
macro_rules! internal_fix { ($x:ident) => (Item::Fixed(Fixed::Internal(InternalFixed { val: InternalInternal::$x })))}
/// An error from the `parse` function.
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct ParseError(ParseErrorKind);
#[derive(Debug, Clone, PartialEq, Copy)]
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
enum ParseErrorKind {
/// Given field is out of permitted range.
OutOfRange,
@ -253,7 +335,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
/// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`.
pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Option<&NaiveTime>,
off: Option<&(String, Duration)>, items: I) -> fmt::Result
off: Option<&(String, FixedOffset)>, items: I) -> fmt::Result
where I: Iterator<Item=Item<'a>> {
// full and abbreviated month and weekday names
static SHORT_MONTHS: [&'static str; 12] =
@ -269,6 +351,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
for item in items {
match item {
Item::Literal(s) | Item::Space(s) => try!(write!(w, "{}", s)),
Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => try!(write!(w, "{}", s)),
Item::Numeric(spec, pad) => {
use self::Numeric::*;
@ -279,26 +362,30 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7;
let (width, v) = match spec {
Year => (4, date.map(|d| d.year() as i64)),
YearDiv100 => (2, date.map(|d| div_floor(d.year() as i64, 100))),
YearMod100 => (2, date.map(|d| mod_floor(d.year() as i64, 100))),
IsoYear => (4, date.map(|d| d.isoweekdate().0 as i64)),
IsoYearDiv100 => (2, date.map(|d| div_floor(d.isoweekdate().0 as i64, 100))),
IsoYearMod100 => (2, date.map(|d| mod_floor(d.isoweekdate().0 as i64, 100))),
Month => (2, date.map(|d| d.month() as i64)),
Day => (2, date.map(|d| d.day() as i64)),
WeekFromSun => (2, date.map(|d| week_from_sun(d) as i64)),
WeekFromMon => (2, date.map(|d| week_from_mon(d) as i64)),
IsoWeek => (2, date.map(|d| d.isoweekdate().1 as i64)),
NumDaysFromSun => (1, date.map(|d| d.weekday().num_days_from_sunday() as i64)),
WeekdayFromMon => (1, date.map(|d| d.weekday().number_from_monday() as i64)),
Ordinal => (3, date.map(|d| d.ordinal() as i64)),
Hour => (2, time.map(|t| t.hour() as i64)),
Hour12 => (2, time.map(|t| t.hour12().1 as i64)),
Minute => (2, time.map(|t| t.minute() as i64)),
Second => (2, time.map(|t| (t.second() +
t.nanosecond() / 1_000_000_000) as i64)),
Nanosecond => (9, time.map(|t| (t.nanosecond() % 1_000_000_000) as i64)),
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(
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()
.num_days_from_sunday()))),
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() +
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) {
(Some(d), Some(t), None) =>
Some(d.and_time(*t).timestamp()),
@ -306,10 +393,13 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
Some((d.and_time(*t) - off).timestamp()),
(_, _, _) => None
}),
// for the future expansion
Internal(ref int) => match int._dummy {},
};
if let Some(v) = v {
if (spec == Year || spec == IsoYear) && !(0 <= v && v < 10000) {
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 => try!(write!(w, "{:+}", v)),
@ -333,15 +423,15 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
fn write_local_minus_utc(w: &mut fmt::Formatter, off: Duration,
fn write_local_minus_utc(w: &mut fmt::Formatter, off: FixedOffset,
allow_zulu: bool, use_colon: bool) -> fmt::Result {
let off = off.num_minutes();
let off = off.local_minus_utc();
if !allow_zulu || off != 0 {
let (sign, off) = if off < 0 {('-', -off)} else {('+', off)};
if use_colon {
write!(w, "{}{:02}:{:02}", sign, off / 60, off % 60)
write!(w, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
} else {
write!(w, "{}{:02}{:02}", sign, off / 60, off % 60)
write!(w, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
}
} else {
write!(w, "Z")
@ -391,6 +481,21 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
let nano = t.nanosecond() % 1_000_000_000;
write!(w, ".{:09}", nano)
}),
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) =>
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:03}", nano / 1_000_000)
}),
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) =>
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:06}", nano / 1_000)
}),
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) =>
time.map(|t| {
let nano = t.nanosecond() % 1_000_000_000;
write!(w, "{:09}", nano)
}),
TimezoneName =>
off.map(|&(ref name, _)| write!(w, "{}", *name)),
TimezoneOffsetColon =>
@ -401,6 +506,8 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
off.map(|&(_, off)| write_local_minus_utc(w, off, false, false)),
TimezoneOffsetZ =>
off.map(|&(_, off)| write_local_minus_utc(w, off, true, false)),
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`
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
let sec = t.second() + t.nanosecond() / 1_000_000_000;
@ -436,7 +543,7 @@ pub fn format<'a, I>(w: &mut fmt::Formatter, date: Option<&NaiveDate>, time: Opt
Ok(())
}
pub mod parsed;
mod parsed;
// due to the size of parsing routines, they are in separate modules.
mod scan;
@ -453,7 +560,7 @@ pub struct DelayedFormat<I> {
/// The time view, if any.
time: Option<NaiveTime>,
/// The name and local-to-UTC difference for the offset (timezone), if any.
off: Option<(String, Duration)>,
off: Option<(String, FixedOffset)>,
/// An iterator returning formatting items.
items: I,
}
@ -468,7 +575,7 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> DelayedFormat<I> {
pub fn new_with_offset<Off>(date: Option<NaiveDate>, time: Option<NaiveTime>,
offset: &Off, items: I) -> DelayedFormat<I>
where Off: Offset + fmt::Display {
let name_and_diff = (offset.to_string(), offset.local_minus_utc());
let name_and_diff = (offset.to_string(), offset.fix());
DelayedFormat { date: date, time: time, off: Some(name_and_diff), items: items }
}
}
@ -479,3 +586,40 @@ impl<'a, I: Iterator<Item=Item<'a>> + Clone> fmt::Display for DelayedFormat<I> {
}
}
// this implementation is here only because we need some private code from `scan`
/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
///
/// # Example
///
/// ~~~~
/// use chrono::Weekday;
///
/// assert_eq!("Sunday".parse::<Weekday>(), Ok(Weekday::Sun));
/// assert!("any day".parse::<Weekday>().is_err());
/// ~~~~
///
/// The parsing is case-insensitive.
///
/// ~~~~
/// # use chrono::Weekday;
/// assert_eq!("mON".parse::<Weekday>(), Ok(Weekday::Mon));
/// ~~~~
///
/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted.
///
/// ~~~~
/// # use chrono::Weekday;
/// assert!("thurs".parse::<Weekday>().is_err());
/// ~~~~
impl FromStr for Weekday {
type Err = ParseWeekdayError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(("", w)) = scan::short_or_long_weekday(s) {
Ok(w)
} else {
Err(ParseWeekdayError { _dummy: () })
}
}
}

View File

@ -1,5 +1,4 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// Portions copyright (c) 2015, John Nagle.
// See README.md and LICENSE.txt for details.
@ -10,7 +9,7 @@ use std::usize;
use Weekday;
use super::scan;
use super::{Parsed, ParseResult, Item};
use super::{Parsed, ParseResult, Item, InternalFixed, InternalInternal};
use super::{OUT_OF_RANGE, INVALID, TOO_SHORT, TOO_LONG, BAD_FORMAT};
fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> {
@ -86,7 +85,7 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
s = s.trim_left();
if let Ok((s_, weekday)) = scan::short_weekday(s) {
if !s_.starts_with(",") { return Err(INVALID); }
if !s_.starts_with(',') { return Err(INVALID); }
s = &s_[1..];
try!(parsed.set_weekday(weekday));
}
@ -94,7 +93,7 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
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 + try_consume!(scan::short_month0(s)) as i64));
try!(parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s)))));
s = try!(scan::space(s)); // mandatory
// distinguish two- and three-digit years from four-digit years
@ -113,16 +112,14 @@ fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
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 = s.trim_left();
if !s.is_empty() { // [ ":" *S 2DIGIT ]
s = try!(scan::char(s, b':')).trim_left();
try!(parsed.set_second(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))));
}
s = try!(scan::space(s)); // mandatory
if let Some(offset) = try_consume!(scan::timezone_offset_2822(s)) {
// only set the offset when it is definitely known (i.e. not `-0000`)
try!(parsed.set_offset(offset as i64));
try!(parsed.set_offset(i64::from(offset)));
}
Ok((s, ()))
@ -177,14 +174,14 @@ fn parse_rfc3339<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a st
try!(parsed.set_minute(try_consume!(scan::number(s, 2, 2))));
s = try!(scan::char(s, b':'));
try!(parsed.set_second(try_consume!(scan::number(s, 2, 2))));
if s.starts_with(".") {
if s.starts_with('.') {
let nanosecond = try_consume!(scan::nanosecond(&s[1..]));
try!(parsed.set_nanosecond(nanosecond));
}
let offset = try_consume!(scan::timezone_offset_zulu(s, |s| scan::char(s, b':')));
if offset <= -86400 || offset >= 86400 { return Err(OUT_OF_RANGE); }
try!(parsed.set_offset(offset as i64));
if offset <= -86_400 || offset >= 86_400 { return Err(OUT_OF_RANGE); }
try!(parsed.set_offset(i64::from(offset)));
Ok((s, ()))
}
@ -219,15 +216,21 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
s = &s[prefix.len()..];
}
Item::Space(_) => {
Item::OwnedLiteral(ref prefix) => {
if s.len() < prefix.len() { return Err(TOO_SHORT); }
if !s.starts_with(&prefix[..]) { return Err(INVALID); }
s = &s[prefix.len()..];
}
Item::Space(_) | Item::OwnedSpace(_) => {
s = s.trim_left();
}
Item::Numeric(spec, _pad) => {
use super::Numeric::*;
type Setter = fn(&mut Parsed, i64) -> ParseResult<()>;
let (width, signed, set): (usize, bool,
fn(&mut Parsed, i64) -> ParseResult<()>) = match spec {
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),
@ -248,14 +251,17 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
Second => (2, false, Parsed::set_second),
Nanosecond => (9, false, Parsed::set_nanosecond),
Timestamp => (usize::MAX, false, Parsed::set_timestamp),
// for the future expansion
Internal(ref int) => match int._dummy {},
};
s = s.trim_left();
let v = if signed {
if s.starts_with("-") {
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))
} else if s.starts_with("+") {
} else if s.starts_with('+') {
try_consume!(scan::number(&s[1..], 1, usize::MAX))
} else {
// if there is no explicit sign, we respect the original `width`
@ -273,12 +279,12 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
match spec {
ShortMonthName => {
let month0 = try_consume!(scan::short_month0(s));
try!(parsed.set_month(month0 as i64 + 1));
try!(parsed.set_month(i64::from(month0) + 1));
}
LongMonthName => {
let month0 = try_consume!(scan::short_or_long_month0(s));
try!(parsed.set_month(month0 as i64 + 1));
try!(parsed.set_month(i64::from(month0) + 1));
}
ShortWeekdayName => {
@ -302,25 +308,48 @@ pub fn parse<'a, I>(parsed: &mut Parsed, mut s: &str, items: I) -> ParseResult<(
s = &s[2..];
}
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9=> {
if s.starts_with(".") {
Nanosecond | Nanosecond3 | Nanosecond6 | Nanosecond9 => {
if s.starts_with('.') {
let nano = try_consume!(scan::nanosecond(&s[1..]));
try!(parsed.set_nanosecond(nano));
}
}
Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => {
if s.len() < 3 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 3));
try!(parsed.set_nanosecond(nano));
}
Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => {
if s.len() < 6 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 6));
try!(parsed.set_nanosecond(nano));
}
Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => {
if s.len() < 9 { return Err(TOO_SHORT); }
let nano = try_consume!(scan::nanosecond_fixed(s, 9));
try!(parsed.set_nanosecond(nano));
}
TimezoneName => return Err(BAD_FORMAT),
TimezoneOffsetColon | TimezoneOffset => {
let offset = try_consume!(scan::timezone_offset(s.trim_left(),
scan::colon_or_space));
try!(parsed.set_offset(offset as i64));
try!(parsed.set_offset(i64::from(offset)));
}
TimezoneOffsetColonZ | TimezoneOffsetZ => {
let offset = try_consume!(scan::timezone_offset_zulu(s.trim_left(),
scan::colon_or_space));
try!(parsed.set_offset(offset as i64));
try!(parsed.set_offset(i64::from(offset)));
}
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)));
}
RFC2822 => try_consume!(parse_rfc2822(parsed, s)),
@ -359,9 +388,11 @@ fn test_parse() {
($fmt:expr, $items:expr; $err:tt) => (
assert_eq!(parse_all($fmt, &$items), Err($err))
);
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (
assert_eq!(parse_all($fmt, &$items), Ok(Parsed { $($k: Some($v),)* ..Parsed::new() }))
);
($fmt:expr, $items:expr; $($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
let mut expected = Parsed::new();
$(expected.$k = Some($v);)*
assert_eq!(parse_all($fmt, &$items), Ok(expected))
});
}
// empty string
@ -521,6 +552,39 @@ fn test_parse() {
check!(". 4", [fix!(Nanosecond)]; INVALID);
check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming
// fixed: nanoseconds without the dot
check!("", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("0", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("4", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("42", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("421", [internal_fix!(Nanosecond3NoDot)]; nanosecond: 421_000_000);
check!("42143", [internal_fix!(Nanosecond3NoDot), num!(Second)]; nanosecond: 421_000_000, second: 43);
check!("42195", [internal_fix!(Nanosecond3NoDot)]; TOO_LONG);
check!("4x", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!(" 4", [internal_fix!(Nanosecond3NoDot)]; INVALID);
check!(".421", [internal_fix!(Nanosecond3NoDot)]; INVALID);
check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("42195", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("421950", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 421_950_000);
check!("000003", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 3000);
check!("000000", [internal_fix!(Nanosecond6NoDot)]; nanosecond: 0);
check!("4x", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!(" 4", [internal_fix!(Nanosecond6NoDot)]; INVALID);
check!(".42100", [internal_fix!(Nanosecond6NoDot)]; INVALID);
check!("", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!("42195", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!("421950803", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803);
check!("000000003", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 3);
check!("42195080354", [internal_fix!(Nanosecond9NoDot), num!(Second)]; nanosecond: 421_950_803, second: 54); // don't skip digits that come after the 9
check!("421950803547", [internal_fix!(Nanosecond9NoDot)]; TOO_LONG);
check!("000000000", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 0);
check!("00000000x", [internal_fix!(Nanosecond9NoDot)]; INVALID);
check!(" 4", [internal_fix!(Nanosecond9NoDot)]; INVALID);
check!(".42100000", [internal_fix!(Nanosecond9NoDot)]; INVALID);
// fixed: timezone offsets
check!("+00:00", [fix!(TimezoneOffset)]; offset: 0);
check!("-00:00", [fix!(TimezoneOffset)]; offset: 0);
@ -559,6 +623,10 @@ fn test_parse() {
check!("zulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 0);
check!("+1234ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
check!("+12:34ulu", [fix!(TimezoneOffsetZ), lit!("ulu")]; offset: 754 * 60);
check!("Z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
check!("z", [internal_fix!(TimezoneOffsetPermissive)]; offset: 0);
check!("+12:00", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
check!("+12", [internal_fix!(TimezoneOffsetPermissive)]; offset: 12 * 60 * 60);
check!("???", [fix!(TimezoneName)]; BAD_FORMAT); // not allowed
// some practical examples
@ -567,6 +635,11 @@ fn test_parse() {
num!(Hour), lit!(":"), num!(Minute), lit!(":"), num!(Second), fix!(TimezoneOffset)];
year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
minute: 37, second: 5, offset: 32400);
check!("20150204143705567",
[num!(Year), num!(Month), num!(Day),
num!(Hour), num!(Minute), num!(Second), internal_fix!(Nanosecond3NoDot)];
year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2,
minute: 37, second: 5, nanosecond: 567000000);
check!("Mon, 10 Jun 2013 09:32:37 GMT",
[fix!(ShortWeekdayName), lit!(","), sp!(" "), num!(Day), sp!(" "),
fix!(ShortMonthName), sp!(" "), num!(Year), sp!(" "), num!(Hour), lit!(":"),
@ -590,8 +663,8 @@ fn test_parse() {
#[cfg(test)]
#[test]
fn test_rfc2822() {
use datetime::DateTime;
use offset::fixed::FixedOffset;
use DateTime;
use offset::FixedOffset;
use super::*;
use super::NOT_ENOUGH;
@ -600,6 +673,7 @@ fn test_rfc2822() {
("Tue, 20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // normal case
("20 Jan 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // no day of week
("20 JAN 2015 17:35:20 -0800", Ok("Tue, 20 Jan 2015 17:35:20 -0800")), // upper case month
("Tue, 20 Jan 2015 17:35 -0800", Ok("Tue, 20 Jan 2015 17:35:00 -0800")), // no second
("11 Sep 2001 09:45:00 EST", Ok("Tue, 11 Sep 2001 09:45:00 -0500")),
("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month
("Tue, 20 Jan 2015", Err(TOO_SHORT)), // omitted fields
@ -642,40 +716,40 @@ fn test_rfc2822() {
#[cfg(test)]
#[test]
fn parse_rfc850() {
use ::{UTC, TimeZone};
use ::{Utc, TimeZone};
static RFC850_FMT: &'static str = "%A, %d-%b-%y %T GMT";
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
let dt = UTC.ymd(1994, 11, 6).and_hms(8, 49, 37);
let dt = Utc.ymd(1994, 11, 6).and_hms(8, 49, 37);
// Check that the format is what we expect
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
// Check that it parses correctly
assert_eq!(Ok(dt), UTC.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
// Check that the rest of the weekdays parse correctly (this test originally failed because
// Sunday parsed incorrectly).
let testdates = [
(UTC.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
(UTC.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 7).and_hms(8, 49, 37), "Monday, 07-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 8).and_hms(8, 49, 37), "Tuesday, 08-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 9).and_hms(8, 49, 37), "Wednesday, 09-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 10).and_hms(8, 49, 37), "Thursday, 10-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 11).and_hms(8, 49, 37), "Friday, 11-Nov-94 08:49:37 GMT"),
(Utc.ymd(1994, 11, 12).and_hms(8, 49, 37), "Saturday, 12-Nov-94 08:49:37 GMT"),
];
for val in &testdates {
assert_eq!(Ok(val.0), UTC.datetime_from_str(val.1, RFC850_FMT));
assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
}
}
#[cfg(test)]
#[test]
fn test_rfc3339() {
use datetime::DateTime;
use offset::fixed::FixedOffset;
use DateTime;
use offset::FixedOffset;
use super::*;
// Test data - (input, Ok(expected result after parse and format) or Err(error code))

View File

@ -1,22 +1,18 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! A collection of parsed date and time items.
//! They can be constructed incrementally while being checked for consistency.
use num::traits::ToPrimitive;
use num_traits::ToPrimitive;
use oldtime::Duration as OldDuration;
use {Datelike, Timelike};
use Weekday;
use div::div_rem;
use duration::Duration;
use offset::{TimeZone, Offset, LocalResult};
use offset::fixed::FixedOffset;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use datetime::DateTime;
use offset::{TimeZone, Offset, LocalResult, FixedOffset};
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use DateTime;
use super::{ParseResult, OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
/// Parsed parts of date and time. There are two classes of methods:
@ -45,13 +41,13 @@ pub struct Parsed {
/// Year modulo 100. Implies that the year is >= 1 BCE when set.
pub year_mod_100: Option<i32>,
/// Year in the [ISO week date](../../naive/date/index.html#week-date).
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date).
///
/// This can be negative unlike [`isoyear_div_100`](#structfield.isoyear_div_100) and
/// [`isoyear_mod_100`](#structfield.isoyear_mod_100) fields.
pub isoyear: Option<i32>,
/// Year in the [ISO week date](../../naive/date/index.html#week-date), divided by 100.
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), divided by 100.
/// Implies that the year is >= 1 BCE when set.
///
/// Due to the common usage, if this field is missing but
@ -59,7 +55,7 @@ pub struct Parsed {
/// it is inferred to 19 when `isoyear_mod_100 >= 70` and 20 otherwise.
pub isoyear_div_100: Option<i32>,
/// Year in the [ISO week date](../../naive/date/index.html#week-date), modulo 100.
/// Year in the [ISO week date](../naive/struct.NaiveDate.html#week-date), modulo 100.
/// Implies that the year is >= 1 BCE when set.
pub isoyear_mod_100: Option<i32>,
@ -74,7 +70,7 @@ pub struct Parsed {
/// (0--53, 1--53 or 1--52 depending on the year).
pub week_from_mon: Option<u32>,
/// [ISO week number](../../naive/date/index.html#week-date)
/// [ISO week number](../naive/struct.NaiveDate.html#week-date)
/// (1--52 or 1--53 depending on the year).
pub isoweek: Option<u32>,
@ -109,6 +105,9 @@ pub struct Parsed {
/// Offset from the local time to UTC, in seconds.
pub offset: Option<i32>,
/// A dummy field to make this type not fully destructible (required for API stability).
_dummy: (),
}
/// Checks if `old` is either empty or has the same value to `new` (i.e. "consistent"),
@ -122,14 +121,23 @@ fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<(
}
}
impl Default for Parsed {
fn default() -> Parsed {
Parsed {
year: None, year_div_100: None, year_mod_100: None, isoyear: None,
isoyear_div_100: None, isoyear_mod_100: None, month: None,
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
second: None, nanosecond: None, timestamp: None, offset: None,
_dummy: (),
}
}
}
impl Parsed {
/// Returns the initial value of parsed parts.
pub fn new() -> Parsed {
Parsed { year: None, year_div_100: None, year_mod_100: None, isoyear: None,
isoyear_div_100: None, isoyear_mod_100: None, month: None,
week_from_sun: None, week_from_mon: None, isoweek: None, weekday: None,
ordinal: None, day: None, hour_div_12: None, hour_mod_12: None, minute: None,
second: None, nanosecond: None, timestamp: None, offset: None }
Parsed::default()
}
/// Tries to set the [`year`](#structfield.year) field from given value.
@ -324,7 +332,10 @@ impl Parsed {
// verify the ISO week date.
let verify_isoweekdate = |date: NaiveDate| {
let (isoyear, isoweek, weekday) = date.isoweekdate();
let week = date.iso_week();
let isoyear = week.year();
let isoweek = week.week();
let weekday = date.weekday();
let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 {
let (q, r) = div_rem(isoyear, 100);
(Some(q), Some(r))
@ -383,7 +394,7 @@ impl Parsed {
if week_from_sun > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
let ndays = firstweek + (week_from_sun as i32 - 1) * 7 +
weekday.num_days_from_sunday() as i32;
let date = try!(newyear.checked_add(Duration::days(ndays as i64))
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE));
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
@ -408,7 +419,7 @@ impl Parsed {
if week_from_mon > 53 { return Err(OUT_OF_RANGE); } // can it overflow?
let ndays = firstweek + (week_from_mon as i32 - 1) * 7 +
weekday.num_days_from_monday() as i32;
let date = try!(newyear.checked_add(Duration::days(ndays as i64))
let date = try!(newyear.checked_add_signed(OldDuration::days(i64::from(ndays)))
.ok_or(OUT_OF_RANGE));
if date.year() != year { return Err(OUT_OF_RANGE); } // early exit for correct error
@ -491,7 +502,7 @@ impl Parsed {
// verify the timestamp field if any
// the following is safe, `timestamp` is very limited in range
let timestamp = datetime.timestamp() - offset as i64;
let timestamp = datetime.timestamp() - i64::from(offset);
if let Some(given_timestamp) = self.timestamp {
// if `datetime` represents a leap second, it might be off by one second.
if given_timestamp != timestamp &&
@ -514,7 +525,7 @@ impl Parsed {
}
// reconstruct date and time fields from timestamp
let ts = try!(timestamp.checked_add(offset as i64).ok_or(OUT_OF_RANGE));
let ts = try!(timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE));
let datetime = NaiveDateTime::from_timestamp_opt(ts, 0);
let mut datetime = try!(datetime.ok_or(OUT_OF_RANGE));
@ -527,19 +538,18 @@ impl Parsed {
// it's okay, just do not try to overwrite the existing field.
59 => {}
// `datetime` is known to be off by one second.
0 => { datetime = datetime - Duration::seconds(1); }
0 => { datetime -= OldDuration::seconds(1); }
// otherwise it is impossible.
_ => return Err(IMPOSSIBLE)
}
// ...and we have the correct candidates for other fields.
} else {
try!(parsed.set_second(datetime.second() as i64));
try!(parsed.set_second(i64::from(datetime.second())));
}
try!(parsed.set_year (datetime.year() as i64));
try!(parsed.set_ordinal(datetime.ordinal() as i64)); // more efficient than ymd
try!(parsed.set_hour (datetime.hour() as i64));
try!(parsed.set_minute (datetime.minute() as i64));
try!(parsed.set_nanosecond(0)); // no nanosecond precision in timestamp
try!(parsed.set_year (i64::from(datetime.year())));
try!(parsed.set_ordinal(i64::from(datetime.ordinal()))); // more efficient than ymd
try!(parsed.set_hour (i64::from(datetime.hour())));
try!(parsed.set_minute (i64::from(datetime.minute())));
// validate other fields (e.g. week) and return
let date = try!(parsed.to_naive_date());
@ -555,7 +565,7 @@ impl Parsed {
/// Returns a parsed fixed time zone offset out of given fields.
pub fn to_fixed_offset(&self) -> ParseResult<FixedOffset> {
self.offset.and_then(|offset| FixedOffset::east_opt(offset)).ok_or(OUT_OF_RANGE)
self.offset.and_then(FixedOffset::east_opt).ok_or(OUT_OF_RANGE)
}
/// Returns a parsed timezone-aware date and time out of given fields.
@ -593,20 +603,13 @@ impl Parsed {
let nanosecond = self.nanosecond.unwrap_or(0);
let dt = NaiveDateTime::from_timestamp_opt(timestamp, nanosecond);
let dt = try!(dt.ok_or(OUT_OF_RANGE));
// we cannot handle offsets larger than i32 at all. give up if so.
// we can instead make `to_naive_datetime_with_offset` to accept i64, but this makes
// the algorithm too complex and tons of edge cases. i32 should be enough for all.
let offset = tz.offset_from_utc_datetime(&dt).local_minus_utc().num_seconds();
guessed_offset = try!(offset.to_i32().ok_or(OUT_OF_RANGE));
guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc();
}
// checks if the given `DateTime` has a consistent `Offset` with given `self.offset`.
let check_offset = |dt: &DateTime<Tz>| {
if let Some(offset) = self.offset {
let delta = dt.offset().local_minus_utc().num_seconds();
// if `delta` does not fit in `i32`, it cannot equal to `self.offset` anyway.
delta.to_i32() == Some(offset)
dt.offset().fix().local_minus_utc() == offset
} else {
true
}
@ -637,11 +640,8 @@ mod tests {
use super::super::{OUT_OF_RANGE, IMPOSSIBLE, NOT_ENOUGH};
use Datelike;
use Weekday::*;
use naive::date::{self, NaiveDate};
use naive::time::NaiveTime;
use offset::TimeZone;
use offset::utc::UTC;
use offset::fixed::FixedOffset;
use naive::{MIN_DATE, MAX_DATE, NaiveDate, NaiveTime};
use offset::{TimeZone, Utc, FixedOffset};
#[test]
fn test_parsed_set_fields() {
@ -760,7 +760,7 @@ mod tests {
ymd(0, 1, 1));
assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1),
Err(OUT_OF_RANGE));
let max_year = date::MAX.year();
let max_year = MAX_DATE.year();
assert_eq!(parse!(year_div_100: max_year / 100,
year_mod_100: max_year % 100, month: 1, day: 1),
ymd(max_year, 1, 1));
@ -932,7 +932,7 @@ mod tests {
ymdhmsn(2012,2,3, 5,6,7,890_123_456));
assert_eq!(parse!(timestamp: 0), ymdhms(1970,1,1, 0,0,0));
assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970,1,1, 0,0,1));
assert_eq!(parse!(timestamp: 1, nanosecond: 1), Err(IMPOSSIBLE));
assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970,1,1, 0,0,1, 1));
assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014,12,31, 4,26,40));
assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833,11,24, 17,31,44));
@ -958,15 +958,18 @@ mod tests {
ymdhmsn(2014,12,31, 4,26,40,12_345_678));
// more timestamps
let max_days_from_year_1970 = date::MAX - NaiveDate::from_ymd(1970,1,1);
let year_0_from_year_1970 = NaiveDate::from_ymd(0,1,1) - NaiveDate::from_ymd(1970,1,1);
let min_days_from_year_1970 = date::MIN - NaiveDate::from_ymd(1970,1,1);
let max_days_from_year_1970 =
MAX_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1));
let year_0_from_year_1970 =
NaiveDate::from_ymd(0,1,1).signed_duration_since(NaiveDate::from_ymd(1970,1,1));
let min_days_from_year_1970 =
MIN_DATE.signed_duration_since(NaiveDate::from_ymd(1970,1,1));
assert_eq!(parse!(timestamp: min_days_from_year_1970.num_seconds()),
ymdhms(date::MIN.year(),1,1, 0,0,0));
ymdhms(MIN_DATE.year(),1,1, 0,0,0));
assert_eq!(parse!(timestamp: year_0_from_year_1970.num_seconds()),
ymdhms(0,1,1, 0,0,0));
assert_eq!(parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399),
ymdhms(date::MAX.year(),12,31, 23,59,59));
ymdhms(MAX_DATE.year(),12,31, 23,59,59));
// leap seconds #1: partial fields
assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE));
@ -1038,7 +1041,7 @@ mod tests {
minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876),
ymdhmsn(2014,12,31, 1,42,4,12_345_678, -9876));
assert_eq!(parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 86400),
minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400),
Err(OUT_OF_RANGE)); // `FixedOffset` does not support such huge offset
}
@ -1051,11 +1054,11 @@ mod tests {
}
// single result from ymdhms
assert_eq!(parse!(UTC;
assert_eq!(parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
assert_eq!(parse!(UTC;
Ok(Utc.ymd(2014, 12, 31).and_hms_nano(4, 26, 40, 12_345_678)));
assert_eq!(parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
Err(IMPOSSIBLE));
@ -1070,9 +1073,9 @@ mod tests {
.and_hms_nano(13, 26, 40, 12_345_678)));
// single result from timestamp
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 0),
Ok(UTC.ymd(2014, 12, 31).and_hms(4, 26, 40)));
assert_eq!(parse!(UTC; timestamp: 1_420_000_000, offset: 32400),
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 0),
Ok(Utc.ymd(2014, 12, 31).and_hms(4, 26, 40)));
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400),
Err(IMPOSSIBLE));
assert_eq!(parse!(FixedOffset::east(32400); timestamp: 1_420_000_000, offset: 0),
Err(IMPOSSIBLE));

View File

@ -1,5 +1,4 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
@ -37,7 +36,8 @@ pub fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> {
if window.len() > max { window = &window[..max]; }
// scan digits
let upto = window.iter().position(|&c| c < b'0' || b'9' < c).unwrap_or(window.len());
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});
}
@ -66,6 +66,20 @@ pub fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
Ok((s, v))
}
/// Tries to consume a fixed number of digits as a fractional second.
/// 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));
// 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));
Ok((s, v))
}
/// Tries to parse the month index (0 through 11) with the first three ASCII letters.
pub fn short_month0(s: &str) -> ParseResult<(&str, u8)> {
if s.len() < 3 { return Err(TOO_SHORT); }
@ -171,8 +185,15 @@ pub fn colon_or_space(s: &str) -> ParseResult<&str> {
///
/// The additional `colon` may be used to parse a mandatory or optional `:`
/// between hours and minutes, and should return either a new suffix or `Err` when parsing fails.
pub fn timezone_offset<F>(mut s: &str, mut colon: F) -> ParseResult<(&str, i32)>
pub fn timezone_offset<F>(s: &str, consume_colon: F) -> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str> {
timezone_offset_internal(s, consume_colon, false)
}
fn timezone_offset_internal<F>(mut s: &str, mut consume_colon: F, allow_missing_minutes: bool)
-> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str>
{
fn digits(s: &str) -> ParseResult<(u8, u8)> {
let b = s.as_bytes();
if b.len() < 2 {
@ -191,68 +212,89 @@ pub fn timezone_offset<F>(mut s: &str, mut colon: F) -> ParseResult<(&str, i32)>
// hours (00--99)
let hours = match try!(digits(s)) {
(h1 @ b'0'...b'9', h2 @ b'0'...b'9') => ((h1 - b'0') * 10 + (h2 - b'0')) as i32,
(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!(colon(s));
s = try!(consume_colon(s));
// minutes (00--59)
let minutes = match try!(digits(s)) {
(m1 @ b'0'...b'5', m2 @ b'0'...b'9') => ((m1 - b'0') * 10 + (m2 - b'0')) as i32,
(b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE),
_ => return Err(INVALID),
// if the next two items are digits then we have to add minutes
let minutes = if let Ok(ds) = digits(s) {
match ds {
(m1 @ b'0'...b'5', m2 @ b'0'...b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')),
(b'6'...b'9', b'0'...b'9') => return Err(OUT_OF_RANGE),
_ => return Err(INVALID),
}
} else if allow_missing_minutes {
0
} else {
return Err(TOO_SHORT);
};
s = match s.len() {
len if len >= 2 => &s[2..],
len if len == 0 => s,
_ => return Err(TOO_SHORT),
};
s = &s[2..];
let seconds = hours * 3600 + minutes * 60;
Ok((s, if negative {-seconds} else {seconds}))
}
/// Same to `timezone_offset` but also allows for `z`/`Z` which is same to `+00:00`.
pub fn timezone_offset_zulu<F>(s: &str, colon: F) -> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str> {
pub fn timezone_offset_zulu<F>(s: &str, colon: F)
-> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str>
{
match s.as_bytes().first() {
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
_ => timezone_offset(s, colon),
}
}
/// Same to `timezone_offset` but also allows for `z`/`Z` which is same to
/// `+00:00`, and allows missing minutes entirely.
pub fn timezone_offset_permissive<F>(s: &str, colon: F)
-> ParseResult<(&str, i32)>
where F: FnMut(&str) -> ParseResult<&str>
{
match s.as_bytes().first() {
Some(&b'z') | Some(&b'Z') => Ok((&s[1..], 0)),
_ => timezone_offset_internal(s, colon, true),
}
}
/// Same to `timezone_offset` but also allows for RFC 2822 legacy timezones.
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
pub fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
// tries to parse legacy time zone names
let upto = s.as_bytes().iter().position(|&c| match c { b'a'...b'z' | b'A'...b'Z' => false,
_ => true }).unwrap_or(s.len());
_ => true })
.unwrap_or_else(|| s.len());
if upto > 0 {
let name = &s[..upto];
let s = &s[upto..];
let offset_hours = |o| Ok((s, Some(o * 3600)));
if equals(name, "gmt") || equals(name, "ut") {
Ok((s, Some(0)))
} else if equals(name, "est") {
Ok((s, Some(-5 * 3600)))
offset_hours(0)
} else if equals(name, "edt") {
Ok((s, Some(-4 * 3600)))
} else if equals(name, "cst") {
Ok((s, Some(-6 * 3600)))
} else if equals(name, "cdt") {
Ok((s, Some(-5 * 3600)))
} else if equals(name, "mst") {
Ok((s, Some(-7 * 3600)))
} else if equals(name, "mdt") {
Ok((s, Some(-6 * 3600)))
offset_hours(-4)
} else if equals(name, "est") || equals(name, "cdt") {
offset_hours(-5)
} else if equals(name, "cst") || equals(name, "mdt") {
offset_hours(-6)
} else if equals(name, "mst") || equals(name, "pdt") {
offset_hours(-7)
} else if equals(name, "pst") {
Ok((s, Some(-8 * 3600)))
} else if equals(name, "pdt") {
Ok((s, Some(-7 * 3600)))
offset_hours(-8)
} else {
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
if offset == 0 && s.starts_with('-') { // -0000 is not same to +0000
Ok((s_, None))
} else {
Ok((s_, Some(offset)))

View File

@ -1,5 +1,4 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
@ -9,77 +8,81 @@
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]
| |
`%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`.
| |
`%d` | `08` | Day number (01--31), zero-padded to 2 digits.
`%e` | ` 8` | Same to `%d` but space-padded. Same to `%_d`.
| |
`%a` | `Sun` | Abbreviated weekday name. Always 3 letters.
`%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing.
`%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6.
`%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601)
| |
`%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [3]
`%W` | `27` | Same to `%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]
| |
`%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`.
| |
| | **TIME SPECIFIERS:**
`%H` | `00` | Hour number (00--23), zero-padded to 2 digits.
`%k` | ` 0` | Same to `%H` but space-padded. Same to `%_H`.
`%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits.
`%l` | `12` | Same to `%I` but space-padded. Same to `%_I`.
| |
`%P` | `am` | `am` or `pm` in 12-hour clocks.
`%p` | `AM` | `AM` or `PM` in 12-hour clocks.
| |
`%M` | `34` | Minute number (00--59), zero-padded to 2 digits.
`%S` | `60` | Second number (00--60), zero-padded to 2 digits. [5]
`%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [8]
`%.f` | `.026490` | Similar to `.%f` but left-aligned. [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]
| |
`%R` | `00:34` | Hour-minute format. Same to `%H:%M`.
`%T` | `00:34:60` | Hour-minute-second format. Same to `%H:%M:%S`.
`%X` | `00:34:60` | Same to `%T`.
`%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same to `%I:%M:%S %p`.
| |
| | **TIME ZONE SPECIFIERS:**
`%Z` | `ACST` | *Formatting only:* Local time zone name.
`%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`).
`%:z` | `+09:30` | Same to `%z` but with a colon.
| |
| | **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]
| |
`%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [7]
| |
| | **SPECIAL SPECIFIERS:**
`%t` | | Literal tab (`\t`).
`%n` | | Literal newline (`\n`).
`%%` | | Literal percent sign.
| 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] |
| | | |
| `%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`. |
| | | |
| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. |
| `%e` | ` 8` | Same to `%d` but space-padded. Same to `%_d`. |
| | | |
| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. |
| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. |
| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. |
| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) |
| | | |
| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [3] |
| `%W` | `27` | Same to `%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] |
| | | |
| `%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`. |
| | | |
| | | **TIME SPECIFIERS:** |
| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. |
| `%k` | ` 0` | Same to `%H` but space-padded. Same to `%_H`. |
| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. |
| `%l` | `12` | Same to `%I` but space-padded. Same to `%_I`. |
| | | |
| `%P` | `am` | `am` or `pm` in 12-hour clocks. |
| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. |
| | | |
| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. |
| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [5] |
| `%f` | `026490000` | The fractional seconds (in nanoseconds) since last whole second. [8] |
| `%.f` | `.026490`| Similar to `.%f` but left-aligned. These all consume the leading dot. [8] |
| `%.3f`| `.026` | Similar to `.%f` but left-aligned but fixed to a length of 3. [8] |
| `%.6f`| `.026490` | Similar to `.%f` but left-aligned but fixed to a length of 6. [8] |
| `%.9f`| `.026490000` | Similar to `.%f` but left-aligned but fixed to a length of 9. [8] |
| `%3f` | `026` | Similar to `%.3f` but without the leading dot. [8] |
| `%6f` | `026490` | Similar to `%.6f` but without the leading dot. [8] |
| `%9f` | `026490000` | Similar to `%.9f` but without the leading dot. [8] |
| | | |
| `%R` | `00:34` | Hour-minute format. Same to `%H:%M`. |
| `%T` | `00:34:60` | Hour-minute-second format. Same to `%H:%M:%S`. |
| `%X` | `00:34:60` | Same to `%T`. |
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same to `%I:%M:%S %p`. |
| | | |
| | | **TIME ZONE SPECIFIERS:** |
| `%Z` | `ACST` | *Formatting only:* Local time zone name. |
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
| `%:z` | `+09:30` | Same to `%z` but with a colon. |
| `%#z` | `+09` | *Parsing only:* Same to `%z` but allows minutes to be missing or present. |
| | | |
| | | **DATE & TIME SPECIFIERS:** |
|`%c`|`Sun Jul 8 00:34:60 2001`|`ctime` date & time format. Same to `%a %b %e %T %Y` sans `\n`.|
| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [6] |
| | | |
| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [7] |
| | | |
| | | **SPECIAL SPECIFIERS:** |
| `%t` | | Literal tab (`\t`). |
| `%n` | | Literal newline (`\n`). |
| `%%` | | Literal percent sign. |
It is possible to override the default padding behavior of numeric specifiers `%?`.
This is not allowed for other specifiers and will result in the `BAD_FORMAT` error.
@ -123,7 +126,7 @@ Notes:
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`:
8. `%f`, `%.f`, `%.3f`, `%.6f`, `%.9f`, `%3f`, `%6f`, `%9f`:
The default `%f` is right-aligned and always zero-padded to 9 digits
for the compatibility with glibc and others,
@ -145,12 +148,18 @@ Notes:
Note that they can read nothing if the fractional part is zero or
the next character is not `.` however will print with the specified length.
The 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`),
and parsing `07`, `070000` etc. will yield the same.
Note that they can read nothing if the fractional part is zero.
*/
use super::{Item, Numeric, Fixed, Pad};
use super::{Item, Numeric, Fixed, InternalFixed, InternalInternal, Pad};
/// Parsing iterator for `strftime`-like format strings.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct StrftimeItems<'a> {
/// Remaining portion of the string.
remainder: &'a str,
@ -168,20 +177,22 @@ impl<'a> StrftimeItems<'a> {
}
}
const HAVE_ALTERNATES: &'static str = "z";
impl<'a> Iterator for StrftimeItems<'a> {
type Item = Item<'a>;
fn next(&mut self) -> Option<Item<'a>> {
// we have some reconstructed items to return
if !self.recons.is_empty() {
let item = self.recons[0];
let item = self.recons[0].clone();
self.recons = &self.recons[1..];
return Some(item);
}
match self.remainder.chars().next() {
// we are done
None => return None,
None => None,
// the next item is a specifier
Some('%') => {
@ -206,7 +217,11 @@ impl<'a> Iterator for StrftimeItems<'a> {
'_' => Some(Pad::Space),
_ => None,
};
let spec = if pad_override.is_some() { next!() } else { spec };
let is_alternate = spec == '#';
let spec = if pad_override.is_some() || is_alternate { next!() } else { spec };
if is_alternate && !HAVE_ALTERNATES.contains(spec) {
return Some(Item::Error);
}
macro_rules! recons {
[$head:expr, $($tail:expr),+] => ({
@ -238,7 +253,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
'Y' => num0!(Year),
'Z' => fix!(TimezoneName),
'a' => fix!(ShortWeekdayName),
'b' => fix!(ShortMonthName),
'b' | 'h' => fix!(ShortMonthName),
'c' => recons![fix!(ShortWeekdayName), sp!(" "), fix!(ShortMonthName),
sp!(" "), nums!(Day), sp!(" "), num0!(Hour), lit!(":"),
num0!(Minute), lit!(":"), num0!(Second), sp!(" "), num0!(Year)],
@ -246,7 +261,6 @@ impl<'a> Iterator for StrftimeItems<'a> {
'e' => nums!(Day),
'f' => num0!(Nanosecond),
'g' => num0!(IsoYearMod100),
'h' => fix!(ShortMonthName),
'j' => num0!(Ordinal),
'k' => nums!(Hour),
'l' => nums!(Hour12),
@ -264,7 +278,11 @@ impl<'a> Iterator for StrftimeItems<'a> {
'x' => recons![num0!(Month), lit!("/"), num0!(Day), lit!("/"),
num0!(YearMod100)],
'y' => num0!(YearMod100),
'z' => fix!(TimezoneOffset),
'z' => if is_alternate {
internal_fix!(TimezoneOffsetPermissive)
} else {
fix!(TimezoneOffset)
},
'+' => fix!(RFC3339),
':' => match next!() {
'z' => fix!(TimezoneOffsetColon),
@ -286,6 +304,18 @@ impl<'a> Iterator for StrftimeItems<'a> {
'f' => fix!(Nanosecond),
_ => Item::Error,
},
'3' => match next!() {
'f' => internal_fix!(Nanosecond3NoDot),
_ => Item::Error,
},
'6' => match next!() {
'f' => internal_fix!(Nanosecond6NoDot),
_ => Item::Error,
},
'9' => match next!() {
'f' => internal_fix!(Nanosecond9NoDot),
_ => Item::Error,
},
'%' => lit!("%"),
_ => Item::Error, // no such specifier
};
@ -293,8 +323,8 @@ impl<'a> Iterator for StrftimeItems<'a> {
// adjust `item` if we have any padding modifier
if let Some(new_pad) = pad_override {
match item {
Item::Numeric(kind, _pad) if self.recons.is_empty() =>
Some(Item::Numeric(kind, new_pad)),
Item::Numeric(ref kind, _pad) if self.recons.is_empty() =>
Some(Item::Numeric(kind.clone(), new_pad)),
_ => Some(Item::Error), // no reconstructed or non-numeric item allowed
}
} else {
@ -306,7 +336,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
Some(c) if c.is_whitespace() => {
// `%` is not a whitespace, so `c != '%'` is redundant
let nextspec = self.remainder.find(|c: char| !c.is_whitespace())
.unwrap_or(self.remainder.len());
.unwrap_or_else(|| self.remainder.len());
assert!(nextspec > 0);
let item = sp!(&self.remainder[..nextspec]);
self.remainder = &self.remainder[nextspec..];
@ -316,7 +346,7 @@ impl<'a> Iterator for StrftimeItems<'a> {
// the next item is literal
_ => {
let nextspec = self.remainder.find(|c: char| c.is_whitespace() || c == '%')
.unwrap_or(self.remainder.len());
.unwrap_or_else(|| self.remainder.len());
assert!(nextspec > 0);
let item = lit!(&self.remainder[..nextspec]);
self.remainder = &self.remainder[nextspec..];
@ -370,14 +400,17 @@ fn test_strftime_items() {
assert_eq!(parse_and_collect("%-e"), [num!(Day)]);
assert_eq!(parse_and_collect("%0e"), [num0!(Day)]);
assert_eq!(parse_and_collect("%_e"), [nums!(Day)]);
assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]);
assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]);
assert_eq!(parse_and_collect("%#m"), [Item::Error]);
}
#[cfg(test)]
#[test]
fn test_strftime_docs() {
use {FixedOffset, TimeZone};
use {FixedOffset, TimeZone, Timelike};
let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_000);
let dt = FixedOffset::east(34200).ymd(2001, 7, 8).and_hms_nano(0, 34, 59, 1_026_490_708);
// date specifiers
assert_eq!(dt.format("%Y").to_string(), "2001");
@ -416,11 +449,16 @@ fn test_strftime_docs() {
assert_eq!(dt.format("%p").to_string(), "AM");
assert_eq!(dt.format("%M").to_string(), "34");
assert_eq!(dt.format("%S").to_string(), "60");
assert_eq!(dt.format("%f").to_string(), "026490000");
assert_eq!(dt.format("%.f").to_string(), ".026490");
assert_eq!(dt.format("%f").to_string(), "026490708");
assert_eq!(dt.format("%.f").to_string(), ".026490708");
assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(),
".026490");
assert_eq!(dt.format("%.3f").to_string(), ".026");
assert_eq!(dt.format("%.6f").to_string(), ".026490");
assert_eq!(dt.format("%.9f").to_string(), ".026490000");
assert_eq!(dt.format("%.9f").to_string(), ".026490708");
assert_eq!(dt.format("%3f").to_string(), "026");
assert_eq!(dt.format("%6f").to_string(), "026490");
assert_eq!(dt.format("%9f").to_string(), "026490708");
assert_eq!(dt.format("%R").to_string(), "00:34");
assert_eq!(dt.format("%T").to_string(), "00:34:60");
assert_eq!(dt.format("%X").to_string(), "00:34:60");
@ -433,7 +471,9 @@ fn test_strftime_docs() {
// date & time specifiers
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490+09:30");
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30");
assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(),
"2001-07-08T00:34:60.026490+09:30");
assert_eq!(dt.format("%s").to_string(), "994518299");
// special specifiers

View File

@ -1,10 +1,8 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! # Chrono 0.2.25
//! # Chrono: Date and Time for Rust
//!
//! Date and time handling for Rust. (also known as `rust-chrono`)
//! It aims to be a feature-complete superset of
//! the [time](https://github.com/rust-lang-deprecated/time) library.
//! In particular,
@ -14,29 +12,31 @@
//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
//!
//! There were several previous attempts to bring a good date and time library to Rust,
//! which Chrono builts upon and should acknowledge:
//! which Chrono builds upon and should acknowledge:
//!
//! * [Initial research on
//! the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
//! * Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
//! * Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
//!
//! Any significant changes to Chrono are documented in
//! the [`CHANGELOG.md`](https://github.com/chronotope/chrono/blob/master/CHANGELOG.md) file.
//!
//! ## Usage
//!
//! Put this in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! chrono = "0.2"
//! chrono = "0.4"
//! ```
//!
//! Or, if you want [Serde](https://github.com/serde-rs/serde) or
//! [rustc-serialize](https://github.com/rust-lang-nursery/rustc-serialize) support,
//! include the features like this:
//! Or, if you want [Serde](https://github.com/serde-rs/serde) include the
//! feature like this:
//!
//! ```toml
//! [dependencies]
//! chrono = { version = "0.2", features = ["serde", "rustc-serialize"] }
//! chrono = { version = "0.4", features = ["serde"] }
//! ```
//!
//! Then put this in your crate root:
@ -45,20 +45,39 @@
//! extern crate chrono;
//! ```
//!
//! Avoid using `use chrono::*;` as Chrono exports several modules other than types.
//! If you prefer the glob imports, use the following instead:
//!
//! ```rust
//! use chrono::prelude::*;
//! ```
//!
//! ## Overview
//!
//! ### Duration
//!
//! [**`Duration`**](./struct.Duration.html)
//! represents the magnitude of a time span. `Duration` used to be provided by Chrono.
//! It has been moved to the `time` crate as the
//! [`time::Duration`](https://doc.rust-lang.org/time/time/struct.Duration.html) type, but is
//! still re-exported from Chrono.
//! Chrono currently uses
//! the [`time::Duration`](https://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,
//! the reference will refer this type as `OldDuration`.
//! Note that this is an "accurate" duration represented as seconds and
//! nanoseconds and does not represent "nominal" components such as days or
//! months.
//!
//! Chrono does not yet natively support
//! the standard [`Duration`](https://docs.rs/time/0.1.40/time/struct.Duration.html) type,
//! but it will be supported in the future.
//! Meanwhile you can convert between two types with
//! [`Duration::from_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.from_std)
//! and
//! [`Duration::to_std`](https://docs.rs/time/0.1.40/time/struct.Duration.html#method.to_std)
//! methods.
//!
//! ### Date and Time
//!
//! Chrono provides a
//! [**`DateTime`**](./datetime/struct.DateTime.html)
//! [**`DateTime`**](./struct.DateTime.html)
//! type to represent a date and a time in a timezone.
//!
//! For more abstract moment-in-time tracking such as internal timekeeping
@ -73,11 +92,11 @@
//! which defines how the local date is converted to and back from the UTC date.
//! There are three well-known `TimeZone` implementations:
//!
//! * [**`UTC`**](./offset/utc/struct.UTC.html) specifies the UTC time zone. It is most efficient.
//! * [**`Utc`**](./offset/struct.Utc.html) specifies the UTC time zone. It is most efficient.
//!
//! * [**`Local`**](./offset/local/struct.Local.html) specifies the system local time zone.
//! * [**`Local`**](./offset/struct.Local.html) specifies the system local time zone.
//!
//! * [**`FixedOffset`**](./offset/fixed/struct.FixedOffset.html) specifies
//! * [**`FixedOffset`**](./offset/struct.FixedOffset.html) specifies
//! an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30.
//! This often results from the parsed textual date and time.
//! Since it stores the most information and does not depend on the system environment,
@ -85,43 +104,44 @@
//!
//! `DateTime`s with different `TimeZone` types are distinct and do not mix,
//! but can be converted to each other using
//! the [`DateTime::with_timezone`](./datetime/struct.DateTime.html#method.with_timezone) method.
//! the [`DateTime::with_timezone`](./struct.DateTime.html#method.with_timezone) method.
//!
//! You can get the current date and time in the UTC time zone
//! ([`UTC::now()`](./offset/utc/struct.UTC.html#method.now))
//! ([`Utc::now()`](./offset/struct.Utc.html#method.now))
//! or in the local time zone
//! ([`Local::now()`](./offset/local/struct.Local.html#method.now)).
//! ([`Local::now()`](./offset/struct.Local.html#method.now)).
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//!
//! let utc: DateTime<UTC> = UTC::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
//! let utc: DateTime<Utc> = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z`
//! let local: DateTime<Local> = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00`
//! # let _ = utc; let _ = local;
//! ~~~~
//! ```
//!
//! Alternatively, you can create your own date and time.
//! This is a bit verbose due to Rust's lack of function and method overloading,
//! but in turn we get a rich combination of initialization methods.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//! use chrono::offset::LocalResult;
//!
//! let dt = UTC.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
//! let dt = Utc.ymd(2014, 7, 8).and_hms(9, 10, 11); // `2014-07-08T09:10:11Z`
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
//! assert_eq!(dt, UTC.yo(2014, 189).and_hms(9, 10, 11));
//! assert_eq!(dt, Utc.yo(2014, 189).and_hms(9, 10, 11));
//! // July 8 is Tuesday in ISO week 28 of the year 2014.
//! assert_eq!(dt, UTC.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms(9, 10, 11));
//!
//! let dt = UTC.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
//! assert_eq!(dt, UTC.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
//! let dt = Utc.ymd(2014, 7, 8).and_hms_milli(9, 10, 11, 12); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_micro(9, 10, 11, 12_000));
//! assert_eq!(dt, Utc.ymd(2014, 7, 8).and_hms_nano(9, 10, 11, 12_000_000));
//!
//! // dynamic verification
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
//! LocalResult::Single(UTC.ymd(2014, 7, 8).and_hms(21, 15, 33)));
//! assert_eq!(UTC.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
//! assert_eq!(UTC.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
//! LocalResult::Single(Utc.ymd(2014, 7, 8).and_hms(21, 15, 33)));
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
//!
//! // other time zone objects can be used to construct a local datetime.
//! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
@ -129,7 +149,7 @@
//! let fixed_dt = FixedOffset::east(9 * 3600).ymd(2014, 7, 8).and_hms_milli(18, 10, 11, 12);
//! assert_eq!(dt, fixed_dt);
//! # let _ = local_dt;
//! ~~~~
//! ```
//!
//! Various properties are available to the date and time, and can be altered individually.
//! Most of them are defined in the traits [`Datelike`](./trait.Datelike.html) and
@ -137,8 +157,10 @@
//! Addition and subtraction is also supported.
//! The following illustrates most supported operations to the date and time:
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! # extern crate chrono; extern crate time; fn main() {
//! use chrono::prelude::*;
//! use time::Duration;
//!
//! # /* we intentionally fake the datetime...
//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`:
@ -156,9 +178,9 @@
//! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1
//!
//! // time zone accessor and manipulation
//! assert_eq!(dt.offset().local_minus_utc(), Duration::hours(9));
//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
//! assert_eq!(dt.timezone(), FixedOffset::east(9 * 3600));
//! assert_eq!(dt.with_timezone(&UTC), UTC.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
//! assert_eq!(dt.with_timezone(&Utc), Utc.ymd(2014, 11, 28).and_hms_nano(12, 45, 59, 324310806));
//!
//! // a sample of property manipulations (validates dynamically)
//! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
@ -166,28 +188,34 @@
//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
//!
//! // arithmetic operations
//! assert_eq!(UTC.ymd(2014, 11, 14).and_hms(8, 9, 10) - UTC.ymd(2014, 11, 14).and_hms(10, 9, 8),
//! Duration::seconds(-2 * 3600 + 2));
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
//! UTC.ymd(2001, 9, 9).and_hms(1, 46, 40));
//! assert_eq!(UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
//! UTC.ymd(1938, 4, 24).and_hms(22, 13, 20));
//! ~~~~
//! let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
//! let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
//! assert_eq!(dt1.signed_duration_since(dt2), Duration::seconds(-2 * 3600 + 2));
//! assert_eq!(dt2.signed_duration_since(dt1), Duration::seconds(2 * 3600 - 2));
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) + Duration::seconds(1_000_000_000),
//! Utc.ymd(2001, 9, 9).and_hms(1, 46, 40));
//! assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0) - Duration::seconds(1_000_000_000),
//! Utc.ymd(1938, 4, 24).and_hms(22, 13, 20));
//! # }
//! ```
//!
//! Formatting is done via the [`format`](./datetime/struct.DateTime.html#method.format) method,
//! ### Formatting and Parsing
//!
//! Formatting is done via the [`format`](./struct.DateTime.html#method.format) method,
//! which format is equivalent to the familiar `strftime` format.
//! (See the [`format::strftime` module documentation](./format/strftime/index.html#specifiers)
//! for full syntax.)
//!
//! See [`format::strftime`](./format/strftime/index.html#specifiers)
//! documentation for full syntax and list of specifiers.
//!
//! The default `to_string` method and `{:?}` specifier also give a reasonable representation.
//! Chrono also provides [`to_rfc2822`](./datetime/struct.DateTime.html#method.to_rfc2822) and
//! [`to_rfc3339`](./datetime/struct.DateTime.html#method.to_rfc3339) methods
//! Chrono also provides [`to_rfc2822`](./struct.DateTime.html#method.to_rfc2822) and
//! [`to_rfc3339`](./struct.DateTime.html#method.to_rfc3339) methods
//! for well-known formats.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//!
//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
//! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string());
@ -196,24 +224,28 @@
//! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000");
//! assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00");
//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
//! ~~~~
//!
//! // Note that milli/nanoseconds are only printed if they are non-zero
//! let dt_nano = Utc.ymd(2014, 11, 28).and_hms_nano(12, 0, 9, 1);
//! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z");
//! ```
//!
//! Parsing can be done with three methods:
//!
//! 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
//! (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
//! on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<UTC>` and
//! on a string) can be used for parsing `DateTime<FixedOffset>`, `DateTime<Utc>` and
//! `DateTime<Local>` values. This parses what the `{:?}`
//! ([`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html))
//! format specifier prints, and requires the offset to be present.
//!
//! 2. [`DateTime::parse_from_str`](./datetime/struct.DateTime.html#method.parse_from_str) parses
//! 2. [`DateTime::parse_from_str`](./struct.DateTime.html#method.parse_from_str) parses
//! a date and time with offsets and returns `DateTime<FixedOffset>`.
//! This should be used when the offset is a part of input and the caller cannot guess that.
//! It *cannot* be used when the offset can be missing.
//! [`DateTime::parse_from_rfc2822`](./datetime/struct.DateTime.html#method.parse_from_rfc2822)
//! [`DateTime::parse_from_rfc2822`](./struct.DateTime.html#method.parse_from_rfc2822)
//! and
//! [`DateTime::parse_from_rfc3339`](./datetime/struct.DateTime.html#method.parse_from_rfc3339)
//! [`DateTime::parse_from_rfc3339`](./struct.DateTime.html#method.parse_from_rfc3339)
//! are similar but for well-known formats.
//!
//! 3. [`Offset::datetime_from_str`](./offset/trait.TimeZone.html#method.datetime_from_str) is
@ -225,15 +257,15 @@
//! More detailed control over the parsing process is available via
//! [`format`](./format/index.html) module.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//!
//! let dt = UTC.ymd(2014, 11, 28).and_hms(12, 0, 9);
//! let dt = Utc.ymd(2014, 11, 28).and_hms(12, 0, 9);
//! let fixed_dt = dt.with_timezone(&FixedOffset::east(9*3600));
//!
//! // method 1
//! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<UTC>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<UTC>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T12:00:09Z".parse::<DateTime<Utc>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<Utc>>(), Ok(dt.clone()));
//! assert_eq!("2014-11-28T21:00:09+09:00".parse::<DateTime<FixedOffset>>(), Ok(fixed_dt.clone()));
//!
//! // method 2
@ -244,58 +276,88 @@
//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
//!
//! // method 3
//! assert_eq!(UTC.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
//! assert_eq!(UTC.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
//! assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
//! assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
//!
//! // oops, the year is missing!
//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
//! // oops, the format string does not include the year at all!
//! assert!(UTC.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
//! // oops, the weekday is incorrect!
//! assert!(UTC.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
//! ~~~~
//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
//! ```
//!
//! Again : See [`format::strftime`](./format/strftime/index.html#specifiers)
//! documentation for full syntax and list of specifiers.
//!
//! ### Conversion from and to EPOCH timestamps
//!
//! Use [`Utc.timestamp(seconds, nanoseconds)`](./offset/trait.TimeZone.html#method.timestamp)
//! to construct a [`DateTime<Utc>`](./struct.DateTime.html) from a UNIX timestamp
//! (seconds, nanoseconds that passed since January 1st 1970).
//!
//! Use [`DateTime.timestamp`](./struct.DateTime.html#method.timestamp) to get the timestamp (in seconds)
//! from a [`DateTime`](./struct.DateTime.html). Additionally, you can use
//! [`DateTime.timestamp_subsec_nanos`](./struct.DateTime.html#method.timestamp_subsec_nanos)
//! to get the number of additional number of nanoseconds.
//!
//! ```rust
//! # use chrono::DateTime;
//! # use chrono::Utc;
//! // We need the trait in scope to use Utc::timestamp().
//! use chrono::TimeZone;
//!
//! // Construct a datetime from epoch:
//! let dt = Utc.timestamp(1_500_000_000, 0);
//! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000");
//!
//! // Get epoch value from a datetime:
//! let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap();
//! assert_eq!(dt.timestamp(), 1_500_000_000);
//! ```
//!
//! ### Individual date
//!
//! Chrono also provides an individual date type ([**`Date`**](./date/struct.Date.html)).
//! Chrono also provides an individual date type ([**`Date`**](./struct.Date.html)).
//! It also has time zones attached, and have to be constructed via time zones.
//! Most operations available to `DateTime` are also available to `Date` whenever appropriate.
//!
//! ~~~~ {.rust}
//! use chrono::*;
//! ```rust
//! use chrono::prelude::*;
//! use chrono::offset::LocalResult;
//!
//! # // these *may* fail, but only very rarely. just rerun the test if you were that unfortunate ;)
//! assert_eq!(UTC::today(), UTC::now().date());
//! assert_eq!(Utc::today(), Utc::now().date());
//! assert_eq!(Local::today(), Local::now().date());
//!
//! assert_eq!(UTC.ymd(2014, 11, 28).weekday(), Weekday::Fri);
//! assert_eq!(UTC.ymd_opt(2014, 11, 31), LocalResult::None);
//! assert_eq!(UTC.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
//! assert_eq!(Utc.ymd(2014, 11, 28).weekday(), Weekday::Fri);
//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
//! assert_eq!(Utc.ymd(2014, 11, 28).and_hms_milli(7, 8, 9, 10).format("%H%M%S").to_string(),
//! "070809");
//! ~~~~
//! ```
//!
//! There is no timezone-aware `Time` due to the lack of usefulness and also the complexity.
//!
//! `DateTime` has [`date`](./datetime/struct.DateTime.html#method.date) method
//! `DateTime` has [`date`](./struct.DateTime.html#method.date) method
//! which returns a `Date` which represents its date component.
//! There is also a [`time`](./datetime/struct.DateTime.html#method.time) method,
//! There is also a [`time`](./struct.DateTime.html#method.time) method,
//! which simply returns a naive local time described below.
//!
//! ### Naive date and time
//!
//! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime`
//! as [**`NaiveDate`**](./naive/date/struct.NaiveDate.html),
//! [**`NaiveTime`**](./naive/time/struct.NaiveTime.html) and
//! [**`NaiveDateTime`**](./naive/datetime/struct.NaiveDateTime.html) respectively.
//! as [**`NaiveDate`**](./naive/struct.NaiveDate.html),
//! [**`NaiveTime`**](./naive/struct.NaiveTime.html) and
//! [**`NaiveDateTime`**](./naive/struct.NaiveDateTime.html) respectively.
//!
//! They have almost equivalent interfaces as their timezone-aware twins,
//! but are not associated to time zones obviously and can be quite low-level.
//! They are mostly useful for building blocks for higher-level types.
//!
//! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions:
//! [`naive_local`](./datetime/struct.DateTime.html#method.naive_local) returns
//! [`naive_local`](./struct.DateTime.html#method.naive_local) returns
//! a view to the naive local time,
//! and [`naive_utc`](./datetime/struct.DateTime.html#method.naive_utc) returns
//! and [`naive_utc`](./struct.DateTime.html#method.naive_utc) returns
//! a view to the naive UTC time.
//!
//! ## Limitations
@ -307,7 +369,7 @@
//! Time types are limited in the nanosecond accuracy.
//!
//! [Leap seconds are supported in the representation but
//! Chrono doesn't try to make use of them](./naive/time/index.html#leap-second-handling).
//! Chrono doesn't try to make use of them](./naive/struct.NaiveTime.html#leap-second-handling).
//! (The main reason is that leap seconds are not really predictable.)
//! Almost *every* operation over the possible leap seconds will ignore them.
//! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
@ -316,33 +378,68 @@
//! Chrono inherently does not support an inaccurate or partial date and time representation.
//! Any operation that can be ambiguous will return `None` in such cases.
//! For example, "a month later" of 2014-01-30 is not well-defined
//! and consequently `UTC.ymd(2014, 1, 30).with_month(2)` returns `None`.
//! and consequently `Utc.ymd(2014, 1, 30).with_month(2)` returns `None`.
//!
//! Advanced time zone handling is not yet supported (but is planned in 0.3).
//! Advanced time zone handling is not yet supported.
//! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
#![doc(html_root_url = "https://lifthrasiir.github.io/rust-chrono/")]
#![doc(html_root_url = "https://docs.rs/chrono/latest/")]
#![cfg_attr(bench, feature(test))] // lib stability features as per RFC #507
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
extern crate time as stdtime;
extern crate num;
// The explicit 'static lifetimes are still needed for rustc 1.13-16
// backward compatibility, and this appeases clippy. If minimum rustc
// becomes 1.17, should be able to remove this, those 'static lifetimes,
// and use `static` in a lot of places `const` is used now.
//
// Similarly, redundant_field_names lints on not using the
// field-init-shorthand, which was stabilized in rust 1.17.
//
// Changing trivially_copy_pass_by_ref would require an incompatible version
// bump.
#![cfg_attr(feature = "cargo-clippy", allow(
const_static_lifetime,
redundant_field_names,
trivially_copy_pass_by_ref,
))]
#[cfg(feature="clock")]
extern crate time as oldtime;
extern crate num_integer;
extern crate num_traits;
#[cfg(feature = "rustc-serialize")]
extern crate rustc_serialize;
#[cfg(feature = "serde")]
extern crate serde;
extern crate serde as serdelib;
pub use duration::Duration;
pub use offset::{TimeZone, Offset, LocalResult};
pub use offset::utc::UTC;
pub use offset::fixed::FixedOffset;
pub use offset::local::Local;
pub use naive::date::NaiveDate;
pub use naive::time::NaiveTime;
pub use naive::datetime::NaiveDateTime;
pub use date::Date;
pub use datetime::DateTime;
// this reexport is to aid the transition and should not be in the prelude!
pub use oldtime::Duration;
#[cfg(feature="clock")]
#[doc(no_inline)] pub use offset::Local;
#[doc(no_inline)] pub use offset::{TimeZone, Offset, LocalResult, Utc, FixedOffset};
#[doc(no_inline)] pub use naive::{NaiveDate, IsoWeek, NaiveTime, NaiveDateTime};
pub use date::{Date, MIN_DATE, MAX_DATE};
pub use datetime::{DateTime, SecondsFormat};
#[cfg(feature = "rustc-serialize")]
pub use datetime::rustc_serialize::TsSeconds;
pub use format::{ParseError, ParseResult};
pub use round::SubsecRound;
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
pub mod prelude {
#[doc(no_inline)] pub use {Datelike, Timelike, Weekday};
#[doc(no_inline)] pub use {TimeZone, Offset};
#[cfg(feature="clock")]
#[doc(no_inline)] pub use Local;
#[doc(no_inline)] pub use {Utc, FixedOffset};
#[doc(no_inline)] pub use {NaiveDate, NaiveTime, NaiveDateTime};
#[doc(no_inline)] pub use Date;
#[doc(no_inline)] pub use {DateTime, SecondsFormat};
#[doc(no_inline)] pub use SubsecRound;
}
// useful throughout the codebase
macro_rules! try_opt {
@ -350,13 +447,8 @@ macro_rules! try_opt {
}
mod div;
pub mod duration {
//! ISO 8601 duration.
//!
//! This used to be a part of rust-chrono,
//! but has been subsequently merged into Rust's standard library.
pub use stdtime::Duration;
}
#[cfg(not(feature="clock"))]
mod oldtime;
pub mod offset;
pub mod naive {
//! Date and time types which do not concern about the timezones.
@ -364,20 +456,59 @@ pub mod naive {
//! They are primarily building blocks for other types
//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
//! but can be also used for the simpler date and time handling.
pub mod date;
pub mod time;
pub mod datetime;
mod internals;
mod date;
mod isoweek;
mod time;
mod datetime;
pub use self::date::{NaiveDate, MIN_DATE, MAX_DATE};
pub use self::isoweek::IsoWeek;
pub use self::time::NaiveTime;
pub use self::datetime::NaiveDateTime;
#[cfg(feature = "rustc-serialize")]
#[allow(deprecated)]
pub use self::datetime::rustc_serialize::TsSeconds;
/// Serialization/Deserialization of naive types in alternate formats
///
/// The various modules in here are intended to be used with serde's [`with`
/// annotation][1] to serialize as something other than the default [RFC
/// 3339][2] format.
///
/// [1]: https://serde.rs/attributes.html#field-attributes
/// [2]: https://tools.ietf.org/html/rfc3339
#[cfg(feature = "serde")]
pub mod serde {
pub use super::datetime::serde::*;
}
}
pub mod date;
pub mod datetime;
mod date;
mod datetime;
pub mod format;
mod round;
/// Serialization/Deserialization in alternate formats
///
/// The various modules in here are intended to be used with serde's [`with`
/// annotation][1] to serialize as something other than the default [RFC
/// 3339][2] format.
///
/// [1]: https://serde.rs/attributes.html#field-attributes
/// [2]: https://tools.ietf.org/html/rfc3339
#[cfg(feature = "serde")]
pub mod serde {
pub use super::datetime::serde::*;
}
/// The day of week.
///
/// The order of the days of week depends on the context.
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub enum Weekday {
/// Monday.
@ -509,7 +640,7 @@ impl Weekday {
/// 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.
impl num::traits::FromPrimitive for Weekday {
impl num_traits::FromPrimitive for Weekday {
#[inline]
fn from_i64(n: i64) -> Option<Weekday> {
match n {
@ -539,10 +670,131 @@ impl num::traits::FromPrimitive for Weekday {
}
}
use std::fmt;
/// An error resulting from reading `Weekday` value with `FromStr`.
#[derive(Clone, PartialEq)]
pub struct ParseWeekdayError {
_dummy: (),
}
impl fmt::Debug for ParseWeekdayError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ParseWeekdayError {{ .. }}")
}
}
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
#[cfg(feature = "serde")]
mod weekday_serde {
use super::Weekday;
use std::fmt;
use serdelib::{ser, de};
impl ser::Serialize for Weekday {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer
{
serializer.serialize_str(&format!("{:?}", self))
}
}
struct WeekdayVisitor;
impl<'de> de::Visitor<'de> for WeekdayVisitor {
type Value = Weekday;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Weekday")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where E: de::Error
{
value.parse().map_err(|_| E::custom("short or long weekday names expected"))
}
}
impl<'de> de::Deserialize<'de> for Weekday {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: de::Deserializer<'de>
{
deserializer.deserialize_str(WeekdayVisitor)
}
}
#[cfg(test)]
extern crate serde_json;
#[test]
fn test_serde_serialize() {
use self::serde_json::to_string;
use Weekday::*;
let cases: Vec<(Weekday, &str)> = vec![
(Mon, "\"Mon\""),
(Tue, "\"Tue\""),
(Wed, "\"Wed\""),
(Thu, "\"Thu\""),
(Fri, "\"Fri\""),
(Sat, "\"Sat\""),
(Sun, "\"Sun\""),
];
for (weekday, expected_str) in cases {
let string = to_string(&weekday).unwrap();
assert_eq!(string, expected_str);
}
}
#[test]
fn test_serde_deserialize() {
use self::serde_json::from_str;
use Weekday::*;
let cases: Vec<(&str, Weekday)> = vec![
("\"mon\"", Mon),
("\"MONDAY\"", Mon),
("\"MonDay\"", Mon),
("\"mOn\"", Mon),
("\"tue\"", Tue),
("\"tuesday\"", Tue),
("\"wed\"", Wed),
("\"wednesday\"", Wed),
("\"thu\"", Thu),
("\"thursday\"", Thu),
("\"fri\"", Fri),
("\"friday\"", Fri),
("\"sat\"", Sat),
("\"saturday\"", Sat),
("\"sun\"", Sun),
("\"sunday\"", Sun),
];
for (str, expected_weekday) in cases {
let weekday = from_str::<Weekday>(str).unwrap();
assert_eq!(weekday, expected_weekday);
}
let errors: Vec<&str> = vec![
"\"not a weekday\"",
"\"monDAYs\"",
"\"mond\"",
"mon",
"\"thur\"",
"\"thurs\"",
];
for str in errors {
from_str::<Weekday>(str).unwrap_err();
}
}
}
/// The common set of methods for date component.
pub trait Datelike: Sized {
/// Returns the year number in the [calendar date](./naive/date/index.html#calendar-date).
/// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date).
fn year(&self) -> i32;
/// Returns the absolute year number starting from 1 with a boolean flag,
@ -590,9 +842,8 @@ pub trait Datelike: Sized {
/// Returns the day of week.
fn weekday(&self) -> Weekday;
/// Returns the ISO week date: an adjusted year, week number and day of week.
/// The adjusted year may differ from that of the calendar date.
fn isoweekdate(&self) -> (i32, u32, Weekday);
/// Returns the ISO week.
fn iso_week(&self) -> IsoWeek;
/// Makes a new value with the year number changed.
///
@ -629,7 +880,16 @@ 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, 1 (Day 1) in the proleptic Gregorian calendar.
/// Returns the number of days since January 1, Year 1 (aka Day 1) in the
/// proleptic Gregorian calendar.
///
/// # Example:
///
/// ~~~
/// use chrono::{NaiveDate, Datelike};
/// assert_eq!(NaiveDate::from_ymd(1970, 1, 1).num_days_from_ce(), 719163);
/// assert_eq!(NaiveDate::from_ymd(0, 1, 1).num_days_from_ce(), -365);
/// ~~~
fn num_days_from_ce(&self) -> i32 {
// 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;
@ -637,7 +897,7 @@ pub trait Datelike: Sized {
if year < 0 {
let excess = 1 + (-year) / 400;
year += excess * 400;
ndays -= excess * 146097;
ndays -= excess * 146_097;
}
let div_100 = year / 100;
ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2);
@ -670,7 +930,7 @@ pub trait Timelike: Sized {
/// Returns the number of nanoseconds since the whole non-leap second.
/// The range from 1,000,000,000 to 1,999,999,999 represents
/// the [leap second](./naive/time/index.html#leap-second-handling).
/// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling).
fn nanosecond(&self) -> u32;
/// Makes a new value with the hour number changed.
@ -704,11 +964,13 @@ pub trait Timelike: Sized {
}
}
#[cfg(test)] extern crate num_iter;
#[test]
fn test_readme_doomsday() {
use num::iter::range_inclusive;
use num_iter::range_inclusive;
for y in range_inclusive(naive::date::MIN.year(), naive::date::MAX.year()) {
for y in range_inclusive(naive::MIN_DATE.year(), naive::MAX_DATE.year()) {
// even months
let d4 = NaiveDate::from_ymd(y, 4, 4);
let d6 = NaiveDate::from_ymd(y, 6, 6);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,779 @@
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! The internal implementation of the calendar and ordinal date.
//!
//! The current implementation is optimized for determining year, month, day and day of week.
//! 4-bit `YearFlags` map to one of 14 possible classes of year in the Gregorian calendar,
//! which are included in every packed `NaiveDate` instance.
//! The conversion between the packed calendar date (`Mdf`) and the ordinal date (`Of`) is
//! based on the moderately-sized lookup table (~1.5KB)
//! and the packed representation is chosen for the efficient lookup.
//! Every internal data structure does not validate its input,
//! but the conversion keeps the valid value valid and the invalid value invalid
//! so that the user-facing `NaiveDate` can validate the input as late as possible.
#![allow(dead_code)] // some internal methods have been left for consistency
use std::{i32, fmt};
use num_traits::FromPrimitive;
use Weekday;
use div::{div_rem, mod_floor};
/// The internal date representation. This also includes the packed `Mdf` value.
pub type DateImpl = i32;
pub const MAX_YEAR: DateImpl = i32::MAX >> 13;
pub const MIN_YEAR: DateImpl = i32::MIN >> 13;
/// The year flags (aka the dominical letter).
///
/// There are 14 possible classes of year in the Gregorian calendar:
/// common and leap years starting with Monday through Sunday.
/// The `YearFlags` stores this information into 4 bits `abbb`,
/// where `a` is `1` for the common year (simplifies the `Of` validation)
/// and `bbb` is a non-zero `Weekday` (mapping `Mon` to 7) of the last day in the past year
/// (simplifies the day of week calculation from the 1-based ordinal).
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct YearFlags(pub u8);
pub const A: YearFlags = YearFlags(0o15); pub const AG: YearFlags = YearFlags(0o05);
pub const B: YearFlags = YearFlags(0o14); pub const BA: YearFlags = YearFlags(0o04);
pub const C: YearFlags = YearFlags(0o13); pub const CB: YearFlags = YearFlags(0o03);
pub const D: YearFlags = YearFlags(0o12); pub const DC: YearFlags = YearFlags(0o02);
pub const E: YearFlags = YearFlags(0o11); pub const ED: YearFlags = YearFlags(0o01);
pub const F: YearFlags = YearFlags(0o17); pub const FE: YearFlags = YearFlags(0o07);
pub const G: YearFlags = YearFlags(0o16); pub const GF: YearFlags = YearFlags(0o06);
static YEAR_TO_FLAGS: [YearFlags; 400] = [
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F,
ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B,
AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100
C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D,
CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G,
FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C,
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200
E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C,
BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F,
ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B,
AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300
G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E,
DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A,
GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D,
CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G,
FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400
];
static YEAR_DELTAS: [u8; 401] = [
0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20,
20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100
25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29,
29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34,
34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39,
39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44,
44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, // 200
49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53,
53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58,
58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63,
63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68,
68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, // 300
73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77,
77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82,
82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87,
87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92,
92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97 // 400+1
];
pub fn cycle_to_yo(cycle: u32) -> (u32, u32) {
let (mut year_mod_400, mut ordinal0) = div_rem(cycle, 365);
let delta = u32::from(YEAR_DELTAS[year_mod_400 as usize]);
if ordinal0 < delta {
year_mod_400 -= 1;
ordinal0 += 365 - u32::from(YEAR_DELTAS[year_mod_400 as usize]);
} else {
ordinal0 -= delta;
}
(year_mod_400, ordinal0 + 1)
}
pub fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 {
year_mod_400 * 365 + u32::from(YEAR_DELTAS[year_mod_400 as usize]) + ordinal - 1
}
impl YearFlags {
#[inline]
pub fn from_year(year: i32) -> YearFlags {
let year = mod_floor(year, 400);
YearFlags::from_year_mod_400(year)
}
#[inline]
pub fn from_year_mod_400(year: i32) -> YearFlags {
YEAR_TO_FLAGS[year as usize]
}
#[inline]
pub fn ndays(&self) -> u32 {
let YearFlags(flags) = *self;
366 - u32::from(flags >> 3)
}
#[inline]
pub fn isoweek_delta(&self) -> u32 {
let YearFlags(flags) = *self;
let mut delta = u32::from(flags) & 0b0111;
if delta < 3 { delta += 7; }
delta
}
#[inline]
pub fn nisoweeks(&self) -> u32 {
let YearFlags(flags) = *self;
52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1)
}
}
impl fmt::Debug for YearFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let YearFlags(flags) = *self;
match flags {
0o15 => "A".fmt(f), 0o05 => "AG".fmt(f),
0o14 => "B".fmt(f), 0o04 => "BA".fmt(f),
0o13 => "C".fmt(f), 0o03 => "CB".fmt(f),
0o12 => "D".fmt(f), 0o02 => "DC".fmt(f),
0o11 => "E".fmt(f), 0o01 => "ED".fmt(f),
0o10 => "F?".fmt(f), 0o00 => "FE?".fmt(f), // non-canonical
0o17 => "F".fmt(f), 0o07 => "FE".fmt(f),
0o16 => "G".fmt(f), 0o06 => "GF".fmt(f),
_ => write!(f, "YearFlags({})", flags),
}
}
}
pub const MIN_OL: u32 = 1 << 1;
pub const MAX_OL: u32 = 366 << 1; // larger than the non-leap last day `(365 << 1) | 1`
pub const MIN_MDL: u32 = (1 << 6) | (1 << 1);
pub const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1;
const XX: i8 = -128;
static MDL_TO_OL: [i8; (MAX_MDL as usize + 1)] = [
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0
XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2
XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3
XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4
XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5
XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6
XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7
XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8
XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9
XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10
XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11
XX, XX, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, // 12
];
static OL_TO_MDL: [u8; (MAX_OL as usize + 1)] = [
0, 0, // 0
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, // 2
74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74,
72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3
76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76,
74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4
80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80,
78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5
82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82,
80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6
86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86,
84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7
88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88,
86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8
90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90,
88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9
94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94,
92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10
96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96,
94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11
100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100,
98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98,100, 98, // 12
];
/// Ordinal (day of year) and year flags: `(ordinal << 4) | flags`.
///
/// The whole bits except for the least 3 bits are referred as `Ol` (ordinal and leap flag),
/// which is an index to the `OL_TO_MDL` lookup table.
#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub struct Of(pub u32);
impl Of {
#[inline]
fn clamp_ordinal(ordinal: u32) -> u32 {
if ordinal > 366 {0} else {ordinal}
}
#[inline]
pub fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
let ordinal = Of::clamp_ordinal(ordinal);
Of((ordinal << 4) | u32::from(flags))
}
#[inline]
pub fn from_mdf(Mdf(mdf): Mdf) -> Of {
let mdl = mdf >> 3;
match MDL_TO_OL.get(mdl as usize) {
Some(&v) => Of(mdf.wrapping_sub((i32::from(v) as u32 & 0x3ff) << 3)),
None => Of(0)
}
}
#[inline]
pub fn valid(&self) -> bool {
let Of(of) = *self;
let ol = of >> 3;
MIN_OL <= ol && ol <= MAX_OL
}
#[inline]
pub fn ordinal(&self) -> u32 {
let Of(of) = *self;
of >> 4
}
#[inline]
pub fn with_ordinal(&self, ordinal: u32) -> Of {
let ordinal = Of::clamp_ordinal(ordinal);
let Of(of) = *self;
Of((of & 0b1111) | (ordinal << 4))
}
#[inline]
pub fn flags(&self) -> YearFlags {
let Of(of) = *self;
YearFlags((of & 0b1111) as u8)
}
#[inline]
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Of {
let Of(of) = *self;
Of((of & !0b1111) | u32::from(flags))
}
#[inline]
pub fn weekday(&self) -> Weekday {
let Of(of) = *self;
Weekday::from_u32(((of >> 4) + (of & 0b111)) % 7).unwrap()
}
#[inline]
pub fn isoweekdate_raw(&self) -> (u32, Weekday) {
// week ordinal = ordinal + delta
let Of(of) = *self;
let weekord = (of >> 4).wrapping_add(self.flags().isoweek_delta());
(weekord / 7, Weekday::from_u32(weekord % 7).unwrap())
}
#[inline]
pub fn to_mdf(&self) -> Mdf {
Mdf::from_of(*self)
}
#[inline]
pub fn succ(&self) -> Of {
let Of(of) = *self;
Of(of + (1 << 4))
}
#[inline]
pub fn pred(&self) -> Of {
let Of(of) = *self;
Of(of - (1 << 4))
}
}
impl fmt::Debug for Of {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Of(of) = *self;
write!(f, "Of(({} << 4) | {:#04o} /*{:?}*/)",
of >> 4, of & 0b1111, YearFlags((of & 0b1111) as u8))
}
}
/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags`
///
/// The whole bits except for the least 3 bits are referred as `Mdl`
/// (month, day of month and leap flag),
/// which is an index to the `MDL_TO_OL` lookup table.
#[derive(PartialEq, PartialOrd, Copy, Clone)]
pub struct Mdf(pub u32);
impl Mdf {
#[inline]
fn clamp_month(month: u32) -> u32 {
if month > 12 {0} else {month}
}
#[inline]
fn clamp_day(day: u32) -> u32 {
if day > 31 {0} else {day}
}
#[inline]
pub fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
let month = Mdf::clamp_month(month);
let day = Mdf::clamp_day(day);
Mdf((month << 9) | (day << 4) | u32::from(flags))
}
#[inline]
pub fn from_of(Of(of): Of) -> Mdf {
let ol = of >> 3;
match OL_TO_MDL.get(ol as usize) {
Some(&v) => Mdf(of + (u32::from(v) << 3)),
None => Mdf(0)
}
}
#[inline]
pub fn valid(&self) -> bool {
let Mdf(mdf) = *self;
let mdl = mdf >> 3;
match MDL_TO_OL.get(mdl as usize) {
Some(&v) => v >= 0,
None => false
}
}
#[inline]
pub fn month(&self) -> u32 {
let Mdf(mdf) = *self;
mdf >> 9
}
#[inline]
pub fn with_month(&self, month: u32) -> Mdf {
let month = Mdf::clamp_month(month);
let Mdf(mdf) = *self;
Mdf((mdf & 0b1_1111_1111) | (month << 9))
}
#[inline]
pub fn day(&self) -> u32 {
let Mdf(mdf) = *self;
(mdf >> 4) & 0b1_1111
}
#[inline]
pub fn with_day(&self, day: u32) -> Mdf {
let day = Mdf::clamp_day(day);
let Mdf(mdf) = *self;
Mdf((mdf & !0b1_1111_0000) | (day << 4))
}
#[inline]
pub fn flags(&self) -> YearFlags {
let Mdf(mdf) = *self;
YearFlags((mdf & 0b1111) as u8)
}
#[inline]
pub fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf {
let Mdf(mdf) = *self;
Mdf((mdf & !0b1111) | u32::from(flags))
}
#[inline]
pub fn to_of(&self) -> Of {
Of::from_mdf(*self)
}
}
impl fmt::Debug for Mdf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Mdf(mdf) = *self;
write!(f, "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)",
mdf >> 9, (mdf >> 4) & 0b1_1111, mdf & 0b1111, YearFlags((mdf & 0b1111) as u8))
}
}
#[cfg(test)]
mod tests {
#[cfg(test)] extern crate num_iter;
#[cfg(bench)] extern crate test;
use Weekday;
use super::{Of, Mdf};
use super::{YearFlags, A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF};
use self::num_iter::range_inclusive;
use std::u32;
const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G];
const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF];
const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF];
#[test]
fn test_year_flags_ndays_from_year() {
assert_eq!(YearFlags::from_year(2014).ndays(), 365);
assert_eq!(YearFlags::from_year(2012).ndays(), 366);
assert_eq!(YearFlags::from_year(2000).ndays(), 366);
assert_eq!(YearFlags::from_year(1900).ndays(), 365);
assert_eq!(YearFlags::from_year(1600).ndays(), 366);
assert_eq!(YearFlags::from_year( 1).ndays(), 365);
assert_eq!(YearFlags::from_year( 0).ndays(), 366); // 1 BCE (proleptic Gregorian)
assert_eq!(YearFlags::from_year( -1).ndays(), 365); // 2 BCE
assert_eq!(YearFlags::from_year( -4).ndays(), 366); // 5 BCE
assert_eq!(YearFlags::from_year( -99).ndays(), 365); // 100 BCE
assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE
assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE
assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE
}
#[test]
fn test_year_flags_nisoweeks() {
assert_eq!(A.nisoweeks(), 52);
assert_eq!(B.nisoweeks(), 52);
assert_eq!(C.nisoweeks(), 52);
assert_eq!(D.nisoweeks(), 53);
assert_eq!(E.nisoweeks(), 52);
assert_eq!(F.nisoweeks(), 52);
assert_eq!(G.nisoweeks(), 52);
assert_eq!(AG.nisoweeks(), 52);
assert_eq!(BA.nisoweeks(), 52);
assert_eq!(CB.nisoweeks(), 52);
assert_eq!(DC.nisoweeks(), 53);
assert_eq!(ED.nisoweeks(), 53);
assert_eq!(FE.nisoweeks(), 52);
assert_eq!(GF.nisoweeks(), 52);
}
#[cfg(bench)]
#[bench]
fn bench_year_flags_from_year(bh: &mut test::Bencher) {
bh.iter(|| {
for year in -999i32..1000 {
YearFlags::from_year(year);
}
});
}
#[test]
fn test_of() {
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
for ordinal in range_inclusive(ordinal1, ordinal2) {
let of = Of::new(ordinal, flags);
assert!(of.valid() == expected,
"ordinal {} = {:?} should be {} for dominical year {:?}",
ordinal, of, if expected {"valid"} else {"invalid"}, flags);
}
}
for &flags in NONLEAP_FLAGS.iter() {
check(false, flags, 0, 0);
check(true, flags, 1, 365);
check(false, flags, 366, 1024);
check(false, flags, u32::MAX, u32::MAX);
}
for &flags in LEAP_FLAGS.iter() {
check(false, flags, 0, 0);
check(true, flags, 1, 366);
check(false, flags, 367, 1024);
check(false, flags, u32::MAX, u32::MAX);
}
}
#[test]
fn test_mdf_valid() {
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32,
month2: u32, day2: u32) {
for month in range_inclusive(month1, month2) {
for day in range_inclusive(day1, day2) {
let mdf = Mdf::new(month, day, flags);
assert!(mdf.valid() == expected,
"month {} day {} = {:?} should be {} for dominical year {:?}",
month, day, mdf, if expected {"valid"} else {"invalid"}, flags);
}
}
}
for &flags in NONLEAP_FLAGS.iter() {
check(false, flags, 0, 0, 0, 1024);
check(false, flags, 0, 0, 16, 0);
check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024);
check(true, flags, 2, 1, 2, 28); check(false, flags, 2, 29, 2, 1024);
check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024);
check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024);
check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024);
check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024);
check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024);
check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024);
check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024);
check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024);
check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024);
check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024);
check(false, flags, 13, 0, 16, 1024);
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
check(false, flags, 0, u32::MAX, 16, u32::MAX);
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
}
for &flags in LEAP_FLAGS.iter() {
check(false, flags, 0, 0, 0, 1024);
check(false, flags, 0, 0, 16, 0);
check(true, flags, 1, 1, 1, 31); check(false, flags, 1, 32, 1, 1024);
check(true, flags, 2, 1, 2, 29); check(false, flags, 2, 30, 2, 1024);
check(true, flags, 3, 1, 3, 31); check(false, flags, 3, 32, 3, 1024);
check(true, flags, 4, 1, 4, 30); check(false, flags, 4, 31, 4, 1024);
check(true, flags, 5, 1, 5, 31); check(false, flags, 5, 32, 5, 1024);
check(true, flags, 6, 1, 6, 30); check(false, flags, 6, 31, 6, 1024);
check(true, flags, 7, 1, 7, 31); check(false, flags, 7, 32, 7, 1024);
check(true, flags, 8, 1, 8, 31); check(false, flags, 8, 32, 8, 1024);
check(true, flags, 9, 1, 9, 30); check(false, flags, 9, 31, 9, 1024);
check(true, flags, 10, 1, 10, 31); check(false, flags, 10, 32, 10, 1024);
check(true, flags, 11, 1, 11, 30); check(false, flags, 11, 31, 11, 1024);
check(true, flags, 12, 1, 12, 31); check(false, flags, 12, 32, 12, 1024);
check(false, flags, 13, 0, 16, 1024);
check(false, flags, u32::MAX, 0, u32::MAX, 1024);
check(false, flags, 0, u32::MAX, 16, u32::MAX);
check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX);
}
}
#[test]
fn test_of_fields() {
for &flags in FLAGS.iter() {
for ordinal in range_inclusive(1u32, 366) {
let of = Of::new(ordinal, flags);
if of.valid() {
assert_eq!(of.ordinal(), ordinal);
}
}
}
}
#[test]
fn test_of_with_fields() {
fn check(flags: YearFlags, ordinal: u32) {
let of = Of::new(ordinal, flags);
for ordinal in range_inclusive(0u32, 1024) {
let of = of.with_ordinal(ordinal);
assert_eq!(of.valid(), Of::new(ordinal, flags).valid());
if of.valid() {
assert_eq!(of.ordinal(), ordinal);
}
}
}
for &flags in NONLEAP_FLAGS.iter() {
check(flags, 1);
check(flags, 365);
}
for &flags in LEAP_FLAGS.iter() {
check(flags, 1);
check(flags, 366);
}
}
#[test]
fn test_of_weekday() {
assert_eq!(Of::new(1, A).weekday(), Weekday::Sun);
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat);
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri);
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu);
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed);
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue);
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon);
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun);
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat);
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri);
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu);
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed);
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue);
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon);
for &flags in FLAGS.iter() {
let mut prev = Of::new(1, flags).weekday();
for ordinal in range_inclusive(2u32, flags.ndays()) {
let of = Of::new(ordinal, flags);
let expected = prev.succ();
assert_eq!(of.weekday(), expected);
prev = expected;
}
}
}
#[test]
fn test_mdf_fields() {
for &flags in FLAGS.iter() {
for month in range_inclusive(1u32, 12) {
for day in range_inclusive(1u32, 31) {
let mdf = Mdf::new(month, day, flags);
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
}
}
}
}
}
#[test]
fn test_mdf_with_fields() {
fn check(flags: YearFlags, month: u32, day: u32) {
let mdf = Mdf::new(month, day, flags);
for month in range_inclusive(0u32, 16) {
let mdf = mdf.with_month(month);
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
}
}
for day in range_inclusive(0u32, 1024) {
let mdf = mdf.with_day(day);
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid());
if mdf.valid() {
assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day);
}
}
}
for &flags in NONLEAP_FLAGS.iter() {
check(flags, 1, 1);
check(flags, 1, 31);
check(flags, 2, 1);
check(flags, 2, 28);
check(flags, 2, 29);
check(flags, 12, 31);
}
for &flags in LEAP_FLAGS.iter() {
check(flags, 1, 1);
check(flags, 1, 31);
check(flags, 2, 1);
check(flags, 2, 29);
check(flags, 2, 30);
check(flags, 12, 31);
}
}
#[test]
fn test_of_isoweekdate_raw() {
for &flags in FLAGS.iter() {
// January 4 should be in the first week
let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw();
assert_eq!(week, 1);
}
}
#[test]
fn test_of_to_mdf() {
for i in range_inclusive(0u32, 8192) {
let of = Of(i);
assert_eq!(of.valid(), of.to_mdf().valid());
}
}
#[test]
fn test_mdf_to_of() {
for i in range_inclusive(0u32, 8192) {
let mdf = Mdf(i);
assert_eq!(mdf.valid(), mdf.to_of().valid());
}
}
#[test]
fn test_of_to_mdf_to_of() {
for i in range_inclusive(0u32, 8192) {
let of = Of(i);
if of.valid() {
assert_eq!(of, of.to_mdf().to_of());
}
}
}
#[test]
fn test_mdf_to_of_to_mdf() {
for i in range_inclusive(0u32, 8192) {
let mdf = Mdf(i);
if mdf.valid() {
assert_eq!(mdf, mdf.to_of().to_mdf());
}
}
}
}

View File

@ -0,0 +1,161 @@
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
//! ISO 8601 week.
use std::fmt;
use super::internals::{DateImpl, Of, YearFlags};
/// ISO 8601 week.
///
/// This type, combined with [`Weekday`](../enum.Weekday.html),
/// constitues the ISO 8601 [week date](./struct.NaiveDate.html#week-date).
/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types
/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method.
#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct IsoWeek {
// note that this allows for larger year range than `NaiveDate`.
// this is crucial because we have an edge case for the first and last week supported,
// which year number might not match the calendar year number.
ywf: DateImpl, // (year << 10) | (week << 4) | flag
}
/// Returns the corresponding `IsoWeek` from the year and the `Of` internal value.
//
// internal use only. we don't expose the public constructor for `IsoWeek` for now,
// because the year range for the week date and the calendar date do not match and
// it is confusing to have a date that is out of range in one and not in another.
// currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`.
pub fn iso_week_from_yof(year: i32, of: Of) -> IsoWeek {
let (rawweek, _) = of.isoweekdate_raw();
let (year, week) = if rawweek < 1 { // previous year
let prevlastweek = YearFlags::from_year(year - 1).nisoweeks();
(year - 1, prevlastweek)
} else {
let lastweek = of.flags().nisoweeks();
if rawweek > lastweek { // next year
(year + 1, 1)
} else {
(year, rawweek)
}
};
IsoWeek { ywf: (year << 10) | (week << 4) as DateImpl | DateImpl::from(of.flags().0) }
}
impl IsoWeek {
/// Returns the year number for this ISO week.
///
/// # Example
///
/// ~~~~
/// use chrono::{NaiveDate, Datelike, Weekday};
///
/// let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
/// assert_eq!(d.iso_week().year(), 2015);
/// ~~~~
///
/// This year number might not match the calendar year number.
/// Continuing the example...
///
/// ~~~~
/// # use chrono::{NaiveDate, Datelike, Weekday};
/// # let d = NaiveDate::from_isoywd(2015, 1, Weekday::Mon);
/// assert_eq!(d.year(), 2014);
/// assert_eq!(d, NaiveDate::from_ymd(2014, 12, 29));
/// ~~~~
#[inline]
pub fn year(&self) -> i32 {
self.ywf >> 10
}
/// Returns the ISO week number starting from 1.
///
/// The return value ranges from 1 to 53. (The last week of year differs by years.)
///
/// # Example
///
/// ~~~~
/// use chrono::{NaiveDate, Datelike, Weekday};
///
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
/// assert_eq!(d.iso_week().week(), 15);
/// ~~~~
#[inline]
pub fn week(&self) -> u32 {
((self.ywf >> 4) & 0x3f) as u32
}
/// Returns the ISO week number starting from 0.
///
/// The return value ranges from 0 to 52. (The last week of year differs by years.)
///
/// # Example
///
/// ~~~~
/// use chrono::{NaiveDate, Datelike, Weekday};
///
/// let d = NaiveDate::from_isoywd(2015, 15, Weekday::Mon);
/// assert_eq!(d.iso_week().week0(), 14);
/// ~~~~
#[inline]
pub fn week0(&self) -> u32 {
((self.ywf >> 4) & 0x3f) as u32 - 1
}
}
/// The `Debug` output of the ISO week `w` is same to
/// [`d.format("%G-W%V")`](../format/strftime/index.html)
/// where `d` is any `NaiveDate` value in that week.
///
/// # Example
///
/// ~~~~
/// use chrono::{NaiveDate, Datelike};
///
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(2015, 9, 5).iso_week()), "2015-W36");
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 3).iso_week()), "0000-W01");
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(9999, 12, 31).iso_week()), "9999-W52");
/// ~~~~
///
/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE.
///
/// ~~~~
/// # use chrono::{NaiveDate, Datelike};
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd( 0, 1, 2).iso_week()), "-0001-W52");
/// assert_eq!(format!("{:?}", NaiveDate::from_ymd(10000, 12, 31).iso_week()), "+10000-W52");
/// ~~~~
impl fmt::Debug for IsoWeek {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let year = self.year();
let week = self.week();
if 0 <= year && year <= 9999 {
write!(f, "{:04}-W{:02}", year, week)
} else {
// ISO 8601 requires the explicit sign for out-of-range years
write!(f, "{:+05}-W{:02}", year, week)
}
}
}
#[cfg(test)]
mod tests {
use naive::{internals, MIN_DATE, MAX_DATE};
use Datelike;
#[test]
fn test_iso_week_extremes() {
let minweek = MIN_DATE.iso_week();
let maxweek = MAX_DATE.iso_week();
assert_eq!(minweek.year(), internals::MIN_YEAR);
assert_eq!(minweek.week(), 1);
assert_eq!(minweek.week0(), 0);
assert_eq!(format!("{:?}", minweek), MIN_DATE.format("%G-W%V").to_string());
assert_eq!(maxweek.year(), internals::MAX_YEAR + 1);
assert_eq!(maxweek.week(), 1);
assert_eq!(maxweek.week0(), 0);
assert_eq!(format!("{:?}", maxweek), MAX_DATE.format("%G-W%V").to_string());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,44 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* The time zone which has a fixed offset from UTC.
*/
//! The time zone which has a fixed offset from UTC.
use std::ops::{Add, Sub};
use std::fmt;
use oldtime::Duration as OldDuration;
use Timelike;
use div::div_mod_floor;
use duration::Duration;
use naive::date::NaiveDate;
use naive::datetime::NaiveDateTime;
use naive::{NaiveTime, NaiveDate, NaiveDateTime};
use DateTime;
use super::{TimeZone, Offset, LocalResult};
/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59.
#[derive(Copy, Clone, PartialEq, Eq)]
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on a `FixedOffset` struct is the preferred way to construct
/// `DateTime<FixedOffset>` instances. See the [`east`](#method.east) and
/// [`west`](#method.west) methods for examples.
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
pub struct FixedOffset {
local_minus_utc: i32,
}
impl FixedOffset {
/// Makes a new `FixedOffset` from the serialized representation.
/// Used for serialization formats.
#[cfg(feature = "rustc-serialize")]
fn from_serialized(secs: i32) -> Option<FixedOffset> {
// check if the values are in the range
if secs <= -86400 || 86400 <= secs { return None; }
let offset = FixedOffset { local_minus_utc: secs };
Some(offset)
}
/// Returns a serialized representation of this `FixedOffset`.
#[cfg(feature = "rustc-serialize")]
fn to_serialized(&self) -> i32 {
self.local_minus_utc
}
/// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference.
/// The negative `secs` means the Western Hemisphere.
///
/// Panics on the out-of-bound `secs`.
///
/// # Example
///
/// ~~~~
/// use chrono::{FixedOffset, TimeZone};
/// let hour = 3600;
/// let datetime = FixedOffset::east(5 * hour).ymd(2016, 11, 08)
/// .and_hms(0, 0, 0);
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
/// ~~~~
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
@ -51,7 +48,7 @@ impl FixedOffset {
///
/// Returns `None` on the out-of-bound `secs`.
pub fn east_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: secs })
} else {
None
@ -62,6 +59,16 @@ impl FixedOffset {
/// The negative `secs` means the Eastern Hemisphere.
///
/// Panics on the out-of-bound `secs`.
///
/// # Example
///
/// ~~~~
/// use chrono::{FixedOffset, TimeZone};
/// let hour = 3600;
/// let datetime = FixedOffset::west(5 * hour).ymd(2016, 11, 08)
/// .and_hms(0, 0, 0);
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
/// ~~~~
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
@ -71,32 +78,42 @@ impl FixedOffset {
///
/// Returns `None` on the out-of-bound `secs`.
pub fn west_opt(secs: i32) -> Option<FixedOffset> {
if -86400 < secs && secs < 86400 {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: -secs })
} else {
None
}
}
/// Returns the number of seconds to add to convert from UTC to the local time.
pub fn local_minus_utc(&self) -> i32 {
self.local_minus_utc
}
/// Returns the number of seconds to add to convert from the local time to UTC.
pub fn utc_minus_local(&self) -> i32 {
-self.local_minus_utc
}
}
impl TimeZone for FixedOffset {
type Offset = FixedOffset;
fn from_offset(offset: &FixedOffset) -> FixedOffset { offset.clone() }
fn from_offset(offset: &FixedOffset) -> FixedOffset { *offset }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<FixedOffset> {
LocalResult::Single(self.clone())
LocalResult::Single(*self)
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<FixedOffset> {
LocalResult::Single(self.clone())
LocalResult::Single(*self)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { self.clone() }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { self.clone() }
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { *self }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { *self }
}
impl Offset for FixedOffset {
fn local_minus_utc(&self) -> Duration { Duration::seconds(self.local_minus_utc as i64) }
fn fix(&self) -> FixedOffset { *self }
}
impl fmt::Debug for FixedOffset {
@ -117,72 +134,91 @@ impl fmt::Display for FixedOffset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) }
}
#[cfg(feature = "rustc-serialize")]
mod rustc_serialize {
use super::FixedOffset;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
// addition or subtraction of FixedOffset to/from Timelike values is same to
// adding or subtracting the offset's local_minus_utc value
// but keep keeps the leap second information.
// this should be implemented more efficiently, but for the time being, this is generic right now.
// TODO the current serialization format is NEVER intentionally defined.
// this basically follows the automatically generated implementation for those traits,
// plus manual verification steps for avoiding security problem.
// in the future it is likely to be redefined to more sane and reasonable format.
fn add_with_leapsecond<T>(lhs: &T, rhs: i32) -> T
where T: Timelike + Add<OldDuration, Output=T>
{
// extract and temporarily remove the fractional part and later recover it
let nanos = lhs.nanosecond();
let lhs = lhs.with_nanosecond(0).unwrap();
(lhs + OldDuration::seconds(i64::from(rhs))).with_nanosecond(nanos).unwrap()
}
impl Encodable for FixedOffset {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
let secs = self.to_serialized();
s.emit_struct("FixedOffset", 1, |s| {
try!(s.emit_struct_field("local_minus_utc", 0, |s| secs.encode(s)));
Ok(())
})
}
}
impl Add<FixedOffset> for NaiveTime {
type Output = NaiveTime;
impl Decodable for FixedOffset {
fn decode<D: Decoder>(d: &mut D) -> Result<FixedOffset, D::Error> {
d.read_struct("FixedOffset", 1, |d| {
let secs = try!(d.read_struct_field("local_minus_utc", 0, Decodable::decode));
FixedOffset::from_serialized(secs).ok_or_else(|| d.error("invalid offset"))
})
}
}
#[test]
fn test_encodable() {
use rustc_serialize::json::encode;
assert_eq!(encode(&FixedOffset::east(0)).ok(),
Some(r#"{"local_minus_utc":0}"#.into()));
assert_eq!(encode(&FixedOffset::east(1234)).ok(),
Some(r#"{"local_minus_utc":1234}"#.into()));
assert_eq!(encode(&FixedOffset::east(86399)).ok(),
Some(r#"{"local_minus_utc":86399}"#.into()));
assert_eq!(encode(&FixedOffset::west(1234)).ok(),
Some(r#"{"local_minus_utc":-1234}"#.into()));
assert_eq!(encode(&FixedOffset::west(86399)).ok(),
Some(r#"{"local_minus_utc":-86399}"#.into()));
}
#[test]
fn test_decodable() {
use rustc_serialize::json;
let decode = |s: &str| json::decode::<FixedOffset>(s);
assert_eq!(decode(r#"{"local_minus_utc":0}"#).ok(), Some(FixedOffset::east(0)));
assert_eq!(decode(r#"{"local_minus_utc": 1234}"#).ok(), Some(FixedOffset::east(1234)));
assert_eq!(decode(r#"{"local_minus_utc":86399}"#).ok(), Some(FixedOffset::east(86399)));
assert_eq!(decode(r#"{"local_minus_utc":-1234}"#).ok(), Some(FixedOffset::west(1234)));
assert_eq!(decode(r#"{"local_minus_utc":-86399}"#).ok(), Some(FixedOffset::west(86399)));
assert!(decode(r#"{"local_minus_utc":86400}"#).is_err());
assert!(decode(r#"{"local_minus_utc":-86400}"#).is_err());
assert!(decode(r#"{"local_minus_utc":0.1}"#).is_err());
assert!(decode(r#"{"local_minus_utc":null}"#).is_err());
assert!(decode(r#"{}"#).is_err());
assert!(decode(r#"0"#).is_err());
assert!(decode(r#"1234"#).is_err());
assert!(decode(r#""string""#).is_err());
assert!(decode(r#"null"#).is_err());
#[inline]
fn add(self, rhs: FixedOffset) -> NaiveTime {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl Sub<FixedOffset> for NaiveTime {
type Output = NaiveTime;
#[inline]
fn sub(self, rhs: FixedOffset) -> NaiveTime {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
impl Add<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn add(self, rhs: FixedOffset) -> NaiveDateTime {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl Sub<FixedOffset> for NaiveDateTime {
type Output = NaiveDateTime;
#[inline]
fn sub(self, rhs: FixedOffset) -> NaiveDateTime {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
impl<Tz: TimeZone> Add<FixedOffset> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn add(self, rhs: FixedOffset) -> DateTime<Tz> {
add_with_leapsecond(&self, rhs.local_minus_utc)
}
}
impl<Tz: TimeZone> Sub<FixedOffset> for DateTime<Tz> {
type Output = DateTime<Tz>;
#[inline]
fn sub(self, rhs: FixedOffset) -> DateTime<Tz> {
add_with_leapsecond(&self, -rhs.local_minus_utc)
}
}
#[cfg(test)]
mod tests {
use offset::TimeZone;
use super::FixedOffset;
#[test]
fn test_date_extreme_offset() {
// starting from 0.3 we don't have an offset exceeding one day.
// this makes everything easier!
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29)),
"2012-02-29+23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::east(86399).ymd(2012, 2, 29).and_hms(5, 6, 7)),
"2012-02-29T05:06:07+23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4)),
"2012-03-04-23:59:59".to_string());
assert_eq!(format!("{:?}", FixedOffset::west(86399).ymd(2012, 3, 4).and_hms(5, 6, 7)),
"2012-03-04T05:06:07-23:59:59".to_string());
}
}

View File

@ -1,39 +1,32 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* The local (system) time zone.
*/
//! The local (system) time zone.
use stdtime;
use oldtime;
use {Datelike, Timelike};
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use {Date, DateTime};
use super::{TimeZone, LocalResult};
use super::fixed::FixedOffset;
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
/// This assumes that `time` is working correctly, i.e. any error is fatal.
fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
fn tm_to_datetime(mut tm: oldtime::Tm) -> DateTime<Local> {
if tm.tm_sec >= 60 {
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
tm.tm_sec = 59;
}
#[cfg(not(windows))]
fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate {
fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
// from_yo is more efficient than from_ymd (since it's the internal representation).
NaiveDate::from_yo(tm.tm_year + 1900, tm.tm_yday as u32 + 1)
}
#[cfg(windows)]
fn tm_to_naive_date(tm: &stdtime::Tm) -> NaiveDate {
fn tm_to_naive_date(tm: &oldtime::Tm) -> NaiveDate {
// ...but tm_yday is broken in Windows (issue #85)
NaiveDate::from_ymd(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
}
@ -42,17 +35,17 @@ fn tm_to_datetime(mut tm: stdtime::Tm) -> DateTime<Local> {
let time = NaiveTime::from_hms_nano(tm.tm_hour as u32, tm.tm_min as u32,
tm.tm_sec as u32, tm.tm_nsec as u32);
let offset = FixedOffset::east(tm.tm_utcoff);
DateTime::from_utc(date.and_time(time) + Duration::seconds(-tm.tm_utcoff as i64), offset)
DateTime::from_utc(date.and_time(time) - offset, offset)
}
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec {
fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> oldtime::Timespec {
// well, this exploits an undocumented `Tm::to_timespec` behavior
// to get the exact function we want (either `timegm` or `mktime`).
// the number 1 is arbitrary but should be non-zero to trigger `mktime`.
let tm_utcoff = if local {1} else {0};
let tm = stdtime::Tm {
let tm = oldtime::Tm {
tm_sec: d.second() as i32,
tm_min: d.minute() as i32,
tm_hour: d.hour() as i32,
@ -63,14 +56,28 @@ fn datetime_to_timespec(d: &NaiveDateTime, local: bool) -> stdtime::Timespec {
tm_yday: 0, // and this
tm_isdst: -1,
tm_utcoff: tm_utcoff,
tm_nsec: d.nanosecond() as i32,
// do not set this, OS APIs are heavily inconsistent in terms of leap second handling
tm_nsec: 0,
};
tm.to_timespec()
}
/// The local timescale. This is implemented via the standard `time` crate.
#[derive(Copy, Clone)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on the Local struct is the preferred way to construct `DateTime<Local>`
/// instances.
///
/// # Example
///
/// ~~~~
/// use chrono::{Local, DateTime, TimeZone};
///
/// let dt: DateTime<Local> = Local::now();
/// let dt: DateTime<Local> = Local.timestamp(0, 0);
/// ~~~~
#[derive(Copy, Clone, Debug)]
pub struct Local;
impl Local {
@ -81,7 +88,7 @@ impl Local {
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<Local> {
tm_to_datetime(stdtime::now())
tm_to_datetime(oldtime::now())
}
}
@ -94,6 +101,7 @@ impl TimeZone for Local {
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
self.from_local_date(local).map(|date| *date.offset())
}
fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<FixedOffset> {
self.from_local_datetime(local).map(|datetime| *datetime.offset())
}
@ -101,6 +109,7 @@ impl TimeZone for Local {
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
*self.from_utc_date(utc).offset()
}
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset {
*self.from_utc_datetime(utc).offset()
}
@ -111,20 +120,63 @@ impl TimeZone for Local {
// in the other words, we use the offset at the local midnight
// but keep the actual date unaltered (much like `FixedOffset`).
let midnight = self.from_local_datetime(&local.and_hms(0, 0, 0));
midnight.map(|datetime| Date::from_utc(*local, datetime.offset().clone()))
midnight.map(|datetime| Date::from_utc(*local, *datetime.offset()))
}
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Local>> {
let timespec = datetime_to_timespec(local, true);
LocalResult::Single(tm_to_datetime(stdtime::at(timespec)))
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
let mut tm = oldtime::at(timespec);
assert_eq!(tm.tm_nsec, 0);
tm.tm_nsec = local.nanosecond() as i32;
LocalResult::Single(tm_to_datetime(tm))
}
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
let midnight = self.from_utc_datetime(&utc.and_hms(0, 0, 0));
Date::from_utc(*utc, midnight.offset().clone())
Date::from_utc(*utc, *midnight.offset())
}
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Local> {
let timespec = datetime_to_timespec(utc, false);
tm_to_datetime(stdtime::at(timespec))
// datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
let mut tm = oldtime::at(timespec);
assert_eq!(tm.tm_nsec, 0);
tm.tm_nsec = utc.nanosecond() as i32;
tm_to_datetime(tm)
}
}
#[cfg(test)]
mod tests {
use Datelike;
use offset::TimeZone;
use super::Local;
#[test]
fn test_local_date_sanity_check() { // issue #27
assert_eq!(Local.ymd(2999, 12, 28).day(), 28);
}
#[test]
fn test_leap_second() { // issue #123
let today = Local::today();
let dt = today.and_hms_milli(1, 2, 59, 1000);
let timestr = dt.time().to_string();
// the OS API may or may not support the leap second,
// but there are only two sensible options.
assert!(timestr == "01:02:60" || timestr == "01:03:00",
"unexpected timestr {:?}", timestr);
let dt = today.and_hms_milli(1, 2, 3, 1234);
let timestr = dt.time().to_string();
assert!(timestr == "01:02:03.234" || timestr == "01:02:04.234",
"unexpected timestr {:?}", timestr);
}
}

View File

@ -1,34 +1,28 @@
// This is a part of rust-chrono.
// Copyright (c) 2014-2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* The time zone, which calculates offsets from the local time to UTC.
*
* There are three operations provided by the `TimeZone` trait:
*
* 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
* 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
* 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
*
* 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
* 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
* which implements `Offset` (which then passed to `TimeZone` for actual implementations).
* Technically speaking `TimeZone` has a total knowledge about given timescale,
* but `Offset` is used as a cache to avoid the repeated conversion
* and provides implementations for 1 and 3.
* An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
*/
//! The time zone, which calculates offsets from the local time to UTC.
//!
//! There are four operations provided by the `TimeZone` trait:
//!
//! 1. Converting the local `NaiveDateTime` to `DateTime<Tz>`
//! 2. Converting the UTC `NaiveDateTime` to `DateTime<Tz>`
//! 3. Converting `DateTime<Tz>` to the local `NaiveDateTime`
//! 4. Constructing `DateTime<Tz>` objects from various offsets
//!
//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types.
//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type
//! which implements `Offset` (which then passed to `TimeZone` for actual implementations).
//! Technically speaking `TimeZone` has a total knowledge about given timescale,
//! but `Offset` is used as a cache to avoid the repeated conversion
//! and provides implementations for 1 and 3.
//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance.
use std::fmt;
use Weekday;
use duration::Duration;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use naive::{NaiveDate, NaiveTime, NaiveDateTime};
use {Date, DateTime};
use format::{parse, Parsed, ParseResult, StrftimeItems};
/// The conversion result from the local time to the timezone-aware datetime types.
@ -159,11 +153,14 @@ impl<T: fmt::Debug> LocalResult<T> {
/// The offset from the local time to UTC.
pub trait Offset: Sized + Clone + fmt::Debug {
/// Returns the offset from UTC to the local time stored.
fn local_minus_utc(&self) -> Duration;
/// Returns the fixed offset from UTC to the local time stored.
fn fix(&self) -> FixedOffset;
}
/// The time zone.
///
/// The methods here are the primarily constructors for [`Date`](../struct.Date.html) and
/// [`DateTime`](../struct.DateTime.html) types.
pub trait TimeZone: Sized + Clone {
/// An associated offset type.
/// This type is used to store the actual offset in date and time types.
@ -177,6 +174,14 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Panics on the out-of-range date, invalid month and/or day.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone};
///
/// assert_eq!(Utc.ymd(2015, 5, 15).to_string(), "2015-05-15UTC");
/// ~~~~
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
self.ymd_opt(year, month, day).unwrap()
}
@ -188,6 +193,15 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Returns `None` on the out-of-range date, invalid month and/or day.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, LocalResult, TimeZone};
///
/// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC");
/// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None);
/// ~~~~
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
match NaiveDate::from_ymd_opt(year, month, day) {
Some(d) => self.from_local_date(&d),
@ -202,6 +216,14 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Panics on the out-of-range date and/or invalid DOY.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone};
///
/// assert_eq!(Utc.yo(2015, 135).to_string(), "2015-05-15UTC");
/// ~~~~
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
self.yo_opt(year, ordinal).unwrap()
}
@ -229,6 +251,14 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date.
///
/// Panics on the out-of-range date and/or invalid week number.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, Weekday, TimeZone};
///
/// assert_eq!(Utc.isoywd(2015, 20, Weekday::Fri).to_string(), "2015-05-15UTC");
/// ~~~~
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
self.isoywd_opt(year, week, weekday).unwrap()
}
@ -253,7 +283,16 @@ pub trait TimeZone: Sized + Clone {
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
///
/// Panics on the out-of-range number of seconds and/or invalid nanosecond.
/// Panics on the out-of-range number of seconds and/or invalid nanosecond,
/// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt).
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone};
///
/// assert_eq!(Utc.timestamp(1431648000, 0).to_string(), "2015-05-15 00:00:00 UTC");
/// ~~~~
fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime<Self> {
self.timestamp_opt(secs, nsecs).unwrap()
}
@ -262,7 +301,8 @@ pub trait TimeZone: Sized + Clone {
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp")
/// and the number of nanoseconds since the last whole non-leap second.
///
/// Returns `None` on the out-of-range number of seconds and/or invalid nanosecond.
/// Returns `LocalResult::None` on out-of-range number of seconds and/or
/// invalid nanosecond, otherwise always returns `LocalResult::Single`.
fn timestamp_opt(&self, secs: i64, nsecs: u32) -> LocalResult<DateTime<Self>> {
match NaiveDateTime::from_timestamp_opt(secs, nsecs) {
Some(dt) => LocalResult::Single(self.from_utc_datetime(&dt)),
@ -270,9 +310,48 @@ pub trait TimeZone: Sized + Clone {
}
}
/// Makes a new `DateTime` from the number of non-leap milliseconds
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
///
/// Panics on out-of-range number of milliseconds for a non-panicking
/// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt).
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone};
///
/// assert_eq!(Utc.timestamp_millis(1431648000).timestamp(), 1431648);
/// ~~~~
fn timestamp_millis(&self, millis: i64) -> DateTime<Self> {
self.timestamp_millis_opt(millis).unwrap()
}
/// Makes a new `DateTime` from the number of non-leap milliseconds
/// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp").
///
///
/// Returns `LocalResult::None` on out-of-range number of milliseconds
/// and/or invalid nanosecond, otherwise always returns
/// `LocalResult::Single`.
///
/// # Example
///
/// ~~~~
/// use chrono::{Utc, TimeZone, LocalResult};
/// match Utc.timestamp_millis_opt(1431648000) {
/// LocalResult::Single(dt) => assert_eq!(dt.timestamp(), 1431648),
/// _ => panic!("Incorrect timestamp_millis"),
/// };
/// ~~~~
fn timestamp_millis_opt(&self, millis: i64) -> LocalResult<DateTime<Self>> {
let (secs, millis) = (millis / 1000, millis % 1000);
self.timestamp_opt(secs, millis as u32 * 1_000_000)
}
/// Parses a string with the specified format string and
/// returns a `DateTime` with the current offset.
/// See the [`format::strftime` module](../../format/strftime/index.html)
/// See the [`format::strftime` module](../format/strftime/index.html)
/// on the supported escape sequences.
///
/// If the format does not include offsets, the current offset is assumed;
@ -298,14 +377,15 @@ pub trait TimeZone: Sized + Clone {
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
self.offset_from_local_date(local).map(|offset| {
Date::from_utc(*local - offset.local_minus_utc(), offset)
// since FixedOffset is within +/- 1 day, the date is never affected
Date::from_utc(*local, offset)
})
}
/// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible.
fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<Self>> {
self.offset_from_local_datetime(local).map(|offset| {
DateTime::from_utc(*local - offset.local_minus_utc(), offset)
DateTime::from_utc(*local - offset.fix(), offset)
})
}
@ -318,17 +398,22 @@ pub trait TimeZone: Sized + Clone {
/// Converts the UTC `NaiveDate` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
Date::from_utc(utc.clone(), self.offset_from_utc_date(utc))
Date::from_utc(*utc, self.offset_from_utc_date(utc))
}
/// Converts the UTC `NaiveDateTime` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime<Self> {
DateTime::from_utc(utc.clone(), self.offset_from_utc_datetime(utc))
DateTime::from_utc(*utc, self.offset_from_utc_datetime(utc))
}
}
pub mod utc;
pub mod fixed;
pub mod local;
mod utc;
mod fixed;
#[cfg(feature="clock")]
mod local;
pub use self::utc::Utc;
pub use self::fixed::FixedOffset;
#[cfg(feature="clock")]
pub use self::local::Local;

View File

@ -1,64 +1,75 @@
// This is a part of rust-chrono.
// Copyright (c) 2015, Kang Seonghoon.
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
/*!
* The UTC (Coordinated Universal Time) time zone.
*/
//! The UTC (Coordinated Universal Time) time zone.
use std::fmt;
use stdtime;
#[cfg(feature="clock")]
use oldtime;
use duration::Duration;
use naive::date::NaiveDate;
use naive::datetime::NaiveDateTime;
use date::Date;
use datetime::DateTime;
use super::{TimeZone, Offset, LocalResult};
use naive::{NaiveDate, NaiveDateTime};
#[cfg(feature="clock")]
use {Date, DateTime};
use super::{TimeZone, Offset, LocalResult, FixedOffset};
/// The UTC time zone. This is the most efficient time zone when you don't need the local time.
/// It is also used as an offset (which is also a dummy type).
///
/// Using the [`TimeZone`](./trait.TimeZone.html) methods
/// on the UTC struct is the preferred way to construct `DateTime<Utc>`
/// instances.
///
/// # Example
///
/// ~~~~
/// use chrono::{DateTime, TimeZone, NaiveDateTime, Utc};
///
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
///
/// assert_eq!(Utc.timestamp(61, 0), dt);
/// assert_eq!(Utc.ymd(1970, 1, 1).and_hms(0, 1, 1), dt);
/// ~~~~
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
pub struct UTC;
pub struct Utc;
impl UTC {
#[cfg(feature="clock")]
impl Utc {
/// Returns a `Date` which corresponds to the current date.
pub fn today() -> Date<UTC> { UTC::now().date() }
pub fn today() -> Date<Utc> { Utc::now().date() }
/// Returns a `DateTime` which corresponds to the current date.
pub fn now() -> DateTime<UTC> {
let spec = stdtime::get_time();
pub fn now() -> DateTime<Utc> {
let spec = oldtime::get_time();
let naive = NaiveDateTime::from_timestamp(spec.sec, spec.nsec as u32);
DateTime::from_utc(naive, UTC)
DateTime::from_utc(naive, Utc)
}
}
impl TimeZone for UTC {
type Offset = UTC;
impl TimeZone for Utc {
type Offset = Utc;
fn from_offset(_state: &UTC) -> UTC { UTC }
fn from_offset(_state: &Utc) -> Utc { Utc }
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<UTC> {
LocalResult::Single(UTC)
fn offset_from_local_date(&self, _local: &NaiveDate) -> LocalResult<Utc> {
LocalResult::Single(Utc)
}
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<UTC> {
LocalResult::Single(UTC)
fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> LocalResult<Utc> {
LocalResult::Single(Utc)
}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> UTC { UTC }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> UTC { UTC}
fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc { Utc }
fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { Utc }
}
impl Offset for UTC {
fn local_minus_utc(&self) -> Duration { Duration::zero() }
impl Offset for Utc {
fn fix(&self) -> FixedOffset { FixedOffset::east(0) }
}
impl fmt::Debug for UTC {
impl fmt::Debug for Utc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Z") }
}
impl fmt::Display for UTC {
impl fmt::Display for Utc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "UTC") }
}

640
third_party/rust/chrono/src/oldtime.rs vendored Normal file
View File

@ -0,0 +1,640 @@
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Temporal quantification
use std::{fmt, i64};
use std::error::Error;
use std::ops::{Add, Sub, Mul, Div, Neg};
use std::time::Duration as StdDuration;
/// The number of nanoseconds in a microsecond.
const NANOS_PER_MICRO: i32 = 1000;
/// The number of nanoseconds in a millisecond.
const NANOS_PER_MILLI: i32 = 1000_000;
/// The number of nanoseconds in seconds.
const NANOS_PER_SEC: i32 = 1_000_000_000;
/// The number of microseconds per second.
const MICROS_PER_SEC: i64 = 1000_000;
/// The number of milliseconds per second.
const MILLIS_PER_SEC: i64 = 1000;
/// The number of seconds in a minute.
const SECS_PER_MINUTE: i64 = 60;
/// The number of seconds in an hour.
const SECS_PER_HOUR: i64 = 3600;
/// The number of (non-leap) seconds in days.
const SECS_PER_DAY: i64 = 86400;
/// The number of (non-leap) seconds in a week.
const SECS_PER_WEEK: i64 = 604800;
macro_rules! try_opt {
($e:expr) => (match $e { Some(v) => v, None => return None })
}
/// ISO 8601 time duration with nanosecond precision.
/// This also allows for the negative duration; see individual methods for details.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Duration {
secs: i64,
nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC
}
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
pub const MIN: Duration = Duration {
secs: i64::MIN / MILLIS_PER_SEC - 1,
nanos: NANOS_PER_SEC + (i64::MIN % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
pub const MAX: Duration = Duration {
secs: i64::MAX / MILLIS_PER_SEC,
nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI
};
impl Duration {
/// Makes a new `Duration` with given number of weeks.
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn weeks(weeks: i64) -> Duration {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of days.
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn days(days: i64) -> Duration {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of hours.
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn hours(hours: i64) -> Duration {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of minutes.
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
pub fn minutes(minutes: i64) -> Duration {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
Duration::seconds(secs)
}
/// Makes a new `Duration` with given number of seconds.
/// Panics when the duration is more than `i64::MAX` milliseconds
/// or less than `i64::MIN` milliseconds.
#[inline]
pub fn seconds(seconds: i64) -> Duration {
let d = Duration { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
panic!("Duration::seconds out of bounds");
}
d
}
/// Makes a new `Duration` with given number of milliseconds.
#[inline]
pub fn milliseconds(milliseconds: i64) -> Duration {
let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC);
let nanos = millis as i32 * NANOS_PER_MILLI;
Duration { secs: secs, nanos: nanos }
}
/// Makes a new `Duration` with given number of microseconds.
#[inline]
pub fn microseconds(microseconds: i64) -> Duration {
let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
let nanos = micros as i32 * NANOS_PER_MICRO;
Duration { secs: secs, nanos: nanos }
}
/// Makes a new `Duration` with given number of nanoseconds.
#[inline]
pub fn nanoseconds(nanos: i64) -> Duration {
let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64);
Duration { secs: secs, nanos: nanos as i32 }
}
/// Returns the total number of whole weeks in the duration.
#[inline]
pub fn num_weeks(&self) -> i64 {
self.num_days() / 7
}
/// Returns the total number of whole days in the duration.
pub fn num_days(&self) -> i64 {
self.num_seconds() / SECS_PER_DAY
}
/// Returns the total number of whole hours in the duration.
#[inline]
pub fn num_hours(&self) -> i64 {
self.num_seconds() / SECS_PER_HOUR
}
/// Returns the total number of whole minutes in the duration.
#[inline]
pub fn num_minutes(&self) -> i64 {
self.num_seconds() / SECS_PER_MINUTE
}
/// Returns the total number of whole seconds in the duration.
pub fn num_seconds(&self) -> i64 {
// If secs is negative, nanos should be subtracted from the duration.
if self.secs < 0 && self.nanos > 0 {
self.secs + 1
} else {
self.secs
}
}
/// Returns the number of nanoseconds such that
/// `nanos_mod_sec() + num_seconds() * NANOS_PER_SEC` is the total number of
/// nanoseconds in the duration.
fn nanos_mod_sec(&self) -> i32 {
if self.secs < 0 && self.nanos > 0 {
self.nanos - NANOS_PER_SEC
} else {
self.nanos
}
}
/// Returns the total number of whole milliseconds in the duration,
pub fn num_milliseconds(&self) -> i64 {
// A proper Duration will not overflow, because MIN and MAX are defined
// such that the range is exactly i64 milliseconds.
let secs_part = self.num_seconds() * MILLIS_PER_SEC;
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MILLI;
secs_part + nanos_part as i64
}
/// Returns the total number of whole microseconds in the duration,
/// or `None` on overflow (exceeding 2^63 microseconds in either direction).
pub fn num_microseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC));
let nanos_part = self.nanos_mod_sec() / NANOS_PER_MICRO;
secs_part.checked_add(nanos_part as i64)
}
/// Returns the total number of whole nanoseconds in the duration,
/// or `None` on overflow (exceeding 2^63 nanoseconds in either direction).
pub fn num_nanoseconds(&self) -> Option<i64> {
let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64));
let nanos_part = self.nanos_mod_sec();
secs_part.checked_add(nanos_part as i64)
}
/// Add two durations, returning `None` if overflow occurred.
pub fn checked_add(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs = try_opt!(secs.checked_add(1));
}
let d = Duration { secs: secs, nanos: nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX { None } else { Some(d) }
}
/// Subtract two durations, returning `None` if overflow occurred.
pub fn checked_sub(&self, rhs: &Duration) -> Option<Duration> {
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
let mut nanos = self.nanos - rhs.nanos;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs = try_opt!(secs.checked_sub(1));
}
let d = Duration { secs: secs, nanos: nanos };
// Even if d is within the bounds of i64 seconds,
// it might still overflow i64 milliseconds.
if d < MIN || d > MAX { None } else { Some(d) }
}
/// The minimum possible `Duration`: `i64::MIN` milliseconds.
#[inline]
pub fn min_value() -> Duration { MIN }
/// The maximum possible `Duration`: `i64::MAX` milliseconds.
#[inline]
pub fn max_value() -> Duration { MAX }
/// A duration where the stored seconds and nanoseconds are equal to zero.
#[inline]
pub fn zero() -> Duration {
Duration { secs: 0, nanos: 0 }
}
/// Returns `true` if the duration equals `Duration::zero()`.
#[inline]
pub fn is_zero(&self) -> bool {
self.secs == 0 && self.nanos == 0
}
/// Creates a `time::Duration` object from `std::time::Duration`
///
/// This function errors when original duration is larger than the maximum
/// value supported for this type.
pub fn from_std(duration: StdDuration) -> Result<Duration, OutOfRangeError> {
// We need to check secs as u64 before coercing to i64
if duration.as_secs() > MAX.secs as u64 {
return Err(OutOfRangeError(()));
}
let d = Duration {
secs: duration.as_secs() as i64,
nanos: duration.subsec_nanos() as i32,
};
if d > MAX {
return Err(OutOfRangeError(()));
}
Ok(d)
}
/// Creates a `std::time::Duration` object from `time::Duration`
///
/// This function errors when duration is less than zero. As standard
/// library implementation is limited to non-negative values.
pub fn to_std(&self) -> Result<StdDuration, OutOfRangeError> {
if self.secs < 0 {
return Err(OutOfRangeError(()));
}
Ok(StdDuration::new(self.secs as u64, self.nanos as u32))
}
}
impl Neg for Duration {
type Output = Duration;
#[inline]
fn neg(self) -> Duration {
if self.nanos == 0 {
Duration { secs: -self.secs, nanos: 0 }
} else {
Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos }
}
}
}
impl Add for Duration {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
let mut secs = self.secs + rhs.secs;
let mut nanos = self.nanos + rhs.nanos;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs += 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl Sub for Duration {
type Output = Duration;
fn sub(self, rhs: Duration) -> Duration {
let mut secs = self.secs - rhs.secs;
let mut nanos = self.nanos - rhs.nanos;
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl Mul<i32> for Duration {
type Output = Duration;
fn mul(self, rhs: i32) -> Duration {
// Multiply nanoseconds as i64, because it cannot overflow that way.
let total_nanos = self.nanos as i64 * rhs as i64;
let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64);
let secs = self.secs * rhs as i64 + extra_secs;
Duration { secs: secs, nanos: nanos as i32 }
}
}
impl Div<i32> for Duration {
type Output = Duration;
fn div(self, rhs: i32) -> Duration {
let mut secs = self.secs / rhs as i64;
let carry = self.secs - secs * rhs as i64;
let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64;
let mut nanos = self.nanos / rhs + extra_nanos as i32;
if nanos >= NANOS_PER_SEC {
nanos -= NANOS_PER_SEC;
secs += 1;
}
if nanos < 0 {
nanos += NANOS_PER_SEC;
secs -= 1;
}
Duration { secs: secs, nanos: nanos }
}
}
impl fmt::Display for Duration {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// technically speaking, negative duration is not valid ISO 8601,
// but we need to print it anyway.
let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") };
let days = abs.secs / SECS_PER_DAY;
let secs = abs.secs - days * SECS_PER_DAY;
let hasdate = days != 0;
let hastime = (secs != 0 || abs.nanos != 0) || !hasdate;
try!(write!(f, "{}P", sign));
if hasdate {
try!(write!(f, "{}D", days));
}
if hastime {
if abs.nanos == 0 {
try!(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));
} else if abs.nanos % NANOS_PER_MICRO == 0 {
try!(write!(f, "T{}.{:06}S", secs, abs.nanos / NANOS_PER_MICRO));
} else {
try!(write!(f, "T{}.{:09}S", secs, abs.nanos));
}
}
Ok(())
}
}
/// Represents error when converting `Duration` to/from a standard library
/// implementation
///
/// The `std::time::Duration` supports a range from zero to `u64::MAX`
/// *seconds*, while this module supports signed range of up to
/// `i64::MAX` of *milliseconds*.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OutOfRangeError(());
impl fmt::Display for OutOfRangeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl Error for OutOfRangeError {
fn description(&self) -> &str {
"Source duration value is out of range for the target type"
}
}
// Copied from libnum
#[inline]
fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
(div_floor_64(this, other), mod_floor_64(this, other))
}
#[inline]
fn div_floor_64(this: i64, other: i64) -> i64 {
match div_rem_64(this, other) {
(d, r) if (r > 0 && other < 0)
|| (r < 0 && other > 0) => d - 1,
(d, _) => d,
}
}
#[inline]
fn mod_floor_64(this: i64, other: i64) -> i64 {
match this % other {
r if (r > 0 && other < 0)
|| (r < 0 && other > 0) => r + other,
r => r,
}
}
#[inline]
fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other)
}
#[cfg(test)]
mod tests {
use super::{Duration, MIN, MAX, OutOfRangeError};
use std::{i32, i64};
use std::time::Duration as StdDuration;
#[test]
fn test_duration() {
assert!(Duration::seconds(1) != Duration::zero());
assert_eq!(Duration::seconds(1) + Duration::seconds(2), Duration::seconds(3));
assert_eq!(Duration::seconds(86399) + Duration::seconds(4),
Duration::days(1) + Duration::seconds(3));
assert_eq!(Duration::days(10) - Duration::seconds(1000), Duration::seconds(863000));
assert_eq!(Duration::days(10) - Duration::seconds(1000000), Duration::seconds(-136000));
assert_eq!(Duration::days(2) + Duration::seconds(86399) +
Duration::nanoseconds(1234567890),
Duration::days(3) + Duration::nanoseconds(234567890));
assert_eq!(-Duration::days(3), Duration::days(-3));
assert_eq!(-(Duration::days(3) + Duration::seconds(70)),
Duration::days(-4) + Duration::seconds(86400-70));
}
#[test]
fn test_duration_num_days() {
assert_eq!(Duration::zero().num_days(), 0);
assert_eq!(Duration::days(1).num_days(), 1);
assert_eq!(Duration::days(-1).num_days(), -1);
assert_eq!(Duration::seconds(86399).num_days(), 0);
assert_eq!(Duration::seconds(86401).num_days(), 1);
assert_eq!(Duration::seconds(-86399).num_days(), 0);
assert_eq!(Duration::seconds(-86401).num_days(), -1);
assert_eq!(Duration::days(i32::MAX as i64).num_days(), i32::MAX as i64);
assert_eq!(Duration::days(i32::MIN as i64).num_days(), i32::MIN as i64);
}
#[test]
fn test_duration_num_seconds() {
assert_eq!(Duration::zero().num_seconds(), 0);
assert_eq!(Duration::seconds(1).num_seconds(), 1);
assert_eq!(Duration::seconds(-1).num_seconds(), -1);
assert_eq!(Duration::milliseconds(999).num_seconds(), 0);
assert_eq!(Duration::milliseconds(1001).num_seconds(), 1);
assert_eq!(Duration::milliseconds(-999).num_seconds(), 0);
assert_eq!(Duration::milliseconds(-1001).num_seconds(), -1);
}
#[test]
fn test_duration_num_milliseconds() {
assert_eq!(Duration::zero().num_milliseconds(), 0);
assert_eq!(Duration::milliseconds(1).num_milliseconds(), 1);
assert_eq!(Duration::milliseconds(-1).num_milliseconds(), -1);
assert_eq!(Duration::microseconds(999).num_milliseconds(), 0);
assert_eq!(Duration::microseconds(1001).num_milliseconds(), 1);
assert_eq!(Duration::microseconds(-999).num_milliseconds(), 0);
assert_eq!(Duration::microseconds(-1001).num_milliseconds(), -1);
assert_eq!(Duration::milliseconds(i64::MAX).num_milliseconds(), i64::MAX);
assert_eq!(Duration::milliseconds(i64::MIN).num_milliseconds(), i64::MIN);
assert_eq!(MAX.num_milliseconds(), i64::MAX);
assert_eq!(MIN.num_milliseconds(), i64::MIN);
}
#[test]
fn test_duration_num_microseconds() {
assert_eq!(Duration::zero().num_microseconds(), Some(0));
assert_eq!(Duration::microseconds(1).num_microseconds(), Some(1));
assert_eq!(Duration::microseconds(-1).num_microseconds(), Some(-1));
assert_eq!(Duration::nanoseconds(999).num_microseconds(), Some(0));
assert_eq!(Duration::nanoseconds(1001).num_microseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-999).num_microseconds(), Some(0));
assert_eq!(Duration::nanoseconds(-1001).num_microseconds(), Some(-1));
assert_eq!(Duration::microseconds(i64::MAX).num_microseconds(), Some(i64::MAX));
assert_eq!(Duration::microseconds(i64::MIN).num_microseconds(), Some(i64::MIN));
assert_eq!(MAX.num_microseconds(), None);
assert_eq!(MIN.num_microseconds(), None);
// overflow checks
const MICROS_PER_DAY: i64 = 86400_000_000;
assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY).num_microseconds(),
Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY).num_microseconds(),
Some(i64::MIN / MICROS_PER_DAY * MICROS_PER_DAY));
assert_eq!(Duration::days(i64::MAX / MICROS_PER_DAY + 1).num_microseconds(), None);
assert_eq!(Duration::days(i64::MIN / MICROS_PER_DAY - 1).num_microseconds(), None);
}
#[test]
fn test_duration_num_nanoseconds() {
assert_eq!(Duration::zero().num_nanoseconds(), Some(0));
assert_eq!(Duration::nanoseconds(1).num_nanoseconds(), Some(1));
assert_eq!(Duration::nanoseconds(-1).num_nanoseconds(), Some(-1));
assert_eq!(Duration::nanoseconds(i64::MAX).num_nanoseconds(), Some(i64::MAX));
assert_eq!(Duration::nanoseconds(i64::MIN).num_nanoseconds(), Some(i64::MIN));
assert_eq!(MAX.num_nanoseconds(), None);
assert_eq!(MIN.num_nanoseconds(), None);
// overflow checks
const NANOS_PER_DAY: i64 = 86400_000_000_000;
assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY).num_nanoseconds(),
Some(i64::MIN / NANOS_PER_DAY * NANOS_PER_DAY));
assert_eq!(Duration::days(i64::MAX / NANOS_PER_DAY + 1).num_nanoseconds(), None);
assert_eq!(Duration::days(i64::MIN / NANOS_PER_DAY - 1).num_nanoseconds(), None);
}
#[test]
fn test_duration_checked_ops() {
assert_eq!(Duration::milliseconds(i64::MAX - 1).checked_add(&Duration::microseconds(999)),
Some(Duration::milliseconds(i64::MAX - 2) + Duration::microseconds(1999)));
assert!(Duration::milliseconds(i64::MAX).checked_add(&Duration::microseconds(1000))
.is_none());
assert_eq!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(0)),
Some(Duration::milliseconds(i64::MIN)));
assert!(Duration::milliseconds(i64::MIN).checked_sub(&Duration::milliseconds(1))
.is_none());
}
#[test]
fn test_duration_mul() {
assert_eq!(Duration::zero() * i32::MAX, Duration::zero());
assert_eq!(Duration::zero() * i32::MIN, Duration::zero());
assert_eq!(Duration::nanoseconds(1) * 0, Duration::zero());
assert_eq!(Duration::nanoseconds(1) * 1, Duration::nanoseconds(1));
assert_eq!(Duration::nanoseconds(1) * 1_000_000_000, Duration::seconds(1));
assert_eq!(Duration::nanoseconds(1) * -1_000_000_000, -Duration::seconds(1));
assert_eq!(-Duration::nanoseconds(1) * 1_000_000_000, -Duration::seconds(1));
assert_eq!(Duration::nanoseconds(30) * 333_333_333,
Duration::seconds(10) - Duration::nanoseconds(10));
assert_eq!((Duration::nanoseconds(1) + Duration::seconds(1) + Duration::days(1)) * 3,
Duration::nanoseconds(3) + Duration::seconds(3) + Duration::days(3));
assert_eq!(Duration::milliseconds(1500) * -2, Duration::seconds(-3));
assert_eq!(Duration::milliseconds(-1500) * 2, Duration::seconds(-3));
}
#[test]
fn test_duration_div() {
assert_eq!(Duration::zero() / i32::MAX, Duration::zero());
assert_eq!(Duration::zero() / i32::MIN, Duration::zero());
assert_eq!(Duration::nanoseconds(123_456_789) / 1, Duration::nanoseconds(123_456_789));
assert_eq!(Duration::nanoseconds(123_456_789) / -1, -Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / -1, Duration::nanoseconds(123_456_789));
assert_eq!(-Duration::nanoseconds(123_456_789) / 1, -Duration::nanoseconds(123_456_789));
assert_eq!(Duration::seconds(1) / 3, Duration::nanoseconds(333_333_333));
assert_eq!(Duration::seconds(4) / 3, Duration::nanoseconds(1_333_333_333));
assert_eq!(Duration::seconds(-1) / 2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(1) / -2, Duration::milliseconds(-500));
assert_eq!(Duration::seconds(-1) / -2, Duration::milliseconds(500));
assert_eq!(Duration::seconds(-4) / 3, Duration::nanoseconds(-1_333_333_333));
assert_eq!(Duration::seconds(-4) / -3, Duration::nanoseconds(1_333_333_333));
}
#[test]
fn test_duration_fmt() {
assert_eq!(Duration::zero().to_string(), "PT0S");
assert_eq!(Duration::days(42).to_string(), "P42D");
assert_eq!(Duration::days(-42).to_string(), "-P42D");
assert_eq!(Duration::seconds(42).to_string(), "PT42S");
assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S");
assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S");
assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S");
assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(),
"P7DT6.543S");
assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S");
assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S");
// the format specifier should have no effect on `Duration`
assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)),
"P1DT2.345S");
}
#[test]
fn test_to_std() {
assert_eq!(Duration::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
assert_eq!(Duration::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0)));
assert_eq!(Duration::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
assert_eq!(Duration::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
assert_eq!(Duration::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000)));
assert_eq!(Duration::seconds(-1).to_std(),
Err(OutOfRangeError(())));
assert_eq!(Duration::milliseconds(-1).to_std(),
Err(OutOfRangeError(())));
}
#[test]
fn test_from_std() {
assert_eq!(Ok(Duration::seconds(1)),
Duration::from_std(StdDuration::new(1, 0)));
assert_eq!(Ok(Duration::seconds(86401)),
Duration::from_std(StdDuration::new(86401, 0)));
assert_eq!(Ok(Duration::milliseconds(123)),
Duration::from_std(StdDuration::new(0, 123000000)));
assert_eq!(Ok(Duration::milliseconds(123765)),
Duration::from_std(StdDuration::new(123, 765000000)));
assert_eq!(Ok(Duration::nanoseconds(777)),
Duration::from_std(StdDuration::new(0, 777)));
assert_eq!(Ok(MAX),
Duration::from_std(StdDuration::new(9223372036854775, 807000000)));
assert_eq!(Duration::from_std(StdDuration::new(9223372036854776, 0)),
Err(OutOfRangeError(())));
assert_eq!(Duration::from_std(StdDuration::new(9223372036854775, 807000001)),
Err(OutOfRangeError(())));
}
}

178
third_party/rust/chrono/src/round.rs vendored Normal file
View File

@ -0,0 +1,178 @@
// This is a part of Chrono.
// See README.md and LICENSE.txt for details.
use Timelike;
use std::ops::{Add, Sub};
use oldtime::Duration;
/// Extension trait for subsecond rounding or truncation to a maximum number
/// of digits. Rounding can be used to decrease the error variance when
/// serializing/persisting to lower precision. Truncation is the default
/// behavior in Chrono display formatting. Either can be used to guarantee
/// equality (e.g. for testing) when round-tripping through a lower precision
/// format.
pub trait SubsecRound {
/// Return a copy rounded to the specified number of subsecond digits. With
/// 9 or more digits, self is returned unmodified. Halfway values are
/// rounded up (away from zero).
///
/// # Example
/// ``` rust
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
/// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000);
/// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000);
/// ```
fn round_subsecs(self, digits: u16) -> Self;
/// Return a copy truncated to the specified number of subsecond
/// digits. With 9 or more digits, self is returned unmodified.
///
/// # Example
/// ``` rust
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc};
/// let dt = Utc.ymd(2018, 1, 11).and_hms_milli(12, 0, 0, 154);
/// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000);
/// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000);
/// ```
fn trunc_subsecs(self, digits: u16) -> Self;
}
impl<T> SubsecRound for T
where T: Timelike + Add<Duration, Output=T> + Sub<Duration, Output=T>
{
fn round_subsecs(self, digits: u16) -> T {
let span = span_for_digits(digits);
let delta_down = self.nanosecond() % span;
if delta_down > 0 {
let delta_up = span - delta_down;
if delta_up <= delta_down {
self + Duration::nanoseconds(delta_up.into())
} else {
self - Duration::nanoseconds(delta_down.into())
}
} else {
self // unchanged
}
}
fn trunc_subsecs(self, digits: u16) -> T {
let span = span_for_digits(digits);
let delta_down = self.nanosecond() % span;
if delta_down > 0 {
self - Duration::nanoseconds(delta_down.into())
} else {
self // unchanged
}
}
}
// Return the maximum span in nanoseconds for the target number of digits.
fn span_for_digits(digits: u16) -> u32 {
// fast lookup form of: 10^(9-min(9,digits))
match digits {
0 => 1_000_000_000,
1 => 100_000_000,
2 => 10_000_000,
3 => 1_000_000,
4 => 100_000,
5 => 10_000,
6 => 1_000,
7 => 100,
8 => 10,
_ => 1
}
}
#[cfg(test)]
mod tests {
use Timelike;
use offset::{FixedOffset, TimeZone, Utc};
use super::SubsecRound;
#[test]
fn test_round() {
let pst = FixedOffset::east(8 * 60 * 60);
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
assert_eq!(dt.round_subsecs(10), dt);
assert_eq!(dt.round_subsecs(9), dt);
assert_eq!(dt.round_subsecs(8).nanosecond(), 084_660_680);
assert_eq!(dt.round_subsecs(7).nanosecond(), 084_660_700);
assert_eq!(dt.round_subsecs(6).nanosecond(), 084_661_000);
assert_eq!(dt.round_subsecs(5).nanosecond(), 084_660_000);
assert_eq!(dt.round_subsecs(4).nanosecond(), 084_700_000);
assert_eq!(dt.round_subsecs(3).nanosecond(), 085_000_000);
assert_eq!(dt.round_subsecs(2).nanosecond(), 080_000_000);
assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000);
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
assert_eq!(dt.round_subsecs(0).second(), 13);
let dt = Utc.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
assert_eq!(dt.round_subsecs(9), dt);
assert_eq!(dt.round_subsecs(4), dt);
assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000);
assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000);
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
assert_eq!(dt.round_subsecs(0).second(), 28);
}
#[test]
fn test_round_leap_nanos() {
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
assert_eq!(dt.round_subsecs(9), dt);
assert_eq!(dt.round_subsecs(4), dt);
assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000);
assert_eq!(dt.round_subsecs(1).second(), 59);
assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
assert_eq!(dt.round_subsecs(0).second(), 0);
}
#[test]
fn test_trunc() {
let pst = FixedOffset::east(8 * 60 * 60);
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 13, 084_660_684);
assert_eq!(dt.trunc_subsecs(10), dt);
assert_eq!(dt.trunc_subsecs(9), dt);
assert_eq!(dt.trunc_subsecs(8).nanosecond(), 084_660_680);
assert_eq!(dt.trunc_subsecs(7).nanosecond(), 084_660_600);
assert_eq!(dt.trunc_subsecs(6).nanosecond(), 084_660_000);
assert_eq!(dt.trunc_subsecs(5).nanosecond(), 084_660_000);
assert_eq!(dt.trunc_subsecs(4).nanosecond(), 084_600_000);
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 084_000_000);
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 080_000_000);
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0);
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
assert_eq!(dt.trunc_subsecs(0).second(), 13);
let dt = pst.ymd(2018, 1, 11).and_hms_nano(10, 5, 27, 750_500_000);
assert_eq!(dt.trunc_subsecs(9), dt);
assert_eq!(dt.trunc_subsecs(4), dt);
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000);
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000);
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
assert_eq!(dt.trunc_subsecs(0).second(), 27);
}
#[test]
fn test_trunc_leap_nanos() {
let dt = Utc.ymd(2016, 12, 31).and_hms_nano(23, 59, 59, 1_750_500_000);
assert_eq!(dt.trunc_subsecs(9), dt);
assert_eq!(dt.trunc_subsecs(4), dt);
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000);
assert_eq!(dt.trunc_subsecs(1).second(), 59);
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000);
assert_eq!(dt.trunc_subsecs(0).second(), 59);
}
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"0d6e35c432fe662ef7dc142493f41cf4cc4ba31d00b199b4574310b3fcdd123f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","src/lib.rs":"8526b07deb77957ee84ac0ccbb7b30d7570a44dbf3bc06c5db36debce7516163"},"package":"d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"}
{"files":{".travis.yml":"49df6cd8acc5622158f32a21ed04e9386b1d9d69990824b9ca62e9f7d8821f60","Cargo.toml":"f491aec76f2252ca15a333dd3cfd18cfd91ebf841c7d607857dfa75c7080cb9a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"7ad5fb625f7ef61595a2180f3b26715457552faa8bb08526c70b416da29b2533","RELEASES.md":"ebe114c148e4fc43b63c2523e13e9d903f07db139ab70f48320f9cb8c17ac9d8","benches/roots.rs":"df3554c0025d78235b5dca975b641f3bb10cae8d5ad3a79eb6cbd1a21475f133","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","build.rs":"16de2aa57e754fc1526d0400b5d87a3f771296705fca54601aa598b6f74ded8f","ci/rustup.sh":"2aa9e89e4af81ed9da86bdcf7cdabe512287c877248783b69eed1eccf09ad6bb","ci/test_full.sh":"fd4928a73c13905d939d009801bd448a7a9d2ca00a30260eedd1feb03fc88e11","src/lib.rs":"1e19a19aa0d414d7548e4bc9510f89bed122430564e044302edd4a21a1b83134","src/roots.rs":"51a994a5e0bf505911cf912954283f52e7aa582ce0cd1c483e6b2e4c09a47b9e","tests/roots.rs":"ef70f711cb1544311c343dbaf411ad2598432e82b6dfa3d166c1d99096991d9e"},"package":"e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"}

View File

@ -2,18 +2,14 @@ language: rust
rust:
- 1.8.0
- 1.15.0
- 1.20.0
- stable
- beta
- nightly
sudo: false
script:
- cargo build --verbose
- ./ci/test_full.sh
- cargo doc
after_success: |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
[ $TRAVIS_RUST_VERSION = nightly ] &&
ssh-agent ./ci/deploy.sh
notifications:
email:
on_success: never

View File

@ -1,15 +1,35 @@
[package]
authors = ["The Rust Project Developers"]
description = "Integer traits and functions"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics"]
categories = [ "algorithms", "science" ]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num"
name = "num-integer"
version = "0.1.35"
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "num-integer"
version = "0.1.39"
authors = ["The Rust Project Developers"]
build = "build.rs"
description = "Integer traits and functions"
homepage = "https://github.com/rust-num/num-integer"
documentation = "https://docs.rs/num-integer"
readme = "README.md"
keywords = ["mathematics", "numerics"]
categories = ["algorithms", "science", "no-std"]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num-integer"
[package.metadata.docs.rs]
features = ["std"]
[dependencies.num-traits]
path = "../traits"
version = "0.1.32"
version = "0.2.4"
default-features = false
[features]
default = ["std"]
i128 = ["num-traits/i128"]
std = ["num-traits/std"]

50
third_party/rust/num-integer/README.md vendored Normal file
View File

@ -0,0 +1,50 @@
# num-integer
[![crate](https://img.shields.io/crates/v/num-integer.svg)](https://crates.io/crates/num-integer)
[![documentation](https://docs.rs/num-integer/badge.svg)](https://docs.rs/num-integer)
![minimum rustc 1.8](https://img.shields.io/badge/rustc-1.8+-red.svg)
[![Travis status](https://travis-ci.org/rust-num/num-integer.svg?branch=master)](https://travis-ci.org/rust-num/num-integer)
`Integer` trait and functions for Rust.
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
num-integer = "0.1"
```
and this to your crate root:
```rust
extern crate num_integer;
```
## Features
This crate can be used without the standard library (`#![no_std]`) by disabling
the default `std` feature. Use this in `Cargo.toml`:
```toml
[dependencies.num-integer]
version = "0.1.36"
default-features = false
```
There is no functional difference with and without `std` at this time, but
there may be in the future.
Implementations for `i128` and `u128` are only available with Rust 1.26 and
later. The build script automatically detects this, but you can make it
mandatory by enabling the `i128` crate feature.
## Releases
Release notes are available in [RELEASES.md](RELEASES.md).
## Compatibility
The `num-integer` crate is tested for rustc 1.8 and greater.

View File

@ -0,0 +1,49 @@
# Release 0.1.39
- [The new `Roots` trait provides `sqrt`, `cbrt`, and `nth_root` methods][9],
calculating an `Integer`'s principal roots rounded toward zero.
**Contributors**: @cuviper
[9]: https://github.com/rust-num/num-integer/pull/9
# Release 0.1.38
- [Support for 128-bit integers is now automatically detected and enabled.][8]
Setting the `i128` crate feature now causes the build script to panic if such
support is not detected.
**Contributors**: @cuviper
[8]: https://github.com/rust-num/num-integer/pull/8
# Release 0.1.37
- [`Integer` is now implemented for `i128` and `u128`][7] starting with Rust
1.26, enabled by the new `i128` crate feature.
**Contributors**: @cuviper
[7]: https://github.com/rust-num/num-integer/pull/7
# Release 0.1.36
- [num-integer now has its own source repository][num-356] at [rust-num/num-integer][home].
- [Corrected the argument order documented in `Integer::is_multiple_of`][1]
- [There is now a `std` feature][5], enabled by default, along with the implication
that building *without* this feature makes this a `#[no_std]` crate.
- There is no difference in the API at this time.
**Contributors**: @cuviper, @jaystrictor
[home]: https://github.com/rust-num/num-integer
[num-356]: https://github.com/rust-num/num/pull/356
[1]: https://github.com/rust-num/num-integer/pull/1
[5]: https://github.com/rust-num/num-integer/pull/5
# Prior releases
No prior release notes were kept. Thanks all the same to the many
contributors that have made this crate what it is!

View File

@ -0,0 +1,174 @@
//! Benchmark sqrt and cbrt
#![feature(test)]
extern crate num_integer;
extern crate num_traits;
extern crate test;
use num_integer::Integer;
use num_traits::checked_pow;
use num_traits::{AsPrimitive, PrimInt, WrappingAdd, WrappingMul};
use test::{black_box, Bencher};
trait BenchInteger: Integer + PrimInt + WrappingAdd + WrappingMul + 'static {}
impl<T> BenchInteger for T
where
T: Integer + PrimInt + WrappingAdd + WrappingMul + 'static,
{
}
fn bench<T, F>(b: &mut Bencher, v: &[T], f: F, n: u32)
where
T: BenchInteger,
F: Fn(&T) -> T,
{
// Pre-validate the results...
for i in v {
let rt = f(i);
if *i >= T::zero() {
let rt1 = rt + T::one();
assert!(rt.pow(n) <= *i);
if let Some(x) = checked_pow(rt1, n as usize) {
assert!(*i < x);
}
} else {
let rt1 = rt - T::one();
assert!(rt < T::zero());
assert!(*i <= rt.pow(n));
if let Some(x) = checked_pow(rt1, n as usize) {
assert!(x < *i);
}
};
}
// Now just run as fast as we can!
b.iter(|| {
for i in v {
black_box(f(i));
}
});
}
// Simple PRNG so we don't have to worry about rand compatibility
fn lcg<T>(x: T) -> T
where
u32: AsPrimitive<T>,
T: BenchInteger,
{
// LCG parameters from Numerical Recipes
// (but we're applying it to arbitrary sizes)
const LCG_A: u32 = 1664525;
const LCG_C: u32 = 1013904223;
x.wrapping_mul(&LCG_A.as_()).wrapping_add(&LCG_C.as_())
}
fn bench_rand<T, F>(b: &mut Bencher, f: F, n: u32)
where
u32: AsPrimitive<T>,
T: BenchInteger,
F: Fn(&T) -> T,
{
let mut x: T = 3u32.as_();
let v: Vec<T> = (0..1000)
.map(|_| {
x = lcg(x);
x
})
.collect();
bench(b, &v, f, n);
}
fn bench_rand_pos<T, F>(b: &mut Bencher, f: F, n: u32)
where
u32: AsPrimitive<T>,
T: BenchInteger,
F: Fn(&T) -> T,
{
let mut x: T = 3u32.as_();
let v: Vec<T> = (0..1000)
.map(|_| {
x = lcg(x);
while x < T::zero() {
x = lcg(x);
}
x
})
.collect();
bench(b, &v, f, n);
}
fn bench_small<T, F>(b: &mut Bencher, f: F, n: u32)
where
u32: AsPrimitive<T>,
T: BenchInteger,
F: Fn(&T) -> T,
{
let v: Vec<T> = (0..1000).map(|i| i.as_()).collect();
bench(b, &v, f, n);
}
fn bench_small_pos<T, F>(b: &mut Bencher, f: F, n: u32)
where
u32: AsPrimitive<T>,
T: BenchInteger,
F: Fn(&T) -> T,
{
let v: Vec<T> = (0..1000)
.map(|i| i.as_().mod_floor(&T::max_value()))
.collect();
bench(b, &v, f, n);
}
macro_rules! bench_roots {
($($T:ident),*) => {$(
mod $T {
use test::Bencher;
use num_integer::Roots;
#[bench]
fn sqrt_rand(b: &mut Bencher) {
::bench_rand_pos(b, $T::sqrt, 2);
}
#[bench]
fn sqrt_small(b: &mut Bencher) {
::bench_small_pos(b, $T::sqrt, 2);
}
#[bench]
fn cbrt_rand(b: &mut Bencher) {
::bench_rand(b, $T::cbrt, 3);
}
#[bench]
fn cbrt_small(b: &mut Bencher) {
::bench_small(b, $T::cbrt, 3);
}
#[bench]
fn fourth_root_rand(b: &mut Bencher) {
::bench_rand_pos(b, |x: &$T| x.nth_root(4), 4);
}
#[bench]
fn fourth_root_small(b: &mut Bencher) {
::bench_small_pos(b, |x: &$T| x.nth_root(4), 4);
}
#[bench]
fn fifth_root_rand(b: &mut Bencher) {
::bench_rand(b, |x: &$T| x.nth_root(5), 5);
}
#[bench]
fn fifth_root_small(b: &mut Bencher) {
::bench_small(b, |x: &$T| x.nth_root(5), 5);
}
}
)*}
}
bench_roots!(i8, i16, i32, i64, i128);
bench_roots!(u8, u16, u32, u64, u128);

35
third_party/rust/num-integer/build.rs vendored Normal file
View File

@ -0,0 +1,35 @@
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() {
if probe("fn main() { 0i128; }") {
println!("cargo:rustc-cfg=has_i128");
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}
}
/// Test if a code snippet can be compiled
fn probe(code: &str) -> bool {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
let mut child = Command::new(rustc)
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()
.expect("rustc probe");
child
.stdin
.as_mut()
.expect("rustc stdin")
.write_all(code.as_bytes())
.expect("write rustc stdin");
child.wait().expect("rustc probe").success()
}

12
third_party/rust/num-integer/ci/rustup.sh vendored Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
# Use rustup to locally run the same suite of tests as .travis.yml.
# (You should first install/update 1.8.0, stable, beta, and nightly.)
set -ex
export TRAVIS_RUST_VERSION
for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
run="rustup run $TRAVIS_RUST_VERSION"
$run cargo build --verbose
$run $PWD/ci/test_full.sh
done

23
third_party/rust/num-integer/ci/test_full.sh vendored Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
set -ex
echo Testing num-integer on rustc ${TRAVIS_RUST_VERSION}
# num-integer should build and test everywhere.
cargo build --verbose
cargo test --verbose
# test `no_std`
cargo build --verbose --no-default-features
cargo test --verbose --no-default-features
# test `i128`
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
cargo build --verbose --features=i128
cargo test --verbose --features=i128
fi
if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then
cargo test --verbose --all-features --benches
fi

View File

@ -9,17 +9,28 @@
// except according to those terms.
//! Integer trait and functions.
#![doc(html_logo_url = "https://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://rust-num.github.io/num/favicon.ico",
html_root_url = "https://rust-num.github.io/num/",
html_playground_url = "http://play.integer32.com/")]
//!
//! ## Compatibility
//!
//! The `num-integer` crate is tested for rustc 1.8 and greater.
#![doc(html_root_url = "https://docs.rs/num-integer/0.1")]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
extern crate num_traits as traits;
use std::ops::Add;
use core::ops::Add;
use core::mem;
use traits::{Num, Signed};
mod roots;
pub use roots::Roots;
pub use roots::{sqrt, cbrt, nth_root};
pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
/// Floored integer division.
///
@ -88,7 +99,7 @@ pub trait Integer: Sized + Num + PartialOrd + Ord + Eq {
/// Deprecated, use `is_multiple_of` instead.
fn divides(&self, other: &Self) -> bool;
/// Returns `true` if `other` is a multiple of `self`.
/// Returns `true` if `self` is a multiple of `other`.
///
/// # Examples
///
@ -269,7 +280,7 @@ macro_rules! impl_integer_for_isize {
while m != 0 {
m >>= m.trailing_zeros();
if n > m { ::std::mem::swap(&mut n, &mut m) }
if n > m { mem::swap(&mut n, &mut m) }
m -= n;
}
@ -314,6 +325,7 @@ macro_rules! impl_integer_for_isize {
#[cfg(test)]
mod $test_mod {
use Integer;
use core::mem;
/// Checks that the division rule holds for:
///
@ -391,7 +403,7 @@ macro_rules! impl_integer_for_isize {
fn test_gcd_cmp_with_euclidean() {
fn euclidean_gcd(mut m: $T, mut n: $T) -> $T {
while m != 0 {
::std::mem::swap(&mut m, &mut n);
mem::swap(&mut m, &mut n);
m %= n;
}
@ -495,6 +507,8 @@ impl_integer_for_isize!(i16, test_integer_i16);
impl_integer_for_isize!(i32, test_integer_i32);
impl_integer_for_isize!(i64, test_integer_i64);
impl_integer_for_isize!(isize, test_integer_isize);
#[cfg(has_i128)]
impl_integer_for_isize!(i128, test_integer_i128);
macro_rules! impl_integer_for_usize {
($T:ty, $test_mod:ident) => (
@ -528,7 +542,7 @@ macro_rules! impl_integer_for_usize {
while m != 0 {
m >>= m.trailing_zeros();
if n > m { ::std::mem::swap(&mut n, &mut m) }
if n > m { mem::swap(&mut n, &mut m) }
m -= n;
}
@ -575,6 +589,7 @@ macro_rules! impl_integer_for_usize {
#[cfg(test)]
mod $test_mod {
use Integer;
use core::mem;
#[test]
fn test_div_mod_floor() {
@ -602,7 +617,7 @@ macro_rules! impl_integer_for_usize {
fn test_gcd_cmp_with_euclidean() {
fn euclidean_gcd(mut m: $T, mut n: $T) -> $T {
while m != 0 {
::std::mem::swap(&mut m, &mut n);
mem::swap(&mut m, &mut n);
m %= n;
}
n
@ -666,6 +681,8 @@ impl_integer_for_usize!(u16, test_integer_u16);
impl_integer_for_usize!(u32, test_integer_u32);
impl_integer_for_usize!(u64, test_integer_u64);
impl_integer_for_usize!(usize, test_integer_usize);
#[cfg(has_i128)]
impl_integer_for_usize!(u128, test_integer_u128);
/// An iterator over binomial coefficients.
pub struct IterBinomial<T> {
@ -819,9 +836,10 @@ fn test_iter_binomial() {
macro_rules! check_simple {
($t:ty) => { {
let n: $t = 3;
let c: Vec<_> = IterBinomial::new(n).collect();
let expected = vec![1, 3, 3, 1];
assert_eq!(c, expected);
let expected = [1, 3, 3, 1];
for (b, &e) in IterBinomial::new(n).zip(&expected) {
assert_eq!(b, e);
}
} }
}
@ -837,9 +855,8 @@ fn test_iter_binomial() {
macro_rules! check_binomial {
($t:ty, $n:expr) => { {
let n: $t = $n;
let c: Vec<_> = IterBinomial::new(n).collect();
let mut k: $t = 0;
for b in c {
for b in IterBinomial::new(n) {
assert_eq!(b, binomial(n, k));
k += 1;
}

View File

@ -0,0 +1,380 @@
use core;
use core::mem;
use traits::checked_pow;
use traits::PrimInt;
use Integer;
/// Provides methods to compute an integer's square root, cube root,
/// and arbitrary `n`th root.
pub trait Roots: Integer {
/// Returns the truncated principal `n`th root of an integer
/// -- `if x >= 0 { ⌊ⁿ√x⌋ } else { ⌈ⁿ√x⌉ }`
///
/// This is solving for `r` in `rⁿ = x`, rounding toward zero.
/// If `x` is positive, the result will satisfy `rⁿ ≤ x < (r+1)ⁿ`.
/// If `x` is negative and `n` is odd, then `(r-1)ⁿ < x ≤ rⁿ`.
///
/// # Panics
///
/// Panics if `n` is zero:
///
/// ```should_panic
/// # use num_integer::Roots;
/// println!("can't compute ⁰√x : {}", 123.nth_root(0));
/// ```
///
/// or if `n` is even and `self` is negative:
///
/// ```should_panic
/// # use num_integer::Roots;
/// println!("no imaginary numbers... {}", (-1).nth_root(10));
/// ```
///
/// # Examples
///
/// ```
/// use num_integer::Roots;
///
/// let x: i32 = 12345;
/// assert_eq!(x.nth_root(1), x);
/// assert_eq!(x.nth_root(2), x.sqrt());
/// assert_eq!(x.nth_root(3), x.cbrt());
/// assert_eq!(x.nth_root(4), 10);
/// assert_eq!(x.nth_root(13), 2);
/// assert_eq!(x.nth_root(14), 1);
/// assert_eq!(x.nth_root(std::u32::MAX), 1);
///
/// assert_eq!(std::i32::MAX.nth_root(30), 2);
/// assert_eq!(std::i32::MAX.nth_root(31), 1);
/// assert_eq!(std::i32::MIN.nth_root(31), -2);
/// assert_eq!((std::i32::MIN + 1).nth_root(31), -1);
///
/// assert_eq!(std::u32::MAX.nth_root(31), 2);
/// assert_eq!(std::u32::MAX.nth_root(32), 1);
/// ```
fn nth_root(&self, n: u32) -> Self;
/// Returns the truncated principal square root of an integer -- `⌊√x⌋`
///
/// This is solving for `r` in `r² = x`, rounding toward zero.
/// The result will satisfy `r² ≤ x < (r+1)²`.
///
/// # Panics
///
/// Panics if `self` is less than zero:
///
/// ```should_panic
/// # use num_integer::Roots;
/// println!("no imaginary numbers... {}", (-1).sqrt());
/// ```
///
/// # Examples
///
/// ```
/// use num_integer::Roots;
///
/// let x: i32 = 12345;
/// assert_eq!((x * x).sqrt(), x);
/// assert_eq!((x * x + 1).sqrt(), x);
/// assert_eq!((x * x - 1).sqrt(), x - 1);
/// ```
#[inline]
fn sqrt(&self) -> Self {
self.nth_root(2)
}
/// Returns the truncated principal cube root of an integer --
/// `if x >= 0 { ⌊∛x⌋ } else { ⌈∛x⌉ }`
///
/// This is solving for `r` in `r³ = x`, rounding toward zero.
/// If `x` is positive, the result will satisfy `r³ ≤ x < (r+1)³`.
/// If `x` is negative, then `(r-1)³ < x ≤ r³`.
///
/// # Examples
///
/// ```
/// use num_integer::Roots;
///
/// let x: i32 = 1234;
/// assert_eq!((x * x * x).cbrt(), x);
/// assert_eq!((x * x * x + 1).cbrt(), x);
/// assert_eq!((x * x * x - 1).cbrt(), x - 1);
///
/// assert_eq!((-(x * x * x)).cbrt(), -x);
/// assert_eq!((-(x * x * x + 1)).cbrt(), -x);
/// assert_eq!((-(x * x * x - 1)).cbrt(), -(x - 1));
/// ```
#[inline]
fn cbrt(&self) -> Self {
self.nth_root(3)
}
}
/// Returns the truncated principal square root of an integer --
/// see [Roots::sqrt](trait.Roots.html#method.sqrt).
#[inline]
pub fn sqrt<T: Roots>(x: T) -> T {
x.sqrt()
}
/// Returns the truncated principal cube root of an integer --
/// see [Roots::cbrt](trait.Roots.html#method.cbrt).
#[inline]
pub fn cbrt<T: Roots>(x: T) -> T {
x.cbrt()
}
/// Returns the truncated principal `n`th root of an integer --
/// see [Roots::nth_root](trait.Roots.html#tymethod.nth_root).
#[inline]
pub fn nth_root<T: Roots>(x: T, n: u32) -> T {
x.nth_root(n)
}
macro_rules! signed_roots {
($T:ty, $U:ty) => {
impl Roots for $T {
#[inline]
fn nth_root(&self, n: u32) -> Self {
if *self >= 0 {
(*self as $U).nth_root(n) as Self
} else {
assert!(n.is_odd(), "even roots of a negative are imaginary");
-((self.wrapping_neg() as $U).nth_root(n) as Self)
}
}
#[inline]
fn sqrt(&self) -> Self {
assert!(*self >= 0, "the square root of a negative is imaginary");
(*self as $U).sqrt() as Self
}
#[inline]
fn cbrt(&self) -> Self {
if *self >= 0 {
(*self as $U).cbrt() as Self
} else {
-((self.wrapping_neg() as $U).cbrt() as Self)
}
}
}
};
}
signed_roots!(i8, u8);
signed_roots!(i16, u16);
signed_roots!(i32, u32);
signed_roots!(i64, u64);
#[cfg(has_i128)]
signed_roots!(i128, u128);
signed_roots!(isize, usize);
#[inline]
fn fixpoint<T, F>(mut x: T, f: F) -> T
where
T: Integer + Copy,
F: Fn(T) -> T,
{
let mut xn = f(x);
while x < xn {
x = xn;
xn = f(x);
}
while x > xn {
x = xn;
xn = f(x);
}
x
}
#[inline]
fn bits<T>() -> u32 {
8 * mem::size_of::<T>() as u32
}
#[inline]
fn log2<T: PrimInt>(x: T) -> u32 {
debug_assert!(x > T::zero());
bits::<T>() - 1 - x.leading_zeros()
}
macro_rules! unsigned_roots {
($T:ident) => {
impl Roots for $T {
fn nth_root(&self, n: u32) -> Self {
// Specialize small roots
match n {
0 => panic!("can't find a root of degree 0!"),
1 => return *self,
2 => return self.sqrt(),
3 => return self.cbrt(),
_ => (),
}
// The root of values less than 2ⁿ can only be 0 or 1.
if bits::<$T>() <= n || *self < (1 << n) {
return (*self > 0) as $T;
}
if bits::<$T>() > 64 {
// 128-bit division is slow, so do a bitwise `nth_root` until it's small enough.
return if *self <= core::u64::MAX as $T {
(*self as u64).nth_root(n) as $T
} else {
let lo = (self >> n).nth_root(n) << 1;
let hi = lo + 1;
// 128-bit `checked_mul` also involves division, but we can't always
// compute `hiⁿ` without risking overflow. Try to avoid it though...
if hi.next_power_of_two().trailing_zeros() * n >= bits::<$T>() {
match checked_pow(hi, n as usize) {
Some(x) if x <= *self => hi,
_ => lo,
}
} else {
if hi.pow(n) <= *self {
hi
} else {
lo
}
}
};
}
#[cfg(feature = "std")]
#[inline]
fn guess(x: $T, n: u32) -> $T {
// for smaller inputs, `f64` doesn't justify its cost.
if bits::<$T>() <= 32 || x <= core::u32::MAX as $T {
1 << ((log2(x) + n - 1) / n)
} else {
((x as f64).ln() / f64::from(n)).exp() as $T
}
}
#[cfg(not(feature = "std"))]
#[inline]
fn guess(x: $T, n: u32) -> $T {
1 << ((log2(x) + n - 1) / n)
}
// https://en.wikipedia.org/wiki/Nth_root_algorithm
let n1 = n - 1;
let next = |x: $T| {
let y = match checked_pow(x, n1 as usize) {
Some(ax) => self / ax,
None => 0,
};
(y + x * n1 as $T) / n as $T
};
fixpoint(guess(*self, n), next)
}
fn sqrt(&self) -> Self {
if bits::<$T>() > 64 {
// 128-bit division is slow, so do a bitwise `sqrt` until it's small enough.
// https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations
return if *self <= core::u64::MAX as $T {
(*self as u64).sqrt() as $T
} else {
let lo = (self >> 2u32).sqrt() << 1;
let hi = lo + 1;
if hi * hi <= *self {
hi
} else {
lo
}
};
}
if *self < 4 {
return (*self > 0) as Self;
}
#[cfg(feature = "std")]
#[inline]
fn guess(x: $T) -> $T {
(x as f64).sqrt() as $T
}
#[cfg(not(feature = "std"))]
#[inline]
fn guess(x: $T) -> $T {
1 << ((log2(x) + 1) / 2)
}
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
let next = |x: $T| (self / x + x) >> 1;
fixpoint(guess(*self), next)
}
fn cbrt(&self) -> Self {
if bits::<$T>() > 64 {
// 128-bit division is slow, so do a bitwise `cbrt` until it's small enough.
return if *self <= core::u64::MAX as $T {
(*self as u64).cbrt() as $T
} else {
let lo = (self >> 3u32).cbrt() << 1;
let hi = lo + 1;
if hi * hi * hi <= *self {
hi
} else {
lo
}
};
}
if bits::<$T>() <= 32 {
// Implementation based on Hacker's Delight `icbrt2`
let mut x = *self;
let mut y2 = 0;
let mut y = 0;
let smax = bits::<$T>() / 3;
for s in (0..smax + 1).rev() {
let s = s * 3;
y2 *= 4;
y *= 2;
let b = 3 * (y2 + y) + 1;
if x >> s >= b {
x -= b << s;
y2 += 2 * y + 1;
y += 1;
}
}
return y;
}
if *self < 8 {
return (*self > 0) as Self;
}
if *self <= core::u32::MAX as $T {
return (*self as u32).cbrt() as $T;
}
#[cfg(feature = "std")]
#[inline]
fn guess(x: $T) -> $T {
(x as f64).cbrt() as $T
}
#[cfg(not(feature = "std"))]
#[inline]
fn guess(x: $T) -> $T {
1 << ((log2(x) + 2) / 3)
}
// https://en.wikipedia.org/wiki/Cube_root#Numerical_methods
let next = |x: $T| (self / (x * x) + x * 2) / 3;
fixpoint(guess(*self), next)
}
}
};
}
unsigned_roots!(u8);
unsigned_roots!(u16);
unsigned_roots!(u32);
unsigned_roots!(u64);
#[cfg(has_i128)]
unsigned_roots!(u128);
unsigned_roots!(usize);

View File

@ -0,0 +1,276 @@
extern crate num_integer;
extern crate num_traits;
use num_integer::Roots;
use num_traits::checked_pow;
use num_traits::{AsPrimitive, PrimInt, Signed};
use std::f64::MANTISSA_DIGITS;
use std::fmt::Debug;
use std::mem;
trait TestInteger: Roots + PrimInt + Debug + AsPrimitive<f64> + 'static {}
impl<T> TestInteger for T
where
T: Roots + PrimInt + Debug + AsPrimitive<f64> + 'static,
{
}
/// Check that each root is correct
///
/// If `x` is positive, check `rⁿ ≤ x < (r+1)ⁿ`.
/// If `x` is negative, check `(r-1)ⁿ < x ≤ rⁿ`.
fn check<T>(v: &[T], n: u32)
where
T: TestInteger,
{
for i in v {
let rt = i.nth_root(n);
// println!("nth_root({:?}, {}) = {:?}", i, n, rt);
if n == 2 {
assert_eq!(rt, i.sqrt());
} else if n == 3 {
assert_eq!(rt, i.cbrt());
}
if *i >= T::zero() {
let rt1 = rt + T::one();
assert!(rt.pow(n) <= *i);
if let Some(x) = checked_pow(rt1, n as usize) {
assert!(*i < x);
}
} else {
let rt1 = rt - T::one();
assert!(rt < T::zero());
assert!(*i <= rt.pow(n));
if let Some(x) = checked_pow(rt1, n as usize) {
assert!(x < *i);
}
};
}
}
/// Get the maximum value that will round down as `f64` (if any),
/// and its successor that will round up.
///
/// Important because the `std` implementations cast to `f64` to
/// get a close approximation of the roots.
fn mantissa_max<T>() -> Option<(T, T)>
where
T: TestInteger,
{
let bits = if T::min_value().is_zero() {
8 * mem::size_of::<T>()
} else {
8 * mem::size_of::<T>() - 1
};
if bits > MANTISSA_DIGITS as usize {
let rounding_bit = T::one() << (bits - MANTISSA_DIGITS as usize - 1);
let x = T::max_value() - rounding_bit;
let x1 = x + T::one();
let x2 = x1 + T::one();
assert!(x.as_() < x1.as_());
assert_eq!(x1.as_(), x2.as_());
Some((x, x1))
} else {
None
}
}
fn extend<T>(v: &mut Vec<T>, start: T, end: T)
where
T: TestInteger,
{
let mut i = start;
while i < end {
v.push(i);
i = i + T::one();
}
v.push(i);
}
fn extend_shl<T>(v: &mut Vec<T>, start: T, end: T, mask: T)
where
T: TestInteger,
{
let mut i = start;
while i != end {
v.push(i);
i = (i << 1) & mask;
}
}
fn extend_shr<T>(v: &mut Vec<T>, start: T, end: T)
where
T: TestInteger,
{
let mut i = start;
while i != end {
v.push(i);
i = i >> 1;
}
}
fn pos<T>() -> Vec<T>
where
T: TestInteger,
i8: AsPrimitive<T>,
{
let mut v: Vec<T> = vec![];
if mem::size_of::<T>() == 1 {
extend(&mut v, T::zero(), T::max_value());
} else {
extend(&mut v, T::zero(), i8::max_value().as_());
extend(
&mut v,
T::max_value() - i8::max_value().as_(),
T::max_value(),
);
if let Some((i, j)) = mantissa_max::<T>() {
v.push(i);
v.push(j);
}
extend_shl(&mut v, T::max_value(), T::zero(), !T::min_value());
extend_shr(&mut v, T::max_value(), T::zero());
}
v
}
fn neg<T>() -> Vec<T>
where
T: TestInteger + Signed,
i8: AsPrimitive<T>,
{
let mut v: Vec<T> = vec![];
if mem::size_of::<T>() <= 1 {
extend(&mut v, T::min_value(), T::zero());
} else {
extend(&mut v, i8::min_value().as_(), T::zero());
extend(
&mut v,
T::min_value(),
T::min_value() - i8::min_value().as_(),
);
if let Some((i, j)) = mantissa_max::<T>() {
v.push(-i);
v.push(-j);
}
extend_shl(&mut v, -T::one(), T::min_value(), !T::zero());
extend_shr(&mut v, T::min_value(), -T::one());
}
v
}
macro_rules! test_roots {
($I:ident, $U:ident) => {
mod $I {
use check;
use neg;
use num_integer::Roots;
use pos;
use std::mem;
#[test]
#[should_panic]
fn zeroth_root() {
(123 as $I).nth_root(0);
}
#[test]
fn sqrt() {
check(&pos::<$I>(), 2);
}
#[test]
#[should_panic]
fn sqrt_neg() {
(-123 as $I).sqrt();
}
#[test]
fn cbrt() {
check(&pos::<$I>(), 3);
}
#[test]
fn cbrt_neg() {
check(&neg::<$I>(), 3);
}
#[test]
fn nth_root() {
let bits = 8 * mem::size_of::<$I>() as u32 - 1;
let pos = pos::<$I>();
for n in 4..bits {
check(&pos, n);
}
}
#[test]
fn nth_root_neg() {
let bits = 8 * mem::size_of::<$I>() as u32 - 1;
let neg = neg::<$I>();
for n in 2..bits / 2 {
check(&neg, 2 * n + 1);
}
}
#[test]
fn bit_size() {
let bits = 8 * mem::size_of::<$I>() as u32 - 1;
assert_eq!($I::max_value().nth_root(bits - 1), 2);
assert_eq!($I::max_value().nth_root(bits), 1);
assert_eq!($I::min_value().nth_root(bits), -2);
assert_eq!(($I::min_value() + 1).nth_root(bits), -1);
}
}
mod $U {
use check;
use num_integer::Roots;
use pos;
use std::mem;
#[test]
#[should_panic]
fn zeroth_root() {
(123 as $U).nth_root(0);
}
#[test]
fn sqrt() {
check(&pos::<$U>(), 2);
}
#[test]
fn cbrt() {
check(&pos::<$U>(), 3);
}
#[test]
fn nth_root() {
let bits = 8 * mem::size_of::<$I>() as u32 - 1;
let pos = pos::<$I>();
for n in 4..bits {
check(&pos, n);
}
}
#[test]
fn bit_size() {
let bits = 8 * mem::size_of::<$U>() as u32;
assert_eq!($U::max_value().nth_root(bits - 1), 2);
assert_eq!($U::max_value().nth_root(bits), 1);
}
}
};
}
test_roots!(i8, u8);
test_roots!(i16, u16);
test_roots!(i32, u32);
test_roots!(i64, u64);
#[cfg(has_i128)]
test_roots!(i128, u128);
test_roots!(isize, usize);

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"69eae59293fd84125e4e17e4c8b24f25257d9e13dd37563b9c482a384d20589c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","src/lib.rs":"7cc73113897d1399dafc7f64ecc53f809295a4df3b401b42c2c8b1a7743a3748"},"package":"7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"}

View File

@ -1,23 +0,0 @@
[package]
authors = ["The Rust Project Developers"]
description = "External iterators for generic mathematics"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics"]
categories = [ "algorithms", "science" ]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num"
name = "num-iter"
version = "0.1.34"
[dependencies]
[dependencies.num-integer]
optional = false
path = "../integer"
version = "0.1.32"
[dependencies.num-traits]
optional = false
path = "../traits"
version = "0.1.32"

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,25 +0,0 @@
Copyright (c) 2014 The Rust Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,378 +0,0 @@
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! External iterators for generic mathematics
#![doc(html_logo_url = "https://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://rust-num.github.io/num/favicon.ico",
html_root_url = "https://rust-num.github.io/num/",
html_playground_url = "http://play.integer32.com/")]
extern crate num_traits as traits;
extern crate num_integer as integer;
use integer::Integer;
use traits::{Zero, One, CheckedAdd, ToPrimitive};
use std::ops::{Add, Sub};
/// An iterator over the range [start, stop)
#[derive(Clone)]
pub struct Range<A> {
state: A,
stop: A,
one: A
}
/// Returns an iterator over the given range [start, stop) (that is, starting
/// at start (inclusive), and ending at stop (exclusive)).
///
/// # Example
///
/// ```rust
/// let array = [0, 1, 2, 3, 4];
///
/// for i in num_iter::range(0, 5) {
/// println!("{}", i);
/// assert_eq!(i, array[i]);
/// }
/// ```
#[inline]
pub fn range<A>(start: A, stop: A) -> Range<A>
where A: Add<A, Output = A> + PartialOrd + Clone + One
{
Range{state: start, stop: stop, one: One::one()}
}
// FIXME: rust-lang/rust#10414: Unfortunate type bound
impl<A> Iterator for Range<A>
where A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
if self.state < self.stop {
let result = self.state.clone();
self.state = self.state.clone() + self.one.clone();
Some(result)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
// This first checks if the elements are representable as i64. If they aren't, try u64 (to
// handle cases like range(huge, huger)). We don't use usize/int because the difference of
// the i64/u64 might lie within their range.
let bound = match self.state.to_i64() {
Some(a) => {
let sz = self.stop.to_i64().map(|b| b.checked_sub(a));
match sz {
Some(Some(bound)) => bound.to_usize(),
_ => None,
}
},
None => match self.state.to_u64() {
Some(a) => {
let sz = self.stop.to_u64().map(|b| b.checked_sub(a));
match sz {
Some(Some(bound)) => bound.to_usize(),
_ => None
}
},
None => None
}
};
match bound {
Some(b) => (b, Some(b)),
// Standard fallback for unbounded/unrepresentable bounds
None => (0, None)
}
}
}
/// `Integer` is required to ensure the range will be the same regardless of
/// the direction it is consumed.
impl<A> DoubleEndedIterator for Range<A>
where A: Integer + Clone + ToPrimitive
{
#[inline]
fn next_back(&mut self) -> Option<A> {
if self.stop > self.state {
self.stop = self.stop.clone() - self.one.clone();
Some(self.stop.clone())
} else {
None
}
}
}
/// An iterator over the range [start, stop]
#[derive(Clone)]
pub struct RangeInclusive<A> {
range: Range<A>,
done: bool,
}
/// Return an iterator over the range [start, stop]
#[inline]
pub fn range_inclusive<A>(start: A, stop: A) -> RangeInclusive<A>
where A: Add<A, Output = A> + PartialOrd + Clone + One
{
RangeInclusive{range: range(start, stop), done: false}
}
impl<A> Iterator for RangeInclusive<A>
where A: Add<A, Output = A> + PartialOrd + Clone + ToPrimitive
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
match self.range.next() {
Some(x) => Some(x),
None => {
if !self.done && self.range.state == self.range.stop {
self.done = true;
Some(self.range.stop.clone())
} else {
None
}
}
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let (lo, hi) = self.range.size_hint();
if self.done {
(lo, hi)
} else {
let lo = lo.saturating_add(1);
let hi = match hi {
Some(x) => x.checked_add(1),
None => None
};
(lo, hi)
}
}
}
impl<A> DoubleEndedIterator for RangeInclusive<A>
where A: Sub<A, Output = A> + Integer + Clone + ToPrimitive
{
#[inline]
fn next_back(&mut self) -> Option<A> {
if self.range.stop > self.range.state {
let result = self.range.stop.clone();
self.range.stop = self.range.stop.clone() - self.range.one.clone();
Some(result)
} else if !self.done && self.range.state == self.range.stop {
self.done = true;
Some(self.range.stop.clone())
} else {
None
}
}
}
/// An iterator over the range [start, stop) by `step`. It handles overflow by stopping.
#[derive(Clone)]
pub struct RangeStep<A> {
state: A,
stop: A,
step: A,
rev: bool,
}
/// Return an iterator over the range [start, stop) by `step`. It handles overflow by stopping.
#[inline]
pub fn range_step<A>(start: A, stop: A, step: A) -> RangeStep<A>
where A: CheckedAdd + PartialOrd + Clone + Zero
{
let rev = step < Zero::zero();
RangeStep{state: start, stop: stop, step: step, rev: rev}
}
impl<A> Iterator for RangeStep<A>
where A: CheckedAdd + PartialOrd + Clone
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
if (self.rev && self.state > self.stop) || (!self.rev && self.state < self.stop) {
let result = self.state.clone();
match self.state.checked_add(&self.step) {
Some(x) => self.state = x,
None => self.state = self.stop.clone()
}
Some(result)
} else {
None
}
}
}
/// An iterator over the range [start, stop] by `step`. It handles overflow by stopping.
#[derive(Clone)]
pub struct RangeStepInclusive<A> {
state: A,
stop: A,
step: A,
rev: bool,
done: bool,
}
/// Return an iterator over the range [start, stop] by `step`. It handles overflow by stopping.
#[inline]
pub fn range_step_inclusive<A>(start: A, stop: A, step: A) -> RangeStepInclusive<A>
where A: CheckedAdd + PartialOrd + Clone + Zero
{
let rev = step < Zero::zero();
RangeStepInclusive{state: start, stop: stop, step: step, rev: rev, done: false}
}
impl<A> Iterator for RangeStepInclusive<A>
where A: CheckedAdd + PartialOrd + Clone + PartialEq
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
if !self.done && ((self.rev && self.state >= self.stop) ||
(!self.rev && self.state <= self.stop)) {
let result = self.state.clone();
match self.state.checked_add(&self.step) {
Some(x) => self.state = x,
None => self.done = true
}
Some(result)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use std::usize;
use std::ops::{Add, Mul};
use std::cmp::Ordering;
use traits::{One, ToPrimitive};
#[test]
fn test_range() {
/// A mock type to check Range when ToPrimitive returns None
struct Foo;
impl ToPrimitive for Foo {
fn to_i64(&self) -> Option<i64> { None }
fn to_u64(&self) -> Option<u64> { None }
}
impl Add<Foo> for Foo {
type Output = Foo;
fn add(self, _: Foo) -> Foo {
Foo
}
}
impl PartialEq for Foo {
fn eq(&self, _: &Foo) -> bool {
true
}
}
impl PartialOrd for Foo {
fn partial_cmp(&self, _: &Foo) -> Option<Ordering> {
None
}
}
impl Clone for Foo {
fn clone(&self) -> Foo {
Foo
}
}
impl Mul<Foo> for Foo {
type Output = Foo;
fn mul(self, _: Foo) -> Foo {
Foo
}
}
impl One for Foo {
fn one() -> Foo {
Foo
}
}
assert!(super::range(0, 5).collect::<Vec<isize>>() == vec![0, 1, 2, 3, 4]);
assert!(super::range(-10, -1).collect::<Vec<isize>>() ==
vec![-10, -9, -8, -7, -6, -5, -4, -3, -2]);
assert!(super::range(0, 5).rev().collect::<Vec<isize>>() == vec![4, 3, 2, 1, 0]);
assert_eq!(super::range(200, -5).count(), 0);
assert_eq!(super::range(200, -5).rev().count(), 0);
assert_eq!(super::range(200, 200).count(), 0);
assert_eq!(super::range(200, 200).rev().count(), 0);
assert_eq!(super::range(0, 100).size_hint(), (100, Some(100)));
// this test is only meaningful when sizeof usize < sizeof u64
assert_eq!(super::range(usize::MAX - 1, usize::MAX).size_hint(), (1, Some(1)));
assert_eq!(super::range(-10, -1).size_hint(), (9, Some(9)));
}
#[test]
fn test_range_inclusive() {
assert!(super::range_inclusive(0, 5).collect::<Vec<isize>>() ==
vec![0, 1, 2, 3, 4, 5]);
assert!(super::range_inclusive(0, 5).rev().collect::<Vec<isize>>() ==
vec![5, 4, 3, 2, 1, 0]);
assert_eq!(super::range_inclusive(200, -5).count(), 0);
assert_eq!(super::range_inclusive(200, -5).rev().count(), 0);
assert!(super::range_inclusive(200, 200).collect::<Vec<isize>>() == vec![200]);
assert!(super::range_inclusive(200, 200).rev().collect::<Vec<isize>>() == vec![200]);
}
#[test]
fn test_range_step() {
assert!(super::range_step(0, 20, 5).collect::<Vec<isize>>() ==
vec![0, 5, 10, 15]);
assert!(super::range_step(20, 0, -5).collect::<Vec<isize>>() ==
vec![20, 15, 10, 5]);
assert!(super::range_step(20, 0, -6).collect::<Vec<isize>>() ==
vec![20, 14, 8, 2]);
assert!(super::range_step(200u8, 255, 50).collect::<Vec<u8>>() ==
vec![200u8, 250]);
assert!(super::range_step(200, -5, 1).collect::<Vec<isize>>() == vec![]);
assert!(super::range_step(200, 200, 1).collect::<Vec<isize>>() == vec![]);
}
#[test]
fn test_range_step_inclusive() {
assert!(super::range_step_inclusive(0, 20, 5).collect::<Vec<isize>>() ==
vec![0, 5, 10, 15, 20]);
assert!(super::range_step_inclusive(20, 0, -5).collect::<Vec<isize>>() ==
vec![20, 15, 10, 5, 0]);
assert!(super::range_step_inclusive(20, 0, -6).collect::<Vec<isize>>() ==
vec![20, 14, 8, 2]);
assert!(super::range_step_inclusive(200u8, 255, 50).collect::<Vec<u8>>() ==
vec![200u8, 250]);
assert!(super::range_step_inclusive(200, -5, 1).collect::<Vec<isize>>() ==
vec![]);
assert!(super::range_step_inclusive(200, 200, 1).collect::<Vec<isize>>() ==
vec![200]);
}
}

View File

@ -1 +1 @@
{"files":{".travis.yml":"f2329e77821e13e0b77cc97e624d76f6ccd92c929c1334cf6ac7888b01c5a481","Cargo.toml":"d6798dc92f8b83a8811446ae2e47c51564eb2b49162051b61a1eeec8bbed0da6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"fc9a4b3719a828c9128e957ee7b9e2f795cc92a7cc50c1dda50e14126e0b4a77","RELEASES.md":"86c3e6703e6948bfc23c165a8b121930b8da4ffc7c38ab49d7a9e27b1655090e","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","ci/rustup.sh":"723d546a1ffefcdd5d4db9fb26dbf4128954e3991aff32932284cdc67fa5c85e","ci/test_full.sh":"c64b1d6f96baad0ea3bceb6842e7a706c4b414c99fddca55c2c30c81ae63d33a","src/bounds.rs":"d75e65f4f0e1337be06d2753ca06c23f099beb91d15215ac3230e9d441fcf4d0","src/cast.rs":"c06d1fd279b78386795384885a12742f9f31c95f364761acf0ed110184dc6bbc","src/float.rs":"841a2614519aac9f54b87e84a542df49346aa27254bd226b2dabe7966304fd0f","src/identities.rs":"f5389cf96b0d7ef55bd688961a58c4d7c0f3bd3bd48fe771948534e6bc05f29c","src/int.rs":"d6c51c943198a3aa071bfeb748101b2ce806fdef1ed6c26ef8f28f1e5a810f47","src/lib.rs":"29e4583f84b13ce2c0a6e5bbf4932183a19878e3cbab2387896b9e11c7729d5d","src/ops/checked.rs":"b2fc7ce21bca9c237f29149a553156a80d5ef94100c55128e05b946a575634c4","src/ops/mod.rs":"668ea4d117bc1fdf7eaf0fe16692fa40dfbdfcbc7a2010237fe395ce0086e02e","src/ops/saturating.rs":"46821d815c90c16b2f6bec0b94b4d7ebdbddf3ea42edc0467de738c56abf6436","src/ops/wrapping.rs":"5b274e7fc77d11ba37bb2fc98dabbbb8910f83b14d2505f775bd5a2e19445f6b","src/pow.rs":"4cedc57fc1446f436f729a68c1c4de797d316e4c1c341dead8f6ea0801c9f1ac","src/real.rs":"5e9436436593ed9c005078b9fe33477714490f959cae66ae2ae610da3eceb5f6","src/sign.rs":"f86aa7e9720698807455eabaf5f77b9fe68432fbb1a9291faf73b0c9f648d540"},"package":"e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"}
{"files":{".travis.yml":"49df6cd8acc5622158f32a21ed04e9386b1d9d69990824b9ca62e9f7d8821f60","Cargo.toml":"9786af550e08e1eac4a902cc4c148729bf3cbbdf6caa803786b2f7c829ff023f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"ec49ad594875e0fc5b060af011fa82baa7e58cda2b9c50fa8f1ef9780ba3fe96","RELEASES.md":"1f79030c9864dac0e7b14dda47fa7cc8313c82621ee26739eeeb1abf0b5d1fc4","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","build.rs":"16de2aa57e754fc1526d0400b5d87a3f771296705fca54601aa598b6f74ded8f","ci/rustup.sh":"1698b0030a5b0807b10c5d0ed9e9f5fe58e1b259016007723ffd1426284473e0","ci/test_full.sh":"3538ba4ff317568a6001fa5cb0fc433b1a36df3aab58a0fc9c0487ef39ca5810","src/bounds.rs":"dfbcd607c3bc648356f38d2cc63ceb20cd5820b7da06c5758efe91d0b0642ec2","src/cast.rs":"b5bc61c83267d80d6b71f7f0effa9cd193d30e026965f749434105b66c94c78a","src/float.rs":"a33b6a812cfbfc46e6884434c036c5a8fdeb91cb353d75c352512f1294e318b1","src/identities.rs":"d9037a084657bde78a73735e9a45bac4ed41f1954d567eb28d5bc2e1338c3ea8","src/int.rs":"287f6a6193908f4bd465d930db91b190845c466e64911df1280ddb12737d78ee","src/lib.rs":"a248f6e5e80eb786a71e748c783fc99e6b01f5f3fb9c9fe2f8ba42dfeadf3414","src/macros.rs":"b589a98c2468ca98131c641a058601964c4e82d75129b1c16fdc17aca8779eca","src/ops/checked.rs":"f30376b3f23e924220bf11d8c6addd844d7c3649c8d15f5417dcdbbf43560195","src/ops/inv.rs":"dd80b9bd48d815f17855a25842287942317fa49d1fdcdd655b61bd20ef927cda","src/ops/mod.rs":"036b2a1900dc8e7295a91060e660184b2bd98f33b5db81a62b08cf8d3df726cf","src/ops/mul_add.rs":"b36e058a5c1882958417c7a4a54baa0ba181d6290821fc0859459d8b8ba3571a","src/ops/saturating.rs":"81c34038ec88fe49f4286e6e1213a1da831c5e66caac651f0a61506a4534c9ce","src/ops/wrapping.rs":"8d334dd2b8128e640bff6c3e93ea301182241987b3ed1fc158268292827bbd0c","src/pow.rs":"caad87386fb717eada6dae32a00e536e8a355bf5b4c31037f6afb163775bd218","src/real.rs":"a93ac4a48084cb49e5c97eafa54aaf0ea2f627b4e372a8b77d784c5e158d2c3c","src/sign.rs":"d82757198fe3144676316035e5574801f724821f604ad0fee892e86e83f9d802","tests/cast.rs":"2c4b4f2185ec0d687e1bde292731dbc5efec527ab393478b5adf26f6e1352231"},"package":"0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"}

View File

@ -1,6 +1,8 @@
language: rust
rust:
- 1.8.0
- 1.15.0
- 1.20.0
- stable
- beta
- nightly

View File

@ -12,19 +12,23 @@
[package]
name = "num-traits"
version = "0.2.0"
version = "0.2.6"
authors = ["The Rust Project Developers"]
build = "build.rs"
description = "Numeric traits for generic mathematics"
homepage = "https://github.com/rust-num/num-traits"
documentation = "https://docs.rs/num-traits"
readme = "README.md"
keywords = ["mathematics", "numerics"]
categories = ["algorithms", "science"]
categories = ["algorithms", "science", "no-std"]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num-traits"
[package.metadata.docs.rs]
features = ["std"]
[dependencies]
[features]
default = ["std"]
i128 = []
std = []

View File

@ -25,7 +25,7 @@ extern crate num_traits;
## Features
This crate can be used without the standard library (`#![no_std]`) by disabling
the default `std` feature. Use this in `Cargo.toml`:
the default `std` feature. Use this in `Cargo.toml`:
```toml
[dependencies.num-traits]
@ -33,7 +33,14 @@ version = "0.2"
default-features = false
```
The `Float` and `Real` traits are only available when `std` is enabled.
The `Float` and `Real` traits are only available when `std` is enabled. The
`FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32`
and `f64` also require `std`, as do implementations of signed and floating-
point exponents in `Pow`.
Implementations for `i128` and `u128` are only available with Rust 1.26 and
later. The build script automatically detects this, but you can make it
mandatory by enabling the `i128` crate feature.
## Releases

View File

@ -1,8 +1,95 @@
# Release 0.2.0
# Release 0.2.6 (2018-09-13)
- **breaking change**: There is now a `std` feature, enabled by default, along
- [Documented that `pow(0, 0)` returns `1`][79]. Mathematically, this is not
strictly defined, but the current behavior is a pragmatic choice that has
precedent in Rust `core` for the primitives and in many other languages.
- [The new `WrappingShl` and `WrappingShr` traits][81] will wrap the shift count
if it exceeds the bit size of the type.
**Contributors**: @cuviper, @edmccard, @meltinglava
[79]: https://github.com/rust-num/num-traits/pull/79
[81]: https://github.com/rust-num/num-traits/pull/81
# Release 0.2.5 (2018-06-20)
- [Documentation for `mul_add` now clarifies that it's not always faster.][70]
- [The default methods in `FromPrimitive` and `ToPrimitive` are more robust.][73]
**Contributors**: @cuviper, @frewsxcv
[70]: https://github.com/rust-num/num-traits/pull/70
[73]: https://github.com/rust-num/num-traits/pull/73
# Release 0.2.4 (2018-05-11)
- [Support for 128-bit integers is now automatically detected and enabled.][69]
Setting the `i128` crate feature now causes the build script to panic if such
support is not detected.
**Contributors**: @cuviper
[69]: https://github.com/rust-num/num-traits/pull/69
# Release 0.2.3 (2018-05-10)
- [The new `CheckedNeg` and `CheckedRem` traits][63] perform checked `Neg` and
`Rem`, returning `Some(output)` or `None` on overflow.
- [The `no_std` implementation of `FloatCore::to_degrees` for `f32`][61] now
uses a constant for greater accuracy, mirroring [rust#47919]. (With `std` it
just calls the inherent `f32::to_degrees` in the standard library.)
- [The new `MulAdd` and `MulAddAssign` traits][59] perform a fused multiply-
add. For integer types this is just a convenience, but for floating point
types this produces a more accurate result than the separate operations.
- [All applicable traits are now implemented for 128-bit integers][60] starting
with Rust 1.26, enabled by the new `i128` crate feature. The `FromPrimitive`
and `ToPrimitive` traits now also have corresponding 128-bit methods, which
default to converting via 64-bit integers for compatibility.
**Contributors**: @cuviper, @LEXUGE, @regexident, @vks
[59]: https://github.com/rust-num/num-traits/pull/59
[60]: https://github.com/rust-num/num-traits/pull/60
[61]: https://github.com/rust-num/num-traits/pull/61
[63]: https://github.com/rust-num/num-traits/pull/63
[rust#47919]: https://github.com/rust-lang/rust/pull/47919
# Release 0.2.2 (2018-03-18)
- [Casting from floating point to integers now returns `None` on overflow][52],
avoiding [rustc's undefined behavior][rust-10184]. This applies to the `cast`
function and the traits `NumCast`, `FromPrimitive`, and `ToPrimitive`.
**Contributors**: @apopiak, @cuviper, @dbarella
[52]: https://github.com/rust-num/num-traits/pull/52
[rust-10184]: https://github.com/rust-lang/rust/issues/10184
# Release 0.2.1 (2018-03-01)
- [The new `FloatCore` trait][32] offers a subset of `Float` for `#![no_std]` use.
[This includes everything][41] except the transcendental functions and FMA.
- [The new `Inv` trait][37] returns the multiplicative inverse, or reciprocal.
- [The new `Pow` trait][37] performs exponentiation, much like the existing `pow`
function, but with generic exponent types.
- [The new `One::is_one` method][39] tests if a value equals 1. Implementers
should override this method if there's a more efficient way to check for 1,
rather than comparing with a temporary `one()`.
**Contributors**: @clarcharr, @cuviper, @vks
[32]: https://github.com/rust-num/num-traits/pull/32
[37]: https://github.com/rust-num/num-traits/pull/37
[39]: https://github.com/rust-num/num-traits/pull/39
[41]: https://github.com/rust-num/num-traits/pull/41
# Release 0.2.0 (2018-02-06)
- **breaking change**: [There is now a `std` feature][30], enabled by default, along
with the implication that building *without* this feature makes this a
`#[no_std]` crate.
`#![no_std]` crate.
- The `Float` and `Real` traits are only available when `std` is enabled.
- Otherwise, the API is unchanged, and num-traits 0.1.43 now re-exports its
items from num-traits 0.2 for compatibility (the [semver-trick]).
@ -10,12 +97,17 @@
**Contributors**: @cuviper, @termoshtt, @vks
[semver-trick]: https://github.com/dtolnay/semver-trick
[30]: https://github.com/rust-num/num-traits/pull/30
# Release 0.1.43
- All items are now re-exported from num-traits 0.2 for compatibility.
# Release 0.1.43 (2018-02-06)
# Release 0.1.42
- All items are now [re-exported from num-traits 0.2][31] for compatibility.
[31]: https://github.com/rust-num/num-traits/pull/31
# Release 0.1.42 (2018-01-22)
- [num-traits now has its own source repository][num-356] at [rust-num/num-traits][home].
- [`ParseFloatError` now implements `Display`][22].

35
third_party/rust/num-traits/build.rs vendored Normal file
View File

@ -0,0 +1,35 @@
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() {
if probe("fn main() { 0i128; }") {
println!("cargo:rustc-cfg=has_i128");
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}
}
/// Test if a code snippet can be compiled
fn probe(code: &str) -> bool {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
let mut child = Command::new(rustc)
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()
.expect("rustc probe");
child
.stdin
.as_mut()
.expect("rustc stdin")
.write_all(code.as_bytes())
.expect("write rustc stdin");
child.wait().expect("rustc probe").success()
}

View File

@ -5,8 +5,7 @@
set -ex
export TRAVIS_RUST_VERSION
for TRAVIS_RUST_VERSION in 1.8.0 stable beta nightly; do
for TRAVIS_RUST_VERSION in 1.8.0 1.15.0 1.20.0 stable beta nightly; do
run="rustup run $TRAVIS_RUST_VERSION"
$run cargo build --verbose
$run $PWD/ci/test_full.sh
done

View File

@ -11,3 +11,9 @@ cargo test --verbose
# test `no_std`
cargo build --verbose --no-default-features
cargo test --verbose --no-default-features
# test `i128`
if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable)$ ]]; then
cargo build --verbose --features=i128
cargo test --verbose --features=i128
fi

View File

@ -1,7 +1,9 @@
use core::{usize, u8, u16, u32, u64};
use core::{isize, i8, i16, i32, i64};
use core::{f32, f64};
use core::num::Wrapping;
use core::{f32, f64};
#[cfg(has_i128)]
use core::{i128, u128};
use core::{i16, i32, i64, i8, isize};
use core::{u16, u32, u64, u8, usize};
/// Numbers which have upper and lower bounds
pub trait Bounded {
@ -16,29 +18,41 @@ macro_rules! bounded_impl {
($t:ty, $min:expr, $max:expr) => {
impl Bounded for $t {
#[inline]
fn min_value() -> $t { $min }
fn min_value() -> $t {
$min
}
#[inline]
fn max_value() -> $t { $max }
fn max_value() -> $t {
$max
}
}
}
};
}
bounded_impl!(usize, usize::MIN, usize::MAX);
bounded_impl!(u8, u8::MIN, u8::MAX);
bounded_impl!(u16, u16::MIN, u16::MAX);
bounded_impl!(u32, u32::MIN, u32::MAX);
bounded_impl!(u64, u64::MIN, u64::MAX);
bounded_impl!(u8, u8::MIN, u8::MAX);
bounded_impl!(u16, u16::MIN, u16::MAX);
bounded_impl!(u32, u32::MIN, u32::MAX);
bounded_impl!(u64, u64::MIN, u64::MAX);
#[cfg(has_i128)]
bounded_impl!(u128, u128::MIN, u128::MAX);
bounded_impl!(isize, isize::MIN, isize::MAX);
bounded_impl!(i8, i8::MIN, i8::MAX);
bounded_impl!(i16, i16::MIN, i16::MAX);
bounded_impl!(i32, i32::MIN, i32::MAX);
bounded_impl!(i64, i64::MIN, i64::MAX);
bounded_impl!(i8, i8::MIN, i8::MAX);
bounded_impl!(i16, i16::MIN, i16::MAX);
bounded_impl!(i32, i32::MIN, i32::MAX);
bounded_impl!(i64, i64::MIN, i64::MAX);
#[cfg(has_i128)]
bounded_impl!(i128, i128::MIN, i128::MAX);
impl<T: Bounded> Bounded for Wrapping<T> {
fn min_value() -> Self { Wrapping(T::min_value()) }
fn max_value() -> Self { Wrapping(T::max_value()) }
fn min_value() -> Self {
Wrapping(T::min_value())
}
fn max_value() -> Self {
Wrapping(T::max_value())
}
}
bounded_impl!(f32, f32::MIN, f32::MAX);
@ -53,9 +67,9 @@ macro_rules! for_each_tuple_ {
);
}
macro_rules! for_each_tuple {
( $m:ident ) => (
($m:ident) => {
for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
);
};
}
macro_rules! bounded_tuple {
@ -76,7 +90,6 @@ macro_rules! bounded_tuple {
for_each_tuple!(bounded_tuple);
bounded_impl!(f64, f64::MIN, f64::MAX);
#[test]
fn wrapping_bounded() {
macro_rules! test_wrapping_bounded {
@ -91,6 +104,21 @@ fn wrapping_bounded() {
test_wrapping_bounded!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[cfg(has_i128)]
#[test]
fn wrapping_bounded_i128() {
macro_rules! test_wrapping_bounded {
($($t:ty)+) => {
$(
assert_eq!(Wrapping::<$t>::min_value().0, <$t>::min_value());
assert_eq!(Wrapping::<$t>::max_value().0, <$t>::max_value());
)+
};
}
test_wrapping_bounded!(u128 i128);
}
#[test]
fn wrapping_is_bounded() {
fn require_bounded<T: Bounded>(_: &T) {}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
use core::ops::{Add, Mul};
use core::num::Wrapping;
use core::ops::{Add, Mul};
/// Defines an additive identity element for `Self`.
pub trait Zero: Sized + Add<Self, Output = Self> {
@ -17,7 +17,7 @@ pub trait Zero: Sized + Add<Self, Output = Self> {
/// This function should return the same result at all times regardless of
/// external mutable state, for example values stored in TLS or in
/// `static mut`s.
// FIXME (#5527): This should be an associated constant
// This cannot be an associated constant, because of bignums.
fn zero() -> Self;
/// Returns `true` if `self` is equal to the additive identity.
@ -29,29 +29,40 @@ macro_rules! zero_impl {
($t:ty, $v:expr) => {
impl Zero for $t {
#[inline]
fn zero() -> $t { $v }
fn zero() -> $t {
$v
}
#[inline]
fn is_zero(&self) -> bool { *self == $v }
fn is_zero(&self) -> bool {
*self == $v
}
}
}
};
}
zero_impl!(usize, 0usize);
zero_impl!(u8, 0u8);
zero_impl!(u16, 0u16);
zero_impl!(u32, 0u32);
zero_impl!(u64, 0u64);
zero_impl!(usize, 0);
zero_impl!(u8, 0);
zero_impl!(u16, 0);
zero_impl!(u32, 0);
zero_impl!(u64, 0);
#[cfg(has_i128)]
zero_impl!(u128, 0);
zero_impl!(isize, 0isize);
zero_impl!(i8, 0i8);
zero_impl!(i16, 0i16);
zero_impl!(i32, 0i32);
zero_impl!(i64, 0i64);
zero_impl!(isize, 0);
zero_impl!(i8, 0);
zero_impl!(i16, 0);
zero_impl!(i32, 0);
zero_impl!(i64, 0);
#[cfg(has_i128)]
zero_impl!(i128, 0);
zero_impl!(f32, 0.0f32);
zero_impl!(f64, 0.0f64);
zero_impl!(f32, 0.0);
zero_impl!(f64, 0.0);
impl<T: Zero> Zero for Wrapping<T> where Wrapping<T>: Add<Output=Wrapping<T>> {
impl<T: Zero> Zero for Wrapping<T>
where
Wrapping<T>: Add<Output = Wrapping<T>>,
{
fn is_zero(&self) -> bool {
self.0.is_zero()
}
@ -60,7 +71,6 @@ impl<T: Zero> Zero for Wrapping<T> where Wrapping<T>: Add<Output=Wrapping<T>> {
}
}
/// Defines a multiplicative identity element for `Self`.
pub trait One: Sized + Mul<Self, Output = Self> {
/// Returns the multiplicative identity element of `Self`, `1`.
@ -77,35 +87,57 @@ pub trait One: Sized + Mul<Self, Output = Self> {
/// This function should return the same result at all times regardless of
/// external mutable state, for example values stored in TLS or in
/// `static mut`s.
// FIXME (#5527): This should be an associated constant
// This cannot be an associated constant, because of bignums.
fn one() -> Self;
/// Returns `true` if `self` is equal to the multiplicative identity.
///
/// For performance reasons, it's best to implement this manually.
/// After a semver bump, this method will be required, and the
/// `where Self: PartialEq` bound will be removed.
#[inline]
fn is_one(&self) -> bool
where
Self: PartialEq,
{
*self == Self::one()
}
}
macro_rules! one_impl {
($t:ty, $v:expr) => {
impl One for $t {
#[inline]
fn one() -> $t { $v }
fn one() -> $t {
$v
}
}
}
};
}
one_impl!(usize, 1usize);
one_impl!(u8, 1u8);
one_impl!(u16, 1u16);
one_impl!(u32, 1u32);
one_impl!(u64, 1u64);
one_impl!(usize, 1);
one_impl!(u8, 1);
one_impl!(u16, 1);
one_impl!(u32, 1);
one_impl!(u64, 1);
#[cfg(has_i128)]
one_impl!(u128, 1);
one_impl!(isize, 1isize);
one_impl!(i8, 1i8);
one_impl!(i16, 1i16);
one_impl!(i32, 1i32);
one_impl!(i64, 1i64);
one_impl!(isize, 1);
one_impl!(i8, 1);
one_impl!(i16, 1);
one_impl!(i32, 1);
one_impl!(i64, 1);
#[cfg(has_i128)]
one_impl!(i128, 1);
one_impl!(f32, 1.0f32);
one_impl!(f64, 1.0f64);
one_impl!(f32, 1.0);
one_impl!(f64, 1.0);
impl<T: One> One for Wrapping<T> where Wrapping<T>: Mul<Output=Wrapping<T>> {
impl<T: One> One for Wrapping<T>
where
Wrapping<T>: Mul<Output = Wrapping<T>>,
{
fn one() -> Self {
Wrapping(T::one())
}
@ -114,11 +146,16 @@ impl<T: One> One for Wrapping<T> where Wrapping<T>: Mul<Output=Wrapping<T>> {
// Some helper functions provided for backwards compatibility.
/// Returns the additive identity, `0`.
#[inline(always)] pub fn zero<T: Zero>() -> T { Zero::zero() }
#[inline(always)]
pub fn zero<T: Zero>() -> T {
Zero::zero()
}
/// Returns the multiplicative identity, `1`.
#[inline(always)] pub fn one<T: One>() -> T { One::one() }
#[inline(always)]
pub fn one<T: One>() -> T {
One::one()
}
#[test]
fn wrapping_identities() {

View File

@ -1,26 +1,29 @@
use core::ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr};
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
use {Num, NumCast};
use bounds::Bounded;
use ops::checked::*;
use ops::saturating::Saturating;
use {Num, NumCast};
pub trait PrimInt
: Sized
pub trait PrimInt:
Sized
+ Copy
+ Num + NumCast
+ Num
+ NumCast
+ Bounded
+ PartialOrd + Ord + Eq
+ Not<Output=Self>
+ BitAnd<Output=Self>
+ BitOr<Output=Self>
+ BitXor<Output=Self>
+ Shl<usize, Output=Self>
+ Shr<usize, Output=Self>
+ CheckedAdd<Output=Self>
+ CheckedSub<Output=Self>
+ CheckedMul<Output=Self>
+ CheckedDiv<Output=Self>
+ PartialOrd
+ Ord
+ Eq
+ Not<Output = Self>
+ BitAnd<Output = Self>
+ BitOr<Output = Self>
+ BitXor<Output = Self>
+ Shl<usize, Output = Self>
+ Shr<usize, Output = Self>
+ CheckedAdd<Output = Self>
+ CheckedSub<Output = Self>
+ CheckedMul<Output = Self>
+ CheckedDiv<Output = Self>
+ Saturating
{
/// Returns the number of ones in the binary representation of `self`.
@ -278,7 +281,7 @@ pub trait PrimInt
}
macro_rules! prim_int_impl {
($T:ty, $S:ty, $U:ty) => (
($T:ty, $S:ty, $U:ty) => {
impl PrimInt for $T {
#[inline]
fn count_ones(self) -> u32 {
@ -360,17 +363,21 @@ macro_rules! prim_int_impl {
<$T>::pow(self, exp)
}
}
)
};
}
// prim_int_impl!(type, signed, unsigned);
prim_int_impl!(u8, i8, u8);
prim_int_impl!(u16, i16, u16);
prim_int_impl!(u32, i32, u32);
prim_int_impl!(u64, i64, u64);
prim_int_impl!(u8, i8, u8);
prim_int_impl!(u16, i16, u16);
prim_int_impl!(u32, i32, u32);
prim_int_impl!(u64, i64, u64);
#[cfg(has_i128)]
prim_int_impl!(u128, i128, u128);
prim_int_impl!(usize, isize, usize);
prim_int_impl!(i8, i8, u8);
prim_int_impl!(i16, i16, u16);
prim_int_impl!(i32, i32, u32);
prim_int_impl!(i64, i64, u64);
prim_int_impl!(i8, i8, u8);
prim_int_impl!(i16, i16, u16);
prim_int_impl!(i32, i32, u32);
prim_int_impl!(i64, i64, u64);
#[cfg(has_i128)]
prim_int_impl!(i128, i128, u128);
prim_int_impl!(isize, isize, usize);

View File

@ -15,47 +15,51 @@
//! The `num-traits` crate is tested for rustc 1.8 and greater.
#![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
#![deny(unconditional_recursion)]
#![cfg_attr(not(feature = "std"), no_std)]
#![no_std]
#[cfg(feature = "std")]
extern crate core;
extern crate std;
use core::ops::{Add, Sub, Mul, Div, Rem};
use core::ops::{AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
use core::num::Wrapping;
use core::fmt;
use core::num::Wrapping;
use core::ops::{Add, Div, Mul, Rem, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
pub use bounds::Bounded;
#[cfg(feature = "std")]
pub use float::Float;
pub use float::FloatConst;
// pub use real::Real; // NOTE: Don't do this, it breaks `use num_traits::*;`.
pub use identities::{Zero, One, zero, one};
pub use ops::checked::{CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedShl, CheckedShr};
pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingSub};
pub use ops::saturating::Saturating;
pub use sign::{Signed, Unsigned, abs, abs_sub, signum};
pub use cast::{AsPrimitive, FromPrimitive, ToPrimitive, NumCast, cast};
// pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`.
pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive};
pub use identities::{one, zero, One, Zero};
pub use int::PrimInt;
pub use pow::{pow, checked_pow};
pub use ops::checked::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
};
pub use ops::inv::Inv;
pub use ops::mul_add::{MulAdd, MulAddAssign};
pub use ops::saturating::Saturating;
pub use ops::wrapping::{WrappingAdd, WrappingMul, WrappingShl, WrappingShr, WrappingSub};
pub use pow::{checked_pow, pow, Pow};
pub use sign::{abs, abs_sub, signum, Signed, Unsigned};
#[macro_use]
mod macros;
pub mod identities;
pub mod sign;
pub mod ops;
pub mod bounds;
pub mod cast;
pub mod float;
pub mod identities;
pub mod int;
pub mod ops;
pub mod pow;
#[cfg(feature = "std")]
pub mod real;
pub mod cast;
pub mod int;
pub mod pow;
pub mod sign;
/// The base trait for numeric types, covering `0` and `1` values,
/// comparisons, basic numeric operations, and string conversion.
pub trait Num: PartialEq + Zero + One + NumOps
{
pub trait Num: PartialEq + Zero + One + NumOps {
type FromStrRadixErr;
/// Convert from a string and radix <= 36.
@ -77,68 +81,81 @@ pub trait Num: PartialEq + Zero + One + NumOps
/// The trait for types implementing basic numeric operations
///
/// This is automatically implemented for types which implement the operators.
pub trait NumOps<Rhs = Self, Output = Self>
: Add<Rhs, Output = Output>
pub trait NumOps<Rhs = Self, Output = Self>:
Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
{
}
impl<T, Rhs, Output> NumOps<Rhs, Output> for T
where T: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>
{}
where
T: Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Div<Rhs, Output = Output>
+ Rem<Rhs, Output = Output>,
{
}
/// The trait for `Num` types which also implement numeric operations taking
/// the second operand by reference.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumRef: Num + for<'r> NumOps<&'r Self> {}
impl<T> NumRef for T where T: Num + for<'r> NumOps<&'r T> {}
impl<T> NumRef for T
where
T: Num + for<'r> NumOps<&'r T>,
{
}
/// The trait for references which implement numeric operations, taking the
/// second operand either by value or by reference.
///
/// This is automatically implemented for types which implement the operators.
pub trait RefNum<Base>: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
impl<T, Base> RefNum<Base> for T where T: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
impl<T, Base> RefNum<Base> for T
where
T: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base>,
{
}
/// The trait for types implementing numeric assignment operators (like `+=`).
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssignOps<Rhs = Self>
: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
pub trait NumAssignOps<Rhs = Self>:
AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
{
}
impl<T, Rhs> NumAssignOps<Rhs> for T
where T: AddAssign<Rhs>
+ SubAssign<Rhs>
+ MulAssign<Rhs>
+ DivAssign<Rhs>
+ RemAssign<Rhs>
{}
where
T: AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>,
{
}
/// The trait for `Num` types which also implement assignment operators.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssign: Num + NumAssignOps {}
impl<T> NumAssign for T where T: Num + NumAssignOps {}
impl<T> NumAssign for T
where
T: Num + NumAssignOps,
{
}
/// The trait for `NumAssign` types which also implement assignment operations
/// taking the second operand by reference.
///
/// This is automatically implemented for types which implement the operators.
pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {}
impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}
impl<T> NumAssignRef for T
where
T: NumAssign + for<'r> NumAssignOps<&'r T>,
{
}
macro_rules! int_trait_impl {
($name:ident for $($t:ty)*) => ($(
@ -154,11 +171,16 @@ macro_rules! int_trait_impl {
)*)
}
int_trait_impl!(Num for usize u8 u16 u32 u64 isize i8 i16 i32 i64);
#[cfg(has_i128)]
int_trait_impl!(Num for u128 i128);
impl<T: Num> Num for Wrapping<T>
where Wrapping<T>:
Add<Output = Wrapping<T>> + Sub<Output = Wrapping<T>>
+ Mul<Output = Wrapping<T>> + Div<Output = Wrapping<T>> + Rem<Output = Wrapping<T>>
where
Wrapping<T>: Add<Output = Wrapping<T>>
+ Sub<Output = Wrapping<T>>
+ Mul<Output = Wrapping<T>>
+ Div<Output = Wrapping<T>>
+ Rem<Output = Wrapping<T>>,
{
type FromStrRadixErr = T::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
@ -166,7 +188,6 @@ impl<T: Num> Num for Wrapping<T>
}
}
#[derive(Debug)]
pub enum FloatErrorKind {
Empty,
@ -434,7 +455,8 @@ fn check_numref_ops() {
#[test]
fn check_refnum_ops() {
fn compute<T: Copy>(x: &T, y: T) -> T
where for<'a> &'a T: RefNum<T>
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}
@ -444,7 +466,8 @@ fn check_refnum_ops() {
#[test]
fn check_refref_ops() {
fn compute<T>(x: &T, y: &T) -> T
where for<'a> &'a T: RefNum<T>
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}

View File

@ -0,0 +1,37 @@
// not all are used in all features configurations
#![allow(unused)]
/// Forward a method to an inherent method or a base trait method.
macro_rules! forward {
($( Self :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
=> {$(
#[inline]
fn $method(self $( , $arg : $ty )* ) -> $ret {
Self::$method(self $( , $arg )* )
}
)*};
($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*)
=> {$(
#[inline]
fn $method(self $( , $arg : $ty )* ) -> $ret {
<Self as $base>::$method(self $( , $arg )* )
}
)*};
($( $base:ident :: $method:ident ( $( $arg:ident : $ty:ty ),* ) -> $ret:ty ; )*)
=> {$(
#[inline]
fn $method( $( $arg : $ty ),* ) -> $ret {
<Self as $base>::$method( $( $arg ),* )
}
)*}
}
macro_rules! constant {
($( $method:ident () -> $ret:expr ; )*)
=> {$(
#[inline]
fn $method() -> Self {
$ret
}
)*};
}

View File

@ -1,8 +1,8 @@
use core::ops::{Add, Sub, Mul, Div, Shl, Shr};
use core::ops::{Add, Div, Mul, Rem, Shl, Shr, Sub};
/// Performs addition that returns `None` instead of wrapping around on
/// overflow.
pub trait CheckedAdd: Sized + Add<Self, Output=Self> {
pub trait CheckedAdd: Sized + Add<Self, Output = Self> {
/// Adds two numbers, checking for overflow. If overflow happens, `None` is
/// returned.
fn checked_add(&self, v: &Self) -> Option<Self>;
@ -16,7 +16,7 @@ macro_rules! checked_impl {
<$t>::$method(*self, *v)
}
}
}
};
}
checked_impl!(CheckedAdd, checked_add, u8);
@ -24,15 +24,19 @@ checked_impl!(CheckedAdd, checked_add, u16);
checked_impl!(CheckedAdd, checked_add, u32);
checked_impl!(CheckedAdd, checked_add, u64);
checked_impl!(CheckedAdd, checked_add, usize);
#[cfg(has_i128)]
checked_impl!(CheckedAdd, checked_add, u128);
checked_impl!(CheckedAdd, checked_add, i8);
checked_impl!(CheckedAdd, checked_add, i16);
checked_impl!(CheckedAdd, checked_add, i32);
checked_impl!(CheckedAdd, checked_add, i64);
checked_impl!(CheckedAdd, checked_add, isize);
#[cfg(has_i128)]
checked_impl!(CheckedAdd, checked_add, i128);
/// Performs subtraction that returns `None` instead of wrapping around on underflow.
pub trait CheckedSub: Sized + Sub<Self, Output=Self> {
pub trait CheckedSub: Sized + Sub<Self, Output = Self> {
/// Subtracts two numbers, checking for underflow. If underflow happens,
/// `None` is returned.
fn checked_sub(&self, v: &Self) -> Option<Self>;
@ -43,16 +47,20 @@ checked_impl!(CheckedSub, checked_sub, u16);
checked_impl!(CheckedSub, checked_sub, u32);
checked_impl!(CheckedSub, checked_sub, u64);
checked_impl!(CheckedSub, checked_sub, usize);
#[cfg(has_i128)]
checked_impl!(CheckedSub, checked_sub, u128);
checked_impl!(CheckedSub, checked_sub, i8);
checked_impl!(CheckedSub, checked_sub, i16);
checked_impl!(CheckedSub, checked_sub, i32);
checked_impl!(CheckedSub, checked_sub, i64);
checked_impl!(CheckedSub, checked_sub, isize);
#[cfg(has_i128)]
checked_impl!(CheckedSub, checked_sub, i128);
/// Performs multiplication that returns `None` instead of wrapping around on underflow or
/// overflow.
pub trait CheckedMul: Sized + Mul<Self, Output=Self> {
pub trait CheckedMul: Sized + Mul<Self, Output = Self> {
/// Multiplies two numbers, checking for underflow or overflow. If underflow
/// or overflow happens, `None` is returned.
fn checked_mul(&self, v: &Self) -> Option<Self>;
@ -63,16 +71,20 @@ checked_impl!(CheckedMul, checked_mul, u16);
checked_impl!(CheckedMul, checked_mul, u32);
checked_impl!(CheckedMul, checked_mul, u64);
checked_impl!(CheckedMul, checked_mul, usize);
#[cfg(has_i128)]
checked_impl!(CheckedMul, checked_mul, u128);
checked_impl!(CheckedMul, checked_mul, i8);
checked_impl!(CheckedMul, checked_mul, i16);
checked_impl!(CheckedMul, checked_mul, i32);
checked_impl!(CheckedMul, checked_mul, i64);
checked_impl!(CheckedMul, checked_mul, isize);
#[cfg(has_i128)]
checked_impl!(CheckedMul, checked_mul, i128);
/// Performs division that returns `None` instead of panicking on division by zero and instead of
/// wrapping around on underflow and overflow.
pub trait CheckedDiv: Sized + Div<Self, Output=Self> {
pub trait CheckedDiv: Sized + Div<Self, Output = Self> {
/// Divides two numbers, checking for underflow, overflow and division by
/// zero. If any of that happens, `None` is returned.
fn checked_div(&self, v: &Self) -> Option<Self>;
@ -83,15 +95,108 @@ checked_impl!(CheckedDiv, checked_div, u16);
checked_impl!(CheckedDiv, checked_div, u32);
checked_impl!(CheckedDiv, checked_div, u64);
checked_impl!(CheckedDiv, checked_div, usize);
#[cfg(has_i128)]
checked_impl!(CheckedDiv, checked_div, u128);
checked_impl!(CheckedDiv, checked_div, i8);
checked_impl!(CheckedDiv, checked_div, i16);
checked_impl!(CheckedDiv, checked_div, i32);
checked_impl!(CheckedDiv, checked_div, i64);
checked_impl!(CheckedDiv, checked_div, isize);
#[cfg(has_i128)]
checked_impl!(CheckedDiv, checked_div, i128);
/// Performs an integral remainder that returns `None` instead of panicking on division by zero and
/// instead of wrapping around on underflow and overflow.
pub trait CheckedRem: Sized + Rem<Self, Output = Self> {
/// Finds the remainder of dividing two numbers, checking for underflow, overflow and division
/// by zero. If any of that happens, `None` is returned.
///
/// # Examples
///
/// ```
/// use num_traits::CheckedRem;
/// use std::i32::MIN;
///
/// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3));
/// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3));
/// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3));
/// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3));
///
/// assert_eq!(CheckedRem::checked_rem(&10, &0), None);
///
/// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0));
/// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None);
/// ```
fn checked_rem(&self, v: &Self) -> Option<Self>;
}
checked_impl!(CheckedRem, checked_rem, u8);
checked_impl!(CheckedRem, checked_rem, u16);
checked_impl!(CheckedRem, checked_rem, u32);
checked_impl!(CheckedRem, checked_rem, u64);
checked_impl!(CheckedRem, checked_rem, usize);
#[cfg(has_i128)]
checked_impl!(CheckedRem, checked_rem, u128);
checked_impl!(CheckedRem, checked_rem, i8);
checked_impl!(CheckedRem, checked_rem, i16);
checked_impl!(CheckedRem, checked_rem, i32);
checked_impl!(CheckedRem, checked_rem, i64);
checked_impl!(CheckedRem, checked_rem, isize);
#[cfg(has_i128)]
checked_impl!(CheckedRem, checked_rem, i128);
macro_rules! checked_impl_unary {
($trait_name:ident, $method:ident, $t:ty) => {
impl $trait_name for $t {
#[inline]
fn $method(&self) -> Option<$t> {
<$t>::$method(*self)
}
}
};
}
/// Performs negation that returns `None` if the result can't be represented.
pub trait CheckedNeg: Sized {
/// Negates a number, returning `None` for results that can't be represented, like signed `MIN`
/// values that can't be positive, or non-zero unsigned values that can't be negative.
///
/// # Examples
///
/// ```
/// use num_traits::CheckedNeg;
/// use std::i32::MIN;
///
/// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1));
/// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1));
/// assert_eq!(CheckedNeg::checked_neg(&MIN), None);
///
/// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0));
/// assert_eq!(CheckedNeg::checked_neg(&1_u32), None);
/// ```
fn checked_neg(&self) -> Option<Self>;
}
checked_impl_unary!(CheckedNeg, checked_neg, u8);
checked_impl_unary!(CheckedNeg, checked_neg, u16);
checked_impl_unary!(CheckedNeg, checked_neg, u32);
checked_impl_unary!(CheckedNeg, checked_neg, u64);
checked_impl_unary!(CheckedNeg, checked_neg, usize);
#[cfg(has_i128)]
checked_impl_unary!(CheckedNeg, checked_neg, u128);
checked_impl_unary!(CheckedNeg, checked_neg, i8);
checked_impl_unary!(CheckedNeg, checked_neg, i16);
checked_impl_unary!(CheckedNeg, checked_neg, i32);
checked_impl_unary!(CheckedNeg, checked_neg, i64);
checked_impl_unary!(CheckedNeg, checked_neg, isize);
#[cfg(has_i128)]
checked_impl_unary!(CheckedNeg, checked_neg, i128);
/// Performs a left shift that returns `None` on overflow.
pub trait CheckedShl: Sized + Shl<u32, Output=Self> {
pub trait CheckedShl: Sized + Shl<u32, Output = Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens,
/// `None` is returned.
///
@ -116,7 +221,7 @@ macro_rules! checked_shift_impl {
<$t>::$method(*self, rhs)
}
}
}
};
}
checked_shift_impl!(CheckedShl, checked_shl, u8);
@ -124,15 +229,19 @@ checked_shift_impl!(CheckedShl, checked_shl, u16);
checked_shift_impl!(CheckedShl, checked_shl, u32);
checked_shift_impl!(CheckedShl, checked_shl, u64);
checked_shift_impl!(CheckedShl, checked_shl, usize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShl, checked_shl, u128);
checked_shift_impl!(CheckedShl, checked_shl, i8);
checked_shift_impl!(CheckedShl, checked_shl, i16);
checked_shift_impl!(CheckedShl, checked_shl, i32);
checked_shift_impl!(CheckedShl, checked_shl, i64);
checked_shift_impl!(CheckedShl, checked_shl, isize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShl, checked_shl, i128);
/// Performs a right shift that returns `None` on overflow.
pub trait CheckedShr: Sized + Shr<u32, Output=Self> {
pub trait CheckedShr: Sized + Shr<u32, Output = Self> {
/// Shifts a number to the left, checking for overflow. If overflow happens,
/// `None` is returned.
///
@ -154,9 +263,13 @@ checked_shift_impl!(CheckedShr, checked_shr, u16);
checked_shift_impl!(CheckedShr, checked_shr, u32);
checked_shift_impl!(CheckedShr, checked_shr, u64);
checked_shift_impl!(CheckedShr, checked_shr, usize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShr, checked_shr, u128);
checked_shift_impl!(CheckedShr, checked_shr, i8);
checked_shift_impl!(CheckedShr, checked_shr, i16);
checked_shift_impl!(CheckedShr, checked_shr, i32);
checked_shift_impl!(CheckedShr, checked_shr, i64);
checked_shift_impl!(CheckedShr, checked_shr, isize);
#[cfg(has_i128)]
checked_shift_impl!(CheckedShr, checked_shr, i128);

View File

@ -0,0 +1,47 @@
/// Unary operator for retrieving the multiplicative inverse, or reciprocal, of a value.
pub trait Inv {
/// The result after applying the operator.
type Output;
/// Returns the multiplicative inverse of `self`.
///
/// # Examples
///
/// ```
/// use std::f64::INFINITY;
/// use num_traits::Inv;
///
/// assert_eq!(7.0.inv() * 7.0, 1.0);
/// assert_eq!((-0.0).inv(), -INFINITY);
/// ```
fn inv(self) -> Self::Output;
}
impl Inv for f32 {
type Output = f32;
#[inline]
fn inv(self) -> f32 {
1.0 / self
}
}
impl Inv for f64 {
type Output = f64;
#[inline]
fn inv(self) -> f64 {
1.0 / self
}
}
impl<'a> Inv for &'a f32 {
type Output = f32;
#[inline]
fn inv(self) -> f32 {
1.0 / *self
}
}
impl<'a> Inv for &'a f64 {
type Output = f64;
#[inline]
fn inv(self) -> f64 {
1.0 / *self
}
}

View File

@ -1,3 +1,5 @@
pub mod saturating;
pub mod checked;
pub mod inv;
pub mod mul_add;
pub mod saturating;
pub mod wrapping;

View File

@ -0,0 +1,151 @@
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// Note that `A` and `B` are `Self` by default, but this is not mandatory.
///
/// # Example
///
/// ```
/// use std::f32;
///
/// let m = 10.0_f32;
/// let x = 4.0_f32;
/// let b = 60.0_f32;
///
/// // 100.0
/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs();
///
/// assert!(abs_difference <= f32::EPSILON);
/// ```
pub trait MulAdd<A = Self, B = Self> {
/// The resulting type after applying the fused multiply-add.
type Output;
/// Performs the fused multiply-add operation.
fn mul_add(self, a: A, b: B) -> Self::Output;
}
/// The fused multiply-add assignment operation.
pub trait MulAddAssign<A = Self, B = Self> {
/// Performs the fused multiply-add operation.
fn mul_add_assign(&mut self, a: A, b: B);
}
#[cfg(feature = "std")]
impl MulAdd<f32, f32> for f32 {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
f32::mul_add(self, a, b)
}
}
#[cfg(feature = "std")]
impl MulAdd<f64, f64> for f64 {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
f64::mul_add(self, a, b)
}
}
macro_rules! mul_add_impl {
($trait_name:ident for $($t:ty)*) => {$(
impl $trait_name for $t {
type Output = Self;
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self::Output {
(self * a) + b
}
}
)*}
}
mul_add_impl!(MulAdd for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_impl!(MulAdd for i128 u128);
#[cfg(feature = "std")]
impl MulAddAssign<f32, f32> for f32 {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = f32::mul_add(*self, a, b)
}
}
#[cfg(feature = "std")]
impl MulAddAssign<f64, f64> for f64 {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = f64::mul_add(*self, a, b)
}
}
macro_rules! mul_add_assign_impl {
($trait_name:ident for $($t:ty)*) => {$(
impl $trait_name for $t {
#[inline]
fn mul_add_assign(&mut self, a: Self, b: Self) {
*self = (*self * a) + b
}
}
)*}
}
mul_add_assign_impl!(MulAddAssign for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
mul_add_assign_impl!(MulAddAssign for i128 u128);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mul_add_integer() {
macro_rules! test_mul_add {
($($t:ident)+) => {
$(
{
let m: $t = 2;
let x: $t = 3;
let b: $t = 4;
assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b));
}
)+
};
}
test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[test]
#[cfg(feature = "std")]
fn mul_add_float() {
macro_rules! test_mul_add {
($($t:ident)+) => {
$(
{
use core::$t;
let m: $t = 12.0;
let x: $t = 3.4;
let b: $t = 5.6;
let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs();
assert!(abs_difference <= $t::EPSILON);
}
)+
};
}
test_mul_add!(f32 f64);
}
}

View File

@ -26,3 +26,5 @@ macro_rules! saturating_impl {
}
saturating_impl!(Saturating for isize usize i8 u8 i16 u16 i32 u32 i64 u64);
#[cfg(has_i128)]
saturating_impl!(Saturating for i128 u128);

View File

@ -1,5 +1,5 @@
use core::ops::{Add, Sub, Mul};
use core::num::Wrapping;
use core::ops::{Add, Mul, Shl, Shr, Sub};
macro_rules! wrapping_impl {
($trait_name:ident, $method:ident, $t:ty) => {
@ -17,11 +17,11 @@ macro_rules! wrapping_impl {
<$t>::$method(*self, *v)
}
}
}
};
}
/// Performs addition that wraps around on overflow.
pub trait WrappingAdd: Sized + Add<Self, Output=Self> {
pub trait WrappingAdd: Sized + Add<Self, Output = Self> {
/// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of
/// the type.
fn wrapping_add(&self, v: &Self) -> Self;
@ -32,15 +32,19 @@ wrapping_impl!(WrappingAdd, wrapping_add, u16);
wrapping_impl!(WrappingAdd, wrapping_add, u32);
wrapping_impl!(WrappingAdd, wrapping_add, u64);
wrapping_impl!(WrappingAdd, wrapping_add, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingAdd, wrapping_add, u128);
wrapping_impl!(WrappingAdd, wrapping_add, i8);
wrapping_impl!(WrappingAdd, wrapping_add, i16);
wrapping_impl!(WrappingAdd, wrapping_add, i32);
wrapping_impl!(WrappingAdd, wrapping_add, i64);
wrapping_impl!(WrappingAdd, wrapping_add, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingAdd, wrapping_add, i128);
/// Performs subtraction that wraps around on overflow.
pub trait WrappingSub: Sized + Sub<Self, Output=Self> {
pub trait WrappingSub: Sized + Sub<Self, Output = Self> {
/// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary
/// of the type.
fn wrapping_sub(&self, v: &Self) -> Self;
@ -51,15 +55,19 @@ wrapping_impl!(WrappingSub, wrapping_sub, u16);
wrapping_impl!(WrappingSub, wrapping_sub, u32);
wrapping_impl!(WrappingSub, wrapping_sub, u64);
wrapping_impl!(WrappingSub, wrapping_sub, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingSub, wrapping_sub, u128);
wrapping_impl!(WrappingSub, wrapping_sub, i8);
wrapping_impl!(WrappingSub, wrapping_sub, i16);
wrapping_impl!(WrappingSub, wrapping_sub, i32);
wrapping_impl!(WrappingSub, wrapping_sub, i64);
wrapping_impl!(WrappingSub, wrapping_sub, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingSub, wrapping_sub, i128);
/// Performs multiplication that wraps around on overflow.
pub trait WrappingMul: Sized + Mul<Self, Output=Self> {
pub trait WrappingMul: Sized + Mul<Self, Output = Self> {
/// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary
/// of the type.
fn wrapping_mul(&self, v: &Self) -> Self;
@ -70,42 +78,167 @@ wrapping_impl!(WrappingMul, wrapping_mul, u16);
wrapping_impl!(WrappingMul, wrapping_mul, u32);
wrapping_impl!(WrappingMul, wrapping_mul, u64);
wrapping_impl!(WrappingMul, wrapping_mul, usize);
#[cfg(has_i128)]
wrapping_impl!(WrappingMul, wrapping_mul, u128);
wrapping_impl!(WrappingMul, wrapping_mul, i8);
wrapping_impl!(WrappingMul, wrapping_mul, i16);
wrapping_impl!(WrappingMul, wrapping_mul, i32);
wrapping_impl!(WrappingMul, wrapping_mul, i64);
wrapping_impl!(WrappingMul, wrapping_mul, isize);
#[cfg(has_i128)]
wrapping_impl!(WrappingMul, wrapping_mul, i128);
macro_rules! wrapping_shift_impl {
($trait_name:ident, $method:ident, $t:ty) => {
impl $trait_name for $t {
#[inline]
fn $method(&self, rhs: u32) -> $t {
<$t>::$method(*self, rhs)
}
}
};
}
/// Performs a left shift that does not panic.
pub trait WrappingShl: Sized + Shl<usize, Output = Self> {
/// Panic-free bitwise shift-left; yields `self << mask(rhs)`,
/// where `mask` removes any high order bits of `rhs` that would
/// cause the shift to exceed the bitwidth of the type.
///
/// ```
/// use num_traits::WrappingShl;
///
/// let x: u16 = 0x0001;
///
/// assert_eq!(WrappingShl::wrapping_shl(&x, 0), 0x0001);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 1), 0x0002);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 15), 0x8000);
/// assert_eq!(WrappingShl::wrapping_shl(&x, 16), 0x0001);
/// ```
fn wrapping_shl(&self, rhs: u32) -> Self;
}
wrapping_shift_impl!(WrappingShl, wrapping_shl, u8);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u16);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u32);
wrapping_shift_impl!(WrappingShl, wrapping_shl, u64);
wrapping_shift_impl!(WrappingShl, wrapping_shl, usize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShl, wrapping_shl, u128);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i8);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i16);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i32);
wrapping_shift_impl!(WrappingShl, wrapping_shl, i64);
wrapping_shift_impl!(WrappingShl, wrapping_shl, isize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShl, wrapping_shl, i128);
/// Performs a right shift that does not panic.
pub trait WrappingShr: Sized + Shr<usize, Output = Self> {
/// Panic-free bitwise shift-right; yields `self >> mask(rhs)`,
/// where `mask` removes any high order bits of `rhs` that would
/// cause the shift to exceed the bitwidth of the type.
///
/// ```
/// use num_traits::WrappingShr;
///
/// let x: u16 = 0x8000;
///
/// assert_eq!(WrappingShr::wrapping_shr(&x, 0), 0x8000);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 1), 0x4000);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 15), 0x0001);
/// assert_eq!(WrappingShr::wrapping_shr(&x, 16), 0x8000);
/// ```
fn wrapping_shr(&self, rhs: u32) -> Self;
}
wrapping_shift_impl!(WrappingShr, wrapping_shr, u8);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u16);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u32);
wrapping_shift_impl!(WrappingShr, wrapping_shr, u64);
wrapping_shift_impl!(WrappingShr, wrapping_shr, usize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShr, wrapping_shr, u128);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i8);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i16);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i32);
wrapping_shift_impl!(WrappingShr, wrapping_shr, i64);
wrapping_shift_impl!(WrappingShr, wrapping_shr, isize);
#[cfg(has_i128)]
wrapping_shift_impl!(WrappingShr, wrapping_shr, i128);
// Well this is a bit funny, but all the more appropriate.
impl<T: WrappingAdd> WrappingAdd for Wrapping<T> where Wrapping<T>: Add<Output = Wrapping<T>> {
impl<T: WrappingAdd> WrappingAdd for Wrapping<T>
where
Wrapping<T>: Add<Output = Wrapping<T>>,
{
fn wrapping_add(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_add(&v.0))
}
}
impl<T: WrappingSub> WrappingSub for Wrapping<T> where Wrapping<T>: Sub<Output = Wrapping<T>> {
impl<T: WrappingSub> WrappingSub for Wrapping<T>
where
Wrapping<T>: Sub<Output = Wrapping<T>>,
{
fn wrapping_sub(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_sub(&v.0))
}
}
impl<T: WrappingMul> WrappingMul for Wrapping<T> where Wrapping<T>: Mul<Output = Wrapping<T>> {
impl<T: WrappingMul> WrappingMul for Wrapping<T>
where
Wrapping<T>: Mul<Output = Wrapping<T>>,
{
fn wrapping_mul(&self, v: &Self) -> Self {
Wrapping(self.0.wrapping_mul(&v.0))
}
}
impl<T: WrappingShl> WrappingShl for Wrapping<T>
where
Wrapping<T>: Shl<usize, Output = Wrapping<T>>,
{
fn wrapping_shl(&self, rhs: u32) -> Self {
Wrapping(self.0.wrapping_shl(rhs))
}
}
impl<T: WrappingShr> WrappingShr for Wrapping<T>
where
Wrapping<T>: Shr<usize, Output = Wrapping<T>>,
{
fn wrapping_shr(&self, rhs: u32) -> Self {
Wrapping(self.0.wrapping_shr(rhs))
}
}
#[test]
fn test_wrapping_traits() {
fn wrapping_add<T: WrappingAdd>(a: T, b: T) -> T { a.wrapping_add(&b) }
fn wrapping_sub<T: WrappingSub>(a: T, b: T) -> T { a.wrapping_sub(&b) }
fn wrapping_mul<T: WrappingMul>(a: T, b: T) -> T { a.wrapping_mul(&b) }
fn wrapping_add<T: WrappingAdd>(a: T, b: T) -> T {
a.wrapping_add(&b)
}
fn wrapping_sub<T: WrappingSub>(a: T, b: T) -> T {
a.wrapping_sub(&b)
}
fn wrapping_mul<T: WrappingMul>(a: T, b: T) -> T {
a.wrapping_mul(&b)
}
fn wrapping_shl<T: WrappingShl>(a: T, b: u32) -> T {
a.wrapping_shl(b)
}
fn wrapping_shr<T: WrappingShr>(a: T, b: u32) -> T {
a.wrapping_shr(b)
}
assert_eq!(wrapping_add(255, 1), 0u8);
assert_eq!(wrapping_sub(0, 1), 255u8);
assert_eq!(wrapping_mul(255, 2), 254u8);
assert_eq!(wrapping_shl(255, 8), 255u8);
assert_eq!(wrapping_shr(255, 8), 255u8);
assert_eq!(wrapping_add(255, 1), (Wrapping(255u8) + Wrapping(1u8)).0);
assert_eq!(wrapping_sub(0, 1), (Wrapping(0u8) - Wrapping(1u8)).0);
assert_eq!(wrapping_mul(255, 2), (Wrapping(255u8) * Wrapping(2u8)).0);
assert_eq!(wrapping_shl(255, 8), (Wrapping(255u8) << 8).0);
assert_eq!(wrapping_shr(255, 8), (Wrapping(255u8) >> 8).0);
}
#[test]
@ -125,3 +258,15 @@ fn wrapping_is_wrappingmul() {
fn require_wrappingmul<T: WrappingMul>(_: &T) {}
require_wrappingmul(&Wrapping(42));
}
#[test]
fn wrapping_is_wrappingshl() {
fn require_wrappingshl<T: WrappingShl>(_: &T) {}
require_wrappingshl(&Wrapping(42));
}
#[test]
fn wrapping_is_wrappingshr() {
fn require_wrappingshr<T: WrappingShr>(_: &T) {}
require_wrappingshr(&Wrapping(42));
}

View File

@ -1,8 +1,180 @@
use core::num::Wrapping;
use core::ops::Mul;
use {One, CheckedMul};
use {CheckedMul, One};
/// Binary operator for raising a value to a power.
pub trait Pow<RHS> {
/// The result after applying the operator.
type Output;
/// Returns `self` to the power `rhs`.
///
/// # Examples
///
/// ```
/// use num_traits::Pow;
/// assert_eq!(Pow::pow(10u32, 2u32), 100);
/// ```
fn pow(self, rhs: RHS) -> Self::Output;
}
macro_rules! pow_impl {
($t:ty) => {
pow_impl!($t, u8);
pow_impl!($t, usize);
// FIXME: these should be possible
// pow_impl!($t, u16);
// pow_impl!($t, u32);
// pow_impl!($t, u64);
};
($t:ty, $rhs:ty) => {
pow_impl!($t, $rhs, usize, pow);
};
($t:ty, $rhs:ty, $desired_rhs:ty, $method:expr) => {
impl Pow<$rhs> for $t {
type Output = $t;
#[inline]
fn pow(self, rhs: $rhs) -> $t {
($method)(self, <$desired_rhs>::from(rhs))
}
}
impl<'a> Pow<&'a $rhs> for $t {
type Output = $t;
#[inline]
fn pow(self, rhs: &'a $rhs) -> $t {
($method)(self, <$desired_rhs>::from(*rhs))
}
}
impl<'a> Pow<$rhs> for &'a $t {
type Output = $t;
#[inline]
fn pow(self, rhs: $rhs) -> $t {
($method)(*self, <$desired_rhs>::from(rhs))
}
}
impl<'a, 'b> Pow<&'a $rhs> for &'b $t {
type Output = $t;
#[inline]
fn pow(self, rhs: &'a $rhs) -> $t {
($method)(*self, <$desired_rhs>::from(*rhs))
}
}
};
}
pow_impl!(u8, u8, u32, u8::pow);
pow_impl!(u8, u16, u32, u8::pow);
pow_impl!(u8, u32, u32, u8::pow);
pow_impl!(u8, usize);
pow_impl!(i8, u8, u32, i8::pow);
pow_impl!(i8, u16, u32, i8::pow);
pow_impl!(i8, u32, u32, i8::pow);
pow_impl!(i8, usize);
pow_impl!(u16, u8, u32, u16::pow);
pow_impl!(u16, u16, u32, u16::pow);
pow_impl!(u16, u32, u32, u16::pow);
pow_impl!(u16, usize);
pow_impl!(i16, u8, u32, i16::pow);
pow_impl!(i16, u16, u32, i16::pow);
pow_impl!(i16, u32, u32, i16::pow);
pow_impl!(i16, usize);
pow_impl!(u32, u8, u32, u32::pow);
pow_impl!(u32, u16, u32, u32::pow);
pow_impl!(u32, u32, u32, u32::pow);
pow_impl!(u32, usize);
pow_impl!(i32, u8, u32, i32::pow);
pow_impl!(i32, u16, u32, i32::pow);
pow_impl!(i32, u32, u32, i32::pow);
pow_impl!(i32, usize);
pow_impl!(u64, u8, u32, u64::pow);
pow_impl!(u64, u16, u32, u64::pow);
pow_impl!(u64, u32, u32, u64::pow);
pow_impl!(u64, usize);
pow_impl!(i64, u8, u32, i64::pow);
pow_impl!(i64, u16, u32, i64::pow);
pow_impl!(i64, u32, u32, i64::pow);
pow_impl!(i64, usize);
#[cfg(has_i128)]
pow_impl!(u128, u8, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, u16, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, u32, u32, u128::pow);
#[cfg(has_i128)]
pow_impl!(u128, usize);
#[cfg(has_i128)]
pow_impl!(i128, u8, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, u16, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, u32, u32, i128::pow);
#[cfg(has_i128)]
pow_impl!(i128, usize);
pow_impl!(usize, u8, u32, usize::pow);
pow_impl!(usize, u16, u32, usize::pow);
pow_impl!(usize, u32, u32, usize::pow);
pow_impl!(usize, usize);
pow_impl!(isize, u8, u32, isize::pow);
pow_impl!(isize, u16, u32, isize::pow);
pow_impl!(isize, u32, u32, isize::pow);
pow_impl!(isize, usize);
pow_impl!(Wrapping<u8>);
pow_impl!(Wrapping<i8>);
pow_impl!(Wrapping<u16>);
pow_impl!(Wrapping<i16>);
pow_impl!(Wrapping<u32>);
pow_impl!(Wrapping<i32>);
pow_impl!(Wrapping<u64>);
pow_impl!(Wrapping<i64>);
#[cfg(has_i128)]
pow_impl!(Wrapping<u128>);
#[cfg(has_i128)]
pow_impl!(Wrapping<i128>);
pow_impl!(Wrapping<usize>);
pow_impl!(Wrapping<isize>);
// FIXME: these should be possible
// pow_impl!(u8, u64);
// pow_impl!(i16, u64);
// pow_impl!(i8, u64);
// pow_impl!(u16, u64);
// pow_impl!(u32, u64);
// pow_impl!(i32, u64);
// pow_impl!(u64, u64);
// pow_impl!(i64, u64);
// pow_impl!(usize, u64);
// pow_impl!(isize, u64);
#[cfg(feature = "std")]
mod float_impls {
use super::Pow;
pow_impl!(f32, i8, i32, f32::powi);
pow_impl!(f32, u8, i32, f32::powi);
pow_impl!(f32, i16, i32, f32::powi);
pow_impl!(f32, u16, i32, f32::powi);
pow_impl!(f32, i32, i32, f32::powi);
pow_impl!(f64, i8, i32, f64::powi);
pow_impl!(f64, u8, i32, f64::powi);
pow_impl!(f64, i16, i32, f64::powi);
pow_impl!(f64, u16, i32, f64::powi);
pow_impl!(f64, i32, i32, f64::powi);
pow_impl!(f32, f32, f32, f32::powf);
pow_impl!(f64, f32, f64, f64::powf);
pow_impl!(f64, f64, f64, f64::powf);
}
/// Raises a value to the power of exp, using exponentiation by squaring.
///
/// Note that `0⁰` (`pow(0, 0)`) returnes `1`. Mathematically this is undefined.
///
/// # Example
///
/// ```rust
@ -10,16 +182,21 @@ use {One, CheckedMul};
///
/// assert_eq!(pow(2i8, 4), 16);
/// assert_eq!(pow(6u8, 3), 216);
/// assert_eq!(pow(0u8, 0), 1); // Be aware if this case affects you
/// ```
#[inline]
pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) -> T {
if exp == 0 { return T::one() }
if exp == 0 {
return T::one();
}
while exp & 1 == 0 {
base = base.clone() * base;
exp >>= 1;
}
if exp == 1 { return base }
if exp == 1 {
return base;
}
let mut acc = base.clone();
while exp > 1 {
@ -34,6 +211,8 @@ pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) ->
/// Raises a value to the power of exp, returning `None` if an overflow occurred.
///
/// Note that `0⁰` (`checked_pow(0, 0)`) returnes `Some(1)`. Mathematically this is undefined.
///
/// Otherwise same as the `pow` function.
///
/// # Example
@ -44,22 +223,31 @@ pub fn pow<T: Clone + One + Mul<T, Output = T>>(mut base: T, mut exp: usize) ->
/// assert_eq!(checked_pow(2i8, 4), Some(16));
/// assert_eq!(checked_pow(7i8, 8), None);
/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801));
/// assert_eq!(checked_pow(0u32, 0), Some(1)); // Be aware if this case affect you
/// ```
#[inline]
pub fn checked_pow<T: Clone + One + CheckedMul>(mut base: T, mut exp: usize) -> Option<T> {
if exp == 0 { return Some(T::one()) }
if exp == 0 {
return Some(T::one());
}
macro_rules! optry {
( $ expr : expr ) => {
if let Some(val) = $expr { val } else { return None }
}
($expr:expr) => {
if let Some(val) = $expr {
val
} else {
return None;
}
};
}
while exp & 1 == 0 {
base = optry!(base.checked_mul(&base));
exp >>= 1;
}
if exp == 1 { return Some(base) }
if exp == 1 {
return Some(base);
}
let mut acc = base.clone();
while exp > 1 {

View File

@ -1,6 +1,6 @@
use std::ops::Neg;
use {Num, NumCast, Float};
use {Float, Num, NumCast};
// NOTE: These doctests have the same issue as those in src/float.rs.
// They're testing the inherent methods directly, and not those of `Real`.
@ -12,13 +12,7 @@ use {Num, NumCast, Float};
/// for a list of data types that could meaningfully implement this trait.
///
/// This trait is only available with the `std` feature.
pub trait Real
: Num
+ Copy
+ NumCast
+ PartialOrd
+ Neg<Output = Self>
{
pub trait Real: Num + Copy + NumCast + PartialOrd + Neg<Output = Self> {
/// Returns the smallest finite value that this type can represent.
///
/// ```
@ -215,8 +209,10 @@ pub trait Real
fn is_sign_negative(self) -> bool;
/// Fused multiply-add. Computes `(self * a) + b` with only one rounding
/// error. This produces a more accurate result with better performance than
/// a separate multiplication operation followed by an add.
/// error, yielding a more accurate result than an unfused multiply-add.
///
/// Using `mul_add` can be more performant than an unfused multiply-add if
/// the target architecture has a dedicated `fma` CPU instruction.
///
/// ```
/// use num_traits::real::Real;
@ -782,145 +778,55 @@ pub trait Real
}
impl<T: Float> Real for T {
fn min_value() -> Self {
Self::min_value()
forward! {
Float::min_value() -> Self;
Float::min_positive_value() -> Self;
Float::epsilon() -> Self;
Float::max_value() -> Self;
}
fn min_positive_value() -> Self {
Self::min_positive_value()
}
fn epsilon() -> Self {
Self::epsilon()
}
fn max_value() -> Self {
Self::max_value()
}
fn floor(self) -> Self {
self.floor()
}
fn ceil(self) -> Self {
self.ceil()
}
fn round(self) -> Self {
self.round()
}
fn trunc(self) -> Self {
self.trunc()
}
fn fract(self) -> Self {
self.fract()
}
fn abs(self) -> Self {
self.abs()
}
fn signum(self) -> Self {
self.signum()
}
fn is_sign_positive(self) -> bool {
self.is_sign_positive()
}
fn is_sign_negative(self) -> bool {
self.is_sign_negative()
}
fn mul_add(self, a: Self, b: Self) -> Self {
self.mul_add(a, b)
}
fn recip(self) -> Self {
self.recip()
}
fn powi(self, n: i32) -> Self {
self.powi(n)
}
fn powf(self, n: Self) -> Self {
self.powf(n)
}
fn sqrt(self) -> Self {
self.sqrt()
}
fn exp(self) -> Self {
self.exp()
}
fn exp2(self) -> Self {
self.exp2()
}
fn ln(self) -> Self {
self.ln()
}
fn log(self, base: Self) -> Self {
self.log(base)
}
fn log2(self) -> Self {
self.log2()
}
fn log10(self) -> Self {
self.log10()
}
fn to_degrees(self) -> Self {
self.to_degrees()
}
fn to_radians(self) -> Self {
self.to_radians()
}
fn max(self, other: Self) -> Self {
self.max(other)
}
fn min(self, other: Self) -> Self {
self.min(other)
}
fn abs_sub(self, other: Self) -> Self {
self.abs_sub(other)
}
fn cbrt(self) -> Self {
self.cbrt()
}
fn hypot(self, other: Self) -> Self {
self.hypot(other)
}
fn sin(self) -> Self {
self.sin()
}
fn cos(self) -> Self {
self.cos()
}
fn tan(self) -> Self {
self.tan()
}
fn asin(self) -> Self {
self.asin()
}
fn acos(self) -> Self {
self.acos()
}
fn atan(self) -> Self {
self.atan()
}
fn atan2(self, other: Self) -> Self {
self.atan2(other)
}
fn sin_cos(self) -> (Self, Self) {
self.sin_cos()
}
fn exp_m1(self) -> Self {
self.exp_m1()
}
fn ln_1p(self) -> Self {
self.ln_1p()
}
fn sinh(self) -> Self {
self.sinh()
}
fn cosh(self) -> Self {
self.cosh()
}
fn tanh(self) -> Self {
self.tanh()
}
fn asinh(self) -> Self {
self.asinh()
}
fn acosh(self) -> Self {
self.acosh()
}
fn atanh(self) -> Self {
self.atanh()
forward! {
Float::floor(self) -> Self;
Float::ceil(self) -> Self;
Float::round(self) -> Self;
Float::trunc(self) -> Self;
Float::fract(self) -> Self;
Float::abs(self) -> Self;
Float::signum(self) -> Self;
Float::is_sign_positive(self) -> bool;
Float::is_sign_negative(self) -> bool;
Float::mul_add(self, a: Self, b: Self) -> Self;
Float::recip(self) -> Self;
Float::powi(self, n: i32) -> Self;
Float::powf(self, n: Self) -> Self;
Float::sqrt(self) -> Self;
Float::exp(self) -> Self;
Float::exp2(self) -> Self;
Float::ln(self) -> Self;
Float::log(self, base: Self) -> Self;
Float::log2(self) -> Self;
Float::log10(self) -> Self;
Float::to_degrees(self) -> Self;
Float::to_radians(self) -> Self;
Float::max(self, other: Self) -> Self;
Float::min(self, other: Self) -> Self;
Float::abs_sub(self, other: Self) -> Self;
Float::cbrt(self) -> Self;
Float::hypot(self, other: Self) -> Self;
Float::sin(self) -> Self;
Float::cos(self) -> Self;
Float::tan(self) -> Self;
Float::asin(self) -> Self;
Float::acos(self) -> Self;
Float::atan(self) -> Self;
Float::atan2(self, other: Self) -> Self;
Float::sin_cos(self) -> (Self, Self);
Float::exp_m1(self) -> Self;
Float::ln_1p(self) -> Self;
Float::sinh(self) -> Self;
Float::cosh(self) -> Self;
Float::tanh(self) -> Self;
Float::asinh(self) -> Self;
Float::acosh(self) -> Self;
Float::atanh(self) -> Self;
}
}

View File

@ -1,7 +1,7 @@
use core::ops::Neg;
use core::{f32, f64};
use core::num::Wrapping;
use core::ops::Neg;
use float::FloatCore;
use Num;
/// Useful functions for signed numbers (i.e. numbers that can be negative).
@ -74,7 +74,12 @@ macro_rules! signed_impl {
signed_impl!(isize i8 i16 i32 i64);
impl<T: Signed> Signed for Wrapping<T> where Wrapping<T>: Num + Neg<Output=Wrapping<T>>
#[cfg(has_i128)]
signed_impl!(i128);
impl<T: Signed> Signed for Wrapping<T>
where
Wrapping<T>: Num + Neg<Output = Wrapping<T>>,
{
#[inline]
fn abs(&self) -> Self {
@ -92,33 +97,23 @@ impl<T: Signed> Signed for Wrapping<T> where Wrapping<T>: Num + Neg<Output=Wrapp
}
#[inline]
fn is_positive(&self) -> bool { self.0.is_positive() }
fn is_positive(&self) -> bool {
self.0.is_positive()
}
#[inline]
fn is_negative(&self) -> bool { self.0.is_negative() }
fn is_negative(&self) -> bool {
self.0.is_negative()
}
}
macro_rules! signed_float_impl {
($t:ty, $nan:expr, $inf:expr, $neg_inf:expr) => {
($t:ty) => {
impl Signed for $t {
/// Computes the absolute value. Returns `NAN` if the number is `NAN`.
#[inline]
#[cfg(feature = "std")]
fn abs(&self) -> $t {
(*self).abs()
}
/// Computes the absolute value. Returns `NAN` if the number is `NAN`.
#[inline]
#[cfg(not(feature = "std"))]
fn abs(&self) -> $t {
if self.is_positive() {
*self
} else if self.is_negative() {
-*self
} else {
$nan
}
FloatCore::abs(*self)
}
/// The positive difference of two numbers. Returns `0.0` if the number is
@ -126,51 +121,40 @@ macro_rules! signed_float_impl {
/// and `other` is returned.
#[inline]
fn abs_sub(&self, other: &$t) -> $t {
if *self <= *other { 0. } else { *self - *other }
}
/// # Returns
///
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is NaN
#[inline]
#[cfg(feature = "std")]
fn signum(&self) -> $t {
use Float;
Float::signum(*self)
}
/// # Returns
///
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is NaN
#[inline]
#[cfg(not(feature = "std"))]
fn signum(&self) -> $t {
if self.is_positive() {
1.0
} else if self.is_negative() {
-1.0
if *self <= *other {
0.
} else {
$nan
*self - *other
}
}
/// # Returns
///
/// - `1.0` if the number is positive, `+0.0` or `INFINITY`
/// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
/// - `NAN` if the number is NaN
#[inline]
fn signum(&self) -> $t {
FloatCore::signum(*self)
}
/// Returns `true` if the number is positive, including `+0.0` and `INFINITY`
#[inline]
fn is_positive(&self) -> bool { *self > 0.0 || (1.0 / *self) == $inf }
fn is_positive(&self) -> bool {
FloatCore::is_sign_positive(*self)
}
/// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY`
#[inline]
fn is_negative(&self) -> bool { *self < 0.0 || (1.0 / *self) == $neg_inf }
fn is_negative(&self) -> bool {
FloatCore::is_sign_negative(*self)
}
}
}
};
}
signed_float_impl!(f32, f32::NAN, f32::INFINITY, f32::NEG_INFINITY);
signed_float_impl!(f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY);
signed_float_impl!(f32);
signed_float_impl!(f64);
/// Computes the absolute value.
///
@ -204,7 +188,10 @@ pub fn abs_sub<T: Signed>(x: T, y: T) -> T {
/// * `0` if the number is zero
/// * `1` if the number is positive
/// * `-1` if the number is negative
#[inline(always)] pub fn signum<T: Signed>(value: T) -> T { value.signum() }
#[inline(always)]
pub fn signum<T: Signed>(value: T) -> T {
value.signum()
}
/// A trait for values which cannot be negative
pub trait Unsigned: Num {}
@ -216,8 +203,14 @@ macro_rules! empty_trait_impl {
}
empty_trait_impl!(Unsigned for usize u8 u16 u32 u64);
#[cfg(has_i128)]
empty_trait_impl!(Unsigned for u128);
impl<T: Unsigned> Unsigned for Wrapping<T> where Wrapping<T>: Num {}
impl<T: Unsigned> Unsigned for Wrapping<T>
where
Wrapping<T>: Num,
{
}
#[test]
fn unsigned_wrapping_is_unsigned() {

View File

@ -0,0 +1,396 @@
//! Tests of `num_traits::cast`.
#![no_std]
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
extern crate num_traits;
use num_traits::cast::*;
use num_traits::Bounded;
use core::{f32, f64};
#[cfg(has_i128)]
use core::{i128, u128};
use core::{i16, i32, i64, i8, isize};
use core::{u16, u32, u64, u8, usize};
use core::fmt::Debug;
use core::mem;
use core::num::Wrapping;
#[test]
fn to_primitive_float() {
let f32_toolarge = 1e39f64;
assert_eq!(f32_toolarge.to_f32(), None);
assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));
assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY));
assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan()));
}
#[test]
fn wrapping_to_primitive() {
macro_rules! test_wrapping_to_primitive {
($($t:ty)+) => {
$({
let i: $t = 0;
let w = Wrapping(i);
assert_eq!(i.to_u8(), w.to_u8());
assert_eq!(i.to_u16(), w.to_u16());
assert_eq!(i.to_u32(), w.to_u32());
assert_eq!(i.to_u64(), w.to_u64());
assert_eq!(i.to_usize(), w.to_usize());
assert_eq!(i.to_i8(), w.to_i8());
assert_eq!(i.to_i16(), w.to_i16());
assert_eq!(i.to_i32(), w.to_i32());
assert_eq!(i.to_i64(), w.to_i64());
assert_eq!(i.to_isize(), w.to_isize());
assert_eq!(i.to_f32(), w.to_f32());
assert_eq!(i.to_f64(), w.to_f64());
})+
};
}
test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[test]
fn wrapping_is_toprimitive() {
fn require_toprimitive<T: ToPrimitive>(_: &T) {}
require_toprimitive(&Wrapping(42));
}
#[test]
fn wrapping_is_fromprimitive() {
fn require_fromprimitive<T: FromPrimitive>(_: &T) {}
require_fromprimitive(&Wrapping(42));
}
#[test]
fn wrapping_is_numcast() {
fn require_numcast<T: NumCast>(_: &T) {}
require_numcast(&Wrapping(42));
}
#[test]
fn as_primitive() {
let x: f32 = (1.625f64).as_();
assert_eq!(x, 1.625f32);
let x: f32 = (3.14159265358979323846f64).as_();
assert_eq!(x, 3.1415927f32);
let x: u8 = (768i16).as_();
assert_eq!(x, 0);
}
#[test]
fn float_to_integer_checks_overflow() {
// This will overflow an i32
let source: f64 = 1.0e+123f64;
// Expect the overflow to be caught
assert_eq!(cast::<f64, i32>(source), None);
}
#[test]
fn cast_to_int_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, isize>(big_f));
assert_eq!(None, cast::<f64, i8>(big_f));
assert_eq!(None, cast::<f64, i16>(big_f));
assert_eq!(None, cast::<f64, i32>(big_f));
assert_eq!(None, cast::<f64, i64>(big_f));
assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
assert_eq!(None, cast::<f64, isize>(small_f));
assert_eq!(None, cast::<f64, i8>(small_f));
assert_eq!(None, cast::<f64, i16>(small_f));
assert_eq!(None, cast::<f64, i32>(small_f));
assert_eq!(None, cast::<f64, i64>(small_f));
}
#[test]
fn cast_to_unsigned_int_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, usize>(big_f));
assert_eq!(None, cast::<f64, u8>(big_f));
assert_eq!(None, cast::<f64, u16>(big_f));
assert_eq!(None, cast::<f64, u32>(big_f));
assert_eq!(None, cast::<f64, u64>(big_f));
assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
assert_eq!(None, cast::<f64, usize>(small_f));
assert_eq!(None, cast::<f64, u8>(small_f));
assert_eq!(None, cast::<f64, u16>(small_f));
assert_eq!(None, cast::<f64, u32>(small_f));
assert_eq!(None, cast::<f64, u64>(small_f));
}
#[test]
#[cfg(has_i128)]
fn cast_to_i128_checks_overflow() {
let big_f: f64 = 1.0e123;
let normal_f: f64 = 1.0;
let small_f: f64 = -1.0e123;
assert_eq!(None, cast::<f64, i128>(big_f));
assert_eq!(None, cast::<f64, u128>(big_f));
assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
assert_eq!(None, cast::<f64, i128>(small_f));
assert_eq!(None, cast::<f64, u128>(small_f));
}
#[cfg(feature = "std")]
fn dbg(args: ::core::fmt::Arguments) {
println!("{}", args);
}
#[cfg(not(feature = "std"))]
fn dbg(_: ::core::fmt::Arguments) {}
// Rust 1.8 doesn't handle cfg on macros correctly
macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } }
macro_rules! float_test_edge {
($f:ident -> $($t:ident)+) => { $({
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() {
$t::MIN as $f - 1.0
} else {
($t::MIN as $f).raw_offset(1).floor()
};
let fmin = small.raw_offset(-1);
dbg!(" testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small);
assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f));
assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin));
assert_eq!(None, cast::<$f, $t>(small));
let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() {
($t::MAX, $t::MAX as $f + 1.0)
} else {
let large = $t::MAX as $f; // rounds up!
let max = large.raw_offset(-1) as $t; // the next smallest possible
assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS);
(max, large)
};
let fmax = large.raw_offset(-1);
dbg!(" testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large);
assert_eq!(Some(max), cast::<$f, $t>(max as $f));
assert_eq!(Some(max), cast::<$f, $t>(fmax));
assert_eq!(None, cast::<$f, $t>(large));
dbg!(" testing non-finite values");
assert_eq!(None, cast::<$f, $t>($f::NAN));
assert_eq!(None, cast::<$f, $t>($f::INFINITY));
assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY));
})+}
}
trait RawOffset: Sized {
type Raw;
fn raw_offset(self, offset: Self::Raw) -> Self;
}
impl RawOffset for f32 {
type Raw = i32;
fn raw_offset(self, offset: Self::Raw) -> Self {
unsafe {
let raw: Self::Raw = mem::transmute(self);
mem::transmute(raw + offset)
}
}
}
impl RawOffset for f64 {
type Raw = i64;
fn raw_offset(self, offset: Self::Raw) -> Self {
unsafe {
let raw: Self::Raw = mem::transmute(self);
mem::transmute(raw + offset)
}
}
}
#[test]
fn cast_float_to_int_edge_cases() {
float_test_edge!(f32 -> isize i8 i16 i32 i64);
float_test_edge!(f32 -> usize u8 u16 u32 u64);
float_test_edge!(f64 -> isize i8 i16 i32 i64);
float_test_edge!(f64 -> usize u8 u16 u32 u64);
}
#[test]
#[cfg(has_i128)]
fn cast_float_to_i128_edge_cases() {
float_test_edge!(f32 -> i128 u128);
float_test_edge!(f64 -> i128 u128);
}
macro_rules! int_test_edge {
($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
fn test_edge() {
dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) {
Greater => {
assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN));
}
Equal => {
assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN));
}
Less => {
let min = $t::MIN as $f;
assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
assert_eq!(None, cast::<$f, $t>(min - 1));
}
}
match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) {
Greater => {
let max = $t::MAX as $f;
assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
assert_eq!(None, cast::<$f, $t>(max + 1));
}
Equal => {
assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX));
}
Less => {
assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX));
}
}
}
test_edge();
})+}
}
#[test]
fn cast_int_to_int_edge_cases() {
use core::cmp::Ordering::*;
macro_rules! test_edge {
($( $from:ident )+) => { $({
int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
})+}
}
test_edge!(isize i8 i16 i32 i64);
test_edge!(usize u8 u16 u32 u64);
}
#[test]
#[cfg(has_i128)]
fn cast_int_to_128_edge_cases() {
use core::cmp::Ordering::*;
macro_rules! test_edge {
($( $t:ident )+) => {
$(
int_test_edge!($t -> { i128 u128 } with i128 u128);
)+
int_test_edge!(i128 -> { $( $t )+ } with i128 u128);
int_test_edge!(u128 -> { $( $t )+ } with i128 u128);
}
}
test_edge!(isize i8 i16 i32 i64 i128);
test_edge!(usize u8 u16 u32 u64 u128);
}
#[test]
fn newtype_from_primitive() {
#[derive(PartialEq, Debug)]
struct New<T>(T);
// minimal impl
impl<T: FromPrimitive> FromPrimitive for New<T> {
fn from_i64(n: i64) -> Option<Self> {
T::from_i64(n).map(New)
}
fn from_u64(n: u64) -> Option<Self> {
T::from_u64(n).map(New)
}
}
macro_rules! assert_eq_from {
($( $from:ident )+) => {$(
assert_eq!(T::$from(Bounded::min_value()).map(New),
New::<T>::$from(Bounded::min_value()));
assert_eq!(T::$from(Bounded::max_value()).map(New),
New::<T>::$from(Bounded::max_value()));
)+}
}
fn check<T: PartialEq + Debug + FromPrimitive>() {
assert_eq_from!(from_i8 from_i16 from_i32 from_i64 from_isize);
assert_eq_from!(from_u8 from_u16 from_u32 from_u64 from_usize);
assert_eq_from!(from_f32 from_f64);
}
macro_rules! check {
($( $ty:ty )+) => {$( check::<$ty>(); )+}
}
check!(i8 i16 i32 i64 isize);
check!(u8 u16 u32 u64 usize);
}
#[test]
fn newtype_to_primitive() {
#[derive(PartialEq, Debug)]
struct New<T>(T);
// minimal impl
impl<T: ToPrimitive> ToPrimitive for New<T> {
fn to_i64(&self) -> Option<i64> {
self.0.to_i64()
}
fn to_u64(&self) -> Option<u64> {
self.0.to_u64()
}
}
macro_rules! assert_eq_to {
($( $to:ident )+) => {$(
assert_eq!(T::$to(&Bounded::min_value()),
New::<T>::$to(&New(Bounded::min_value())));
assert_eq!(T::$to(&Bounded::max_value()),
New::<T>::$to(&New(Bounded::max_value())));
)+}
}
fn check<T: PartialEq + Debug + Bounded + ToPrimitive>() {
assert_eq_to!(to_i8 to_i16 to_i32 to_i64 to_isize);
assert_eq_to!(to_u8 to_u16 to_u32 to_u64 to_usize);
assert_eq_to!(to_f32 to_f64);
}
macro_rules! check {
($( $ty:ty )+) => {$( check::<$ty>(); )+}
}
check!(i8 i16 i32 i64 isize);
check!(u8 u16 u32 u64 usize);
}

View File

@ -1 +0,0 @@
{"files":{".travis.yml":"b692f971728cdb37ce24ba3a64dabbd5d1a9df5f2e865e4affbdc1af7ed2ae50","Cargo.toml":"b79d083fe859fb905da576c0b9e603d25f73724411f635e2a123b01900385265","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"d7c9353e9eaac0bece0bba326f318f8bc56135e0b20b2a542dd1cb9ff79dde33","benches/bigint.rs":"20d98b7f41d721c62d46711ef9aa61d29d49b3a901faa715ff61bd43c61156f9","benches/shootout-pidigits.rs":"4e2392cdeba33986f8b294c0808b30c85bcf4bbc2a39537af849648cd0d749e1","bors.toml":"1c81ede536a37edd30fe4e622ff0531b25372403ac9475a5d6c50f14156565a2","ci/deploy.enc":"96fd3f8e1dc04d22c877a49b68788794f6cb515d0b7606df51d8b6be53131cd8","ci/deploy.sh":"8b7eeb9822e49bbfe36a22d846d5c06b4b7962330b3e4a5a64d59aeee1e45d66","ci/rustup.sh":"57e22a9e2a1dcfe4dcea577db7703e5700adf81792b5f0e2c04800f653094eda","ci/test_full.sh":"2fdd8f5d39d2c67c3fd340870031c3ddcc2536f3908f8b5f07504a2f899da061","doc/favicon.ico":"7bc6bd910e0a3b5ccf9c8480a35cbec866563224d12b23052f1a7ffe9eb17c75","doc/index.html":"6977977443a0dd8c930a798c16e39bd99cdcbf78e5829e9502456117aa3a28dd","doc/rust-logo-128x128-blk-v2.png":"a3b727c6ff3ff9014b01a9ac0e721b027344a080ca7dc1107533b4c63a369af1","src/lib.rs":"8d45ef22ffe42258a8636ff482bb7fe1077eb7326fb4c4a6852e77e92a881742"},"package":"a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"}

View File

@ -1,70 +0,0 @@
[package]
authors = ["The Rust Project Developers"]
description = "A collection of numeric types and traits for Rust, including bigint,\ncomplex, rational, range iterators, generic integers, and more!\n"
documentation = "http://rust-num.github.io/num"
homepage = "https://github.com/rust-num/num"
keywords = ["mathematics", "numerics", "bignum"]
categories = [ "algorithms", "data-structures", "science" ]
license = "MIT/Apache-2.0"
repository = "https://github.com/rust-num/num"
name = "num"
version = "0.1.40"
[[bench]]
name = "bigint"
[[bench]]
harness = false
name = "shootout-pidigits"
[dependencies]
[dependencies.num-bigint]
optional = true
path = "bigint"
version = "0.1.40"
[dependencies.num-complex]
optional = true
path = "complex"
version = "0.1.39"
[dependencies.num-integer]
path = "./integer"
version = "0.1.35"
[dependencies.num-iter]
optional = false
path = "iter"
version = "0.1.34"
[dependencies.num-rational]
optional = true
path = "rational"
version = "0.1.39"
[dependencies.num-traits]
path = "./traits"
version = "0.1.40"
[dev-dependencies]
[dev-dependencies.rand]
version = "0.3.8"
[features]
bigint = ["num-bigint"]
complex = ["num-complex"]
rational = ["num-rational"]
default = ["bigint", "complex", "rational", "rustc-serialize"]
serde = [
"num-bigint/serde",
"num-complex/serde",
"num-rational/serde"
]
rustc-serialize = [
"num-bigint/rustc-serialize",
"num-complex/rustc-serialize",
"num-rational/rustc-serialize"
]

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,25 +0,0 @@
Copyright (c) 2014 The Rust Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,30 +0,0 @@
# num
A collection of numeric types and traits for Rust.
This includes new types for big integers, rationals, and complex numbers,
new traits for generic programming on numeric properties like `Integer`,
and generic range iterators.
[Documentation](http://rust-num.github.io/num)
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
num = "0.1"
```
and this to your crate root:
```rust
extern crate num;
```
## Compatibility
Most of the `num` crates are tested for rustc 1.8 and greater.
The exceptions are `num-derive` which requires at least rustc 1.15,
and the deprecated `num-macros` which requires nightly rustc.

View File

@ -1,252 +0,0 @@
#![feature(test)]
extern crate test;
extern crate num;
extern crate rand;
use std::mem::replace;
use test::Bencher;
use num::{BigInt, BigUint, Zero, One, FromPrimitive};
use num::bigint::RandBigInt;
use rand::{SeedableRng, StdRng};
fn get_rng() -> StdRng {
let seed: &[_] = &[1, 2, 3, 4];
SeedableRng::from_seed(seed)
}
fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
let mut rng = get_rng();
let x = rng.gen_bigint(xbits);
let y = rng.gen_bigint(ybits);
b.iter(|| &x * &y);
}
fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
let mut rng = get_rng();
let x = rng.gen_bigint(xbits);
let y = rng.gen_bigint(ybits);
b.iter(|| &x / &y);
}
fn factorial(n: usize) -> BigUint {
let mut f: BigUint = One::one();
for i in 1..(n+1) {
let bu: BigUint = FromPrimitive::from_usize(i).unwrap();
f = f * bu;
}
f
}
/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
let mut f0: BigUint = Zero::zero();
let mut f1: BigUint = One::one();
for _ in 0..n {
let f2 = f0 + &f1;
f0 = replace(&mut f1, f2);
}
f0
}
/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
let mut f0: BigUint = Zero::zero();
let mut f1: BigUint = One::one();
for _ in 0..n {
f1 = f1 + &f0;
f0 = &f1 - f0;
}
f0
}
#[bench]
fn multiply_0(b: &mut Bencher) {
multiply_bench(b, 1 << 8, 1 << 8);
}
#[bench]
fn multiply_1(b: &mut Bencher) {
multiply_bench(b, 1 << 8, 1 << 16);
}
#[bench]
fn multiply_2(b: &mut Bencher) {
multiply_bench(b, 1 << 16, 1 << 16);
}
#[bench]
fn divide_0(b: &mut Bencher) {
divide_bench(b, 1 << 8, 1 << 6);
}
#[bench]
fn divide_1(b: &mut Bencher) {
divide_bench(b, 1 << 12, 1 << 8);
}
#[bench]
fn divide_2(b: &mut Bencher) {
divide_bench(b, 1 << 16, 1 << 12);
}
#[bench]
fn factorial_100(b: &mut Bencher) {
b.iter(|| factorial(100));
}
#[bench]
fn fib_100(b: &mut Bencher) {
b.iter(|| fib(100));
}
#[bench]
fn fib_1000(b: &mut Bencher) {
b.iter(|| fib(1000));
}
#[bench]
fn fib_10000(b: &mut Bencher) {
b.iter(|| fib(10000));
}
#[bench]
fn fib2_100(b: &mut Bencher) {
b.iter(|| fib2(100));
}
#[bench]
fn fib2_1000(b: &mut Bencher) {
b.iter(|| fib2(1000));
}
#[bench]
fn fib2_10000(b: &mut Bencher) {
b.iter(|| fib2(10000));
}
#[bench]
fn fac_to_string(b: &mut Bencher) {
let fac = factorial(100);
b.iter(|| fac.to_string());
}
#[bench]
fn fib_to_string(b: &mut Bencher) {
let fib = fib(100);
b.iter(|| fib.to_string());
}
fn to_str_radix_bench(b: &mut Bencher, radix: u32) {
let mut rng = get_rng();
let x = rng.gen_bigint(1009);
b.iter(|| x.to_str_radix(radix));
}
#[bench]
fn to_str_radix_02(b: &mut Bencher) {
to_str_radix_bench(b, 2);
}
#[bench]
fn to_str_radix_08(b: &mut Bencher) {
to_str_radix_bench(b, 8);
}
#[bench]
fn to_str_radix_10(b: &mut Bencher) {
to_str_radix_bench(b, 10);
}
#[bench]
fn to_str_radix_16(b: &mut Bencher) {
to_str_radix_bench(b, 16);
}
#[bench]
fn to_str_radix_36(b: &mut Bencher) {
to_str_radix_bench(b, 36);
}
fn from_str_radix_bench(b: &mut Bencher, radix: u32) {
use num::Num;
let mut rng = get_rng();
let x = rng.gen_bigint(1009);
let s = x.to_str_radix(radix);
assert_eq!(x, BigInt::from_str_radix(&s, radix).unwrap());
b.iter(|| BigInt::from_str_radix(&s, radix));
}
#[bench]
fn from_str_radix_02(b: &mut Bencher) {
from_str_radix_bench(b, 2);
}
#[bench]
fn from_str_radix_08(b: &mut Bencher) {
from_str_radix_bench(b, 8);
}
#[bench]
fn from_str_radix_10(b: &mut Bencher) {
from_str_radix_bench(b, 10);
}
#[bench]
fn from_str_radix_16(b: &mut Bencher) {
from_str_radix_bench(b, 16);
}
#[bench]
fn from_str_radix_36(b: &mut Bencher) {
from_str_radix_bench(b, 36);
}
#[bench]
fn shl(b: &mut Bencher) {
let n = BigUint::one() << 1000;
b.iter(|| {
let mut m = n.clone();
for i in 0..50 {
m = m << i;
}
})
}
#[bench]
fn shr(b: &mut Bencher) {
let n = BigUint::one() << 2000;
b.iter(|| {
let mut m = n.clone();
for i in 0..50 {
m = m >> i;
}
})
}
#[bench]
fn hash(b: &mut Bencher) {
use std::collections::HashSet;
let mut rng = get_rng();
let v: Vec<BigInt> = (1000..2000).map(|bits| rng.gen_bigint(bits)).collect();
b.iter(|| {
let h: HashSet<&BigInt> = v.iter().collect();
assert_eq!(h.len(), v.len());
});
}
#[bench]
fn pow_bench(b: &mut Bencher) {
b.iter(|| {
let upper = 100_usize;
for i in 2..upper + 1 {
for j in 2..upper + 1 {
let i_big = BigUint::from_usize(i).unwrap();
num::pow(i_big, j);
}
}
});
}

View File

@ -1,131 +0,0 @@
// The Computer Language Benchmarks Game
// http://benchmarksgame.alioth.debian.org/
//
// contributed by the Rust Project Developers
// Copyright (c) 2013-2014 The Rust Project Developers
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
//
// - Neither the name of "The Computer Language Benchmarks Game" nor
// the name of "The Computer Language Shootout Benchmarks" nor the
// names of its contributors may be used to endorse or promote
// products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
extern crate num;
use std::str::FromStr;
use std::io;
use num::traits::{FromPrimitive, ToPrimitive};
use num::{BigInt, Integer, One, Zero};
struct Context {
numer: BigInt,
accum: BigInt,
denom: BigInt,
}
impl Context {
fn new() -> Context {
Context {
numer: One::one(),
accum: Zero::zero(),
denom: One::one(),
}
}
fn from_i32(i: i32) -> BigInt {
FromPrimitive::from_i32(i).unwrap()
}
fn extract_digit(&self) -> i32 {
if self.numer > self.accum {return -1;}
let (q, r) =
(&self.numer * Context::from_i32(3) + &self.accum)
.div_rem(&self.denom);
if r + &self.numer >= self.denom {return -1;}
q.to_i32().unwrap()
}
fn next_term(&mut self, k: i32) {
let y2 = Context::from_i32(k * 2 + 1);
self.accum = (&self.accum + (&self.numer << 1)) * &y2;
self.numer = &self.numer * Context::from_i32(k);
self.denom = &self.denom * y2;
}
fn eliminate_digit(&mut self, d: i32) {
let d = Context::from_i32(d);
let ten = Context::from_i32(10);
self.accum = (&self.accum - &self.denom * d) * &ten;
self.numer = &self.numer * ten;
}
}
fn pidigits(n: isize, out: &mut io::Write) -> io::Result<()> {
let mut k = 0;
let mut context = Context::new();
for i in 1..(n+1) {
let mut d;
loop {
k += 1;
context.next_term(k);
d = context.extract_digit();
if d != -1 {break;}
}
try!(write!(out, "{}", d));
if i % 10 == 0 { try!(write!(out, "\t:{}\n", i)); }
context.eliminate_digit(d);
}
let m = n % 10;
if m != 0 {
for _ in m..10 { try!(write!(out, " ")); }
try!(write!(out, "\t:{}\n", n));
}
Ok(())
}
const DEFAULT_DIGITS: isize = 512;
fn main() {
let args = std::env::args().collect::<Vec<_>>();
let n = if args.len() < 2 {
DEFAULT_DIGITS
} else if args[1] == "--bench" {
return pidigits(DEFAULT_DIGITS, &mut std::io::sink()).unwrap()
} else {
FromStr::from_str(&args[1]).unwrap()
};
pidigits(n, &mut std::io::stdout()).unwrap();
}

Binary file not shown.

View File

@ -1,12 +0,0 @@
#!/bin/sh
set -ex
cp doc/* target/doc/
pip install ghp-import --user
$HOME/.local/bin/ghp-import -n target/doc
openssl aes-256-cbc -K $encrypted_9e86330b283d_key -iv $encrypted_9e86330b283d_iv -in ./ci/deploy.enc -out ./ci/deploy -d
chmod 600 ./ci/deploy
ssh-add ./ci/deploy
git push -qf ssh://git@github.com/${TRAVIS_REPO_SLUG}.git gh-pages

View File

@ -1,12 +0,0 @@
#!/bin/sh
# Use rustup to locally run the same suite of tests as .travis.yml.
# (You should first install/update 1.8.0, 1.15.0, beta, and nightly.)
set -ex
for toolchain in 1.8.0 1.15.0 beta nightly; do
run="rustup run $toolchain"
$run cargo build --verbose
$run $PWD/ci/test_full.sh $toolchain
$run cargo doc
done

View File

@ -1,49 +0,0 @@
#!/bin/bash
set -ex
echo Testing num on rustc ${TRAVIS_RUST_VERSION:=$1}
# All of these packages should build and test everywhere.
for package in bigint complex integer iter rational traits; do
cargo build --manifest-path $package/Cargo.toml
cargo test --manifest-path $package/Cargo.toml
done
# They all should build with minimal features too
for package in bigint complex integer iter rational traits; do
cargo build --manifest-path $package/Cargo.toml --no-default-features
cargo test --manifest-path $package/Cargo.toml --no-default-features
done
# Each isolated feature should also work everywhere.
for feature in '' bigint rational complex; do
cargo build --verbose --no-default-features --features="$feature"
cargo test --verbose --no-default-features --features="$feature"
done
# Build test for the serde feature
cargo build --verbose --features "serde"
# Downgrade serde and build test the 0.7.0 channel as well
cargo update -p serde --precise 0.7.0
cargo build --verbose --features "serde"
if [ "$TRAVIS_RUST_VERSION" = 1.8.0 ]; then exit; fi
# num-derive should build on 1.15.0+
cargo build --verbose --manifest-path=derive/Cargo.toml
if [ "$TRAVIS_RUST_VERSION" != nightly ]; then exit; fi
# num-derive testing requires compiletest_rs, which requires nightly
cargo test --verbose --manifest-path=derive/Cargo.toml
# num-macros only works on nightly, soon to be deprecated
cargo build --verbose --manifest-path=macros/Cargo.toml
cargo test --verbose --manifest-path=macros/Cargo.toml
# benchmarks only work on nightly
cargo bench --verbose

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1 +0,0 @@
<meta http-equiv=refresh content=0;url=num/index.html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -1,111 +0,0 @@
// Copyright 2014-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A collection of numeric types and traits for Rust.
//!
//! This includes new types for big integers, rationals, and complex numbers,
//! new traits for generic programming on numeric properties like `Integer`,
//! and generic range iterators.
//!
//! ## Example
//!
//! This example uses the BigRational type and [Newton's method][newt] to
//! approximate a square root to arbitrary precision:
//!
//! ```
//! extern crate num;
//! # #[cfg(all(feature = "bigint", feature="rational"))]
//! # mod test {
//!
//! use num::FromPrimitive;
//! use num::bigint::BigInt;
//! use num::rational::{Ratio, BigRational};
//!
//! # pub
//! fn approx_sqrt(number: u64, iterations: usize) -> BigRational {
//! let start: Ratio<BigInt> = Ratio::from_integer(FromPrimitive::from_u64(number).unwrap());
//! let mut approx = start.clone();
//!
//! for _ in 0..iterations {
//! approx = (&approx + (&start / &approx)) /
//! Ratio::from_integer(FromPrimitive::from_u64(2).unwrap());
//! }
//!
//! approx
//! }
//! # }
//! # #[cfg(not(all(feature = "bigint", feature="rational")))]
//! # mod test { pub fn approx_sqrt(n: u64, _: usize) -> u64 { n } }
//! # use test::approx_sqrt;
//!
//! fn main() {
//! println!("{}", approx_sqrt(10, 4)); // prints 4057691201/1283082416
//! }
//!
//! ```
//!
//! [newt]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method
#![doc(html_logo_url = "https://rust-num.github.io/num/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://rust-num.github.io/num/favicon.ico",
html_root_url = "https://rust-num.github.io/num/",
html_playground_url = "http://play.integer32.com/")]
extern crate num_traits;
extern crate num_integer;
extern crate num_iter;
#[cfg(feature = "num-complex")]
extern crate num_complex;
#[cfg(feature = "num-bigint")]
extern crate num_bigint;
#[cfg(feature = "num-rational")]
extern crate num_rational;
#[cfg(feature = "num-bigint")]
pub use num_bigint::{BigInt, BigUint};
#[cfg(feature = "num-rational")]
pub use num_rational::Rational;
#[cfg(all(feature = "num-rational", feature="num-bigint"))]
pub use num_rational::BigRational;
#[cfg(feature = "num-complex")]
pub use num_complex::Complex;
pub use num_integer::Integer;
pub use num_iter::{range, range_inclusive, range_step, range_step_inclusive};
pub use num_traits::{Num, Zero, One, Signed, Unsigned, Bounded,
one, zero, abs, abs_sub, signum,
Saturating, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
PrimInt, Float, ToPrimitive, FromPrimitive, NumCast, cast,
pow, checked_pow, clamp};
#[cfg(feature = "num-bigint")]
pub mod bigint {
pub use num_bigint::*;
}
#[cfg(feature = "num-complex")]
pub mod complex {
pub use num_complex::*;
}
pub mod integer {
pub use num_integer::*;
}
pub mod iter {
pub use num_iter::*;
}
pub mod traits {
pub use num_traits::*;
}
#[cfg(feature = "num-rational")]
pub mod rational {
pub use num_rational::*;
}

View File

@ -1 +1 @@
{"files":{".travis.yml":"93ce7e20127d942fcf3a571d004e02a3b9ec38e98c71f32a561ce41b5c27f500","Cargo.toml":"b6f076a871f5bb585272bf3d458e789d89d5ad6723e152d49c07f99320c8a0ce","LICENSE":"6ac8711fb340c62ce0a4ecd463342d3fa0e8e70de697c863a2e1c0c53006003c","README.md":"8f548f0d5a9b2661839258c14a70284638fc4ce3017fc7ed2b9540ae3617ad2d","appveyor.yml":"53ebe35598907954204812614a160355f36750ede4546bc69027cbaf7643d34c","examples/extract.rs":"53b555617192330bd2f95235b82749905ad52617a5a3ba2648dbafbfff8a49a0","examples/extract_lorem.rs":"4e4e70927d2928a411ce4abe37ef4924c7156c4fd2697527c01fbd1797a76969","examples/write_dir.rs":"1a39da7b5d31997bf353a56d009ecdcf31da765f9ab191b20792e1904a594183","examples/write_sample.rs":"72a7c2db8074d20651ba04d0a49a7d6fb213ca674c63ab48db506deb69cba64c","script/doc-upload.cfg":"10684c8f5ef7e7388cfc4742ab9a060fe8f7fc1c51a63ede3c2443260ecc2d5e","src/compression.rs":"535e422f801caf53829014a0982698483c504da4082ebb571e468d4721d8434b","src/cp437.rs":"7954c5b7df6f72fe3fe2c377c5de09973c2fa704c7ae0274a5490594e4b69517","src/crc32.rs":"8b585059958a7150a4e40f0bd95198cc906385eb42468652f96e9d3294145b01","src/lib.rs":"15206ffb3612b4bbde73604ed2e194c4ced23487ffdefe01f42653bf1bdffff8","src/read.rs":"30ee981fcf690675d3f9c892b49e1a2539b4e9511d078d13e89686e3887f250f","src/result.rs":"1006e46206a5b1491ed1d4fde0955a391df78cd42aa9582784b19170332a3783","src/spec.rs":"39b5e115745d962978e55ffb63ae2d62d0176c4c5ab457eea907a7aa33bf4e18","src/types.rs":"4aca0943f436cf52d446e1ae65c078f349821936e25ecced332f660b95382d75","src/write.rs":"eb9f2ce88180a9ee80b28a92e40ffa70f50377d2b5cdcb0e6ce763edbf0fa703","tests/data/invalid_offset.zip":"c5534a1803145f6344b04c437d436bd583852c78dd3937f4a73a2a39aa2d76b2","tests/data/mimetype.zip":"4bc6fd161d3e844b3e2b7f10fd0f9077ff565bb39fd2bfa34e7f8e9cd62db06d","tests/data/zip64_demo.zip":"223072a480f60d6a700e427b294e575ae2d6265f3e9881b647927f31d96c7e01","tests/invalid_date.rs":"e78b63b469d1f768bb452fefb84b013d3f9a4bf71dba2f9e12ff9e9c6ff8fd25"},"package":"77ce0ceee93c995954a31f77903925a6a8bb094709445238e344f2107910e29e"}
{"files":{".travis.yml":"1eaede3ba40c7f30a94c832ece27fb34771515c60938f31200a7ef2f97c6eb12","Cargo.toml":"00f64858860f669e881bf7e797c7f75bcdeb11e299bc9c7344fe3082ccb8b628","LICENSE":"6ac8711fb340c62ce0a4ecd463342d3fa0e8e70de697c863a2e1c0c53006003c","README.md":"face441ad06a7ef87477d5e96f3c456bef8f16662b03b5084d6fed7705fc4e99","appveyor.yml":"5c2aabf9d1fd01ca3dc33a14241bc0ce4a7327d2cbc3c3af350334bf5d107315","examples/extract.rs":"53b555617192330bd2f95235b82749905ad52617a5a3ba2648dbafbfff8a49a0","examples/extract_lorem.rs":"4e4e70927d2928a411ce4abe37ef4924c7156c4fd2697527c01fbd1797a76969","examples/stdin_info.rs":"54a9ce97b75a83c81de56c46cfe477dbd34dd98f739eb767f329b0e6dcb7490f","examples/write_dir.rs":"1a39da7b5d31997bf353a56d009ecdcf31da765f9ab191b20792e1904a594183","examples/write_sample.rs":"72a7c2db8074d20651ba04d0a49a7d6fb213ca674c63ab48db506deb69cba64c","script/doc-upload.cfg":"10684c8f5ef7e7388cfc4742ab9a060fe8f7fc1c51a63ede3c2443260ecc2d5e","src/compression.rs":"64ea510cfac9c9e5fed605a56fa44a7407df5cc51d4f63180bb2d4f2b307fdb2","src/cp437.rs":"7954c5b7df6f72fe3fe2c377c5de09973c2fa704c7ae0274a5490594e4b69517","src/crc32.rs":"3b503ee23047a4950e79f6956d0615f58c3cd8ded7f1fcd3f64e46dca37871da","src/lib.rs":"0afaf385ce7c7102016d2ba4c66c9cc7384ea710892477ed66bd1f560d83a732","src/read.rs":"d40a0b60c3b2dff73d6161e6d798a0e4aa0546a5f825bcddf6cf1bf52bf5366c","src/result.rs":"1006e46206a5b1491ed1d4fde0955a391df78cd42aa9582784b19170332a3783","src/spec.rs":"39b5e115745d962978e55ffb63ae2d62d0176c4c5ab457eea907a7aa33bf4e18","src/types.rs":"e2d7d3757013ca3ab149ee58fb5813fbeb7834e168870b28474816b47b869e61","src/write.rs":"59e3aff9339c9a16597101a5761391e93cc42eb99fb8c9057a6ee1e4e134da86","tests/data/invalid_offset.zip":"c5534a1803145f6344b04c437d436bd583852c78dd3937f4a73a2a39aa2d76b2","tests/data/mimetype.zip":"4bc6fd161d3e844b3e2b7f10fd0f9077ff565bb39fd2bfa34e7f8e9cd62db06d","tests/data/zip64_demo.zip":"223072a480f60d6a700e427b294e575ae2d6265f3e9881b647927f31d96c7e01","tests/invalid_date.rs":"e78b63b469d1f768bb452fefb84b013d3f9a4bf71dba2f9e12ff9e9c6ff8fd25"},"package":"36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"}

View File

@ -14,6 +14,8 @@ notifications:
script:
- cargo test
- cargo test --no-default-features
- cargo test --no-default-features --features "deflate-zlib"
- cargo test --no-default-features --features "deflate-miniz"
- cargo doc --no-deps
- rustdoc --test README.md -L target/debug

View File

@ -12,7 +12,7 @@
[package]
name = "zip"
version = "0.3.3"
version = "0.4.2"
authors = ["Mathijs van de Nes <git@mathijs.vd-nes.nl>"]
description = "Library to support the reading and writing of zip files.\n"
documentation = "http://mvdnes.github.io/rust-docs/zip-rs/zip/index.html"
@ -25,7 +25,6 @@ optional = true
[dependencies.flate2]
version = "1.0"
features = ["rust_backend"]
optional = true
default-features = false
@ -42,4 +41,6 @@ version = "1.0"
[features]
default = ["bzip2", "deflate"]
deflate = ["flate2"]
deflate = ["flate2", "flate2/rust_backend"]
deflate-miniz = ["flate2", "flate2/miniz-sys"]
deflate-zlib = ["flate2", "flate2/zlib"]

View File

@ -20,7 +20,6 @@ Supported compression formats:
Currently unsupported zip extensions:
* Most of ZIP64, although there is some support for archives with more than 65535 files
* Encryption
* Multi-disk
@ -33,14 +32,26 @@ With all default features:
```toml
[dependencies]
zip = "0.3"
zip = "0.4"
```
Without the default features:
```toml
[dependencies]
zip = { version = "0.3", default-features = false }
zip = { version = "0.4", default-features = false }
```
You can further control the backend of `deflate` compression method with these features:
* `deflate` (enabled by default) uses [miniz_oxide](https://github.com/Frommi/miniz_oxide)
* `deflate-miniz` uses [miniz](https://github.com/richgel999/miniz)
* `deflate-zlib` uses zlib
For example:
```toml
[dependencies]
zip = { version = "0.4", features = ["deflate-zlib"], default-features = false }
```
Examples
@ -51,3 +62,4 @@ See the [examples directory](examples) for:
* how to write a directory of files to a zip (using [walkdir](https://github.com/BurntSushi/walkdir)).
* How to extract a zip file.
* How to extract a single file from a zip.
* How to read a zip from the standard input.

View File

@ -42,3 +42,4 @@ build: false
test_script:
- cargo test
- cargo test --no-default-features
- cargo test --no-default-features --features "deflate-miniz"

View File

@ -0,0 +1,31 @@
extern crate zip;
use std::io::{self, Read};
fn main() {
std::process::exit(real_main());
}
fn real_main() -> i32 {
let stdin = io::stdin();
let mut stdin_handle = stdin.lock();
let mut buf = [0u8; 16];
loop {
match zip::read::read_zipfile_from_stream(&mut stdin_handle) {
Ok(Some(mut file)) => {
println!("{}: {} bytes ({} bytes packed)", file.name(), file.size(), file.compressed_size());
match file.read(&mut buf) {
Ok(n) => println!("The first {} bytes are: {:?}", n, &buf[0..n]),
Err(e) => println!("Could not read the file: {:?}", e),
};
},
Ok(None) => break,
Err(e) => {
println!("Error encountered while reading zip: {:?}", e);
return 1;
},
}
}
return 0;
}

View File

@ -9,7 +9,7 @@ pub enum CompressionMethod
/// The file is stored (no compression)
Stored,
/// The file is Deflated
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
Deflated,
/// File is compressed using BZIP2 algorithm
#[cfg(feature = "bzip2")]
@ -23,7 +23,7 @@ impl CompressionMethod {
pub fn from_u16(val: u16) -> CompressionMethod {
match val {
0 => CompressionMethod::Stored,
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
8 => CompressionMethod::Deflated,
#[cfg(feature = "bzip2")]
12 => CompressionMethod::Bzip2,
@ -35,7 +35,7 @@ impl CompressionMethod {
pub fn to_u16(self) -> u16 {
match self {
CompressionMethod::Stored => 0,
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
CompressionMethod::Deflated => 8,
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 => 12,
@ -65,22 +65,22 @@ mod test {
}
}
#[cfg(all(not(feature = "bzip2"), feature = "deflate"))]
#[cfg(all(not(feature = "bzip2"), feature = "flate2"))]
fn methods() -> Vec<CompressionMethod> {
vec![CompressionMethod::Stored, CompressionMethod::Deflated]
}
#[cfg(all(not(feature = "deflate"), feature = "bzip2"))]
#[cfg(all(not(feature = "flate2"), feature = "bzip2"))]
fn methods() -> Vec<CompressionMethod> {
vec![CompressionMethod::Stored, CompressionMethod::Bzip2]
}
#[cfg(all(feature = "bzip2", feature = "deflate"))]
#[cfg(all(feature = "bzip2", feature = "flate2"))]
fn methods() -> Vec<CompressionMethod> {
vec![CompressionMethod::Stored, CompressionMethod::Deflated, CompressionMethod::Bzip2]
}
#[cfg(all(not(feature = "bzip2"), not(feature = "deflate")))]
#[cfg(all(not(feature = "bzip2"), not(feature = "flate2")))]
fn methods() -> Vec<CompressionMethod> {
vec![CompressionMethod::Stored]
}

View File

@ -87,6 +87,10 @@ impl<R> Crc32Reader<R>
{
self.check == self.crc
}
pub fn into_inner(self) -> R {
self.inner
}
}
impl<R: Read> Read for Crc32Reader<R>

View File

@ -4,7 +4,7 @@
#[cfg(feature = "bzip2")]
extern crate bzip2;
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
extern crate flate2;
extern crate msdos_time;
extern crate podio;

View File

@ -7,15 +7,16 @@ use result::{ZipResult, ZipError};
use std::io;
use std::io::prelude::*;
use std::collections::HashMap;
use std::borrow::Cow;
use podio::{ReadPodExt, LittleEndian};
use types::{ZipFileData, System};
use cp437::FromCp437;
use msdos_time::{TmMsDosExt, MsDosDateTime};
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
use flate2;
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
use flate2::read::DeflateDecoder;
#[cfg(feature = "bzip2")]
@ -73,11 +74,13 @@ pub struct ZipArchive<R: Read + io::Seek>
files: Vec<ZipFileData>,
names_map: HashMap<String, usize>,
offset: u64,
comment: Vec<u8>,
}
enum ZipFileReader<'a> {
NoReader,
Stored(Crc32Reader<io::Take<&'a mut Read>>),
#[cfg(feature = "deflate")]
#[cfg(feature = "flate2")]
Deflated(Crc32Reader<flate2::read::DeflateDecoder<io::Take<&'a mut Read>>>),
#[cfg(feature = "bzip2")]
Bzip2(Crc32Reader<BzDecoder<io::Take<&'a mut Read>>>),
@ -85,7 +88,7 @@ enum ZipFileReader<'a> {
/// A struct for reading a zip file
pub struct ZipFile<'a> {
data: &'a ZipFileData,
data: Cow<'a, ZipFileData>,
reader: ZipFileReader<'a>,
}
@ -94,75 +97,115 @@ fn unsupported_zip_error<T>(detail: &'static str) -> ZipResult<T>
Err(ZipError::UnsupportedArchive(detail))
}
fn make_reader<'a>(
compression_method: ::compression::CompressionMethod,
crc32: u32,
reader: io::Take<&'a mut io::Read>)
-> ZipResult<ZipFileReader<'a>> {
match compression_method {
CompressionMethod::Stored =>
{
Ok(ZipFileReader::Stored(Crc32Reader::new(
reader,
crc32)))
},
#[cfg(feature = "flate2")]
CompressionMethod::Deflated =>
{
let deflate_reader = DeflateDecoder::new(reader);
Ok(ZipFileReader::Deflated(Crc32Reader::new(
deflate_reader,
crc32)))
},
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 =>
{
let bzip2_reader = BzDecoder::new(reader);
Ok(ZipFileReader::Bzip2(Crc32Reader::new(
bzip2_reader,
crc32)))
},
_ => unsupported_zip_error("Compression method not supported"),
}
}
impl<R: Read+io::Seek> ZipArchive<R>
{
/// Get the directory start offset and number of files. This is done in a
/// separate function to ease the control flow design.
fn get_directory_counts(mut reader: &mut R,
fn get_directory_counts(reader: &mut R,
footer: &spec::CentralDirectoryEnd,
cde_start_pos: u64) -> ZipResult<(u64, u64, usize)> {
// Some zip files have data prepended to them, resulting in the
// offsets all being too small. Get the amount of error by comparing
// the actual file position we found the CDE at with the offset
// recorded in the CDE.
let archive_offset = cde_start_pos.checked_sub(footer.central_directory_size as u64)
.and_then(|x| x.checked_sub(footer.central_directory_offset as u64))
.ok_or(ZipError::InvalidArchive("Invalid central directory size or offset"))?;
let directory_start = footer.central_directory_offset as u64 + archive_offset;
let number_of_files = footer.number_of_files_on_this_disk as usize;
// See if there's a ZIP64 footer. The ZIP64 locator if present will
// have its signature 20 bytes in front of the standard footer. The
// standard footer, in turn, is 22+N bytes large, where N is the
// comment length. Therefore:
if let Err(_) = reader.seek(io::SeekFrom::Current(-(20 + 22 + footer.zip_file_comment.len() as i64))) {
let zip64locator = if reader.seek(io::SeekFrom::End(-(20 + 22 + footer.zip_file_comment.len() as i64))).is_ok() {
match spec::Zip64CentralDirectoryEndLocator::parse(reader) {
Ok(loc) => Some(loc),
Err(ZipError::InvalidArchive(_)) => {
// No ZIP64 header; that's actually fine. We're done here.
None
},
Err(e) => {
// Yikes, a real problem
return Err(e);
}
}
}
else {
// Empty Zip files will have nothing else so this error might be fine. If
// not, we'll find out soon.
return Ok((archive_offset, directory_start, number_of_files));
}
let locator64 = match spec::Zip64CentralDirectoryEndLocator::parse(&mut reader) {
Ok(loc) => loc,
Err(ZipError::InvalidArchive(_)) => {
// No ZIP64 header; that's actually fine. We're done here.
return Ok((archive_offset, directory_start, number_of_files));
},
Err(e) => {
// Yikes, a real problem
return Err(e);
},
None
};
// If we got here, this is indeed a ZIP64 file.
match zip64locator {
None => {
// Some zip files have data prepended to them, resulting in the
// offsets all being too small. Get the amount of error by comparing
// the actual file position we found the CDE at with the offset
// recorded in the CDE.
let archive_offset = cde_start_pos.checked_sub(footer.central_directory_size as u64)
.and_then(|x| x.checked_sub(footer.central_directory_offset as u64))
.ok_or(ZipError::InvalidArchive("Invalid central directory size or offset"))?;
if footer.disk_number as u32 != locator64.disk_with_central_directory {
return unsupported_zip_error("Support for multi-disk files is not implemented")
let directory_start = footer.central_directory_offset as u64 + archive_offset;
let number_of_files = footer.number_of_files_on_this_disk as usize;
return Ok((archive_offset, directory_start, number_of_files));
},
Some(locator64) => {
// If we got here, this is indeed a ZIP64 file.
if footer.disk_number as u32 != locator64.disk_with_central_directory {
return unsupported_zip_error("Support for multi-disk files is not implemented")
}
// We need to reassess `archive_offset`. We know where the ZIP64
// central-directory-end structure *should* be, but unfortunately we
// don't know how to precisely relate that location to our current
// actual offset in the file, since there may be junk at its
// beginning. Therefore we need to perform another search, as in
// read::CentralDirectoryEnd::find_and_parse, except now we search
// forward.
let search_upper_bound = cde_start_pos
.checked_sub(60) // minimum size of Zip64CentralDirectoryEnd + Zip64CentralDirectoryEndLocator
.ok_or(ZipError::InvalidArchive("File cannot contain ZIP64 central directory end"))?;
let (footer, archive_offset) = spec::Zip64CentralDirectoryEnd::find_and_parse(
reader,
locator64.end_of_central_directory_offset,
search_upper_bound)?;
if footer.disk_number != footer.disk_with_central_directory {
return unsupported_zip_error("Support for multi-disk files is not implemented")
}
let directory_start = footer.central_directory_offset + archive_offset;
Ok((archive_offset, directory_start, footer.number_of_files as usize))
},
}
// We need to reassess `archive_offset`. We know where the ZIP64
// central-directory-end structure *should* be, but unfortunately we
// don't know how to precisely relate that location to our current
// actual offset in the file, since there may be junk at its
// beginning. Therefore we need to perform another search, as in
// read::CentralDirectoryEnd::find_and_parse, except now we search
// forward.
let search_upper_bound = reader.seek(io::SeekFrom::Current(0))?
.checked_sub(60) // minimum size of Zip64CentralDirectoryEnd + Zip64CentralDirectoryEndLocator
.ok_or(ZipError::InvalidArchive("File cannot contain ZIP64 central directory end"))?;
let (footer, archive_offset) = spec::Zip64CentralDirectoryEnd::find_and_parse(
&mut reader,
locator64.end_of_central_directory_offset,
search_upper_bound)?;
if footer.disk_number != footer.disk_with_central_directory {
return unsupported_zip_error("Support for multi-disk files is not implemented")
}
let directory_start = footer.central_directory_offset + archive_offset;
Ok((archive_offset, directory_start, footer.number_of_files as usize))
}
/// Opens a Zip archive and parses the central directory
@ -177,10 +220,13 @@ impl<R: Read+io::Seek> ZipArchive<R>
let (archive_offset, directory_start, number_of_files) =
try!(Self::get_directory_counts(&mut reader, &footer, cde_start_pos));
let mut files = Vec::with_capacity(number_of_files);
let mut files = Vec::new();
let mut names_map = HashMap::new();
try!(reader.seek(io::SeekFrom::Start(directory_start)));
if let Err(_) = reader.seek(io::SeekFrom::Start(directory_start)) {
return Err(ZipError::InvalidArchive("Could not seek to start of central directory"));
}
for _ in 0 .. number_of_files
{
let file = try!(central_header_to_zip_file(&mut reader, archive_offset));
@ -193,6 +239,7 @@ impl<R: Read+io::Seek> ZipArchive<R>
files: files,
names_map: names_map,
offset: archive_offset,
comment: footer.zip_file_comment,
})
}
@ -246,33 +293,7 @@ impl<R: Read+io::Seek> ZipArchive<R>
try!(self.reader.seek(io::SeekFrom::Start(pos)));
let limit_reader = (self.reader.by_ref() as &mut Read).take(data.compressed_size);
let reader = match data.compression_method
{
CompressionMethod::Stored =>
{
ZipFileReader::Stored(Crc32Reader::new(
limit_reader,
data.crc32))
},
#[cfg(feature = "deflate")]
CompressionMethod::Deflated =>
{
let deflate_reader = DeflateDecoder::new(limit_reader);
ZipFileReader::Deflated(Crc32Reader::new(
deflate_reader,
data.crc32))
},
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 =>
{
let bzip2_reader = BzDecoder::new(limit_reader);
ZipFileReader::Bzip2(Crc32Reader::new(
bzip2_reader,
data.crc32))
},
_ => return unsupported_zip_error("Compression method not supported"),
};
Ok(ZipFile { reader: reader, data: data })
Ok(ZipFile { reader: try!(make_reader(data.compression_method, data.crc32, limit_reader)), data: Cow::Borrowed(data) })
}
/// Unwrap and return the inner reader object
@ -329,23 +350,6 @@ fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R, archive_offset:
false => file_comment_raw.from_cp437(),
};
// Remember end of central header
let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
// Parse local header
try!(reader.seek(io::SeekFrom::Start(offset)));
let signature = try!(reader.read_u32::<LittleEndian>());
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
{
return Err(ZipError::InvalidArchive("Invalid local file header"))
}
try!(reader.seek(io::SeekFrom::Current(22)));
let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let magic_and_header = 4 + 22 + 2 + 2;
let data_start = offset + magic_and_header + file_name_length + extra_field_length;
// Construct the result
let mut result = ZipFileData
{
@ -361,7 +365,7 @@ fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R, archive_offset:
file_name_raw: file_name_raw,
file_comment: file_comment,
header_start: offset,
data_start: data_start,
data_start: 0,
external_attributes: external_file_attributes,
};
@ -370,13 +374,30 @@ fn central_header_to_zip_file<R: Read+io::Seek>(reader: &mut R, archive_offset:
Err(e) => try!(Err(e)),
}
// Remember end of central header
let return_position = try!(reader.seek(io::SeekFrom::Current(0)));
// Parse local header
try!(reader.seek(io::SeekFrom::Start(result.header_start)));
let signature = try!(reader.read_u32::<LittleEndian>());
if signature != spec::LOCAL_FILE_HEADER_SIGNATURE
{
return Err(ZipError::InvalidArchive("Invalid local file header"))
}
try!(reader.seek(io::SeekFrom::Current(22)));
let file_name_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as u64;
let magic_and_header = 4 + 22 + 2 + 2;
result.data_start = result.header_start + magic_and_header + file_name_length + extra_field_length;
// Go back after the central header
try!(reader.seek(io::SeekFrom::Start(return_position)));
Ok(result)
}
fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
fn parse_extra_field(file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
{
let mut reader = io::Cursor::new(data);
@ -386,22 +407,34 @@ fn parse_extra_field(_file: &mut ZipFileData, data: &[u8]) -> ZipResult<()>
let len = try!(reader.read_u16::<LittleEndian>());
match kind
{
_ => try!(reader.seek(io::SeekFrom::Current(len as i64))),
// Zip64 extended information extra field
0x0001 => {
file.uncompressed_size = try!(reader.read_u64::<LittleEndian>());
file.compressed_size = try!(reader.read_u64::<LittleEndian>());
try!(reader.read_u64::<LittleEndian>()); // relative header offset
try!(reader.read_u32::<LittleEndian>()); // disk start number
},
_ => { try!(reader.seek(io::SeekFrom::Current(len as i64))); },
};
}
Ok(())
}
fn get_reader<'a>(reader: &'a mut ZipFileReader) -> &'a mut Read {
match *reader {
ZipFileReader::NoReader => panic!("ZipFileReader was in an invalid state"),
ZipFileReader::Stored(ref mut r) => r as &mut Read,
#[cfg(feature = "flate2")]
ZipFileReader::Deflated(ref mut r) => r as &mut Read,
#[cfg(feature = "bzip2")]
ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
}
}
/// Methods for retreiving information on zip files
impl<'a> ZipFile<'a> {
fn get_reader(&mut self) -> &mut Read {
match self.reader {
ZipFileReader::Stored(ref mut r) => r as &mut Read,
#[cfg(feature = "deflate")]
ZipFileReader::Deflated(ref mut r) => r as &mut Read,
#[cfg(feature = "bzip2")]
ZipFileReader::Bzip2(ref mut r) => r as &mut Read,
}
get_reader(&mut self.reader)
}
/// Get the version of the file
pub fn version_made_by(&self) -> (u8, u8) {
@ -442,6 +475,10 @@ impl<'a> ZipFile<'a> {
}
/// Get unix mode for the file
pub fn unix_mode(&self) -> Option<u32> {
if self.data.external_attributes == 0 {
return None;
}
match self.data.system {
System::Unix => {
Some(self.data.external_attributes >> 16)
@ -479,6 +516,128 @@ impl<'a> Read for ZipFile<'a> {
}
}
impl<'a> Drop for ZipFile<'a> {
fn drop(&mut self) {
// self.data is Owned, this reader is constructed by a streaming reader.
// In this case, we want to exhaust the reader so that the next file is accessible.
if let Cow::Owned(_) = self.data {
let mut buffer = [0; 1<<16];
// Get the inner `Take` reader so all decompression and CRC calculation is skipped.
let innerreader = ::std::mem::replace(&mut self.reader, ZipFileReader::NoReader);
let mut reader = match innerreader {
ZipFileReader::NoReader => panic!("ZipFileReader was in an invalid state"),
ZipFileReader::Stored(crcreader) => crcreader.into_inner(),
#[cfg(feature = "flate2")]
ZipFileReader::Deflated(crcreader) => crcreader.into_inner().into_inner(),
#[cfg(feature = "bzip2")]
ZipFileReader::Bzip2(crcreader) => crcreader.into_inner().into_inner(),
};
loop {
match reader.read(&mut buffer) {
Ok(0) => break,
Ok(_) => (),
Err(e) => panic!("Could not consume all of the output of the current ZipFile: {:?}", e),
}
}
}
}
}
/// Read ZipFile structures from a non-seekable reader.
///
/// This is an alternative method to read a zip file. If possible, use the ZipArchive functions
/// as some information will be missing when reading this manner.
///
/// Reads a file header from the start of the stream. Will return `Ok(Some(..))` if a file is
/// present at the start of the stream. Returns `Ok(None)` if the start of the central directory
/// is encountered. No more files should be read after this.
///
/// The Drop implementation of ZipFile ensures that the reader will be correctly positioned after
/// the structure is done.
///
/// Missing fields are:
/// * `comment`: set to an empty string
/// * `data_start`: set to 0
/// * `external_attributes`: `unix_mode()`: will return None
pub fn read_zipfile_from_stream<'a, R: io::Read>(reader: &'a mut R) -> ZipResult<Option<ZipFile>> {
let signature = try!(reader.read_u32::<LittleEndian>());
match signature {
spec::LOCAL_FILE_HEADER_SIGNATURE => (),
spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE => return Ok(None),
_ => return Err(ZipError::InvalidArchive("Invalid local file header")),
}
let version_made_by = try!(reader.read_u16::<LittleEndian>());
let flags = try!(reader.read_u16::<LittleEndian>());
let encrypted = flags & 1 == 1;
let is_utf8 = flags & (1 << 11) != 0;
let using_data_descriptor = flags & (1 << 3) != 0;
let compression_method = CompressionMethod::from_u16(try!(reader.read_u16::<LittleEndian>()));
let last_mod_time = try!(reader.read_u16::<LittleEndian>());
let last_mod_date = try!(reader.read_u16::<LittleEndian>());
let crc32 = try!(reader.read_u32::<LittleEndian>());
let compressed_size = try!(reader.read_u32::<LittleEndian>());
let uncompressed_size = try!(reader.read_u32::<LittleEndian>());
let file_name_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let extra_field_length = try!(reader.read_u16::<LittleEndian>()) as usize;
let file_name_raw = try!(ReadPodExt::read_exact(reader, file_name_length));
let extra_field = try!(ReadPodExt::read_exact(reader, extra_field_length));
let file_name = match is_utf8
{
true => String::from_utf8_lossy(&*file_name_raw).into_owned(),
false => file_name_raw.clone().from_cp437(),
};
let mut result = ZipFileData
{
system: System::from_u8((version_made_by >> 8) as u8),
version_made_by: version_made_by as u8,
encrypted: encrypted,
compression_method: compression_method,
last_modified_time: ::time::Tm::from_msdos(MsDosDateTime::new(last_mod_time, last_mod_date)).unwrap_or(TM_1980_01_01),
crc32: crc32,
compressed_size: compressed_size as u64,
uncompressed_size: uncompressed_size as u64,
file_name: file_name,
file_name_raw: file_name_raw,
file_comment: String::new(), // file comment is only available in the central directory
// header_start and data start are not available, but also don't matter, since seeking is
// not available.
header_start: 0,
data_start: 0,
// The external_attributes field is only available in the central directory.
// We set this to zero, which should be valid as the docs state 'If input came
// from standard input, this field is set to zero.'
external_attributes: 0,
};
match parse_extra_field(&mut result, &extra_field) {
Ok(..) | Err(ZipError::Io(..)) => {},
Err(e) => try!(Err(e)),
}
if encrypted {
return unsupported_zip_error("Encrypted files are not supported")
}
if using_data_descriptor {
return unsupported_zip_error("The file length is not available in the local header");
}
let limit_reader = (reader as &'a mut io::Read).take(result.compressed_size as u64);
let result_crc32 = result.crc32;
let result_compression_method = result.compression_method;
Ok(Some(ZipFile {
data: Cow::Owned(result),
reader: try!(make_reader(result_compression_method, result_crc32, limit_reader))
}))
}
#[cfg(test)]
mod test {
#[test]
@ -502,4 +661,31 @@ mod test {
let reader = ZipArchive::new(io::Cursor::new(v)).unwrap();
assert!(reader.len() == 1);
}
#[test]
fn zip_comment() {
use std::io;
use super::ZipArchive;
let mut v = Vec::new();
v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
let reader = ZipArchive::new(io::Cursor::new(v)).unwrap();
assert!(reader.comment == b"zip-rs");
}
#[test]
fn zip_read_streaming() {
use std::io;
use super::read_zipfile_from_stream;
let mut v = Vec::new();
v.extend_from_slice(include_bytes!("../tests/data/mimetype.zip"));
let mut reader = io::Cursor::new(v);
loop {
match read_zipfile_from_stream(&mut reader).unwrap() {
None => break,
_ => (),
}
}
}
}

View File

@ -28,7 +28,7 @@ impl System {
pub const DEFAULT_VERSION: u8 = 46;
/// Structure representing a ZIP file.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct ZipFileData
{
/// Compatibility of the file attribute information
@ -66,9 +66,20 @@ impl ZipFileData {
let no_null_filename = match self.file_name.find('\0') {
Some(index) => &self.file_name[0..index],
None => &self.file_name,
};
}.to_string();
::std::path::Path::new(no_null_filename)
// zip files can contain both / and \ as separators regardless of the OS
// and as we want to return a sanitized PathBuf that only supports the
// OS separator let's convert incompatible separators to compatible ones
let separator = ::std::path::MAIN_SEPARATOR;
let opposite_separator = match separator {
'/' => '\\',
'\\' | _ => '/',
};
let filename =
no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string());
::std::path::Path::new(&filename)
.components()
.filter(|component| match *component {
::std::path::Component::Normal(..) => true,

Some files were not shown because too many files have changed in this diff Show More