|
@@ -1,20 +1,18 @@
|
|
use crate::impl_type_option;
|
|
use crate::impl_type_option;
|
|
|
|
+use crate::services::entities::{CellIdentifier, CellIdentifierPayload};
|
|
|
|
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
|
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
|
|
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, TypeOptionCellData};
|
|
use bytes::Bytes;
|
|
use bytes::Bytes;
|
|
use chrono::format::strftime::StrftimeItems;
|
|
use chrono::format::strftime::StrftimeItems;
|
|
-use chrono::{Datelike, NaiveDateTime};
|
|
|
|
|
|
+use chrono::NaiveDateTime;
|
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|
-use flowy_error::{ErrorCode, FlowyError};
|
|
|
|
|
|
+use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
|
use flowy_grid_data_model::entities::{
|
|
use flowy_grid_data_model::entities::{
|
|
CellChangeset, CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
|
|
CellChangeset, CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
|
|
};
|
|
};
|
|
-
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
+use std::ops::Add;
|
|
use std::str::FromStr;
|
|
use std::str::FromStr;
|
|
-
|
|
|
|
-use crate::services::entities::{CellIdentifier, CellIdentifierPayload};
|
|
|
|
-use crate::services::field::type_options::util::get_cell_data;
|
|
|
|
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
|
|
|
use strum_macros::EnumIter;
|
|
use strum_macros::EnumIter;
|
|
|
|
|
|
// Date
|
|
// Date
|
|
@@ -33,18 +31,45 @@ impl_type_option!(DateTypeOption, FieldType::DateTime);
|
|
|
|
|
|
impl DateTypeOption {
|
|
impl DateTypeOption {
|
|
#[allow(dead_code)]
|
|
#[allow(dead_code)]
|
|
- fn today_from_timestamp(&self, timestamp: i64) -> String {
|
|
|
|
|
|
+ fn today_desc_from_timestamp(&self, timestamp: i64) -> String {
|
|
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
|
let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
|
|
- self.today_from_native(native)
|
|
|
|
|
|
+ self.today_desc_from_native(native)
|
|
}
|
|
}
|
|
|
|
|
|
- fn today_from_native(&self, naive: chrono::NaiveDateTime) -> String {
|
|
|
|
- let utc: chrono::DateTime<chrono::Utc> = chrono::DateTime::from_utc(naive, chrono::Utc);
|
|
|
|
- let local: chrono::DateTime<chrono::Local> = chrono::DateTime::from(utc);
|
|
|
|
- let output = format!("{}", local.format_with_items(StrftimeItems::new(&self.fmt_str())));
|
|
|
|
|
|
+ fn today_desc_from_str(&self, s: String) -> String {
|
|
|
|
+ match NaiveDateTime::parse_from_str(&s, &self.fmt_str()) {
|
|
|
|
+ Ok(native) => self.today_desc_from_native(native),
|
|
|
|
+ Err(_) => "".to_owned(),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn today_desc_from_native(&self, native: chrono::NaiveDateTime) -> String {
|
|
|
|
+ let utc = self.utc_date_time_from_native(native);
|
|
|
|
+ // let china_timezone = FixedOffset::east(8 * 3600);
|
|
|
|
+ // let a = utc.with_timezone(&china_timezone);
|
|
|
|
+ let output = format!("{}", utc.format_with_items(StrftimeItems::new(&self.fmt_str())));
|
|
output
|
|
output
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ fn timestamp_from_str(&self, s: &str) -> FlowyResult<i64> {
|
|
|
|
+ match NaiveDateTime::parse_from_str(s, &self.fmt_str()) {
|
|
|
|
+ Ok(native) => {
|
|
|
|
+ let utc = self.utc_date_time_from_native(native);
|
|
|
|
+ Ok(utc.timestamp())
|
|
|
|
+ }
|
|
|
|
+ Err(_) => Err(ErrorCode::InvalidData.into()),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn utc_date_time_from_timestamp(&self, timestamp: i64) -> chrono::DateTime<chrono::Utc> {
|
|
|
|
+ let native = NaiveDateTime::from_timestamp(timestamp, 0);
|
|
|
|
+ self.utc_date_time_from_native(native)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn utc_date_time_from_native(&self, naive: chrono::NaiveDateTime) -> chrono::DateTime<chrono::Utc> {
|
|
|
|
+ chrono::DateTime::<chrono::Utc>::from_utc(naive, chrono::Utc)
|
|
|
|
+ }
|
|
|
|
+
|
|
fn fmt_str(&self) -> String {
|
|
fn fmt_str(&self) -> String {
|
|
if self.include_time {
|
|
if self.include_time {
|
|
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
|
format!("{} {}", self.date_format.format_str(), self.time_format.format_str())
|
|
@@ -63,14 +88,11 @@ impl CellDataOperation for DateTypeOption {
|
|
|
|
|
|
let cell_data = type_option_cell_data.data;
|
|
let cell_data = type_option_cell_data.data;
|
|
if let Ok(timestamp) = cell_data.parse::<i64>() {
|
|
if let Ok(timestamp) = cell_data.parse::<i64>() {
|
|
- let native = NaiveDateTime::from_timestamp(timestamp, 0);
|
|
|
|
- return DecodedCellData::new(format!("{}", timestamp), self.today_from_native(native));
|
|
|
|
|
|
+ return DecodedCellData::new(format!("{}", timestamp), self.today_desc_from_timestamp(timestamp));
|
|
}
|
|
}
|
|
|
|
|
|
- return match NaiveDateTime::parse_from_str(&cell_data, &self.fmt_str()) {
|
|
|
|
- Ok(date_time) => DecodedCellData::new(format!("{}", date_time.timestamp()), cell_data),
|
|
|
|
- Err(_) => DecodedCellData::default(),
|
|
|
|
- };
|
|
|
|
|
|
+ let cell_content = self.today_desc_from_str(cell_data.clone());
|
|
|
|
+ return DecodedCellData::new(cell_data, cell_content);
|
|
}
|
|
}
|
|
|
|
|
|
DecodedCellData::default()
|
|
DecodedCellData::default()
|
|
@@ -79,25 +101,29 @@ impl CellDataOperation for DateTypeOption {
|
|
fn apply_changeset<T: Into<CellContentChangeset>>(
|
|
fn apply_changeset<T: Into<CellContentChangeset>>(
|
|
&self,
|
|
&self,
|
|
changeset: T,
|
|
changeset: T,
|
|
- cell_meta: Option<CellMeta>,
|
|
|
|
|
|
+ _cell_meta: Option<CellMeta>,
|
|
) -> Result<String, FlowyError> {
|
|
) -> Result<String, FlowyError> {
|
|
let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?;
|
|
let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?;
|
|
- match cell_meta {
|
|
|
|
- None => Ok(TypeOptionCellData::new("", self.field_type()).json()),
|
|
|
|
- Some(cell_meta) => {
|
|
|
|
- let s = match content_changeset.timestamp() {
|
|
|
|
- None => get_cell_data(&cell_meta),
|
|
|
|
- Some(timestamp) => timestamp.to_string(),
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- Ok(TypeOptionCellData::new(s, self.field_type()).json())
|
|
|
|
-
|
|
|
|
- // let changeset = changeset.into();
|
|
|
|
- // if changeset.parse::<f64>().is_err() || changeset.parse::<i64>().is_err() {
|
|
|
|
- // return Err(FlowyError::internal().context(format!("Parse {} failed", changeset)));
|
|
|
|
- // };
|
|
|
|
|
|
+ let cell_content = match content_changeset.date_timestamp() {
|
|
|
|
+ None => "".to_owned(),
|
|
|
|
+ Some(date_timestamp) => {
|
|
|
|
+ //
|
|
|
|
+ match (self.include_time, content_changeset.time) {
|
|
|
|
+ (true, Some(time)) => {
|
|
|
|
+ let utc = self.utc_date_time_from_timestamp(date_timestamp);
|
|
|
|
+ let mut date_str = format!(
|
|
|
|
+ "{}",
|
|
|
|
+ utc.format_with_items(StrftimeItems::new(self.date_format.format_str()))
|
|
|
|
+ );
|
|
|
|
+ date_str = date_str.add(&time);
|
|
|
|
+ let timestamp = self.timestamp_from_str(&date_str)?;
|
|
|
|
+ timestamp.to_string()
|
|
|
|
+ }
|
|
|
|
+ _ => date_timestamp.to_string(),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ };
|
|
|
|
+ Ok(TypeOptionCellData::new(cell_content, self.field_type()).json())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -197,7 +223,7 @@ impl TimeFormat {
|
|
// https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html
|
|
// https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html
|
|
pub fn format_str(&self) -> &'static str {
|
|
pub fn format_str(&self) -> &'static str {
|
|
match self {
|
|
match self {
|
|
- TimeFormat::TwelveHour => "%r",
|
|
|
|
|
|
+ TimeFormat::TwelveHour => "%I:%M %p",
|
|
TimeFormat::TwentyFourHour => "%R",
|
|
TimeFormat::TwentyFourHour => "%R",
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -240,6 +266,22 @@ impl TryInto<DateChangesetParams> for DateChangesetPayload {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+impl std::convert::From<DateChangesetParams> for CellChangeset {
|
|
|
|
+ fn from(params: DateChangesetParams) -> Self {
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: params.date,
|
|
|
|
+ time: params.time,
|
|
|
|
+ };
|
|
|
|
+ let s = serde_json::to_string(&changeset).unwrap();
|
|
|
|
+ CellChangeset {
|
|
|
|
+ grid_id: params.cell_identifier.grid_id,
|
|
|
|
+ row_id: params.cell_identifier.row_id,
|
|
|
|
+ field_id: params.cell_identifier.field_id,
|
|
|
|
+ cell_content_changeset: Some(s),
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
#[derive(Clone, Serialize, Deserialize)]
|
|
pub struct DateCellContentChangeset {
|
|
pub struct DateCellContentChangeset {
|
|
pub date: Option<String>,
|
|
pub date: Option<String>,
|
|
@@ -247,50 +289,29 @@ pub struct DateCellContentChangeset {
|
|
}
|
|
}
|
|
|
|
|
|
impl DateCellContentChangeset {
|
|
impl DateCellContentChangeset {
|
|
- pub fn timestamp(self) -> Option<i64> {
|
|
|
|
- let mut timestamp = 0;
|
|
|
|
- if let Some(date) = self.date {
|
|
|
|
|
|
+ pub fn date_timestamp(&self) -> Option<i64> {
|
|
|
|
+ if let Some(date) = &self.date {
|
|
match date.parse::<i64>() {
|
|
match date.parse::<i64>() {
|
|
- Ok(date_timestamp) => {
|
|
|
|
- timestamp += date_timestamp;
|
|
|
|
- }
|
|
|
|
- Err(_) => {}
|
|
|
|
|
|
+ Ok(date_timestamp) => Some(date_timestamp),
|
|
|
|
+ Err(_) => None,
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- return None;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if let Some(time) = self.time {
|
|
|
|
- match time.parse::<i64>() {
|
|
|
|
- Ok(time_timestamp) => timestamp += time_timestamp,
|
|
|
|
- Err(_) => {}
|
|
|
|
- }
|
|
|
|
|
|
+ None
|
|
}
|
|
}
|
|
-
|
|
|
|
- return Some(timestamp);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-impl std::convert::From<DateChangesetParams> for CellChangeset {
|
|
|
|
- fn from(params: DateChangesetParams) -> Self {
|
|
|
|
- let changeset = DateCellContentChangeset {
|
|
|
|
- date: params.date,
|
|
|
|
- time: params.time,
|
|
|
|
- };
|
|
|
|
|
|
+impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
|
|
|
|
+ fn from(changeset: DateCellContentChangeset) -> Self {
|
|
let s = serde_json::to_string(&changeset).unwrap();
|
|
let s = serde_json::to_string(&changeset).unwrap();
|
|
- CellChangeset {
|
|
|
|
- grid_id: params.cell_identifier.grid_id,
|
|
|
|
- row_id: params.cell_identifier.row_id,
|
|
|
|
- field_id: params.cell_identifier.field_id,
|
|
|
|
- cell_content_changeset: Some(s),
|
|
|
|
- }
|
|
|
|
|
|
+ CellContentChangeset::from(s)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
mod tests {
|
|
mod tests {
|
|
use crate::services::field::FieldBuilder;
|
|
use crate::services::field::FieldBuilder;
|
|
- use crate::services::field::{DateFormat, DateTypeOption, TimeFormat};
|
|
|
|
|
|
+ use crate::services::field::{DateCellContentChangeset, DateFormat, DateTypeOption, TimeFormat};
|
|
use crate::services::row::{CellDataOperation, TypeOptionCellData};
|
|
use crate::services::row::{CellDataOperation, TypeOptionCellData};
|
|
use flowy_grid_data_model::entities::FieldType;
|
|
use flowy_grid_data_model::entities::FieldType;
|
|
use strum::IntoEnumIterator;
|
|
use strum::IntoEnumIterator;
|
|
@@ -362,14 +383,20 @@ mod tests {
|
|
type_option.time_format = time_format;
|
|
type_option.time_format = time_format;
|
|
match time_format {
|
|
match time_format {
|
|
TimeFormat::TwentyFourHour => {
|
|
TimeFormat::TwentyFourHour => {
|
|
- assert_eq!("Mar 14,2022".to_owned(), type_option.today_from_timestamp(1647251762));
|
|
|
|
|
|
+ assert_eq!(
|
|
|
|
+ "Mar 14,2022".to_owned(),
|
|
|
|
+ type_option.today_desc_from_timestamp(1647251762)
|
|
|
|
+ );
|
|
assert_eq!(
|
|
assert_eq!(
|
|
"Mar 14,2022".to_owned(),
|
|
"Mar 14,2022".to_owned(),
|
|
type_option.decode_cell_data(data("1647251762"), &field_meta).content
|
|
type_option.decode_cell_data(data("1647251762"), &field_meta).content
|
|
);
|
|
);
|
|
}
|
|
}
|
|
TimeFormat::TwelveHour => {
|
|
TimeFormat::TwelveHour => {
|
|
- assert_eq!("Mar 14,2022".to_owned(), type_option.today_from_timestamp(1647251762));
|
|
|
|
|
|
+ assert_eq!(
|
|
|
|
+ "Mar 14,2022".to_owned(),
|
|
|
|
+ type_option.today_desc_from_timestamp(1647251762)
|
|
|
|
+ );
|
|
assert_eq!(
|
|
assert_eq!(
|
|
"Mar 14,2022".to_owned(),
|
|
"Mar 14,2022".to_owned(),
|
|
type_option.decode_cell_data(data("1647251762"), &field_meta).content
|
|
type_option.decode_cell_data(data("1647251762"), &field_meta).content
|
|
@@ -379,6 +406,72 @@ mod tests {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[test]
|
|
|
|
+ fn date_description_apply_changeset_test() {
|
|
|
|
+ let mut type_option = DateTypeOption::default();
|
|
|
|
+ let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
|
|
|
+ let date_timestamp = "1653609600".to_owned();
|
|
|
|
+
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: Some(date_timestamp.clone()),
|
|
|
|
+ time: None,
|
|
|
|
+ };
|
|
|
|
+ let result = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+ let content = type_option.decode_cell_data(result.clone(), &field_meta).content;
|
|
|
|
+ assert_eq!(content, "May 27,2022".to_owned());
|
|
|
|
+
|
|
|
|
+ type_option.include_time = true;
|
|
|
|
+ let content = type_option.decode_cell_data(result, &field_meta).content;
|
|
|
|
+ assert_eq!(content, "May 27,2022 00:00".to_owned());
|
|
|
|
+
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: Some(date_timestamp.clone()),
|
|
|
|
+ time: Some("1:00".to_owned()),
|
|
|
|
+ };
|
|
|
|
+ let result = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+ let content = type_option.decode_cell_data(result, &field_meta).content;
|
|
|
|
+ assert_eq!(content, "May 27,2022 01:00".to_owned());
|
|
|
|
+
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: Some(date_timestamp),
|
|
|
|
+ time: Some("1:00 am".to_owned()),
|
|
|
|
+ };
|
|
|
|
+ type_option.time_format = TimeFormat::TwelveHour;
|
|
|
|
+ let result = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+ let content = type_option.decode_cell_data(result, &field_meta).content;
|
|
|
|
+ assert_eq!(content, "May 27,2022 01:00 AM".to_owned());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ #[test]
|
|
|
|
+ #[should_panic]
|
|
|
|
+ fn date_description_apply_changeset_error_test() {
|
|
|
|
+ let mut type_option = DateTypeOption::default();
|
|
|
|
+ type_option.include_time = true;
|
|
|
|
+ let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
|
|
|
|
+ let date_timestamp = "1653609600".to_owned();
|
|
|
|
+
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: Some(date_timestamp.clone()),
|
|
|
|
+ time: Some("1:a0".to_owned()),
|
|
|
|
+ };
|
|
|
|
+ let _ = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+
|
|
|
|
+ let changeset = DateCellContentChangeset {
|
|
|
|
+ date: Some(date_timestamp.clone()),
|
|
|
|
+ time: Some("1:".to_owned()),
|
|
|
|
+ };
|
|
|
|
+ let _ = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+
|
|
|
|
+ // let changeset = DateCellContentChangeset {
|
|
|
|
+ // date: Some(date_timestamp),
|
|
|
|
+ // time: Some("1:00 am".to_owned()),
|
|
|
|
+ // };
|
|
|
|
+ // type_option.time_format = TimeFormat::TwelveHour;
|
|
|
|
+ // let result = type_option.apply_changeset(changeset, None).unwrap();
|
|
|
|
+ // let content = type_option.decode_cell_data(result, &field_meta).content;
|
|
|
|
+ // assert_eq!(content, "May 27,2022 01:00 AM".to_owned());
|
|
|
|
+ }
|
|
|
|
+
|
|
#[test]
|
|
#[test]
|
|
#[should_panic]
|
|
#[should_panic]
|
|
fn date_description_invalid_data_test() {
|
|
fn date_description_invalid_data_test() {
|