diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 11c1d819..c9d4de08 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -161,6 +161,7 @@ static KNOWN_TYPES: Lazy> = La ("BigUint64Array", ("BigUint64Array", false, false)), ("DataView", ("DataView", false, false)), ("DateTime", ("Date", false, false)), + ("NaiveDateTime", ("Date", false ,false)), ("Date", ("Date", false, false)), ("JsDate", ("Date", false, false)), ("JsBuffer", ("Buffer", false, false)), diff --git a/crates/napi/src/bindgen_runtime/js_values/date.rs b/crates/napi/src/bindgen_runtime/js_values/date.rs index 0f622ec4..6ed32266 100644 --- a/crates/napi/src/bindgen_runtime/js_values/date.rs +++ b/crates/napi/src/bindgen_runtime/js_values/date.rs @@ -1,4 +1,4 @@ -use std::ptr; +use std::{ptr, str::FromStr}; use chrono::{DateTime, NaiveDateTime, Utc}; @@ -29,6 +29,97 @@ impl ValidateNapiValue for DateTime { } } +impl ToNapiValue for NaiveDateTime { + unsafe fn to_napi_value(env: sys::napi_env, val: NaiveDateTime) -> Result { + let mut ptr = std::ptr::null_mut(); + let millis_since_epoch_utc = val.timestamp_millis() as f64; + + check_status!( + unsafe { sys::napi_create_date(env, millis_since_epoch_utc, &mut ptr) }, + "Failed to convert rust type `NaiveDateTime` into napi value", + )?; + + Ok(ptr) + } +} + +impl FromNapiValue for NaiveDateTime { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let mut to_iso_string = ptr::null_mut(); + check_status!( + unsafe { + napi_sys::napi_create_string_utf8( + env, + "toISOString\0".as_ptr().cast(), + 11, + &mut to_iso_string, + ) + }, + "create toISOString JavaScript string failed" + )?; + let mut to_iso_string_method = ptr::null_mut(); + check_status!( + unsafe { sys::napi_get_property(env, napi_val, to_iso_string, &mut to_iso_string_method) }, + "get toISOString method failed" + )?; + let mut iso_string_value = ptr::null_mut(); + check_status!( + unsafe { + sys::napi_call_function( + env, + napi_val, + to_iso_string_method, + 0, + ptr::null(), + &mut iso_string_value, + ) + }, + "Call toISOString on Date Object failed" + )?; + + let mut iso_string_length = 0; + check_status!( + unsafe { + sys::napi_get_value_string_utf8( + env, + iso_string_value, + ptr::null_mut(), + 0, + &mut iso_string_length, + ) + }, + "Get ISOString length failed" + )?; + let mut iso_string = String::with_capacity(iso_string_length + 1); + check_status!( + unsafe { + sys::napi_get_value_string_utf8( + env, + iso_string_value, + iso_string.as_mut_ptr().cast(), + iso_string_length, + &mut iso_string_length, + ) + }, + "Get ISOString length failed" + )?; + + unsafe { iso_string.as_mut_vec().set_len(iso_string_length) }; + + let naive = NaiveDateTime::from_str(iso_string.as_str()).map_err(|err| { + Error::new( + Status::InvalidArg, + format!( + "Failed to convert napi value into rust type `NaiveDateTime` {} {}", + err, iso_string + ), + ) + })?; + + Ok(naive) + } +} + impl ToNapiValue for DateTime { unsafe fn to_napi_value(env: sys::napi_env, val: DateTime) -> Result { let mut ptr = std::ptr::null_mut(); diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index 2d7ed1a8..1cad4f57 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -268,6 +268,10 @@ Generated by [AVA](https://avajs.dev). ␊ export function chronoDateToMillis(input: Date): number␊ ␊ + export function chronoNativeDateTime(date: Date): number␊ + ␊ + export function chronoNativeDateTimeReturn(): Date | null␊ + ␊ export function concatLatin1(s: string): string␊ ␊ export function concatStr(s: string): string␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index 910b4e95..65b9a973 100644 Binary files a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap and b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap differ diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index 4d15cb67..19e112eb 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -130,6 +130,8 @@ import { tsfnReturnPromise, tsfnReturnPromiseTimeout, returnFromSharedCrate, + chronoNativeDateTime, + chronoNativeDateTimeReturn, } from '../' test('export const', (t) => { @@ -980,3 +982,14 @@ Napi5Test('Class with getter setter closures', (t) => { // @ts-expect-error t.is(instance.age, 0.3) }) + +Napi5Test('Date to chrono::NativeDateTime test', (t) => { + const fixture = new Date() + t.is(chronoNativeDateTime(fixture), fixture.valueOf()) +}) + +Napi5Test('Date from chrono::NativeDateTime test', (t) => { + const fixture = chronoNativeDateTimeReturn() + t.true(fixture instanceof Date) + t.is(fixture?.toISOString(), '2016-12-23T15:25:59.325Z') +}) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 63f523ab..aedd504f 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -258,6 +258,10 @@ export function chronoDateAdd1Minute(input: Date): Date export function chronoDateToMillis(input: Date): number +export function chronoNativeDateTime(date: Date): number + +export function chronoNativeDateTimeReturn(): Date | null + export function concatLatin1(s: string): string export function concatStr(s: string): string diff --git a/examples/napi/src/date.rs b/examples/napi/src/date.rs index f13ee76a..584e6d15 100644 --- a/examples/napi/src/date.rs +++ b/examples/napi/src/date.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use chrono::{Duration, Utc}; use napi::bindgen_prelude::*; @@ -21,3 +23,13 @@ pub struct Dates { pub start: chrono::DateTime, pub end: Option>, } + +#[napi] +pub fn chrono_native_date_time(date: chrono::NaiveDateTime) -> i64 { + date.timestamp_millis() +} + +#[napi] +pub fn chrono_native_date_time_return() -> Option { + chrono::NaiveDateTime::from_str("2016-12-23T15:25:59.325").ok() +}