Skip to content

Commit

Permalink
Merge pull request #986 from chronotope/merge-0.4
Browse files Browse the repository at this point in the history
Merge 0.4
  • Loading branch information
djc authored Mar 17, 2023
2 parents ac3e398 + 5b04073 commit 1a0d47a
Show file tree
Hide file tree
Showing 20 changed files with 326 additions and 132 deletions.
15 changes: 6 additions & 9 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@ on:
push:
branches: [main, 0.4.x]
pull_request:
paths:
- "**.rs"
- .github/**
- .ci/**
- Cargo.toml
- CITATION.cff
- deny.toml

jobs:
lint:
Expand All @@ -20,8 +13,12 @@ jobs:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo fmt -- --check --color=always
- run: cargo clippy --color=always -- -D warnings
- run: |
cargo fmt --check -- --color=always
cargo fmt --check --manifest-path fuzz/Cargo.toml
- run: |
cargo clippy --color=always -- -D warnings -A clippy::manual-non-exhaustive
cargo clippy --manifest-path fuzz/Cargo.toml --color=always -- -D warnings
env:
RUSTFLAGS: "-Dwarnings"
Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ on:
push:
branches: [main, 0.4.x]
pull_request:
paths:
- "**.rs"
- .github/**
- .ci/**
- Cargo.toml

jobs:
timezones_linux:
Expand Down Expand Up @@ -68,6 +63,7 @@ jobs:
toolchain: ${{ matrix.rust_version }}
- uses: Swatinem/rust-cache@v2
- run: cargo check --benches
- run: cargo check --manifest-path fuzz/Cargo.toml --all-targets
# run --lib and --doc to avoid the long running integration tests which are run elsewhere
- run: cargo test --lib --all-features --color=always -- --color=always
- run: cargo test --doc --all-features --color=always -- --color=always
Expand Down Expand Up @@ -119,7 +115,7 @@ jobs:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
wasm-pack --version
- run: cargo build --target ${{ matrix.target }} --color=always

features_check_wasm:
strategy:
matrix:
Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ serde = { version = "1.0.99", default-features = false, optional = true }
pure-rust-locales = { version = "0.5.2", optional = true }
criterion = { version = "0.4.0", optional = true }
rkyv = {version = "0.7", optional = true}
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }
arbitrary = { version = "1.0.0", features = ["derive"], optional = true }

[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies]
Expand All @@ -42,6 +41,9 @@ js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.45.0", features = ["Win32_System_Time", "Win32_Foundation"], optional = true }

[target.'cfg(unix)'.dependencies]
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }

[dev-dependencies]
serde_json = { version = "1" }
serde_derive = { version = "1", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ edition = "2018"
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.3"
libfuzzer-sys = "0.4"

[dependencies.chrono]
path = ".."
Expand Down
4 changes: 2 additions & 2 deletions fuzz/fuzz_targets/fuzz_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
use chrono::prelude::*;
if let Ok(data) = std::str::from_utf8(data) {
let _ = DateTime::parse_from_rfc2822(data);
let _ = DateTime::parse_from_rfc3339(data);
let _ = DateTime::<FixedOffset>::parse_from_rfc2822(data);
let _ = DateTime::<FixedOffset>::parse_from_rfc3339(data);
}
});
12 changes: 4 additions & 8 deletions src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub enum Pad {
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum Numeric {
/// Full Gregorian year (FW=4, PW=∞).
/// May accept years before 1 BCE or after 9999 CE, given an initial sign.
/// May accept years before 1 BCE or after 9999 CE, given an initial sign (+/-).
Year,
/// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year.
YearDiv100,
Expand Down Expand Up @@ -350,7 +350,7 @@ pub struct ParseError(ParseErrorKind);

impl ParseError {
/// The category of parse error
pub fn kind(&self) -> ParseErrorKind {
pub const fn kind(&self) -> ParseErrorKind {
self.0
}
}
Expand Down Expand Up @@ -516,12 +516,8 @@ fn format_inner(
Item::Numeric(ref spec, ref pad) => {
use self::Numeric::*;

let week_from_sun = |d: &NaiveDate| {
(d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 7) / 7
};
let week_from_mon = |d: &NaiveDate| {
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 7) / 7
};
let week_from_sun = |d: &NaiveDate| d.weeks_from(Weekday::Sun);
let week_from_mon = |d: &NaiveDate| d.weeks_from(Weekday::Mon);

let (width, v) = match *spec {
Year => (4, date.map(|d| i64::from(d.year()))),
Expand Down
5 changes: 2 additions & 3 deletions src/format/parsed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,8 @@ impl Parsed {
// verify the ordinal and other (non-ISO) week dates.
let verify_ordinal = |date: NaiveDate| {
let ordinal = date.ordinal();
let weekday = date.weekday();
let week_from_sun = (ordinal as i32 - weekday.num_days_from_sunday() as i32 + 7) / 7;
let week_from_mon = (ordinal as i32 - weekday.num_days_from_monday() as i32 + 7) / 7;
let week_from_sun = date.weeks_from(Weekday::Sun);
let week_from_mon = date.weeks_from(Weekday::Mon);
self.ordinal.unwrap_or(ordinal) == ordinal
&& self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun
&& self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon
Expand Down
4 changes: 2 additions & 2 deletions src/format/scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)
let mut n = 0i64;
for (i, c) in bytes.iter().take(max).cloned().enumerate() {
// cloned() = copied()
if !(b'0'..=b'9').contains(&c) {
if !c.is_ascii_digit() {
if i < min {
return Err(INVALID);
} else {
Expand Down Expand Up @@ -79,7 +79,7 @@ pub(super) fn nanosecond(s: &str) -> ParseResult<(&str, i64)> {
let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?;

// if there are more than 9 digits, skip next digits.
let s = s.trim_left_matches(|c: char| ('0'..='9').contains(&c));
let s = s.trim_left_matches(|c: char| c.is_ascii_digit());

Ok((s, v))
}
Expand Down
6 changes: 3 additions & 3 deletions src/format/strftime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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. chrono supports years from -262144 to 262143. |
| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. chrono supports years from -262144 to 262143. Note: years before 1 BCE or after 9999 CE, require an initial sign (+/-).|
| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^1] |
| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^1] |
| | | |
Expand Down Expand Up @@ -584,7 +584,7 @@ fn test_strftime_docs() {
assert_eq!(dt.format("%A").to_string(), "Sunday");
assert_eq!(dt.format("%w").to_string(), "0");
assert_eq!(dt.format("%u").to_string(), "7");
assert_eq!(dt.format("%U").to_string(), "28");
assert_eq!(dt.format("%U").to_string(), "27");
assert_eq!(dt.format("%W").to_string(), "27");
assert_eq!(dt.format("%G").to_string(), "2001");
assert_eq!(dt.format("%g").to_string(), "01");
Expand Down Expand Up @@ -663,7 +663,7 @@ fn test_strftime_docs() {
#[cfg(feature = "unstable-locales")]
#[test]
fn test_strftime_docs_localized() {
use crate::{FixedOffset, NaiveDate, TimeZone};
use crate::{FixedOffset, NaiveDate};

let dt = NaiveDate::from_ymd_opt(2001, 7, 8)
.and_then(|d| d.and_hms_nano_opt(0, 34, 59, 1_026_490_708))
Expand Down
2 changes: 1 addition & 1 deletion src/month.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ pub struct Months(pub(crate) u32);

impl Months {
/// Construct a new `Months` from a number of months
pub fn new(num: u32) -> Self {
pub const fn new(num: u32) -> Self {
Self(num)
}
}
Expand Down
104 changes: 96 additions & 8 deletions src/naive/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ pub struct Days(pub(crate) u64);

impl Days {
/// Construct a new `Days` from a number of days
pub fn new(num: u64) -> Self {
pub const fn new(num: u64) -> Self {
Self(num)
}
}
Expand Down Expand Up @@ -234,6 +234,9 @@ fn test_date_bounds() {
}

impl NaiveDate {
pub(crate) fn weeks_from(&self, day: Weekday) -> i32 {
(self.ordinal() as i32 - self.weekday().num_days_from(day) as i32 + 6) / 7
}
/// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification.
fn from_of(year: i32, of: Of) -> Option<NaiveDate> {
if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
Expand Down Expand Up @@ -644,6 +647,10 @@ impl NaiveDate {
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(2)),
/// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap())
/// );
/// assert_eq!(
/// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(1000000000000)),
/// None
/// );
/// ```
pub fn checked_add_days(self, days: Days) -> Option<Self> {
if days.0 == 0 {
Expand All @@ -663,6 +670,10 @@ impl NaiveDate {
/// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(6)),
/// Some(NaiveDate::from_ymd_opt(2022, 2, 14).unwrap())
/// );
/// assert_eq!(
/// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(1000000000000)),
/// None
/// );
/// ```
pub fn checked_sub_days(self, days: Days) -> Option<Self> {
if days.0 == 0 {
Expand All @@ -673,7 +684,11 @@ impl NaiveDate {
}

fn diff_days(self, days: i64) -> Option<Self> {
self.checked_add_signed(TimeDelta::days(days))
let secs = days.checked_mul(86400)?; // 86400 seconds in one day
if secs >= core::i64::MAX / 1000 || secs <= core::i64::MIN / 1000 {
return None; // See the `time` 0.1 crate. Outside these bounds, `TimeDelta::seconds` will panic
}
self.checked_add_signed(TimeDelta::seconds(secs))
}

/// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`.
Expand All @@ -691,7 +706,7 @@ impl NaiveDate {
/// assert_eq!(dt.time(), t);
/// ```
#[inline]
pub fn and_time(&self, time: NaiveTime) -> NaiveDateTime {
pub const fn and_time(&self, time: NaiveTime) -> NaiveDateTime {
NaiveDateTime::new(*self, time)
}

Expand Down Expand Up @@ -881,7 +896,7 @@ impl NaiveDate {

/// Returns the packed ordinal-flags.
#[inline]
fn of(&self) -> Of {
const fn of(&self) -> Of {
Of((self.ymdf & 0b1_1111_1111_1111) as u32)
}

Expand Down Expand Up @@ -1204,7 +1219,7 @@ impl NaiveDate {
/// }
/// ```
#[inline]
pub fn iter_days(&self) -> NaiveDateDaysIterator {
pub const fn iter_days(&self) -> NaiveDateDaysIterator {
NaiveDateDaysIterator { value: *self }
}

Expand Down Expand Up @@ -1235,14 +1250,14 @@ impl NaiveDate {
/// }
/// ```
#[inline]
pub fn iter_weeks(&self) -> NaiveDateWeeksIterator {
pub const fn iter_weeks(&self) -> NaiveDateWeeksIterator {
NaiveDateWeeksIterator { value: *self }
}

/// Returns the [`NaiveWeek`] that the date belongs to, starting with the [`Weekday`]
/// specified.
#[inline]
pub fn week(&self, start: Weekday) -> NaiveWeek {
pub const fn week(&self, start: Weekday) -> NaiveWeek {
NaiveWeek { date: *self, start }
}

Expand Down Expand Up @@ -2784,6 +2799,16 @@ mod tests {
assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err());
assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err());
assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient

assert_eq!(
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
NaiveDate::from_ymd_opt(2020, 1, 12),
);

assert_eq!(
NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
NaiveDate::from_ymd_opt(2019, 1, 13),
);
}

#[test]
Expand Down Expand Up @@ -2822,7 +2847,7 @@ mod tests {
// corner cases
assert_eq!(
NaiveDate::from_ymd_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(),
"2008,08,53,53,01"
"2008,08,52,53,01"
);
assert_eq!(
NaiveDate::from_ymd_opt(2010, 1, 3).unwrap().format("%G,%g,%U,%W,%V").to_string(),
Expand Down Expand Up @@ -2871,4 +2896,67 @@ mod tests {
assert!(days.contains(&date));
}
}

#[test]
fn test_weeks_from() {
// tests per: https://github.com/chronotope/chrono/issues/961
// these internally use `weeks_from` via the parsing infrastructure
assert_eq!(
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
NaiveDate::from_ymd_opt(2020, 1, 12),
);
assert_eq!(
NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
NaiveDate::from_ymd_opt(2019, 1, 13),
);

// direct tests
for (y, starts_on) in &[
(2019, Weekday::Tue),
(2020, Weekday::Wed),
(2021, Weekday::Fri),
(2022, Weekday::Sat),
(2023, Weekday::Sun),
(2024, Weekday::Mon),
(2025, Weekday::Wed),
(2026, Weekday::Thu),
] {
for day in &[
Weekday::Mon,
Weekday::Tue,
Weekday::Wed,
Weekday::Thu,
Weekday::Fri,
Weekday::Sat,
Weekday::Sun,
] {
assert_eq!(
NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)),
Some(if day == starts_on { 1 } else { 0 })
);

// last day must always be in week 52 or 53
assert!([52, 53]
.contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)),);
}
}

let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap();

// 400 years covers all year types
for day in &[
Weekday::Mon,
Weekday::Tue,
Weekday::Wed,
Weekday::Thu,
Weekday::Fri,
Weekday::Sat,
Weekday::Sun,
] {
// must always be below 54
for dplus in 1..(400 * 366) {
assert!((base + Days::new(dplus)).weeks_from(*day) < 54)
}
}
}
}
Loading

0 comments on commit 1a0d47a

Please sign in to comment.