import { configFromStringAndFormat } from './from-string-and-format';
+import { configFromArray } from './from-array';
import { hooks } from '../utils/hooks';
import { deprecate } from '../utils/deprecate';
import getParsingFlags from './parsing-flags';
+import {defaultLocaleMonthsShort} from '../units/month';
+import {defaultLocaleWeekdaysShort} from '../units/day-of-week';
// iso 8601 regex
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
}
// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
-var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;
-
-// date and time from ref 2822 format
-export function configFromRFC2822(config) {
- var string, match, dayFormat,
- dateFormat, timeFormat, tzFormat;
- var timezones = {
- ' GMT': ' +0000',
- ' EDT': ' -0400',
- ' EST': ' -0500',
- ' CDT': ' -0500',
- ' CST': ' -0600',
- ' MDT': ' -0600',
- ' MST': ' -0700',
- ' PDT': ' -0700',
- ' PST': ' -0800'
+var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;
+
+function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
+ var result = {
+ year: yearStr.length === 2 ? untrucateYear(parse10(yearStr)) : parse10(yearStr),
+ month: defaultLocaleMonthsShort.indexOf(monthStr),
+ day: parse10(dayStr),
+ hour: parse10(hourStr),
+ minute: parse10(minuteStr)
};
- var military = 'YXWVUTSRQPONZABCDEFGHIKLM';
- var timezone, timezoneIndex;
- string = config._i
- .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace
- .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space
- .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces
- match = basicRfcRegex.exec(string);
+ if (secondStr) {
+ result.second = parse10(secondStr);
+ }
- if (match) {
- dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : '';
- dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY ');
- timeFormat = 'HH:mm' + (match[4] ? ':ss' : '');
+ return result;
+}
- // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
- if (match[1]) { // day of week given
- var momentDate = new Date(match[2]);
- var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()];
+function untrucateYear(year) {
+ return year > 60 ? 1900 + year : 2000 + year;
+}
- if (match[1].substr(0,3) !== momentDay) {
- getParsingFlags(config).weekdayMismatch = true;
- config._isValid = false;
- return;
- }
+function preprocessRFC2822(s) {
+ // Remove comments and folding whitespace and replace multiple-spaces with a single space
+ return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim();
+}
+
+function signedOffset(offHourStr, offMinuteStr) {
+ var offHour = parse10(offHourStr) || 0,
+ offMin = parse10(offMinuteStr) || 0,
+ offMinSigned = offHour < 0 ? -offMin : offMin;
+ return offHour * 60 + offMinSigned;
+}
+
+function checkWeekday(weekdayStr, parsedInput, config) {
+ if (weekdayStr) {
+ // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
+ var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
+ weekdayActual = new Date(parsedInput.year, parsedInput.month, parsedInput.day).getDay();
+ if (weekdayProvided !== weekdayActual) {
+ getParsingFlags(config).weekdayMismatch = true;
+ config._isValid = false;
+ return false;
}
+ }
+ return true;
+}
- switch (match[5].length) {
- case 2: // military
- if (timezoneIndex === 0) {
- timezone = ' +0000';
- } else {
- timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12;
- timezone = ((timezoneIndex < 0) ? ' -' : ' +') +
- (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00';
- }
- break;
- case 4: // Zone
- timezone = timezones[match[5]];
- break;
- default: // UT or +/-9999
- timezone = timezones[' GMT'];
+var obsOffsets = {
+ GMT: 0,
+ EDT: -4 * 60,
+ EST: -5 * 60,
+ CDT: 5 * 60,
+ CST: 6 * 60,
+ MDT: 6 * 60,
+ MST: 7 * 60,
+ PDT: 7 * 60,
+ PST: 8 * 60
+};
+
+function parse10(inty) {
+ return parseInt(inty, 10);
+}
+
+function calculateOffset(obsOffset, milOffset, offHourStr, offMinuteStr) {
+ if (obsOffset) {
+ return obsOffsets[obsOffset];
+ } else {
+ return (milOffset) ? 0 : signedOffset(offHourStr, offMinuteStr);
+ }
+}
+
+// date and time from ref 2822 format
+export function configFromRFC2822(config) {
+ var match = rfc2822.exec(preprocessRFC2822(config._i));
+ if (match) {
+ var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
+ if (!checkWeekday(match[1], parsedArray, config)) {
+ return;
}
- match[5] = timezone;
- config._i = match.splice(1).join('');
- tzFormat = ' ZZ';
- config._f = dayFormat + dateFormat + timeFormat + tzFormat;
- configFromStringAndFormat(config);
+
+ config._a = [parsedArray.year, parsedArray.month, parsedArray.day, parsedArray.hour, parsedArray.minute, parsedArray.second];
+ config._tzm = calculateOffset(match[8], match[9], match[10], match[11]);
+
+ configFromArray(config);
getParsingFlags(config).rfc2822 = true;
} else {
config._isValid = false;
assert.equal(moment([2012, 0, 9]).format('w ww wo'), '2 02 2-я', 'Jan 9 2012 should be week 2');
});
+test('parsing RFC 2822', function (assert) {
+ var testCases = {
+ 'clean RFC2822 datetime with all options': 'Tue, 01 Nov 2016 01:23:45 UT',
+ 'clean RFC2822 datetime without comma': 'Tue 01 Nov 2016 02:23:45 GMT',
+ 'clean RFC2822 datetime without seconds': 'Tue, 01 Nov 2016 03:23 +0000',
+ 'clean RFC2822 datetime without century': 'Tue, 01 Nov 16 04:23:45 Z',
+ 'clean RFC2822 datetime without day': '01 Nov 2016 05:23:45 z',
+ 'clean RFC2822 datetime with single-digit day-of-month': 'Tue, 1 Nov 2016 06:23:45 GMT',
+ 'RFC2822 datetime with CFWSs': '(Init Comment) Tue,\n 1 Nov 2016 (Split\n Comment) 07:23:45 +0000 (GMT)'
+ };
+ var testCase;
+
+ for (testCase in testCases) {
+ var testResult = moment(testCases[testCase], moment.RFC_2822, true);
+ assert.ok(testResult.isValid(), testResult);
+ assert.ok(testResult.parsingFlags().rfc2822, testResult + ' - rfc2822 parsingFlag');
+ }
+});
+
+test('non RFC 2822 strings', function (assert) {
+ var testCases = {
+ 'RFC2822 datetime with all options but invalid day delimiter': 'Tue. 01 Nov 2016 01:23:45 GMT',
+ 'RFC2822 datetime with mismatching Day (week v date)': 'Mon, 01 Nov 2016 01:23:45 GMT'
+ };
+ var testCase;
+
+ for (testCase in testCases) {
+ var testResult = moment(testCases[testCase], moment.RFC_2822, true);
+ assert.ok(!testResult.isValid(), testResult);
+ assert.ok(!testResult.parsingFlags().rfc2822, testResult + ' - rfc2822 parsingFlag');
+ }
+});