From 19f41af1c079550fa311d0aee7d9be25d7c4e4f2 Mon Sep 17 00:00:00 2001 From: John French Date: Fri, 28 Jun 2019 21:28:05 +0200 Subject: [PATCH] src: add napi_date PR-URL: https://github.com/nodejs/node-addon-api/pull/497 Reviewed-By: NickNaso Reviewed-By: Michael Dawson Reviewed-By: Gabriel Schulhof Reviewed-By: Anna Henningsen --- README.md | 1 + doc/basic_types.md | 8 ++++++ doc/date.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++ doc/value.md | 9 ++++++ napi-inl.h | 44 ++++++++++++++++++++++++++++++ napi.h | 24 ++++++++++++++++ test/binding.cc | 6 ++++ test/binding.gyp | 1 + test/date.cc | 45 ++++++++++++++++++++++++++++++ test/date.js | 20 ++++++++++++++ test/index.js | 6 ++++ 11 files changed, 232 insertions(+) create mode 100644 doc/date.md create mode 100644 test/date.cc create mode 100644 test/date.js diff --git a/README.md b/README.md index 77e1c7e..9ab1e9f 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ The following is the documentation for node-addon-api. - [String](doc/string.md) - [Name](doc/basic_types.md#name) - [Number](doc/number.md) + - [Date](doc/date.md) - [BigInt](doc/bigint.md) - [Boolean](doc/boolean.md) - [Env](doc/env.md) diff --git a/doc/basic_types.md b/doc/basic_types.md index b01269d..03ec14b 100644 --- a/doc/basic_types.md +++ b/doc/basic_types.md @@ -257,6 +257,14 @@ bool Napi::Value::IsExternal() const; Returns `true` if the underlying value is a N-API external object or `false` otherwise. +#### IsDate +```cpp +bool Napi::Value::IsDate() const; +``` + +Returns `true` if the underlying value is a JavaScript `Date` or `false` +otherwise. + #### ToBoolean ```cpp Napi::Boolean Napi::Value::ToBoolean() const; diff --git a/doc/date.md b/doc/date.md new file mode 100644 index 0000000..959b4b9 --- /dev/null +++ b/doc/date.md @@ -0,0 +1,68 @@ +# Date + +`Napi::Date` class is a representation of the JavaScript `Date` object. The +`Napi::Date` class inherits its behavior from `Napi::Value` class +(for more info see [`Napi::Value`](value.md)) + +## Methods + +### Constructor + +Creates a new _empty_ instance of a `Napi::Date` object. + +```cpp +Napi::Date::Date(); +``` + +Creates a new _non-empty_ instance of a `Napi::Date` object. + +```cpp +Napi::Date::Date(napi_env env, napi_value value); +``` + + - `[in] env`: The environment in which to construct the `Napi::Date` object. + - `[in] value`: The `napi_value` which is a handle for a JavaScript `Date`. + +### New + +Creates a new instance of a `Napi::Date` object. + +```cpp +static Napi::Date Napi::Date::New(Napi::Env env, double value); +``` + + - `[in] env`: The environment in which to construct the `Napi::Date` object. + - `[in] value`: The time value the JavaScript `Date` will contain represented + as the number of milliseconds since 1 January 1970 00:00:00 UTC. + +Returns a new instance of `Napi::Date` object. + +### ValueOf + +```cpp +double Napi::Date::ValueOf() const; +``` + +Returns the time value as `double` primitive represented as the number of + milliseconds since 1 January 1970 00:00:00 UTC. + +## Operators + +### operator double + +Converts a `Napi::Date` value to a `double` primitive. + +```cpp +Napi::Date::operator double() const; +``` + +### Example + +The following shows an example of casting a `Napi::Date` value to a `double` + primitive. + +```cpp +double operatorVal = Napi::Date::New(Env(), 0); // Napi::Date to double +// or +auto instanceVal = info[0].As().ValueOf(); +``` diff --git a/doc/value.md b/doc/value.md index e9f9f8a..2d25eb7 100644 --- a/doc/value.md +++ b/doc/value.md @@ -10,6 +10,7 @@ The following classes inherit, either directly or indirectly, from `Napi::Value` - [`Napi::ArrayBuffer`](array_buffer.md) - [`Napi::Boolean`](boolean.md) - [`Napi::Buffer`](buffer.md) +- [`Napi::Date`](date.md) - [`Napi::External`](external.md) - [`Napi::Function`](function.md) - [`Napi::Name`](name.md) @@ -226,6 +227,14 @@ bool Napi::Value::IsBuffer() const; Returns a `bool` indicating if this `Napi::Value` is a Node buffer. +### IsDate + +```cpp +bool Napi::Value::IsDate() const; +``` + +Returns a `bool` indicating if this `Napi::Value` is a JavaScript date. + ### As ```cpp diff --git a/napi-inl.h b/napi-inl.h index b7a29a0..64315ae 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -379,6 +379,19 @@ inline bool Value::IsBigInt() const { } #endif // NAPI_EXPERIMENTAL +#if (NAPI_VERSION > 4) +inline bool Value::IsDate() const { + if (IsEmpty()) { + return false; + } + + bool result; + napi_status status = napi_is_date(_env, _value, &result); + NAPI_THROW_IF_FAILED(_env, status, false); + return result; +} +#endif + inline bool Value::IsString() const { return Type() == napi_string; } @@ -660,6 +673,37 @@ inline void BigInt::ToWords(int* sign_bit, size_t* word_count, uint64_t* words) } #endif // NAPI_EXPERIMENTAL +#if (NAPI_VERSION > 4) +//////////////////////////////////////////////////////////////////////////////// +// Date Class +//////////////////////////////////////////////////////////////////////////////// + +inline Date Date::New(napi_env env, double val) { + napi_value value; + napi_status status = napi_create_date(env, val, &value); + NAPI_THROW_IF_FAILED(env, status, Date()); + return Date(env, value); +} + +inline Date::Date() : Value() { +} + +inline Date::Date(napi_env env, napi_value value) : Value(env, value) { +} + +inline Date::operator double() const { + return ValueOf(); +} + +inline double Date::ValueOf() const { + double result; + napi_status status = napi_get_date_value( + _env, _value, &result); + NAPI_THROW_IF_FAILED(_env, status, 0); + return result; +} +#endif + //////////////////////////////////////////////////////////////////////////////// // Name class //////////////////////////////////////////////////////////////////////////////// diff --git a/napi.h b/napi.h index aa650de..921aaa8 100644 --- a/napi.h +++ b/napi.h @@ -116,6 +116,9 @@ namespace Napi { #if (NAPI_VERSION > 2147483646) class BigInt; #endif // NAPI_EXPERIMENTAL +#if (NAPI_VERSION > 4) + class Date; +#endif class String; class Object; class Array; @@ -246,6 +249,9 @@ namespace Napi { #if (NAPI_VERSION > 2147483646) bool IsBigInt() const; ///< Tests if a value is a JavaScript bigint. #endif // NAPI_EXPERIMENTAL +#if (NAPI_VERSION > 4) + bool IsDate() const; ///< Tests if a value is a JavaScript date. +#endif bool IsString() const; ///< Tests if a value is a JavaScript string. bool IsSymbol() const; ///< Tests if a value is a JavaScript symbol. bool IsArray() const; ///< Tests if a value is a JavaScript array. @@ -358,6 +364,24 @@ namespace Napi { }; #endif // NAPI_EXPERIMENTAL +#if (NAPI_VERSION > 4) + /// A JavaScript date value. + class Date : public Value { + public: + /// Creates a new Date value from a double primitive. + static Date New( + napi_env env, ///< N-API environment + double value ///< Number value + ); + + Date(); ///< Creates a new _empty_ Date instance. + Date(napi_env env, napi_value value); ///< Wraps a N-API value primitive. + operator double() const; ///< Converts a Date value to double primitive + + double ValueOf() const; ///< Converts a Date value to a double primitive. + }; + #endif + /// A JavaScript string or symbol value (that can be used as a property name). class Name : public Value { public: diff --git a/test/binding.cc b/test/binding.cc index bf0f7c0..dca0077 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -20,6 +20,9 @@ Object InitBuffer(Env env); #if (NAPI_VERSION > 2) Object InitCallbackScope(Env env); #endif +#if (NAPI_VERSION > 4) +Object InitDate(Env env); +#endif Object InitDataView(Env env); Object InitDataViewReadWrite(Env env); Object InitError(Env env); @@ -56,6 +59,9 @@ Object Init(Env env, Object exports) { // released in once it is no longer experimental #if (NAPI_VERSION > 2147483646) exports.Set("bigint", InitBigInt(env)); +#endif +#if (NAPI_VERSION > 4) + exports.Set("date", InitDate(env)); #endif exports.Set("buffer", InitBuffer(env)); #if (NAPI_VERSION > 2) diff --git a/test/binding.gyp b/test/binding.gyp index c8acb1b..f8c1cb0 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -14,6 +14,7 @@ 'basic_types/number.cc', 'basic_types/value.cc', 'bigint.cc', + 'date.cc', 'binding.cc', 'buffer.cc', 'callbackscope.cc', diff --git a/test/date.cc b/test/date.cc new file mode 100644 index 0000000..6c1d2cb --- /dev/null +++ b/test/date.cc @@ -0,0 +1,45 @@ +#define NAPI_EXPERIMENTAL +#include "napi.h" + +using namespace Napi; + +#if (NAPI_VERSION > 4) +namespace { + +Value CreateDate(const CallbackInfo& info) { + double input = info[0].As().DoubleValue(); + + return Date::New(info.Env(), input); +} + +Value IsDate(const CallbackInfo& info) { + Date input = info[0].As(); + + return Boolean::New(info.Env(), input.IsDate()); +} + +Value ValueOf(const CallbackInfo& info) { + Date input = info[0].As(); + + return Number::New(info.Env(), input.ValueOf()); +} + +Value OperatorValue(const CallbackInfo& info) { + Date input = info[0].As(); + + return Boolean::New(info.Env(), input.ValueOf() == static_cast(input)); +} + +} // anonymous namespace + +Object InitDate(Env env) { + Object exports = Object::New(env); + exports["CreateDate"] = Function::New(env, CreateDate); + exports["IsDate"] = Function::New(env, IsDate); + exports["ValueOf"] = Function::New(env, ValueOf); + exports["OperatorValue"] = Function::New(env, OperatorValue); + + return exports; +} + +#endif diff --git a/test/date.js b/test/date.js new file mode 100644 index 0000000..16e618e --- /dev/null +++ b/test/date.js @@ -0,0 +1,20 @@ +'use strict'; + +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`./build/${buildType}/binding.node`)); +test(require(`./build/${buildType}/binding_noexcept.node`)); + +function test(binding) { + const { + CreateDate, + IsDate, + ValueOf, + OperatorValue, + } = binding.date; + assert.deepStrictEqual(CreateDate(0), new Date(0)); + assert.strictEqual(IsDate(new Date(0)), true); + assert.strictEqual(ValueOf(new Date(42)), 42); + assert.strictEqual(OperatorValue(new Date(42)), true); +} diff --git a/test/index.js b/test/index.js index 7f97168..d03c2e5 100644 --- a/test/index.js +++ b/test/index.js @@ -18,6 +18,7 @@ let testModules = [ 'basic_types/number', 'basic_types/value', 'bigint', + 'date', 'buffer', 'callbackscope', 'dataview/dataview', @@ -69,6 +70,11 @@ if ((process.env.npm_config_NAPI_VERSION !== undefined) && testModules.splice(testModules.indexOf('threadsafe_function/threadsafe_function'), 1); } +if ((process.env.npm_config_NAPI_VERSION !== undefined) && + (process.env.npm_config_NAPI_VERSION < 5)) { + testModules.splice(testModules.indexOf('date'), 1); +} + if (typeof global.gc === 'function') { console.log('Starting test suite\n');