Fix Calendar: No expanded occurrences are returned for a daily recurrences crossing DST

This commit is contained in:
Maurus Decimus
2026-07-19 22:59:03 +02:00
parent 152db36247
commit 2d08933fbc
4 changed files with 115 additions and 108 deletions

View File

@@ -4,7 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{Alarm, AlarmDelta, ArchivedAlarmDelta, ArchivedCalendarEventData};
use super::{
Alarm, AlarmDelta, ArchivedAlarmDelta, ArchivedCalendarEventData, expand::resolve_local,
};
use calcard::{
common::timezone::Tz,
icalendar::{
@@ -12,7 +14,6 @@ use calcard::{
ICalendarRelated, ICalendarValue,
},
};
use chrono::{DateTime, TimeZone};
use std::str::FromStr;
use store::write::bitpack::BitpackIterator;
use utils::codec::leb128::Leb128Reader;
@@ -74,18 +75,12 @@ impl ArchivedCalendarEventData {
for start_offset in unpacker {
let start_date_naive = start_offset as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) else {
continue;
};
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz)
&& alarm_time > start_time
@@ -124,18 +119,12 @@ impl ArchivedCalendarEventData {
// Single event
let start_date_naive = offset_or_count as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) else {
continue;
};
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz)
&& alarm_time > start_time
@@ -265,10 +254,7 @@ impl AlarmDelta {
AlarmDelta::Start(delta) => Some(start + delta),
AlarmDelta::End(delta) => Some(end + delta),
AlarmDelta::FixedUtc(timestamp) => Some(*timestamp),
AlarmDelta::FixedFloating(timestamp) => default_tz
.from_local_datetime(&DateTime::from_timestamp(*timestamp, 0)?.naive_local())
.single()
.map(|dt| dt.timestamp()),
AlarmDelta::FixedFloating(timestamp) => resolve_local(default_tz, *timestamp),
}
}
}
@@ -279,12 +265,9 @@ impl ArchivedAlarmDelta {
ArchivedAlarmDelta::Start(delta) => Some(start + delta.to_native()),
ArchivedAlarmDelta::End(delta) => Some(end + delta.to_native()),
ArchivedAlarmDelta::FixedUtc(timestamp) => Some(timestamp.to_native()),
ArchivedAlarmDelta::FixedFloating(timestamp) => default_tz
.from_local_datetime(
&DateTime::from_timestamp(timestamp.to_native(), 0)?.naive_local(),
)
.single()
.map(|dt| dt.timestamp()),
ArchivedAlarmDelta::FixedFloating(timestamp) => {
resolve_local(default_tz, timestamp.to_native())
}
}
}
}

View File

@@ -55,18 +55,13 @@ impl ArchivedCalendarEventData {
for start_offset in unpacker {
let start_date_naive = start_offset as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) else {
expansion_id += 1;
continue;
};
if limit.is_in_range(is_todo, start, end) {
expansion.push(CalendarEventExpansion {
@@ -85,20 +80,11 @@ impl ArchivedCalendarEventData {
// Single event
let start_date_naive = offset_or_count as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
if limit.is_in_range(is_todo, start, end) {
if let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) && limit.is_in_range(is_todo, start, end)
{
expansion.push(CalendarEventExpansion {
comp_id,
expansion_id: base_expansion_id,
@@ -157,25 +143,17 @@ impl CalendarEventData {
if expansion_ids.remove(&expansion_id) {
let start_date_naive = start_offset as i64 + base_offset;
let end_date_naive = start_date_naive + range.duration as i64;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
expansion.push(CalendarEventExpansion {
comp_id: range.id as u32,
expansion_id,
start,
end,
});
if let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) {
expansion.push(CalendarEventExpansion {
comp_id: range.id as u32,
expansion_id,
start,
end,
});
}
match_count -= 1;
if match_count == 0 {
@@ -194,25 +172,17 @@ impl CalendarEventData {
// Single event
let start_date_naive = offset_or_count as i64 + base_offset;
let end_date_naive = start_date_naive + range.duration as i64;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
expansion.push(CalendarEventExpansion {
comp_id: range.id as u32,
expansion_id: base_expansion_id,
start,
end,
});
if let (Some(start), Some(end)) = (
resolve_local(start_tz, start_date_naive),
resolve_local(end_tz, end_date_naive),
) {
expansion.push(CalendarEventExpansion {
comp_id: range.id as u32,
expansion_id: base_expansion_id,
start,
end,
});
}
if expansion_ids.is_empty() {
break 'outer;
@@ -263,14 +233,8 @@ impl CalendarEventData {
};
let start_date_naive = start_offset as i64 + self.base_offset;
let end_date_naive = start_date_naive + range.duration as i64;
let start = start_tz
.from_local_datetime(&DateTime::from_timestamp(start_date_naive, 0)?.naive_local())
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(&DateTime::from_timestamp(end_date_naive, 0)?.naive_local())
.single()?
.timestamp();
let start = resolve_local(start_tz, start_date_naive)?;
let end = resolve_local(end_tz, end_date_naive)?;
Some(CalendarEventExpansion {
comp_id,
@@ -297,3 +261,9 @@ impl CalendarEventExpansion {
self.comp_id != u32::MAX && self.start != i64::MAX && self.end != i64::MAX
}
}
pub(crate) fn resolve_local(tz: Tz, naive_secs: i64) -> Option<i64> {
tz.from_local_datetime(&DateTime::from_timestamp(naive_secs, 0)?.naive_local())
.earliest()
.map(|dt| dt.timestamp())
}