Merge pull request #1069 from simonvandel/chrono_datetime

feat: Add support for Date <-> chrono::DateTime<Utc>
This commit is contained in:
LongYinan 2022-02-11 15:18:34 +08:00 committed by GitHub
commit 14773af159
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 104 additions and 2 deletions

View file

@ -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"),

View file

@ -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

View file

@ -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;

View 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))
}
}

View file

@ -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"

View file

@ -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␊

View file

@ -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),
)
})

View file

@ -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

View file

@ -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>>,
}