3 // github.com/samsonjs/strftime
6 // Copyright 2010 - 2016 Sami Samhuri <sami@samhuri.net>
9 // http://sjs.mit-license.org
16 days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
17 shortDays: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
18 months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
19 shortMonths: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
25 c: '%a %d %b %Y %X %Z',
38 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
39 shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
40 months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
41 shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
43 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
44 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
45 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
53 c: '%a %d %b %Y %X %Z',
66 days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
67 shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
68 months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
69 shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
71 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
72 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
73 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th',
81 c: '%a %d %b %Y %X %Z',
94 days: ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'],
95 shortDays: ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb'],
96 months: ['enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio', 'julio', 'agosto', 'septiembre', 'octubre', 'noviembre',' diciembre'],
97 shortMonths: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'],
103 c: '%a %d %b %Y %X %Z',
116 days: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
117 shortDays: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
118 months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
119 shortMonths: ['janv.', 'févr.', 'mars', 'avril', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],
125 c: '%a %d %b %Y %X %Z',
138 days: ['domenica', 'lunedì', 'martedì', 'mercoledì', 'giovedì', 'venerdì', 'sabato'],
139 shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
140 months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
141 shortMonths: ['pr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'],
147 c: '%a %d %b %Y %X %Z',
160 days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'],
161 shortDays: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
162 months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
163 shortMonths: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
169 c: '%a %d %b %Y %X %Z',
182 days: ['domingo', 'segunda', 'terça', 'quarta', 'quinta', 'sexta', 'sábado'],
183 shortDays: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
184 months: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
185 shortMonths: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
191 c: '%a %d %b %Y %X %Z',
204 days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
205 shortDays: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
206 months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
207 shortMonths: ['янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'],
226 days: ['Pazar', 'Pazartesi', 'Salı','Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'],
227 shortDays: ['Paz', 'Pzt', 'Sal', 'Çrş', 'Prş', 'Cum', 'Cts'],
228 months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'],
229 shortMonths: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'],
235 c: '%a %d %b %Y %X %Z',
247 // By michaeljayt<michaeljayt@gmail.com>
248 // https://github.com/michaeljayt/strftime/commit/bcb4c12743811d51e568175aa7bff3fd2a77cef3
250 days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
251 shortDays: ['日', '一', '二', '三', '四', '五', '六'],
252 months: ['一月份', '二月份', '三月份', '四月份', '五月份', '六月份', '七月份', '八月份', '九月份', '十月份', '十一月份', '十二月份'],
253 shortMonths: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
259 c: '%a %d %b %Y %X %Z',
272 var DefaultLocale = Locales['en_US'],
273 defaultStrftime = new Strftime(DefaultLocale, 0, false),
274 isCommonJS = typeof module !== 'undefined',
277 // CommonJS / Node module
279 namespace = module.exports = defaultStrftime;
281 // Browsers and other environments
283 // Get the global object. Works in ES3, ES5, and ES5 strict mode.
284 namespace = (function() { return this || (1,eval)('this'); }());
285 namespace.strftime = defaultStrftime;
288 // Polyfill Date.now for old browsers.
289 if (typeof Date.now !== 'function') {
290 Date.now = function() {
295 function Strftime(locale, customTimezoneOffset, useUtcTimezone) {
296 var _locale = locale || DefaultLocale,
297 _customTimezoneOffset = customTimezoneOffset || 0,
298 _useUtcBasedDate = useUtcTimezone || false,
300 // we store unix timestamp value here to not create new Date() each iteration (each millisecond)
301 // Date.now() is 2 times faster than new Date()
302 // while millisecond precise is enough here
303 // this could be very helpful when strftime triggered a lot of times one by one
304 _cachedDateTimestamp = 0,
307 function _strftime(format, date) {
311 var currentTimestamp = Date.now();
312 if (currentTimestamp > _cachedDateTimestamp) {
313 _cachedDateTimestamp = currentTimestamp;
314 _cachedDate = new Date(_cachedDateTimestamp);
316 timestamp = _cachedDateTimestamp;
318 if (_useUtcBasedDate) {
319 // how to avoid duplication of date instantiation for utc here?
320 // we tied to getTimezoneOffset of the current date
321 _cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset);
325 timestamp = _cachedDateTimestamp;
330 timestamp = date.getTime();
332 if (_useUtcBasedDate) {
333 var utcOffset = getTimestampToUtcOffsetFor(date);
334 date = new Date(timestamp + utcOffset + _customTimezoneOffset);
335 // If we've crossed a DST boundary with this calculation we need to
336 // adjust the new date accordingly or it will be off by an hour in UTC.
337 if (getTimestampToUtcOffsetFor(date) !== utcOffset) {
338 var newUTCOffset = getTimestampToUtcOffsetFor(date);
339 date = new Date(timestamp + newUTCOffset + _customTimezoneOffset);
344 return _processFormat(format, date, _locale, timestamp);
347 function _processFormat(format, date, locale, timestamp) {
348 var resultString = '',
351 length = format.length,
354 for (var i = 0; i < length; i++) {
356 var currentCharCode = format.charCodeAt(i);
358 if (isInScope === true) {
360 if (currentCharCode === 45) {
365 else if (currentCharCode === 95) {
370 else if (currentCharCode === 48) {
375 else if (currentCharCode === 58) {
377 warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime");
383 switch (currentCharCode) {
385 // Examples for new Date(0) in GMT
396 resultString += locale.days[date.getDay()];
402 resultString += locale.months[date.getMonth()];
408 resultString += padTill2(Math.floor(date.getFullYear() / 100), padding);
414 resultString += _processFormat(locale.formats.D, date, locale, timestamp);
420 resultString += _processFormat(locale.formats.F, date, locale, timestamp);
426 resultString += padTill2(date.getHours(), padding);
432 resultString += padTill2(hours12(date.getHours()), padding);
438 resultString += padTill3(Math.floor(timestamp % 1000));
444 resultString += padTill2(date.getMinutes(), padding);
450 resultString += date.getHours() < 12 ? locale.am : locale.pm;
456 resultString += _processFormat(locale.formats.R, date, locale, timestamp);
462 resultString += padTill2(date.getSeconds(), padding);
468 resultString += _processFormat(locale.formats.T, date, locale, timestamp);
474 resultString += padTill2(weekNumber(date, 'sunday'), padding);
480 resultString += padTill2(weekNumber(date, 'monday'), padding);
486 resultString += _processFormat(locale.formats.X, date, locale, timestamp);
492 resultString += date.getFullYear();
498 if (_useUtcBasedDate && _customTimezoneOffset === 0) {
499 resultString += "GMT";
503 var tzString = date.toString().match(/\(([\w\s]+)\)/);
504 resultString += tzString && tzString[1] || '';
511 resultString += locale.shortDays[date.getDay()];
517 resultString += locale.shortMonths[date.getMonth()];
523 resultString += _processFormat(locale.formats.c, date, locale, timestamp);
529 resultString += padTill2(date.getDate(), padding);
535 resultString += padTill2(date.getDate(), padding == null ? ' ' : padding);
541 resultString += locale.shortMonths[date.getMonth()];
547 var y = new Date(date.getFullYear(), 0, 1);
548 var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24));
549 resultString += padTill3(day);
555 resultString += padTill2(date.getHours(), padding == null ? ' ' : padding);
561 resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding);
567 resultString += padTill2(date.getMonth() + 1, padding);
573 resultString += '\n';
579 // Try to use an ordinal suffix from the locale, but fall back to using the old
580 // function for compatibility with old locales that lack them.
581 var day = date.getDate();
582 if (locale.ordinalSuffixes) {
583 resultString += String(day) + (locale.ordinalSuffixes[day - 1] || ordinal(day));
586 resultString += String(day) + ordinal(day);
593 resultString += date.getHours() < 12 ? locale.AM : locale.PM;
599 resultString += _processFormat(locale.formats.r, date, locale, timestamp);
605 resultString += Math.floor(timestamp / 1000);
611 resultString += '\t';
617 var day = date.getDay();
618 resultString += day === 0 ? 7 : day;
619 break; // 1 - 7, Monday is first day of the week
624 resultString += _processFormat(locale.formats.v, date, locale, timestamp);
630 resultString += date.getDay();
631 break; // 0 - 6, Sunday is first day of the week
636 resultString += _processFormat(locale.formats.x, date, locale, timestamp);
642 resultString += ('' + date.getFullYear()).slice(2);
648 if (_useUtcBasedDate && _customTimezoneOffset === 0) {
649 resultString += extendedTZ ? "+00:00" : "+0000";
653 if (_customTimezoneOffset !== 0) {
654 off = _customTimezoneOffset / (60 * 1000);
657 off = -date.getTimezoneOffset();
659 var sign = off < 0 ? '-' : '+';
660 var sep = extendedTZ ? ':' : '';
661 var hours = Math.floor(Math.abs(off / 60));
662 var mins = Math.abs(off % 60);
663 resultString += sign + padTill2(hours) + sep + padTill2(mins);
671 resultString += format[i];
681 if (currentCharCode === 37) {
686 resultString += format[i];
692 var strftime = _strftime;
694 strftime.localize = function(locale) {
695 return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate);
698 strftime.localizeByIdentifier = function(localeIdentifier) {
699 var locale = Locales[localeIdentifier];
701 warn('[WARNING] No locale found with identifier "' + localeIdentifier + '".');
704 return strftime.localize(locale);
707 strftime.timezone = function(timezone) {
708 var customTimezoneOffset = _customTimezoneOffset;
709 var useUtcBasedDate = _useUtcBasedDate;
711 var timezoneType = typeof timezone;
712 if (timezoneType === 'number' || timezoneType === 'string') {
713 useUtcBasedDate = true;
715 // ISO 8601 format timezone string, [-+]HHMM
716 if (timezoneType === 'string') {
717 var sign = timezone[0] === '-' ? -1 : 1,
718 hours = parseInt(timezone.slice(1, 3), 10),
719 minutes = parseInt(timezone.slice(3, 5), 10);
721 customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000;
724 else if (timezoneType === 'number') {
725 customTimezoneOffset = timezone * 60 * 1000;
729 return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate);
732 strftime.utc = function() {
733 return new Strftime(_locale, _customTimezoneOffset, true);
739 function padTill2(numberToPad, paddingChar) {
740 if (paddingChar === '' || numberToPad > 9) {
743 if (paddingChar == null) {
746 return paddingChar + numberToPad;
749 function padTill3(numberToPad) {
750 if (numberToPad > 99) {
753 if (numberToPad > 9) {
754 return '0' + numberToPad;
756 return '00' + numberToPad;
759 function hours12(hour) {
763 else if (hour > 12) {
769 // firstWeekday: 'sunday' or 'monday', default is 'sunday'
771 // Pilfered & ported from Ruby's strftime implementation.
772 function weekNumber(date, firstWeekday) {
773 firstWeekday = firstWeekday || 'sunday';
775 // This works by shifting the weekday back by one day if we
776 // are treating Monday as the first day of the week.
777 var weekday = date.getDay();
778 if (firstWeekday === 'monday') {
779 if (weekday === 0) // Sunday
785 var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1),
786 dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
787 yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000),
788 weekNum = (yday + 7 - weekday) / 7;
790 return Math.floor(weekNum);
793 // Get the ordinal suffix for a number: st, nd, rd, or th
794 function ordinal(number) {
796 var ii = number % 100;
798 if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
808 function getTimestampToUtcOffsetFor(date) {
809 return (date.getTimezoneOffset() || 0) * 60000;
812 function warn(message) {
813 if (typeof console !== 'undefined' && typeof console.warn == 'function') {
814 console.warn(message)