Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement inequality post processing for timestamps #164

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions crates/proof-of-sql-parser/src/posql_time/timezone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ impl PoSQLTimeZone {
PoSQLTimeZone::FixedOffset(offset)
}
}

/// For comparisons, normalize a timestamp based on a timezone offset so
/// it can be compared to another timestamp.
pub fn normalize_to_utc(timestamp: i64, tz: &PoSQLTimeZone) -> i64 {
match tz {
PoSQLTimeZone::Utc => timestamp, // No adjustment needed
PoSQLTimeZone::FixedOffset(offset) => timestamp - *offset as i64, // Adjust by offset in seconds
}
}
}

impl TryFrom<&Option<Arc<str>>> for PoSQLTimeZone {
Expand Down Expand Up @@ -77,7 +86,7 @@ impl fmt::Display for PoSQLTimeZone {

#[cfg(test)]
mod timezone_parsing_tests {
use crate::posql_time::timezone;
use crate::posql_time::{timezone, PoSQLTimeZone, PoSQLTimestamp};
use alloc::format;

#[test]
Expand All @@ -97,11 +106,6 @@ mod timezone_parsing_tests {
let timezone = timezone::PoSQLTimeZone::Utc;
assert_eq!(format!("{timezone}"), "+00:00");
}
}

#[cfg(test)]
mod timezone_offset_tests {
use crate::posql_time::{timestamp::PoSQLTimestamp, timezone};

#[test]
fn test_utc_timezone() {
Expand Down Expand Up @@ -134,4 +138,43 @@ mod timezone_offset_tests {
let result = PoSQLTimestamp::try_from(input).unwrap();
assert_eq!(result.timezone(), expected_timezone);
}

#[test]
fn test_from_offset() {
// UTC case
let tz = PoSQLTimeZone::from_offset(0);
assert!(matches!(tz, PoSQLTimeZone::Utc));

// Fixed offset case
let tz = PoSQLTimeZone::from_offset(3600); // UTC+1
assert!(matches!(tz, PoSQLTimeZone::FixedOffset(3600)));

// Negative offset case (UTC-5)
let tz = PoSQLTimeZone::from_offset(-18000);
assert!(matches!(tz, PoSQLTimeZone::FixedOffset(-18000)));
}

#[test]
fn test_normalize_to_utc_with_utc() {
let timestamp = 1231006505; // Unix timestamp in seconds
let tz = PoSQLTimeZone::Utc;
let normalized = PoSQLTimeZone::normalize_to_utc(timestamp, &tz);
assert_eq!(normalized, timestamp); // No adjustment for UTC
}

#[test]
fn test_normalize_to_utc_with_positive_offset() {
let timestamp = 1231006505; // Unix timestamp in seconds
let tz = PoSQLTimeZone::FixedOffset(3600); // UTC+1 (3600 seconds offset)
let normalized = PoSQLTimeZone::normalize_to_utc(timestamp, &tz);
assert_eq!(normalized, 1231006505 - 3600); // Adjusted by 1 hour
}

#[test]
fn test_normalize_to_utc_with_negative_offset() {
let timestamp = 1231006505; // Unix timestamp in seconds
let tz = PoSQLTimeZone::FixedOffset(-18000); // UTC-5 (18000 seconds offset)
let normalized = PoSQLTimeZone::normalize_to_utc(timestamp, &tz);
assert_eq!(normalized, 1231006505 + 18000); // Adjusted by 5 hours
}
}
177 changes: 177 additions & 0 deletions crates/proof-of-sql-parser/src/posql_time/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ pub enum PoSQLTimeUnit {
Nanosecond,
}

impl PoSQLTimeUnit {
/// Convert the numerical unit of one timeunit to another for comparison purposes.
pub fn normalize_timeunit(
timestamp: i64,
from_unit: PoSQLTimeUnit,
to_unit: PoSQLTimeUnit,
) -> i64 {
match (from_unit, to_unit) {
// Conversions from Seconds
(PoSQLTimeUnit::Second, PoSQLTimeUnit::Millisecond) => timestamp * 1000,
(PoSQLTimeUnit::Second, PoSQLTimeUnit::Microsecond) => timestamp * 1_000_000,
(PoSQLTimeUnit::Second, PoSQLTimeUnit::Nanosecond) => timestamp * 1_000_000_000,

// Conversions from Milliseconds
(PoSQLTimeUnit::Millisecond, PoSQLTimeUnit::Second) => timestamp / 1000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm if we accept rounding then 10AM and 10AM plus 1ms will be identical if we normalize to s.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok no rounding, got it

(PoSQLTimeUnit::Millisecond, PoSQLTimeUnit::Microsecond) => timestamp * 1000,
(PoSQLTimeUnit::Millisecond, PoSQLTimeUnit::Nanosecond) => timestamp * 1_000_000,

// Conversions from Microseconds
(PoSQLTimeUnit::Microsecond, PoSQLTimeUnit::Second) => timestamp / 1_000_000,
(PoSQLTimeUnit::Microsecond, PoSQLTimeUnit::Millisecond) => timestamp / 1000,
(PoSQLTimeUnit::Microsecond, PoSQLTimeUnit::Nanosecond) => timestamp * 1000,

// Conversions from Nanoseconds
(PoSQLTimeUnit::Nanosecond, PoSQLTimeUnit::Second) => timestamp / 1_000_000_000,
(PoSQLTimeUnit::Nanosecond, PoSQLTimeUnit::Millisecond) => timestamp / 1_000_000,
(PoSQLTimeUnit::Nanosecond, PoSQLTimeUnit::Microsecond) => timestamp / 1000,

// If units are the same, no adjustment is needed
_ => timestamp,
}
}
}

impl TryFrom<&str> for PoSQLTimeUnit {
type Error = PoSQLTimestampError;
fn try_from(value: &str) -> Result<Self, PoSQLTimestampError> {
Expand Down Expand Up @@ -107,4 +141,147 @@ mod time_unit_tests {
expected.timestamp_nanos_opt().unwrap()
);
}
#[test]
fn test_normalize_timeunit_seconds_to_milliseconds() {
let timestamp = 1231006505; // seconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Second,
PoSQLTimeUnit::Millisecond,
);
assert_eq!(result, 1231006505000); // converted to milliseconds
}

#[test]
fn test_normalize_timeunit_seconds_to_microseconds() {
let timestamp = 1231006505; // seconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Second,
PoSQLTimeUnit::Microsecond,
);
assert_eq!(result, 1231006505000000); // converted to microseconds
}

#[test]
fn test_normalize_timeunit_seconds_to_nanoseconds() {
let timestamp = 1231006505; // seconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Second,
PoSQLTimeUnit::Nanosecond,
);
assert_eq!(result, 1231006505000000000); // converted to nanoseconds
}

#[test]
fn test_normalize_timeunit_milliseconds_to_seconds() {
let timestamp = 1231006505000; // milliseconds
Copy link
Contributor

@iajoiner iajoiner Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about 1231006505001? normalize_timeunit(1231006505000, MS, S) == normalize_timeunit(1231006505001, MS, S)

let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Millisecond,
PoSQLTimeUnit::Second,
);
assert_eq!(result, 1231006505); // converted to seconds
}

