This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 398ad2f80 feat(rust): make rust chrono optional (#3683)
398ad2f80 is described below

commit 398ad2f80c55bbe1f8d75def24009d1291019257
Author: Shawn Yang <[email protected]>
AuthorDate: Fri May 15 22:57:57 2026 +0800

    feat(rust): make rust chrono optional (#3683)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence of the final clean AI review results
    from both fresh reviewers on the current PR diff or current HEAD after
    the latest code changes.
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 compiler/README.md                                 |  30 +-
 compiler/fory_compiler/frontend/fdl/parser.py      |   1 +
 compiler/fory_compiler/generators/rust.py          |  28 +-
 .../fory_compiler/tests/test_generated_code.py     |  47 ++
 docs/compiler/compiler-guide.md                    |   9 +-
 docs/compiler/schema-idl.md                        |  70 +-
 docs/guide/rust/basic-serialization.md             |  34 +-
 docs/specification/xlang_type_mapping.md           |   4 +-
 integration_tests/idl_tests/idl/example.fdl        |   1 +
 integration_tests/idl_tests/idl/optional_types.fdl |   2 +
 integration_tests/idl_tests/rust/Cargo.lock        |   4 -
 integration_tests/idl_tests/rust/Cargo.toml        |   2 +-
 rust/fory-core/Cargo.toml                          |   9 +-
 rust/fory-core/src/lib.rs                          |   6 +-
 rust/fory-core/src/resolver/type_resolver.rs       |  15 +-
 rust/fory-core/src/row/row.rs                      |  51 +-
 rust/fory-core/src/serializer/datetime.rs          | 414 ++++++-----
 rust/fory-core/src/serializer/skip.rs              |   6 +-
 rust/fory-core/src/type_id.rs                      |  10 +-
 rust/fory-core/src/types/mod.rs                    |   2 +
 rust/fory-core/src/types/temporal.rs               | 763 +++++++++++++++++++++
 rust/fory-core/src/util/mod.rs                     |   9 -
 rust/fory-derive/Cargo.toml                        |   4 +-
 rust/fory-derive/src/lib.rs                        |   6 +-
 rust/fory-derive/src/object/util.rs                |   4 +-
 rust/fory/Cargo.toml                               |   5 +
 rust/fory/src/lib.rs                               |  12 +-
 rust/tests/Cargo.toml                              |   6 +-
 rust/tests/tests/compatible/test_basic_type.rs     |  58 +-
 rust/tests/tests/test_complex_struct.rs            |  10 +-
 rust/tests/tests/test_cross_language.rs            |  22 +-
 rust/tests/tests/test_fory.rs                      |  13 +-
 32 files changed, 1309 insertions(+), 348 deletions(-)

diff --git a/compiler/README.md b/compiler/README.md
index fb443ea4d..236c38b77 100644
--- a/compiler/README.md
+++ b/compiler/README.md
@@ -185,21 +185,21 @@ message Config { ... }  // Registered as "package.Config"
 
 ### Primitive Types
 
-| FDL Type    | Java        | Python              | Go          | Rust         
           | C++                    | C#               | JavaScript         |
-| ----------- | ----------- | ------------------- | ----------- | 
----------------------- | ---------------------- | ---------------- | 
------------------ |
-| `bool`      | `boolean`   | `bool`              | `bool`      | `bool`       
           | `bool`                 | `bool`           | `boolean`          |
-| `int8`      | `byte`      | `pyfory.Int8`       | `int8`      | `i8`         
           | `int8_t`               | `sbyte`          | `number`           |
-| `int16`     | `short`     | `pyfory.Int16`      | `int16`     | `i16`        
           | `int16_t`              | `short`          | `number`           |
-| `int32`     | `int`       | `pyfory.Int32`      | `int32`     | `i32`        
           | `int32_t`              | `int`            | `number`           |
-| `int64`     | `long`      | `pyfory.Int64`      | `int64`     | `i64`        
           | `int64_t`              | `long`           | `bigint \| number` |
-| `float16`   | `Float16`   | `pyfory.Float16`    | `float16`   | `Float16`    
           | `fory::float16_t`      | `Half`           | `number`           |
-| `bfloat16`  | `BFloat16`  | `pyfory.BFloat16`   | `bfloat16`  | `BFloat16`   
           | `fory::bfloat16_t`     | `BFloat16`       | `number`           |
-| `float32`   | `float`     | `pyfory.Float32`    | `float32`   | `f32`        
           | `float`                | `float`          | `number`           |
-| `float64`   | `double`    | `pyfory.Float64`    | `float64`   | `f64`        
           | `double`               | `double`         | `number`           |
-| `string`    | `String`    | `str`               | `string`    | `String`     
           | `std::string`          | `string`         | `string`           |
-| `bytes`     | `byte[]`    | `bytes`             | `[]byte`    | `Vec<u8>`    
           | `std::vector<uint8_t>` | `byte[]`         | `Uint8Array`       |
-| `date`      | `LocalDate` | `datetime.date`     | `time.Time` | 
`chrono::NaiveDate`     | `fory::Date`           | `DateOnly`       | `Date`    
         |
-| `timestamp` | `Instant`   | `datetime.datetime` | `time.Time` | 
`chrono::NaiveDateTime` | `fory::Timestamp`      | `DateTimeOffset` | `Date`    
         |
+| FDL Type    | Java        | Python              | Go          | Rust         
     | C++                    | C#               | JavaScript         |
+| ----------- | ----------- | ------------------- | ----------- | 
----------------- | ---------------------- | ---------------- | 
------------------ |
+| `bool`      | `boolean`   | `bool`              | `bool`      | `bool`       
     | `bool`                 | `bool`           | `boolean`          |
+| `int8`      | `byte`      | `pyfory.Int8`       | `int8`      | `i8`         
     | `int8_t`               | `sbyte`          | `number`           |
+| `int16`     | `short`     | `pyfory.Int16`      | `int16`     | `i16`        
     | `int16_t`              | `short`          | `number`           |
+| `int32`     | `int`       | `pyfory.Int32`      | `int32`     | `i32`        
     | `int32_t`              | `int`            | `number`           |
+| `int64`     | `long`      | `pyfory.Int64`      | `int64`     | `i64`        
     | `int64_t`              | `long`           | `bigint \| number` |
+| `float16`   | `Float16`   | `pyfory.Float16`    | `float16`   | `Float16`    
     | `fory::float16_t`      | `Half`           | `number`           |
+| `bfloat16`  | `BFloat16`  | `pyfory.BFloat16`   | `bfloat16`  | `BFloat16`   
     | `fory::bfloat16_t`     | `BFloat16`       | `number`           |
+| `float32`   | `float`     | `pyfory.Float32`    | `float32`   | `f32`        
     | `float`                | `float`          | `number`           |
+| `float64`   | `double`    | `pyfory.Float64`    | `float64`   | `f64`        
     | `double`               | `double`         | `number`           |
+| `string`    | `String`    | `str`               | `string`    | `String`     
     | `std::string`          | `string`         | `string`           |
+| `bytes`     | `byte[]`    | `bytes`             | `[]byte`    | `Vec<u8>`    
     | `std::vector<uint8_t>` | `byte[]`         | `Uint8Array`       |
+| `date`      | `LocalDate` | `datetime.date`     | `time.Time` | `fory::Date` 
     | `fory::Date`           | `DateOnly`       | `Date`             |
+| `timestamp` | `Instant`   | `datetime.datetime` | `time.Time` | 
`fory::Timestamp` | `fory::Timestamp`      | `DateTimeOffset` | `Date`          
   |
 
 ### Collection Types
 
diff --git a/compiler/fory_compiler/frontend/fdl/parser.py 
b/compiler/fory_compiler/frontend/fdl/parser.py
index 2a8acf9eb..9ded037fe 100644
--- a/compiler/fory_compiler/frontend/fdl/parser.py
+++ b/compiler/fory_compiler/frontend/fdl/parser.py
@@ -54,6 +54,7 @@ KNOWN_FILE_OPTIONS: Set[str] = {
     "enable_auto_type_id",
     "go_nested_type_style",
     "swift_namespace_style",
+    "rust_use_chrono_temporal_types",
     "evolving",
 }
 
diff --git a/compiler/fory_compiler/generators/rust.py 
b/compiler/fory_compiler/generators/rust.py
index 64885b530..42fe2aae7 100644
--- a/compiler/fory_compiler/generators/rust.py
+++ b/compiler/fory_compiler/generators/rust.py
@@ -61,13 +61,35 @@ class RustGenerator(BaseGenerator):
         PrimitiveKind.FLOAT64: "f64",
         PrimitiveKind.STRING: "::std::string::String",
         PrimitiveKind.BYTES: "::std::vec::Vec<u8>",
+        PrimitiveKind.DECIMAL: "::fory::Decimal",
+        PrimitiveKind.ANY: "::std::boxed::Box<dyn ::std::any::Any>",
+    }
+
+    FORY_TEMPORAL_MAP = {
+        PrimitiveKind.DATE: "::fory::Date",
+        PrimitiveKind.TIMESTAMP: "::fory::Timestamp",
+        PrimitiveKind.DURATION: "::fory::Duration",
+    }
+
+    CHRONO_TEMPORAL_MAP = {
         PrimitiveKind.DATE: "::chrono::NaiveDate",
         PrimitiveKind.TIMESTAMP: "::chrono::NaiveDateTime",
         PrimitiveKind.DURATION: "::chrono::Duration",
-        PrimitiveKind.DECIMAL: "::fory::Decimal",
-        PrimitiveKind.ANY: "::std::boxed::Box<dyn ::std::any::Any>",
     }
 
+    def use_chrono_temporal_types(self) -> bool:
+        return self.schema.get_option("rust_use_chrono_temporal_types") is True
+
+    def primitive_type_name(self, kind: PrimitiveKind) -> str:
+        if kind in self.FORY_TEMPORAL_MAP:
+            temporal_map = (
+                self.CHRONO_TEMPORAL_MAP
+                if self.use_chrono_temporal_types()
+                else self.FORY_TEMPORAL_MAP
+            )
+            return temporal_map[kind]
+        return self.PRIMITIVE_MAP[kind]
+
     def generate(self) -> List[GeneratedFile]:
         """Generate Rust files for the schema."""
         files = []
@@ -723,7 +745,7 @@ class RustGenerator(BaseGenerator):
         if isinstance(field_type, PrimitiveType):
             if field_type.kind == PrimitiveKind.ANY:
                 return "::std::boxed::Box<dyn ::std::any::Any>"
-            base_type = self.PRIMITIVE_MAP[field_type.kind]
+            base_type = self.primitive_type_name(field_type.kind)
             if nullable:
                 return f"::std::option::Option<{base_type}>"
             return base_type
diff --git a/compiler/fory_compiler/tests/test_generated_code.py 
b/compiler/fory_compiler/tests/test_generated_code.py
index b4fde196b..754612454 100644
--- a/compiler/fory_compiler/tests/test_generated_code.py
+++ b/compiler/fory_compiler/tests/test_generated_code.py
@@ -154,6 +154,53 @@ def test_generated_code_scalar_types_equivalent():
     assert_all_languages_equal(schemas)
 
 
+def test_rust_generated_code_uses_fory_temporal_carriers():
+    schema = parse_fdl(
+        dedent(
+            """
+            package gen;
+
+            message TemporalTypes {
+                date day = 1;
+                timestamp instant = 2;
+                duration elapsed = 3;
+            }
+            """
+        )
+    )
+
+    rust_output = render_files(generate_files(schema, RustGenerator))
+    assert "pub day: ::fory::Date," in rust_output
+    assert "pub instant: ::fory::Timestamp," in rust_output
+    assert "pub elapsed: ::fory::Duration," in rust_output
+    assert "chrono::" not in rust_output
+
+
+def test_rust_generated_code_can_use_chrono_temporal_types():
+    schema = parse_fdl(
+        dedent(
+            """
+            package gen;
+            option rust_use_chrono_temporal_types = true;
+
+            message TemporalTypes {
+                date day = 1;
+                timestamp instant = 2;
+                duration elapsed = 3;
+            }
+            """
+        )
+    )
+
+    rust_output = render_files(generate_files(schema, RustGenerator))
+    assert "pub day: ::chrono::NaiveDate," in rust_output
+    assert "pub instant: ::chrono::NaiveDateTime," in rust_output
+    assert "pub elapsed: ::chrono::Duration," in rust_output
+    assert "::fory::Date" not in rust_output
+    assert "::fory::Timestamp" not in rust_output
+    assert "::fory::Duration" not in rust_output
+
+
 def test_generated_code_integer_encoding_variants_equivalent():
     fdl = dedent(
         """
diff --git a/docs/compiler/compiler-guide.md b/docs/compiler/compiler-guide.md
index a1c35e026..8cefb9abf 100644
--- a/docs/compiler/compiler-guide.md
+++ b/docs/compiler/compiler-guide.md
@@ -73,7 +73,14 @@ Compile options:
 | `--emit-fdl`                          | Emit translated FDL (for non-FDL 
inputs)              | `false`       |
 | `--emit-fdl-path`                     | Write translated FDL to this path 
(file or directory) | (stdout)      |
 
-For both `go_nested_type_style` and `swift_namespace_style`, schema-level file 
options are supported (`option ... = ...;`) and the CLI flag overrides the 
schema option when both are present.
+Schema-level file options are supported for language-specific generation 
choices.
+For `go_nested_type_style` and `swift_namespace_style`, the CLI flag overrides
+the schema option when both are present. Rust temporal codegen has no CLI flag:
+set `option rust_use_chrono_temporal_types = true;` in the schema to generate
+`chrono::NaiveDate`, `chrono::NaiveDateTime`, and `chrono::Duration` instead of
+the default `fory::Date`, `fory::Timestamp`, and `fory::Duration`. Crates that
+compile generated chrono-based Rust code must depend on `chrono` and enable
+Fory's `chrono` feature.
 
 Scan options (with `--scan-generated`):
 
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index b30ce9083..784bb9610 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -217,6 +217,29 @@ message Payment {
 
 The CLI flag `--swift_namespace_style` overrides this schema option when both 
are set.
 
+### Rust Chrono Temporal Types Option
+
+Rust generated code uses Fory's lightweight temporal carrier types by default:
+`fory::Date`, `fory::Timestamp`, and `fory::Duration`. Set
+`rust_use_chrono_temporal_types` when the generated Rust API should expose
+chrono temporal types instead:
+
+```protobuf
+package payment;
+option rust_use_chrono_temporal_types = true;
+
+message Event {
+    date business_day = 1;
+    timestamp created_at = 2;
+    duration timeout = 3;
+}
+```
+
+With this option, Rust code maps `date` to `chrono::NaiveDate`, `timestamp` to
+`chrono::NaiveDateTime`, and `duration` to `chrono::Duration`. The Rust crate
+that compiles the generated code must depend on `chrono` and enable Fory's
+`chrono` feature.
+
 ### Java Outer Classname Option
 
 Generate all types as inner classes of a single outer wrapper class:
@@ -1150,27 +1173,38 @@ Underscore spellings for integer encoding are not FDL 
type names.
 
 ##### Date
 
-| Language   | Type                        | Notes                   |
-| ---------- | --------------------------- | ----------------------- |
-| Java       | `java.time.LocalDate`       |                         |
-| Python     | `datetime.date`             |                         |
-| Go         | `time.Time`                 | Time portion ignored    |
-| Rust       | `chrono::NaiveDate`         | Requires `chrono` crate |
-| C++        | `fory::serialization::Date` |                         |
-| JavaScript | `Date`                      |                         |
-| Dart       | `LocalDate`                 | Fory package type       |
+| Language   | Type                        | Notes                             
                                          |
+| ---------- | --------------------------- | 
--------------------------------------------------------------------------- |
+| Java       | `java.time.LocalDate`       |                                   
                                          |
+| Python     | `datetime.date`             |                                   
                                          |
+| Go         | `time.Time`                 | Time portion ignored              
                                          |
+| Rust       | `fory::Date`                | Set 
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDate` |
+| C++        | `fory::serialization::Date` |                                   
                                          |
+| JavaScript | `Date`                      |                                   
                                          |
+| Dart       | `LocalDate`                 | Fory package type                 
                                          |
 
 ##### Timestamp
 
-| Language   | Type                             | Notes                   |
-| ---------- | -------------------------------- | ----------------------- |
-| Java       | `java.time.Instant`              | UTC-based               |
-| Python     | `datetime.datetime`              |                         |
-| Go         | `time.Time`                      |                         |
-| Rust       | `chrono::NaiveDateTime`          | Requires `chrono` crate |
-| C++        | `fory::serialization::Timestamp` |                         |
-| JavaScript | `Date`                           |                         |
-| Dart       | `Timestamp`                      | Fory package type       |
+| Language   | Type                             | Notes                        
                                                   |
+| ---------- | -------------------------------- | 
------------------------------------------------------------------------------- 
|
+| Java       | `java.time.Instant`              | UTC-based                    
                                                   |
+| Python     | `datetime.datetime`              |                              
                                                   |
+| Go         | `time.Time`                      |                              
                                                   |
+| Rust       | `fory::Timestamp`                | Set 
`rust_use_chrono_temporal_types = true` to generate `chrono::NaiveDateTime` |
+| C++        | `fory::serialization::Timestamp` |                              
                                                   |
+| JavaScript | `Date`                           |                              
                                                   |
+| Dart       | `Timestamp`                      | Fory package type            
                                                   |
+
+##### Duration
+
+| Language | Type                            | Notes                           
                                           |
+| -------- | ------------------------------- | 
-------------------------------------------------------------------------- |
+| Java     | `java.time.Duration`            |                                 
                                           |
+| Python   | `datetime.timedelta`            |                                 
                                           |
+| Go       | `time.Duration`                 |                                 
                                           |
+| Rust     | `fory::Duration`                | Set 
`rust_use_chrono_temporal_types = true` to generate `chrono::Duration` |
+| C++      | `fory::serialization::Duration` |                                 
                                           |
+| Dart     | `Duration`                      |                                 
                                           |
 
 #### Any
 
diff --git a/docs/guide/rust/basic-serialization.md 
b/docs/guide/rust/basic-serialization.md
index 2d2386f5a..63bb9a355 100644
--- a/docs/guide/rust/basic-serialization.md
+++ b/docs/guide/rust/basic-serialization.md
@@ -119,10 +119,36 @@ assert_eq!(person, decoded);
 
 ### Date and Time
 
-| Rust Type               | Description                |
-| ----------------------- | -------------------------- |
-| `chrono::NaiveDate`     | Date without timezone      |
-| `chrono::NaiveDateTime` | Timestamp without timezone |
+| Rust Type   | Description                                             |
+| ----------- | ------------------------------------------------------- |
+| `Date`      | Date without timezone, stored as epoch days             |
+| `Timestamp` | Point in time, stored as epoch seconds and nanos        |
+| `Duration`  | Signed duration, stored as seconds and normalized nanos |
+
+The built-in carriers expose dependency-free constructors, accessors, 
conversions, and checked
+arithmetic:
+
+```rust
+use fory::{Date, Duration, Timestamp};
+
+let date = Date::from_epoch_days(19_782);
+assert_eq!(date.checked_add_days(1)?.epoch_days(), 19_783);
+
+let timestamp = Timestamp::from_epoch_millis(-1);
+assert_eq!(timestamp.to_epoch_millis()?, -1);
+
+let duration = Duration::from_parts(1, 1_500_000_000)?;
+assert_eq!(duration.to_millis()?, 2_500);
+let later = timestamp.checked_add_duration(duration)?;
+```
+
+`chrono::NaiveDate`, `chrono::NaiveDateTime`, and `chrono::Duration` are 
supported when the Rust
+`chrono` feature is enabled:
+
+```toml
+[dependencies]
+fory = { version = "0.13", features = ["chrono"] }
+```
 
 ### Custom Types
 
diff --git a/docs/specification/xlang_type_mapping.md 
b/docs/specification/xlang_type_mapping.md
index a2a49ebfb..96b1e6a72 100644
--- a/docs/specification/xlang_type_mapping.md
+++ b/docs/specification/xlang_type_mapping.md
@@ -89,8 +89,8 @@ FDL spells them as an encoding modifier plus a semantic 
integer type.
 | union                              | 33           | Union                    
                 | typing.Union                              | /                
                     | `std::variant<Ts...>`                               | /  
                                            | tagged union enum                 
|
 | none                               | 36           | null                     
                 | None                                      | null             
                     | `std::monostate`                                    | 
nil                                            | `()`                           
   |
 | duration                           | 37           | Duration                 
                 | timedelta                                 | Number           
                     | duration                                            | 
Duration                                       | Duration                       
   |
-| timestamp                          | 38           | Instant                  
                 | datetime                                  | Number           
                     | std::chrono::nanoseconds                            | 
Time                                           | DateTime                       
   |
-| date                               | 39           | LocalDate                
                 | datetime.date                             | Date             
                     | fory::serialization::Date                           | 
fory.Date                                      | chrono::NaiveDate              
   |
+| timestamp                          | 38           | Instant                  
                 | datetime                                  | Number           
                     | std::chrono::nanoseconds                            | 
Time                                           | Timestamp                      
   |
+| date                               | 39           | LocalDate                
                 | datetime.date                             | Date             
                     | fory::serialization::Date                           | 
fory.Date                                      | Date                           
   |
 | decimal                            | 40           | BigDecimal               
                 | Decimal                                   | Decimal          
                     | /                                                   | 
fory.Decimal                                   | fory::Decimal                  
   |
 | binary                             | 41           | byte[]                   
                 | bytes                                     | /                
                     | `uint8_t[n]/vector<T>`                              | 
`[n]uint8/[]T`                                 | `Vec<u8>`                      
   |
 | `array<bool>` (bool_array)         | 43           | bool[]                   
                 | BoolArray / ndarray(np.bool\_)            | BoolArray / 
Type.boolArray()          | `bool[n]`                                           
| `[n]bool/[]T`                                  | `Vec<bool>`                  
     |
diff --git a/integration_tests/idl_tests/idl/example.fdl 
b/integration_tests/idl_tests/idl/example.fdl
index 4ade5cd85..bf635448b 100644
--- a/integration_tests/idl_tests/idl/example.fdl
+++ b/integration_tests/idl_tests/idl/example.fdl
@@ -20,6 +20,7 @@ package example;
 
 option go_package = 
"github.com/apache/fory/integration_tests/idl_tests/go/example/generated;example";
 option evolving = false;
+option rust_use_chrono_temporal_types = true;
 
 enum ExampleState [id=1504] {
     UNKNOWN = 0;
diff --git a/integration_tests/idl_tests/idl/optional_types.fdl 
b/integration_tests/idl_tests/idl/optional_types.fdl
index fba9a28b3..fb83bfd63 100644
--- a/integration_tests/idl_tests/idl/optional_types.fdl
+++ b/integration_tests/idl_tests/idl/optional_types.fdl
@@ -17,6 +17,8 @@
 
 package optional_types;
 
+option rust_use_chrono_temporal_types = true;
+
 message AllOptionalTypes [id=120] {
     optional bool bool_value = 1;
     optional int8 int8_value = 2;
diff --git a/integration_tests/idl_tests/rust/Cargo.lock 
b/integration_tests/idl_tests/rust/Cargo.lock
index 72f12773b..0eb6a8bd7 100644
--- a/integration_tests/idl_tests/rust/Cargo.lock
+++ b/integration_tests/idl_tests/rust/Cargo.lock
@@ -93,9 +93,6 @@ dependencies = [
  "num-bigint",
  "num_enum",
  "paste",
- "proc-macro2",
- "quote",
- "syn 2.0.114",
  "thiserror",
 ]
 
@@ -107,7 +104,6 @@ dependencies = [
  "proc-macro2",
  "quote",
  "syn 2.0.114",
- "thiserror",
 ]
 
 [[package]]
diff --git a/integration_tests/idl_tests/rust/Cargo.toml 
b/integration_tests/idl_tests/rust/Cargo.toml
index 66f3fbd28..d15105f2b 100644
--- a/integration_tests/idl_tests/rust/Cargo.toml
+++ b/integration_tests/idl_tests/rust/Cargo.toml
@@ -23,5 +23,5 @@ license = "Apache-2.0"
 
 [dependencies]
 chrono = "0.4"
-fory = { path = "../../../rust/fory" }
+fory = { path = "../../../rust/fory", features = ["chrono"] }
 fory-core = { path = "../../../rust/fory-core" }
diff --git a/rust/fory-core/Cargo.toml b/rust/fory-core/Cargo.toml
index 6a96e6f8d..9fae8c6ff 100644
--- a/rust/fory-core/Cargo.toml
+++ b/rust/fory-core/Cargo.toml
@@ -28,16 +28,17 @@ keywords = ["serialization", "serde", "trait-object", 
"zero-copy", "schema-evolu
 categories = ["encoding"]
 
 [dependencies]
-proc-macro2 = { default-features = false, version = "1.0" }
-syn = { default-features = false, version = "2.0", features = ["full", "fold"] 
}
-quote = { default-features = false, version = "1.0" }
 byteorder = { version = "1.4" }
-chrono = "0.4"
+chrono = { version = "0.4", default-features = false, features = ["std"], 
optional = true }
 thiserror = { default-features = false, version = "1.0" }
 num_enum = "0.5.1"
 paste = "1.0"
 num-bigint = "0.4"
 
+[features]
+default = []
+chrono = ["dep:chrono"]
+tests = []
 
 [[bench]]
 name = "simd_bench"
diff --git a/rust/fory-core/src/lib.rs b/rust/fory-core/src/lib.rs
index 4d1ac219b..65716af4a 100644
--- a/rust/fory-core/src/lib.rs
+++ b/rust/fory-core/src/lib.rs
@@ -32,7 +32,7 @@
 //! - **`serializer`**: Type-specific serialization implementations
 //! - **`resolver`**: Type resolution and metadata management
 //! - **`meta`**: Metadata handling for schema evolution
-//! - **`types`**: Runtime value carriers such as decimal, Float16, BFloat16, 
and weak refs
+//! - **`types`**: Runtime value carriers such as temporal values, decimal, 
Float16, BFloat16, and weak refs
 //! - **`type_id`**: Type IDs and protocol header helpers
 //! - **`error`**: Error handling and result types
 //! - **`util`**: Utility functions and helpers
@@ -52,7 +52,7 @@
 //! - Primitive types (bool, integers, floats, strings)
 //! - Collections (Vec, HashMap, BTreeMap)
 //! - Optional types (`Option<T>`)
-//! - Date/time types (chrono integration)
+//! - Date/time carriers with optional chrono integration
 //! - Custom structs and enums
 //! - Trait objects (Box, Rc, Arc)
 //!
@@ -205,4 +205,4 @@ pub use crate::serializer::{read_data, write_data, 
ForyDefault, Serializer, Stru
 pub use crate::type_id::TypeId;
 pub use crate::types::bfloat16::bfloat16 as BFloat16;
 pub use crate::types::float16::float16 as Float16;
-pub use crate::types::{ArcWeak, Decimal, RcWeak};
+pub use crate::types::{ArcWeak, Date, Decimal, Duration, RcWeak, Timestamp};
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 3fcacda79..7983cc0c8 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -24,8 +24,10 @@ use crate::meta::{
 use crate::resolver::RefMode;
 use crate::serializer::{ForyDefault, Serializer, StructSerializer};
 use crate::type_id::{get_ext_actual_type_id, is_enum_type_id};
+use crate::types::{Date, Duration, Timestamp};
 use crate::TypeId;
-use chrono::{NaiveDate, NaiveDateTime};
+#[cfg(feature = "chrono")]
+use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
 use std::collections::{HashSet, LinkedList};
 use std::rc::Rc;
 use std::vec;
@@ -771,8 +773,15 @@ impl TypeResolver {
         self.register_internal_serializer::<usize>(TypeId::USIZE)?;
         self.register_internal_serializer::<u128>(TypeId::U128)?;
         self.register_internal_serializer::<String>(TypeId::STRING)?;
-        self.register_internal_serializer::<NaiveDateTime>(TypeId::TIMESTAMP)?;
-        self.register_internal_serializer::<NaiveDate>(TypeId::DATE)?;
+        #[cfg(feature = "chrono")]
+        {
+            
self.register_internal_serializer::<ChronoDuration>(TypeId::DURATION)?;
+            
self.register_internal_serializer::<NaiveDateTime>(TypeId::TIMESTAMP)?;
+            self.register_internal_serializer::<NaiveDate>(TypeId::DATE)?;
+        }
+        self.register_internal_serializer::<Duration>(TypeId::DURATION)?;
+        self.register_internal_serializer::<Timestamp>(TypeId::TIMESTAMP)?;
+        self.register_internal_serializer::<Date>(TypeId::DATE)?;
         
self.register_internal_serializer::<crate::types::Decimal>(TypeId::DECIMAL)?;
 
         self.register_internal_serializer::<Vec<bool>>(TypeId::BOOL_ARRAY)?;
diff --git a/rust/fory-core/src/row/row.rs b/rust/fory-core/src/row/row.rs
index 147a2f110..8f6e5181c 100644
--- a/rust/fory-core/src/row/row.rs
+++ b/rust/fory-core/src/row/row.rs
@@ -15,10 +15,9 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use crate::util::EPOCH;
+use crate::types::{Date, Duration, Timestamp};
 use crate::{buffer::Writer, error::Error};
 use byteorder::{ByteOrder, LittleEndian};
-use chrono::{DateTime, Days, NaiveDate, NaiveDateTime};
 use std::collections::BTreeMap;
 use std::marker::PhantomData;
 
@@ -133,40 +132,50 @@ impl<'a, T: Row<'a>, const N: usize> Row<'a> for [T; N] {
     }
 }
 
-impl Row<'_> for NaiveDate {
-    type ReadResult = Result<NaiveDate, Error>;
+impl Row<'_> for Date {
+    type ReadResult = Result<Date, Error>;
 
     fn write(v: &Self, writer: &mut Writer) -> Result<(), Error> {
-        let days_since_epoch = v.signed_duration_since(EPOCH).num_days();
-        writer.write_u32(days_since_epoch as u32);
+        let days = i32::try_from(v.epoch_days()).map_err(|_| {
+            Error::invalid_data(format!(
+                "row date day count {} exceeds date32 range",
+                v.epoch_days()
+            ))
+        })?;
+        writer.write_i32(days);
         Ok(())
     }
 
     fn cast(bytes: &[u8]) -> Self::ReadResult {
-        let days = LittleEndian::read_u32(bytes);
-        EPOCH
-            .checked_add_days(Days::new(days.into()))
-            .ok_or(Error::invalid_data(format!(
-                "Date out of range, {days} days since epoch"
-            )))
+        Ok(Date::from_epoch_days(i64::from(LittleEndian::read_i32(
+            bytes,
+        ))))
     }
 }
 
-impl Row<'_> for NaiveDateTime {
-    type ReadResult = Result<NaiveDateTime, Error>;
+impl Row<'_> for Timestamp {
+    type ReadResult = Result<Timestamp, Error>;
 
     fn write(v: &Self, writer: &mut Writer) -> Result<(), Error> {
-        writer.write_i64(v.and_utc().timestamp_millis());
+        writer.write_i64(v.to_epoch_micros()?);
         Ok(())
     }
 
     fn cast(bytes: &[u8]) -> Self::ReadResult {
-        let timestamp = LittleEndian::read_u64(bytes);
-        DateTime::from_timestamp_millis(timestamp as i64)
-            .map(|dt| dt.naive_utc())
-            .ok_or(Error::invalid_data(format!(
-                "Date out of range, timestamp:{timestamp}"
-            )))
+        Ok(Timestamp::from_epoch_micros(LittleEndian::read_i64(bytes)))
+    }
+}
+
+impl Row<'_> for Duration {
+    type ReadResult = Result<Duration, Error>;
+
+    fn write(v: &Self, writer: &mut Writer) -> Result<(), Error> {
+        writer.write_i64(v.to_micros()?);
+        Ok(())
+    }
+
+    fn cast(bytes: &[u8]) -> Self::ReadResult {
+        Ok(Duration::from_micros(LittleEndian::read_i64(bytes)))
     }
 }
 
diff --git a/rust/fory-core/src/serializer/datetime.rs 
b/rust/fory-core/src/serializer/datetime.rs
index 5823d0c7c..efa757245 100644
--- a/rust/fory-core/src/serializer/datetime.rs
+++ b/rust/fory-core/src/serializer/datetime.rs
@@ -23,19 +23,14 @@ use crate::serializer::util::read_basic_type_info;
 use crate::serializer::ForyDefault;
 use crate::serializer::Serializer;
 use crate::type_id::TypeId;
-use crate::util::EPOCH;
-use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime, TimeDelta};
+use crate::types::{Date, Duration, Timestamp};
 use std::mem;
-use std::time::Duration;
 
-impl Serializer for NaiveDateTime {
+impl Serializer for Timestamp {
     #[inline(always)]
     fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> 
{
-        let dt = self.and_utc();
-        let seconds = dt.timestamp();
-        let nanos = dt.timestamp_subsec_nanos();
-        context.writer.write_i64(seconds);
-        context.writer.write_u32(nanos);
+        context.writer.write_i64(self.seconds());
+        context.writer.write_u32(self.subsec_nanos());
         Ok(())
     }
 
@@ -43,9 +38,7 @@ impl Serializer for NaiveDateTime {
     fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
         let seconds = context.reader.read_i64()?;
         let nanos = context.reader.read_u32()?;
-        #[allow(deprecated)]
-        let result = NaiveDateTime::from_timestamp(seconds, nanos);
-        Ok(result)
+        Timestamp::new(seconds, nanos)
     }
 
     #[inline(always)]
@@ -85,18 +78,22 @@ impl Serializer for NaiveDateTime {
     }
 }
 
-impl Serializer for NaiveDate {
+impl ForyDefault for Timestamp {
+    #[inline(always)]
+    fn fory_default() -> Self {
+        Timestamp::default()
+    }
+}
+
+impl Serializer for Date {
     #[inline(always)]
     fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> 
{
-        let days_since_epoch = self.signed_duration_since(EPOCH).num_days();
+        let days = self.epoch_days();
         if context.is_xlang() {
-            context.writer.write_var_i64(days_since_epoch);
+            context.writer.write_var_i64(days);
         } else {
-            let native_days = i32::try_from(days_since_epoch).map_err(|_| {
-                Error::invalid_data(format!(
-                    "date day count {} exceeds native i32 range",
-                    days_since_epoch
-                ))
+            let native_days = i32::try_from(days).map_err(|_| {
+                Error::invalid_data(format!("date day count {} exceeds native 
i32 range", days))
             })?;
             context.writer.write_i32(native_days);
         }
@@ -110,18 +107,7 @@ impl Serializer for NaiveDate {
         } else {
             i64::from(context.reader.read_i32()?)
         };
-        let duration = TimeDelta::try_days(days).ok_or_else(|| {
-            Error::invalid_data(format!(
-                "date day count {} is out of chrono::TimeDelta range",
-                days
-            ))
-        })?;
-        EPOCH.checked_add_signed(duration).ok_or_else(|| {
-            Error::invalid_data(format!(
-                "date day count {} is out of chrono::NaiveDate range",
-                days
-            ))
-        })
+        Ok(Date::from_epoch_days(days))
     }
 
     #[inline(always)]
@@ -161,62 +147,31 @@ impl Serializer for NaiveDate {
     }
 }
 
-impl ForyDefault for NaiveDateTime {
+impl ForyDefault for Date {
     #[inline(always)]
     fn fory_default() -> Self {
-        NaiveDateTime::default()
-    }
-}
-
-impl ForyDefault for NaiveDate {
-    #[inline(always)]
-    fn fory_default() -> Self {
-        NaiveDate::default()
+        Date::default()
     }
 }
 
 impl Serializer for Duration {
     #[inline(always)]
     fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> 
{
-        let raw = self.as_secs();
-        if raw > i64::MAX as u64 {
-            return Err(Error::invalid_data(format!(
-                "std::time::Duration seconds {} exceeds i64::MAX and cannot be 
encoded with write_var_i64",
-                raw
-            )));
-        }
-        let secs = raw as i64;
-        let nanos = self.subsec_nanos() as i32;
-        context.writer.write_var_i64(secs);
-        context.writer.write_i32(nanos);
+        context.writer.write_var_i64(self.seconds());
+        context.writer.write_i32(self.subsec_nanos() as i32);
         Ok(())
     }
 
     #[inline(always)]
     fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
-        let secs = context.reader.read_var_i64()?;
-        if secs < 0 {
-            return Err(Error::invalid_data(format!(
-                "negative duration seconds {} cannot be represented as 
std::time::Duration; use chrono::Duration instead",
-                secs
-            )));
-        }
+        let seconds = context.reader.read_var_i64()?;
         let nanos = context.reader.read_i32()?;
-        if !(0..=999_999_999).contains(&nanos) {
-            // negative nanos will also be rejected, even though the xlang 
spec actually allows it.
-            // RFC 1040 
(https://rust-lang.github.io/rfcs/1040-duration-reform.html#detailed-design) 
explicitly forbids negative nanoseconds.
-            // If supporting for negative nanoseconds is really needed, we can 
implement **normalization** similar to chrono and Java.
-            return Err(Error::invalid_data(format!(
-                "duration nanoseconds {} out of valid range [0, 999_999_999] 
for std::time::Duration",
-                nanos
-            )));
-        }
-        Ok(Duration::new(secs as u64, nanos as u32))
+        Duration::new(seconds, nanos)
     }
 
     #[inline(always)]
     fn fory_reserved_space() -> usize {
-        9 + mem::size_of::<i32>() // max write_var_i64 payload is 9 bytes + 4 
bytes for i32
+        9 + mem::size_of::<i32>()
     }
 
     #[inline(always)]
@@ -254,82 +209,175 @@ impl Serializer for Duration {
 impl ForyDefault for Duration {
     #[inline(always)]
     fn fory_default() -> Self {
-        Duration::ZERO
+        Duration::default()
     }
 }
 
-impl Serializer for ChronoDuration {
-    #[inline(always)]
-    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> 
{
-        let secs = self.num_seconds();
-        let nanos = self.subsec_nanos();
-        context.writer.write_var_i64(secs);
-        context.writer.write_i32(nanos);
-        Ok(())
-    }
+#[cfg(feature = "chrono")]
+mod chrono_support {
+    use super::*;
+    use chrono::{Duration as ChronoDuration, NaiveDate, NaiveDateTime};
 
-    #[inline(always)]
-    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
-        let secs = context.reader.read_var_i64()?;
-        let nanos = context.reader.read_i32()?;
-        if !(-999_999_999..=999_999_999).contains(&nanos) {
-            // chrono supports negative nanoseconds by applying normalization 
internally.
-            return Err(Error::invalid_data(format!(
-                "duration nanoseconds {} out of valid range [-999_999_999, 
999_999_999]",
-                nanos
-            )));
-        }
-        ChronoDuration::try_seconds(secs) // the maximum seconds chrono 
supports is i64::MAX / 1_000, which is smaller than what the spec 
allows(i64::MAX)
-            .and_then(|d| d.checked_add(&ChronoDuration::nanoseconds(nanos as 
i64)))
-            .ok_or_else(|| {
-                Error::invalid_data(format!(
-                    "duration seconds {} out of chrono::Duration valid range",
-                    secs
-                ))
-            })
-    }
+    impl Serializer for NaiveDateTime {
+        #[inline(always)]
+        fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), 
Error> {
+            Timestamp::from(*self).fory_write_data(context)
+        }
 
-    #[inline(always)]
-    fn fory_reserved_space() -> usize {
-        9 + mem::size_of::<i32>() // max write_var_i64 payload is 9 bytes + 4 
bytes for i32
-    }
+        #[inline(always)]
+        fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
+            Timestamp::fory_read_data(context)?.try_into()
+        }
 
-    #[inline(always)]
-    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
-        Ok(TypeId::DURATION)
-    }
+        #[inline(always)]
+        fn fory_reserved_space() -> usize {
+            Timestamp::fory_reserved_space()
+        }
 
-    #[inline(always)]
-    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
-        Ok(TypeId::DURATION)
+        #[inline(always)]
+        fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::TIMESTAMP)
+        }
+
+        #[inline(always)]
+        fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::TIMESTAMP)
+        }
+
+        #[inline(always)]
+        fn fory_static_type_id() -> TypeId {
+            TypeId::TIMESTAMP
+        }
+
+        #[inline(always)]
+        fn as_any(&self) -> &dyn std::any::Any {
+            self
+        }
+
+        #[inline(always)]
+        fn fory_write_type_info(context: &mut WriteContext) -> Result<(), 
Error> {
+            Timestamp::fory_write_type_info(context)
+        }
+
+        #[inline(always)]
+        fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> 
{
+            read_basic_type_info::<Self>(context)
+        }
     }
 
-    #[inline(always)]
-    fn fory_static_type_id() -> TypeId {
-        TypeId::DURATION
+    impl ForyDefault for NaiveDateTime {
+        #[inline(always)]
+        fn fory_default() -> Self {
+            NaiveDateTime::default()
+        }
     }
 
-    #[inline(always)]
-    fn as_any(&self) -> &dyn std::any::Any {
-        self
+    impl Serializer for NaiveDate {
+        #[inline(always)]
+        fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), 
Error> {
+            Date::from(*self).fory_write_data(context)
+        }
+
+        #[inline(always)]
+        fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
+            Date::fory_read_data(context)?.try_into()
+        }
+
+        #[inline(always)]
+        fn fory_reserved_space() -> usize {
+            Date::fory_reserved_space()
+        }
+
+        #[inline(always)]
+        fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::DATE)
+        }
+
+        #[inline(always)]
+        fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::DATE)
+        }
+
+        #[inline(always)]
+        fn fory_static_type_id() -> TypeId {
+            TypeId::DATE
+        }
+
+        #[inline(always)]
+        fn as_any(&self) -> &dyn std::any::Any {
+            self
+        }
+
+        #[inline(always)]
+        fn fory_write_type_info(context: &mut WriteContext) -> Result<(), 
Error> {
+            Date::fory_write_type_info(context)
+        }
+
+        #[inline(always)]
+        fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> 
{
+            read_basic_type_info::<Self>(context)
+        }
     }
 
-    #[inline(always)]
-    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
-        context.writer.write_u8(TypeId::DURATION as u8);
-        Ok(())
+    impl ForyDefault for NaiveDate {
+        #[inline(always)]
+        fn fory_default() -> Self {
+            NaiveDate::default()
+        }
     }
 
-    #[inline(always)]
-    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
-        read_basic_type_info::<Self>(context)
+    impl Serializer for ChronoDuration {
+        #[inline(always)]
+        fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), 
Error> {
+            Duration::try_from(*self)?.fory_write_data(context)
+        }
+
+        #[inline(always)]
+        fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
+            Duration::fory_read_data(context)?.try_into()
+        }
+
+        #[inline(always)]
+        fn fory_reserved_space() -> usize {
+            Duration::fory_reserved_space()
+        }
+
+        #[inline(always)]
+        fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::DURATION)
+        }
+
+        #[inline(always)]
+        fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
+            Ok(TypeId::DURATION)
+        }
+
+        #[inline(always)]
+        fn fory_static_type_id() -> TypeId {
+            TypeId::DURATION
+        }
+
+        #[inline(always)]
+        fn as_any(&self) -> &dyn std::any::Any {
+            self
+        }
+
+        #[inline(always)]
+        fn fory_write_type_info(context: &mut WriteContext) -> Result<(), 
Error> {
+            Duration::fory_write_type_info(context)
+        }
+
+        #[inline(always)]
+        fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> 
{
+            read_basic_type_info::<Self>(context)
+        }
     }
-}
 
-impl ForyDefault for ChronoDuration {
-    #[inline(always)]
-    fn fory_default() -> Self {
-        ChronoDuration::zero()
+    impl ForyDefault for ChronoDuration {
+        #[inline(always)]
+        fn fory_default() -> Self {
+            ChronoDuration::zero()
+        }
     }
 }
 
@@ -339,80 +387,72 @@ mod tests {
     use crate::fory::Fory;
 
     #[test]
-    fn test_std_duration_serialization() {
+    fn test_temporal_carrier_serialization() {
         let fory = Fory::default();
 
-        // Test various durations
-        let test_cases = vec![
-            Duration::ZERO,
-            Duration::new(0, 0),
-            Duration::new(1, 0),
-            Duration::new(0, 1),
-            Duration::new(123, 456789),
-            Duration::new(i64::MAX as u64, 999_999_999),
+        let timestamps = [
+            Timestamp::UNIX_EPOCH,
+            Timestamp::new(1, 0).unwrap(),
+            Timestamp::new(-1, 999_999_999).unwrap(),
         ];
-
-        for duration in test_cases {
-            let bytes = fory.serialize(&duration).unwrap();
-            let deserialized: Duration = fory.deserialize(&bytes).unwrap();
-            assert_eq!(
-                duration, deserialized,
-                "Failed for duration: {:?}",
-                duration
-            );
+        for timestamp in timestamps {
+            let bytes = fory.serialize(&timestamp).unwrap();
+            let deserialized: Timestamp = fory.deserialize(&bytes).unwrap();
+            assert_eq!(timestamp, deserialized);
         }
-    }
-
-    #[test]
-    fn test_chrono_duration_serialization() {
-        let fory = Fory::default();
 
-        // Test various durations
-        let test_cases = vec![
-            ChronoDuration::zero(),
-            ChronoDuration::new(0, 0).unwrap(),
-            ChronoDuration::new(1, 0).unwrap(),
-            ChronoDuration::new(0, 1).unwrap(),
-            ChronoDuration::new(123, 456789).unwrap(),
-            ChronoDuration::seconds(-1),
-            ChronoDuration::nanoseconds(-1),
-            ChronoDuration::microseconds(-456789),
-            ChronoDuration::MAX,
-            ChronoDuration::MIN,
+        let dates = [
+            Date::UNIX_EPOCH,
+            Date::from_epoch_days(-1),
+            Date::from_epoch_days(18_628),
         ];
+        for date in dates {
+            let bytes = fory.serialize(&date).unwrap();
+            let deserialized: Date = fory.deserialize(&bytes).unwrap();
+            assert_eq!(date, deserialized);
+        }
 
-        for duration in test_cases {
+        let durations = [
+            Duration::ZERO,
+            Duration::new(1, 0).unwrap(),
+            Duration::new(0, -1).unwrap(),
+            Duration::new(-123, 456_789).unwrap(),
+        ];
+        for duration in durations {
             let bytes = fory.serialize(&duration).unwrap();
-            let deserialized: ChronoDuration = 
fory.deserialize(&bytes).unwrap();
-            assert_eq!(
-                duration, deserialized,
-                "Failed for duration: {:?}",
-                duration
-            );
+            let deserialized: Duration = fory.deserialize(&bytes).unwrap();
+            assert_eq!(duration, deserialized);
         }
     }
 
     #[test]
-    fn test_chrono_duration_out_of_range_is_error() {
-        let fory = Fory::default();
-        let too_large = Duration::new(i64::MAX as u64, 0);
-        let bytes = fory.serialize(&too_large).unwrap();
-        let result: Result<ChronoDuration, _> = fory.deserialize(&bytes);
-        assert!(
-            result.is_err(),
-            "out-of-range seconds should not be deserialized into 
chrono::Duration!"
+    fn test_duration_normalizes_negative_nanoseconds() {
+        assert_eq!(
+            Duration::new(0, -1).unwrap(),
+            Duration::from_normalized(-1, 999_999_999).unwrap()
         );
     }
 
+    #[cfg(feature = "chrono")]
     #[test]
-    fn test_negative_std_duration_read_is_error() {
+    fn test_chrono_temporal_feature_serialization() {
+        use chrono::{DateTime, Duration as ChronoDuration, NaiveDate, 
NaiveDateTime};
+
         let fory = Fory::default();
-        let negative_duration = ChronoDuration::seconds(-1);
-        let bytes = fory.serialize(&negative_duration).unwrap();
-        let result: Result<Duration, _> = fory.deserialize(&bytes);
-        assert!(
-            result.is_err(),
-            "negative duration should not be deserialized into 
std::time::Duration!"
-        );
+        let date = NaiveDate::from_ymd_opt(2024, 2, 3).unwrap();
+        let timestamp = DateTime::from_timestamp(100, 1).unwrap().naive_utc();
+        let duration = ChronoDuration::nanoseconds(-1);
+
+        let bytes = fory.serialize(&date).unwrap();
+        let deserialized: NaiveDate = fory.deserialize(&bytes).unwrap();
+        assert_eq!(date, deserialized);
+
+        let bytes = fory.serialize(&timestamp).unwrap();
+        let deserialized: NaiveDateTime = fory.deserialize(&bytes).unwrap();
+        assert_eq!(timestamp, deserialized);
+
+        let bytes = fory.serialize(&duration).unwrap();
+        let deserialized: ChronoDuration = fory.deserialize(&bytes).unwrap();
+        assert_eq!(duration, deserialized);
     }
 }
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
index 944dadf34..4d8b90651 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -23,9 +23,9 @@ use crate::serializer::collection::{DECL_ELEMENT_TYPE, 
HAS_NULL, IS_SAME_TYPE};
 use crate::serializer::util;
 use crate::serializer::Serializer;
 use crate::type_id as types;
+use crate::types::{Date, Duration, Timestamp};
 use crate::util::ENABLE_FORY_DEBUG_OUTPUT;
 use crate::RefFlag;
-use chrono::{Duration, NaiveDate, NaiveDateTime};
 use std::rc::Rc;
 
 #[allow(unreachable_code)]
@@ -608,12 +608,12 @@ fn skip_value(
 
         // ============ TIMESTAMP (TypeId = 36) ============
         types::TIMESTAMP => {
-            <NaiveDateTime as Serializer>::fory_read_data(context)?;
+            <Timestamp as Serializer>::fory_read_data(context)?;
         }
 
         // ============ DATE (TypeId = 37) ============
         types::DATE => {
-            <NaiveDate as Serializer>::fory_read_data(context)?;
+            <Date as Serializer>::fory_read_data(context)?;
         }
 
         // ============ DECIMAL (TypeId = 38) ============
diff --git a/rust/fory-core/src/type_id.rs b/rust/fory-core/src/type_id.rs
index 5c7ed103e..9b7db9e0d 100644
--- a/rust/fory-core/src/type_id.rs
+++ b/rust/fory-core/src/type_id.rs
@@ -184,7 +184,7 @@ pub const fn is_enum_type_id(type_id: TypeId) -> bool {
     matches!(type_id, TypeId::ENUM | TypeId::NAMED_ENUM | TypeId::UNION)
 }
 
-pub static BASIC_TYPES: [TypeId; 34] = [
+pub static BASIC_TYPES: [TypeId; 35] = [
     TypeId::BOOL,
     TypeId::INT8,
     TypeId::INT16,
@@ -200,6 +200,7 @@ pub static BASIC_TYPES: [TypeId; 34] = [
     TypeId::STRING,
     TypeId::DATE,
     TypeId::TIMESTAMP,
+    TypeId::DURATION,
     TypeId::BOOL_ARRAY,
     TypeId::BINARY,
     TypeId::INT8_ARRAY,
@@ -272,7 +273,7 @@ pub static PRIMITIVE_ARRAY_TYPES: [u32; 19] = [
     TypeId::USIZE_ARRAY as u32,
     TypeId::ISIZE_ARRAY as u32,
 ];
-pub static BASIC_TYPE_NAMES: [&str; 20] = [
+pub static BASIC_TYPE_NAMES: [&str; 21] = [
     "bool",
     "i8",
     "i16",
@@ -282,8 +283,9 @@ pub static BASIC_TYPE_NAMES: [&str; 20] = [
     "f32",
     "f64",
     "String",
-    "NaiveDate",
-    "NaiveDateTime",
+    "Date",
+    "Timestamp",
+    "Duration",
     "u8",
     "u16",
     "u32",
diff --git a/rust/fory-core/src/types/mod.rs b/rust/fory-core/src/types/mod.rs
index c204d91f1..673d004c6 100644
--- a/rust/fory-core/src/types/mod.rs
+++ b/rust/fory-core/src/types/mod.rs
@@ -18,7 +18,9 @@
 pub mod bfloat16;
 pub mod decimal;
 pub mod float16;
+pub mod temporal;
 pub mod weak;
 
 pub use decimal::Decimal;
+pub use temporal::{Date, Duration, Timestamp};
 pub use weak::{ArcWeak, RcWeak};
diff --git a/rust/fory-core/src/types/temporal.rs 
b/rust/fory-core/src/types/temporal.rs
new file mode 100644
index 000000000..de3eb36be
--- /dev/null
+++ b/rust/fory-core/src/types/temporal.rs
@@ -0,0 +1,763 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::error::Error;
+
+const NANOS_PER_SECOND: i32 = 1_000_000_000;
+const NANOS_PER_SECOND_I128: i128 = NANOS_PER_SECOND as i128;
+const NANOS_PER_MILLI: i128 = 1_000_000;
+const NANOS_PER_MICRO: i128 = 1_000;
+const MILLIS_PER_SECOND: i64 = 1_000;
+const MICROS_PER_SECOND: i64 = 1_000_000;
+const SECONDS_PER_MINUTE: i64 = 60;
+const SECONDS_PER_HOUR: i64 = 60 * SECONDS_PER_MINUTE;
+const SECONDS_PER_DAY: i64 = 24 * SECONDS_PER_HOUR;
+
+/// Date without timezone, represented as signed days since Unix epoch.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Date {
+    days: i64,
+}
+
+impl Date {
+    pub const UNIX_EPOCH: Self = Self { days: 0 };
+
+    /// Creates a date from signed days since 1970-01-01.
+    #[inline(always)]
+    pub const fn from_epoch_days(days: i64) -> Self {
+        Self { days }
+    }
+
+    /// Returns the signed day count from 1970-01-01.
+    #[inline(always)]
+    pub const fn epoch_days(self) -> i64 {
+        self.days
+    }
+
+    /// Returns a date offset by `days`.
+    #[inline(always)]
+    pub fn checked_add_days(self, days: i64) -> Result<Self, Error> {
+        self.days
+            .checked_add(days)
+            .map(Self::from_epoch_days)
+            .ok_or_else(|| {
+                Error::invalid_data(format!(
+                    "date day count {} overflow adding {} days",
+                    self.days, days
+                ))
+            })
+    }
+
+    /// Returns a date offset backward by `days`.
+    #[inline(always)]
+    pub fn checked_sub_days(self, days: i64) -> Result<Self, Error> {
+        self.days
+            .checked_sub(days)
+            .map(Self::from_epoch_days)
+            .ok_or_else(|| {
+                Error::invalid_data(format!(
+                    "date day count {} overflow subtracting {} days",
+                    self.days, days
+                ))
+            })
+    }
+
+    /// Returns the signed day distance from `earlier` to this date.
+    #[inline(always)]
+    pub fn days_since(self, earlier: Self) -> Result<i64, Error> {
+        self.days.checked_sub(earlier.days).ok_or_else(|| {
+            Error::invalid_data(format!(
+                "date day count {} overflow subtracting {}",
+                self.days, earlier.days
+            ))
+        })
+    }
+}
+
+/// Point in time, represented as seconds and nanoseconds since Unix epoch.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Timestamp {
+    seconds: i64,
+    nanos: u32,
+}
+
+impl Timestamp {
+    pub const UNIX_EPOCH: Self = Self {
+        seconds: 0,
+        nanos: 0,
+    };
+
+    /// Creates a timestamp from epoch seconds and a nanosecond component.
+    #[inline(always)]
+    pub fn new(seconds: i64, nanos: u32) -> Result<Self, Error> {
+        if nanos >= NANOS_PER_SECOND as u32 {
+            return Err(Error::invalid_data(format!(
+                "timestamp nanoseconds {} out of valid range [0, 999_999_999]",
+                nanos
+            )));
+        }
+        Ok(Self { seconds, nanos })
+    }
+
+    /// Creates a timestamp from whole seconds since Unix epoch.
+    #[inline(always)]
+    pub const fn from_epoch_seconds(seconds: i64) -> Self {
+        Self { seconds, nanos: 0 }
+    }
+
+    /// Creates a timestamp from milliseconds since Unix epoch.
+    #[inline(always)]
+    pub fn from_epoch_millis(millis: i64) -> Self {
+        let seconds = millis.div_euclid(MILLIS_PER_SECOND);
+        let millis = millis.rem_euclid(MILLIS_PER_SECOND);
+        Self {
+            seconds,
+            nanos: (millis as u32) * 1_000_000,
+        }
+    }
+
+    /// Creates a timestamp from microseconds since Unix epoch.
+    #[inline(always)]
+    pub fn from_epoch_micros(micros: i64) -> Self {
+        let seconds = micros.div_euclid(MICROS_PER_SECOND);
+        let micros = micros.rem_euclid(MICROS_PER_SECOND);
+        Self {
+            seconds,
+            nanos: (micros as u32) * 1_000,
+        }
+    }
+
+    /// Creates a timestamp from nanoseconds since Unix epoch.
+    #[inline(always)]
+    pub fn from_epoch_nanos(nanos: i128) -> Result<Self, Error> {
+        let (seconds, nanos) = split_total_nanos(nanos, "timestamp")?;
+        Ok(Self { seconds, nanos })
+    }
+
+    /// Returns the whole seconds component.
+    #[inline(always)]
+    pub const fn seconds(self) -> i64 {
+        self.seconds
+    }
+
+    /// Returns the normalized nanosecond component.
+    #[inline(always)]
+    pub const fn subsec_nanos(self) -> u32 {
+        self.nanos
+    }
+
+    /// Returns milliseconds since Unix epoch, rounded down for 
sub-millisecond values.
+    #[inline(always)]
+    pub fn to_epoch_millis(self) -> Result<i64, Error> {
+        total_unit(self.to_epoch_nanos(), NANOS_PER_MILLI, "timestamp")
+    }
+
+    /// Returns microseconds since Unix epoch, rounded down for 
sub-microsecond values.
+    #[inline(always)]
+    pub fn to_epoch_micros(self) -> Result<i64, Error> {
+        total_unit(self.to_epoch_nanos(), NANOS_PER_MICRO, "timestamp")
+    }
+
+    /// Returns nanoseconds since Unix epoch.
+    #[inline(always)]
+    pub fn to_epoch_nanos(self) -> i128 {
+        i128::from(self.seconds) * NANOS_PER_SECOND_I128 + 
i128::from(self.nanos)
+    }
+
+    /// Adds a signed duration to this timestamp.
+    #[inline(always)]
+    pub fn checked_add_duration(self, duration: Duration) -> Result<Self, 
Error> {
+        Self::from_epoch_nanos(self.to_epoch_nanos() + duration.to_nanos())
+    }
+
+    /// Subtracts a signed duration from this timestamp.
+    #[inline(always)]
+    pub fn checked_sub_duration(self, duration: Duration) -> Result<Self, 
Error> {
+        Self::from_epoch_nanos(self.to_epoch_nanos() - duration.to_nanos())
+    }
+
+    /// Returns the signed duration from `earlier` to this timestamp.
+    #[inline(always)]
+    pub fn duration_since(self, earlier: Self) -> Result<Duration, Error> {
+        Duration::from_nanos(self.to_epoch_nanos() - earlier.to_epoch_nanos())
+    }
+}
+
+/// Signed duration, represented as seconds and normalized nanoseconds.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Duration {
+    seconds: i64,
+    nanos: u32,
+}
+
+impl Duration {
+    pub const ZERO: Self = Self {
+        seconds: 0,
+        nanos: 0,
+    };
+
+    /// Creates a duration from seconds and a nanosecond adjustment.
+    #[inline(always)]
+    pub fn new(seconds: i64, nanos: i32) -> Result<Self, Error> {
+        if !(-(NANOS_PER_SECOND - 1)..=(NANOS_PER_SECOND - 
1)).contains(&nanos) {
+            return Err(Error::invalid_data(format!(
+                "duration nanoseconds {} out of valid range [-999_999_999, 
999_999_999]",
+                nanos
+            )));
+        }
+        if nanos < 0 {
+            let seconds = seconds.checked_sub(1).ok_or_else(|| {
+                Error::invalid_data(
+                    "duration seconds underflow while normalizing negative 
nanoseconds",
+                )
+            })?;
+            return Ok(Self {
+                seconds,
+                nanos: (nanos + NANOS_PER_SECOND) as u32,
+            });
+        }
+        Ok(Self {
+            seconds,
+            nanos: nanos as u32,
+        })
+    }
+
+    /// Creates a duration from normalized seconds and nanoseconds.
+    #[inline(always)]
+    pub fn from_normalized(seconds: i64, nanos: u32) -> Result<Self, Error> {
+        if nanos >= NANOS_PER_SECOND as u32 {
+            return Err(Error::invalid_data(format!(
+                "duration nanoseconds {} out of valid range [0, 999_999_999]",
+                nanos
+            )));
+        }
+        Ok(Self { seconds, nanos })
+    }
+
+    /// Creates a duration from seconds and an arbitrary nanosecond adjustment.
+    #[inline(always)]
+    pub fn from_parts(seconds: i64, nanos: i64) -> Result<Self, Error> {
+        Self::from_nanos(i128::from(seconds) * NANOS_PER_SECOND_I128 + 
i128::from(nanos))
+    }
+
+    /// Creates a duration from whole seconds.
+    #[inline(always)]
+    pub const fn from_secs(seconds: i64) -> Self {
+        Self { seconds, nanos: 0 }
+    }
+
+    /// Creates a duration from minutes.
+    #[inline(always)]
+    pub fn from_minutes(minutes: i64) -> Result<Self, Error> {
+        checked_duration_seconds(minutes, SECONDS_PER_MINUTE, "minutes")
+    }
+
+    /// Creates a duration from hours.
+    #[inline(always)]
+    pub fn from_hours(hours: i64) -> Result<Self, Error> {
+        checked_duration_seconds(hours, SECONDS_PER_HOUR, "hours")
+    }
+
+    /// Creates a duration from days.
+    #[inline(always)]
+    pub fn from_days(days: i64) -> Result<Self, Error> {
+        checked_duration_seconds(days, SECONDS_PER_DAY, "days")
+    }
+
+    /// Creates a duration from milliseconds.
+    #[inline(always)]
+    pub fn from_millis(millis: i64) -> Self {
+        let seconds = millis.div_euclid(MILLIS_PER_SECOND);
+        let millis = millis.rem_euclid(MILLIS_PER_SECOND);
+        Self {
+            seconds,
+            nanos: (millis as u32) * 1_000_000,
+        }
+    }
+
+    /// Creates a duration from microseconds.
+    #[inline(always)]
+    pub fn from_micros(micros: i64) -> Self {
+        let seconds = micros.div_euclid(MICROS_PER_SECOND);
+        let micros = micros.rem_euclid(MICROS_PER_SECOND);
+        Self {
+            seconds,
+            nanos: (micros as u32) * 1_000,
+        }
+    }
+
+    /// Creates a duration from nanoseconds.
+    #[inline(always)]
+    pub fn from_nanos(nanos: i128) -> Result<Self, Error> {
+        let (seconds, nanos) = split_total_nanos(nanos, "duration")?;
+        Ok(Self { seconds, nanos })
+    }
+
+    /// Returns the normalized seconds component.
+    #[inline(always)]
+    pub const fn seconds(self) -> i64 {
+        self.seconds
+    }
+
+    /// Returns the normalized nanosecond component.
+    #[inline(always)]
+    pub const fn subsec_nanos(self) -> u32 {
+        self.nanos
+    }
+
+    /// Returns whether this duration is zero.
+    #[inline(always)]
+    pub const fn is_zero(self) -> bool {
+        self.seconds == 0 && self.nanos == 0
+    }
+
+    /// Returns whether this duration is greater than zero.
+    #[inline(always)]
+    pub const fn is_positive(self) -> bool {
+        self.seconds > 0 || (self.seconds == 0 && self.nanos > 0)
+    }
+
+    /// Returns whether this duration is less than zero.
+    #[inline(always)]
+    pub const fn is_negative(self) -> bool {
+        self.seconds < 0
+    }
+
+    /// Returns whole milliseconds, rounded down for sub-millisecond values.
+    #[inline(always)]
+    pub fn to_millis(self) -> Result<i64, Error> {
+        total_unit(self.to_nanos(), NANOS_PER_MILLI, "duration")
+    }
+
+    /// Returns whole microseconds, rounded down for sub-microsecond values.
+    #[inline(always)]
+    pub fn to_micros(self) -> Result<i64, Error> {
+        total_unit(self.to_nanos(), NANOS_PER_MICRO, "duration")
+    }
+
+    /// Returns nanoseconds.
+    #[inline(always)]
+    pub fn to_nanos(self) -> i128 {
+        i128::from(self.seconds) * NANOS_PER_SECOND_I128 + 
i128::from(self.nanos)
+    }
+
+    /// Adds two durations.
+    #[inline(always)]
+    pub fn checked_add(self, other: Self) -> Result<Self, Error> {
+        Self::from_nanos(self.to_nanos() + other.to_nanos())
+    }
+
+    /// Subtracts `other` from this duration.
+    #[inline(always)]
+    pub fn checked_sub(self, other: Self) -> Result<Self, Error> {
+        Self::from_nanos(self.to_nanos() - other.to_nanos())
+    }
+
+    /// Negates this duration.
+    #[inline(always)]
+    pub fn checked_neg(self) -> Result<Self, Error> {
+        Self::from_nanos(-self.to_nanos())
+    }
+
+    /// Returns the absolute duration.
+    #[inline(always)]
+    pub fn abs(self) -> Result<Self, Error> {
+        if self.is_negative() {
+            self.checked_neg()
+        } else {
+            Ok(self)
+        }
+    }
+}
+
+#[inline(always)]
+fn split_total_nanos(nanos: i128, value_name: &str) -> Result<(i64, u32), 
Error> {
+    let total_nanos = nanos;
+    let seconds = total_nanos.div_euclid(NANOS_PER_SECOND_I128);
+    let nanos = total_nanos.rem_euclid(NANOS_PER_SECOND_I128) as u32;
+    let seconds = i64::try_from(seconds).map_err(|_| {
+        Error::invalid_data(format!(
+            "{} nanoseconds {} out of supported seconds range",
+            value_name, total_nanos
+        ))
+    })?;
+    Ok((seconds, nanos))
+}
+
+#[inline(always)]
+fn total_unit(nanos: i128, nanos_per_unit: i128, value_name: &str) -> 
Result<i64, Error> {
+    let units = nanos.div_euclid(nanos_per_unit);
+    i64::try_from(units).map_err(|_| {
+        Error::invalid_data(format!(
+            "{} nanoseconds {} overflow conversion to requested unit",
+            value_name, nanos
+        ))
+    })
+}
+
+#[inline(always)]
+fn checked_duration_seconds(value: i64, multiplier: i64, unit: &str) -> 
Result<Duration, Error> {
+    value
+        .checked_mul(multiplier)
+        .map(Duration::from_secs)
+        .ok_or_else(|| {
+            Error::invalid_data(format!(
+                "duration {} {} overflow second conversion",
+                value, unit
+            ))
+        })
+}
+
+impl TryFrom<std::time::Duration> for Duration {
+    type Error = Error;
+
+    #[inline(always)]
+    fn try_from(value: std::time::Duration) -> Result<Self, Self::Error> {
+        let seconds = i64::try_from(value.as_secs()).map_err(|_| {
+            Error::invalid_data(format!(
+                "std::time::Duration seconds {} exceed Fory duration range",
+                value.as_secs()
+            ))
+        })?;
+        Self::from_normalized(seconds, value.subsec_nanos())
+    }
+}
+
+impl TryFrom<Duration> for std::time::Duration {
+    type Error = Error;
+
+    #[inline(always)]
+    fn try_from(value: Duration) -> Result<Self, Self::Error> {
+        if value.is_negative() {
+            return Err(Error::invalid_data(format!(
+                "negative Fory duration {:?} cannot convert to 
std::time::Duration",
+                value
+            )));
+        }
+        Ok(std::time::Duration::new(
+            u64::try_from(value.seconds()).map_err(|_| {
+                Error::invalid_data(format!(
+                    "duration seconds {} exceed std::time::Duration range",
+                    value.seconds()
+                ))
+            })?,
+            value.subsec_nanos(),
+        ))
+    }
+}
+
+impl TryFrom<std::time::SystemTime> for Timestamp {
+    type Error = Error;
+
+    #[inline(always)]
+    fn try_from(value: std::time::SystemTime) -> Result<Self, Self::Error> {
+        match value.duration_since(std::time::UNIX_EPOCH) {
+            Ok(duration) => {
+                let seconds = i64::try_from(duration.as_secs()).map_err(|_| {
+                    Error::invalid_data(format!(
+                        "SystemTime seconds {} exceed Fory timestamp range",
+                        duration.as_secs()
+                    ))
+                })?;
+                Self::new(seconds, duration.subsec_nanos())
+            }
+            Err(error) => {
+                let duration = error.duration();
+                let nanos = i128::from(duration.as_secs()) * 
NANOS_PER_SECOND_I128
+                    + i128::from(duration.subsec_nanos());
+                Self::from_epoch_nanos(-nanos)
+            }
+        }
+    }
+}
+
+impl TryFrom<Timestamp> for std::time::SystemTime {
+    type Error = Error;
+
+    #[inline(always)]
+    fn try_from(value: Timestamp) -> Result<Self, Self::Error> {
+        let nanos = value.to_epoch_nanos();
+        if nanos >= 0 {
+            std::time::UNIX_EPOCH
+                .checked_add(std_duration_from_nanos(nanos)?)
+                .ok_or_else(|| {
+                    Error::invalid_data(format!("timestamp {:?} exceeds 
SystemTime range", value))
+                })
+        } else {
+            std::time::UNIX_EPOCH
+                .checked_sub(std_duration_from_nanos(-nanos)?)
+                .ok_or_else(|| {
+                    Error::invalid_data(format!("timestamp {:?} exceeds 
SystemTime range", value))
+                })
+        }
+    }
+}
+
+#[inline(always)]
+fn std_duration_from_nanos(nanos: i128) -> Result<std::time::Duration, Error> {
+    debug_assert!(nanos >= 0);
+    let total_nanos = nanos;
+    let seconds = total_nanos / NANOS_PER_SECOND_I128;
+    let nanos = (total_nanos % NANOS_PER_SECOND_I128) as u32;
+    let seconds = u64::try_from(seconds).map_err(|_| {
+        Error::invalid_data(format!(
+            "nanoseconds {} exceed std::time::Duration range",
+            total_nanos
+        ))
+    })?;
+    Ok(std::time::Duration::new(seconds, nanos))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn date_day_api() {
+        let date = Date::from_epoch_days(19_782);
+        assert_eq!(date.epoch_days(), 19_782);
+        assert_eq!(
+            date.checked_add_days(1).unwrap(),
+            Date::from_epoch_days(19_783)
+        );
+        assert_eq!(
+            date.checked_sub_days(1).unwrap(),
+            Date::from_epoch_days(19_781)
+        );
+        assert_eq!(
+            date.days_since(Date::UNIX_EPOCH).unwrap(),
+            date.epoch_days()
+        );
+        assert!(Date::from_epoch_days(i64::MAX).checked_add_days(1).is_err());
+    }
+
+    #[test]
+    fn timestamp_epoch_api() {
+        let millis = Timestamp::from_epoch_millis(-1);
+        assert_eq!(millis.seconds(), -1);
+        assert_eq!(millis.subsec_nanos(), 999_000_000);
+        assert_eq!(millis.to_epoch_millis().unwrap(), -1);
+
+        let nanos = Timestamp::from_epoch_nanos(-1).unwrap();
+        assert_eq!(nanos.seconds(), -1);
+        assert_eq!(nanos.subsec_nanos(), 999_999_999);
+        assert_eq!(nanos.to_epoch_nanos(), -1);
+        assert_eq!(nanos.to_epoch_micros().unwrap(), -1);
+
+        let duration = Duration::from_micros(1);
+        let timestamp = Timestamp::UNIX_EPOCH
+            .checked_add_duration(duration)
+            .unwrap();
+        assert_eq!(timestamp.to_epoch_micros().unwrap(), 1);
+        assert_eq!(
+            timestamp.duration_since(Timestamp::UNIX_EPOCH).unwrap(),
+            duration
+        );
+        assert_eq!(
+            Timestamp::UNIX_EPOCH
+                .checked_sub_duration(Duration::from_nanos(1).unwrap())
+                .unwrap(),
+            nanos
+        );
+
+        assert_eq!(Timestamp::from_epoch_seconds(10).seconds(), 10);
+    }
+
+    #[test]
+    fn duration_api() {
+        let nanos = Duration::from_nanos(-1).unwrap();
+        assert_eq!(nanos.seconds(), -1);
+        assert_eq!(nanos.subsec_nanos(), 999_999_999);
+        assert!(nanos.is_negative());
+        assert_eq!(nanos.to_nanos(), -1);
+        assert_eq!(nanos.to_micros().unwrap(), -1);
+
+        let duration = Duration::from_parts(1, 1_500_000_000).unwrap();
+        assert_eq!(duration, Duration::from_normalized(2, 
500_000_000).unwrap());
+        assert_eq!(
+            Duration::from_millis(-1),
+            Duration::from_normalized(-1, 999_000_000).unwrap()
+        );
+        assert_eq!(
+            nanos.checked_neg().unwrap(),
+            Duration::from_normalized(0, 1).unwrap()
+        );
+        assert_eq!(
+            Duration::from_secs(1)
+                .checked_add(Duration::from_millis(500))
+                .unwrap(),
+            Duration::from_normalized(1, 500_000_000).unwrap()
+        );
+        assert_eq!(Duration::from_minutes(2).unwrap(), 
Duration::from_secs(120));
+        assert_eq!(Duration::from_hours(2).unwrap(), 
Duration::from_secs(7_200));
+        assert_eq!(
+            Duration::from_days(2).unwrap(),
+            Duration::from_secs(172_800)
+        );
+        assert_eq!(Duration::ZERO.abs().unwrap(), Duration::ZERO);
+    }
+
+    #[test]
+    fn std_time_conversions() {
+        let std_duration = std::time::Duration::new(3, 4);
+        let duration = Duration::try_from(std_duration).unwrap();
+        assert_eq!(duration, Duration::from_normalized(3, 4).unwrap());
+        let roundtrip: std::time::Duration = duration.try_into().unwrap();
+        assert_eq!(roundtrip, std_duration);
+        
assert!(std::time::Duration::try_from(Duration::from_nanos(-1).unwrap()).is_err());
+
+        let before_epoch = Timestamp::from_epoch_nanos(-1).unwrap();
+        let system_time: std::time::SystemTime = 
before_epoch.try_into().unwrap();
+        assert_eq!(Timestamp::try_from(system_time).unwrap(), before_epoch);
+
+        let after_epoch = Timestamp::from_epoch_nanos(1_500_000_001).unwrap();
+        let system_time: std::time::SystemTime = 
after_epoch.try_into().unwrap();
+        assert_eq!(Timestamp::try_from(system_time).unwrap(), after_epoch);
+    }
+
+    #[cfg(feature = "chrono")]
+    #[test]
+    fn chrono_utc_conversion() {
+        use chrono::{DateTime, Utc};
+
+        let datetime = DateTime::<Utc>::from_timestamp(1, 2).unwrap();
+        let timestamp = Timestamp::from(datetime);
+        assert_eq!(timestamp, Timestamp::new(1, 2).unwrap());
+        let roundtrip: DateTime<Utc> = timestamp.try_into().unwrap();
+        assert_eq!(roundtrip, datetime);
+    }
+}
+
+#[cfg(feature = "chrono")]
+mod chrono_support {
+    use super::{Date, Duration, Timestamp};
+    use crate::error::Error;
+    use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeDelta, Utc};
+
+    fn epoch() -> NaiveDate {
+        NaiveDate::from_ymd_opt(1970, 1, 1).expect("1970-01-01 is a valid 
chrono date")
+    }
+
+    impl From<NaiveDate> for Date {
+        #[inline(always)]
+        fn from(value: NaiveDate) -> Self {
+            
Self::from_epoch_days(value.signed_duration_since(epoch()).num_days())
+        }
+    }
+
+    impl TryFrom<Date> for NaiveDate {
+        type Error = Error;
+
+        #[inline(always)]
+        fn try_from(value: Date) -> Result<Self, Self::Error> {
+            let duration = 
TimeDelta::try_days(value.epoch_days()).ok_or_else(|| {
+                Error::invalid_data(format!(
+                    "date day count {} is out of chrono::TimeDelta range",
+                    value.epoch_days()
+                ))
+            })?;
+            epoch().checked_add_signed(duration).ok_or_else(|| {
+                Error::invalid_data(format!(
+                    "date day count {} is out of chrono::NaiveDate range",
+                    value.epoch_days()
+                ))
+            })
+        }
+    }
+
+    impl From<NaiveDateTime> for Timestamp {
+        #[inline(always)]
+        fn from(value: NaiveDateTime) -> Self {
+            let value = value.and_utc();
+            Self {
+                seconds: value.timestamp(),
+                nanos: value.timestamp_subsec_nanos(),
+            }
+        }
+    }
+
+    impl From<DateTime<Utc>> for Timestamp {
+        #[inline(always)]
+        fn from(value: DateTime<Utc>) -> Self {
+            Self {
+                seconds: value.timestamp(),
+                nanos: value.timestamp_subsec_nanos(),
+            }
+        }
+    }
+
+    impl TryFrom<Timestamp> for DateTime<Utc> {
+        type Error = Error;
+
+        #[inline(always)]
+        fn try_from(value: Timestamp) -> Result<Self, Self::Error> {
+            DateTime::from_timestamp(value.seconds(), 
value.subsec_nanos()).ok_or_else(|| {
+                Error::invalid_data(format!(
+                    "timestamp seconds {} nanoseconds {} is out of 
chrono::DateTime<Utc> range",
+                    value.seconds(),
+                    value.subsec_nanos()
+                ))
+            })
+        }
+    }
+
+    impl TryFrom<Timestamp> for NaiveDateTime {
+        type Error = Error;
+
+        #[inline(always)]
+        fn try_from(value: Timestamp) -> Result<Self, Self::Error> {
+            DateTime::from_timestamp(value.seconds(), value.subsec_nanos())
+                .map(|value| value.naive_utc())
+                .ok_or_else(|| {
+                    Error::invalid_data(format!(
+                        "timestamp seconds {} nanoseconds {} is out of 
chrono::NaiveDateTime range",
+                        value.seconds(),
+                        value.subsec_nanos()
+                    ))
+                })
+        }
+    }
+
+    impl TryFrom<chrono::Duration> for Duration {
+        type Error = Error;
+
+        #[inline(always)]
+        fn try_from(value: chrono::Duration) -> Result<Self, Self::Error> {
+            Self::new(value.num_seconds(), value.subsec_nanos())
+        }
+    }
+
+    impl TryFrom<Duration> for chrono::Duration {
+        type Error = Error;
+
+        #[inline(always)]
+        fn try_from(value: Duration) -> Result<Self, Self::Error> {
+            chrono::Duration::try_seconds(value.seconds())
+                .and_then(|duration| {
+                    
duration.checked_add(&chrono::Duration::nanoseconds(i64::from(
+                        value.subsec_nanos(),
+                    )))
+                })
+                .ok_or_else(|| {
+                    Error::invalid_data(format!(
+                        "duration seconds {} out of chrono::Duration valid 
range",
+                        value.seconds()
+                    ))
+                })
+        }
+    }
+}
diff --git a/rust/fory-core/src/util/mod.rs b/rust/fory-core/src/util/mod.rs
index 85d3be0dc..0d36492c0 100644
--- a/rust/fory-core/src/util/mod.rs
+++ b/rust/fory-core/src/util/mod.rs
@@ -24,15 +24,6 @@ pub use string_util::{
 };
 pub use sync::{Spinlock, SpinlockGuard};
 
-use chrono::NaiveDate;
-
-pub const EPOCH: NaiveDate = match NaiveDate::from_ymd_opt(1970, 1, 1) {
-    None => {
-        panic!("Unreachable code")
-    }
-    Some(epoch) => epoch,
-};
-
 /// Set `ENABLE_FORY_DEBUG_OUTPUT=1` at compile time to enable debug output.
 #[allow(unexpected_cfgs)]
 pub const ENABLE_FORY_DEBUG_OUTPUT: bool = cfg!(fory_debug_output);
diff --git a/rust/fory-derive/Cargo.toml b/rust/fory-derive/Cargo.toml
index 1c0c9067e..15b4c47b7 100644
--- a/rust/fory-derive/Cargo.toml
+++ b/rust/fory-derive/Cargo.toml
@@ -40,8 +40,8 @@ syn = { default-features = false, version = "2.0", features = 
[
     "printing",
 ] }
 quote = { default-features = false, version = "1.0" }
-thiserror = { default-features = false, version = "1.0" }
 
 [features]
 default = ["fields-loop-unroll"]
-fields-loop-unroll = []
\ No newline at end of file
+fields-loop-unroll = []
+tests = []
diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs
index f8a726ef1..15ff08df7 100644
--- a/rust/fory-derive/src/lib.rs
+++ b/rust/fory-derive/src/lib.rs
@@ -133,8 +133,10 @@
 //! - `Option<T>` for nullable values
 //!
 //! **Date/Time:**
-//! - `chrono::NaiveDate`
-//! - `chrono::NaiveDateTime`
+//! - `fory::Date`
+//! - `fory::Timestamp`
+//! - `fory::Duration`
+//! - `chrono::NaiveDate`, `chrono::NaiveDateTime`, and `chrono::Duration` 
when the `chrono` feature is enabled
 //!
 //! **Custom Types:**
 //! - Any type that implements `Serializer` (for `Fory`) or `Row` (for 
`ForyRow`)
diff --git a/rust/fory-derive/src/object/util.rs 
b/rust/fory-derive/src/object/util.rs
index 6239d5bc4..d7593fc6e 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -294,9 +294,11 @@ pub(crate) fn get_type_id_by_name(ty: &str) -> u32 {
     // Check internal types
     match unqualified_ty {
         "String" => return TypeId::STRING as u32,
+        "Date" => return TypeId::DATE as u32,
+        "Timestamp" => return TypeId::TIMESTAMP as u32,
+        "Duration" => return TypeId::DURATION as u32,
         "NaiveDate" => return TypeId::DATE as u32,
         "NaiveDateTime" => return TypeId::TIMESTAMP as u32,
-        "Duration" => return TypeId::DURATION as u32,
         "Decimal" => return TypeId::DECIMAL as u32,
         "bytes" => return TypeId::BINARY as u32,
         _ => {}
diff --git a/rust/fory/Cargo.toml b/rust/fory/Cargo.toml
index a8c2645b9..52782f3b8 100644
--- a/rust/fory/Cargo.toml
+++ b/rust/fory/Cargo.toml
@@ -31,3 +31,8 @@ description = "Apache Fory: Blazingly fast multi-language 
serialization framewor
 [dependencies]
 fory-core.workspace = true
 fory-derive.workspace = true
+
+[features]
+default = []
+chrono = ["fory-core/chrono"]
+tests = []
diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs
index af9e019fd..b8714853c 100644
--- a/rust/fory/src/lib.rs
+++ b/rust/fory/src/lib.rs
@@ -979,10 +979,12 @@
 //! - `RefCell<T>` - Interior mutability with runtime borrow checking
 //! - `Mutex<T>` - Thread-safe interior mutability
 //!
-//! ### Date and Time (requires `chrono` feature)
+//! ### Date and Time
 //!
-//! - `chrono::NaiveDate` - Date without timezone
-//! - `chrono::NaiveDateTime` - Timestamp without timezone
+//! - `Date` - Date without timezone, with epoch-day accessors and checked day 
arithmetic
+//! - `Timestamp` - Point in time, with epoch unit conversions and checked 
duration arithmetic
+//! - `Duration` - Signed duration, with normalized parts, total unit 
conversions, and checked arithmetic
+//! - `chrono::NaiveDate`, `chrono::NaiveDateTime`, and `chrono::Duration` 
when the `chrono` feature is enabled
 //!
 //! ### Custom Types
 //!
@@ -1202,7 +1204,7 @@
 
 pub use fory_core::{
     error::Error, fory::Fory, fory::ForyBuilder, register_trait_type, 
row::from_row, row::to_row,
-    ArcWeak, BFloat16, Decimal, Float16, ForyDefault, RcWeak, ReadContext, 
Reader, RefFlag,
-    RefMode, Serializer, TypeId, TypeResolver, WriteContext, Writer,
+    ArcWeak, BFloat16, Date, Decimal, Duration, Float16, ForyDefault, RcWeak, 
ReadContext, Reader,
+    RefFlag, RefMode, Serializer, Timestamp, TypeId, TypeResolver, 
WriteContext, Writer,
 };
 pub use fory_derive::{ForyEnum, ForyRow, ForyStruct, ForyUnion};
diff --git a/rust/tests/Cargo.toml b/rust/tests/Cargo.toml
index 59e854670..c2f2fdc25 100644
--- a/rust/tests/Cargo.toml
+++ b/rust/tests/Cargo.toml
@@ -26,5 +26,9 @@ publish = false
 fory-core = { path = "../fory-core" }
 fory-derive = { path = "../fory-derive" }
 
-chrono = "0.4"
 num-bigint = "0.4"
+
+[features]
+default = []
+chrono = ["fory-core/chrono"]
+tests = []
diff --git a/rust/tests/tests/compatible/test_basic_type.rs 
b/rust/tests/tests/compatible/test_basic_type.rs
index d1e124411..5ebbe33a1 100644
--- a/rust/tests/tests/compatible/test_basic_type.rs
+++ b/rust/tests/tests/compatible/test_basic_type.rs
@@ -15,8 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use chrono::{NaiveDate, NaiveDateTime};
-use fory_core::{fory::Fory, Reader};
+use fory_core::{fory::Fory, Date, Reader, Timestamp};
 
 // primitive_val
 const BOOL_VAL: bool = true;
@@ -29,10 +28,13 @@ const F64_VAL: f64 = 47.0;
 // string
 const STR_LATIN1_VAL: &str = "Çüéâäàåçêëèïî";
 // time
-#[allow(deprecated)]
-const TIMESTAMP_VAL: NaiveDateTime = NaiveDateTime::from_timestamp(100, 0);
-#[allow(deprecated)]
-const LOCAL_DATE_VAL: NaiveDate = NaiveDate::from_ymd(2021, 11, 23);
+fn timestamp_val() -> Timestamp {
+    Timestamp::new(100, 0).unwrap()
+}
+
+fn local_date_val() -> Date {
+    Date::from_epoch_days(18_954)
+}
 
 const BOOL_ARRAY: [bool; 1] = [true];
 const INT8_ARRAY: [i8; 1] = [48];
@@ -53,8 +55,8 @@ fn serialize_non_null(fory: &Fory) -> Vec<u8> {
     fory.serialize_to(&mut buf, &F64_VAL).unwrap();
     fory.serialize_to(&mut buf, &STR_LATIN1_VAL.to_string())
         .unwrap();
-    fory.serialize_to(&mut buf, &LOCAL_DATE_VAL).unwrap();
-    fory.serialize_to(&mut buf, &TIMESTAMP_VAL).unwrap();
+    fory.serialize_to(&mut buf, &local_date_val()).unwrap();
+    fory.serialize_to(&mut buf, &timestamp_val()).unwrap();
     fory.serialize_to(&mut buf, &BOOL_ARRAY.to_vec()).unwrap();
     fory.serialize_to(&mut buf, &INT8_ARRAY.to_vec()).unwrap();
     fory.serialize_to(&mut buf, &INT16_ARRAY.to_vec()).unwrap();
@@ -78,8 +80,9 @@ fn serialize_nullable(fory: &Fory) -> Vec<u8> {
     fory.serialize_to(&mut buf, &Some(F64_VAL)).unwrap();
     fory.serialize_to(&mut buf, &Some(STR_LATIN1_VAL.to_string()))
         .unwrap();
-    fory.serialize_to(&mut buf, &Some(LOCAL_DATE_VAL)).unwrap();
-    fory.serialize_to(&mut buf, &Some(TIMESTAMP_VAL)).unwrap();
+    fory.serialize_to(&mut buf, &Some(local_date_val()))
+        .unwrap();
+    fory.serialize_to(&mut buf, &Some(timestamp_val())).unwrap();
     fory.serialize_to(&mut buf, &Some(BOOL_ARRAY.to_vec()))
         .unwrap();
     fory.serialize_to(&mut buf, &Some(INT8_ARRAY.to_vec()))
@@ -103,9 +106,8 @@ fn serialize_nullable(fory: &Fory) -> Vec<u8> {
     fory.serialize_to(&mut buf, &Option::<f64>::None).unwrap();
     fory.serialize_to(&mut buf, &Option::<String>::None)
         .unwrap();
-    fory.serialize_to(&mut buf, &Option::<NaiveDate>::None)
-        .unwrap();
-    fory.serialize_to(&mut buf, &Option::<NaiveDateTime>::None)
+    fory.serialize_to(&mut buf, &Option::<Date>::None).unwrap();
+    fory.serialize_to(&mut buf, &Option::<Timestamp>::None)
         .unwrap();
     fory.serialize_to(&mut buf, &Option::<Vec<bool>>::None)
         .unwrap();
@@ -141,12 +143,12 @@ fn deserialize_non_null(fory: &Fory, bins: Vec<u8>, 
auto_conv: bool) {
         fory.deserialize_from::<String>(&mut reader).unwrap()
     );
     assert_eq!(
-        LOCAL_DATE_VAL,
-        fory.deserialize_from::<NaiveDate>(&mut reader).unwrap()
+        local_date_val(),
+        fory.deserialize_from::<Date>(&mut reader).unwrap()
     );
     assert_eq!(
-        TIMESTAMP_VAL,
-        fory.deserialize_from::<NaiveDateTime>(&mut reader).unwrap()
+        timestamp_val(),
+        fory.deserialize_from::<Timestamp>(&mut reader).unwrap()
     );
 
     assert_eq!(
@@ -211,12 +213,12 @@ fn deserialize_non_null(fory: &Fory, bins: Vec<u8>, 
auto_conv: bool) {
             fory.deserialize_from::<String>(&mut reader).unwrap()
         );
         assert_eq!(
-            NaiveDate::default(),
-            fory.deserialize_from::<NaiveDate>(&mut reader).unwrap()
+            Date::default(),
+            fory.deserialize_from::<Date>(&mut reader).unwrap()
         );
         assert_eq!(
-            NaiveDateTime::default(),
-            fory.deserialize_from::<NaiveDateTime>(&mut reader).unwrap()
+            Timestamp::default(),
+            fory.deserialize_from::<Timestamp>(&mut reader).unwrap()
         );
 
         assert_eq!(
@@ -286,13 +288,12 @@ fn deserialize_nullable(fory: &Fory, bins: Vec<u8>, 
auto_conv: bool) {
             .unwrap()
     );
     assert_eq!(
-        Some(LOCAL_DATE_VAL),
-        fory.deserialize_from::<Option<NaiveDate>>(&mut reader)
-            .unwrap()
+        Some(local_date_val()),
+        fory.deserialize_from::<Option<Date>>(&mut reader).unwrap()
     );
     assert_eq!(
-        Some(TIMESTAMP_VAL),
-        fory.deserialize_from::<Option<NaiveDateTime>>(&mut reader)
+        Some(timestamp_val()),
+        fory.deserialize_from::<Option<Timestamp>>(&mut reader)
             .unwrap()
     );
     assert_eq!(
@@ -366,12 +367,11 @@ fn deserialize_nullable(fory: &Fory, bins: Vec<u8>, 
auto_conv: bool) {
         );
         assert_eq!(
             None,
-            fory.deserialize_from::<Option<NaiveDate>>(&mut reader)
-                .unwrap()
+            fory.deserialize_from::<Option<Date>>(&mut reader).unwrap()
         );
         assert_eq!(
             None,
-            fory.deserialize_from::<Option<NaiveDateTime>>(&mut reader)
+            fory.deserialize_from::<Option<Timestamp>>(&mut reader)
                 .unwrap()
         );
         assert_eq!(
diff --git a/rust/tests/tests/test_complex_struct.rs 
b/rust/tests/tests/test_complex_struct.rs
index 55e4a4f68..4e5fe5cd6 100644
--- a/rust/tests/tests/test_complex_struct.rs
+++ b/rust/tests/tests/test_complex_struct.rs
@@ -16,9 +16,9 @@
 // under the License.
 
 use fory_core::fory::Fory;
+use fory_core::{Date, Timestamp};
 use fory_derive::{ForyEnum, ForyStruct};
 // use std::any::Any;
-use chrono::{DateTime, NaiveDate, NaiveDateTime};
 use std::collections::HashMap;
 
 // RUSTFLAGS="-Awarnings" cargo expand -p tests --test test_complex_struct
@@ -83,8 +83,8 @@ fn complex_struct() {
         // age: u16,
         op: Option<String>,
         op2: Option<String>,
-        date: NaiveDate,
-        time: NaiveDateTime,
+        date: Date,
+        time: Timestamp,
         c5: f32,
         c6: f64,
     }
@@ -103,8 +103,8 @@ fn complex_struct() {
         // age: 12,
         op: Some("option".to_string()),
         op2: None,
-        date: NaiveDate::from_ymd_opt(2025, 12, 12).unwrap(),
-        time: DateTime::from_timestamp(1689912359, 0).unwrap().naive_utc(),
+        date: Date::from_epoch_days(20_434),
+        time: Timestamp::new(1_689_912_359, 0).unwrap(),
         c5: 2.0,
         c6: 4.0,
     };
diff --git a/rust/tests/tests/test_cross_language.rs 
b/rust/tests/tests/test_cross_language.rs
index 8138878fb..1d100c322 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -15,14 +15,13 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use chrono::{NaiveDate, NaiveDateTime};
 use fory_core::buffer::{Reader, Writer};
 use fory_core::error::Error;
 use fory_core::resolver::TypeResolver;
 use fory_core::serializer::{ForyDefault, Serializer};
 use fory_core::type_id::TypeId;
 use fory_core::util::murmurhash3_x64_128;
-use fory_core::{read_data, write_data, BFloat16, Decimal, Float16, Fory};
+use fory_core::{read_data, write_data, BFloat16, Date, Decimal, Float16, Fory, 
Timestamp};
 use fory_core::{ReadContext, WriteContext};
 use fory_derive::{ForyEnum, ForyStruct, ForyUnion};
 use num_bigint::BigInt;
@@ -116,14 +115,13 @@ fn test_buffer() {
 }
 
 #[test]
-#[allow(deprecated)]
-fn test_naive_date_uses_var_i64_day_count() {
+fn test_date_uses_var_i64_day_count() {
     let fory = Fory::builder()
         .xlang(true)
         .compatible(false)
         .track_ref(false)
         .build();
-    let day = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap();
+    let day = Date::from_epoch_days(-1);
     let mut buf = Vec::new();
     fory.serialize_to(&mut buf, &day).unwrap();
 
@@ -136,10 +134,9 @@ fn test_naive_date_uses_var_i64_day_count() {
 }
 
 #[test]
-#[allow(deprecated)]
-fn test_naive_date_uses_i32_day_count_in_native_mode() {
+fn test_date_uses_i32_day_count_in_native_mode() {
     let fory = Fory::builder().xlang(false).track_ref(false).build();
-    let day = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap();
+    let day = Date::from_epoch_days(-1);
     let mut buf = Vec::new();
     fory.serialize_to(&mut buf, &day).unwrap();
 
@@ -330,10 +327,9 @@ macro_rules! assert_de {
 
 #[test]
 #[ignore]
-#[allow(deprecated)]
 fn test_cross_language_serializer() {
-    let day = NaiveDate::from_ymd_opt(2021, 11, 23).unwrap();
-    let instant = NaiveDateTime::from_timestamp(100, 0);
+    let day = Date::from_epoch_days(18_954);
+    let instant = Timestamp::new(100, 0).unwrap();
     let str_list = vec!["hello".to_string(), "world".to_string()];
     let str_set = HashSet::from(["hello".to_string(), "world".to_string()]);
     let str_map = HashMap::<String, String>::from([
@@ -361,8 +357,8 @@ fn test_cross_language_serializer() {
     assert_de!(fory, reader, f32, -1f32);
     assert_de!(fory, reader, f64, -1f64);
     assert_de!(fory, reader, String, "str".to_string());
-    assert_de!(fory, reader, NaiveDate, day);
-    assert_de!(fory, reader, NaiveDateTime, instant);
+    assert_de!(fory, reader, Date, day);
+    assert_de!(fory, reader, Timestamp, instant);
     assert_de!(fory, reader, Vec<bool>, [true, false]);
     assert_de!(fory, reader, Vec<u8>, [1, i8::MAX as u8]);
     assert_de!(fory, reader, Vec<i16>, [1, i16::MAX]);
diff --git a/rust/tests/tests/test_fory.rs b/rust/tests/tests/test_fory.rs
index 3b141a0ee..596ac1188 100644
--- a/rust/tests/tests/test_fory.rs
+++ b/rust/tests/tests/test_fory.rs
@@ -197,7 +197,7 @@ fn test_serialize_to_detailed() {
     }
 }
 
-use chrono::{DateTime, NaiveDateTime, Utc};
+use fory_core::Timestamp;
 
 macro_rules! impl_value {
     ($record:ident, $value:ident, { $($field:ident : $ty:ty = $expr:expr),* 
$(,)? }) => {
@@ -227,7 +227,7 @@ macro_rules! impl_value {
 pub struct KeyValue {
     feature_key: String,
     count: u64,
-    last_seen_event_time: DateTime<Utc>,
+    last_seen_event_time: Timestamp,
 }
 
 impl_value!(
@@ -235,7 +235,7 @@ impl_value!(
     Value,
     {
         count: u64 = count,
-        last_seen_event_time: NaiveDateTime = last_seen_event_time.naive_utc(),
+        last_seen_event_time: Timestamp = last_seen_event_time,
     }
 );
 
@@ -244,15 +244,12 @@ fn test_in_macro() {
     let key_value = KeyValue {
         feature_key: "test_key".to_string(),
         count: 100,
-        last_seen_event_time: Utc::now(),
+        last_seen_event_time: Timestamp::new(1_689_912_359, 123).unwrap(),
     };
     let (key, value) = key_value.clone().to_key_value();
     assert_eq!(key, "test_key");
     assert_eq!(value.count, 100);
-    assert_eq!(
-        value.last_seen_event_time,
-        key_value.last_seen_event_time.naive_utc()
-    );
+    assert_eq!(value.last_seen_event_time, key_value.last_seen_event_time);
 }
 
 #[test]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to