Skip to content

Commit

Permalink
feat: Implements conversions of Date, Time and DateTime structs into …
Browse files Browse the repository at this point in the history
…chrono NaiveDate, NaiveTime and NaiveDateTime structs.
  • Loading branch information
AlvaroSierra committed Jan 26, 2025
1 parent f6af9fb commit 318139d
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ datafusion-expr = { version = "31", optional = true }
async-trait = { version = "0.1", optional = true }
codepage = { version = "0.1.2", optional = true }
encoding_rs = { version = "0.8.35", optional = true }
chrono = { version = "0.4.39", optional = true }

[dev-dependencies]
serde_derive = "1.0.102"
Expand All @@ -30,6 +31,7 @@ tokio = "1.26"
datafusion = ["dep:datafusion", "dep:datafusion-expr", "dep:async-trait"]
yore = ["dep:yore"]
encoding_rs = ["dep:encoding_rs", "dep:codepage"]
chrono = ["dep:chrono"]

[[example]]
name = "datafusion"
Expand Down
91 changes: 90 additions & 1 deletion src/field/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::str::FromStr;

use crate::Encoding;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

#[cfg(feature = "chrono")]
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
use crate::error::ErrorKind;
use crate::field::FieldInfo;
use crate::memo::MemoReader;
Expand Down Expand Up @@ -407,6 +408,42 @@ impl From<time::Date> for Date {
}
}

#[cfg(feature = "chrono")]
/// An error representing
#[derive(Debug, PartialEq)]
pub struct ChronoDateConversionError;

#[cfg(feature = "chrono")]
impl<T: std::error::Error> From<T> for ChronoDateConversionError{
fn from(_value: T) -> Self {
ChronoDateConversionError
}
}

#[cfg(feature = "chrono")]
impl From<Date> for chrono::NaiveDate {
fn from(value: Date) -> Self {
// The year is enforced to be 4 digits by the contructor, smaller than i32::MAX, therefore
// the year conversion should never fail
// The crate ensures the year, month and day are valid, and because the valid set ofgit ad
// characters is smaller than
chrono::NaiveDate::from_ymd_opt(value.year.try_into().unwrap(), value.month, value.day).unwrap()
}
}

#[cfg(feature = "chrono")]
impl TryFrom<chrono::NaiveDate> for Date {
type Error = ChronoDateConversionError;
fn try_from(value: NaiveDate) -> Result<Self, Self::Error> {

if value.year() > 9999 {
return Err(ChronoDateConversionError)
}

Ok(Self::new(value.day(), value.month(), value.year().try_into()?))
}
}

/// FoxBase representation of a time
/// # note
///
Expand Down Expand Up @@ -476,6 +513,21 @@ impl Time {
}
}

#[cfg(feature = "chrono")]
impl From<Time> for chrono::NaiveTime {
fn from(value: Time) -> Self {
// The dbase crate ensure that the values are within the valid range of chrono
chrono::NaiveTime::from_hms_opt(value.hours, value.minutes, value.seconds).unwrap()
}
}

#[cfg(feature = "chrono")]
impl From<chrono::NaiveTime> for Time {
fn from(value: NaiveTime) -> Self {
Self::new(value.hour(), value.minute(), value.second())
}
}

/// FoxBase representation of a DateTime
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DateTime {
Expand Down Expand Up @@ -521,6 +573,23 @@ impl DateTime {
}
}


#[cfg(feature = "chrono")]
impl From<DateTime> for chrono::NaiveDateTime {
fn from(value: DateTime) -> Self {
chrono::NaiveDateTime::new(value.date.into(), value.time.into())
}
}

#[cfg(feature = "chrono")]
impl TryFrom<chrono::NaiveDateTime> for DateTime {
type Error = ChronoDateConversionError;
fn try_from(value: NaiveDateTime) -> Result<Self, Self::Error> {
Ok(Self::new(value.date().try_into()?, value.time().into()))
}
}


impl WritableAsDbaseField for FieldValue {
fn write_as<E: Encoding, W: Write>(
&self,
Expand Down Expand Up @@ -1010,6 +1079,26 @@ mod test {
test_we_can_read_back(&record_info, &field);
}

#[test]
#[cfg(feature="chrono")]
fn test_chrono_date_conversion() {
let date = Date::new(25, 1, 2025);
let chrono_date = date.into();

assert_eq!(chrono::NaiveDate::from_ymd_opt(2025, 1, 25), Some(chrono_date));
assert_eq!(chrono_date.try_into(), Ok(date));
}

#[test]
#[cfg(feature="chrono")]
fn test_chrono_time_conversion() {
let time = Time::new(16, 5, 10);
let chrono_time = time.into();

assert_eq!(chrono::NaiveTime::from_hms_opt(16, 5, 10), Some(chrono_time));
assert_eq!(time, chrono_time.into());
}

#[test]
fn test_write_read_integer_via_enum() {
use crate::field::FieldName;
Expand Down

0 comments on commit 318139d

Please sign in to comment.