#[test]
fn test_normalize_timeunit_milliseconds_to_microseconds() {
let timestamp = 1231006505; // milliseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Millisecond,
PoSQLTimeUnit::Microsecond,
);
assert_eq!(result, 1231006505000); // converted to microseconds
}

#[test]
fn test_normalize_timeunit_milliseconds_to_nanoseconds() {
let timestamp = 1231006505; // milliseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Millisecond,
PoSQLTimeUnit::Nanosecond,
);
assert_eq!(result, 1231006505000000); // converted to nanoseconds
}

#[test]
fn test_normalize_timeunit_microseconds_to_seconds() {
let timestamp = 1231006505000000; // microseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Microsecond,
PoSQLTimeUnit::Second,
);
assert_eq!(result, 1231006505); // converted to seconds
}

#[test]
fn test_normalize_timeunit_microseconds_to_milliseconds() {
let timestamp = 1231006505000; // microseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Microsecond,
PoSQLTimeUnit::Millisecond,
);
assert_eq!(result, 1231006505); // converted to milliseconds
}

#[test]
fn test_normalize_timeunit_microseconds_to_nanoseconds() {
let timestamp = 1231006505; // microseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Microsecond,
PoSQLTimeUnit::Nanosecond,
);
assert_eq!(result, 1231006505000); // converted to nanoseconds
}

#[test]
fn test_normalize_timeunit_nanoseconds_to_seconds() {
let timestamp = 1231006505000000000; // nanoseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Nanosecond,
PoSQLTimeUnit::Second,
);
assert_eq!(result, 1231006505); // converted to seconds
}

#[test]
fn test_normalize_timeunit_nanoseconds_to_milliseconds() {
let timestamp = 1231006505000000; // nanoseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Nanosecond,
PoSQLTimeUnit::Millisecond,
);
assert_eq!(result, 1231006505); // converted to milliseconds
}

#[test]
fn test_normalize_timeunit_nanoseconds_to_microseconds() {
let timestamp = 1231006505000; // nanoseconds
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Nanosecond,
PoSQLTimeUnit::Microsecond,
);
assert_eq!(result, 1231006505); // converted to microseconds
}

#[test]
fn test_normalize_timeunit_same_units() {
// If from_unit and to_unit are the same, it should return the timestamp as is
let timestamp = 1231006505;
let result = PoSQLTimeUnit::normalize_timeunit(
timestamp,
PoSQLTimeUnit::Second,
PoSQLTimeUnit::Second,
);
assert_eq!(result, timestamp); // No conversion needed
}
}
Loading
Loading