Skip to content

Commit

Permalink
Do not use deprecated chrono types and functions
Browse files Browse the repository at this point in the history
  • Loading branch information
oll3 committed Jun 3, 2024
1 parent 261f1e5 commit 0235f91
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 42 deletions.
9 changes: 4 additions & 5 deletions rrule/src/iter/pos_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::utils::{add_time_to_date, from_ordinal, pymod};
use super::utils::{add_time_to_date, date_from_ordinal, pymod};
use crate::core::{DateTime, Tz};
use chrono::NaiveTime;

Expand Down Expand Up @@ -44,13 +44,12 @@ pub(crate) fn build_pos_list(
let day = i64::try_from(*day)
.expect("dayset is controlled by us and all elements are within range of i64");

// Get ordinal which is UTC and apply timezone
let date = from_ordinal(year_ordinal + day).date().with_timezone(&tz);
// Get ordinal which is UTC
let date = date_from_ordinal(year_ordinal + day);
// Create new Date + Time combination
// Use Date and Timezone from `date`
// Use Time from `timeset`.
let time = timeset[time_pos];
let res = match add_time_to_date(date, time) {
let res = match add_time_to_date(tz, date, time) {
Some(date) => date,
None => continue,
};
Expand Down
20 changes: 7 additions & 13 deletions rrule/src/iter/rrule_iter.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use super::counter_date::DateTimeIter;
use super::utils::add_time_to_date;
use super::{build_pos_list, utils::from_ordinal, IterInfo, MAX_ITER_LOOP};
use super::{build_pos_list, utils::date_from_ordinal, IterInfo, MAX_ITER_LOOP};
use crate::core::{get_hour, get_minute, get_second};
use crate::{core::DateTime, Frequency, RRule};
use chrono::Datelike;
use chrono::{NaiveTime, TimeZone};
use chrono::NaiveTime;
use std::collections::VecDeque;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -124,6 +123,8 @@ impl<'a> RRuleIter<'a> {
self.counter_date.day,
);

let tz = self.dt_start.timezone();

if rrule.by_set_pos.is_empty() {
// Loop over `start..end`
for current_day in &dayset {
Expand All @@ -133,17 +134,10 @@ impl<'a> RRuleIter<'a> {
let year_ordinal = self.ii.year_ordinal();
// Ordinal conversion uses UTC: if we apply local-TZ here, then
// just below we'll end up double-applying.
let date = from_ordinal(year_ordinal + current_day);
// We apply the local-TZ here.
let date = self
.dt_start
.timezone()
.ymd(date.year(), date.month(), date.day());

let date = date_from_ordinal(year_ordinal + current_day);
for time in &self.timeset {
let dt = match add_time_to_date(date, *time) {
Some(dt) => dt,
None => continue,
let Some(dt) = add_time_to_date(tz, date, *time) else {
continue;
};
if Self::try_add_datetime(
dt,
Expand Down
61 changes: 38 additions & 23 deletions rrule/src/iter/utils.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use std::ops;

use crate::core::{duration_from_midnight, DateTime, Tz};
use chrono::{Date, NaiveTime, TimeZone, Utc};

const UTC: Tz = Tz::UTC;
use chrono::{NaiveDate, NaiveTime, Utc};

const DAY_SECS: i64 = 24 * 60 * 60;

/// Converts number of days since unix epoch back to `DataTime`
pub(crate) fn from_ordinal(ordinal: i64) -> DateTime {
let timestamp = ordinal * DAY_SECS;
UTC.timestamp_opt(timestamp, 0).unwrap()
/// Converts number of days since unix epoch to a (naive) date.
pub(crate) fn date_from_ordinal(ordinal: i64) -> NaiveDate {
chrono::DateTime::<Utc>::from_timestamp(ordinal * DAY_SECS, 0)
.unwrap()
.date_naive()
}

/// Returns number of days since unix epoch (rounded down)
Expand Down Expand Up @@ -73,24 +72,40 @@ where
}
}

pub(crate) fn add_time_to_date(date: Date<Tz>, time: NaiveTime) -> Option<DateTime> {
if let Some(dt) = date.and_time(time) {
pub(crate) fn add_time_to_date(tz: Tz, date: NaiveDate, time: NaiveTime) -> Option<DateTime> {
if let Some(dt) = date.and_time(time).and_local_timezone(tz).single() {
return Some(dt);
}
// If the day is a daylight saving time, the above code might now work, and we
// If the day is a daylight saving time, the above code might not work, and we
// can try to get a valid datetime by adding the `time` as a duration instead.
let dt = date.and_hms_opt(0, 0, 0)?;
let dt = date.and_hms_opt(0, 0, 0)?.and_local_timezone(tz).single()?;
let day_duration = duration_from_midnight(time);
dt.checked_add_signed(day_duration)
}

#[cfg(test)]
mod test {

use chrono::Duration;
use chrono::{Duration, TimeZone};

use super::*;

#[test]
fn naive_date_from_ordinal() {
let tests = [
(-1, NaiveDate::from_ymd_opt(1969, 12, 31).unwrap()),
(0, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()),
(1, NaiveDate::from_ymd_opt(1970, 1, 2).unwrap()),
(10, NaiveDate::from_ymd_opt(1970, 1, 11).unwrap()),
(365, NaiveDate::from_ymd_opt(1971, 1, 1).unwrap()),
(19877, NaiveDate::from_ymd_opt(2024, 6, 3).unwrap()),
];

for (days, expected) in tests {
assert_eq!(date_from_ordinal(days), expected, "seconds: {}", days);
}
}

#[test]
fn python_mod() {
assert_eq!(pymod(2, -3), -1);
Expand Down Expand Up @@ -138,20 +153,19 @@ mod test {

#[test]
fn adds_time_to_date() {
const AMERICA_NEW_YORK: Tz = Tz::America__New_York;
const AMERICA_VANCOUVER: Tz = Tz::America__Vancouver;

let tests = [
(
UTC.ymd(2017, 1, 1),
Tz::UTC,
NaiveDate::from_ymd_opt(2017, 1, 1).unwrap(),
NaiveTime::from_hms_opt(1, 15, 30).unwrap(),
Some(UTC.with_ymd_and_hms(2017, 1, 1, 1, 15, 30).unwrap()),
Some(Tz::UTC.with_ymd_and_hms(2017, 1, 1, 1, 15, 30).unwrap()),
),
(
AMERICA_VANCOUVER.ymd(2021, 3, 14),
Tz::America__Vancouver,
NaiveDate::from_ymd_opt(2021, 3, 14).unwrap(),
NaiveTime::from_hms_opt(2, 22, 10).unwrap(),
Some(
AMERICA_VANCOUVER
Tz::America__Vancouver
.with_ymd_and_hms(2021, 3, 14, 0, 0, 0)
.unwrap()
+ Duration::hours(2)
Expand All @@ -160,18 +174,19 @@ mod test {
),
),
(
AMERICA_NEW_YORK.ymd(1997, 10, 26),
Tz::America__New_York,
NaiveDate::from_ymd_opt(1997, 10, 26).unwrap(),
NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
Some(
AMERICA_NEW_YORK
Tz::America__New_York
.with_ymd_and_hms(1997, 10, 26, 9, 0, 0)
.unwrap(),
),
),
];

for (date, time, expected_output) in tests {
let res = add_time_to_date(date, time);
for (tz, date, time, expected_output) in tests {
let res = add_time_to_date(tz, date, time);
assert_eq!(res, expected_output);
}
}
Expand Down
3 changes: 2 additions & 1 deletion rrule/src/parser/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ pub(crate) fn datestring_to_date(
// For more info https://icalendar.org/iCalendar-RFC-5545/3-3-5-date-time.html
let datetime: chrono::DateTime<Tz> = if flags.zulu_timezone_set {
// If a `Z` is present, UTC should be used.
chrono::DateTime::<chrono::Utc>::from_utc(datetime, chrono::Utc).with_timezone(&Tz::UTC)
chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(datetime, chrono::Utc)
.with_timezone(&Tz::UTC)
} else {
// If no `Z` is present, local time should be used.
use chrono::offset::LocalResult;
Expand Down

0 comments on commit 0235f91

Please sign in to comment.