Skip to content

Commit 27775a8

Browse files
committed
ebug
1 parent 7cb20ad commit 27775a8

File tree

2 files changed

+76
-19
lines changed

2 files changed

+76
-19
lines changed

src/common/timezone/src/lib.rs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,38 @@ struct DayEntry {
4444
impl DayEntry {
4545
fn new(date: Date, tz: &TimeZone) -> Self {
4646
let midnight = date.to_datetime(Time::midnight());
47-
let zoned = midnight
48-
.to_zoned(tz.clone())
49-
.expect("construct timezone lut: convert midnight to zoned");
50-
51-
let start_utc = zoned.timestamp().as_second();
47+
let ambiguous = tz.to_ambiguous_zoned(midnight);
48+
let needs_later = ambiguous.is_ambiguous();
49+
let zoned = match (needs_later, ambiguous) {
50+
(true, ambiguous) => ambiguous
51+
.later()
52+
.expect("construct timezone lut: disambiguate midnight via later transition"),
53+
(false, ambiguous) => ambiguous
54+
.compatible()
55+
.expect("construct timezone lut: convert midnight to zoned"),
56+
};
57+
58+
let start_ts = zoned.timestamp();
59+
let start_utc = start_ts.as_second();
5260
let offset = zoned.offset().seconds();
5361

5462
let mut transition_utc = None;
5563
let mut offset_change = 0;
5664

57-
if let Some(trans) = tz.following(zoned.timestamp()).next() {
65+
let follow_start = start_ts
66+
.saturating_sub(SignedDuration::from_secs(1))
67+
.unwrap_or(start_ts);
68+
69+
if let Some(trans) = tz.following(follow_start).next() {
5870
let trans_sec = trans.timestamp().as_second();
5971
if trans_sec < start_utc + SECONDS_PER_DAY {
6072
transition_utc = Some(trans_sec);
61-
offset_change = trans.offset().seconds() - offset;
73+
let before_ts = trans
74+
.timestamp()
75+
.saturating_sub(SignedDuration::from_secs(1))
76+
.unwrap_or(trans.timestamp());
77+
let before_offset = tz.to_offset(before_ts).seconds();
78+
offset_change = trans.offset().seconds() - before_offset;
6279
}
6380
}
6481

@@ -465,4 +482,40 @@ mod tests {
465482
assert!(fast_utc_from_local(&tz, 1850, 1, 1, 0, 0, 0, 0).is_none());
466483
assert!(fast_utc_from_local(&tz, 2350, 1, 1, 0, 0, 0, 0).is_none());
467484
}
485+
486+
#[test]
487+
fn lut_prefers_later_midnight_in_fold() {
488+
let tz = TimeZone::get("Africa/Algiers").unwrap();
489+
let midnight = date(1939, 11, 19).at(0, 0, 0, 0);
490+
let amb_ts = tz.to_ambiguous_timestamp(midnight);
491+
assert!(amb_ts.is_ambiguous());
492+
493+
let later = amb_ts.later().unwrap();
494+
let before_second_midnight = later.as_microsecond() - 1;
495+
496+
let before_components =
497+
fast_components_from_timestamp(before_second_midnight, &tz).unwrap();
498+
assert_eq!(
499+
(
500+
before_components.year,
501+
before_components.month,
502+
before_components.day
503+
),
504+
(1939, 11, 18)
505+
);
506+
507+
let second_midnight_components =
508+
fast_components_from_timestamp(later.as_microsecond(), &tz).unwrap();
509+
assert_eq!(
510+
(
511+
second_midnight_components.year,
512+
second_midnight_components.month,
513+
second_midnight_components.day
514+
),
515+
(1939, 11, 19)
516+
);
517+
assert_eq!(second_midnight_components.hour, 0);
518+
assert_eq!(second_midnight_components.minute, 0);
519+
assert_eq!(second_midnight_components.second, 0);
520+
}
468521
}

src/query/expression/src/utils/date_helper.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -183,23 +183,14 @@ macro_rules! impl_interval_year_month {
183183
let original_offset = ts.offset().seconds();
184184

185185
if let Some(components) = fast_components_from_timestamp(us, tz) {
186-
let ts_micro = (ts.subsec_nanosecond() / 1_000) as u32;
187-
let components_match = components.year == ts.year() as i32
188-
&& components.month == ts.month() as u8
189-
&& components.day == ts.day() as u8
190-
&& components.hour == ts.hour() as u8
191-
&& components.minute == ts.minute() as u8
192-
&& components.second == ts.second() as u8
193-
&& components.micro == ts_micro
194-
&& components.offset_seconds == original_offset;
195186
let new_date = $op(
196187
components.year as i16,
197188
components.month as i8,
198189
components.day as i8,
199190
delta.as_(),
200191
add_months,
201192
)?;
202-
if let Some(new_ts) = fast_utc_from_local(
193+
if let Some(mut new_ts) = fast_utc_from_local(
203194
tz,
204195
new_date.year() as i32,
205196
new_date.month() as u8,
@@ -209,15 +200,28 @@ macro_rules! impl_interval_year_month {
209200
components.second,
210201
components.micro,
211202
) {
212-
if components_match {
203+
if let Some(new_components) = fast_components_from_timestamp(new_ts, tz) {
204+
if new_components.offset_seconds != original_offset {
205+
let shift_secs =
206+
(new_components.offset_seconds - original_offset) as i64;
207+
let shift_micros = shift_secs.saturating_mul(MICROS_PER_SEC);
208+
new_ts = new_ts.checked_add(shift_micros).unwrap_or_else(|| {
209+
if shift_micros.is_negative() {
210+
i64::MIN
211+
} else {
212+
i64::MAX
213+
}
214+
});
215+
}
216+
clamp_timestamp(&mut new_ts);
213217
return Ok(new_ts);
214218
}
215219
}
216220
}
217221

218222
let new_date = $op(ts.year(), ts.month(), ts.day(), delta.as_(), add_months)?;
219223

220-
let mut local =
224+
let local =
221225
new_date.at(ts.hour(), ts.minute(), ts.second(), ts.subsec_nanosecond());
222226
let mut zoned = match local.to_zoned(tz.clone()) {
223227
Ok(z) => z,

0 commit comments

Comments
 (0)