Skip to content

Instantly share code, notes, and snippets.

@jasonsperske
Created November 3, 2016 06:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonsperske/d4e6b35dd2bcb1aaf0206e8f6e615b7f to your computer and use it in GitHub Desktop.
Save jasonsperske/d4e6b35dd2bcb1aaf0206e8f6e615b7f to your computer and use it in GitHub Desktop.
function Parser() {
var int = function(n) {
//keep code readable
return parseInt(n, 10);
};
var def = function(field, source, defaults) {
return source[field] ? source[field] : (defaults ? defaults[field] : undefined);
};
var pad = function(number) {
if (number < 10) {
return '0' + number;
}
return number;
};
var military_hour = function(hour, meridian) {
if(meridian === 'PM' && hour < 12) {
return hour + 12;
} else if(meridian === 'AM' && hour === 12) {
return 0;
} else {
return hour;
}
};
var months = {
"JAN": 1,
"FEB": 2,
"MAR": 3,
"APR": 4, "API": 4,
"MAY": 5,
"JUN": 6,
"JUL": 7,
"AUG": 8,
"SEP": 9,
"OCT": 10,
"NOV": 11,
"DEC": 12
};
//Data extracted from: https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
//"ACT" defies reason and was omitted,
var timezones = {
"ACDT": 10.5,
"ACST": 9.5,
"ADT": -3,
"AEDT": 11,
"AEST": 10,
"AFT": 4.5,
"AKDT": -8,
"AKST": -9,
"AMST": -3,
"AMT": 4,
"ART": -3,
"AST": -4,
"AWST": 8,
"AZOST": 0,
"AZOT": -1,
"AZT": 4,
"BDT": 8,
"BIOT": 6,
"BIT": -12,
"BOT": -4,
"BRST": -2,
"BRT": -3,
"BST": 1,
"BTT": 6,
"CAT": 2,
"CCT": 6.5,
"CDT": -4,
"CEST": 2,
"CET": 1,
"CHADT": 13.75,
"CHAST": 12.75,
"CHOT": 8,
"CHOST": 9,
"CHST": 10,
"CHUT": 10,
"CIST": -8,
"CIT": 8,
"CKT": -10,
"CLST": -3,
"CLT": -4,
"COST": -4,
"COT": -5,
"CST": -5,
"CT": 8,
"CVT": -1,
"CWST": 8.75,
"CXT": 7,
"DAVT": 7,
"DDUT": 10,
"DFT": 1,
"EASST": -5,
"EAST": -6,
"EAT": 3,
"ECT": -5,
"EDT": -4,
"EEST": 3,
"EET": 2,
"EGST": 0,
"EGT": -1,
"EIT": 9,
"EST": -5,
"FET": 3,
"FJT": 12,
"FKST": -3,
"FKT": -4,
"FNT": -2,
"GALT": -6,
"GAMT": -9,
"GET": 4,
"GFT": -3,
"GILT": 12,
"GIT": -9,
"GMT": 0,
"GST": 4,
"GYT": -4,
"HADT": -9,
"HAEC": 2,
"HAST": -10,
"HKT": 8,
"HMT": 5,
"HOVST": 8,
"HOVT": 7,
"ICT": 7,
"IDT": 3,
"IOT": 3,
"IRDT": 4.5,
"IRKT": 8,
"IRST": 3.5,
"IST": 2,
"JST": 9,
"KGT": 6,
"KOST": 11,
"KRAT": 7,
"KST": 9,
"LHST": 11,
"LINT": 14,
"MAGT": 12,
"MART": -9.5,
"MAWT": 5,
"MDT": -6,
"MET": 1,
"MEST": 2,
"MHT": 12,
"MIST": 11,
"MIT": -9.5,
"MMT": 6.5,
"MSK": 3,
"MST": -7,
"MUT": 4,
"MVT": 5,
"MYT": 8,
"NCT": 11,
"NDT": -2.5,
"NFT": 11,
"NPT": 5.75,
"NST": -3.5,
"NT": -3.5,
"NUT": -11,
"NZDT": 13,
"NZST": 12,
"OMST": 6,
"ORAT": 5,
"PDT": -7,
"PET": -5,
"PETT": 12,
"PGT": 10,
"PHOT": 13,
"PKT": 5,
"PMDT": -2,
"PMST": -3,
"PONT": 11,
"PST": 8,
"PYST": -3,
"PYT": -4,
"RET": 4,
"ROTT": -3,
"SAKT": 11,
"SAMT": 4,
"SAST": 2,
"SBT": 11,
"SCT": 4,
"SGT": 8,
"SLST": 5.5,
"SRET": 11,
"SRT": -3,
"SST": 8,
"SYOT": 3,
"TAHT": -10,
"THA": 7,
"TFT": 5,
"TJT": 5,
"TKT": 13,
"TLT": 9,
"TMT": 5,
"TRT": 3,
"TOT": 13,
"TVT": 12,
"ULAST": 9,
"ULAT": 8,
"USZ1": 2,
"UTC": 0,
"UYST": -2,
"UYT": -3,
"UZT": 5,
"VET": -4,
"VLAT": 10,
"VOLT": 4,
"VOST": 6,
"VUT": 11,
"WAKT": 12,
"WAST": 2,
"WAT": 1,
"WEST": 1,
"WET": 0,
"WIT": 7,
"WST": 8,
"YAKT": 9,
"YEKT": 5
};
var adjust = function(moment, offset) {
var adjusted = moment,
d;
//Timezone offets are never longer than ± 13.75
adjusted.minute = moment.minute + (offset * 60);
adjusted.hour = adjusted.hour + Math.floor(adjusted.minute/60);
adjusted.minute = adjusted.minute - (Math.floor(adjusted.minute/60) * 60);
//the Date object understands leap year and verious details about the
//gregorian calendar, so lets take advantage of this
d = new Date(Date.UTC(adjusted.year, adjusted.month -1, adjusted.day, adjusted.hour, adjusted.minute, 0));
adjusted.year = d.getUTCFullYear();
adjusted.month = d.getUTCMonth()+1;
adjusted.day = d.getUTCDate();
adjusted.hour = d.getUTCHours();
adjusted.minute = d.getUTCMinutes();
return adjusted;
};
var patterns = {
Val_Val_YYYY: {
re: /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/,
parse: function(a, b, year) {
//We can't tell if it's day/month or month/day
if(a > 12) {
//A can't be month
return { year: int(year), month: int(b), day: int(a) };
} else if (b > 12) {
//B can't be month
return { year: int(year), month: int(a), day: int(b) };
} else {
//Can't tell so pick based US format
return { year: int(year), month: int(a), day: int(b) };
}
}
},
YYYYMMDD: {
re: /^(\d{4})(?:\.?)(\d{2})(?:\.?)(\d{2})$/,
parse: function(year, month, day) {
return { year: int(year), month: int(month), day: int(day) };
}
},
Month_DD_YYYY: {
re: /(\w+) (\d{1,2})(?:,) (\d{4})$/,
parse: function(month, day, year) {
return { year: int(year), month: months[(month.substring(0,3).toUpperCase())], day: int(day) };
}
},
DD_Month_YYYY: {
re: /(\d{1,2}) (\w+) (\d{4})$/,
parse: function(day, month, year) {
return { year: int(year), month: months[(month.substring(0,3).toUpperCase())], day: int(day) };
}
},
Month_YYYY: {
re: /^(\w+), (\d{4})$/,
parse: function(month, year) {
return { year: int(year), month: months[(month.substring(0,3).toUpperCase())] };
}
},
Month_DD: {
re: /^(\w+) (\d{1,2})$/,
parse: function(month, day) {
return { day: int(day), month: months[(month.substring(0,3).toUpperCase())] };
}
},
HH_MM_SS_meridian: {
re: /^(\d{1,2}):(\d{2}):(\d{2}) (AM|PM)$/,
parse: function(hour, minute, second, meridian) {
return { hour: military_hour(int(hour), meridian.toUpperCase()), minute: int(minute), second: int(second) };
}
},
Month_DD_YYYY_HH_MM_SS_meridian: {
re: /^(?:\w+)(?:,)? (\w*) (\d{1,2}), (\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))? (AM|PM)$/,
parse: function(month, day, year, hour, minute, second, meridian) {
return { day: int(day), month: months[(month.substring(0,3).toUpperCase())], year: int(year), hour: military_hour(int(hour), meridian.toUpperCase()), minute: int(minute), second: int(second) };
}
},
DD_Month_YYYY_HH_MM_SS_meridian: {
re: /^(?:\w+) (\d{1,2})(?:th|st|nd) of (\w+) (\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))? (AM|PM)$/,
parse: function(day, month, year, hour, minute, second, meridian) {
return { day: int(day), month: months[(month.substring(0,3).toUpperCase())], year: int(year), hour: military_hour(int(hour), meridian.toUpperCase()), minute: int(minute), second: int(second) };
}
},
MM_DD_YYYY_HH_MM_SS_meridian: {
re: /^(\d{1,2})\/(\d{1,2})\/(\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))? (AM|PM)$/,
parse: function(month, day, year, hour, minute, second, meridian) {
return { day: int(day), month: int(month), year: int(year), hour: military_hour(int(hour), meridian.toUpperCase()), minute: int(minute), second: int(second) };
}
},
YYYY_MM_DD_HH_MM_SS: {
re: /^(\d{4})(\d{2})(\d{2}) (\d{1,2}):(\d{2})(?::(\d{2}))?/,
parse: function(year, month, day, hour, minute, second) {
return { day: int(day), month: int(month), year: int(year), hour: int(hour), minute: int(minute), second: int(second) };
}
},
DD_Mon_YYYY_HH_MM_SS_TZabbv: {
re: /^(?:\w+), (\d{1,2}) (\w+) (\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))? (\w+)$/,
parse: function(day, month, year, hour, minute, second, tz_abbv) {
var moment = {
day: int(day),
month: months[(month.substring(0,3).toUpperCase())],
year: int(year),
hour: int(hour),
minute: int(minute),
second: int(second)
};
return adjust(moment, timezones[tz_abbv.toUpperCase()]);
}
},
DD_Mon_YYYY_HH_MM_SS_TZoffset: {
re: /^(?:\w+), (\d{1,2}) (\w+) (\d{4}) (\d{1,2}):(\d{2})(?::(\d{2}))? (-|\+)(\d{2})(\d{2})$/,
parse: function(day, month, year, hour, minute, second, tz_direction, tz_offset_hours, tz_offset_minutes) {
var moment = {
day: int(day),
month: months[(month.substring(0,3).toUpperCase())],
year: int(year),
hour: int(hour),
minute: int(minute),
second: int(second)
},
offset = int(tz_offset_hours);
switch (tz_offset_minutes) {
case '15': offset += 0.25; break;
case '30': offset += 0.5; break;
case '45': offset += 0.75; break;
}
if(tz_direction === '-') {
offset = offset * -1;
}
return adjust(moment, offset);
}
},
YYYY_DD_MM_HH_MM_SS_TZoffset: {
re: /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(-|\+)(\d{2}):(\d{2})$/,
parse: function(year, day, month, hour, minute, second, tz_direction, tz_offset_hours, tz_offset_minutes) {
var moment = {
day: int(day),
month: int(month),
year: int(year),
hour: int(hour),
minute: int(minute),
second: int(second)
},
offset = int(tz_offset_hours);
switch (tz_offset_minutes) {
case '15': offset += 0.25; break;
case '30': offset += 0.5; break;
case '45': offset += 0.75; break;
}
if(tz_direction === '-') {
offset = offset * -1;
}
return adjust(moment, offset);
}
}
};
return function(input, defaults) {
for (var pattern in patterns) {
if (patterns.hasOwnProperty(pattern)) {
if(patterns[pattern].re.test(input)) {
var parsed = patterns[pattern].parse.apply(this, patterns[pattern].re.exec(input).splice(1));
//Fill in any missing defaults
parsed.year = def('year', parsed, defaults);
parsed.month = def('month', parsed, defaults);
parsed.day = def('day', parsed, defaults);
parsed.hour = def('hour', parsed, defaults);
parsed.minute = def('minute', parsed, defaults);
parsed.second = def('second', parsed, defaults);
parsed.date = new Date(Date.UTC(parsed.year, parsed.month-1, parsed.day, parsed.hour, parsed.minute, parsed.second));
return parsed;
}
}
}
};
}
var inputs = [//Date Only
"3/20/2016", //Val_Val_YYYY
"20/03/2016",
"20160320", //YYYYMMDD
"2016.03.20",
"March 20, 2016", //Month_DD_YYYY
"Sunday, MAR 20, 2016",
"Sunday, March 20, 2016",
"20 March 2016", //DD_Month_YYYY
//Month/Year Only format
"March, 2016", //Month_YYYY
//Month/Day Only format
"March 20", //Month_DD
//Time Only format
"4:05:07 PM", //HH_MM_SS_meridian
// Full formats
"Sunday, March 20, 2016 4:05 PM", //Month_DD_YYYY_HH_MM_SS_meridian
"Sunday, March 20, 2016 4:05:07 PM",
"Sunday 20th of March 2016 04:05:07 PM",//DD_Month_YYYY_HH_MM_SS_meridian
"3/20/2016 4:05 PM", //MM_DD_YYYY_HH_MM_SS_meridian
"3/20/2016 4:05:07 PM",
"20160320 16:05:07", //YYYY_MM_DD_HH_MM_SS
// Full with timezone
"Sun, 20 Mar 2016 16:05:07 GMT", //DD_Mon_YYYY_HH_MM_SS_TZabbv
"Sun, 20 Mar 2016 4:05:07 MART", //DD_Mon_YYYY_HH_MM_SS_TZabbv
"Sun, 20 Mar 2016 16:05:07 -0800", //DD_Mon_YYYY_HH_MM_SS_TZoffset
"Sun, 1 Mar 2016 2:05:07 -0800", //DD_Mon_YYYY_HH_MM_SS_TZoffset
"2016-20-03T16:05:07-08:00"]; //YYYY_DD_MM_HH_MM_SS_TZoffset
var parse = new Parser();
inputs.forEach(function(input) {
var now = new Date(),
defaults_now = { //Using this exact moment for any missing information
year: now.getUTCFullYear(),
month: now.getUTCMonth() + 1,
day: now.getUTCDate(),
hour: now.getUTCHours(),
minute: now.getUTCMinutes(),
second: now.getUTCSeconds()
},
defaults_logical = { //Using this exact moment for any missing information
year: now.getUTCFullYear(),
month: now.getUTCMonth() + 1,
day: 1,
hour: 0,
minute: 0,
second: 0
},
defaults_starwars = { //Using the day Star Wars:A New Hope was released in theaters
year: 1977,
month: 5,
day: 25,
hour: 0,
minute: 0,
second: 0
};
console.log(parse(input, defaults_logical).date.toISOString()+' from "'+input+'"');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment