Skip to content

Commit

Permalink
Feat/datetime pretty serde (#350)
Browse files Browse the repository at this point in the history
* feat: add pretty formatting alongside datecelldata

* feat: add iana timezone as default

* feat: add timezone

* fix: test case

* feat: add timezone info for datetime serde
  • Loading branch information
speed2exe authored Dec 16, 2024
1 parent 33cca67 commit 26bfffb
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 24 deletions.
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions collab-database/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ chrono-tz = "0.10.0"
percent-encoding = "2.3.1"
sha2 = "0.10.8"
base64 = "0.22.1"
iana-time-zone = "0.1.61"

[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
Expand Down
86 changes: 67 additions & 19 deletions collab-database/src/fields/type_option/date_type_option.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::entity::FieldType;

use crate::error::DatabaseError;
use chrono::Timelike;
use chrono::{DateTime, Timelike};
use chrono::{Datelike, Local, TimeZone};

use crate::fields::{
Expand Down Expand Up @@ -63,17 +63,52 @@ impl From<TimeTypeOption> for TypeOptionData {
}
}

#[derive(Clone, Debug, Serialize, Deserialize, Default)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DateTypeOption {
pub date_format: DateFormat,
pub time_format: TimeFormat,
pub timezone_id: String,
}

impl Default for DateTypeOption {
fn default() -> Self {
DateTypeOption::new()
}
}

impl TypeOptionCellReader for DateTypeOption {
fn json_cell(&self, cell: &Cell) -> Value {
let cell_data = DateCellData::from(cell);
json!(cell_data)
let tz: Tz = self.timezone_id.parse().unwrap_or_default();
let date_cell = DateCellData::from(cell);

let dt_start: Option<DateTime<Tz>> = date_cell
.timestamp
.and_then(|ts| DateTime::from_timestamp(ts, 0).map(|dt| dt.with_timezone(&tz)));
let dt_start_rfc3339 = dt_start.map(|dt| dt.to_rfc3339());
let dt_start_datetime = dt_start.map(|dt| dt.to_string());
let dt_start_date = dt_start.map(|dt| dt.date_naive().to_string());
let dt_start_time = dt_start.map(|dt| dt.time().to_string());

let dt_end: Option<DateTime<Tz>> = date_cell
.end_timestamp
.and_then(|ts| DateTime::from_timestamp(ts, 0).map(|dt| dt.with_timezone(&tz)));
let dt_end_rfc3339 = dt_end.map(|dt| dt.to_rfc3339());
let dt_end_datetime = dt_end.map(|dt| dt.to_string());
let dt_end_date = dt_end.map(|dt| dt.date_naive().to_string());
let dt_end_time = dt_end.map(|dt| dt.time().to_string());

json!({
"start": dt_start_rfc3339,
"end": dt_end_rfc3339,
"timezone": tz.to_string(),

"pretty_start_datetime": dt_start_datetime,
"pretty_start_date": dt_start_date,
"pretty_start_time": dt_start_time,
"pretty_end_datetime": dt_end_datetime,
"pretty_end_date": dt_end_date,
"pretty_end_time": dt_end_time
})
}

fn stringify_cell(&self, cell_data: &Cell) -> String {
Expand Down Expand Up @@ -159,10 +194,14 @@ impl TypeOptionCellWriter for DateTypeOption {

impl DateTypeOption {
pub fn new() -> Self {
let timezone_id = iana_time_zone::get_timezone().unwrap_or_else(|err| {
error!("Failed to get local timezone: {}", err);
"Etc/UTC".to_owned()
});
Self {
date_format: DateFormat::default(),
time_format: TimeFormat::default(),
timezone_id: String::new(),
timezone_id,
}
}

Expand Down Expand Up @@ -627,11 +666,15 @@ mod tests {
assert_eq!(
json_value,
json!({
"timestamp": 1672531200,
"end_timestamp": null,
"include_time": false,
"is_range": false,
"reminder_id": ""
"end": serde_json::Value::Null,
"timezone": "Etc/UTC",
"pretty_end_date": serde_json::Value::Null,
"pretty_end_datetime": serde_json::Value::Null,
"pretty_end_time": serde_json::Value::Null,
"pretty_start_date": "2023-01-01",
"pretty_start_datetime": "2023-01-01 00:00:00 UTC",
"pretty_start_time": "00:00:00",
"start": "2023-01-01T00:00:00+00:00",
})
);
}
Expand Down Expand Up @@ -700,22 +743,27 @@ mod tests {

#[test]
fn date_cell_to_serde() {
let date_type_option = DateTypeOption::default_utc();
let mut date_type_option = DateTypeOption::new();
date_type_option.timezone_id = "Asia/Singapore".to_string();
let cell_writer: Box<dyn TypeOptionCellReader> = Box::new(date_type_option);
{
let mut cell: Cell = new_cell_builder(FieldType::DateTime);
cell.insert(CELL_DATA.into(), "1672531200".into());
cell.insert(CELL_DATA.into(), "1675343111".into());
cell.insert("end_timestamp".into(), "1685543121".into());
let serde_val = cell_writer.json_cell(&cell);
assert_eq!(
serde_val,
serde_json::to_value(DateCellData {
timestamp: Some(1672531200),
end_timestamp: None,
include_time: false,
is_range: false,
reminder_id: "".to_string(),
json!({
"start": "2023-02-02T21:05:11+08:00",
"timezone": "Asia/Singapore",
"end": "2023-05-31T22:25:21+08:00",
"pretty_start_datetime": "2023-02-02 21:05:11 +08",
"pretty_start_date": "2023-02-02",
"pretty_start_time": "21:05:11",
"pretty_end_datetime": "2023-05-31 22:25:21 +08",
"pretty_end_date": "2023-05-31",
"pretty_end_time": "22:25:21",
})
.unwrap()
);
}
}
Expand Down
10 changes: 7 additions & 3 deletions collab-database/src/fields/type_option/timestamp_type_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use chrono::{DateTime, Local, Offset, TimeZone};
use chrono_tz::Tz;
use collab::util::AnyMapExt;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_json::{json, Value};
use std::str::FromStr;
use yrs::Any;

Expand All @@ -26,12 +26,16 @@ pub struct TimestampTypeOption {
impl TypeOptionCellReader for TimestampTypeOption {
/// Return formated date and time string for the cell
fn json_cell(&self, cell: &Cell) -> Value {
TimestampCellData::from(cell)
let mut js_val: serde_json::Value = TimestampCellData::from(cell)
.timestamp
.and_then(|ts| DateTime::from_timestamp(ts, 0))
.map(|dt| dt.to_rfc3339())
.unwrap_or_default()
.into()
.into();
if let Some(obj) = js_val.as_object_mut() {
obj.insert("pretty".to_string(), json!(self.stringify_cell(cell)));
}
js_val
}

fn numeric_cell(&self, _cell: &Cell) -> Option<f64> {
Expand Down

0 comments on commit 26bfffb

Please sign in to comment.