Skip to content

Commit

Permalink
Add DateString type
Browse files Browse the repository at this point in the history
New type that preserves string input as a string, but ensures that
the string is a valid Date.

Additionally, provides a .toDate function to provide the Date
object representation of the string.
  • Loading branch information
Kevin Delisle committed May 1, 2017
1 parent 97f243f commit 5e80837
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
99 changes: 99 additions & 0 deletions lib/date-string.js
@@ -0,0 +1,99 @@
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

'use strict';

var inspect = require('util').inspect;

module.exports = DateString;

/**
* A String whose value is a valid representation of a Date.
* Use this type if you need to preserve the format of the value and still
* check if it's valid.
* Example:
* ```js
* var loopback = require('loopback');
* var dt = new loopback.DateString('2001-01-01');
*
* dt.toString();
* // '2001-01-01'
* dt._date.toISOString();
* // '2001-01-01T00:00:00.000Z'
* ```
*
* You can use this definition on your models as well:
* ```json
* {
* "name": "Person",
* "base": "PersistedModel",
* "properties": {
* "name": {
* "type": "string"
* },
* "dob": {
* "type": "DateString",
* "required": true
* },
* },
* "validations": [],
* "relations": {},
* "acls": [],
* "methods": {}
* }
* ```
* @class DateString
* @param {String} value
* @constructor
*/
function DateString(value) {
if (!(this instanceof DateString)) {
return new DateString(value);
}

if (typeof(value) !== 'string') {
throw new Error('Input must be a string');
}

Object.defineProperty(this, 'when', {
get: () => { return this._when; },
set: (val) => {
var d = new Date(val);
if (isNaN(d.getTime())) {
throw new Error('Invalid date');
} else {
this._when = val;
this._date = d;
}
},
});

this.when = value;
};

/**
* Returns the value of DateString in its original form.
* @returns {String} The Date as a String.
*/
DateString.prototype.toString = function() {
return this.when;
};

/**
* Returns the JSON representation of the DateString object.
* @returns {String} A JSON string.
*/
DateString.prototype.toJSON = function() {
return JSON.stringify({
when: this.when,
});
};

DateString.prototype.inspect = function(depth, options) {
return 'DateString ' + inspect({
when: this.when,
_date: this._date,
});
};
2 changes: 2 additions & 0 deletions lib/types.js
Expand Up @@ -40,6 +40,7 @@ Types.Any.prototype.toObject = Types.Any.prototype.toJSON = function() {
};

module.exports = function(modelTypes) {
var DateString = require('./date-string');
var GeoPoint = require('./geo').GeoPoint;

for (var t in Types) {
Expand All @@ -63,6 +64,7 @@ module.exports = function(modelTypes) {
modelTypes.registerType(Number);
modelTypes.registerType(Boolean);
modelTypes.registerType(Date);
modelTypes.registerType(DateString);
modelTypes.registerType(Buffer, ['Binary']);
modelTypes.registerType(Array);
modelTypes.registerType(GeoPoint);
Expand Down
81 changes: 81 additions & 0 deletions test/date-string.test.js
@@ -0,0 +1,81 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-datasource-juggler
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

/* global describe,it */
/* jshint expr:true */

'use strict';

require('should');

var DateString = require('../lib/date-string');
var fmt = require('util').format;
var inspect = require('util').inspect;
var os = require('os');

describe('DateString', function() {
describe('constructor', function() {
it('should support a valid date string', function() {
var theDate = '2015-01-01';
var date = new DateString(theDate);
date.should.not.eql(null);
date.when.should.eql(theDate);
date.toString().should.eql(theDate);
});

testValidInput('should allow date with time', '2015-01-01 02:00:00');
testValidInput('should allow full UTC datetime', '2015-06-30T20:00:00.000Z');
testValidInput('should allow date with UTC offset', '2015-01-01 20:00:00 GMT-5');

testInvalidInput('should throw on non-date string', 'notadate', 'Invalid date');
testInvalidInput('should throw on incorrect date-like value',
'2015-01-01 25:00:00', 'Invalid date');
testInvalidInput('should throw on non-string input', 20150101,
'Input must be a string');
testInvalidInput('should throw on null input', null, 'Input must be a string');

it('should update internal date on set', function() {
var date = new DateString('2015-01-01');
date.when = '2016-01-01';
date.when.should.eql('2016-01-01');
var d = new Date('2016-01-01');
// The internal date representation should also be updated!
date._date.toString().should.eql(d.toString());
});
it('should return custom inspect output', function() {
var date = new DateString('2015-01-01');
var result = inspect(date);
result.should.not.eql(null);
result.should.eql(fmt('DateString ' + inspect({
when: date.when,
_date: date._date,
})));
});

it('should return JSON output', function() {
var date = new DateString('2015-01-01');
var result = date.toJSON();
result.should.eql(JSON.stringify({when: date.when}));
});

function testValidInput(msg, val) {
it(msg, function() {
var theDate = new DateString(val);
theDate.when.should.eql(val);
var d = new Date(val);
theDate._date.toString().should.eql(d.toString());
});
}

function testInvalidInput(msg, val, err) {
it(msg, () => {
var fn = () => {
var theDate = new DateString(val);
};
fn.should.throw(err);
});
}
});
});

0 comments on commit 5e80837

Please sign in to comment.