napi_h/bindgen_runtime/js_values/
date.rs

1use std::{ptr, str::FromStr};
2
3use chrono::{DateTime, NaiveDateTime, Utc};
4
5use crate::{bindgen_prelude::*, check_status, sys, ValueType};
6
7impl TypeName for DateTime<Utc> {
8  fn type_name() -> &'static str {
9    "DateTime"
10  }
11
12  fn value_type() -> ValueType {
13    ValueType::Object
14  }
15}
16
17impl ValidateNapiValue for DateTime<Utc> {
18  unsafe fn validate(env: sys::napi_env, napi_val: sys::napi_value) -> Result<sys::napi_value> {
19    let mut is_date = false;
20    check_status!(unsafe { sys::napi_is_date(env, napi_val, &mut is_date) })?;
21    if !is_date {
22      return Err(Error::new(
23        Status::InvalidArg,
24        "Expected a Date object".to_owned(),
25      ));
26    }
27
28    Ok(ptr::null_mut())
29  }
30}
31
32impl ToNapiValue for NaiveDateTime {
33  unsafe fn to_napi_value(env: sys::napi_env, val: NaiveDateTime) -> Result<sys::napi_value> {
34    let mut ptr = std::ptr::null_mut();
35    let millis_since_epoch_utc = val.timestamp_millis() as f64;
36
37    check_status!(
38      unsafe { sys::napi_create_date(env, millis_since_epoch_utc, &mut ptr) },
39      "Failed to convert rust type `NaiveDateTime` into napi value",
40    )?;
41
42    Ok(ptr)
43  }
44}
45
46impl FromNapiValue for NaiveDateTime {
47  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
48    let mut to_iso_string = ptr::null_mut();
49    check_status!(
50      unsafe {
51        napi_sys::napi_create_string_utf8(
52          env,
53          "toISOString\0".as_ptr().cast(),
54          11,
55          &mut to_iso_string,
56        )
57      },
58      "create toISOString JavaScript string failed"
59    )?;
60    let mut to_iso_string_method = ptr::null_mut();
61    check_status!(
62      unsafe { sys::napi_get_property(env, napi_val, to_iso_string, &mut to_iso_string_method) },
63      "get toISOString method failed"
64    )?;
65    let mut iso_string_value = ptr::null_mut();
66    check_status!(
67      unsafe {
68        sys::napi_call_function(
69          env,
70          napi_val,
71          to_iso_string_method,
72          0,
73          ptr::null(),
74          &mut iso_string_value,
75        )
76      },
77      "Call toISOString on Date Object failed"
78    )?;
79
80    let mut iso_string_length = 0;
81    check_status!(
82      unsafe {
83        sys::napi_get_value_string_utf8(
84          env,
85          iso_string_value,
86          ptr::null_mut(),
87          0,
88          &mut iso_string_length,
89        )
90      },
91      "Get ISOString length failed"
92    )?;
93    let mut iso_string = String::with_capacity(iso_string_length + 1);
94    check_status!(
95      unsafe {
96        sys::napi_get_value_string_utf8(
97          env,
98          iso_string_value,
99          iso_string.as_mut_ptr().cast(),
100          iso_string_length,
101          &mut iso_string_length,
102        )
103      },
104      "Get ISOString length failed"
105    )?;
106
107    unsafe { iso_string.as_mut_vec().set_len(iso_string_length) };
108
109    let naive = NaiveDateTime::from_str(iso_string.as_str()).map_err(|err| {
110      Error::new(
111        Status::InvalidArg,
112        format!(
113          "Failed to convert napi value into rust type `NaiveDateTime` {} {}",
114          err, iso_string
115        ),
116      )
117    })?;
118
119    Ok(naive)
120  }
121}
122
123impl ToNapiValue for DateTime<Utc> {
124  unsafe fn to_napi_value(env: sys::napi_env, val: DateTime<Utc>) -> Result<sys::napi_value> {
125    let mut ptr = std::ptr::null_mut();
126    let millis_since_epoch_utc = val.timestamp_millis() as f64;
127
128    check_status!(
129      unsafe { sys::napi_create_date(env, millis_since_epoch_utc, &mut ptr) },
130      "Failed to convert rust type `DateTime<Utc>` into napi value",
131    )?;
132
133    Ok(ptr)
134  }
135}
136
137impl FromNapiValue for DateTime<Utc> {
138  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
139    let mut milliseconds_since_epoch_utc = 0.0;
140
141    check_status!(
142      unsafe { sys::napi_get_date_value(env, napi_val, &mut milliseconds_since_epoch_utc) },
143      "Failed to convert napi value into rust type `DateTime<Utc>`",
144    )?;
145
146    let milliseconds_since_epoch_utc = milliseconds_since_epoch_utc as i64;
147    let timestamp_seconds = milliseconds_since_epoch_utc / 1_000;
148    let naive = NaiveDateTime::from_timestamp_opt(
149      timestamp_seconds,
150      (milliseconds_since_epoch_utc % 1_000 * 1_000_000) as u32,
151    )
152    .ok_or_else(|| Error::new(Status::DateExpected, "Found invalid date".to_owned()))?;
153    Ok(DateTime::<Utc>::from_naive_utc_and_offset(naive, Utc))
154  }
155}