Skip to content

Commit 807ac5c

Browse files
committed
add Variant::{as_timestamp_micros/as_timestamp_nanos} and simplify the impl of for timestamp types
1 parent be5b70f commit 807ac5c

File tree

2 files changed

+93
-39
lines changed

2 files changed

+93
-39
lines changed

parquet-variant-compute/src/type_conversion.rs

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717

1818
//! Module for transforming a typed arrow `Array` to `VariantArray`.
1919
20-
use arrow::datatypes::{
21-
self, ArrowPrimitiveType, ArrowTimestampType, Date32Type, TimestampMicrosecondType,
22-
TimestampNanosecondType,
23-
};
20+
use arrow::datatypes::{self, ArrowPrimitiveType, ArrowTimestampType, Date32Type};
2421
use parquet_variant::Variant;
2522

2623
/// Options for controlling the behavior of `cast_to_variant_with_options`.
@@ -41,6 +38,13 @@ pub(crate) trait PrimitiveFromVariant: ArrowPrimitiveType {
4138
fn from_variant(variant: &Variant<'_, '_>) -> Option<Self::Native>;
4239
}
4340

41+
/// Extension trait for Arrow timestamp types that can extract their native value from a Variant
42+
/// We can't use [`PrimitiveFromVariant`] directly because we might need to use methods that
43+
/// are only available on [`ArrowTimestampType`] (such as with_timezone_opt)
44+
pub(crate) trait TimestampFromVariant: ArrowTimestampType {
45+
fn from_variant(variant: &Variant<'_, '_>) -> Option<Self::Native>;
46+
}
47+
4448
/// Macro to generate PrimitiveFromVariant implementations for Arrow primitive types
4549
macro_rules! impl_primitive_from_variant {
4650
($arrow_type:ty, $variant_method:ident $(, $cast_fn:expr)?) => {
@@ -52,6 +56,18 @@ macro_rules! impl_primitive_from_variant {
5256
}
5357
}
5458
};
59+
($arrow_type:ty $(, $variant_method:ident => $cast_fn:expr )+ ) => {
60+
impl TimestampFromVariant for $arrow_type {
61+
fn from_variant(variant: &Variant<'_, '_>) -> Option<Self::Native> {
62+
$(
63+
if let Some(value) = variant.$variant_method() {
64+
return Some($cast_fn(value));
65+
}
66+
)+
67+
None
68+
}
69+
}
70+
};
5571
}
5672

5773
impl_primitive_from_variant!(datatypes::Int32Type, as_int32);
@@ -70,41 +86,13 @@ impl_primitive_from_variant!(
7086
as_naive_date,
7187
Date32Type::from_naive_date
7288
);
73-
74-
pub(crate) trait TimestampFromVariant: ArrowTimestampType {
75-
fn from_variant(variant: &Variant<'_, '_>) -> Option<Self::Native>;
76-
}
77-
78-
macro_rules! impl_timestamp_from_variant {
79-
($timestamp_type:ty,
80-
$( $variant_pattern:pat => $conversion:expr ),+ $(,)?
81-
) => {
82-
impl TimestampFromVariant for $timestamp_type {
83-
fn from_variant(variant: &Variant<'_, '_>) -> Option<Self::Native> {
84-
match variant {
85-
$(
86-
$variant_pattern => $conversion,
87-
)+
88-
_ => None,
89-
}
90-
}
91-
}
92-
};
93-
}
94-
95-
impl_timestamp_from_variant!(
96-
TimestampMicrosecondType,
97-
Variant::TimestampMicros(t) => Some(t.timestamp_micros()),
98-
Variant::TimestampNtzMicros(t) => Some(t.and_utc().timestamp_micros()),
99-
);
100-
101-
impl_timestamp_from_variant!(
102-
TimestampNanosecondType,
103-
Variant::TimestampMicros(t) => Some(t.timestamp_micros()).map(|t| t * 1000),
104-
Variant::TimestampNtzMicros(t) => Some(t.and_utc().timestamp_micros()).map(|t| t * 1000),
105-
Variant::TimestampNanos(t) => t.timestamp_nanos_opt(),
106-
Variant::TimestampNtzNanos(t) => t.and_utc().timestamp_nanos_opt(),
107-
);
89+
impl_primitive_from_variant!(
90+
datatypes::TimestampMicrosecondType,
91+
as_timestamp_micros => |t| t);
92+
impl_primitive_from_variant!(
93+
datatypes::TimestampNanosecondType,
94+
as_timestamp_micros => |t| 1000 * t,
95+
as_timestamp_nanos => |t| t);
10896

10997
/// Convert the value at a specific index in the given array into a `Variant`.
11098
macro_rules! non_generic_conversion_single_value {

parquet-variant/src/variant.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,72 @@ impl<'m, 'v> Variant<'m, 'v> {
561561
}
562562
}
563563

564+
/// Converts this variant to a `i64` representing microseconds since the Unix epoch if possible.
565+
/// This is useful when convert the variant to arrow types.
566+
///
567+
/// Returns Some(i64) for [`Variant::TimestampMicros`] and [`Variant::TimestampNtzMicros`],
568+
/// None for the other variant types.
569+
///
570+
/// ```
571+
/// use parquet_variant::Variant;
572+
/// use chrono::NaiveDate;
573+
///
574+
/// // you can extract an i64 from Variant::TimestampMicros
575+
/// let datetime = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap().and_utc();
576+
/// let v1 = Variant::from(datetime);
577+
/// assert_eq!(v1.as_timestamp_micros(), Some(1759494896789000));
578+
///
579+
/// // or Variant::TimestampNtzMicros
580+
/// let datetime_ntz = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap();
581+
/// let v2 = Variant::from(datetime_ntz);
582+
/// assert_eq!(v1.as_timestamp_micros(), Some(1759494896789000));
583+
///
584+
/// // but not from other variants
585+
/// let datetime_nanos = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_nano_opt(12, 34, 56, 789123456).unwrap().and_utc();
586+
/// let v3 = Variant::from(datetime_nanos);
587+
/// assert_eq!(v3.as_timestamp_micros(), None);
588+
/// ```
589+
pub fn as_timestamp_micros(&self) -> Option<i64> {
590+
match *self {
591+
Variant::TimestampMicros(d) => Some(d.timestamp_micros()),
592+
Variant::TimestampNtzMicros(d) => Some(d.and_utc().timestamp_micros()),
593+
_ => None,
594+
}
595+
}
596+
597+
/// Converts this variant to a `i64` representing nanoseconds since the Unix epoch if possible.
598+
/// This is useful when convert the variant to arrow types.
599+
///
600+
/// Returns Some(i64) for [`Variant::TimestampNanos`] and [`Variant::TimestampNtzNanos`],
601+
/// None for the other variant types.
602+
///
603+
/// ```
604+
/// use parquet_variant::Variant;
605+
/// use chrono::NaiveDate;
606+
///
607+
/// // you can extract an i64 from Variant::TimestampNanos
608+
/// let datetime = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_nano_opt(12, 34, 56, 789123456).unwrap().and_utc();
609+
/// let v1 = Variant::from(datetime);
610+
/// assert_eq!(v1.as_timestamp_nanos(), Some(1759494896789123456));
611+
///
612+
/// // or Variant::TimestampNtzNanos
613+
/// let datetime_ntz = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_nano_opt(12, 34, 56, 789123456).unwrap();
614+
/// let v2 = Variant::from(datetime_ntz);
615+
/// assert_eq!(v1.as_timestamp_nanos(), Some(1759494896789123456));
616+
///
617+
/// // but not from other variants
618+
/// let datetime_micros = NaiveDate::from_ymd_opt(2025, 10, 03).unwrap().and_hms_micro_opt(12, 34, 56, 789).unwrap().and_utc();
619+
/// let v3 = Variant::from(datetime_micros);
620+
/// assert_eq!(v3.as_timestamp_nanos(), None);
621+
/// ```
622+
pub fn as_timestamp_nanos(&self) -> Option<i64> {
623+
match *self {
624+
Variant::TimestampNanos(d) => d.timestamp_nanos_opt(),
625+
Variant::TimestampNtzNanos(d) => d.and_utc().timestamp_nanos_opt(),
626+
_ => None,
627+
}
628+
}
629+
564630
/// Converts this variant to a `NaiveDateTime` if possible.
565631
///
566632
/// Returns `Some(NaiveDateTime)` for timestamp variants,

0 commit comments

Comments
 (0)