Skip to content

Commit 7aa32f2

Browse files
authored
Merge pull request #1401 from pitdicker/merge_0.4.x
Merge 0.4.x
2 parents 58f1f37 + 6e8c150 commit 7aa32f2

File tree

16 files changed

+630
-272
lines changed

16 files changed

+630
-272
lines changed

.github/pull_request_template.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### Thanks for contributing to chrono!
22

3-
If your feature is semver-compatible, please target the 0.4.x branch;
4-
the main branch will be used for 0.5.0 development going forward.
3+
If your feature is semver-compatible, please target the main branch;
4+
for semver-incompatible changes, please target the `0.5.x` branch.
55

66
Please consider adding a test to ensure your bug fix/feature will not break in the future.

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
- uses: taiki-e/install-action@cargo-hack
7878
- uses: Swatinem/rust-cache@v2
7979
- run: |
80-
cargo hack check --feature-powerset --optional-deps serde \
80+
cargo hack check --feature-powerset --optional-deps arbitrary,serde \
8181
--skip __internal_bench,iana-time-zone,pure-rust-locales,libc,winapi,rkyv-16,rkyv-64,rkyv-validation \
8282
--all-targets
8383
# run using `bash` on all platforms for consistent

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
[![Chat][discord-image]][discord]
88
[![codecov.io][codecov-img]][codecov-link]
99

