Merge pull request #1069 from simonvandel/chrono_datetime
feat: Add support for Date <-> chrono::DateTime<Utc>
This commit is contained in:
commit
14773af159
10 changed files with 104 additions and 2 deletions
|
@ -168,6 +168,7 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
("BigInt64Array", "BigInt64Array"),
|
("BigInt64Array", "BigInt64Array"),
|
||||||
("BigUint64Array", "BigUint64Array"),
|
("BigUint64Array", "BigUint64Array"),
|
||||||
("DataView", "DataView"),
|
("DataView", "DataView"),
|
||||||
|
("DateTime", "Date"),
|
||||||
("Date", "Date"),
|
("Date", "Date"),
|
||||||
("JsDate", "Date"),
|
("JsDate", "Date"),
|
||||||
("JsBuffer", "Buffer"),
|
("JsBuffer", "Buffer"),
|
||||||
|
|
|
@ -18,9 +18,10 @@ independent = true
|
||||||
[features]
|
[features]
|
||||||
async = ["tokio_rt"]
|
async = ["tokio_rt"]
|
||||||
compat-mode = []
|
compat-mode = []
|
||||||
default = ["napi3", "compat-mode"] # for most Node.js users
|
default = ["napi3", "compat-mode"] # for most Node.js users
|
||||||
experimental = ["napi-sys/experimental"]
|
experimental = ["napi-sys/experimental"]
|
||||||
full = ["latin1", "napi8", "async", "serde-json", "experimental"]
|
chrono_date = ["chrono", "napi5"]
|
||||||
|
full = ["latin1", "napi8", "async", "serde-json", "experimental", "chrono_date"]
|
||||||
latin1 = ["encoding_rs"]
|
latin1 = ["encoding_rs"]
|
||||||
napi1 = []
|
napi1 = []
|
||||||
napi2 = ["napi1"]
|
napi2 = ["napi1"]
|
||||||
|
@ -54,6 +55,10 @@ napi-sys = { version = "2.1.0", path = "../sys" }
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
|
|
||||||
|
[dependencies.chrono]
|
||||||
|
optional = true
|
||||||
|
version = "0.4"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
features = ["rt", "rt-multi-thread", "sync"]
|
features = ["rt", "rt-multi-thread", "sync"]
|
||||||
optional = true
|
optional = true
|
||||||
|
|
|
@ -8,6 +8,8 @@ mod arraybuffer;
|
||||||
mod bigint;
|
mod bigint;
|
||||||
mod boolean;
|
mod boolean;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
#[cfg(all(feature = "chrono_date", feature = "napi5"))]
|
||||||
|
mod date;
|
||||||
mod either;
|
mod either;
|
||||||
mod external;
|
mod external;
|
||||||
mod function;
|
mod function;
|
||||||
|
|
52
crates/napi/src/bindgen_runtime/js_values/date.rs
Normal file
52
crates/napi/src/bindgen_runtime/js_values/date.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use crate::{bindgen_prelude::*, check_status, sys, ValueType};
|
||||||
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
|
|
||||||
|
impl TypeName for DateTime<Utc> {
|
||||||
|
fn type_name() -> &'static str {
|
||||||
|
"DateTime"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_type() -> ValueType {
|
||||||
|
ValueType::Object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValidateNapiValue for DateTime<Utc> {
|
||||||
|
fn type_of() -> Vec<ValueType> {
|
||||||
|
vec![ValueType::Object]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNapiValue for DateTime<Utc> {
|
||||||
|
unsafe fn to_napi_value(env: sys::napi_env, val: DateTime<Utc>) -> Result<sys::napi_value> {
|
||||||
|
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 `DateTime<Utc>` into napi value",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNapiValue for DateTime<Utc> {
|
||||||
|
unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
|
||||||
|
let mut milliseconds_since_epoch_utc = 0.0;
|
||||||
|
|
||||||
|
check_status!(
|
||||||
|
unsafe { sys::napi_get_date_value(env, napi_val, &mut milliseconds_since_epoch_utc) },
|
||||||
|
"Failed to convert napi value into rust type `DateTime<Utc>`",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let milliseconds_since_epoch_utc = milliseconds_since_epoch_utc as i64;
|
||||||
|
let timestamp_seconds = milliseconds_since_epoch_utc / 1_000;
|
||||||
|
let naive = NaiveDateTime::from_timestamp_opt(
|
||||||
|
timestamp_seconds,
|
||||||
|
(milliseconds_since_epoch_utc % 1_000 * 1_000_000) as u32,
|
||||||
|
)
|
||||||
|
.ok_or_else(|| Error::new(Status::DateExpected, "Found invalid date".to_owned()))?;
|
||||||
|
Ok(DateTime::<Utc>::from_utc(naive, Utc))
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ version = "0.1.0"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
napi = { path = "../../crates/napi", default-features = false, features = [
|
napi = { path = "../../crates/napi", default-features = false, features = [
|
||||||
"tokio_fs",
|
"tokio_fs",
|
||||||
|
@ -18,6 +19,7 @@ napi = { path = "../../crates/napi", default-features = false, features = [
|
||||||
"async",
|
"async",
|
||||||
"experimental",
|
"experimental",
|
||||||
"latin1",
|
"latin1",
|
||||||
|
"chrono_date",
|
||||||
] }
|
] }
|
||||||
napi-derive = { path = "../../crates/macro", features = ["type-def"] }
|
napi-derive = { path = "../../crates/macro", features = ["type-def"] }
|
||||||
serde = "1"
|
serde = "1"
|
||||||
|
|
|
@ -41,6 +41,12 @@ Generated by [AVA](https://avajs.dev).
|
||||||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void␊
|
||||||
export function returnJsFunction(): (...args: any[]) => any␊
|
export function returnJsFunction(): (...args: any[]) => any␊
|
||||||
export function dateToNumber(input: Date): number␊
|
export function dateToNumber(input: Date): number␊
|
||||||
|
export function chronoDateToMillis(input: Date): number␊
|
||||||
|
export function chronoDateAdd1Minute(input: Date): Date␊
|
||||||
|
export interface Dates {␊
|
||||||
|
start: Date␊
|
||||||
|
end?: Date | undefined | null␊
|
||||||
|
}␊
|
||||||
export function eitherStringOrNumber(input: string | number): number␊
|
export function eitherStringOrNumber(input: string | number): number␊
|
||||||
export function returnEither(input: number): string | number␊
|
export function returnEither(input: number): string | number␊
|
||||||
export function either3(input: string | number | boolean): number␊
|
export function either3(input: string | number | boolean): number␊
|
||||||
|
|
Binary file not shown.
|
@ -83,7 +83,9 @@ import {
|
||||||
testSerdeRoundtrip,
|
testSerdeRoundtrip,
|
||||||
createObjWithProperty,
|
createObjWithProperty,
|
||||||
dateToNumber,
|
dateToNumber,
|
||||||
|
chronoDateToMillis,
|
||||||
derefUint8Array,
|
derefUint8Array,
|
||||||
|
chronoDateAdd1Minute,
|
||||||
} from '../'
|
} from '../'
|
||||||
|
|
||||||
test('export const', (t) => {
|
test('export const', (t) => {
|
||||||
|
@ -564,3 +566,12 @@ Napi5Test('Date test', (t) => {
|
||||||
const fixture = new Date('2016-12-24')
|
const fixture = new Date('2016-12-24')
|
||||||
t.is(dateToNumber(fixture), fixture.valueOf())
|
t.is(dateToNumber(fixture), fixture.valueOf())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Napi5Test('Date to chrono test', (t) => {
|
||||||
|
const fixture = new Date('2022-02-09T19:31:55.396Z')
|
||||||
|
t.is(chronoDateToMillis(fixture), fixture.getTime())
|
||||||
|
t.deepEqual(
|
||||||
|
chronoDateAdd1Minute(fixture),
|
||||||
|
new Date(fixture.getTime() + 60 * 1000),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
6
examples/napi/index.d.ts
vendored
6
examples/napi/index.d.ts
vendored
|
@ -31,6 +31,12 @@ export function optionOnly(callback: (arg0?: string | undefined | null) => void)
|
||||||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
||||||
export function returnJsFunction(): (...args: any[]) => any
|
export function returnJsFunction(): (...args: any[]) => any
|
||||||
export function dateToNumber(input: Date): number
|
export function dateToNumber(input: Date): number
|
||||||
|
export function chronoDateToMillis(input: Date): number
|
||||||
|
export function chronoDateAdd1Minute(input: Date): Date
|
||||||
|
export interface Dates {
|
||||||
|
start: Date
|
||||||
|
end?: Date | undefined | null
|
||||||
|
}
|
||||||
export function eitherStringOrNumber(input: string | number): number
|
export function eitherStringOrNumber(input: string | number): number
|
||||||
export function returnEither(input: number): string | number
|
export function returnEither(input: number): string | number
|
||||||
export function either3(input: string | number | boolean): number
|
export function either3(input: string | number | boolean): number
|
||||||
|
|
|
@ -1,6 +1,23 @@
|
||||||
|
use chrono::{Duration, Utc};
|
||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
fn date_to_number(input: Date) -> Result<f64> {
|
fn date_to_number(input: Date) -> Result<f64> {
|
||||||
input.value_of()
|
input.value_of()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn chrono_date_to_millis(input: chrono::DateTime<Utc>) -> i64 {
|
||||||
|
input.timestamp_millis()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
fn chrono_date_add_1_minute(input: chrono::DateTime<Utc>) -> chrono::DateTime<Utc> {
|
||||||
|
input + Duration::minutes(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(object)]
|
||||||
|
pub struct Dates {
|
||||||
|
pub start: chrono::DateTime<Utc>,
|
||||||
|
pub end: Option<chrono::DateTime<Utc>>,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue