Skip to content

Commit

Permalink
Fixes wrong value for microsecs in a day (#1849)
Browse files Browse the repository at this point in the history
`MICROSECONDS_PER_DAY` constant at `time_with_timezone.rs` misses 3
additional zeros for microseconds part.

That breaks conversion from `(pg_sys::TimeADT, i32)` to
`TimeWithTimeZone`.

Fixes #1850
  • Loading branch information
aykut-bozkurt authored Sep 11, 2024
1 parent b4c4441 commit 5eadffb
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 8 deletions.
16 changes: 15 additions & 1 deletion pgrx-tests/src/tests/datetime_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ mod tests {
#[allow(unused_imports)]
use crate as pgrx_tests;

use pgrx::datum::datetime_support::IntervalConversionError;
use pgrx::datum::datetime_support::{IntervalConversionError, USECS_PER_DAY};
use pgrx::datum::{get_timezone_offset, DateTimeConversionError};
use pgrx::prelude::*;
use serde_json::*;
Expand Down Expand Up @@ -133,6 +133,20 @@ mod tests {
assert_eq!(json!({"time W/ Zone test":"12:23:34+02:00"}), json);
}

#[pg_test]
fn test_timetz_from_time_and_zone() {
let timeadt: pg_sys::TimeADT = USECS_PER_DAY / 2;
let zone = 0;
let timetz = TimeWithTimeZone::try_from((timeadt, zone)).unwrap();

let expected_timetz = TimeWithTimeZone::with_timezone(12, 0, 0.0, "UTC").unwrap();

assert_eq!(timetz.hour(), expected_timetz.hour());
assert_eq!(timetz.minute(), expected_timetz.minute());
assert_eq!(timetz.second(), expected_timetz.second());
assert_eq!(timetz.timezone_offset(), expected_timetz.timezone_offset());
}

#[pg_test]
fn test_date_serialization() {
let date: Date = Date::new(2020, 4, 7).unwrap();
Expand Down
3 changes: 3 additions & 0 deletions pgrx/src/datum/datetime_support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ mod ops;

pub use ctor::*;

pub const USECS_PER_SEC: i64 = 1_000_000;
pub const USECS_PER_DAY: i64 = pg_sys::SECS_PER_DAY as i64 * USECS_PER_SEC;

/// Tags to identify which "part" of a date or time-type value to extract or truncate to
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum DateTimeParts {
Expand Down
5 changes: 1 addition & 4 deletions pgrx/src/datum/interval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@
//LICENSE All rights reserved.
//LICENSE
//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
use crate::datum::datetime_support::IntervalConversionError;
use crate::datum::datetime_support::{IntervalConversionError, USECS_PER_DAY, USECS_PER_SEC};
use crate::datum::{DateTimeParts, DateTimeTypeVisitor, FromDatum, IntoDatum, Time, ToIsoString};
use crate::{direct_function_call, pg_sys};
use pgrx_sql_entity_graph::metadata::{
ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable,
};

pub const USECS_PER_SEC: i64 = 1_000_000;
pub const USECS_PER_DAY: i64 = pg_sys::SECS_PER_DAY as i64 * USECS_PER_SEC;

/// From the PG docs <https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT>
/// Internally interval values are stored as months, days, and microseconds. This is done because the number of days in a month varies,
/// and a day can have 23 or 25hours if a daylight savings time adjustment is involved. The months and days fields are integers while
Expand Down
6 changes: 3 additions & 3 deletions pgrx/src/datum/time_with_timezone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::{
DateTimeConversionError, FromDatum, Interval, IntoDatum, Time, TimestampWithTimeZone,
ToIsoString,
};
use crate::datum::datetime_support::{DateTimeParts, HasExtractableParts};
use crate::datum::datetime_support::{DateTimeParts, HasExtractableParts, USECS_PER_DAY};
use crate::{direct_function_call, direct_function_call_as_datum, pg_sys, PgMemoryContexts};
use pgrx_pg_sys::errcodes::PgSqlErrorCode;
use pgrx_pg_sys::PgTryBuilder;
Expand All @@ -24,7 +24,7 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct TimeWithTimeZone(pg_sys::TimeTzADT);
const MICROSECONDS_PER_DAY: pg_sys::TimeADT = 86_400_000;

impl From<TimeWithTimeZone> for pg_sys::TimeTzADT {
#[inline]
fn from(value: TimeWithTimeZone) -> Self {
Expand Down Expand Up @@ -146,7 +146,7 @@ impl TimeWithTimeZone {
}

pub fn modular_from_raw(tuple: (i64, i32)) -> Self {
let time = tuple.0.rem_euclid(MICROSECONDS_PER_DAY);
let time = tuple.0.rem_euclid(USECS_PER_DAY);
let zone = tuple.1.rem_euclid(pg_sys::TZDISP_LIMIT as i32);

Self(pg_sys::TimeTzADT { time, zone })
Expand Down

0 comments on commit 5eadffb

Please sign in to comment.