diff options
| -rw-r--r-- | src/JSON.js | 43 | ||||
| -rw-r--r-- | src/ng/filter/filters.js | 24 | ||||
| -rw-r--r-- | src/ngMock/angular-mocks.js | 266 | ||||
| -rw-r--r-- | test/JsonSpec.js | 52 | ||||
| -rw-r--r-- | test/ng/filter/filtersSpec.js | 32 |
5 files changed, 201 insertions, 216 deletions
diff --git a/src/JSON.js b/src/JSON.js index 21d526c2..cddfc52d 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -1,7 +1,5 @@ 'use strict'; -var array = [].constructor; - /** * @ngdoc function * @name angular.toJson @@ -35,46 +33,11 @@ function toJson(obj, pretty) { function fromJson(json, useNative) { if (!isString(json)) return json; - var obj; - - if (useNative && window.JSON && window.JSON.parse) { - obj = JSON.parse(json); - } else { - obj = parseJson(json, true)(); - } - return transformDates(obj); - - // TODO make forEach optionally recursive and remove this function - // TODO(misko): remove this once the $http service is checked in. - function transformDates(obj) { - if (isString(obj) && 15 <= obj.length && obj.length <= 24) { - return jsonStringToDate(obj); - } else if (isArray(obj) || isObject(obj)) { - forEach(obj, function(val, name) { - obj[name] = transformDates(val); - }); - } - return obj; - } + return (useNative && window.JSON && window.JSON.parse) + ? JSON.parse(json) + : parseJson(json, true)(); } -var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; -function jsonStringToDate(string){ - var match; - if (match = string.match(R_ISO8061_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0; - if (match[9]) { - tzHour = int(match[9] + match[10]); - tzMin = int(match[9] + match[11]); - } - date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); - date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); - return date; - } - return string; -} function jsonDateToString(date){ if (!date) return date; diff --git a/src/ng/filter/filters.js b/src/ng/filter/filters.js index 078c54fc..c792cace 100644 --- a/src/ng/filter/filters.js +++ b/src/ng/filter/filters.js @@ -288,7 +288,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ * (e.g. `"h o''clock"`). * * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or - * number) or ISO 8601 extended datetime string (yyyy-MM-ddTHH:mm:ss.SSSZ). + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). * @param {string=} format Formatting rules (see Description). If not specified, * `mediumDate` is used. * @returns {string} Formatted string or the input if input is not recognized as date/millis. @@ -317,6 +318,27 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+ */ dateFilter.$inject = ['$locale']; function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; + function jsonStringToDate(string){ + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0; + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); + date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); + return date; + } + return string; + } + + return function(date, format) { var text = '', parts = [], diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 306ca77b..f5dd6758 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -373,146 +373,176 @@ angular.mock.$LogProvider = function() { }; -/** - * @ngdoc object - * @name angular.mock.TzDate - * @description - * - * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. - * - * Mock of the Date type which has its timezone specified via constroctor arg. - * - * The main purpose is to create Date-like instances with timezone fixed to the specified timezone - * offset, so that we can test code that depends on local timezone settings without dependency on - * the time zone settings of the machine where the code is running. - * - * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) - * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* - * - * @example - * !!!! WARNING !!!!! - * This is not a complete Date object so only methods that were implemented can be called safely. - * To make matters worse, TzDate instances inherit stuff from Date via a prototype. - * - * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is - * incomplete we might be missing some non-standard methods. This can result in errors like: - * "Date.prototype.foo called on incompatible Object". - * - * <pre> - * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); - * newYearInBratislava.getTimezoneOffset() => -60; - * newYearInBratislava.getFullYear() => 2010; - * newYearInBratislava.getMonth() => 0; - * newYearInBratislava.getDate() => 1; - * newYearInBratislava.getHours() => 0; - * newYearInBratislava.getMinutes() => 0; - * </pre> - * - */ -angular.mock.TzDate = function (offset, timestamp) { - var self = new Date(0); - if (angular.isString(timestamp)) { - var tsStr = timestamp; - - self.origDate = angular.fromJson(angular.toJson({date:timestamp})).date; - - timestamp = self.origDate.getTime(); - if (isNaN(timestamp)) - throw { - name: "Illegal Argument", - message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" - }; - } else { - self.origDate = new Date(timestamp); +(function() { + var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; + + function jsonStringToDate(string){ + var match; + if (match = string.match(R_ISO8061_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0; + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); + date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); + return date; + } + return string; } - var localOffset = new Date(timestamp).getTimezoneOffset(); - self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; - self.date = new Date(timestamp + self.offsetDiff); + function int(str) { + return parseInt(str, 10); + } - self.getTime = function() { - return self.date.getTime() - self.offsetDiff; - }; - self.toLocaleDateString = function() { - return self.date.toLocaleDateString(); - }; + /** + * @ngdoc object + * @name angular.mock.TzDate + * @description + * + * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. + * + * Mock of the Date type which has its timezone specified via constroctor arg. + * + * The main purpose is to create Date-like instances with timezone fixed to the specified timezone + * offset, so that we can test code that depends on local timezone settings without dependency on + * the time zone settings of the machine where the code is running. + * + * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) + * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* + * + * @example + * !!!! WARNING !!!!! + * This is not a complete Date object so only methods that were implemented can be called safely. + * To make matters worse, TzDate instances inherit stuff from Date via a prototype. + * + * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is + * incomplete we might be missing some non-standard methods. This can result in errors like: + * "Date.prototype.foo called on incompatible Object". + * + * <pre> + * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); + * newYearInBratislava.getTimezoneOffset() => -60; + * newYearInBratislava.getFullYear() => 2010; + * newYearInBratislava.getMonth() => 0; + * newYearInBratislava.getDate() => 1; + * newYearInBratislava.getHours() => 0; + * newYearInBratislava.getMinutes() => 0; + * </pre> + * + */ + angular.mock.TzDate = function (offset, timestamp) { + var self = new Date(0); + if (angular.isString(timestamp)) { + var tsStr = timestamp; + + self.origDate = jsonStringToDate(timestamp) + + timestamp = self.origDate.getTime(); + if (isNaN(timestamp)) + throw { + name: "Illegal Argument", + message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" + }; + } else { + self.origDate = new Date(timestamp); + } - self.getFullYear = function() { - return self.date.getFullYear(); - }; + var localOffset = new Date(timestamp).getTimezoneOffset(); + self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; + self.date = new Date(timestamp + self.offsetDiff); - self.getMonth = function() { - return self.date.getMonth(); - }; + self.getTime = function() { + return self.date.getTime() - self.offsetDiff; + }; - self.getDate = function() { - return self.date.getDate(); - }; + self.toLocaleDateString = function() { + return self.date.toLocaleDateString(); + }; - self.getHours = function() { - return self.date.getHours(); - }; + self.getFullYear = function() { + return self.date.getFullYear(); + }; - self.getMinutes = function() { - return self.date.getMinutes(); - }; + self.getMonth = function() { + return self.date.getMonth(); + }; - self.getSeconds = function() { - return self.date.getSeconds(); - }; + self.getDate = function() { + return self.date.getDate(); + }; - self.getTimezoneOffset = function() { - return offset * 60; - }; + self.getHours = function() { + return self.date.getHours(); + }; - self.getUTCFullYear = function() { - return self.origDate.getUTCFullYear(); - }; + self.getMinutes = function() { + return self.date.getMinutes(); + }; - self.getUTCMonth = function() { - return self.origDate.getUTCMonth(); - }; + self.getSeconds = function() { + return self.date.getSeconds(); + }; - self.getUTCDate = function() { - return self.origDate.getUTCDate(); - }; + self.getTimezoneOffset = function() { + return offset * 60; + }; - self.getUTCHours = function() { - return self.origDate.getUTCHours(); - }; + self.getUTCFullYear = function() { + return self.origDate.getUTCFullYear(); + }; - self.getUTCMinutes = function() { - return self.origDate.getUTCMinutes(); - }; + self.getUTCMonth = function() { + return self.origDate.getUTCMonth(); + }; - self.getUTCSeconds = function() { - return self.origDate.getUTCSeconds(); - }; + self.getUTCDate = function() { + return self.origDate.getUTCDate(); + }; - self.getDay = function() { - return self.date.getDay(); - }; + self.getUTCHours = function() { + return self.origDate.getUTCHours(); + }; - //hide all methods not implemented in this mock that the Date prototype exposes - var unimplementedMethods = ['getMilliseconds', 'getUTCDay', - 'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', - 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', - 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', - 'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString', - 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; - - angular.forEach(unimplementedMethods, function(methodName) { - self[methodName] = function() { - throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); + self.getUTCMinutes = function() { + return self.origDate.getUTCMinutes(); }; - }); - return self; -}; + self.getUTCSeconds = function() { + return self.origDate.getUTCSeconds(); + }; + + self.getUTCMilliseconds = function() { + return self.origDate.getUTCMilliseconds(); + }; + + self.getDay = function() { + return self.date.getDay(); + }; + + //hide all methods not implemented in this mock that the Date prototype exposes + var unimplementedMethods = ['getMilliseconds', 'getUTCDay', + 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', + 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', + 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', + 'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString', + 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; + + angular.forEach(unimplementedMethods, function(methodName) { + self[methodName] = function() { + throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); + }; + }); + + return self; + }; -//make "tzDateInstance instanceof Date" return true -angular.mock.TzDate.prototype = Date.prototype; + //make "tzDateInstance instanceof Date" return true + angular.mock.TzDate.prototype = Date.prototype; +})(); /** diff --git a/test/JsonSpec.js b/test/JsonSpec.js index e4abeef9..ad0cb415 100644 --- a/test/JsonSpec.js +++ b/test/JsonSpec.js @@ -72,9 +72,8 @@ describe('json', function() { }); it('should serialize UTC dates', function() { - var date = jsonStringToDate('2009-10-09T01:02:03.027Z'); + var date = new angular.mock.TzDate(-1, '2009-10-09T01:02:03.027Z'); expect(toJson(date)).toEqual('"2009-10-09T01:02:03.027Z"'); - expect(fromJson('"2009-10-09T01:02:03.027Z"').getTime()).toEqual(date.getTime()); }); it('should prevent recursion', function() { @@ -156,17 +155,7 @@ describe('json', function() { expect(fromJson('{}', true)).toEqual({}); expect(spy).toHaveBeenCalled(); }); - - - it('should convert timestamp strings to Date objects', function() { - expect(fromJson('"2010-12-22T17:23:17.974Z"', true) instanceof Date).toBe(true); - expect(fromJson('["2010-12-22T17:23:17.974Z"]', true)[0] instanceof Date).toBe(true); - expect(fromJson('{"t":"2010-12-22T17:23:17.974Z"}', true).t instanceof Date).toBe(true); - expect(fromJson('{"t":["2010-12-22T17:23:17.974Z"]}', true).t[0] instanceof Date).toBe(true); - expect(fromJson('{"t":{"t":"2010-12-22T17:23:17.974Z"}}', true).t.t instanceof Date).toBe(true); - }); }); - } @@ -220,45 +209,6 @@ describe('json', function() { }); - describe('iso 8061 date', function() { - it('should read/write to date', function() { - var date = new Date('Sep 10 2003 13:02:03 GMT'); - expect(jsonDateToString(date)).toBe('2003-09-10T13:02:03.000Z'); - expect(jsonStringToDate(jsonDateToString(date)).getTime()).toBe(date.getTime()); - }); - - - it('should convert to date', function() { - //full ISO8061 - expect(jsonStringToDate('2003-09-10T13:02:03.000Z')).toEqual(new Date('Sep 10 2003 13:02:03 GMT')); - - expect(jsonStringToDate('2003-09-10T13:02:03.000+00:00')).toEqual(new Date('Sep 10 2003 13:02:03 GMT')); - - expect(jsonStringToDate('20030910T033203-0930')).toEqual(new Date('Sep 10 2003 13:02:03 GMT')); - - //no millis - expect(jsonStringToDate('2003-09-10T13:02:03Z')).toEqual(new Date('Sep 10 2003 13:02:03 GMT')); - - //no seconds - expect(jsonStringToDate('2003-09-10T13:02Z')).toEqual(new Date('Sep 10 2003 13:02:00 GMT')); - - //no minutes - expect(jsonStringToDate('2003-09-10T13Z')).toEqual(new Date('Sep 10 2003 13:00:00 GMT')); - - //no time - expect(jsonStringToDate('2003-09-10')).toEqual(new Date('Sep 10 2003 00:00:00 GMT')); - - expect(jsonStringToDate('2011-12-28T13:02:09-08:00')).toEqual(new Date('Dec 28 2011 21:02:09 GMT')); - }); - - - it('should parse date', function() { - var date = jsonStringToDate('2003-09-10T13:02:03.000Z'); - expect(jsonDateToString(date)).toBe('2003-09-10T13:02:03.000Z'); - expect(jsonStringToDate('str')).toBe('str'); - }); - }); - describe('string', function() { it('should quote', function() { expect(quoteUnicode('a')).toBe('"a"'); diff --git a/test/ng/filter/filtersSpec.js b/test/ng/filter/filtersSpec.js index 98651c58..9ea200a3 100644 --- a/test/ng/filter/filtersSpec.js +++ b/test/ng/filter/filtersSpec.js @@ -267,14 +267,34 @@ describe('filters', function() { toEqual('12:05 PM'); }); - it('should be able to parse ISO 8601 dates/times using', function() { - var isoString = '2010-09-03T05:05:08.872Z'; - expect(date(isoString)). - toEqual(date(isoString, 'mediumDate')); - }); - it('should parse format ending with non-replaced string', function() { expect(date(morning, 'yy/xxx')).toEqual('10/xxx'); }); + + + it('should support various iso8061 date strings as input', function() { + var format = 'yyyy-MM ss'; + + //full ISO8061 + expect(date('2003-09-10T13:02:03.000Z', format)).toEqual('2003-09 03'); + + expect(date('2003-09-10T13:02:03.000+00:00', format)).toEqual('2003-09 03'); + + expect(date('2003-09-10T13:02:03-08:00', format)).toEqual('2003-09 03'); + + expect(date('20030910T033203-0930', format)).toEqual('2003-09 03'); + + //no millis + expect(date('2003-09-10T13:02:03Z', format)).toEqual('2003-09 03'); + + //no seconds + expect(date('2003-09-10T13:02Z', format)).toEqual('2003-09 00'); + + //no minutes + expect(date('2003-09-10T13Z', format)).toEqual('2003-09 00'); + + //no time + expect(date('2003-09-10', format)).toEqual('2003-09 00'); + }); }); }); |
