vintage-time
v1.5.2
Published
DateTime x DateOnly library with locale support. Compatible with sequelize, joi, momentjs and plain javascript Dates
Downloads
20
Maintainers
Readme
vintage-time
DateTime x DateOnly library with locale support. Compatible with sequelize, joi and plain javascript Dates
Distributed through the npm packge vintage-time
Installation
npm install vintage-time
or
yarn add vintage-time
Usage
This library defines a clear separation between values that should be DateOnly and those that should be DateTime.
For this end, we have two distinc classes that are (kinda) compatible to each other:
- DateOnly - only stores the date part (year, month and day). It completly ignores the timezone.
- DateTime - stores date and time (year, month, day, hour, minute, second, millisecond, timezone).
Examples:
const date = new Date('2024-01-01 12:44:00.567');
const dateOnly = DateOnly.fromJsDate(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
const dateTime = DateTime.fromJsDate(date);
console.log(dateTime.toString()); // logs: "2024-01-01T12:44:00.567" + localTimezoneOffset
Simplified usage
The library ships a set of helper functions that automatically handle most scenarios and return the appropriate result.
toDateOnly
This method converts a date compatible value into a DateOnly instance. So it works out of the box with values like: numbers, Date, string, Moment, DateTime and DateOnly.
An alternative to this method is to call DateOnly.fromAnyDate
directly which handles almost the same scenarios. Another one would be to call any of the specialized factory methods depending on the input.
let date, dateOnly;
// # Create from a plain objects:
date = {year: 2024, month: 1, day: 1};
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
// # Create from a Date object:
date = new Date('2024-01-01 12:44:00.567');
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
// # Create from a numeric timestamp:
date = new Date('2024-01-01 12:44:00.567');
dateOnly = toDateTime(date.getTime());
console.log(dateOnly.toString()); // logs: "2024-01-01"
// # Create from a Moment object:
date = moment('2024-01-01 12:44:00.567');
const dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
// # Create from a formatted date-time string:
date = '2024-01-01 12:44:00.567'; // '2024-01-01T12:44:00.567' also works!
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
// # Create from a formatted date-only string:
date = '2024-01-10';
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-10"
// # Create from a formatted date-time string: (Notice that the timezone is ignored when working with DateOnly)
date = '2024-01-01 12:44:00.567Z'; // '2024-01-01T12:44:00.567Z' also works!
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
date = '2024-01-01 12:44:00.567-10:00'; // '2024-01-01T12:44:00.567Z' also works!
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
date = '2024-01-01 12:44:00.567+10:00'; // '2024-01-01T12:44:00.567Z' also works!
dateOnly = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01"
Calling the method with null or undefined will always return undefined
toDateOnly.now
Shortcut for calling DateOnly.now()
which returns a DateOnly instance from the current system timestamp.
const dateOnly = toDateTime.now(); // Simillar to: toDateTime(new Date())
toDateTime
This method converts a date compatible value into a DateOnly instance. So it works out of the box with values like: numbers, Date, string, Moment, DateTime and DateOnly.
An alternative to this method is to call DateOnly.fromAnyDate
directly which handles almost the same scenarios. Another one would be to call any of the specialized factory methods depending on the input.
let date, dateTime;
// # Create from a plain objects specifying only the date part:
date = {year: 2024, month: 1, day: 1};
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01"
// # Create from a plain objects specifying the date and time part:
date = {year: 2024, month: 1, day: 1, hour: 22, second: 1, timezone: 'UTC'};
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T22:00:01.000Z"
// # Create from a plain objects specifying the date and time part: (Ommiting timezone automatically picks the local system timezone)
date = {year: 2024, month: 1, day: 1, hour: 22, second: 1};
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T22:00:01.000" + localTimezoneOffset
// # Create from a plain objects specifying the date and time part: (Timezone offset can be used instead of a timezone name)
date = {year: 2024, month: 1, day: 1, hour: 22, second: 1, offset: 7};
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T22:00:01.000+07:00"
// # Create from a Date object:
date = new Date('2024-01-01 12:44:00.567');
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T12:44:00.567" + localTimezoneOffset
// # Create from a numeric timestamp:
date = new Date('2024-01-01 12:44:00.567');
dateTime = toDateTime(date.getTime());
console.log(dateTime.toString()); // logs: "2024-01-01T12:44:00.567" + localTimezoneOffset
// # Create from a Moment object:
date = moment('2024-01-01 12:44:00.567');
const dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T12:44:00.567" + localTimezoneOffset
// # Create from a formatted date-time string:
date = '2024-01-01 12:44:00.567'; // '2024-01-01T12:44:00.567' also works!
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-01T12:44:00.567" + localTimezoneOffset
// # Create from a formatted date-only string: (DateOnly instances are always treated as UTC dates)
date = '2024-01-10';
dateTime = toDateTime(date);
console.log(dateTime.toString()); // logs: "2024-01-10T00:00:00.000Z"
// # Create from a formatted date-time string: (The original timezone in the string is used)
date = '2024-01-01 12:44:00.567Z'; // '2024-01-01T12:44:00.567Z' also works!
dateTime = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01T12:44:00.567Z"
date = '2024-01-01 12:44:00.567-10:00'; // '2024-01-01T12:44:00.567Z' also works!
dateTime = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01T12:44:00.567-10:00"
date = '2024-01-01 12:44:00.567+10:00'; // '2024-01-01T12:44:00.567Z' also works!
dateTime = toDateTime(date);
console.log(dateOnly.toString()); // logs: "2024-01-01T12:44:00.567+10:00"
Calling the method with null or undefined will always return undefined
toDateTime.now
Shortcut for calling DateTime.now(locale)
which returns a DateTime instance from the current system timestamp.
const dateTime = toDateTime.now(); // Simillar to: toDateTime(new Date())
toDateTime.tz
Shortcut for calling toDateTime(anyDate, locale).toTimezone(tz)
which returns a DateTime instance at a specific timezone.
const dateTime = toDateTime.tz('2023-12-31', 'Asia/Jakarta'); // Change the TZ from UTC to +07:00
console.log(dateTime.toISOString(false)); // Output "2023-12-31T07:00:00.000+07:00"
toDateTime.as
It changes the timezone of the date-time but keeps the original date and time values:
- year
- month
- day
- hour
- minutes
- second
- millisecond
let dateTime = toDateTime.tz('2023-12-31', 'Asia/Jakarta'); // Change the TZ from UTC to +07:00 (shift the offset to the new timezone)
console.log(dateTime.toISOString(false)); // Output "2023-12-31T07:00:00.000+07:00"
console.log(dateTime.toISOString(true)); // Output "2023-12-31T07:00:00.000+07:00"
DateTime.isEqual(dateTime, '2023-12-31'); // true
dateTime = toDateTime.as('2023-12-31', 'Asia/Jakarta'); // Change the TZ from UTC to +07:00 keeping the original date and time values
console.log(dateTime.toISOString(false)); // Output "2023-12-31T00:00:00.000+07:00"
console.log(dateTime.toISOString(true)); // Output "2023-12-30T17:00:00.000Z"
DateTime.isEqual(dateTime, '2023-12-31'); // false
Comparing dates
The best approach is to use the built-in methods which check equality properly and convert all values to the proper date class.
- isEqual - checks equality between dates (
===
usingvalueOf()
) - isBefore - checks dateA < dateB
- isAfter - checks dateA > dateB
- isEqualBefore - checks dateA <= dateB
- isEqualAfter - checks dateA >= dateB
const dateTimeA = toDateTime('2024-01-01T12:44:00.567Z');
const dateTimeB = toDateTime('2024-01-02T07:33:51.120-03:00');
const dateOnlyA = toDateOnly(toDateTime);
const dateOnlyB = toDateOnly(toDateTime);
// Use DateTime for comparing dates for both their date and time values
console.log(DateTime.isEqual(dateOnlyA, dateOnlyA)); // true
console.log(DateTime.isEqual(dateTimeA, dateTimeA)); // true
console.log(DateTime.isEqual(dateOnlyA, dateTimeA)); // false
console.log(DateTime.isEqual(dateTimeA, dateOnlyA)); // false
console.log(DateTime.isEqual(dateOnlyA, dateOnlyB)); // false
console.log(DateTime.isEqualOrBefore(dateOnlyA, dateOnlyB)); // true
console.log(DateTime.isBefore(dateOnlyA, dateOnlyB)); // true
console.log(DateTime.isEqualOrAfter(dateOnlyA, dateOnlyB)); // false
console.log(DateTime.isAfter(dateOnlyA, dateOnlyB)); // false
// Use DateOnly when time does not matter and you only want to check the date parts
console.log(DateOnly.isEqual(dateOnlyA, dateOnlyA)); // true
console.log(DateOnly.isEqual(dateTimeA, dateTimeA)); // true
console.log(DateOnly.isEqual(dateOnlyA, dateTimeA)); // true
console.log(DateOnly.isEqual(dateTimeA, dateOnlyA)); // true
console.log(DateOnly.isEqual(dateOnlyA, dateOnlyB)); // false
console.log(DateOnly.isEqualOrBefore(dateOnlyA, dateOnlyB)); // true
console.log(DateOnly.isBefore(dateOnlyA, dateOnlyB)); // true
console.log(DateOnly.isEqualOrAfter(dateOnlyA, dateOnlyB)); // false
console.log(DateOnly.isAfter(dateOnlyA, dateOnlyB)); // false
Still, it also works through the method valueOf
. It implicitly works as long as both sides of the operator are values of the same type. In the other situations an explicitly call is necessary.
const dateTimeA = toDateTime('2024-01-01T12:44:00.567Z');
const dateTimeB = toDateTime('2024-01-02T07:33:51.120-03:00');
console.log(dateTimeA < dateTimeB); // true
console.log(dateTimeA <= dateTimeB); // true
console.log(dateTimeA > dateTimeB); // false
console.log(dateTimeA >= dateTimeB); // false
// Equality - Note that we explicitly use `.valueOf()` here to avoid comparing by reference
console.log(dateTimeA.valueOf() === toDateOnly(dateTimeA).valueOf()); // true
console.log(dateTimeA.valueOf() !== dateTimeB.valueOf()); // true
console.log(dateTimeA.valueOf() !== dateTimeA.valueOf()); // false
const dateOnlyA = toDateOnly(toDateTime);
const dateOnlyB = toDateOnly(toDateTime);
console.log(dateOnlyA < dateOnlyB); // true
console.log(dateOnlyA <= dateOnlyB); // true
console.log(dateOnlyA > dateOnlyB); // false
console.log(dateOnlyA >= dateOnlyB); // false
// Equality - Note that we explicitly use `.valueOf()` here to avoid comparing by reference
console.log(dateOnlyA.valueOf() === toDateOnly(dateTimeA).valueOf()); // true
console.log(dateOnlyA.valueOf() !== dateOnlyB.valueOf()); // true
console.log(dateOnlyA.valueOf() !== dateOnlyA.valueOf()); // false
//
Plugins
Sequelize
vintage-time
requires sequelize 6.x
installed. Please make sure to have it installed in order to work.
Even without using the plugin, sequelize already accepts both DateOnly
and DateTime
for queries.
Since sequeize only supports JS natve Dates, we need to define getters and setters to our Date properties in order to use a different class. This plugin offers two built-in methods to define the getters and setters.
dateOnlyColumn
function dateOnlyColumn(propertyName: string, strict?: boolean): {
type: typeof DataTypes.DATEONLY,
get(): DateOnly | null;
set(value: any): void;
};
Used to defined a DateOnly
column (sequelize type DATEONLY).
const {dateOnlyColumn} = require('vintage-time/plugins/sequelize');
const model = sequelize.define('DateDummy', {
startDate: {
...dateOnlyColumn('startDate'),
allowNull: true,
},
},
{tableName: 'dummies'}
);
// ...
const entry = model.findOne();
console.log(entry.startDate instanceof DateOnly); // true
The parameter strict
when true
enforces that the setter should only accept DateOnly instances. Otherwise when false
it attempts to convert any value to a DateOnly
instance.
dateTimeColumn
function dateTimeColumn(propertyName: string, strict?: boolean): {
type: typeof DataTypes.DATEO,
get(): DateTime | null;
set(value: any): void;
};
Used to defined a DateTime
column (sequelize type DATE).
const {dateTimeColumn} = require('vintage-time/plugins/sequelize');
const model = sequelize.define('DateDummy', {
archivedAt: {
...dateTimeColumn('archivedAt'),
allowNull: false,
defaultValue: () => toDateTime.now(),
},
},
{tableName: 'dummies'}
);
// ...
const entry = model.findOne();
console.log(entry.archivedAt instanceof DateTime); // true
The parameter strict
when true
enforces that the setter should only accept DateOnly instances. Otherwise when false
it attempts to convert any value to a DateTime
instance.
Joi
vintage-time
requires joi 17.x
installed. Please make sure to have it installed in order to work.
The plugin exposes a set of validators to be used together with Joi validation schemas.
anyDate
Accepts both DateOnl
y and DateTime
compatible strings.
Unless it is set to raw()
, the validator outputs a DateTime
instance.
const {anyDate} = require('vintage-time/plugins/joi');
joi = joi.extend(anyDate);
const schema = joi.object().keys({date: joi.anyDate()});
// These are valid strings
schema.validate({date: '2020-07-19'});
schema.validate({date: '2020-07-19 00:00:00.000Z'});
schema.validate({date: '2020-07-19 00:00:00Z'});
schema.validate({date: '2020-07-19 00:00:00-03:00'});
schema.validate({date: '2020-07-19 01:20:03'});
schema.validate({date: '2020-07-19 01:20:03.657Z'});
schema.validate({date: '2020-07-19T00:00:00Z'});
schema.validate({date: '2020-07-19T00:00:00-03:00'});
schema.validate({date: '2020-07-19T01:20:03'});
schema.validate({date: '2020-07-19T01:20:03.657Z'});
// These are not
schema.validate({date: '07/19/2020'});
schema.validate({date: '2020/07/19'});
schema.validate({date: '01:20:03.657Z'});
schema.validate({date: '2020/07/19 at 3:00 PM'});
dateOnly
Accepts DateOnly
compatible strings.
Unless it is set to raw()
, the validator outputs a DateOnly
instance.
const {dateOnly} = require('vintage-time/plugins/joi');
joi = joi.extend(dateOnly);
const schema = joi.object().keys({date: joi.dateOnly()});
// These are valid strings
schema.validate({date: '2020-07-19'});
schema.validate({date: '1990-01-11'});
// These are not
schema.validate({date: '2020-07-19 00:00:00Z'});
schema.validate({date: '2020-07-19 00:00:00-03:00'});
schema.validate({date: '2020-07-19 01:20:03'});
schema.validate({date: '2020-07-19 01:20:03.657Z'});
schema.validate({date: '2020-07-19T01:20:03'});
schema.validate({date: '2020-07-19T01:20:03.657Z'});
schema.validate({date: '2020-07-19T00:00:00Z'});
schema.validate({date: '2020-07-19T00:00:00-03:00'});
schema.validate({date: '07/19/2020'});
schema.validate({date: '2020/07/19'});
schema.validate({date: '01:20:03.657Z'});
schema.validate({date: '2020/07/19 at 3:00 PM'});
dateTime
Accepts DateTime
compatible strings.
Unless it is set to raw()
, the validator outputs a DateTime
instance.
const {dateTime} = require('vintage-time/plugins/joi');
joi = joi.extend(dateTime);
const schema = joi.object().keys({date: joi.dateTime()});
// These are valid strings
schema.validate({date: '2020-07-19 00:00:00.000Z'});
schema.validate({date: '2020-07-19 00:00:00Z'});
schema.validate({date: '2020-07-19 00:00:00-03:00'});
schema.validate({date: '2020-07-19 01:20:03'});
schema.validate({date: '2020-07-19 01:20:03.657Z'});
schema.validate({date: '2020-07-19T00:00:00Z'});
schema.validate({date: '2020-07-19T00:00:00-03:00'});
schema.validate({date: '2020-07-19T01:20:03'});
schema.validate({date: '2020-07-19T01:20:03.657Z'});
// These are not
schema.validate({date: '2020-07-19'});
schema.validate({date: '1990-01-11'});
schema.validate({date: '07/19/2020'});
schema.validate({date: '2020/07/19'});
schema.validate({date: '01:20:03.657Z'});
schema.validate({date: '2020/07/19 at 3:00 PM'});