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

Extracted and exposed parsing logic #8

Merged
merged 7 commits into from
Feb 22, 2017
76 changes: 61 additions & 15 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@ Utils._getCurrentDate = function(options) {

/* istanbul ignore next */
Utils._getFutureDate = function(str, options) {
// Work around bug in SugarDate
// if yesterday or today is in str `future:false` else true
var future = !/yesterday|today/.test(str);

// Makes unit testing possible, by allowing this
// function to be mocked.
return SugarDate.create(str, _.assign({}, options, {future: true}));
return SugarDate.create(str, _.assign({}, options, {future: future}));
};

Utils.activateDateParser = function() {
Expand Down Expand Up @@ -545,27 +549,69 @@ Utils.parseDateTimeField = function(field, options) {

var parsedDateWithTimezone = parseDateTime(field, options, timezone);
if (SugarDate.isValid(parsedDateWithTimezone)) {
Utils.Logger.debug('Utils.parseDateTimeField: parsedDateWithTimezone:', parsedDateWithTimezone.toISOString());

var hasTZOffset = /[zZ]|[+-][01]\d:?[0-5]\d/.test(field);

// Convert to string. Important to remove offset/tz info (if none was provided originally)
// or moment will ignore the passed in tz in later step.
var tzFormatStr = hasTZOffset ? '{Z}' : '';
var dateString = SugarDate.format(parsedDateWithTimezone, '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{SSS}' + tzFormatStr);
Utils.Logger.debug('Utils.parseDateTimeField: dateString:', dateString);

// This parses the dateString in the timezone specified.
var dateMoment = moment.tz(dateString, timezone);
Utils.Logger.debug('Utils.parseDateTimeField: moment:', dateMoment);

var hasTZOffset = Utils.hasTZOffset(field);
var dateMoment = Utils.dateToTimezone(parsedDateWithTimezone, timezone, hasTZOffset);
// Add the dateMoment to the return object
rtnObject.moment = dateMoment;
}

return rtnObject;
};

/**
* Utils.dateToTimezone - create a Moment.js date in a timezone
*
* @param {Date} date The date to create in the timezone
* @param {String} timezone Timezone to create the date into ex, 'America/New_York'
* @param {Boolean} hasTZOffset Specify if the incoming date had a offset already applied.
* @return {Moment} A moment.js object
*/
Utils.dateToTimezone = function(date, timezone, hasTZOffset) {
Utils.Logger.debug('Utils.dateToTimezone: date:', date.toISOString());

// Convert to string. Important to remove offset/tz info (if none was provided originally)
// or moment will ignore the passed in tz in later step.
var tzFormatStr = hasTZOffset ? '{Z}' : '';
var sugarDateString = SugarDate.format(date, '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{SSS}' + tzFormatStr);
Utils.Logger.debug('Utils.applyTzOffset: sugarDateString:', sugarDateString);

// This parses the dateString in the timezone specified.
var dateMoment = moment.tz(sugarDateString, timezone);
Utils.Logger.debug('Utils.applyTzOffset: moment:', dateMoment);

return dateMoment;
};

/**
* Utils.applyTzOffset - Applies TZ Offset
*
* @param {Date} date The date to offset
* @param {String} timezone Timezone to offset the date for ex, 'America/New_York'
* @return {Date} A date with the offset applied
*/
Utils.applyTzOffset = function(date, timezone) {
Utils.Logger.debug('Utils.applyTzOffset: before:', date);

SugarDate.addMinutes(date, moment.tz.zone(timezone).offset(date));

Utils.Logger.debug('Utils.applyTzOffset: after:', date);
return date;
};

/**
* Utils.hasTZOffset - Checks to see if the string has a timezone offset
* Examples
* - `-06:00`
* - `+0600`
* - `Z`
*
* @param {String} str The date string to be checked
* @return {Boolean} true or false
*/
Utils.hasTZOffset = function(str) {
return /[zZ]|[+-][01]\d:?[0-5]\d/.test(str);
};

/**
* Parses a boolean-ish input into a boolean object.
*
Expand Down
37 changes: 25 additions & 12 deletions spec/specs.js
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,6 @@ describe('Utils', function() {

it('should parse a datetime string', function() {
expectValidDate('tomorrow', moment().add(1, 'days').startOf('day').toDate());
expect(Utils._getFutureDate).toHaveBeenCalledWith('tomorrow', {locale: 'en'});
});

it('should return as invalid if no string is passed', function() {
Expand All @@ -836,22 +835,19 @@ describe('Utils', function() {
it('parses a date without locale', function() {
var d = Utils.parseDateTimeField('now');
var expected = moment().toDate();
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {locale: 'en'});
// We give a margin of 100ms
expectDatesToBeClose(d.parsed, expected);
expectDatesToBeClose(d.moment.toDate(), expected);
});

it('parses a date with locale', function() {
var d = Utils.parseDateTimeField('11/1/2017', {locale: 'en-GB', timezone: 'UTC'});
expect(Utils._getFutureDate).toHaveBeenCalledWith('11/1/2017', {locale: 'en-GB'});
expect(d.moment.toISOString()).toEqual('2017-01-11T00:00:00.000Z');

// .parsed is expected to be in the timezone of the host machine.
expect(d.parsed).toEqual(moment('2017-01-11').toDate());

d = Utils.parseDateTimeField('11/1/2017', {locale: 'en', timezone: 'UTC'});
expect(Utils._getFutureDate).toHaveBeenCalledWith('11/1/2017', {locale: 'en'});
expect(d.moment.toISOString()).toEqual('2017-11-01T00:00:00.000Z');

// .parsed is expected to be in the timezone of the host machine.
Expand All @@ -861,7 +857,6 @@ describe('Utils', function() {
it('defaults locale when timezone is Europe/London', function() {
var d = Utils.parseDateTimeField('now', {timezone: 'Europe/London'});
var expected = moment().tz('Europe/London').toDate();
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {locale: 'en-GB'});
expectDatesToBeClose(d.moment.toDate(), expected);

// .parsed is expected to be in the timezone of the host machine.
Expand All @@ -871,8 +866,7 @@ describe('Utils', function() {

describe('Timezones', function() {
it('parses a date without timezone', function() {
var d = Utils.parseDateTimeField('1/11/2017');
expect(Utils._getFutureDate).toHaveBeenCalledWith('1/11/2017', {locale: 'en'});
var d = Utils.parseDateTimeField('2017-01-11');
expect(d.moment.toISOString()).toEqual(moment('2017-01-11').toISOString());

// .parsed is expected to be in the timezone of the host machine.
Expand All @@ -881,7 +875,6 @@ describe('Utils', function() {

it('parses a date with timezone', function() {
var d = Utils.parseDateTimeField('1/11/2017', {timezone: 'America/New_York'});
expect(Utils._getFutureDate).toHaveBeenCalledWith('1/11/2017', {locale: 'en'});
expect(d.moment.format()).toEqual('2017-01-11T00:00:00-05:00');

// .parsed is expected to be in the timezone of the host machine.
Expand Down Expand Up @@ -915,9 +908,8 @@ describe('Utils', function() {
checkTime(time, false, 'America/Chicago');
checkTime(time, false, 'UTC');

// NOTE: Disable for now. There is a bug with SugarDate.
// checkTime(time, true, 'America/Chicago');
// checkTime(time, true, 'UTC');
checkTime(time, true, 'America/Chicago');
checkTime(time, true, 'UTC');
});
});

Expand All @@ -942,7 +934,7 @@ describe('Utils', function() {
describe('Offset Modifiers', function() {
it('should strip offset modifier from string to parse', function() {
Utils.parseDateTimeField('now +1d');
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', {locale: 'en'});
expect(Utils._getFutureDate).toHaveBeenCalledWith('now', jasmine.any(Object));
});

describe('Increment', function() {
Expand Down Expand Up @@ -1167,6 +1159,27 @@ describe('Utils', function() {
});
});
});

describe('Apply Timezone Offset', function() {
it('should return a date in the correct offset', function() {
var d;

d = Utils.applyTzOffset(new Date('2017-02-14'), 'America/Chicago');
expect(d).toEqual(moment.tz('2017-02-14', 'America/Chicago').toDate());
expect(d.toISOString()).toEqual('2017-02-14T06:00:00.000Z');

d = Utils.applyTzOffset(new Date('2017-02-15'), 'America/New_York');
expect(d).toEqual(moment.tz('2017-02-15', 'America/New_York').toDate());
expect(d.toISOString()).toEqual('2017-02-15T05:00:00.000Z');

d = Utils.applyTzOffset(new Date('2017-02-16'), 'Europe/London');
expect(d).toEqual(moment.tz('2017-02-16', 'Europe/London').toDate());
expect(d.toISOString()).toEqual('2017-02-16T00:00:00.000Z');

d = Utils.applyTzOffset(new Date(), 'Europe/London');
expectDatesToBeClose(d, moment().tz('Europe/London').toDate());
});
});
});

describe('Parse Boolean Field', function() {
Expand Down