10-
[gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg
11-
[gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest
10+
[gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg?branch=0.5.x
11+
[gh-checks]: https://github.com/chronotope/chrono/actions/workflows/test.yml?query=branch%3A0.5.x
1212
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg
1313
[cratesio]: https://crates.io/crates/chrono
1414
[docsrs-image]: https://docs.rs/chrono/badge.svg

src/datetime/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
2626
#[cfg(feature = "clock")]
2727
use crate::offset::Local;
2828
use crate::offset::{FixedOffset, Offset, TimeZone, Utc};
29+
use crate::try_opt;
2930
use crate::{Datelike, Months, TimeDelta, Timelike, Weekday};
3031

3132
#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))]
@@ -87,7 +88,10 @@ impl<Tz: TimeZone> DateTime<Tz> {
8788
/// ```
8889
#[inline]
8990
#[must_use]
90-
pub fn from_naive_utc_and_offset(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
91+
pub const fn from_naive_utc_and_offset(
92+
datetime: NaiveDateTime,
93+
offset: Tz::Offset,
94+
) -> DateTime<Tz> {
9195
DateTime { datetime, offset }
9296
}
9397

@@ -591,8 +595,11 @@ impl DateTime<Utc> {
591595
/// ```
592596
#[inline]
593597
#[must_use]
594-
pub fn from_timestamp(secs: i64, nsecs: u32) -> Option<Self> {
595-
NaiveDateTime::from_timestamp_opt(secs, nsecs).as_ref().map(NaiveDateTime::and_utc)
598+
pub const fn from_timestamp(secs: i64, nsecs: u32) -> Option<Self> {
599+
Some(DateTime {
600+
datetime: try_opt!(NaiveDateTime::from_timestamp_opt(secs, nsecs)),
601+
offset: Utc,
602+
})
596603
}
597604

598605
/// Makes a new [`DateTime<Utc>`] from the number of non-leap milliseconds
@@ -623,14 +630,6 @@ impl DateTime<Utc> {
623630
NaiveDateTime::from_timestamp_millis(millis).as_ref().map(NaiveDateTime::and_utc)
624631
}
625632

626-
// FIXME: remove when our MSRV is 1.61+
627-
// This method is used by `NaiveDateTime::and_utc` because `DateTime::from_naive_utc_and_offset`
628-
// can't be made const yet.
629-
// Trait bounds in const function / implementation blocks were not supported until 1.61.
630-
pub(crate) const fn from_naive_utc(datetime: NaiveDateTime) -> Self {
631-
DateTime { datetime, offset: Utc }
632-
}
633-
634633
/// The Unix Epoch, 1970-01-01 00:00:00 UTC.
635634
pub const UNIX_EPOCH: Self = Self { datetime: NaiveDateTime::UNIX_EPOCH, offset: Utc };
636635
}
@@ -1674,7 +1673,7 @@ impl From<DateTime<Utc>> for js_sys::Date {
16741673

16751674
// Note that implementation of Arbitrary cannot be simply derived for DateTime<Tz>, due to
16761675
// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary.
1677-
#[cfg(feature = "arbitrary")]
1676+
#[cfg(all(feature = "arbitrary", feature = "std"))]
16781677
impl<'a, Tz> arbitrary::Arbitrary<'a> for DateTime<Tz>
16791678
where
16801679
Tz: TimeZone,

src/lib.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@
252252
//! # }
253253
//! ```
254254
//!
255-
//! Parsing can be done with three methods:
255+
//! Parsing can be done with two methods:
256256
//!
257257
//! 1. The standard [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) trait
258258
//! (and [`parse`](https://doc.rust-lang.org/std/primitive.str.html#method.parse) method
@@ -270,12 +270,6 @@
270270
//! [`DateTime::parse_from_rfc3339`](./struct.DateTime.html#method.parse_from_rfc3339)
271271
//! are similar but for well-known formats.
272272
//!
273-
//! 3. [`Offset::datetime_from_str`](./offset/trait.TimeZone.html#method.datetime_from_str) is
274-
//! similar but returns `DateTime` of given offset.
275-
//! When the explicit offset is missing from the input, it simply uses given offset.
276-
//! It issues an error when the input contains an explicit offset different
277-
//! from the current offset.
278-
//!
279273
//! More detailed control over the parsing process is available via
280274
//! [`format`](./format/index.html) module.
281275
//!
@@ -352,11 +346,11 @@
352346
//!
353347
//! ## Limitations
354348
//!
355-
//! Only the proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
356-
//! Date types are limited to about +/- 262,000 years from the common epoch.
357-
//! Time types are limited to nanosecond accuracy.
358-
//! Leap seconds can be represented, but Chrono does not fully support them.
359-
//! See [Leap Second Handling](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html#leap-second-handling).
349+
//! * Only the proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
350+
//! * Date types are limited to about +/- 262,000 years from the common epoch.
351+
//! * Time types are limited to nanosecond accuracy.
352+
//! * Leap seconds can be represented, but Chrono does not fully support them.
353+
//! See [Leap Second Handling](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html#leap-second-handling).
360354
//!
361355
//! ## Rust version requirements
362356
//!
@@ -365,18 +359,6 @@
365359
//! The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done
366360
//! lightly.
367361
//!
368-
//! Chrono inherently does not support an inaccurate or partial date and time representation.
369-
//! Any operation that can be ambiguous will return `None` in such cases.
370-
//! For example, "a month later" of 2014-01-30 is not well-defined
371-
//! and consequently `Utc.ymd_opt(2014, 1, 30).unwrap().with_month(2)` returns `None`.
372-
//!
373-
//! Non ISO week handling is not yet supported.
374-
//! For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext)
375-
//! crate ([sources](https://github.com/bcourtine/chrono-ext/)).
376-
//!
377-
//! Advanced time zone handling is not yet supported.
378-
//! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
379-
//!
380362
//! ## Relation between chrono and time 0.1
381363
//!
382364
//! Rust first had a `time` module added to `std` in its 0.7 release. It later moved to

src/month.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::OutOfRange;
3636
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
3737
)]
3838
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
39-
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
39+
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
4040
pub enum Month {
4141
/// January
4242
January = 0,
@@ -187,7 +187,7 @@ impl TryFrom<u8> for Month {
187187

188188
/// A duration in calendar months
189189
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
190-
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
190+
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
191191
pub struct Months(pub(crate) u32);
192192

193193
impl Months {

src/naive/date.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ impl NaiveWeek {
6060
/// ```
6161
#[inline]
6262
#[must_use]
63-
pub fn first_day(&self) -> NaiveDate {
63+
pub const fn first_day(&self) -> NaiveDate {
6464
let start = self.start.num_days_from_monday() as i32;
6565
let ref_day = self.date.weekday().num_days_from_monday() as i32;
6666
// Calculate the number of days to subtract from `self.date`.
6767
// Do not construct an intermediate date beyond `self.date`, because that may be out of
6868
// range if `date` is close to `NaiveDate::MAX`.
6969
let days = start - ref_day - if start > ref_day { 7 } else { 0 };
70-
self.date.add_days(days).unwrap()
70+
expect!(self.date.add_days(days), "first weekday out of range for `NaiveDate`")
7171
}
7272

7373
/// Returns a date representing the last day of the week.
@@ -88,14 +88,14 @@ impl NaiveWeek {
8888
/// ```
8989
#[inline]
9090
#[must_use]
91-
pub fn last_day(&self) -> NaiveDate {
91+
pub const fn last_day(&self) -> NaiveDate {
9292
let end = self.start.pred().num_days_from_monday() as i32;
9393
let ref_day = self.date.weekday().num_days_from_monday() as i32;
9494
// Calculate the number of days to add to `self.date`.
9595
// Do not construct an intermediate date before `self.date` (like with `first_day()`),
9696
// because that may be out of range if `date` is close to `NaiveDate::MIN`.
9797
let days = end - ref_day + if end < ref_day { 7 } else { 0 };
98-
self.date.add_days(days).unwrap()
98+
expect!(self.date.add_days(days), "last weekday out of range for `NaiveDate`")
9999
}
100100

101101
/// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
@@ -118,7 +118,7 @@ impl NaiveWeek {
118118
/// ```
119119
#[inline]
120120
#[must_use]
121-
pub fn days(&self) -> RangeInclusive<NaiveDate> {
121+
pub const fn days(&self) -> RangeInclusive<NaiveDate> {
122122
self.first_day()..=self.last_day()
123123
}
124124
}
@@ -206,7 +206,7 @@ pub const MIN_DATE: NaiveDate = NaiveDate::MIN;
206206
#[deprecated(since = "0.4.20", note = "Use NaiveDate::MAX instead")]
207207
pub const MAX_DATE: NaiveDate = NaiveDate::MAX;
208208

209-
#[cfg(feature = "arbitrary")]
209+
#[cfg(all(feature = "arbitrary", feature = "std"))]
210210
impl arbitrary::Arbitrary<'_> for NaiveDate {
211211
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveDate> {
212212
let year = u.int_in_range(MIN_YEAR..=MAX_YEAR)?;
@@ -1154,9 +1154,12 @@ impl NaiveDate {
11541154
/// assert_eq!(NaiveDate::MAX.checked_add_signed(TimeDelta::days(1)), None);
11551155
/// ```
11561156
#[must_use]
1157-
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
1158-
let days = i32::try_from(rhs.num_days()).ok()?;
1159-
self.add_days(days)
1157+
pub const fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
1158+
let days = rhs.num_days();
1159+
if days < i32::MIN as i64 || days > i32::MAX as i64 {
1160+
return None;
1161+
}
1162+
self.add_days(days as i32)
11601163
}
11611164

11621165
/// Subtracts the number of whole days in the given `TimeDelta` from the current date.
@@ -1180,9 +1183,12 @@ impl NaiveDate {
11801183
/// assert_eq!(NaiveDate::MIN.checked_sub_signed(TimeDelta::days(1)), None);
11811184
/// ```
11821185
#[must_use]
1183-
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
1184-
let days = i32::try_from(-rhs.num_days()).ok()?;
1185-
self.add_days(days)
1186+
pub const fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
1187+
let days = -rhs.num_days();
1188+
if days < i32::MIN as i64 || days > i32::MAX as i64 {
1189+
return None;
1190+
}
1191+
self.add_days(days as i32)
11861192
}
11871193

11881194
/// Subtracts another `NaiveDate` from the current date.
@@ -1208,7 +1214,7 @@ impl NaiveDate {
12081214
/// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(1614, 1, 1)), TimeDelta::days(365*400 + 97));
12091215
/// ```
12101216
#[must_use]
1211-
pub fn signed_duration_since(self, rhs: NaiveDate) -> TimeDelta {
1217+
pub const fn signed_duration_since(self, rhs: NaiveDate) -> TimeDelta {
12121218
let year1 = self.year();
12131219
let year2 = rhs.year();
12141220
let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400);
@@ -1553,7 +1559,7 @@ impl Datelike for NaiveDate {
15531559
/// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().day(), 14);
15541560
/// ```
15551561
///
1556-
/// Combined with [`NaiveDate::pred`](#method.pred),
1562+
/// Combined with [`NaiveDate::pred_opt`](#method.pred_opt),
15571563
/// one can determine the number of days in a particular month.
15581564
/// (Note that this panics when `year` is out of range.)
15591565
///
@@ -1610,7 +1616,7 @@ impl Datelike for NaiveDate {
16101616
/// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().ordinal(), 74);
16111617
/// ```
16121618
///
1613-
/// Combined with [`NaiveDate::pred`](#method.pred),
1619+
/// Combined with [`NaiveDate::pred_opt`](#method.pred_opt),
16141620
/// one can determine the number of days in a particular year.
16151621
/// (Note that this panics when `year` is out of range.)
16161622
///

src/naive/datetime/mod.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime::MAX;
7979
archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash))
8080
)]
8181
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
82-
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
82+
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))]
8383
pub struct NaiveDateTime {
8484
date: NaiveDate,
8585
time: NaiveTime,
@@ -502,9 +502,11 @@ impl NaiveDateTime {
502502
#[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")]
503503
#[inline]
504504
#[must_use]
505-
pub fn timestamp_nanos(&self) -> i64 {
506-
self.timestamp_nanos_opt()
507-
.expect("value can not be represented in a timestamp with nanosecond precision.")
505+
pub const fn timestamp_nanos(&self) -> i64 {
506+
expect!(
507+
self.timestamp_nanos_opt(),
508+
"value can not be represented in a timestamp with nanosecond precision."
509+
)
508510
}
509511

510512
/// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970.
@@ -540,9 +542,9 @@ impl NaiveDateTime {
540542
/// ```
541543
#[inline]
542544
#[must_use]
543-
pub fn timestamp_nanos_opt(&self) -> Option<i64> {
545+
pub const fn timestamp_nanos_opt(&self) -> Option<i64> {
544546
let mut timestamp = self.timestamp();
545-
let mut timestamp_subsec_nanos = i64::from(self.timestamp_subsec_nanos());
547+
let mut timestamp_subsec_nanos = self.timestamp_subsec_nanos() as i64;
546548

547549
// subsec nanos are always non-negative, however the timestamp itself (both in seconds and in nanos) can be
548550
// negative. Now i64::MIN is NOT dividable by 1_000_000_000, so
@@ -560,7 +562,7 @@ impl NaiveDateTime {
560562
timestamp += 1;
561563
}
562564

563-
timestamp.checked_mul(1_000_000_000).and_then(|ns| ns.checked_add(timestamp_subsec_nanos))
565+
try_opt!(timestamp.checked_mul(1_000_000_000)).checked_add(timestamp_subsec_nanos)
564566
}
565567

566568
/// Returns the number of milliseconds since the last whole non-leap second.
@@ -697,7 +699,7 @@ impl NaiveDateTime {
697699
/// Some(from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap()));
698700
/// ```
699701
#[must_use]
700-
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
702+
pub const fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
701703
let (time, rhs) = self.time.overflowing_add_signed(rhs);
702704

703705
// early checking to avoid overflow in OldTimeDelta::seconds
@@ -850,15 +852,15 @@ impl NaiveDateTime {
850852
/// Some(from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap()));
851853
/// ```
852854
#[must_use]
853-
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
855+
pub const fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
854856
let (time, rhs) = self.time.overflowing_sub_signed(rhs);
855857

856858
// early checking to avoid overflow in OldTimeDelta::seconds
857859
if rhs <= (-1 << MAX_SECS_BITS) || rhs >= (1 << MAX_SECS_BITS) {
858860
return None;
859861
}
860862

861-
let date = self.date.checked_sub_signed(TimeDelta::seconds(rhs))?;
863+
let date = try_opt!(self.date.checked_sub_signed(TimeDelta::seconds(rhs)));
862864
Some(NaiveDateTime { date, time })
863865
}
864866

@@ -947,8 +949,13 @@ impl NaiveDateTime {
947949
/// TimeDelta::seconds(3600) - TimeDelta::milliseconds(500));
948950
/// ```
949951
#[must_use]
950-
pub fn signed_duration_since(self, rhs: NaiveDateTime) -> TimeDelta {
951-
self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time)
952+
pub const fn signed_duration_since(self, rhs: NaiveDateTime) -> TimeDelta {
953+
expect!(
954+
self.date
955+
.signed_duration_since(rhs.date)
956+
.checked_add(&self.time.signed_duration_since(rhs.time)),
957+
"always in range"
958+
)
952959
}
953960

954961
/// Formats the combined date and time with the specified formatting items.
@@ -1064,8 +1071,7 @@ impl NaiveDateTime {
10641071
/// ```
10651072
#[must_use]
10661073
pub const fn and_utc(&self) -> DateTime<Utc> {
1067-
// FIXME: use `DateTime::from_naive_utc_and_offset` when our MSRV is 1.61+.
1068-
DateTime::from_naive_utc(*self)
1074+
DateTime::from_naive_utc_and_offset(*self, Utc)
10691075
}
10701076

10711077
/// The minimum possible `NaiveDateTime`.

0 commit comments

Comments
 (0)