Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit ac4318a

Browse files
committed
refactor(fromJson/date filter): move date string logic to date filter
Breaks angular.fromJson which doesn't deserialize date strings into date objects. This was done to make fromJson compatible with JSON.parse. If you do require the old behavior - if at all neeeded then because of json deserialization of XHR responses - then please create a custom $http transform: $httpProvider.defaults.transformResponse.push(function(data) { // recursively parse dates from data object here // see code removed in this diff for hints }); Closes #202
1 parent bb2fa6f commit ac4318a

File tree

5 files changed

+201
-216
lines changed

5 files changed

+201
-216
lines changed

src/JSON.js

+3-40
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
'use strict';
22

3-
var array = [].constructor;
4-
53
/**
64
* @ngdoc function
75
* @name angular.toJson
@@ -35,46 +33,11 @@ function toJson(obj, pretty) {
3533
function fromJson(json, useNative) {
3634
if (!isString(json)) return json;
3735

38-
var obj;
39-
40-
if (useNative && window.JSON && window.JSON.parse) {
41-
obj = JSON.parse(json);
42-
} else {
43-
obj = parseJson(json, true)();
44-
}
45-
return transformDates(obj);
46-
47-
// TODO make forEach optionally recursive and remove this function
48-
// TODO(misko): remove this once the $http service is checked in.
49-
function transformDates(obj) {
50-
if (isString(obj) && 15 <= obj.length && obj.length <= 24) {
51-
return jsonStringToDate(obj);
52-
} else if (isArray(obj) || isObject(obj)) {
53-
forEach(obj, function(val, name) {
54-
obj[name] = transformDates(val);
55-
});
56-
}
57-
return obj;
58-
}
36+
return (useNative && window.JSON && window.JSON.parse)
37+
? JSON.parse(json)
38+
: parseJson(json, true)();
5939
}
6040

61-
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
62-
function jsonStringToDate(string){
63-
var match;
64-
if (match = string.match(R_ISO8061_STR)) {
65-
var date = new Date(0),
66-
tzHour = 0,
67-
tzMin = 0;
68-
if (match[9]) {
69-
tzHour = int(match[9] + match[10]);
70-
tzMin = int(match[9] + match[11]);
71-
}
72-
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
73-
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
74-
return date;
75-
}
76-
return string;
77-
}
7841

7942
function jsonDateToString(date){
8043
if (!date) return date;

src/ng/filter/filters.js

+23-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
288288
* (e.g. `"h o''clock"`).
289289
*
290290
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
291-
* number) or ISO 8601 extended datetime string (yyyy-MM-ddTHH:mm:ss.SSSZ).
291+
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
292+
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
292293
* @param {string=} format Formatting rules (see Description). If not specified,
293294
* `mediumDate` is used.
294295
* @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+
317318
*/
318319
dateFilter.$inject = ['$locale'];
319320
function dateFilter($locale) {
321+
322+
323+
var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
324+
function jsonStringToDate(string){
325+
var match;
326+
if (match = string.match(R_ISO8601_STR)) {
327+
var date = new Date(0),
328+
tzHour = 0,
329+
tzMin = 0;
330+
if (match[9]) {
331+
tzHour = int(match[9] + match[10]);
332+
tzMin = int(match[9] + match[11]);
333+
}
334+
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
335+
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
336+
return date;
337+
}
338+
return string;
339+
}
340+
341+
320342
return function(date, format) {
321343
var text = '',
322344
parts = [],

src/ngMock/angular-mocks.js

+148-118
Original file line numberDiff line numberDiff line change
@@ -373,146 +373,176 @@ angular.mock.$LogProvider = function() {
373373
};
374374

375375

376-
/**
377-
* @ngdoc object
378-
* @name angular.mock.TzDate
379-
* @description
380-
*
381-
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
382-
*
383-
* Mock of the Date type which has its timezone specified via constroctor arg.
384-
*
385-
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
386-
* offset, so that we can test code that depends on local timezone settings without dependency on
387-
* the time zone settings of the machine where the code is running.
388-
*
389-
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
390-
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
391-
*
392-
* @example
393-
* !!!! WARNING !!!!!
394-
* This is not a complete Date object so only methods that were implemented can be called safely.
395-
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
396-
*
397-
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
398-
* incomplete we might be missing some non-standard methods. This can result in errors like:
399-
* "Date.prototype.foo called on incompatible Object".
400-
*
401-
* <pre>
402-
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
403-
* newYearInBratislava.getTimezoneOffset() => -60;
404-
* newYearInBratislava.getFullYear() => 2010;
405-
* newYearInBratislava.getMonth() => 0;
406-
* newYearInBratislava.getDate() => 1;
407-
* newYearInBratislava.getHours() => 0;
408-
* newYearInBratislava.getMinutes() => 0;
409-
* </pre>
410-
*
411-
*/
412-
angular.mock.TzDate = function (offset, timestamp) {
413-
var self = new Date(0);
414-
if (angular.isString(timestamp)) {
415-
var tsStr = timestamp;
416-
417-
self.origDate = angular.fromJson(angular.toJson({date:timestamp})).date;
418-
419-
timestamp = self.origDate.getTime();
420-
if (isNaN(timestamp))
421-
throw {
422-
name: "Illegal Argument",
423-
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
424-
};
425-
} else {
426-
self.origDate = new Date(timestamp);
376+
(function() {
377+
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
378+
379+
function jsonStringToDate(string){
380+
var match;
381+
if (match = string.match(R_ISO8061_STR)) {
382+
var date = new Date(0),
383+
tzHour = 0,
384+
tzMin = 0;
385+
if (match[9]) {
386+
tzHour = int(match[9] + match[10]);
387+
tzMin = int(match[9] + match[11]);
388+
}
389+
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
390+
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
391+
return date;
392+
}
393+
return string;
427394
}
428395

429-
var localOffset = new Date(timestamp).getTimezoneOffset();
430-
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
431-
self.date = new Date(timestamp + self.offsetDiff);
396+
function int(str) {
397+
return parseInt(str, 10);
398+
}
432399

433-
self.getTime = function() {
434-
return self.date.getTime() - self.offsetDiff;
435-
};
436400

437-
self.toLocaleDateString = function() {
438-
return self.date.toLocaleDateString();
439-
};
401+
/**
402+
* @ngdoc object
403+
* @name angular.mock.TzDate
404+
* @description
405+
*
406+
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
407+
*
408+
* Mock of the Date type which has its timezone specified via constroctor arg.
409+
*
410+
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
411+
* offset, so that we can test code that depends on local timezone settings without dependency on
412+
* the time zone settings of the machine where the code is running.
413+
*
414+
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
415+
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
416+
*
417+
* @example
418+
* !!!! WARNING !!!!!
419+
* This is not a complete Date object so only methods that were implemented can be called safely.
420+
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
421+
*
422+
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
423+
* incomplete we might be missing some non-standard methods. This can result in errors like:
424+
* "Date.prototype.foo called on incompatible Object".
425+
*
426+
* <pre>
427+
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
428+
* newYearInBratislava.getTimezoneOffset() => -60;
429+
* newYearInBratislava.getFullYear() => 2010;
430+
* newYearInBratislava.getMonth() => 0;
431+
* newYearInBratislava.getDate() => 1;
432+
* newYearInBratislava.getHours() => 0;
433+
* newYearInBratislava.getMinutes() => 0;
434+
* </pre>
435+
*
436+
*/
437+
angular.mock.TzDate = function (offset, timestamp) {
438+
var self = new Date(0);
439+
if (angular.isString(timestamp)) {
440+
var tsStr = timestamp;
441+
442+
self.origDate = jsonStringToDate(timestamp)
443+
444+
timestamp = self.origDate.getTime();
445+
if (isNaN(timestamp))
446+
throw {
447+
name: "Illegal Argument",
448+
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
449+
};
450+
} else {
451+
self.origDate = new Date(timestamp);
452+
}
440453

441-
self.getFullYear = function() {
442-
return self.date.getFullYear();
443-
};
454+
var localOffset = new Date(timestamp).getTimezoneOffset();
455+
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
456+
self.date = new Date(timestamp + self.offsetDiff);
444457

445-
self.getMonth = function() {
446-
return self.date.getMonth();
447-
};
458+
self.getTime = function() {
459+
return self.date.getTime() - self.offsetDiff;
460+
};
448461

449-
self.getDate = function() {
450-
return self.date.getDate();
451-
};
462+
self.toLocaleDateString = function() {
463+
return self.date.toLocaleDateString();
464+
};
452465

453-
self.getHours = function() {
454-
return self.date.getHours();
455-
};
466+
self.getFullYear = function() {
467+
return self.date.getFullYear();
468+
};
456469

457-
self.getMinutes = function() {
458-
return self.date.getMinutes();
459-
};
470+
self.getMonth = function() {
471+
return self.date.getMonth();
472+
};
460473

461-
self.getSeconds = function() {
462-
return self.date.getSeconds();
463-
};
474+
self.getDate = function() {
475+
return self.date.getDate();
476+
};
464477

465-
self.getTimezoneOffset = function() {
466-
return offset * 60;
467-
};
478+
self.getHours = function() {
479+
return self.date.getHours();
480+
};
468481

469-
self.getUTCFullYear = function() {
470-
return self.origDate.getUTCFullYear();
471-
};
482+
self.getMinutes = function() {
483+
return self.date.getMinutes();
484+
};
472485

473-
self.getUTCMonth = function() {
474-
return self.origDate.getUTCMonth();
475-
};
486+
self.getSeconds = function() {
487+
return self.date.getSeconds();
488+
};
476489

477-
self.getUTCDate = function() {
478-
return self.origDate.getUTCDate();
479-
};
490+
self.getTimezoneOffset = function() {
491+
return offset * 60;
492+
};
480493

481-
self.getUTCHours = function() {
482-
return self.origDate.getUTCHours();
483-
};
494+
self.getUTCFullYear = function() {
495+
return self.origDate.getUTCFullYear();
496+
};
484497

485-
self.getUTCMinutes = function() {
486-
return self.origDate.getUTCMinutes();
487-
};
498+
self.getUTCMonth = function() {
499+
return self.origDate.getUTCMonth();
500+
};
488501

489-
self.getUTCSeconds = function() {
490-
return self.origDate.getUTCSeconds();
491-
};
502+
self.getUTCDate = function() {
503+
return self.origDate.getUTCDate();
504+
};
492505

493-
self.getDay = function() {
494-
return self.date.getDay();
495-
};
506+
self.getUTCHours = function() {
507+
return self.origDate.getUTCHours();
508+
};
496509

497-
//hide all methods not implemented in this mock that the Date prototype exposes
498-
var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
499-
'getUTCMilliseconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
500-
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
501-
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
502-
'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
503-
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
504-
505-
angular.forEach(unimplementedMethods, function(methodName) {
506-
self[methodName] = function() {
507-
throw Error("Method '" + methodName + "' is not implemented in the TzDate mock");
510+
self.getUTCMinutes = function() {
511+
return self.origDate.getUTCMinutes();
508512
};
509-
});
510513

511-
return self;
512-
};
514+
self.getUTCSeconds = function() {
515+
return self.origDate.getUTCSeconds();
516+
};
517+
518+
self.getUTCMilliseconds = function() {
519+
return self.origDate.getUTCMilliseconds();
520+
};
521+
522+
self.getDay = function() {
523+
return self.date.getDay();
524+
};
525+
526+
//hide all methods not implemented in this mock that the Date prototype exposes
527+
var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
528+
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
529+
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
530+
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
531+
'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
532+
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
533+
534+
angular.forEach(unimplementedMethods, function(methodName) {
535+
self[methodName] = function() {
536+
throw Error("Method '" + methodName + "' is not implemented in the TzDate mock");
537+
};
538+
});
539+
540+
return self;
541+
};
513542

514-
//make "tzDateInstance instanceof Date" return true
515-
angular.mock.TzDate.prototype = Date.prototype;
543+
//make "tzDateInstance instanceof Date" return true
544+
angular.mock.TzDate.prototype = Date.prototype;
545+
})();
516546

517547

518548
/**

0 commit comments

Comments
 (0)