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

Commit

Permalink
feat(dateParser): add dateParser service
Browse files Browse the repository at this point in the history
Closes #1874
  • Loading branch information
bekos committed Apr 19, 2014
1 parent 73c2dea commit bd2ae0e
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 0 deletions.
126 changes: 126 additions & 0 deletions src/dateparser/dateparser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
angular.module('ui.bootstrap.dateparser', [])

.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {

this.parsers = {};

var formatCodeToRegex = {
'yyyy': {
regex: '\\d{4}',
apply: function(value) { this.year = +value; }
},
'yy': {
regex: '\\d{2}',
apply: function(value) { this.year = +value + 2000; }
},
'y': {
regex: '\\d{1,4}',
apply: function(value) { this.year = +value; }
},
'MMMM': {
regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
},
'MMM': {
regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
},
'MM': {
regex: '0[1-9]|1[0-2]',
apply: function(value) { this.month = value - 1; }
},
'M': {
regex: '[1-9]|1[0-2]',
apply: function(value) { this.month = value - 1; }
},
'dd': {
regex: '[0-2][0-9]{1}|3[0-1]{1}',
apply: function(value) { this.date = +value; }
},
'd': {
regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
apply: function(value) { this.date = +value; }
},
'EEEE': {
regex: $locale.DATETIME_FORMATS.DAY.join('|')
},
'EEE': {
regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
}
};

this.createParser = function(format) {
var map = [], regex = format.split('');

angular.forEach(formatCodeToRegex, function(data, code) {
var index = format.indexOf(code);

if (index > -1) {
format = format.split('');

regex[index] = '(' + data.regex + ')';
format[index] = '$'; // Custom symbol to define consumed part of format
for (var i = index + 1, n = index + code.length; i < n; i++) {
regex[i] = '';
format[i] = '$';
}
format = format.join('');

map.push({ index: index, apply: data.apply });
}
});

return {
regex: new RegExp('^' + regex.join('') + '$'),
map: orderByFilter(map, 'index')
};
};

this.parse = function(input, format) {
if ( !angular.isString(input) ) {
return input;
}

format = $locale.DATETIME_FORMATS[format] || format;

if ( !this.parsers[format] ) {
this.parsers[format] = this.createParser(format);
}

var parser = this.parsers[format],
regex = parser.regex,
map = parser.map,
results = input.match(regex);

if ( results && results.length ) {
var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;

for( var i = 1, n = results.length; i < n; i++ ) {
var mapper = map[i-1];
if ( mapper.apply ) {
mapper.apply.call(fields, results[i]);
}
}

if ( isValid(fields.year, fields.month, fields.date) ) {
dt = new Date( fields.year, fields.month, fields.date, fields.hours);
}

return dt;
}
};

// Check if date is valid for specific month (and year for February).
// Month: 0 = Jan, 1 = Feb, etc
function isValid(year, month, date) {
if ( month === 1 && date > 28) {
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
}

if ( month === 3 || month === 5 || month === 8 || month === 10) {
return date < 31;
}

return true;
}
}]);
96 changes: 96 additions & 0 deletions src/dateparser/test/dateparser.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
describe('date parser', function () {
var dateParser;

beforeEach(module('ui.bootstrap.dateparser'));
beforeEach(inject(function (_dateParser_) {
dateParser = _dateParser_;
}));

function expectParse(input, format, date) {
expect(dateParser.parse(input, format)).toEqual(date);
}

describe('wih custom formats', function() {
it('should work correctly for `dd`, `MM`, `yyyy`', function() {
expectParse('17.11.2013', 'dd.MM.yyyy', new Date(2013, 10, 17, 0));
expectParse('31.12.2013', 'dd.MM.yyyy', new Date(2013, 11, 31, 0));
expectParse('08-03-1991', 'dd-MM-yyyy', new Date(1991, 2, 8, 0));
expectParse('03/05/1980', 'MM/dd/yyyy', new Date(1980, 2, 5, 0));
expectParse('10.01/1983', 'dd.MM/yyyy', new Date(1983, 0, 10, 0));
expectParse('11-09-1980', 'MM-dd-yyyy', new Date(1980, 10, 9, 0));
expectParse('2011/02/05', 'yyyy/MM/dd', new Date(2011, 1, 5, 0));
});

it('should work correctly for `yy`', function() {
expectParse('17.11.13', 'dd.MM.yy', new Date(2013, 10, 17, 0));
expectParse('02-05-11', 'dd-MM-yy', new Date(2011, 4, 2, 0));
expectParse('02/05/80', 'MM/dd/yy', new Date(2080, 1, 5, 0));
expectParse('55/02/05', 'yy/MM/dd', new Date(2055, 1, 5, 0));
expectParse('11-08-13', 'dd-MM-yy', new Date(2013, 7, 11, 0));
});

it('should work correctly for `M`', function() {
expectParse('8/11/2013', 'M/dd/yyyy', new Date(2013, 7, 11, 0));
expectParse('07.11.05', 'dd.M.yy', new Date(2005, 10, 7, 0));
expectParse('02-5-11', 'dd-M-yy', new Date(2011, 4, 2, 0));
expectParse('2/05/1980', 'M/dd/yyyy', new Date(1980, 1, 5, 0));
expectParse('1955/2/05', 'yyyy/M/dd', new Date(1955, 1, 5, 0));
expectParse('02-5-11', 'dd-M-yy', new Date(2011, 4, 2, 0));
});

it('should work correctly for `MMM`', function() {
expectParse('30.Sep.10', 'dd.MMM.yy', new Date(2010, 8, 30, 0));
expectParse('02-May-11', 'dd-MMM-yy', new Date(2011, 4, 2, 0));
expectParse('Feb/05/1980', 'MMM/dd/yyyy', new Date(1980, 1, 5, 0));
expectParse('1955/Feb/05', 'yyyy/MMM/dd', new Date(1955, 1, 5, 0));
});

it('should work correctly for `MMMM`', function() {
expectParse('17.November.13', 'dd.MMMM.yy', new Date(2013, 10, 17, 0));
expectParse('05-March-1980', 'dd-MMMM-yyyy', new Date(1980, 2, 5, 0));
expectParse('February/05/1980', 'MMMM/dd/yyyy', new Date(1980, 1, 5, 0));
expectParse('1949/December/20', 'yyyy/MMMM/dd', new Date(1949, 11, 20, 0));
});

it('should work correctly for `d`', function() {
expectParse('17.November.13', 'd.MMMM.yy', new Date(2013, 10, 17, 0));
expectParse('8-March-1991', 'd-MMMM-yyyy', new Date(1991, 2, 8, 0));
expectParse('February/5/1980', 'MMMM/d/yyyy', new Date(1980, 1, 5, 0));
expectParse('1955/February/5', 'yyyy/MMMM/d', new Date(1955, 1, 5, 0));
expectParse('11-08-13', 'd-MM-yy', new Date(2013, 7, 11, 0));
});
});

describe('wih predefined formats', function() {
it('should work correctly for `shortDate`', function() {
expectParse('9/3/10', 'shortDate', new Date(2010, 8, 3, 0));
});

it('should work correctly for `mediumDate`', function() {
expectParse('Sep 3, 2010', 'mediumDate', new Date(2010, 8, 3, 0));
});

it('should work correctly for `longDate`', function() {
expectParse('September 3, 2010', 'longDate', new Date(2010, 8, 3, 0));
});

it('should work correctly for `fullDate`', function() {
expectParse('Friday, September 3, 2010', 'fullDate', new Date(2010, 8, 3, 0));
});
});

describe('with edge case', function() {
it('should not work for invalid number of days in February', function() {
expect(dateParser.parse('29.02.2013', 'dd.MM.yyyy')).toBeUndefined();
});

it('should work for 29 days in February for leap years', function() {
expectParse('29.02.2000', 'dd.MM.yyyy', new Date(2000, 1, 29, 0));
});

it('should not work for 31 days for some months', function() {
expect(dateParser.parse('31-04-2013', 'dd-MM-yyyy')).toBeUndefined();
expect(dateParser.parse('November 31, 2013', 'MMMM d, yyyy')).toBeUndefined();
});
});
});

0 comments on commit bd2ae0e

Please sign in to comment.