gabotechs commented on code in PR #23542:
URL: https://github.com/apache/datafusion/pull/23542#discussion_r3579418030
##########
datafusion/functions/src/datetime/date_trunc.rs:
##########
@@ -591,52 +591,128 @@ where
fn _date_trunc_coarse_with_tz(
granularity: DateTruncGranularity,
- value: Option<DateTime<Tz>>,
+ value: DateTime<Tz>,
) -> Result<Option<i64>> {
- if let Some(value) = value {
- let local = value.naive_local();
- let truncated = _date_trunc_coarse::<NaiveDateTime>(granularity,
Some(local))?;
- let truncated = truncated.and_then(|truncated| {
- match truncated.and_local_timezone(value.timezone()) {
- LocalResult::None => {
- // This can happen if the date_trunc operation moves the
time into
- // an hour that doesn't exist due to daylight savings. On
known example where
- // this can happen is with historic dates in the
America/Sao_Paulo time zone.
- // To account for this adjust the time by a few hours,
convert to local time,
- // and then adjust the time back.
- truncated
- .sub(TimeDelta::try_hours(3).unwrap())
- .and_local_timezone(value.timezone())
- .single()
- .map(|v| v.add(TimeDelta::try_hours(3).unwrap()))
- }
- LocalResult::Single(datetime) => Some(datetime),
- LocalResult::Ambiguous(datetime1, datetime2) => {
- // Because we are truncating from an equally or more
specific time
- // the original time must have been within the ambiguous
local time
- // period. Therefore the offset of one of these times
should match the
- // offset of the original time.
- if datetime1.offset().fix() == value.offset().fix() {
- Some(datetime1)
- } else {
- Some(datetime2)
- }
+ let local = value.naive_local();
+ let truncated = _date_trunc_coarse::<NaiveDateTime>(granularity,
Some(local))?;
+ let truncated = truncated.and_then(|truncated| {
+ match truncated.and_local_timezone(value.timezone()) {
+ LocalResult::None => {
+ // This can happen if the date_trunc operation moves the time
into
+ // an hour that doesn't exist due to daylight savings. On
known example where
+ // this can happen is with historic dates in the
America/Sao_Paulo time zone.
+ // To account for this adjust the time by a few hours, convert
to local time,
+ // and then adjust the time back.
+ truncated
+ .sub(TimeDelta::try_hours(3).unwrap())
+ .and_local_timezone(value.timezone())
+ .single()
+ .map(|v| v.add(TimeDelta::try_hours(3).unwrap()))
+ }
+ LocalResult::Single(datetime) => Some(datetime),
+ LocalResult::Ambiguous(datetime1, datetime2) => {
+ // Because we are truncating from an equally or more specific
time
+ // the original time must have been within the ambiguous local
time
+ // period. Therefore the offset of one of these times should
match the
+ // offset of the original time.
+ if datetime1.offset().fix() == value.offset().fix() {
+ Some(datetime1)
+ } else {
+ Some(datetime2)
}
}
- });
- Ok(truncated.and_then(|value| value.timestamp_nanos_opt()))
+ }
+ });
+ Ok(truncated.and_then(|value| value.timestamp_nanos_opt()))
+}
+
+/// Days from the Unix epoch to 0000-03-01, the epoch used by the civil
calendar
+/// conversions below.
+const DAYS_EPOCH_SHIFT: i64 = 719_468;
+
+/// Days in a 400 year era of the proleptic Gregorian calendar.
+const DAYS_PER_ERA: i64 = 146_097;
+
+/// Splits a day count relative to the Unix epoch into a proleptic Gregorian
+/// year, month (1-12) and day of month (1-31).
+fn civil_from_days(days: i64) -> (i64, i64, i64) {
+ let z = days + DAYS_EPOCH_SHIFT;
+ let era = z.div_euclid(DAYS_PER_ERA);
+ let day_of_era = z.rem_euclid(DAYS_PER_ERA);
+ let year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524
+ - day_of_era / 146_096)
+ / 365;
+ let day_of_year =
+ day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
+ // Month index with March as 0, so that the leap day falls at the end of
the year.
+ let month_index = (5 * day_of_year + 2) / 153;
+ let day = day_of_year - (153 * month_index + 2) / 5 + 1;
+ let month = if month_index < 10 {
+ month_index + 3
} else {
- _date_trunc_coarse::<NaiveDateTime>(granularity, None)?;
- Ok(None)
- }
+ month_index - 9
+ };
+ let year = year_of_era + era * 400 + i64::from(month <= 2);
+ (year, month, day)
}
Review Comment:
This math looks correct. Is there any link we can put in the comment so that
people and robots can have a reference in case this code needs some maintenance?
For example, I found these:
- https://howardhinnant.github.io/date_algorithms.html#days_from_civil
- https://howardhinnant.github.io/date_algorithms.html#civil_from_days
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]