store: handle DST gaps in epoch_to_local

chrono's timestamp_opt can return None during DST transitions.
Handle all three variants (Single, Ambiguous, None) instead of
unwrapping. For DST gaps, offset by one hour to land in valid
local time.

Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-03-09 17:02:29 -04:00
parent 53e6b32cb4
commit 0e17ab00b0

View file

@ -129,7 +129,16 @@ pub fn now_epoch() -> i64 {
/// Returns (year, month, day, hour, minute, second). /// Returns (year, month, day, hour, minute, second).
pub fn epoch_to_local(epoch: i64) -> (i32, u32, u32, u32, u32, u32) { pub fn epoch_to_local(epoch: i64) -> (i32, u32, u32, u32, u32, u32) {
use chrono::{Datelike, Local, TimeZone, Timelike}; use chrono::{Datelike, Local, TimeZone, Timelike};
let dt = Local.timestamp_opt(epoch, 0).unwrap(); let dt = match Local.timestamp_opt(epoch, 0) {
chrono::LocalResult::Single(dt) => dt,
chrono::LocalResult::Ambiguous(dt, _) => dt,
chrono::LocalResult::None => {
// DST gap — add an hour to land in valid local time
Local.timestamp_opt(epoch + 3600, 0)
.earliest()
.unwrap_or_else(|| chrono::Utc.timestamp_opt(epoch, 0).unwrap().with_timezone(&Local))
}
};
( (
dt.year(), dt.year(),
dt.month(), dt.month(),