use crate::impl_from_and_to_type_option; use crate::services::row::StringifyCellData; use chrono::format::strftime::StrftimeItems; use chrono::NaiveDateTime; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::FlowyError; use flowy_grid_data_model::entities::{Field, FieldType}; use serde::{Deserialize, Serialize}; use strum_macros::EnumIter; // Date #[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] pub struct DateDescription { #[pb(index = 1)] pub date_format: DateFormat, #[pb(index = 2)] pub time_format: TimeFormat, } impl_from_and_to_type_option!(DateDescription, FieldType::DateTime); impl DateDescription { #[allow(dead_code)] fn today_from_timestamp(&self, timestamp: i64) -> String { let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0); self.today_from_native(native) } fn today_from_native(&self, naive: chrono::NaiveDateTime) -> String { let utc: chrono::DateTime = chrono::DateTime::from_utc(naive, chrono::Utc); let local: chrono::DateTime = chrono::DateTime::from(utc); let fmt_str = format!("{} {}", self.date_format.format_str(), self.time_format.format_str()); let output = format!("{}", local.format_with_items(StrftimeItems::new(&fmt_str))); output } } impl StringifyCellData for DateDescription { fn str_from_cell_data(&self, data: String) -> String { match data.parse::() { Ok(timestamp) => { let native = NaiveDateTime::from_timestamp(timestamp, 0); self.today_from_native(native) } Err(e) => { tracing::debug!("DateDescription format {} fail. error: {:?}", data, e); String::new() } } } fn str_to_cell_data(&self, s: &str) -> Result { let timestamp = match s.parse::() { Ok(timestamp) => timestamp, Err(e) => { tracing::error!("Parse {} to i64 failed: {}", s, e); chrono::Utc::now().timestamp() } }; Ok(format!("{}", timestamp)) } } #[derive(Clone, Debug, Copy, EnumIter, Serialize, Deserialize, ProtoBuf_Enum)] pub enum DateFormat { Local = 0, US = 1, ISO = 2, Friendly = 3, } impl std::default::Default for DateFormat { fn default() -> Self { DateFormat::Friendly } } impl std::convert::From for DateFormat { fn from(value: i32) -> Self { match value { 0 => DateFormat::Local, 1 => DateFormat::US, 2 => DateFormat::ISO, 3 => DateFormat::Friendly, _ => { tracing::error!("Unsupported date format, fallback to friendly"); DateFormat::Friendly } } } } impl DateFormat { pub fn value(&self) -> i32 { *self as i32 } // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html pub fn format_str(&self) -> &'static str { match self { DateFormat::Local => "%Y/%m/%d", DateFormat::US => "%Y/%m/%d", DateFormat::ISO => "%Y-%m-%d", DateFormat::Friendly => "%b %d,%Y", } } } #[derive(Clone, Copy, PartialEq, Eq, EnumIter, Debug, Hash, Serialize, Deserialize, ProtoBuf_Enum)] pub enum TimeFormat { TwelveHour = 0, TwentyFourHour = 1, } impl std::convert::From for TimeFormat { fn from(value: i32) -> Self { match value { 0 => TimeFormat::TwelveHour, 1 => TimeFormat::TwentyFourHour, _ => { tracing::error!("Unsupported time format, fallback to TwentyFourHour"); TimeFormat::TwentyFourHour } } } } impl TimeFormat { pub fn value(&self) -> i32 { *self as i32 } // https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html pub fn format_str(&self) -> &'static str { match self { TimeFormat::TwelveHour => "%r", TimeFormat::TwentyFourHour => "%R", } } } impl std::default::Default for TimeFormat { fn default() -> Self { TimeFormat::TwentyFourHour } } #[cfg(test)] mod tests { use crate::services::cell::{DateDescription, DateFormat, TimeFormat}; use crate::services::row::StringifyCellData; use strum::IntoEnumIterator; #[test] fn date_description_date_format_test() { let mut description = DateDescription::default(); let _timestamp = 1647251762; for date_format in DateFormat::iter() { description.date_format = date_format; match date_format { DateFormat::Friendly => { assert_eq!( "Mar 14,2022 17:56".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "Mar 14,2022 17:56".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } DateFormat::US => { assert_eq!( "2022/03/14 17:56".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "2022/03/14 17:56".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } DateFormat::ISO => { assert_eq!( "2022-03-14 17:56".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "2022-03-14 17:56".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } DateFormat::Local => { assert_eq!( "2022/03/14 17:56".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "2022/03/14 17:56".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } } } } #[test] fn date_description_time_format_test() { let mut description = DateDescription::default(); for time_format in TimeFormat::iter() { description.time_format = time_format; match time_format { TimeFormat::TwentyFourHour => { assert_eq!( "Mar 14,2022 17:56".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "Mar 14,2022 17:56".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } TimeFormat::TwelveHour => { assert_eq!( "Mar 14,2022 05:56:02 PM".to_owned(), description.today_from_timestamp(1647251762) ); assert_eq!( "Mar 14,2022 05:56:02 PM".to_owned(), description.str_from_cell_data("1647251762".to_owned()) ); } } } } #[test] fn date_description_invalid_data_test() { let description = DateDescription::default(); description.str_to_cell_data("he").unwrap(); } }