Skip to content

Commit 9f86e55

Browse files
authored
refactor: replace chrono::Tz with jiff::tz::Timezone (#719)
1 parent afee760 commit 9f86e55

File tree

9 files changed

+58
-41
lines changed

9 files changed

+58
-41
lines changed

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ arrow-ipc = { workspace = true }
2626
arrow-schema = { workspace = true }
2727
bytes = "1.10.1"
2828
base64 = "0.22.1"
29-
chrono-tz = { workspace = true }
3029
cookie = "0.18.1"
30+
jiff = { version = "0.2.10", features = ["tzdb-bundle-always"] }
3131
log = { version = "0.4", features = ["kv"] }
3232
once_cell = "1.21"
3333
parking_lot = "0.12.3"

core/src/settings.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,24 @@
1414

1515
use crate::error::Result;
1616
use crate::Error;
17-
use chrono_tz::Tz;
17+
use jiff::tz::TimeZone;
1818
use serde::Deserialize;
1919
use std::collections::BTreeMap;
2020
use std::str::FromStr;
2121

22-
#[derive(Debug, Clone, Default, Copy)]
22+
#[derive(Debug, Clone)]
2323
pub struct ResultFormatSettings {
2424
pub geometry_output_format: GeometryDataType,
25-
pub timezone: Tz,
25+
pub timezone: TimeZone,
26+
}
27+
28+
impl Default for ResultFormatSettings {
29+
fn default() -> Self {
30+
Self {
31+
geometry_output_format: GeometryDataType::default(),
32+
timezone: TimeZone::UTC,
33+
}
34+
}
2635
}
2736

2837
impl ResultFormatSettings {
@@ -31,8 +40,8 @@ impl ResultFormatSettings {
3140
None => Ok(Default::default()),
3241
Some(settings) => {
3342
let timezone = match settings.get("timezone") {
34-
None => Tz::default(),
35-
Some(t) => Tz::from_str(t).map_err(|e| Error::Decode(e.to_string()))?,
43+
None => TimeZone::UTC,
44+
Some(t) => TimeZone::get(t).map_err(|e| Error::Decode(e.to_string()))?,
3645
};
3746

3847
let geometry_output_format = match settings.get("geometry_output_format") {

driver/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ tokio-stream = { workspace = true }
3838
tonic = { workspace = true, optional = true }
3939

4040
async-trait = "0.1"
41+
jiff = { version = "0.2.10", features = ["tzdb-bundle-always"] }
4142
csv = "1.3"
4243
databend-common-ast = "0.2.3"
4344
derive-visitor = { version = "0.4.0", features = ["std-types-drive"] }

driver/src/rest_api.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
use async_trait::async_trait;
16-
use chrono_tz::Tz;
16+
use jiff::tz::TimeZone;
1717
use log::info;
1818
use std::collections::{BTreeMap, VecDeque};
1919
use std::marker::PhantomData;
@@ -333,7 +333,7 @@ impl<T: FromRowStats + std::marker::Unpin> Stream for RestAPIRows<T> {
333333
// Therefore, we could guarantee the `/final` called before the last row.
334334
if self.data.len() > 1 {
335335
if let Some(row) = self.data.pop_front() {
336-
let row = T::try_from_raw_row(row, self.schema.clone(), self.settings.timezone)?;
336+
let row = T::try_from_raw_row(row, self.schema.clone(), &self.settings.timezone)?;
337337
return Poll::Ready(Some(Ok(row)));
338338
}
339339
} else if self.rows.len() > 1 {
@@ -357,7 +357,7 @@ impl<T: FromRowStats + std::marker::Unpin> Stream for RestAPIRows<T> {
357357
self.data.append(&mut new_data);
358358
} else {
359359
for batch in page.batches.into_iter() {
360-
let rows = Rows::try_from((batch, self.settings))?;
360+
let rows = Rows::try_from((batch, self.settings.clone()))?;
361361
self.rows.extend(rows);
362362
}
363363
}
@@ -370,7 +370,7 @@ impl<T: FromRowStats + std::marker::Unpin> Stream for RestAPIRows<T> {
370370
Poll::Ready(Some(Ok(row)))
371371
} else if let Some(row) = self.data.pop_front() {
372372
let row =
373-
T::try_from_raw_row(row, self.schema.clone(), self.settings.timezone)?;
373+
T::try_from_raw_row(row, self.schema.clone(), &self.settings.timezone)?;
374374
Poll::Ready(Some(Ok(row)))
375375
} else {
376376
Poll::Ready(None)
@@ -383,7 +383,8 @@ impl<T: FromRowStats + std::marker::Unpin> Stream for RestAPIRows<T> {
383383

384384
trait FromRowStats: Send + Sync + Clone {
385385
fn from_stats(stats: ServerStats) -> Self;
386-
fn try_from_raw_row(row: Vec<Option<String>>, schema: SchemaRef, tz: Tz) -> Result<Self>;
386+
fn try_from_raw_row(row: Vec<Option<String>>, schema: SchemaRef, tz: &TimeZone)
387+
-> Result<Self>;
387388
fn from_row(row: Row) -> Self;
388389
}
389390

@@ -392,7 +393,11 @@ impl FromRowStats for RowWithStats {
392393
RowWithStats::Stats(stats)
393394
}
394395

395-
fn try_from_raw_row(row: Vec<Option<String>>, schema: SchemaRef, tz: Tz) -> Result<Self> {
396+
fn try_from_raw_row(
397+
row: Vec<Option<String>>,
398+
schema: SchemaRef,
399+
tz: &TimeZone,
400+
) -> Result<Self> {
396401
Ok(RowWithStats::Row(Row::try_from((schema, row, tz))?))
397402
}
398403
fn from_row(row: Row) -> Self {
@@ -405,7 +410,11 @@ impl FromRowStats for RawRowWithStats {
405410
RawRowWithStats::Stats(stats)
406411
}
407412

408-
fn try_from_raw_row(row: Vec<Option<String>>, schema: SchemaRef, tz: Tz) -> Result<Self> {
413+
fn try_from_raw_row(
414+
row: Vec<Option<String>>,
415+
schema: SchemaRef,
416+
tz: &TimeZone,
417+
) -> Result<Self> {
409418
let rows = Row::try_from((schema, row.clone(), tz))?;
410419
Ok(RawRowWithStats::Row(RawRow::new(rows, row)))
411420
}

sql/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ itertools = "0.14"
3333
lexical-core = "1.0.5"
3434
memchr = "2.7"
3535
roaring = { version = "0.10.12", features = ["serde"] }
36-
jiff = "0.2.10"
36+
jiff = { version = "0.2.10", features = ["tzdb-bundle-always"] }
3737
serde = { version = "1.0", default-features = false, features = ["derive"] }
3838
serde_json = { version = "1.0", default-features = false, features = ["std"] }
3939
url = { version = "2.5", default-features = false }

sql/src/raw_rows.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use crate::rows::Row;
1818
use crate::rows::ServerStats;
1919
use crate::value::FormatOptions;
2020
use crate::value::Value;
21-
use chrono_tz::Tz;
2221
use databend_client::schema::SchemaRef;
22+
use jiff::tz::TimeZone;
2323
use lexical_core::WriteFloatOptionsBuilder;
2424
use std::pin::Pin;
2525
use std::task::Context;
@@ -69,10 +69,10 @@ impl RawRow {
6969
}
7070
}
7171

72-
impl TryFrom<(SchemaRef, Vec<Option<String>>, Tz)> for RawRow {
72+
impl TryFrom<(SchemaRef, Vec<Option<String>>, &TimeZone)> for RawRow {
7373
type Error = Error;
7474

75-
fn try_from((schema, data, tz): (SchemaRef, Vec<Option<String>>, Tz)) -> Result<Self> {
75+
fn try_from((schema, data, tz): (SchemaRef, Vec<Option<String>>, &TimeZone)) -> Result<Self> {
7676
let mut values: Vec<Value> = Vec::with_capacity(data.len());
7777
for (field, val) in schema.fields().iter().zip(data.clone().into_iter()) {
7878
values.push(Value::try_from((&field.data_type, val, tz))?);

sql/src/rows.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ use tokio_stream::{Stream, StreamExt};
2222
use crate::error::{Error, Result};
2323
use crate::value::Value;
2424
use arrow::record_batch::RecordBatch;
25-
use chrono_tz::Tz;
2625
use databend_client::schema::SchemaRef;
2726
use databend_client::ResultFormatSettings;
27+
use jiff::tz::TimeZone;
2828

2929
#[derive(Clone, Debug)]
3030
pub enum RowWithStats {
@@ -135,10 +135,10 @@ impl Row {
135135
}
136136
}
137137

138-
impl TryFrom<(SchemaRef, Vec<Option<String>>, Tz)> for Row {
138+
impl TryFrom<(SchemaRef, Vec<Option<String>>, &TimeZone)> for Row {
139139
type Error = Error;
140140

141-
fn try_from((schema, data, tz): (SchemaRef, Vec<Option<String>>, Tz)) -> Result<Self> {
141+
fn try_from((schema, data, tz): (SchemaRef, Vec<Option<String>>, &TimeZone)) -> Result<Self> {
142142
let mut values: Vec<Value> = Vec::with_capacity(data.len());
143143
for (field, val) in schema.fields().iter().zip(data.into_iter()) {
144144
values.push(Value::try_from((&field.data_type, val, tz))?);
@@ -194,7 +194,7 @@ impl TryFrom<(RecordBatch, ResultFormatSettings)> for Rows {
194194
for j in 0..batch_schema.fields().len() {
195195
let v = batch.column(j);
196196
let field = batch_schema.field(j);
197-
let value = Value::try_from((field, v, i, settings))?;
197+
let value = Value::try_from((field, v, i, &settings))?;
198198
values.push(value);
199199
}
200200
rows.push(Row::new(schema.clone(), values));

sql/src/value/arrow_decoder.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl
6767
&ArrowField,
6868
&Arc<dyn ArrowArray>,
6969
usize,
70-
ResultFormatSettings,
70+
&ResultFormatSettings,
7171
)> for Value
7272
{
7373
type Error = Error;
@@ -76,7 +76,7 @@ impl
7676
&ArrowField,
7777
&Arc<dyn ArrowArray>,
7878
usize,
79-
ResultFormatSettings,
79+
&ResultFormatSettings,
8080
),
8181
) -> std::result::Result<Self, Self::Error> {
8282
if let Some(extend_type) = field.metadata().get(EXTENSION_KEY) {
@@ -349,12 +349,7 @@ impl
349349
let timestamp = Timestamp::from_microsecond(ts).map_err(|e| {
350350
Error::Parsing(format!("Invalid timestamp_micros {ts}: {e}"))
351351
})?;
352-
let tz_name = settings.timezone.name();
353-
let dt = timestamp.in_tz(tz_name).map_err(|e| {
354-
Error::Parsing(format!(
355-
"Invalid timezone {tz_name} for timestamp {ts}: {e}"
356-
))
357-
})?;
352+
let dt = timestamp.to_zoned(settings.timezone.clone());
358353
Ok(Value::Timestamp(dt))
359354
}
360355
Some(tz) => Err(ConvertError::new("timestamp", format!("{array:?}"))

sql/src/value/string_decoder.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,21 @@ use crate::cursor_ext::{
2020
};
2121
use crate::error::{ConvertError, Result};
2222
use chrono::{Datelike, NaiveDate};
23-
use chrono_tz::Tz;
2423
use databend_client::schema::{DataType, DecimalDataType, DecimalSize, NumberDataType};
2524
use ethnum::i256;
2625
use hex;
27-
use jiff::{civil::DateTime as JiffDateTime, Zoned};
26+
use jiff::{civil::DateTime as JiffDateTime, tz::TimeZone, Zoned};
2827
use std::io::{BufRead, Cursor};
2928
use std::str::FromStr;
3029

3130
const NULL_VALUE: &str = "NULL";
3231
const TRUE_VALUE: &str = "1";
3332
const FALSE_VALUE: &str = "0";
3433

35-
impl TryFrom<(&DataType, Option<String>, Tz)> for Value {
34+
impl TryFrom<(&DataType, Option<String>, &TimeZone)> for Value {
3635
type Error = Error;
3736

38-
fn try_from((t, v, tz): (&DataType, Option<String>, Tz)) -> Result<Self> {
37+
fn try_from((t, v, tz): (&DataType, Option<String>, &TimeZone)) -> Result<Self> {
3938
match v {
4039
Some(v) => Self::try_from((t, v, tz)),
4140
None => match t {
@@ -49,10 +48,10 @@ impl TryFrom<(&DataType, Option<String>, Tz)> for Value {
4948
}
5049
}
5150

52-
impl TryFrom<(&DataType, String, Tz)> for Value {
51+
impl TryFrom<(&DataType, String, &TimeZone)> for Value {
5352
type Error = Error;
5453

55-
fn try_from((t, v, tz): (&DataType, String, Tz)) -> Result<Self> {
54+
fn try_from((t, v, tz): (&DataType, String, &TimeZone)) -> Result<Self> {
5655
match t {
5756
DataType::Null => Ok(Self::Null),
5857
DataType::EmptyArray => Ok(Self::EmptyArray),
@@ -114,7 +113,9 @@ impl TryFrom<(&DataType, String, Tz)> for Value {
114113
DataType::Interval => Ok(Self::Interval(v)),
115114
DataType::Array(_) | DataType::Map(_) | DataType::Tuple(_) | DataType::Vector(_) => {
116115
let mut reader = Cursor::new(v.as_str());
117-
let decoder = ValueDecoder { timezone: tz };
116+
let decoder = ValueDecoder {
117+
timezone: tz.clone(),
118+
};
118119
decoder.read_field(t, &mut reader)
119120
}
120121
DataType::Nullable(inner) => match inner.as_ref() {
@@ -134,7 +135,7 @@ impl TryFrom<(&DataType, String, Tz)> for Value {
134135
}
135136

136137
struct ValueDecoder {
137-
pub timezone: Tz,
138+
pub timezone: TimeZone,
138139
}
139140

140141
impl ValueDecoder {
@@ -299,7 +300,7 @@ impl ValueDecoder {
299300
let mut buf = Vec::new();
300301
reader.read_quoted_text(&mut buf, b'\'')?;
301302
let v = unsafe { std::str::from_utf8_unchecked(&buf) };
302-
parse_timestamp(v, self.timezone)
303+
parse_timestamp(v, &self.timezone)
303304
}
304305

305306
fn read_timestamp_tz<R: AsRef<[u8]>>(&self, reader: &mut Cursor<R>) -> Result<Value> {
@@ -453,10 +454,12 @@ impl ValueDecoder {
453454
}
454455
}
455456

456-
fn parse_timestamp(ts_string: &str, tz: Tz) -> Result<Value> {
457+
fn parse_timestamp(ts_string: &str, tz: &TimeZone) -> Result<Value> {
457458
let local = JiffDateTime::strptime(TIMESTAMP_FORMAT, ts_string)?;
458-
let dt_with_tz = local.in_tz(tz.name()).map_err(|e| {
459-
Error::Parsing(format!("time {ts_string} not exists in timezone {tz}: {e}"))
459+
let dt_with_tz = local.to_zoned(tz.clone()).map_err(|e| {
460+
Error::Parsing(format!(
461+
"time {ts_string} not exists in timezone {tz:?}: {e}"
462+
))
460463
})?;
461464
Ok(Value::Timestamp(dt_with_tz))
462465
}

0 commit comments

Comments
 (0)