Browse Source

chore: check time format & add time format test

appflowy 3 years ago
parent
commit
efbac6c92d

+ 5 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/date_cal_bloc.dart

@@ -9,7 +9,7 @@ import 'dart:async';
 import 'cell_service/cell_service.dart';
 import 'package:dartz/dartz.dart';
 import 'package:protobuf/protobuf.dart';
-import 'package:fixnum/fixnum.dart' as $fixnum;
+// import 'package:fixnum/fixnum.dart' as $fixnum;
 part 'date_cal_bloc.freezed.dart';
 
 class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
@@ -163,9 +163,10 @@ class DateCalState with _$DateCalState {
     Option<DateCellPersistenceData> dateData = none();
     final time = cellData?.time ?? "";
     if (cellData != null) {
-      final timestamp = $fixnum.Int64.parseInt(cellData.date).toInt();
-      final selectedDay = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
-      dateData = Some(DateCellPersistenceData(date: selectedDay));
+      // final timestamp = $fixnum.Int64.parseInt(cellData.timestamp).toInt();
+      final timestamp = cellData.timestamp * 1000;
+      final selectedDay = DateTime.fromMillisecondsSinceEpoch(timestamp.toInt());
+      dateData = Some(DateCellPersistenceData(date: selectedDay, time: time));
     }
 
     return DateCalState(

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/calendar.dart

@@ -127,7 +127,7 @@ class _CellCalendarWidget extends StatelessWidget {
           if (state.dateTypeOption.includeTime) {
             children.addAll([
               _TimeTextField(
-                time: "",
+                text: state.time,
                 errorText: state.inputTimeError.fold(() => "", (error) => error.toString()),
                 onEditingComplete: (text) {
                   context.read<DateCalBloc>().add(DateCalEvent.setTime(text));
@@ -247,11 +247,11 @@ class _IncludeTimeButton extends StatelessWidget {
 
 class _TimeTextField extends StatefulWidget {
   final String errorText;
-  final String time;
+  final String text;
   final void Function(String) onEditingComplete;
   const _TimeTextField({
     Key? key,
-    required this.time,
+    required this.text,
     required this.errorText,
     required this.onEditingComplete,
   }) : super(key: key);
@@ -267,7 +267,7 @@ class _TimeTextFieldState extends State<_TimeTextField> {
   @override
   void initState() {
     _focusNode = FocusNode();
-    _controller = TextEditingController(text: widget.time);
+    _controller = TextEditingController(text: widget.text);
     _focusNode.addListener(() {
       if (mounted) {
         widget.onEditingComplete(_controller.text);

+ 6 - 0
frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart

@@ -60,6 +60,12 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
   @override
   void initState() {
     obscuteText = widget.obscureText;
+    if (widget.controller != null) {
+      inputText = widget.controller!.text;
+    } else {
+      inputText = widget.initialValue ?? "";
+    }
+
     super.initState();
   }
 

+ 15 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/date_type_option.pb.dart

@@ -7,6 +7,7 @@
 
 import 'dart:core' as $core;
 
+import 'package:fixnum/fixnum.dart' as $fixnum;
 import 'package:protobuf/protobuf.dart' as $pb;
 
 import 'cell_entities.pb.dart' as $0;
@@ -94,6 +95,7 @@ class DateCellData extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DateCellData', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'date')
     ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'time')
+    ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timestamp')
     ..hasRequiredFields = false
   ;
 
@@ -101,6 +103,7 @@ class DateCellData extends $pb.GeneratedMessage {
   factory DateCellData({
     $core.String? date,
     $core.String? time,
+    $fixnum.Int64? timestamp,
   }) {
     final _result = create();
     if (date != null) {
@@ -109,6 +112,9 @@ class DateCellData extends $pb.GeneratedMessage {
     if (time != null) {
       _result.time = time;
     }
+    if (timestamp != null) {
+      _result.timestamp = timestamp;
+    }
     return _result;
   }
   factory DateCellData.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -149,6 +155,15 @@ class DateCellData extends $pb.GeneratedMessage {
   $core.bool hasTime() => $_has(1);
   @$pb.TagNumber(2)
   void clearTime() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get timestamp => $_getI64(2);
+  @$pb.TagNumber(3)
+  set timestamp($fixnum.Int64 v) { $_setInt64(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasTimestamp() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearTimestamp() => clearField(3);
 }
 
 enum DateChangesetPayload_OneOfDate {

+ 2 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/date_type_option.pbjson.dart

@@ -50,11 +50,12 @@ const DateCellData$json = const {
   '2': const [
     const {'1': 'date', '3': 1, '4': 1, '5': 9, '10': 'date'},
     const {'1': 'time', '3': 2, '4': 1, '5': 9, '10': 'time'},
+    const {'1': 'timestamp', '3': 3, '4': 1, '5': 3, '10': 'timestamp'},
   ],
 };
 
 /// Descriptor for `DateCellData`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List dateCellDataDescriptor = $convert.base64Decode('CgxEYXRlQ2VsbERhdGESEgoEZGF0ZRgBIAEoCVIEZGF0ZRISCgR0aW1lGAIgASgJUgR0aW1l');
+final $typed_data.Uint8List dateCellDataDescriptor = $convert.base64Decode('CgxEYXRlQ2VsbERhdGESEgoEZGF0ZRgBIAEoCVIEZGF0ZRISCgR0aW1lGAIgASgJUgR0aW1lEhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1w');
 @$core.Deprecated('Use dateChangesetPayloadDescriptor instead')
 const DateChangesetPayload$json = const {
   '1': 'DateChangesetPayload',

+ 45 - 9
frontend/rust-lib/flowy-grid/src/protobuf/model/date_type_option.rs

@@ -242,6 +242,7 @@ pub struct DateCellData {
     // message fields
     pub date: ::std::string::String,
     pub time: ::std::string::String,
+    pub timestamp: i64,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -309,6 +310,21 @@ impl DateCellData {
     pub fn take_time(&mut self) -> ::std::string::String {
         ::std::mem::replace(&mut self.time, ::std::string::String::new())
     }
+
+    // int64 timestamp = 3;
+
+
+    pub fn get_timestamp(&self) -> i64 {
+        self.timestamp
+    }
+    pub fn clear_timestamp(&mut self) {
+        self.timestamp = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_timestamp(&mut self, v: i64) {
+        self.timestamp = v;
+    }
 }
 
 impl ::protobuf::Message for DateCellData {
@@ -326,6 +342,13 @@ impl ::protobuf::Message for DateCellData {
                 2 => {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.time)?;
                 },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int64()?;
+                    self.timestamp = tmp;
+                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -344,6 +367,9 @@ impl ::protobuf::Message for DateCellData {
         if !self.time.is_empty() {
             my_size += ::protobuf::rt::string_size(2, &self.time);
         }
+        if self.timestamp != 0 {
+            my_size += ::protobuf::rt::value_size(3, self.timestamp, ::protobuf::wire_format::WireTypeVarint);
+        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -356,6 +382,9 @@ impl ::protobuf::Message for DateCellData {
         if !self.time.is_empty() {
             os.write_string(2, &self.time)?;
         }
+        if self.timestamp != 0 {
+            os.write_int64(3, self.timestamp)?;
+        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -404,6 +433,11 @@ impl ::protobuf::Message for DateCellData {
                 |m: &DateCellData| { &m.time },
                 |m: &mut DateCellData| { &mut m.time },
             ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
+                "timestamp",
+                |m: &DateCellData| { &m.timestamp },
+                |m: &mut DateCellData| { &mut m.timestamp },
+            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<DateCellData>(
                 "DateCellData",
                 fields,
@@ -422,6 +456,7 @@ impl ::protobuf::Clear for DateCellData {
     fn clear(&mut self) {
         self.date.clear();
         self.time.clear();
+        self.timestamp = 0;
         self.unknown_fields.clear();
     }
 }
@@ -886,15 +921,16 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     ateTypeOption\x12,\n\x0bdate_format\x18\x01\x20\x01(\x0e2\x0b.DateFormat\
     R\ndateFormat\x12,\n\x0btime_format\x18\x02\x20\x01(\x0e2\x0b.TimeFormat\
     R\ntimeFormat\x12!\n\x0cinclude_time\x18\x03\x20\x01(\x08R\x0bincludeTim\
-    e\"6\n\x0cDateCellData\x12\x12\n\x04date\x18\x01\x20\x01(\tR\x04date\x12\
-    \x12\n\x04time\x18\x02\x20\x01(\tR\x04time\"\xa1\x01\n\x14DateChangesetP\
-    ayload\x12?\n\x0fcell_identifier\x18\x01\x20\x01(\x0b2\x16.CellIdentifie\
-    rPayloadR\x0ecellIdentifier\x12\x14\n\x04date\x18\x02\x20\x01(\tH\0R\x04\
-    date\x12\x14\n\x04time\x18\x03\x20\x01(\tH\x01R\x04timeB\r\n\x0bone_of_d\
-    ateB\r\n\x0bone_of_time*6\n\nDateFormat\x12\t\n\x05Local\x10\0\x12\x06\n\
-    \x02US\x10\x01\x12\x07\n\x03ISO\x10\x02\x12\x0c\n\x08Friendly\x10\x03*0\
-    \n\nTimeFormat\x12\x0e\n\nTwelveHour\x10\0\x12\x12\n\x0eTwentyFourHour\
-    \x10\x01b\x06proto3\
+    e\"T\n\x0cDateCellData\x12\x12\n\x04date\x18\x01\x20\x01(\tR\x04date\x12\
+    \x12\n\x04time\x18\x02\x20\x01(\tR\x04time\x12\x1c\n\ttimestamp\x18\x03\
+    \x20\x01(\x03R\ttimestamp\"\xa1\x01\n\x14DateChangesetPayload\x12?\n\x0f\
+    cell_identifier\x18\x01\x20\x01(\x0b2\x16.CellIdentifierPayloadR\x0ecell\
+    Identifier\x12\x14\n\x04date\x18\x02\x20\x01(\tH\0R\x04date\x12\x14\n\
+    \x04time\x18\x03\x20\x01(\tH\x01R\x04timeB\r\n\x0bone_of_dateB\r\n\x0bon\
+    e_of_time*6\n\nDateFormat\x12\t\n\x05Local\x10\0\x12\x06\n\x02US\x10\x01\
+    \x12\x07\n\x03ISO\x10\x02\x12\x0c\n\x08Friendly\x10\x03*0\n\nTimeFormat\
+    \x12\x0e\n\nTwelveHour\x10\0\x12\x12\n\x0eTwentyFourHour\x10\x01b\x06pro\
+    to3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 1 - 0
frontend/rust-lib/flowy-grid/src/protobuf/proto/date_type_option.proto

@@ -9,6 +9,7 @@ message DateTypeOption {
 message DateCellData {
     string date = 1;
     string time = 2;
+    int64 timestamp = 3;
 }
 message DateChangesetPayload {
     CellIdentifierPayload cell_identifier = 1;

+ 121 - 54
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -6,7 +6,7 @@ use bytes::Bytes;
 use chrono::format::strftime::StrftimeItems;
 use chrono::NaiveDateTime;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{
     CellChangeset, CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
 };
@@ -30,12 +30,12 @@ pub struct DateTypeOption {
 impl_type_option!(DateTypeOption, FieldType::DateTime);
 
 impl DateTypeOption {
-    #[allow(dead_code)]
     fn today_desc_from_timestamp(&self, timestamp: i64) -> String {
         let native = chrono::NaiveDateTime::from_timestamp(timestamp, 0);
         self.today_desc_from_native(native)
     }
 
+    #[allow(dead_code)]
     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),
@@ -89,25 +89,47 @@ impl DateTypeOption {
             return Ok(DateCellData::default());
         }
 
-        let data: DateCellData = serde_json::from_str(&result.unwrap().data)?;
-        Ok(data)
+        let serde_cell_data = DateCellDataSerde::from_str(&result.unwrap().data)?;
+        let time = serde_cell_data.time;
+        let timestamp = serde_cell_data.timestamp;
+        let date = self.decode_cell_data_from_timestamp(serde_cell_data.timestamp).content;
+
+        return Ok(DateCellData { date, time, timestamp });
+    }
+
+    fn decode_cell_data_from_timestamp(&self, timestamp: i64) -> DecodedCellData {
+        if timestamp == 0 {
+            return DecodedCellData::default();
+        }
+
+        let cell_content = self.today_desc_from_timestamp(timestamp);
+        return DecodedCellData::new(timestamp.to_string(), cell_content);
+    }
+
+    fn timestamp_from_utc_with_time(&self, utc: &chrono::DateTime<chrono::Utc>, time: &str) -> FlowyResult<i64> {
+        let mut date_str = format!(
+            "{}",
+            utc.format_with_items(StrftimeItems::new(self.date_format.format_str()))
+        );
+        date_str = date_str.add(&time);
+        self.timestamp_from_str(&date_str)
     }
 }
 
 impl CellDataOperation for DateTypeOption {
     fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> DecodedCellData {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
+            // Return default data if the type_option_cell_data is not FieldType::DateTime.
+            // It happens when switching from one field to another.
+            // For example:
+            // FieldType::RichText -> FieldType::DateTime, it will display empty content on the screen.
             if !type_option_cell_data.is_date() {
                 return DecodedCellData::default();
             }
-
-            let cell_data = type_option_cell_data.data;
-            if let Ok(timestamp) = cell_data.parse::<i64>() {
-                return DecodedCellData::new(format!("{}", timestamp), self.today_desc_from_timestamp(timestamp));
-            }
-
-            let cell_content = self.today_desc_from_str(cell_data.clone());
-            return DecodedCellData::new(cell_data, cell_content);
+            return match DateCellDataSerde::from_str(&type_option_cell_data.data) {
+                Ok(serde_cell_data) => self.decode_cell_data_from_timestamp(serde_cell_data.timestamp),
+                Err(_) => DecodedCellData::default(),
+            };
         }
 
         DecodedCellData::default()
@@ -119,26 +141,20 @@ impl CellDataOperation for DateTypeOption {
         _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let content_changeset: DateCellContentChangeset = serde_json::from_str(&changeset.into())?;
-        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(),
+        let cell_data = match content_changeset.date_timestamp() {
+            None => DateCellDataSerde::default(),
+            Some(date_timestamp) => match (self.include_time, content_changeset.time) {
+                (true, Some(time)) => {
+                    let time = time.to_uppercase();
+                    let utc = self.utc_date_time_from_timestamp(date_timestamp);
+                    let timestamp = self.timestamp_from_utc_with_time(&utc, &time)?;
+                    DateCellDataSerde { timestamp, time }
                 }
-            }
+                _ => DateCellDataSerde::from_timestamp(date_timestamp),
+            },
         };
-        Ok(TypeOptionCellData::new(cell_content, self.field_type()).json())
+
+        Ok(TypeOptionCellData::new(cell_data.to_string(), self.field_type()).json())
     }
 }
 
@@ -250,13 +266,39 @@ impl std::default::Default for TimeFormat {
     }
 }
 
-#[derive(Clone, Debug, Default, ProtoBuf, Serialize, Deserialize)]
+#[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct DateCellData {
     #[pb(index = 1)]
     pub date: String,
 
     #[pb(index = 2)]
     pub time: String,
+
+    #[pb(index = 3)]
+    pub timestamp: i64,
+}
+
+#[derive(Default, Serialize, Deserialize)]
+pub struct DateCellDataSerde {
+    pub timestamp: i64,
+    pub time: String,
+}
+
+impl DateCellDataSerde {
+    fn from_timestamp(timestamp: i64) -> Self {
+        Self {
+            timestamp,
+            time: "".to_string(),
+        }
+    }
+
+    fn to_string(self) -> String {
+        serde_json::to_string(&self).unwrap_or("".to_string())
+    }
+
+    fn from_str(s: &str) -> FlowyResult<Self> {
+        serde_json::from_str::<DateCellDataSerde>(s).map_err(internal_error)
+    }
 }
 
 #[derive(Clone, Debug, Default, ProtoBuf)]
@@ -335,7 +377,9 @@ impl std::convert::From<DateCellContentChangeset> for CellContentChangeset {
 #[cfg(test)]
 mod tests {
     use crate::services::field::FieldBuilder;
-    use crate::services::field::{DateCellContentChangeset, DateFormat, DateTypeOption, TimeFormat};
+    use crate::services::field::{
+        DateCellContentChangeset, DateCellData, DateCellDataSerde, DateFormat, DateTypeOption, TimeFormat,
+    };
     use crate::services::row::{CellDataOperation, TypeOptionCellData};
     use flowy_grid_data_model::entities::FieldType;
     use strum::IntoEnumIterator;
@@ -360,39 +404,25 @@ mod tests {
                 DateFormat::Friendly => {
                     assert_eq!(
                         "Mar 14,2022".to_owned(),
-                        type_option.decode_cell_data(data("1647251762"), &field_meta).content
-                    );
-                    assert_eq!(
-                        // "Mar 14,2022".to_owned(),
-                        "".to_owned(),
-                        type_option
-                            .decode_cell_data(data("Mar 14,2022 17:56"), &field_meta)
-                            .content
+                        type_option.decode_cell_data(data(1647251762), &field_meta).content
                     );
                 }
                 DateFormat::US => {
                     assert_eq!(
                         "2022/03/14".to_owned(),
-                        type_option.decode_cell_data(data("1647251762"), &field_meta).content
-                    );
-                    assert_eq!(
-                        // "2022/03/14".to_owned(),
-                        "".to_owned(),
-                        type_option
-                            .decode_cell_data(data("2022/03/14 17:56"), &field_meta)
-                            .content
+                        type_option.decode_cell_data(data(1647251762), &field_meta).content
                     );
                 }
                 DateFormat::ISO => {
                     assert_eq!(
                         "2022-03-14".to_owned(),
-                        type_option.decode_cell_data(data("1647251762"), &field_meta).content
+                        type_option.decode_cell_data(data(1647251762), &field_meta).content
                     );
                 }
                 DateFormat::Local => {
                     assert_eq!(
                         "2022/03/14".to_owned(),
-                        type_option.decode_cell_data(data("1647251762"), &field_meta).content
+                        type_option.decode_cell_data(data(1647251762), &field_meta).content
                     );
                 }
             }
@@ -413,7 +443,7 @@ mod tests {
                     );
                     assert_eq!(
                         "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 => {
@@ -423,7 +453,39 @@ mod tests {
                     );
                     assert_eq!(
                         "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
+                    );
+                }
+            }
+        }
+    }
+
+    #[test]
+    fn date_description_time_format_test2() {
+        let mut type_option = DateTypeOption::default();
+        let field_meta = FieldBuilder::from_field_type(&FieldType::Number).build();
+        for time_format in TimeFormat::iter() {
+            type_option.time_format = time_format;
+            type_option.include_time = true;
+            match time_format {
+                TimeFormat::TwentyFourHour => {
+                    assert_eq!(
+                        "May 27,2022 00:00".to_owned(),
+                        type_option.today_desc_from_timestamp(1653609600)
+                    );
+                    assert_eq!(
+                        "May 27,2022 00:00".to_owned(),
+                        type_option.decode_cell_data(data(1653609600), &field_meta).content
+                    );
+                }
+                TimeFormat::TwelveHour => {
+                    assert_eq!(
+                        "May 27,2022 12:00 AM".to_owned(),
+                        type_option.today_desc_from_timestamp(1653609600)
+                    );
+                    assert_eq!(
+                        "May 27,2022 12:00 AM".to_owned(),
+                        type_option.decode_cell_data(data(1653609600), &field_meta).content
                     );
                 }
             }
@@ -494,7 +556,12 @@ mod tests {
         type_option.apply_changeset("he", None).unwrap();
     }
 
-    fn data(s: &str) -> String {
-        TypeOptionCellData::new(s, FieldType::DateTime).json()
+    fn data(s: i64) -> String {
+        let json = serde_json::to_string(&DateCellDataSerde {
+            timestamp: s,
+            time: "".to_string(),
+        })
+        .unwrap();
+        TypeOptionCellData::new(&json, FieldType::DateTime).json()
     }
 }