Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit bd2ae0e

Browse files
committedApr 19, 2014
feat(dateParser): add dateParser service
Closes #1874
1 parent 73c2dea commit bd2ae0e

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed
 

‎src/dateparser/dateparser.js

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
angular.module('ui.bootstrap.dateparser', [])
2+
3+
.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
4+
5+
this.parsers = {};
6+
7+
var formatCodeToRegex = {
8+
'yyyy': {
9+
regex: '\\d{4}',
10+
apply: function(value) { this.year = +value; }
11+
},
12+
'yy': {
13+
regex: '\\d{2}',
14+
apply: function(value) { this.year = +value + 2000; }
15+
},
16+
'y': {
17+
regex: '\\d{1,4}',
18+
apply: function(value) { this.year = +value; }
19+
},
20+
'MMMM': {
21+
regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
22+
apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
23+
},
24+
'MMM': {
25+
regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
26+
apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
27+
},
28+
'MM': {
29+
regex: '0[1-9]|1[0-2]',
30+
apply: function(value) { this.month = value - 1; }
31+
},
32+
'M': {
33+
regex: '[1-9]|1[0-2]',
34+
apply: function(value) { this.month = value - 1; }
35+
},
36+
'dd': {
37+
regex: '[0-2][0-9]{1}|3[0-1]{1}',
38+
apply: function(value) { this.date = +value; }
39+
},
40+
'd': {
41+
regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
42+
apply: function(value) { this.date = +value; }
43+
},
44+
'EEEE': {
45+
regex: $locale.DATETIME_FORMATS.DAY.join('|')
46+
},
47+
'EEE': {
48+
regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
49+
}
50+
};
51+
52+
this.createParser = function(format) {
53+
var map = [], regex = format.split('');
54+
55+
angular.forEach(formatCodeToRegex, function(data, code) {
56+
var index = format.indexOf(code);
57+
58+
if (index > -1) {
59+
format = format.split('');
60+
61+
regex[index] = '(' + data.regex + ')';
62+
format[index] = '$'; // Custom symbol to define consumed part of format
63+
for (var i = index + 1, n = index + code.length; i < n; i++) {
64+
regex[i] = '';
65+
format[i] = '$';
66+
}
67+
format = format.join('');
68+
69+
map.push({ index: index, apply: data.apply });
70+
}
71+
});
72+
73+
return {
74+
regex: new RegExp('^' + regex.join('') + '$'),
75+
map: orderByFilter(map, 'index')
76+
};
77+
};
78+
79+
this.parse = function(input, format) {
80+
if ( !angular.isString(input) ) {
81+
return input;
82+
}
83+
84+
format = $locale.DATETIME_FORMATS[format] || format;
85+
86+
if ( !this.parsers[format] ) {
87+
this.parsers[format] = this.createParser(format);
88+
}
89+
90+
var parser = this.parsers[format],
91+
regex = parser.regex,
92+
map = parser.map,
93+
results = input.match(regex);
94+
95+
if ( results && results.length ) {
96+
var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;
97+
98+
for( var i = 1, n = results.length; i < n; i++ ) {
99+
var mapper = map[i-1];
100+
if ( mapper.apply ) {
101+
mapper.apply.call(fields, results[i]);
102+
}
103+
}
104+
105+
if ( isValid(fields.year, fields.month, fields.date) ) {
106+
dt = new Date( fields.year, fields.month, fields.date, fields.hours);
107+
}
108+
109+
return dt;
110+
}
111+
};
112+
113+
// Check if date is valid for specific month (and year for February).
114+
// Month: 0 = Jan, 1 = Feb, etc
115+
function isValid(year, month, date) {
116+
if ( month === 1 && date > 28) {
117+
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
118+
}
119+
120+
if ( month === 3 || month === 5 || month === 8 || month === 10) {
121+
return date < 31;
122+
}
123+
124+
return true;
125+
}
126+
}]);
+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
describe('date parser', function () {
2+
var dateParser;
3+
4+
beforeEach(module('ui.bootstrap.dateparser'));
5+
beforeEach(inject(function (_dateParser_) {
6+
dateParser = _dateParser_;
7+
}));
8+
9+
function expectParse(input, format, date) {
10+
expect(dateParser.parse(input, format)).toEqual(date);
11+
}
12+
13+
describe('wih custom formats', function() {
14+
it('should work correctly for `dd`, `MM`, `yyyy`', function() {
15+
expectParse('17.11.2013', 'dd.MM.yyyy', new Date(2013, 10, 17, 0));
16+
expectParse('31.12.2013', 'dd.MM.yyyy', new Date(2013, 11, 31, 0));
17+
expectParse('08-03-1991', 'dd-MM-yyyy', new Date(1991, 2, 8, 0));
18+
expectParse('03/05/1980', 'MM/dd/yyyy', new Date(1980, 2, 5, 0));
19+
expectParse('10.01/1983', 'dd.MM/yyyy', new Date(1983, 0, 10, 0));
20+
expectParse('11-09-1980', 'MM-dd-yyyy', new Date(1980, 10, 9, 0));
21+
expectParse('2011/02/05', 'yyyy/MM/dd', new Date(2011, 1, 5, 0));
22+
});
23+
24+
it('should work correctly for `yy`', function() {
25+
expectParse('17.11.13', 'dd.MM.yy', new Date(2013, 10, 17, 0));
26+
expectParse('02-05-11', 'dd-MM-yy', new Date(2011, 4, 2, 0));
27+
expectParse('02/05/80', 'MM/dd/yy', new Date(2080, 1, 5, 0));
28+
expectParse('55/02/05', 'yy/MM/dd', new Date(2055, 1, 5, 0));
29+
expectParse('11-08-13', 'dd-MM-yy', new Date(2013, 7, 11, 0));
30+
});
31+
32+
it('should work correctly for `M`', function() {
33+
expectParse('8/11/2013', 'M/dd/yyyy', new Date(2013, 7, 11, 0));
34+
expectParse('07.11.05', 'dd.M.yy', new Date(2005, 10, 7, 0));
35+
expectParse('02-5-11', 'dd-M-yy', new Date(2011, 4, 2, 0));
36+
expectParse('2/05/1980', 'M/dd/yyyy', new Date(1980, 1, 5, 0));
37+
expectParse('1955/2/05', 'yyyy/M/dd', new Date(1955, 1, 5, 0));
38+
expectParse('02-5-11', 'dd-M-yy', new Date(2011, 4, 2, 0));
39+
});
40+
41+
it('should work correctly for `MMM`', function() {
42+
expectParse('30.Sep.10', 'dd.MMM.yy', new Date(2010, 8, 30, 0));
43+
expectParse('02-May-11', 'dd-MMM-yy', new Date(2011, 4, 2, 0));
44+
expectParse('Feb/05/1980', 'MMM/dd/yyyy', new Date(1980, 1, 5, 0));
45+
expectParse('1955/Feb/05', 'yyyy/MMM/dd', new Date(1955, 1, 5, 0));
46+
});
47+
48+
it('should work correctly for `MMMM`', function() {
49+
expectParse('17.November.13', 'dd.MMMM.yy', new Date(2013, 10, 17, 0));
50+
expectParse('05-March-1980', 'dd-MMMM-yyyy', new Date(1980, 2, 5, 0));
51+
expectParse('February/05/1980', 'MMMM/dd/yyyy', new Date(1980, 1, 5, 0));
52+
expectParse('1949/December/20', 'yyyy/MMMM/dd', new Date(1949, 11, 20, 0));
53+
});
54+
55+
it('should work correctly for `d`', function() {
56+
expectParse('17.November.13', 'd.MMMM.yy', new Date(2013, 10, 17, 0));
57+
expectParse('8-March-1991', 'd-MMMM-yyyy', new Date(1991, 2, 8, 0));
58+
expectParse('February/5/1980', 'MMMM/d/yyyy', new Date(1980, 1, 5, 0));
59+
expectParse('1955/February/5', 'yyyy/MMMM/d', new Date(1955, 1, 5, 0));
60+
expectParse('11-08-13', 'd-MM-yy', new Date(2013, 7, 11, 0));
61+
});
62+
});
63+
64+
describe('wih predefined formats', function() {
65+
it('should work correctly for `shortDate`', function() {
66+
expectParse('9/3/10', 'shortDate', new Date(2010, 8, 3, 0));
67+
});
68+
69+
it('should work correctly for `mediumDate`', function() {
70+
expectParse('Sep 3, 2010', 'mediumDate', new Date(2010, 8, 3, 0));
71+
});
72+
73+
it('should work correctly for `longDate`', function() {
74+
expectParse('September 3, 2010', 'longDate', new Date(2010, 8, 3, 0));
75+
});
76+
77+
it('should work correctly for `fullDate`', function() {
78+
expectParse('Friday, September 3, 2010', 'fullDate', new Date(2010, 8, 3, 0));
79+
});
80+
});
81+
82+
describe('with edge case', function() {
83+
it('should not work for invalid number of days in February', function() {
84+
expect(dateParser.parse('29.02.2013', 'dd.MM.yyyy')).toBeUndefined();
85+
});
86+
87+
it('should work for 29 days in February for leap years', function() {
88+
expectParse('29.02.2000', 'dd.MM.yyyy', new Date(2000, 1, 29, 0));
89+
});
90+
91+
it('should not work for 31 days for some months', function() {
92+
expect(dateParser.parse('31-04-2013', 'dd-MM-yyyy')).toBeUndefined();
93+
expect(dateParser.parse('November 31, 2013', 'MMMM d, yyyy')).toBeUndefined();
94+
});
95+
});
96+
});

0 commit comments

Comments
 (0)