Skip to content

Commit

Permalink
Merge pull request #8 from flowxo/change/timezone
Browse files Browse the repository at this point in the history
Extracted and exposed parsing logic.
SugarDate workaround
  • Loading branch information
shepherdsam authored Feb 22, 2017
2 parents 1633544 + 8942050 commit b6c6b86
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 27 deletions.
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

0 comments on commit b6c6b86

Please sign in to comment.