Skip to content

Commit 122fb24

Browse files
committed
Fixed handling of timezone aware datetimes
1 parent abba8de commit 122fb24

File tree

6 files changed

+46
-24
lines changed

6 files changed

+46
-24
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ members = ["cli-excel-rs", "crates/*", "py-excel-rs"]
33
resolver = "2"
44

55
[workspace.package]
6-
version = "0.5.4"
6+
version = "0.5.5"
77
authors = ["Carl Voller"]
88
edition = "2021"
99
homepage = "https://github.com/carlvoller/excel-rs"
1010
license = "MIT"
1111
repository = "https://github.com/carlvoller/excel-rs"
1212

1313
[workspace.dependencies]
14-
excel-rs-xlsx = { version = "0.5.4", path = "crates/excel-rs-xlsx", default-features = false }
15-
excel-rs-csv = { version = "0.5.4", path = "crates/excel-rs-csv", default-features = false }
16-
excel-rs-postgres = { version = "0.5.4", path = "crates/excel-rs-postgres", default-features = false }
14+
excel-rs-xlsx = { version = "0.5.5", path = "crates/excel-rs-xlsx", default-features = false }
15+
excel-rs-csv = { version = "0.5.5", path = "crates/excel-rs-csv", default-features = false }
16+
excel-rs-postgres = { version = "0.5.5", path = "crates/excel-rs-postgres", default-features = false }
1717

1818
[profile.release]
1919
opt-level = 3

benchmarks/test-py-excel-rs.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import py_excel_rs
2-
import datetime
2+
from datetime import datetime, timezone, timedelta
33
import pandas as pd
44

5-
# f = open('organizations-1000000.csv', 'rb')
6-
# xlsx = py_excel_rs.csv_to_xlsx(f.read())
5+
naive_date = datetime.now()
6+
tz_date = datetime.now(timezone(timedelta(hours=8)))
77

8+
data = [[naive_date, tz_date, "hello", 10, 10.888]]
9+
df = pd.DataFrame(data, columns=["Date", "Timezone Date", "hi", "number1", "float2"])
810

9-
data = [[datetime.datetime.now(), "hello", 10, 10.888]]
10-
df = pd.DataFrame(data, columns=["Date", "hi", "number1", "float2"])
11-
12-
xlsx = py_excel_rs.df_to_xlsx(df, should_infer_types=True)
11+
xlsx = py_excel_rs.df_to_xlsx(df, should_infer_types=False)
1312

1413
with open('report.xlsx', 'wb') as f:
1514
f.write(xlsx)

py-excel-rs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "maturin"
44

55
[project]
66
name = "py-excel-rs"
7-
version = "0.5.4"
7+
version = "0.5.5"
88
description = "Some performant utility functions to convert common data structures to XLSX"
99
dependencies = ["pandas", "numpy"]
1010
requires-python = ">=3.7"

py-excel-rs/src/lib.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ mod utils;
33

44
use std::io::Cursor;
55

6-
use chrono::NaiveDateTime;
6+
use chrono::{DateTime, NaiveDateTime, FixedOffset};
77
use excel_rs_csv::{bytes_to_csv, get_headers, get_next_record};
88
use excel_rs_xlsx::WorkBook;
99
use numpy::PyReadonlyArray2;
1010
use postgres::PyPostgresClient;
11-
use utils::chrono_to_xlsx_date;
12-
use pyo3::{prelude::*, types::{PyBytes, PyList}};
11+
use pyo3::{
12+
prelude::*,
13+
types::{PyBytes, PyList},
14+
};
15+
use utils::{chrono_naive_to_xlsx_date, chrono_tz_to_xlsx_date};
1316

1417
#[pymodule]
1518
fn _excel_rs<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
@@ -68,7 +71,9 @@ fn _excel_rs<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
6871
}
6972
} else {
7073
if let Ok(inner_date) = x.extract::<NaiveDateTime>(py) {
71-
format!("{}", inner_date.format("%Y-%m-%d %r"))
74+
format!("{}", inner_date.format("%Y-%m-%d %H:%M:%S"))
75+
} else if let Ok(inner_date) = x.extract::<DateTime<FixedOffset>>(py) {
76+
format!("{}", inner_date.to_rfc3339())
7277
} else {
7378
String::from("")
7479
}
@@ -117,9 +122,11 @@ fn _excel_rs<'py>(m: &Bound<'py, PyModule>) -> PyResult<()> {
117122
}
118123
} else {
119124
if let Ok(inner_date) = x.extract::<NaiveDateTime>(py) {
120-
format!("{}", chrono_to_xlsx_date(inner_date))
125+
format!("{}", chrono_naive_to_xlsx_date(inner_date))
126+
} else if let Ok(inner_date) = x.extract::<DateTime<FixedOffset>>(py) {
127+
format!("{}", chrono_tz_to_xlsx_date(inner_date))
121128
} else {
122-
String::from("")
129+
String::new()
123130
}
124131
}
125132
}

py-excel-rs/src/utils.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
1+
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
22

33
const NANOSECONDS_IN_A_DAY: f64 = 1_000_000_000.0 * 60.0 * 60.0 * 24.0;
44

@@ -8,7 +8,7 @@ const NANOSECONDS_IN_A_DAY: f64 = 1_000_000_000.0 * 60.0 * 60.0 * 24.0;
88
// const EXCEL_BUGGY_DATE_START: &str = "1900-03-01 00:00:00";
99
// const DATE_TIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
1010

11-
pub fn chrono_to_xlsx_date(date: NaiveDateTime) -> f64 {
11+
pub fn chrono_naive_to_xlsx_date(date: NaiveDateTime) -> f64 {
1212
let start_date_only = NaiveDate::from_ymd_opt(1900, 1, 1).expect("Excel Epoch");
1313
let start_date = NaiveDateTime::new(
1414
start_date_only,
@@ -19,3 +19,19 @@ pub fn chrono_to_xlsx_date(date: NaiveDateTime) -> f64 {
1919
let true_delta: f64 = (delta as f64 + NANOSECONDS_IN_A_DAY + NANOSECONDS_IN_A_DAY) / NANOSECONDS_IN_A_DAY;
2020
return true_delta;
2121
}
22+
23+
pub fn chrono_tz_to_xlsx_date(date: DateTime<FixedOffset>) -> f64 {
24+
let offset = date.offset().to_owned();
25+
let start_date_only = NaiveDate::from_ymd_opt(1900, 1, 1).expect("Excel Epoch");
26+
let start_date = NaiveDateTime::new(
27+
start_date_only,
28+
NaiveTime::from_hms_opt(0, 0, 0).expect("Time Epoch"),
29+
);
30+
31+
let start_date_utc = DateTime::<FixedOffset>::from_naive_utc_and_offset(start_date, offset);
32+
33+
let delta = (date - start_date_utc).num_nanoseconds().unwrap();
34+
35+
let true_delta: f64 = (delta as f64 + NANOSECONDS_IN_A_DAY + NANOSECONDS_IN_A_DAY) / NANOSECONDS_IN_A_DAY;
36+
return true_delta;
37+
}

0 commit comments

Comments
 (0)