|
14 | 14 |
|
15 | 15 | use std::sync::LazyLock; |
16 | 16 |
|
| 17 | +use databend_common_column::types::timestamp_tz; |
17 | 18 | use databend_common_exception::Result; |
18 | 19 | use databend_common_timezone::fast_components_from_timestamp; |
19 | 20 | use databend_common_timezone::fast_utc_from_local; |
@@ -260,6 +261,18 @@ fn date_from_components(c: &DateTimeComponents) -> Option<Date> { |
260 | 261 | Date::new(c.year as i16, c.month as i8, c.day as i8).ok() |
261 | 262 | } |
262 | 263 |
|
| 264 | +#[inline] |
| 265 | +pub fn timestamp_tz_local_micros(value: timestamp_tz) -> Option<i64> { |
| 266 | + let offset = value.micros_offset()?; |
| 267 | + value.timestamp().checked_add(offset) |
| 268 | +} |
| 269 | + |
| 270 | +#[inline] |
| 271 | +pub fn timestamp_tz_components_via_lut(value: timestamp_tz) -> Option<DateTimeComponents> { |
| 272 | + let local = timestamp_tz_local_micros(value)?; |
| 273 | + fast_components_from_timestamp(local, &TimeZone::UTC) |
| 274 | +} |
| 275 | + |
263 | 276 | fn datetime_from_components(c: &DateTimeComponents) -> Option<DateTime> { |
264 | 277 | let date = date_from_components(c)?; |
265 | 278 | Some(date.at( |
@@ -1025,55 +1038,39 @@ pub fn today_date(now: &Zoned, tz: &TimeZone) -> i32 { |
1025 | 1038 | // The working hours of all departments of The State Council are from 8 a.m. to 12 p.m. and from 1:30 p.m. to 5:30 p.m. The winter working hours will be implemented after September 17th. |
1026 | 1039 | pub fn calc_date_to_timestamp(val: i32, tz: &TimeZone) -> std::result::Result<i64, String> { |
1027 | 1040 | let ts = (val as i64) * 24 * 3600 * MICROS_PER_SEC; |
1028 | | - let z = ts.to_timestamp(tz); |
1029 | | - |
1030 | | - let tomorrow = z.date().tomorrow(); |
1031 | | - let yesterday = z.date().yesterday(); |
1032 | | - |
1033 | | - // If there were no yesterday or tomorrow, it might be the limit value. |
1034 | | - // e.g. 9999-12-31 |
1035 | | - if tomorrow.is_err() || yesterday.is_err() { |
1036 | | - let tz_offset_micros = tz |
1037 | | - .to_timestamp(date(1970, 1, 1).at(0, 0, 0, 0)) |
1038 | | - .unwrap() |
1039 | | - .as_microsecond(); |
1040 | | - return Ok(ts + tz_offset_micros); |
1041 | | - } |
1042 | | - |
1043 | | - // tomorrow midnight |
1044 | | - let tomorrow_date = tomorrow.map_err(|e| format!("Calc tomorrow midnight with error {}", e))?; |
1045 | | - |
1046 | | - let tomorrow_zoned = tomorrow_date.to_zoned(tz.clone()).unwrap_or(z.clone()); |
1047 | | - let tomorrow_is_dst = tz.to_offset_info(tomorrow_zoned.timestamp()).dst().is_dst(); |
1048 | | - |
1049 | | - // yesterday midnight |
1050 | | - let yesterday_date = |
1051 | | - yesterday.map_err(|e| format!("Calc yesterday midnight with error {}", e))?; |
1052 | | - let yesterday_zoned = yesterday_date.to_zoned(tz.clone()).unwrap_or(z.clone()); |
1053 | | - let yesterday_is_std = tz |
1054 | | - .to_offset_info(yesterday_zoned.timestamp()) |
1055 | | - .dst() |
1056 | | - .is_std(); |
1057 | | - |
1058 | | - // today midnight |
1059 | | - let today_datetime_midnight = z.date().to_datetime(Time::midnight()); |
1060 | | - let today_zoned = today_datetime_midnight |
1061 | | - .to_zoned(tz.clone()) |
1062 | | - .map_err(|e| format!("Calc today midnight with error {}", e))?; |
1063 | | - let today_is_dst = tz.to_offset_info(today_zoned.timestamp()).dst().is_dst(); |
1064 | | - |
1065 | | - let tz_offset_micros = tz |
1066 | | - .to_timestamp(date(1970, 1, 1).at(0, 0, 0, 0)) |
1067 | | - .unwrap() |
1068 | | - .as_microsecond(); |
1069 | | - |
1070 | | - let base_res = ts + tz_offset_micros; |
| 1041 | + let local_date = val.to_date(tz); |
| 1042 | + let year = i32::from(local_date.year()); |
| 1043 | + let month = local_date.month() as u8; |
| 1044 | + let day = local_date.day() as u8; |
| 1045 | + |
| 1046 | + if let Some(micros) = fast_utc_from_local(tz, year, month, day, 0, 0, 0, 0) { |
| 1047 | + return Ok(micros); |
| 1048 | + } |
| 1049 | + |
| 1050 | + let midnight = local_date.to_datetime(Time::midnight()); |
| 1051 | + match midnight.to_zoned(tz.clone()) { |
| 1052 | + Ok(zoned) => Ok(zoned.timestamp().as_microsecond()), |
| 1053 | + Err(_err) => { |
| 1054 | + for minutes in 1..=1440 { |
| 1055 | + let delta = SignedDuration::from_secs((minutes * 60) as i64); |
| 1056 | + if let Ok(adj) = midnight.checked_add(delta) { |
| 1057 | + if let Ok(zoned) = adj.to_zoned(tz.clone()) { |
| 1058 | + return Ok(zoned.timestamp().as_microsecond()); |
| 1059 | + } |
| 1060 | + } else { |
| 1061 | + break; |
| 1062 | + } |
| 1063 | + } |
1071 | 1064 |
|
1072 | | - // Origin:(today_is_dst && tomorrow_is_dst && !yesterday_is_std) || (today_is_dst && !tomorrow_is_dst && yesterday_is_std) |
1073 | | - if today_is_dst && (tomorrow_is_dst != yesterday_is_std) { |
1074 | | - Ok(base_res - 3600 * MICROS_PER_SEC) |
1075 | | - } else { |
1076 | | - Ok(base_res) |
| 1065 | + // The timezone database might not have explicit rules for extremely |
| 1066 | + // old/new dates, so fall back to the legacy behavior that applies the |
| 1067 | + // canonical offset we use for 1970-01-01. |
| 1068 | + let tz_offset_micros = tz |
| 1069 | + .to_timestamp(date(1970, 1, 1).at(0, 0, 0, 0)) |
| 1070 | + .unwrap() |
| 1071 | + .as_microsecond(); |
| 1072 | + Ok(ts + tz_offset_micros) |
| 1073 | + } |
1077 | 1074 | } |
1078 | 1075 | } |
1079 | 1076 |
|
|
0 commit comments