// // strftime // github.com/samsonjs/strftime // @_sjs // // Copyright 2010 - 2016 Sami Samhuri // // MIT License // http://sjs.mit-license.org // ;(function() { var Locales = { de_DE: { days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d.%m.%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, en_CA: { days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], ordinalSuffixes: [ 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'st' ], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d/%m/%y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%r', x: '%D' } }, en_US: { days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ], shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], ordinalSuffixes: [ 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'st' ], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%m/%d/%y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%r', x: '%D' } }, es_MX: { days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'], shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'], months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre',' diciembre'], shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d/%m/%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, fr_FR: { days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'], shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], shortMonths: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d/%m/%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, it_IT: { days: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'], shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'], months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'], shortMonths: ['pr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d/%m/%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, nl_NL: { days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], shortDays: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], shortMonths: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d-%m-%y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, pt_BR: { days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'], shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'], months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'], shortMonths: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X %Z', D: '%d-%m-%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, ru_RU: { days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], shortDays: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { c: '%a %d %b %Y %X', D: '%d.%m.%y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, tr_TR: { days: ['Pazar', 'Pazartesi', 'Salı','Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'], shortDays: ['Paz', 'Pzt', 'Sal', 'Çrş', 'Prş', 'Cum', 'Cts'], months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'], shortMonths: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'], AM: 'ÖÖ', PM: 'ÖS', am: 'ÖÖ', pm: 'ÖS', formats: { c: '%a %d %b %Y %X %Z', D: '%d-%m-%Y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%T', x: '%D' } }, // By michaeljayt // https://github.com/michaeljayt/strftime/commit/bcb4c12743811d51e568175aa7bff3fd2a77cef3 zh_CN: { days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], shortDays: ['日', '一', '二', '三', '四', '五', '六'], months: ['一月份', '二月份', '三月份', '四月份', '五月份', '六月份', '七月份', '八月份', '九月份', '十月份', '十一月份', '十二月份'], shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], AM: '上午', PM: '下午', am: '上午', pm: '下午', formats: { c: '%a %d %b %Y %X %Z', D: '%d/%m/%y', F: '%Y-%m-%d', R: '%H:%M', r: '%I:%M:%S %p', T: '%H:%M:%S', v: '%e-%b-%Y', X: '%r', x: '%D' } } }; var DefaultLocale = Locales['en_US'], defaultStrftime = new Strftime(DefaultLocale, 0, false), isCommonJS = typeof module !== 'undefined', namespace; // CommonJS / Node module if (isCommonJS) { namespace = module.exports = defaultStrftime; } // Browsers and other environments else { // Get the global object. Works in ES3, ES5, and ES5 strict mode. namespace = (function() { return this || (1,eval)('this'); }()); namespace.strftime = defaultStrftime; } // Polyfill Date.now for old browsers. if (typeof Date.now !== 'function') { Date.now = function() { return +new Date(); }; } function Strftime(locale, customTimezoneOffset, useUtcTimezone) { var _locale = locale || DefaultLocale, _customTimezoneOffset = customTimezoneOffset || 0, _useUtcBasedDate = useUtcTimezone || false, // we store unix timestamp value here to not create new Date() each iteration (each millisecond) // Date.now() is 2 times faster than new Date() // while millisecond precise is enough here // this could be very helpful when strftime triggered a lot of times one by one _cachedDateTimestamp = 0, _cachedDate; function _strftime(format, date) { var timestamp; if (!date) { var currentTimestamp = Date.now(); if (currentTimestamp > _cachedDateTimestamp) { _cachedDateTimestamp = currentTimestamp; _cachedDate = new Date(_cachedDateTimestamp); timestamp = _cachedDateTimestamp; if (_useUtcBasedDate) { // how to avoid duplication of date instantiation for utc here? // we tied to getTimezoneOffset of the current date _cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset); } } else { timestamp = _cachedDateTimestamp; } date = _cachedDate; } else { timestamp = date.getTime(); if (_useUtcBasedDate) { var utcOffset = getTimestampToUtcOffsetFor(date); date = new Date(timestamp + utcOffset + _customTimezoneOffset); // If we've crossed a DST boundary with this calculation we need to // adjust the new date accordingly or it will be off by an hour in UTC. if (getTimestampToUtcOffsetFor(date) !== utcOffset) { var newUTCOffset = getTimestampToUtcOffsetFor(date); date = new Date(timestamp + newUTCOffset + _customTimezoneOffset); } } } return _processFormat(format, date, _locale, timestamp); } function _processFormat(format, date, locale, timestamp) { var resultString = '', padding = null, isInScope = false, length = format.length, extendedTZ = false; for (var i = 0; i < length; i++) { var currentCharCode = format.charCodeAt(i); if (isInScope === true) { // '-' if (currentCharCode === 45) { padding = ''; continue; } // '_' else if (currentCharCode === 95) { padding = ' '; continue; } // '0' else if (currentCharCode === 48) { padding = '0'; continue; } // ':' else if (currentCharCode === 58) { if (extendedTZ) { warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime"); } extendedTZ = true; continue; } switch (currentCharCode) { // Examples for new Date(0) in GMT // '%' // case '%': case 37: resultString += '%'; break; // 'Thursday' // case 'A': case 65: resultString += locale.days[date.getDay()]; break; // 'January' // case 'B': case 66: resultString += locale.months[date.getMonth()]; break; // '19' // case 'C': case 67: resultString += padTill2(Math.floor(date.getFullYear() / 100), padding); break; // '01/01/70' // case 'D': case 68: resultString += _processFormat(locale.formats.D, date, locale, timestamp); break; // '1970-01-01' // case 'F': case 70: resultString += _processFormat(locale.formats.F, date, locale, timestamp); break; // '00' // case 'H': case 72: resultString += padTill2(date.getHours(), padding); break; // '12' // case 'I': case 73: resultString += padTill2(hours12(date.getHours()), padding); break; // '000' // case 'L': case 76: resultString += padTill3(Math.floor(timestamp % 1000)); break; // '00' // case 'M': case 77: resultString += padTill2(date.getMinutes(), padding); break; // 'am' // case 'P': case 80: resultString += date.getHours() < 12 ? locale.am : locale.pm; break; // '00:00' // case 'R': case 82: resultString += _processFormat(locale.formats.R, date, locale, timestamp); break; // '00' // case 'S': case 83: resultString += padTill2(date.getSeconds(), padding); break; // '00:00:00' // case 'T': case 84: resultString += _processFormat(locale.formats.T, date, locale, timestamp); break; // '00' // case 'U': case 85: resultString += padTill2(weekNumber(date, 'sunday'), padding); break; // '00' // case 'W': case 87: resultString += padTill2(weekNumber(date, 'monday'), padding); break; // '16:00:00' // case 'X': case 88: resultString += _processFormat(locale.formats.X, date, locale, timestamp); break; // '1970' // case 'Y': case 89: resultString += date.getFullYear(); break; // 'GMT' // case 'Z': case 90: if (_useUtcBasedDate && _customTimezoneOffset === 0) { resultString += "GMT"; } else { // fixme optimize var tzString = date.toString().match(/\(([\w\s]+)\)/); resultString += tzString && tzString[1] || ''; } break; // 'Thu' // case 'a': case 97: resultString += locale.shortDays[date.getDay()]; break; // 'Jan' // case 'b': case 98: resultString += locale.shortMonths[date.getMonth()]; break; // '' // case 'c': case 99: resultString += _processFormat(locale.formats.c, date, locale, timestamp); break; // '01' // case 'd': case 100: resultString += padTill2(date.getDate(), padding); break; // ' 1' // case 'e': case 101: resultString += padTill2(date.getDate(), padding == null ? ' ' : padding); break; // 'Jan' // case 'h': case 104: resultString += locale.shortMonths[date.getMonth()]; break; // '000' // case 'j': case 106: var y = new Date(date.getFullYear(), 0, 1); var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); resultString += padTill3(day); break; // ' 0' // case 'k': case 107: resultString += padTill2(date.getHours(), padding == null ? ' ' : padding); break; // '12' // case 'l': case 108: resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding); break; // '01' // case 'm': case 109: resultString += padTill2(date.getMonth() + 1, padding); break; // '\n' // case 'n': case 110: resultString += '\n'; break; // '1st' // case 'o': case 111: // Try to use an ordinal suffix from the locale, but fall back to using the old // function for compatibility with old locales that lack them. var day = date.getDate(); if (locale.ordinalSuffixes) { resultString += String(day) + (locale.ordinalSuffixes[day - 1] || ordinal(day)); } else { resultString += String(day) + ordinal(day); } break; // 'AM' // case 'p': case 112: resultString += date.getHours() < 12 ? locale.AM : locale.PM; break; // '12:00:00 AM' // case 'r': case 114: resultString += _processFormat(locale.formats.r, date, locale, timestamp); break; // '0' // case 's': case 115: resultString += Math.floor(timestamp / 1000); break; // '\t' // case 't': case 116: resultString += '\t'; break; // '4' // case 'u': case 117: var day = date.getDay(); resultString += day === 0 ? 7 : day; break; // 1 - 7, Monday is first day of the week // ' 1-Jan-1970' // case 'v': case 118: resultString += _processFormat(locale.formats.v, date, locale, timestamp); break; // '4' // case 'w': case 119: resultString += date.getDay(); break; // 0 - 6, Sunday is first day of the week // '12/31/69' // case 'x': case 120: resultString += _processFormat(locale.formats.x, date, locale, timestamp); break; // '70' // case 'y': case 121: resultString += ('' + date.getFullYear()).slice(2); break; // '+0000' // case 'z': case 122: if (_useUtcBasedDate && _customTimezoneOffset === 0) { resultString += extendedTZ ? "+00:00" : "+0000"; } else { var off; if (_customTimezoneOffset !== 0) { off = _customTimezoneOffset / (60 * 1000); } else { off = -date.getTimezoneOffset(); } var sign = off < 0 ? '-' : '+'; var sep = extendedTZ ? ':' : ''; var hours = Math.floor(Math.abs(off / 60)); var mins = Math.abs(off % 60); resultString += sign + padTill2(hours) + sep + padTill2(mins); } break; default: if (isInScope) { resultString += '%'; } resultString += format[i]; break; } padding = null; isInScope = false; continue; } // '%' if (currentCharCode === 37) { isInScope = true; continue; } resultString += format[i]; } return resultString; } var strftime = _strftime; strftime.localize = function(locale) { return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate); }; strftime.localizeByIdentifier = function(localeIdentifier) { var locale = Locales[localeIdentifier]; if (!locale) { warn('[WARNING] No locale found with identifier "' + localeIdentifier + '".'); return strftime; } return strftime.localize(locale); }; strftime.timezone = function(timezone) { var customTimezoneOffset = _customTimezoneOffset; var useUtcBasedDate = _useUtcBasedDate; var timezoneType = typeof timezone; if (timezoneType === 'number' || timezoneType === 'string') { useUtcBasedDate = true; // ISO 8601 format timezone string, [-+]HHMM if (timezoneType === 'string') { var sign = timezone[0] === '-' ? -1 : 1, hours = parseInt(timezone.slice(1, 3), 10), minutes = parseInt(timezone.slice(3, 5), 10); customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000; // in minutes: 420 } else if (timezoneType === 'number') { customTimezoneOffset = timezone * 60 * 1000; } } return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate); }; strftime.utc = function() { return new Strftime(_locale, _customTimezoneOffset, true); }; return strftime; } function padTill2(numberToPad, paddingChar) { if (paddingChar === '' || numberToPad > 9) { return numberToPad; } if (paddingChar == null) { paddingChar = '0'; } return paddingChar + numberToPad; } function padTill3(numberToPad) { if (numberToPad > 99) { return numberToPad; } if (numberToPad > 9) { return '0' + numberToPad; } return '00' + numberToPad; } function hours12(hour) { if (hour === 0) { return 12; } else if (hour > 12) { return hour - 12; } return hour; } // firstWeekday: 'sunday' or 'monday', default is 'sunday' // // Pilfered & ported from Ruby's strftime implementation. function weekNumber(date, firstWeekday) { firstWeekday = firstWeekday || 'sunday'; // This works by shifting the weekday back by one day if we // are treating Monday as the first day of the week. var weekday = date.getDay(); if (firstWeekday === 'monday') { if (weekday === 0) // Sunday weekday = 6; else weekday--; } var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1), dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()), yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000), weekNum = (yday + 7 - weekday) / 7; return Math.floor(weekNum); } // Get the ordinal suffix for a number: st, nd, rd, or th function ordinal(number) { var i = number % 10; var ii = number % 100; if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) { return 'th'; } switch (i) { case 1: return 'st'; case 2: return 'nd'; case 3: return 'rd'; } } function getTimestampToUtcOffsetFor(date) { return (date.getTimezoneOffset() || 0) * 60000; } function warn(message) { if (typeof console !== 'undefined' && typeof console.warn == 'function') { console.warn(message) } } }());