Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 0.4 #986

Merged
merged 19 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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