locutus
Version:
Locutus other languages' standard libraries to JavaScript for fun and educational purposes
1,113 lines (968 loc) • 31.2 kB
JavaScript
'use strict';
var reSpace = '[ \\t]+';
var reSpaceOpt = '[ \\t]*';
var reMeridian = '(?:([ap])\\.?m\\.?([\\t ]|$))';
var reHour24 = '(2[0-4]|[01]?[0-9])';
var reHour24lz = '([01][0-9]|2[0-4])';
var reHour12 = '(0?[1-9]|1[0-2])';
var reMinute = '([0-5]?[0-9])';
var reMinutelz = '([0-5][0-9])';
var reSecond = '(60|[0-5]?[0-9])';
var reSecondlz = '(60|[0-5][0-9])';
var reFrac = '(?:\\.([0-9]+))';
var reDayfull = 'sunday|monday|tuesday|wednesday|thursday|friday|saturday';
var reDayabbr = 'sun|mon|tue|wed|thu|fri|sat';
var reDaytext = reDayfull + '|' + reDayabbr + '|weekdays?';
var reReltextnumber = 'first|second|third|fourth|fifth|sixth|seventh|eighth?|ninth|tenth|eleventh|twelfth';
var reReltexttext = 'next|last|previous|this';
var reReltextunit = '(?:second|sec|minute|min|hour|day|fortnight|forthnight|month|year)s?|weeks|' + reDaytext;
var reYear = '([0-9]{1,4})';
var reYear2 = '([0-9]{2})';
var reYear4 = '([0-9]{4})';
var reYear4withSign = '([+-]?[0-9]{4})';
var reMonth = '(1[0-2]|0?[0-9])';
var reMonthlz = '(0[0-9]|1[0-2])';
var reDay = '(?:(3[01]|[0-2]?[0-9])(?:st|nd|rd|th)?)';
var reDaylz = '(0[0-9]|[1-2][0-9]|3[01])';
var reMonthFull = 'january|february|march|april|may|june|july|august|september|october|november|december';
var reMonthAbbr = 'jan|feb|mar|apr|may|jun|jul|aug|sept?|oct|nov|dec';
var reMonthroman = 'i[vx]|vi{0,3}|xi{0,2}|i{1,3}';
var reMonthText = '(' + reMonthFull + '|' + reMonthAbbr + '|' + reMonthroman + ')';
var reTzCorrection = '((?:GMT)?([+-])' + reHour24 + ':?' + reMinute + '?)';
var reDayOfYear = '(00[1-9]|0[1-9][0-9]|[12][0-9][0-9]|3[0-5][0-9]|36[0-6])';
var reWeekOfYear = '(0[1-9]|[1-4][0-9]|5[0-3])';
var reDateNoYear = reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]*';
function processMeridian(hour, meridian) {
meridian = meridian && meridian.toLowerCase();
switch (meridian) {
case 'a':
hour += hour === 12 ? -12 : 0;
break;
case 'p':
hour += hour !== 12 ? 12 : 0;
break;
}
return hour;
}
function processYear(yearStr) {
var year = +yearStr;
if (yearStr.length < 4 && year < 100) {
year += year < 70 ? 2000 : 1900;
}
return year;
}
function lookupMonth(monthStr) {
return {
jan: 0,
january: 0,
i: 0,
feb: 1,
february: 1,
ii: 1,
mar: 2,
march: 2,
iii: 2,
apr: 3,
april: 3,
iv: 3,
may: 4,
v: 4,
jun: 5,
june: 5,
vi: 5,
jul: 6,
july: 6,
vii: 6,
aug: 7,
august: 7,
viii: 7,
sep: 8,
sept: 8,
september: 8,
ix: 8,
oct: 9,
october: 9,
x: 9,
nov: 10,
november: 10,
xi: 10,
dec: 11,
december: 11,
xii: 11
}[monthStr.toLowerCase()];
}
function lookupWeekday(dayStr) {
var desiredSundayNumber = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var dayNumbers = {
mon: 1,
monday: 1,
tue: 2,
tuesday: 2,
wed: 3,
wednesday: 3,
thu: 4,
thursday: 4,
fri: 5,
friday: 5,
sat: 6,
saturday: 6,
sun: 0,
sunday: 0
};
return dayNumbers[dayStr.toLowerCase()] || desiredSundayNumber;
}
function lookupRelative(relText) {
var relativeNumbers = {
last: -1,
previous: -1,
this: 0,
first: 1,
next: 1,
second: 2,
third: 3,
fourth: 4,
fifth: 5,
sixth: 6,
seventh: 7,
eight: 8,
eighth: 8,
ninth: 9,
tenth: 10,
eleventh: 11,
twelfth: 12
};
var relativeBehavior = {
this: 1
};
var relTextLower = relText.toLowerCase();
return {
amount: relativeNumbers[relTextLower],
behavior: relativeBehavior[relTextLower] || 0
};
}
function processTzCorrection(tzOffset, oldValue) {
var reTzCorrectionLoose = /(?:GMT)?([+-])(\d+)(:?)(\d{0,2})/i;
tzOffset = tzOffset && tzOffset.match(reTzCorrectionLoose);
if (!tzOffset) {
return oldValue;
}
var sign = tzOffset[1] === '-' ? 1 : -1;
var hours = +tzOffset[2];
var minutes = +tzOffset[4];
if (!tzOffset[4] && !tzOffset[3]) {
minutes = Math.floor(hours % 100);
hours = Math.floor(hours / 100);
}
return sign * (hours * 60 + minutes);
}
var formats = {
yesterday: {
regex: /^yesterday/i,
name: 'yesterday',
callback: function callback() {
this.rd -= 1;
return this.resetTime();
}
},
now: {
regex: /^now/i,
name: 'now'
// do nothing
},
noon: {
regex: /^noon/i,
name: 'noon',
callback: function callback() {
return this.resetTime() && this.time(12, 0, 0, 0);
}
},
midnightOrToday: {
regex: /^(midnight|today)/i,
name: 'midnight | today',
callback: function callback() {
return this.resetTime();
}
},
tomorrow: {
regex: /^tomorrow/i,
name: 'tomorrow',
callback: function callback() {
this.rd += 1;
return this.resetTime();
}
},
timestamp: {
regex: /^@(-?\d+)/i,
name: 'timestamp',
callback: function callback(match, timestamp) {
this.rs += +timestamp;
this.y = 1970;
this.m = 0;
this.d = 1;
this.dates = 0;
return this.resetTime() && this.zone(0);
}
},
firstOrLastDay: {
regex: /^(first|last) day of/i,
name: 'firstdayof | lastdayof',
callback: function callback(match, day) {
if (day.toLowerCase() === 'first') {
this.firstOrLastDayOfMonth = 1;
} else {
this.firstOrLastDayOfMonth = -1;
}
}
},
backOrFrontOf: {
regex: RegExp('^(back|front) of ' + reHour24 + reSpaceOpt + reMeridian + '?', 'i'),
name: 'backof | frontof',
callback: function callback(match, side, hours, meridian) {
var back = side.toLowerCase() === 'back';
var hour = +hours;
var minute = 15;
if (!back) {
hour -= 1;
minute = 45;
}
hour = processMeridian(hour, meridian);
return this.resetTime() && this.time(hour, minute, 0, 0);
}
},
weekdayOf: {
regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reDayfull + '|' + reDayabbr + ')' + reSpace + 'of', 'i'),
name: 'weekdayof'
// todo
},
mssqltime: {
regex: RegExp('^' + reHour12 + ':' + reMinutelz + ':' + reSecondlz + '[:.]([0-9]+)' + reMeridian, 'i'),
name: 'mssqltime',
callback: function callback(match, hour, minute, second, frac, meridian) {
return this.time(processMeridian(+hour, meridian), +minute, +second, +frac.substr(0, 3));
}
},
timeLong12: {
regex: RegExp('^' + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),
name: 'timelong12',
callback: function callback(match, hour, minute, second, meridian) {
return this.time(processMeridian(+hour, meridian), +minute, +second, 0);
}
},
timeShort12: {
regex: RegExp('^' + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),
name: 'timeshort12',
callback: function callback(match, hour, minute, meridian) {
return this.time(processMeridian(+hour, meridian), +minute, 0, 0);
}
},
timeTiny12: {
regex: RegExp('^' + reHour12 + reSpaceOpt + reMeridian, 'i'),
name: 'timetiny12',
callback: function callback(match, hour, meridian) {
return this.time(processMeridian(+hour, meridian), 0, 0, 0);
}
},
soap: {
regex: RegExp('^' + reYear4 + '-' + reMonthlz + '-' + reDaylz + 'T' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reFrac + reTzCorrection + '?', 'i'),
name: 'soap',
callback: function callback(match, year, month, day, hour, minute, second, frac, tzCorrection) {
return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, +frac.substr(0, 3)) && this.zone(processTzCorrection(tzCorrection));
}
},
wddx: {
regex: RegExp('^' + reYear4 + '-' + reMonth + '-' + reDay + 'T' + reHour24 + ':' + reMinute + ':' + reSecond),
name: 'wddx',
callback: function callback(match, year, month, day, hour, minute, second) {
return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);
}
},
exif: {
regex: RegExp('^' + reYear4 + ':' + reMonthlz + ':' + reDaylz + ' ' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz, 'i'),
name: 'exif',
callback: function callback(match, year, month, day, hour, minute, second) {
return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);
}
},
xmlRpc: {
regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + 'T' + reHour24 + ':' + reMinutelz + ':' + reSecondlz),
name: 'xmlrpc',
callback: function callback(match, year, month, day, hour, minute, second) {
return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);
}
},
xmlRpcNoColon: {
regex: RegExp('^' + reYear4 + reMonthlz + reDaylz + '[Tt]' + reHour24 + reMinutelz + reSecondlz),
name: 'xmlrpcnocolon',
callback: function callback(match, year, month, day, hour, minute, second) {
return this.ymd(+year, month - 1, +day) && this.time(+hour, +minute, +second, 0);
}
},
clf: {
regex: RegExp('^' + reDay + '/(' + reMonthAbbr + ')/' + reYear4 + ':' + reHour24lz + ':' + reMinutelz + ':' + reSecondlz + reSpace + reTzCorrection, 'i'),
name: 'clf',
callback: function callback(match, day, month, year, hour, minute, second, tzCorrection) {
return this.ymd(+year, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0) && this.zone(processTzCorrection(tzCorrection));
}
},
iso8601long: {
regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond + reFrac, 'i'),
name: 'iso8601long',
callback: function callback(match, hour, minute, second, frac) {
return this.time(+hour, +minute, +second, +frac.substr(0, 3));
}
},
dateTextual: {
regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reDay + '[,.stndrh\\t ]+' + reYear, 'i'),
name: 'datetextual',
callback: function callback(match, month, day, year) {
return this.ymd(processYear(year), lookupMonth(month), +day);
}
},
pointedDate4: {
regex: RegExp('^' + reDay + '[.\\t-]' + reMonth + '[.-]' + reYear4),
name: 'pointeddate4',
callback: function callback(match, day, month, year) {
return this.ymd(+year, month - 1, +day);
}
},
pointedDate2: {
regex: RegExp('^' + reDay + '[.\\t]' + reMonth + '\\.' + reYear2),
name: 'pointeddate2',
callback: function callback(match, day, month, year) {
return this.ymd(processYear(year), month - 1, +day);
}
},
timeLong24: {
regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond),
name: 'timelong24',
callback: function callback(match, hour, minute, second) {
return this.time(+hour, +minute, +second, 0);
}
},
dateNoColon: {
regex: RegExp('^' + reYear4 + reMonthlz + reDaylz),
name: 'datenocolon',
callback: function callback(match, year, month, day) {
return this.ymd(+year, month - 1, +day);
}
},
pgydotd: {
regex: RegExp('^' + reYear4 + '\\.?' + reDayOfYear),
name: 'pgydotd',
callback: function callback(match, year, day) {
return this.ymd(+year, 0, +day);
}
},
timeShort24: {
regex: RegExp('^t?' + reHour24 + '[:.]' + reMinute, 'i'),
name: 'timeshort24',
callback: function callback(match, hour, minute) {
return this.time(+hour, +minute, 0, 0);
}
},
iso8601noColon: {
regex: RegExp('^t?' + reHour24lz + reMinutelz + reSecondlz, 'i'),
name: 'iso8601nocolon',
callback: function callback(match, hour, minute, second) {
return this.time(+hour, +minute, +second, 0);
}
},
iso8601dateSlash: {
// eventhough the trailing slash is optional in PHP
// here it's mandatory and inputs without the slash
// are handled by dateslash
regex: RegExp('^' + reYear4 + '/' + reMonthlz + '/' + reDaylz + '/'),
name: 'iso8601dateslash',
callback: function callback(match, year, month, day) {
return this.ymd(+year, month - 1, +day);
}
},
dateSlash: {
regex: RegExp('^' + reYear4 + '/' + reMonth + '/' + reDay),
name: 'dateslash',
callback: function callback(match, year, month, day) {
return this.ymd(+year, month - 1, +day);
}
},
american: {
regex: RegExp('^' + reMonth + '/' + reDay + '/' + reYear),
name: 'american',
callback: function callback(match, month, day, year) {
return this.ymd(processYear(year), month - 1, +day);
}
},
americanShort: {
regex: RegExp('^' + reMonth + '/' + reDay),
name: 'americanshort',
callback: function callback(match, month, day) {
return this.ymd(this.y, month - 1, +day);
}
},
gnuDateShortOrIso8601date2: {
// iso8601date2 is complete subset of gnudateshort
regex: RegExp('^' + reYear + '-' + reMonth + '-' + reDay),
name: 'gnudateshort | iso8601date2',
callback: function callback(match, year, month, day) {
return this.ymd(processYear(year), month - 1, +day);
}
},
iso8601date4: {
regex: RegExp('^' + reYear4withSign + '-' + reMonthlz + '-' + reDaylz),
name: 'iso8601date4',
callback: function callback(match, year, month, day) {
return this.ymd(+year, month - 1, +day);
}
},
gnuNoColon: {
regex: RegExp('^t?' + reHour24lz + reMinutelz, 'i'),
name: 'gnunocolon',
callback: function callback(match, hour, minute) {
// this rule is a special case
// if time was already set once by any preceding rule, it sets the captured value as year
switch (this.times) {
case 0:
return this.time(+hour, +minute, 0, this.f);
case 1:
this.y = hour * 100 + +minute;
this.times++;
return true;
default:
return false;
}
}
},
gnuDateShorter: {
regex: RegExp('^' + reYear4 + '-' + reMonth),
name: 'gnudateshorter',
callback: function callback(match, year, month) {
return this.ymd(+year, month - 1, 1);
}
},
pgTextReverse: {
// note: allowed years are from 32-9999
// years below 32 should be treated as days in datefull
regex: RegExp('^' + '(\\d{3,4}|[4-9]\\d|3[2-9])-(' + reMonthAbbr + ')-' + reDaylz, 'i'),
name: 'pgtextreverse',
callback: function callback(match, year, month, day) {
return this.ymd(processYear(year), lookupMonth(month), +day);
}
},
dateFull: {
regex: RegExp('^' + reDay + '[ \\t.-]*' + reMonthText + '[ \\t.-]*' + reYear, 'i'),
name: 'datefull',
callback: function callback(match, day, month, year) {
return this.ymd(processYear(year), lookupMonth(month), +day);
}
},
dateNoDay: {
regex: RegExp('^' + reMonthText + '[ .\\t-]*' + reYear4, 'i'),
name: 'datenoday',
callback: function callback(match, month, year) {
return this.ymd(+year, lookupMonth(month), 1);
}
},
dateNoDayRev: {
regex: RegExp('^' + reYear4 + '[ .\\t-]*' + reMonthText, 'i'),
name: 'datenodayrev',
callback: function callback(match, year, month) {
return this.ymd(+year, lookupMonth(month), 1);
}
},
pgTextShort: {
regex: RegExp('^(' + reMonthAbbr + ')-' + reDaylz + '-' + reYear, 'i'),
name: 'pgtextshort',
callback: function callback(match, month, day, year) {
return this.ymd(processYear(year), lookupMonth(month), +day);
}
},
dateNoYear: {
regex: RegExp('^' + reDateNoYear, 'i'),
name: 'datenoyear',
callback: function callback(match, month, day) {
return this.ymd(this.y, lookupMonth(month), +day);
}
},
dateNoYearRev: {
regex: RegExp('^' + reDay + '[ .\\t-]*' + reMonthText, 'i'),
name: 'datenoyearrev',
callback: function callback(match, day, month) {
return this.ymd(this.y, lookupMonth(month), +day);
}
},
isoWeekDay: {
regex: RegExp('^' + reYear4 + '-?W' + reWeekOfYear + '(?:-?([0-7]))?'),
name: 'isoweekday | isoweek',
callback: function callback(match, year, week, day) {
day = day ? +day : 1;
if (!this.ymd(+year, 0, 1)) {
return false;
}
// get day of week for Jan 1st
var dayOfWeek = new Date(this.y, this.m, this.d).getDay();
// and use the day to figure out the offset for day 1 of week 1
dayOfWeek = 0 - (dayOfWeek > 4 ? dayOfWeek - 7 : dayOfWeek);
this.rd += dayOfWeek + (week - 1) * 7 + day;
}
},
relativeText: {
regex: RegExp('^(' + reReltextnumber + '|' + reReltexttext + ')' + reSpace + '(' + reReltextunit + ')', 'i'),
name: 'relativetext',
callback: function callback(match, relValue, relUnit) {
// todo: implement handling of 'this time-unit'
// eslint-disable-next-line no-unused-vars
var _lookupRelative = lookupRelative(relValue),
amount = _lookupRelative.amount,
behavior = _lookupRelative.behavior;
switch (relUnit.toLowerCase()) {
case 'sec':
case 'secs':
case 'second':
case 'seconds':
this.rs += amount;
break;
case 'min':
case 'mins':
case 'minute':
case 'minutes':
this.ri += amount;
break;
case 'hour':
case 'hours':
this.rh += amount;
break;
case 'day':
case 'days':
this.rd += amount;
break;
case 'fortnight':
case 'fortnights':
case 'forthnight':
case 'forthnights':
this.rd += amount * 14;
break;
case 'week':
case 'weeks':
this.rd += amount * 7;
break;
case 'month':
case 'months':
this.rm += amount;
break;
case 'year':
case 'years':
this.ry += amount;
break;
case 'mon':case 'monday':
case 'tue':case 'tuesday':
case 'wed':case 'wednesday':
case 'thu':case 'thursday':
case 'fri':case 'friday':
case 'sat':case 'saturday':
case 'sun':case 'sunday':
this.resetTime();
this.weekday = lookupWeekday(relUnit, 7);
this.weekdayBehavior = 1;
this.rd += (amount > 0 ? amount - 1 : amount) * 7;
break;
case 'weekday':
case 'weekdays':
// todo
break;
}
}
},
relative: {
regex: RegExp('^([+-]*)[ \\t]*(\\d+)' + reSpaceOpt + '(' + reReltextunit + '|week)', 'i'),
name: 'relative',
callback: function callback(match, signs, relValue, relUnit) {
var minuses = signs.replace(/[^-]/g, '').length;
var amount = +relValue * Math.pow(-1, minuses);
switch (relUnit.toLowerCase()) {
case 'sec':
case 'secs':
case 'second':
case 'seconds':
this.rs += amount;
break;
case 'min':
case 'mins':
case 'minute':
case 'minutes':
this.ri += amount;
break;
case 'hour':
case 'hours':
this.rh += amount;
break;
case 'day':
case 'days':
this.rd += amount;
break;
case 'fortnight':
case 'fortnights':
case 'forthnight':
case 'forthnights':
this.rd += amount * 14;
break;
case 'week':
case 'weeks':
this.rd += amount * 7;
break;
case 'month':
case 'months':
this.rm += amount;
break;
case 'year':
case 'years':
this.ry += amount;
break;
case 'mon':case 'monday':
case 'tue':case 'tuesday':
case 'wed':case 'wednesday':
case 'thu':case 'thursday':
case 'fri':case 'friday':
case 'sat':case 'saturday':
case 'sun':case 'sunday':
this.resetTime();
this.weekday = lookupWeekday(relUnit, 7);
this.weekdayBehavior = 1;
this.rd += (amount > 0 ? amount - 1 : amount) * 7;
break;
case 'weekday':
case 'weekdays':
// todo
break;
}
}
},
dayText: {
regex: RegExp('^(' + reDaytext + ')', 'i'),
name: 'daytext',
callback: function callback(match, dayText) {
this.resetTime();
this.weekday = lookupWeekday(dayText, 0);
if (this.weekdayBehavior !== 2) {
this.weekdayBehavior = 1;
}
}
},
relativeTextWeek: {
regex: RegExp('^(' + reReltexttext + ')' + reSpace + 'week', 'i'),
name: 'relativetextweek',
callback: function callback(match, relText) {
this.weekdayBehavior = 2;
switch (relText.toLowerCase()) {
case 'this':
this.rd += 0;
break;
case 'next':
this.rd += 7;
break;
case 'last':
case 'previous':
this.rd -= 7;
break;
}
if (isNaN(this.weekday)) {
this.weekday = 1;
}
}
},
monthFullOrMonthAbbr: {
regex: RegExp('^(' + reMonthFull + '|' + reMonthAbbr + ')', 'i'),
name: 'monthfull | monthabbr',
callback: function callback(match, month) {
return this.ymd(this.y, lookupMonth(month), this.d);
}
},
tzCorrection: {
regex: RegExp('^' + reTzCorrection, 'i'),
name: 'tzcorrection',
callback: function callback(tzCorrection) {
return this.zone(processTzCorrection(tzCorrection));
}
},
ago: {
regex: /^ago/i,
name: 'ago',
callback: function callback() {
this.ry = -this.ry;
this.rm = -this.rm;
this.rd = -this.rd;
this.rh = -this.rh;
this.ri = -this.ri;
this.rs = -this.rs;
this.rf = -this.rf;
}
},
year4: {
regex: RegExp('^' + reYear4),
name: 'year4',
callback: function callback(match, year) {
this.y = +year;
return true;
}
},
whitespace: {
regex: /^[ .,\t]+/,
name: 'whitespace'
// do nothing
},
dateShortWithTimeLong: {
regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute + '[:.]' + reSecond, 'i'),
name: 'dateshortwithtimelong',
callback: function callback(match, month, day, hour, minute, second) {
return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, +second, 0);
}
},
dateShortWithTimeLong12: {
regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinute + '[:.]' + reSecondlz + reSpaceOpt + reMeridian, 'i'),
name: 'dateshortwithtimelong12',
callback: function callback(match, month, day, hour, minute, second, meridian) {
return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, +second, 0);
}
},
dateShortWithTimeShort: {
regex: RegExp('^' + reDateNoYear + 't?' + reHour24 + '[:.]' + reMinute, 'i'),
name: 'dateshortwithtimeshort',
callback: function callback(match, month, day, hour, minute) {
return this.ymd(this.y, lookupMonth(month), +day) && this.time(+hour, +minute, 0, 0);
}
},
dateShortWithTimeShort12: {
regex: RegExp('^' + reDateNoYear + reHour12 + '[:.]' + reMinutelz + reSpaceOpt + reMeridian, 'i'),
name: 'dateshortwithtimeshort12',
callback: function callback(match, month, day, hour, minute, meridian) {
return this.ymd(this.y, lookupMonth(month), +day) && this.time(processMeridian(+hour, meridian), +minute, 0, 0);
}
}
};
var resultProto = {
// date
y: NaN,
m: NaN,
d: NaN,
// time
h: NaN,
i: NaN,
s: NaN,
f: NaN,
// relative shifts
ry: 0,
rm: 0,
rd: 0,
rh: 0,
ri: 0,
rs: 0,
rf: 0,
// weekday related shifts
weekday: NaN,
weekdayBehavior: 0,
// first or last day of month
// 0 none, 1 first, -1 last
firstOrLastDayOfMonth: 0,
// timezone correction in minutes
z: NaN,
// counters
dates: 0,
times: 0,
zones: 0,
// helper functions
ymd: function ymd(y, m, d) {
if (this.dates > 0) {
return false;
}
this.dates++;
this.y = y;
this.m = m;
this.d = d;
return true;
},
time: function time(h, i, s, f) {
if (this.times > 0) {
return false;
}
this.times++;
this.h = h;
this.i = i;
this.s = s;
this.f = f;
return true;
},
resetTime: function resetTime() {
this.h = 0;
this.i = 0;
this.s = 0;
this.f = 0;
this.times = 0;
return true;
},
zone: function zone(minutes) {
if (this.zones <= 1) {
this.zones++;
this.z = minutes;
return true;
}
return false;
},
toDate: function toDate(relativeTo) {
if (this.dates && !this.times) {
this.h = this.i = this.s = this.f = 0;
}
// fill holes
if (isNaN(this.y)) {
this.y = relativeTo.getFullYear();
}
if (isNaN(this.m)) {
this.m = relativeTo.getMonth();
}
if (isNaN(this.d)) {
this.d = relativeTo.getDate();
}
if (isNaN(this.h)) {
this.h = relativeTo.getHours();
}
if (isNaN(this.i)) {
this.i = relativeTo.getMinutes();
}
if (isNaN(this.s)) {
this.s = relativeTo.getSeconds();
}
if (isNaN(this.f)) {
this.f = relativeTo.getMilliseconds();
}
// adjust special early
switch (this.firstOrLastDayOfMonth) {
case 1:
this.d = 1;
break;
case -1:
this.d = 0;
this.m += 1;
break;
}
if (!isNaN(this.weekday)) {
var date = new Date(relativeTo.getTime());
date.setFullYear(this.y, this.m, this.d);
date.setHours(this.h, this.i, this.s, this.f);
var dow = date.getDay();
if (this.weekdayBehavior === 2) {
// To make "this week" work, where the current day of week is a "sunday"
if (dow === 0 && this.weekday !== 0) {
this.weekday = -6;
}
// To make "sunday this week" work, where the current day of week is not a "sunday"
if (this.weekday === 0 && dow !== 0) {
this.weekday = 7;
}
this.d -= dow;
this.d += this.weekday;
} else {
var diff = this.weekday - dow;
// some PHP magic
if (this.rd < 0 && diff < 0 || this.rd >= 0 && diff <= -this.weekdayBehavior) {
diff += 7;
}
if (this.weekday >= 0) {
this.d += diff;
} else {
this.d -= 7 - (Math.abs(this.weekday) - dow);
}
this.weekday = NaN;
}
}
// adjust relative
this.y += this.ry;
this.m += this.rm;
this.d += this.rd;
this.h += this.rh;
this.i += this.ri;
this.s += this.rs;
this.f += this.rf;
this.ry = this.rm = this.rd = 0;
this.rh = this.ri = this.rs = this.rf = 0;
var result = new Date(relativeTo.getTime());
// since Date constructor treats years <= 99 as 1900+
// it can't be used, thus this weird way
result.setFullYear(this.y, this.m, this.d);
result.setHours(this.h, this.i, this.s, this.f);
// note: this is done twice in PHP
// early when processing special relatives
// and late
// todo: check if the logic can be reduced
// to just one time action
switch (this.firstOrLastDayOfMonth) {
case 1:
result.setDate(1);
break;
case -1:
result.setMonth(result.getMonth() + 1, 0);
break;
}
// adjust timezone
if (!isNaN(this.z) && result.getTimezoneOffset() !== this.z) {
result.setUTCFullYear(result.getFullYear(), result.getMonth(), result.getDate());
result.setUTCHours(result.getHours(), result.getMinutes() + this.z, result.getSeconds(), result.getMilliseconds());
}
return result;
}
};
module.exports = function strtotime(str, now) {
// discuss at: https://locutus.io/php/strtotime/
// original by: Caio Ariede (https://caioariede.com)
// improved by: Kevin van Zonneveld (https://kvz.io)
// improved by: Caio Ariede (https://caioariede.com)
// improved by: A. Matías Quezada (https://amatiasq.com)
// improved by: preuter
// improved by: Brett Zamir (https://brett-zamir.me)
// improved by: Mirko Faber
// input by: David
// bugfixed by: Wagner B. Soares
// bugfixed by: Artur Tchernychev
// bugfixed by: Stephan Bösch-Plepelits (https://github.com/plepe)
// reimplemented by: Rafał Kukawski
// note 1: Examples all have a fixed timestamp to prevent
// note 1: tests to fail because of variable time(zones)
// example 1: strtotime('+1 day', 1129633200)
// returns 1: 1129719600
// example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200)
// returns 2: 1130425202
// example 3: strtotime('last month', 1129633200)
// returns 3: 1127041200
// example 4: strtotime('2009-05-04 08:30:00+00')
// returns 4: 1241425800
// example 5: strtotime('2009-05-04 08:30:00+02:00')
// returns 5: 1241418600
if (now == null) {
now = Math.floor(Date.now() / 1000);
}
// the rule order is important
// if multiple rules match, the longest match wins
// if multiple rules match the same string, the first match wins
var rules = [formats.yesterday, formats.now, formats.noon, formats.midnightOrToday, formats.tomorrow, formats.timestamp, formats.firstOrLastDay, formats.backOrFrontOf,
// formats.weekdayOf, // not yet implemented
formats.timeTiny12, formats.timeShort12, formats.timeLong12, formats.mssqltime, formats.timeShort24, formats.timeLong24, formats.iso8601long, formats.gnuNoColon, formats.iso8601noColon, formats.americanShort, formats.american, formats.iso8601date4, formats.iso8601dateSlash, formats.dateSlash, formats.gnuDateShortOrIso8601date2, formats.gnuDateShorter, formats.dateFull, formats.pointedDate4, formats.pointedDate2, formats.dateNoDay, formats.dateNoDayRev, formats.dateTextual, formats.dateNoYear, formats.dateNoYearRev, formats.dateNoColon, formats.xmlRpc, formats.xmlRpcNoColon, formats.soap, formats.wddx, formats.exif, formats.pgydotd, formats.isoWeekDay, formats.pgTextShort, formats.pgTextReverse, formats.clf, formats.year4, formats.ago, formats.dayText, formats.relativeTextWeek, formats.relativeText, formats.monthFullOrMonthAbbr, formats.tzCorrection, formats.dateShortWithTimeShort12, formats.dateShortWithTimeLong12, formats.dateShortWithTimeShort, formats.dateShortWithTimeLong, formats.relative, formats.whitespace];
var result = Object.create(resultProto);
while (str.length) {
var longestMatch = null;
var finalRule = null;
for (var i = 0, l = rules.length; i < l; i++) {
var format = rules[i];
var match = str.match(format.regex);
if (match) {
if (!longestMatch || match[0].length > longestMatch[0].length) {
longestMatch = match;
finalRule = format;
}
}
}
if (!finalRule || finalRule.callback && finalRule.callback.apply(result, longestMatch) === false) {
return false;
}
str = str.substr(longestMatch[0].length);
finalRule = null;
longestMatch = null;
}
return Math.floor(result.toDate(new Date(now * 1000)) / 1000);
};
//# sourceMappingURL=strtotime.js.map