Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change/timezone locale #6

Merged
merged 7 commits into from
Feb 8, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 37 additions & 20 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

var _ = require('lodash');
var SugarDate = require('sugar-date').Date;
require('sugar-date/locales');

var Utils = {};

Expand All @@ -25,30 +27,28 @@ var formatParsedObject = function(type, input, valid, parsed) {
};

/* istanbul ignore next */
Utils._getCurrentDate = function() {
Utils._getCurrentDate = function(options) {
// Makes unit testing possible, by allowing this
// function to be mocked.
return new Date();
return SugarDate.create('now', options);
};

/* istanbul ignore next */
Utils._getFutureDate = function(str) {
Utils._getFutureDate = function(str, options) {
// Makes unit testing possible, by allowing this
// function to be mocked.
return Date.future(str);
return SugarDate.create(str, _.assign({future: true}, options));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be better written as:

return SugarDate.create(str, _.assign({}, options, {future: true}));

The advantage here is that an options object containing future: false would not cause issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I see what you are saying.

};

Utils.activateDateParser = function() {
// Add support for the 'enhanced' date object.
// This augments the Date prototype with extra methods,
// and (importantly) allows use to use the Sugar.js
// date parsing algorithms.
// In an ideal world, we wouldn't augment the prototype,
// but our hands are ties if we want to use the date parsing
// feature that Sugar.js gives us.
// Perhaps one day this can be extracted and added as a
// moment.js plugin.
require('sugar-date');

// v2 of SugarDate supports optionally extending.
// This is still here to support legacy dependencies
SugarDate.extend();
};

/**
Expand Down Expand Up @@ -405,6 +405,8 @@ Utils.cloneTerse = function(input) {
* - Utils.parseDateTimeField('may 25th of next year')
* - Utils.parseDateTimeField('2014-01-18 09:30:00')
* - Utils.parseDateTimeField('2014-01-18 09:30:00 -0400')
* - Utils.parseDateTimeField('2014-01-18 09:30:00', 'en-GB')
* - Utils.parseDateTimeField('2014-01-18 09:30:00', {fromUTC: true, setUTC: true})
* - Utils.parseDateTimeField('in 2 days')
* - Utils.parseDateTimeField('5 minutes from now')
* - Utils.parseDateTimeField('2014-01-18 09:30:00 -0400 +2d +30m')
Expand All @@ -417,24 +419,35 @@ Utils.cloneTerse = function(input) {
* - -4d -6h will reduce the parsed date by 4 days and 6 hours
*
* @param {String} field the date string to parse.
* @param {String} locale the locale string OR options object.
* @return {Object} an object containing the parsed date, or the passed field if it was not a String.
*/
Utils.parseDateTimeField = function(field) {
Utils.parseDateTimeField = function(field, locale) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment in Flow XO core, it would be good to allow the SugarDate creation to be customised for the lifetime of this function invocation by passing in an optional dateCreator fn.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I wrote this comment, this feature has been implemented! So please ignore. 😄

var getRtnObject = function(parsed, isValid) {
return formatParsedObject('date', field, isValid, parsed);
};

var parsedDate;
var options = {};
if (_.isString(locale)) {
options.locale = locale;
} else if (locale) {
options = locale;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused why we need this if/else statement?

This appears to be an addition to the API so why not just make every caller pass in an object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original thinking was to mimic the second param of SugarDate.create which behaves like this. But I do like the idea of not having a mixed type as a parameter.


var previousNewDateInternal;
if (_.isFunction(options.newDateInternal)) {
previousNewDateInternal = SugarDate.getOption('newDateInternal');
SugarDate.setOption('newDateInternal', options.newDateInternal);
}

// Ensure we have Date superpowers
Utils.activateDateParser();
var parsedDate;

if(_.isDate(field)) {
parsedDate = field;

} else if(!_.isString(field)) {
// Just create a date from the passed value.
parsedDate = Date.create(field);
parsedDate = SugarDate.create(field, options);

} else {
// Regex for parsing a offset modifier.
Expand Down Expand Up @@ -482,18 +495,22 @@ Utils.parseDateTimeField = function(field) {
// Otherwise, parse the string to create a date in the future.
parsedDate =
withoutOffsetModifiers === '' && hasOffsetModifier ?
Utils._getCurrentDate() :
Utils._getFutureDate(withoutOffsetModifiers);
Utils._getCurrentDate(options) :
Utils._getFutureDate(withoutOffsetModifiers, options);

if(parsedDate.isValid() && hasOffsetModifier && offsetSecs) {
if(SugarDate.isValid(parsedDate) && hasOffsetModifier && offsetSecs) {
// Apply the offset modifier.
// If it is negative, it will subtract.
parsedDate.addSeconds(offsetSecs);
SugarDate.addSeconds(parsedDate, offsetSecs);
}
}

if (previousNewDateInternal) {
SugarDate.setOption('newDateInternal', previousNewDateInternal);
}

// parsedDate will always be a date at this point.
return getRtnObject(parsedDate, parsedDate.isValid());
return getRtnObject(parsedDate, SugarDate.isValid(parsedDate));
};

/**
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@
"dependencies": {
"bluebird": "^3.4.1",
"lodash": "^4.14.2",
"sugar-date": "1.5.1"
"sugar-date": "^2.0.4"
}
}
72 changes: 53 additions & 19 deletions spec/specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

var util = require('util'),
Utils = require('../lib/index.js'),
_ = require('lodash');
_ = require('lodash'),
SugarDate = require('sugar-date').Date;

describe('Utils', function() {
it('should convert an array to a hashtable', function() {
Expand Down Expand Up @@ -776,14 +777,14 @@ describe('Utils', function() {
valid: false,
parsed: jasmine.any(Date)
});
expect(parsedDate.parsed.isValid()).toBe(false);
expect(SugarDate.isValid(parsedDate.parsed)).toBe(false);
};

beforeEach(function() {
epoch = new Date(0);
spyOn(Utils, '_getFutureDate').and.callFake(function(str) {
var parsed = Date.future(str);
return parsed.isValid() ? epoch : parsed;
var parsed = SugarDate.create(str, {future: true});
return SugarDate.isValid(parsed) ? epoch : parsed;
});
});

Expand All @@ -799,7 +800,7 @@ describe('Utils', function() {

it('should parse a datetime string', function() {
expectValidDate('tomorrow', epoch);
expect(Utils._getFutureDate).toHaveBeenCalledWith('tomorrow');
expect(Utils._getFutureDate).toHaveBeenCalledWith('tomorrow', {});
});

it('should return as invalid if no string is passed', function() {
Expand All @@ -814,10 +815,51 @@ describe('Utils', function() {
expectInvalidDate('invalid');
});

it('should handle a integer', function() {
epoch = new Date(1485410400000);
expectValidDate(1485410400000, new Date(1485410400000));
});

it('should handle a Date', function() {
epoch = new Date(1485410400000);
expectValidDate(new Date(1485410400000), new Date(1485410400000));
});

it('should support newDateInternal as an option', function() {
epoch = SugarDate.create('2017-02-06 1am', {fromUTC: true});
var fn = function() {
var d = new Date();
d.setTime(d.getTime() + (60 * 60 * 1000));
return d;
};
var dateObj = Utils.parseDateTimeField('2017-02-06', {newDateInternal: fn});
expect(dateObj.parsed.toISOString()).toEqual('2017-02-06T01:00:00.000Z');
});

describe('Locales', function() {
it('parses a date without locale', function() {
Utils.parseDateTimeField('now +1d');
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {});

epoch = SugarDate.create('1/11/2017', {fromUTC: true});
var d = Utils.parseDateTimeField('1/11/2017');
expect(d.parsed.toISOString()).toEqual('2017-01-11T00:00:00.000Z');
});

it('parses a date with locale', function() {
Utils.parseDateTimeField('now +1d', 'en-GB');
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {locale: 'en-GB'});

epoch = SugarDate.create('1/11/2017', {fromUTC: true});
var d = Utils.parseDateTimeField('11/1/2017');
expect(d.parsed.toISOString()).toEqual('2017-01-11T00:00:00.000Z');
});
});

describe('Offset Modifiers', function() {
it('should strip offset modifier from string to parse', function() {
Utils.parseDateTimeField('now +1d');
expect(Utils._getFutureDate).toHaveBeenCalledWith('now');
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {});
});

describe('Increment', function() {
Expand Down Expand Up @@ -919,17 +961,17 @@ describe('Utils', function() {
});

it('should not apply the offset modifier if it is 0', function() {
spyOn(epoch, 'addSeconds');
spyOn(SugarDate, 'addSeconds');
Utils.parseDateTimeField('now');
expect(epoch.addSeconds)
expect(SugarDate.addSeconds)
.not.toHaveBeenCalled();
});

it('should not apply the offset modifier if the date is invalid', function() {
epoch = new Date('invalid');
spyOn(epoch, 'addSeconds');
// epoch = new Date('invalid');
spyOn(SugarDate, 'addSeconds');
Utils.parseDateTimeField('some_invalid_date +40h');
expect(epoch.addSeconds)
expect(SugarDate.addSeconds)
.not.toHaveBeenCalled();
});

Expand All @@ -942,10 +984,6 @@ describe('Utils', function() {
expectValidDate('2013-02-08 09:30-0100', epoch);
});

it('should work with the YYYY-MM-DD HHZZ format when the offset is Z', function() {
expectValidDate('2013-02-08 09Z', epoch);
});

it('should work with the YYYY-MM-DD HH:mm:ss.SSSZ format', function() {
expectValidDate('2013-02-08 09:30:26.123+07:00', epoch);
});
Expand All @@ -966,10 +1004,6 @@ describe('Utils', function() {
expectValidDate('2013-02-08 09:30-0100 +1d - 12h', new Date(43200000));
});

it('should work with the YYYY-MM-DD HHZZ format when the offset is Z and modifiers', function() {
expectValidDate('2013-02-08 09Z +1d - 12h', new Date(43200000));
});

it('should work with the YYYY-MM-DD HH:mm:ss.SSSZ format and modifiers', function() {
expectValidDate('2013-02-08 09:30:26.123+07:00 +1d - 12h', new Date(43200000));
});
Expand Down