// // strftime // github.com/samsonjs/strftime // @_sjs // // Copyright 2010 - 2015 Sami Samhuri // // MIT License // http://sjs.mit-license.org // ;(function() { var DefaultLocale = { 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'], AM: 'AM', PM: 'PM', am: 'am', pm: 'pm', formats: { D: '%m/%d/%y', F: '%Y-%m-%d', R: '%H:%M', T: '%H:%M:%S', X: '%T', c: '%a %b %d %X %Y', r: '%I:%M:%S %p', v: '%e-%b-%Y', x: '%D' } }, defaultStrftime = new Strftime(DefaultLocale, 0, false), isCommonJS = typeof module !== 'undefined', namespace; // CommonJS / Node module if (isCommonJS) { namespace = module.exports = adaptedStrftime; namespace.strftime = deprecatedStrftime; } // 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 = adaptedStrftime; } // Deprecated API, to be removed in v1.0 var _require = isCommonJS ? "require('strftime')" : "strftime"; var _deprecationWarnings = {}; function deprecationWarning(name, instead) { if (!_deprecationWarnings[name]) { if (typeof console !== 'undefined' && typeof console.warn == 'function') { console.warn("[WARNING] " + name + " is deprecated and will be removed in version 1.0. Instead, use `" + instead + "`."); } _deprecationWarnings[name] = true; } } namespace.strftimeTZ = deprecatedStrftimeTZ; namespace.strftimeUTC = deprecatedStrftimeUTC; namespace.localizedStrftime = deprecatedStrftimeLocalized; // Adapt the old API while preserving the new API. function adaptForwards(fn) { fn.localize = defaultStrftime.localize.bind(defaultStrftime); fn.timezone = defaultStrftime.timezone.bind(defaultStrftime); fn.utc = defaultStrftime.utc.bind(defaultStrftime); } adaptForwards(adaptedStrftime); function adaptedStrftime(fmt, d, locale) { // d and locale are optional, check if this is (format, locale) if (d && d.days) { locale = d; d = undefined; } if (locale) { deprecationWarning("`" + _require + "(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])"); } var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime; return strftime(fmt, d); } adaptForwards(deprecatedStrftime); function deprecatedStrftime(fmt, d, locale) { if (locale) { deprecationWarning("`" + _require + ".strftime(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])"); } else { deprecationWarning("`" + _require + ".strftime(format, [date])`", _require + "(format, [date])"); } var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime; return strftime(fmt, d); } function deprecatedStrftimeTZ(fmt, d, locale, timezone) { // locale is optional, check if this is (format, date, timezone) if ((typeof locale == 'number' || typeof locale == 'string') && timezone == null) { timezone = locale; locale = undefined; } if (locale) { deprecationWarning("`" + _require + ".strftimeTZ(format, date, locale, tz)`", "var s = " + _require + ".localize(locale).timezone(tz); s(format, [date])` or `var s = " + _require + ".localize(locale); s.timezone(tz)(format, [date])"); } else { deprecationWarning("`" + _require + ".strftimeTZ(format, date, tz)`", "var s = " + _require + ".timezone(tz); s(format, [date])` or `" + _require + ".timezone(tz)(format, [date])"); } var strftime = (locale ? defaultStrftime.localize(locale) : defaultStrftime).timezone(timezone); return strftime(fmt, d); } var utcStrftime = defaultStrftime.utc(); function deprecatedStrftimeUTC(fmt, d, locale) { if (locale) { deprecationWarning("`" + _require + ".strftimeUTC(format, date, locale)`", "var s = " + _require + ".localize(locale).utc(); s(format, [date])"); } else { deprecationWarning("`" + _require + ".strftimeUTC(format, [date])`", "var s = " + _require + ".utc(); s(format, [date])"); } var strftime = locale ? utcStrftime.localize(locale) : utcStrftime; return strftime(fmt, d); } function deprecatedStrftimeLocalized(locale) { deprecationWarning("`" + _require + ".localizedStrftime(locale)`", _require + ".localize(locale)"); return defaultStrftime.localize(locale); } // End of deprecated API // 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) { date = new Date(date.getTime() + getTimestampToUtcOffsetFor(date) + _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) { if (typeof console !== 'undefined' && typeof console.warn == 'function') { console.warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime"); } } extendedTZ = true; continue; } switch (currentCharCode) { // Examples for new Date(0) in GMT // '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: resultString += String(date.getDate()) + ordinal(date.getDate()); 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: 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.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; } }());