From 033126760dbe90eb1cdc6aa97ccc1904b91a1c02 Mon Sep 17 00:00:00 2001 From: B-Vladi Date: Wed, 7 May 2014 20:43:03 +0400 Subject: [PATCH 01/15] Refactoring --- strftime.js | 629 ++++++++++++++++++++++++++++------------------------ 1 file changed, 342 insertions(+), 287 deletions(-) diff --git a/strftime.js b/strftime.js index 4b50c80..8b902ec 100644 --- a/strftime.js +++ b/strftime.js @@ -9,358 +9,413 @@ // http://sjs.mit-license.org // -;(function() { +; +(function () { + //// Where to export the API + var namespace; - //// Where to export the API - var namespace; - - // CommonJS / Node module - if (typeof module !== 'undefined') { - namespace = module.exports = strftime; - } - - // Browsers and other environments - else { - // Get the global object. Works in ES3, ES5, and ES5 strict mode. - namespace = (function(){ return this || (1,eval)('this') }()); - } - - function words(s) { return (s || '').split(' '); } - - var DefaultLocale = - { days: words('Sunday Monday Tuesday Wednesday Thursday Friday Saturday') - , shortDays: words('Sun Mon Tue Wed Thu Fri Sat') - , months: words('January February March April May June July August September October November December') - , shortMonths: words('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec') - , AM: 'AM' - , PM: 'PM' - , am: 'am' - , pm: 'pm' - }; - - namespace.strftime = strftime; - function strftime(fmt, d, locale) { - return _strftime(fmt, d, locale); - } - - // locale is optional - namespace.strftimeTZ = strftime.strftimeTZ = strftimeTZ; - function strftimeTZ(fmt, d, locale, timezone) { - if ((typeof locale == 'number' || typeof locale == 'string') && timezone == null) { - timezone = locale; - locale = undefined; + try { + // CommonJS / Node module + namespace = module.exports = strftime; + } catch (error) { + // Browsers and other environments + // Get the global object. Works in ES3, ES5, and ES5 strict mode. + namespace = new Function('return this')(); } - return _strftime(fmt, d, locale, { timezone: timezone }); - } - namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC; - function strftimeUTC(fmt, d, locale) { - return _strftime(fmt, d, locale, { utc: true }); - } - - namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime; - function localizedStrftime(locale) { - return function(fmt, d, options) { - return strftime(fmt, d, locale, options); + var DefaultLocale = { + days: 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), + shortDays: 'Sun Mon Tue Wed Thu Fri Sat'.split(' '), + months: 'January February March April May June July August September October November December'.split(' '), + shortMonths: 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), + AM: 'AM', + PM: 'PM', + am: 'am', + pm: 'pm' }; - } - // d, locale, and options are optional, but you can't leave - // holes in the argument list. If you pass options you have to pass - // in all the preceding args as well. - // - // options: - // - locale [object] an object with the same structure as DefaultLocale - // - timezone [number] timezone offset in minutes from GMT - function _strftime(fmt, d, locale, options) { - options = options || {}; + var RequiredDateMethods = [ + 'getTime', + 'getTimezoneOffset', + 'getDay', + 'getDate', + 'getMonth', + 'getFullYear', + 'getYear', + 'getHours', + 'getMinutes', + 'getSeconds' + ]; - // d and locale are optional so check if d is really the locale - if (d && !quacksLikeDate(d)) { - locale = d; - d = undefined; - } - d = d || new Date(); - - locale = locale || DefaultLocale; - locale.formats = locale.formats || {}; - - // Hang on to this Unix timestamp because we might mess with it directly below. - var timestamp = d.getTime(); - - var tz = options.timezone; - var tzType = typeof tz; - - if (options.utc || tzType == 'number' || tzType == 'string') { - d = dateToUTC(d); + function strftime(fmt, d, locale) { + return _strftime(fmt, d, locale); } - if (tz) { - // ISO 8601 format timezone string, [-+]HHMM - // - // Convert to the number of minutes and it'll be applied to the date below. - if (tzType == 'string') { - var sign = tz[0] == '-' ? -1 : 1; - var hours = parseInt(tz.slice(1, 3), 10); - var mins = parseInt(tz.slice(3, 5), 10); - tz = sign * (60 * hours) + mins; - } + function strfTimeTZ(fmt, d, locale, timezone) { + var _locale = locale; + var _timezone = timezone; - if (tzType) { - d = new Date(d.getTime() + (tz * 60000)); - } - } - - // Most of the specifiers supported by C's strftime, and some from Ruby. - // Some other syntax extensions from Ruby are supported: %-, %_, and %0 - // to pad with nothing, space, or zero (respectively). - return fmt.replace(/%([-_0]?.)/g, function(_, c) { - var mod, padding; - - if (c.length == 2) { - mod = c[0]; - // omit padding - if (mod == '-') { - padding = ''; + if ((typeof locale === 'number' || typeof locale === 'string') && timezone == null) { + _timezone = locale; + _locale = undefined; } - // pad with space - else if (mod == '_') { - padding = ' '; + + return _strftime(fmt, d, _locale, { + timezone: _timezone + }); + } + + function strftimeUTC(fmt, d, locale) { + return _strftime(fmt, d, locale, { + utc: true + }); + } + + function localizedStrftime(locale) { + return function (fmt, d, options) { + return _strftime(fmt, d, locale, options); + }; + } + + // d, locale, and options are optional, but you can't leave + // holes in the argument list. If you pass options you have to pass + // in all the preceding args as well. + // + // options: + // - locale [object] an object with the same structure as DefaultLocale + // - timezone [number] timezone offset in minutes from GMT + function _strftime(format, d, locale, options) { + var _options = options || {}; + var _locale = locale; + var _d = d; + + // d and locale are optional so check if d is really the locale + if (_d && !quacksLikeDate(_d)) { + _locale = _d; + _d = undefined; } - // pad with zero - else if (mod == '0') { - padding = '0'; + + _d = _d || new Date(); + + _locale = _locale || DefaultLocale; + _locale.formats = _locale.formats || {}; + + // Hang on to this Unix timestamp because we might mess with it directly below. + var timestamp = _d.getTime(); + var tz = _options.timezone; + var tzType = typeof tz; + + if (_options.utc || tzType === 'number' || tzType === 'string') { + _d = dateToUTC(_d); } - else { - // unrecognized, return the format - return _; + + if (tz) { + // ISO 8601 format timezone string, [-+]HHMM + // Convert to the number of minutes and it'll be applied to the date below. + if (tzType === 'string') { + var sign = tz[0] === '-' ? -1 : 1; + var hours = parseInt(tz.slice(1, 3), 10); + var mins = parseInt(tz.slice(3, 5), 10); + + tz = sign * (60 * hours) + mins; + } + + _d = new Date(_d.getTime() + (tz * 60000)); } - c = c[1]; - } - switch (c) { + // Most of the specifiers supported by C's strftime, and some from Ruby. + // Some other syntax extensions from Ruby are supported: %-, %_, and %0 + // to pad with nothing, space, or zero (respectively). + return format.replace(/%([-_0]?.)/g, function (_, c) { + var padding; + var _c = c; - // Examples for new Date(0) in GMT + if (_c.length === 2) { + switch (_c[0]) { + // omit padding + case '-': + padding = ''; + break; - // 'Thursday' - case 'A': return locale.days[d.getDay()]; + // pad with space + case '_': + padding = ' '; + break; - // 'Thu' - case 'a': return locale.shortDays[d.getDay()]; + // pad with zero + case '0': + padding = '0'; + break; - // 'January' - case 'B': return locale.months[d.getMonth()]; + // unrecognized, return the format + default: + return _; + } - // 'Jan' - case 'b': return locale.shortMonths[d.getMonth()]; + _c = _c[1]; + } - // '19' - case 'C': return pad(Math.floor(d.getFullYear() / 100), padding); + switch (_c) { + // Examples for new Date(0) in GMT - // '01/01/70' - case 'D': return _strftime(locale.formats.D || '%m/%d/%y', d, locale); + // 'Thursday' + case 'A': + return _locale.days[_d.getDay()]; - // '01' - case 'd': return pad(d.getDate(), padding); + // 'Thu' + case 'a': + return _locale.shortDays[_d.getDay()]; - // '01' - case 'e': return d.getDate(); + // 'January' + case 'B': + return _locale.months[_d.getMonth()]; - // '1970-01-01' - case 'F': return _strftime(locale.formats.F || '%Y-%m-%d', d, locale); + // 'Jan' + case 'b': + return _locale.shortMonths[_d.getMonth()]; - // '00' - case 'H': return pad(d.getHours(), padding); + // '19' + case 'C': + return pad(Math.floor(_d.getFullYear() / 100), padding); - // 'Jan' - case 'h': return locale.shortMonths[d.getMonth()]; + // '01/01/70' + case 'D': + return _strftime(_locale.formats.D || '%m/%d/%y', _d, _locale); - // '12' - case 'I': return pad(hours12(d), padding); + // '01' + case 'd': + return pad(_d.getDate(), padding); - // '000' - case 'j': - var y = new Date(d.getFullYear(), 0, 1); - var day = Math.ceil((d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - return pad(day, 3); + // '01' + case 'e': + return _d.getDate(); - // ' 0' - case 'k': return pad(d.getHours(), padding == null ? ' ' : padding); + // '1970-01-01' + case 'F': + return _strftime(_locale.formats.F || '%Y-%m-%d', _d, _locale); - // '000' - case 'L': return pad(Math.floor(timestamp % 1000), 3); + // '00' + case 'H': + return pad(_d.getHours(), padding); - // '12' - case 'l': return pad(hours12(d), padding == null ? ' ' : padding); + // 'Jan' + case 'h': + return _locale.shortMonths[_d.getMonth()]; - // '00' - case 'M': return pad(d.getMinutes(), padding); + // '12' + case 'I': + return pad(hours12(_d), padding); - // '01' - case 'm': return pad(d.getMonth() + 1, padding); + // '000' + case 'j': + var y = new Date(_d.getFullYear(), 0, 1); + var day = Math.ceil((_d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + return pad(day, 3); - // '\n' - case 'n': return '\n'; + // ' 0' + case 'k': + return pad(_d.getHours(), padding == null ? ' ' : padding); - // '1st' - case 'o': return String(d.getDate()) + ordinal(d.getDate()); + // '000' + case 'L': + return pad(Math.floor(timestamp % 1000), 3); - // 'am' - case 'P': return d.getHours() < 12 ? locale.am : locale.pm; + // '12' + case 'l': + return pad(hours12(_d), padding == null ? ' ' : padding); - // 'AM' - case 'p': return d.getHours() < 12 ? locale.AM : locale.PM; + // '00' + case 'M': + return pad(_d.getMinutes(), padding); - // '00:00' - case 'R': return _strftime(locale.formats.R || '%H:%M', d, locale); + // '01' + case 'm': + return pad(_d.getMonth() + 1, padding); - // '12:00:00 AM' - case 'r': return _strftime(locale.formats.r || '%I:%M:%S %p', d, locale); + // '\n' + case 'n': + return '\n'; - // '00' - case 'S': return pad(d.getSeconds(), padding); + // '1st' + case 'o': + return String(_d.getDate()) + ordinal(_d.getDate()); - // '0' - case 's': return Math.floor(timestamp / 1000); + // 'am' + case 'P': + return _d.getHours() < 12 ? _locale.am : _locale.pm; - // '00:00:00' - case 'T': return _strftime(locale.formats.T || '%H:%M:%S', d, locale); + // 'AM' + case 'p': + return _d.getHours() < 12 ? _locale.AM : _locale.PM; - // '\t' - case 't': return '\t'; + // '00:00' + case 'R': + return _strftime(_locale.formats.R || '%H:%M', _d, _locale); - // '00' - case 'U': return pad(weekNumber(d, 'sunday'), padding); + // '12:00:00 AM' + case 'r': + return _strftime(_locale.formats.r || '%I:%M:%S %p', _d, _locale); - // '4' - case 'u': - var day = d.getDay(); - return day == 0 ? 7 : day; // 1 - 7, Monday is first day of the week + // '00' + case 'S': + return pad(_d.getSeconds(), padding); - // '1-Jan-1970' - case 'v': return _strftime(locale.formats.v || '%e-%b-%Y', d, locale); + // '0' + case 's': + return Math.floor(timestamp / 1000); - // '00' - case 'W': return pad(weekNumber(d, 'monday'), padding); + // '00:00:00' + case 'T': + return _strftime(_locale.formats.T || '%H:%M:%S', _d, _locale); - // '4' - case 'w': return d.getDay(); // 0 - 6, Sunday is first day of the week + // '\t' + case 't': + return '\t'; - // '1970' - case 'Y': return d.getFullYear(); + // '00' + case 'U': + return pad(weekNumber(_d, 'sunday'), padding); - // '70' - case 'y': - var y = String(d.getFullYear()); - return y.slice(y.length - 2); + // '4' + case 'u': + var day = _d.getDay(); + return day === 0 ? 7 : day; // 1 - 7, Monday is first day of the week - // 'GMT' - case 'Z': - if (options.utc) { - return "GMT"; - } - else { - var tzString = d.toString().match(/\((\w+)\)/); - return tzString && tzString[1] || ''; - } + // '1-Jan-1970' + case 'v': + return _strftime(_locale.formats.v || '%e-%b-%Y', _d, _locale); - // '+0000' - case 'z': - if (options.utc) { - return "+0000"; - } - else { - var off = typeof tz == 'number' ? tz : -d.getTimezoneOffset(); - return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); - } + // '00' + case 'W': + return pad(weekNumber(_d, 'monday'), padding); - default: return c; - } - }); - } + // '4' + case 'w': + return _d.getDay(); // 0 - 6, Sunday is first day of the week - function dateToUTC(d) { - var msDelta = (d.getTimezoneOffset() || 0) * 60000; - return new Date(d.getTime() + msDelta); - } + // '1970' + case 'Y': + return _d.getFullYear(); - var RequiredDateMethods = ['getTime', 'getTimezoneOffset', 'getDay', 'getDate', 'getMonth', 'getFullYear', 'getYear', 'getHours', 'getMinutes', 'getSeconds']; - function quacksLikeDate(x) { - var i = 0 - , n = RequiredDateMethods.length - ; - for (i = 0; i < n; ++i) { - if (typeof x[RequiredDateMethods[i]] != 'function') { - return false; - } - } - return true; - } + // '70' + case 'y': + return String(_d.getFullYear()).slice(-2); - // Default padding is '0' and default length is 2, both are optional. - function pad(n, padding, length) { - // pad(n, ) - if (typeof padding === 'number') { - length = padding; - padding = '0'; + // 'GMT' + case 'Z': + if (_options.utc) { + return 'GMT'; + } else { + var tzString = _d.toString().match(/\((\w+)\)/); + return tzString && tzString[1] || ''; + } + + // '+0000' + case 'z': + if (_options.utc) { + return '+0000'; + } else { + var off = typeof tz === 'number' ? tz : -_d.getTimezoneOffset(); + return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); + } + + default: + return _c; + } + }); } - // Defaults handle pad(n) and pad(n, ) - if (padding == null) { - padding = '0'; - } - length = length || 2; + function dateToUTC(d) { + var msDelta = (d.getTimezoneOffset() || 0) * 60000; - var s = String(n); - // padding may be an empty string, don't loop forever if it is - if (padding) { - while (s.length < length) s = padding + s; + return new Date(d.getTime() + msDelta); } - return s; - } - function hours12(d) { - var hour = d.getHours(); - if (hour == 0) hour = 12; - else if (hour > 12) hour -= 12; - return hour; - } + function quacksLikeDate(x) { + var index = RequiredDateMethods.length; - // Get the ordinal suffix for a number: st, nd, rd, or th - function ordinal(n) { - var i = n % 10 - , ii = n % 100 - ; - if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) { - return 'th'; + while (index--) { + if (typeof x[RequiredDateMethods[index]] !== 'function') { + return false; + } + } + + return true; } - switch (i) { - case 1: return 'st'; - case 2: return 'nd'; - case 3: return 'rd'; - } - } - // firstWeekday: 'sunday' or 'monday', default is 'sunday' - // - // Pilfered & ported from Ruby's strftime implementation. - function weekNumber(d, firstWeekday) { - firstWeekday = firstWeekday || 'sunday'; + // Default padding is '0' and default length is 2, both are optional. + function pad(n, padding) { + var _padding = padding == null ? '0' : padding; + var _n = String(n); - // This works by shifting the weekday back by one day if we - // are treating Monday as the first day of the week. - var wday = d.getDay(); - if (firstWeekday == 'monday') { - if (wday == 0) // Sunday - wday = 6; - else - wday--; + // padding may be an empty string, don't loop forever if it is + switch (_n.length) { + case 0: + _n = _padding + _padding; + break; + case 1: + _n = _padding + _n; + break; + } + + return _n; } - var firstDayOfYear = new Date(d.getFullYear(), 0, 1) - , yday = (d - firstDayOfYear) / 86400000 - , weekNum = (yday + 7 - wday) / 7 - ; - return Math.floor(weekNum); - } + + function hours12(d) { + var hour = d.getHours(); + + if (hour === 0) { + hour = 12; + } else if (hour > 12) { + hour -= 12; + } + + return hour; + } + + // Get the ordinal suffix for a number: st, nd, rd, or th + function ordinal(n) { + var i = n % 10; + var ii = n % 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'; + } + } + + // firstWeekday: 'sunday' or 'monday', default is 'sunday' + // Pilfered & ported from Ruby's strftime implementation. + function weekNumber(d, firstWeekday) { + var firstDayOfYear = new Date(d.getFullYear(), 0, 1); + // This works by shifting the weekday back by one day if we + // are treating Monday as the first day of the week. + var wDay = d.getDay(); + var yDay = (d - firstDayOfYear) / 86400000; + + if (firstWeekday === 'monday') { + if (wDay === 0){ + // Sunday + wDay = 6; + } else { + wDay--; + } + } + + return Math.floor((yDay + 7 - wDay) / 7); + } + + namespace.strftime = strftime; + namespace.strftimeTZ = strftime.strftimeTZ = strfTimeTZ; + namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC; + namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime; }()); From c064afb081bcd0ba5e1410c01d6e584233309d1d Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Wed, 7 May 2014 22:34:18 +0400 Subject: [PATCH 02/15] Fixed the pad function. --- strftime.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/strftime.js b/strftime.js index 8b902ec..b594bf0 100644 --- a/strftime.js +++ b/strftime.js @@ -210,7 +210,7 @@ case 'j': var y = new Date(_d.getFullYear(), 0, 1); var day = Math.ceil((_d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - return pad(day, 3); + return pad(day, null, 3); // ' 0' case 'k': @@ -218,7 +218,7 @@ // '000' case 'L': - return pad(Math.floor(timestamp % 1000), 3); + return pad(Math.floor(timestamp % 1000), null, 3); // '12' case 'l': @@ -344,18 +344,16 @@ } // Default padding is '0' and default length is 2, both are optional. - function pad(n, padding) { + function pad(n, padding, length) { var _padding = padding == null ? '0' : padding; var _n = String(n); + var _length = length || 2; // padding may be an empty string, don't loop forever if it is - switch (_n.length) { - case 0: - _n = _padding + _padding; - break; - case 1: + if (_padding) { + while (_n.length < _length) { _n = _padding + _n; - break; + } } return _n; @@ -402,7 +400,7 @@ var yDay = (d - firstDayOfYear) / 86400000; if (firstWeekday === 'monday') { - if (wDay === 0){ + if (wDay === 0) { // Sunday wDay = 6; } else { From 3e78b18cd8ab606b20243bd129dd0ffcd3b5ad3a Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Sat, 10 May 2014 22:53:32 +0400 Subject: [PATCH 03/15] Optimize for V8 --- strftime.js | 557 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 345 insertions(+), 212 deletions(-) diff --git a/strftime.js b/strftime.js index b594bf0..4cb902d 100644 --- a/strftime.js +++ b/strftime.js @@ -47,6 +47,12 @@ 'getSeconds' ]; + var _options; + var _locale; + var _date; + var _timeZone; + var _timestamp; + function strftime(fmt, d, locale) { return _strftime(fmt, d, locale); } @@ -85,252 +91,375 @@ // - locale [object] an object with the same structure as DefaultLocale // - timezone [number] timezone offset in minutes from GMT function _strftime(format, d, locale, options) { - var _options = options || {}; - var _locale = locale; - var _d = d; + _options = options || {}; + _locale = locale; + _date = d; + _timeZone = _options.timezone; // d and locale are optional so check if d is really the locale - if (_d && !quacksLikeDate(_d)) { - _locale = _d; - _d = undefined; + if (_date && !quacksLikeDate(_date)) { + _locale = _date; + _date = undefined; } - _d = _d || new Date(); + _date = _date || new Date(); + + // Hang on to this Unix timestamp because we might mess with it directly below. + _timestamp = _date.getTime(); _locale = _locale || DefaultLocale; _locale.formats = _locale.formats || {}; - // Hang on to this Unix timestamp because we might mess with it directly below. - var timestamp = _d.getTime(); - var tz = _options.timezone; - var tzType = typeof tz; + var tzType = typeof _timeZone; if (_options.utc || tzType === 'number' || tzType === 'string') { - _d = dateToUTC(_d); + _date = dateToUTC(_date); } - if (tz) { + if (_timeZone) { // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. if (tzType === 'string') { - var sign = tz[0] === '-' ? -1 : 1; - var hours = parseInt(tz.slice(1, 3), 10); - var mins = parseInt(tz.slice(3, 5), 10); + var sign = _timeZone[0] === '-' ? -1 : 1; + var hours = parseInt(_timeZone.slice(1, 3), 10); + var mins = parseInt(_timeZone.slice(3, 5), 10); - tz = sign * (60 * hours) + mins; + _timeZone = sign * (60 * hours) + mins; } - _d = new Date(_d.getTime() + (tz * 60000)); + _date = new Date(_date.getTime() + (_timeZone * 60000)); } // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 // to pad with nothing, space, or zero (respectively). - return format.replace(/%([-_0]?.)/g, function (_, c) { - var padding; - var _c = c; + /*var part; + var regExp = /%([-_0]?.)/g;*/ - if (_c.length === 2) { - switch (_c[0]) { - // omit padding - case '-': - padding = ''; - break; + // while (part = regExp.exec(format)) { + // var padding; + // var _c = part[1]; + // + // if (_c.length === 2) { + // switch (_c[0]) { + // // omit padding + // case '-': + // padding = ''; + // break; + // + // // pad with space + // case '_': + // padding = ' '; + // break; + // + // // pad with zero + // case '0': + // padding = '0'; + // break; + // + // // unrecognized, return the format + // default: + // return part[0]; + // } + // + // _c = _c[1]; + // } + // + // switch (_c) { + // // Examples for new Date(0) in GMT + // + // // 'Thursday' + // case 'A': + // return _locale.days[_d.getDay()]; + // + // // 'Thu' + // case 'a': + // return _locale.shortDays[_d.getDay()]; + // + // // 'January' + // case 'B': + // return _locale.months[_d.getMonth()]; + // + // // 'Jan' + // case 'b': + // return _locale.shortMonths[_d.getMonth()]; + // + // // '19' + // case 'C': + // return pad(Math.floor(_d.getFullYear() / 100), padding); + // + // // '01/01/70' + // case 'D': + // return _strftime(_locale.formats.D || '%m/%d/%y', _d, _locale); + // + // // '01' + // case 'd': + // return pad(_d.getDate(), padding); + // + // // '01' + // case 'e': + // return _d.getDate(); + // + // // '1970-01-01' + // case 'F': + // return _strftime(_locale.formats.F || '%Y-%m-%d', _d, _locale); + // + // // '00' + // case 'H': + // return pad(_d.getHours(), padding); + // + // // 'Jan' + // case 'h': + // return _locale.shortMonths[_d.getMonth()]; + // + // // '12' + // case 'I': + // return pad(hours12(_d), padding); + // + // // '000' + // case 'j': + // var y = new Date(_d.getFullYear(), 0, 1); + // var day = Math.ceil((_d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + // return pad(day, null, 3); + // + // // ' 0' + // case 'k': + // return pad(_d.getHours(), padding == null ? ' ' : padding); + // + // // '000' + // case 'L': + // return pad(Math.floor(timestamp % 1000), null, 3); + // + // // '12' + // case 'l': + // return pad(hours12(_d), padding == null ? ' ' : padding); + // + // // '00' + // case 'M': + // return pad(_d.getMinutes(), padding); + // + // // '01' + // case 'm': + // return pad(_d.getMonth() + 1, padding); + // + // // '\n' + // case 'n': + // return '\n'; + // + // // '1st' + // case 'o': + // return String(_d.getDate()) + ordinal(_d.getDate()); + // + // // 'am' + // case 'P': + // return _d.getHours() < 12 ? _locale.am : _locale.pm; + // + // // 'AM' + // case 'p': + // return _d.getHours() < 12 ? _locale.AM : _locale.PM; + // + // // '00:00' + // case 'R': + // return _strftime(_locale.formats.R || '%H:%M', _d, _locale); + // + // // '12:00:00 AM' + // case 'r': + // return _strftime(_locale.formats.r || '%I:%M:%S %p', _d, _locale); + // + // // '00' + // case 'S': + // return pad(_d.getSeconds(), padding); + // + // // '0' + // case 's': + // return Math.floor(timestamp / 1000); + // + // // '00:00:00' + // case 'T': + // return _strftime(_locale.formats.T || '%H:%M:%S', _d, _locale); + // + // // '\t' + // case 't': + // return '\t'; + // + // // '00' + // case 'U': + // return pad(weekNumber(_d, 'sunday'), padding); + // + // // '4' + // case 'u': + // var day = _d.getDay(); + // return day === 0 ? 7 : day; // 1 - 7, Monday is first day of the week + // + // // '1-Jan-1970' + // case 'v': + // return _strftime(_locale.formats.v || '%e-%b-%Y', _d, _locale); + // + // // '00' + // case 'W': + // return pad(weekNumber(_d, 'monday'), padding); + // + // // '4' + // case 'w': + // return _d.getDay(); // 0 - 6, Sunday is first day of the week + // + // // '1970' + // case 'Y': + // return _d.getFullYear(); + // + // // '70' + // case 'y': + // return String(_d.getFullYear()).slice(-2); + // + // // 'GMT' + // case 'Z': + // if (_options.utc) { + // return 'GMT'; + // } else { + // var tzString = _d.toString().match(/\((\w+)\)/); + // return tzString && tzString[1] || ''; + // } + // + // // '+0000' + // case 'z': + // if (_options.utc) { + // return '+0000'; + // } else { + // var off = typeof tz === 'number' ? tz : -_d.getTimezoneOffset(); + // return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); + // } + // + // default: + // return _c; + // } + // } - // pad with space - case '_': - padding = ' '; - break; + return format.replace(/%([-_0]?.)/g, processing); + } - // pad with zero - case '0': - padding = '0'; - break; + function processing(_, c) { + var padding; + var _c = c; + var day; - // unrecognized, return the format - default: - return _; - } + if (_c.length === 2) { + switch (_c[0]) { + // omit padding + case '-': + padding = ''; + break; - _c = _c[1]; - } + // pad with space + case '_': + padding = ' '; + break; - switch (_c) { - // Examples for new Date(0) in GMT - - // 'Thursday' - case 'A': - return _locale.days[_d.getDay()]; - - // 'Thu' - case 'a': - return _locale.shortDays[_d.getDay()]; - - // 'January' - case 'B': - return _locale.months[_d.getMonth()]; - - // 'Jan' - case 'b': - return _locale.shortMonths[_d.getMonth()]; - - // '19' - case 'C': - return pad(Math.floor(_d.getFullYear() / 100), padding); - - // '01/01/70' - case 'D': - return _strftime(_locale.formats.D || '%m/%d/%y', _d, _locale); - - // '01' - case 'd': - return pad(_d.getDate(), padding); - - // '01' - case 'e': - return _d.getDate(); - - // '1970-01-01' - case 'F': - return _strftime(_locale.formats.F || '%Y-%m-%d', _d, _locale); - - // '00' - case 'H': - return pad(_d.getHours(), padding); - - // 'Jan' - case 'h': - return _locale.shortMonths[_d.getMonth()]; - - // '12' - case 'I': - return pad(hours12(_d), padding); - - // '000' - case 'j': - var y = new Date(_d.getFullYear(), 0, 1); - var day = Math.ceil((_d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - return pad(day, null, 3); - - // ' 0' - case 'k': - return pad(_d.getHours(), padding == null ? ' ' : padding); - - // '000' - case 'L': - return pad(Math.floor(timestamp % 1000), null, 3); - - // '12' - case 'l': - return pad(hours12(_d), padding == null ? ' ' : padding); - - // '00' - case 'M': - return pad(_d.getMinutes(), padding); - - // '01' - case 'm': - return pad(_d.getMonth() + 1, padding); - - // '\n' - case 'n': - return '\n'; - - // '1st' - case 'o': - return String(_d.getDate()) + ordinal(_d.getDate()); - - // 'am' - case 'P': - return _d.getHours() < 12 ? _locale.am : _locale.pm; - - // 'AM' - case 'p': - return _d.getHours() < 12 ? _locale.AM : _locale.PM; - - // '00:00' - case 'R': - return _strftime(_locale.formats.R || '%H:%M', _d, _locale); - - // '12:00:00 AM' - case 'r': - return _strftime(_locale.formats.r || '%I:%M:%S %p', _d, _locale); - - // '00' - case 'S': - return pad(_d.getSeconds(), padding); - - // '0' - case 's': - return Math.floor(timestamp / 1000); - - // '00:00:00' - case 'T': - return _strftime(_locale.formats.T || '%H:%M:%S', _d, _locale); - - // '\t' - case 't': - return '\t'; - - // '00' - case 'U': - return pad(weekNumber(_d, 'sunday'), padding); - - // '4' - case 'u': - var day = _d.getDay(); - return day === 0 ? 7 : day; // 1 - 7, Monday is first day of the week - - // '1-Jan-1970' - case 'v': - return _strftime(_locale.formats.v || '%e-%b-%Y', _d, _locale); - - // '00' - case 'W': - return pad(weekNumber(_d, 'monday'), padding); - - // '4' - case 'w': - return _d.getDay(); // 0 - 6, Sunday is first day of the week - - // '1970' - case 'Y': - return _d.getFullYear(); - - // '70' - case 'y': - return String(_d.getFullYear()).slice(-2); - - // 'GMT' - case 'Z': - if (_options.utc) { - return 'GMT'; - } else { - var tzString = _d.toString().match(/\((\w+)\)/); - return tzString && tzString[1] || ''; - } - - // '+0000' - case 'z': - if (_options.utc) { - return '+0000'; - } else { - var off = typeof tz === 'number' ? tz : -_d.getTimezoneOffset(); - return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); - } + // pad with zero + case '0': + padding = '0'; + break; + // unrecognized, return the format default: - return _c; + return _; } - }); - } - - function dateToUTC(d) { - var msDelta = (d.getTimezoneOffset() || 0) * 60000; - - return new Date(d.getTime() + msDelta); + + _c = _c[1]; + } + + if (_c === 'A') { + return _locale.days[_date.getDay()]; + } else if (_c === 'a') { + return _locale.shortDays[_date.getDay()]; + } else if (_c === 'B') { + return _locale.months[_date.getMonth()]; + } else if (_c === 'b') { + return _locale.shortMonths[_date.getMonth()]; + } else if (_c === 'C') { + return pad(Math.floor(_date.getFullYear() / 100), padding); + } else if (_c === 'D') { + return _strftime(_locale.formats.D || '%m/%d/%y', _date, _locale); + } else if (_c === 'd') { + return pad(_date.getDate(), padding); + } else if (_c === 'e') { + return _date.getDate(); + } else if (_c === 'F') { + return _strftime(_locale.formats.F || '%Y-%m-%d', _date, _locale); + } else if (_c === 'H') { + return pad(_date.getHours(), padding); + } else if (_c === 'h') { + return _locale.shortMonths[_date.getMonth()]; + } else if (_c === 'I') { + return pad(hours12(_date), padding); + } else if (_c === 'j') { + var y = new Date(_date.getFullYear(), 0, 1); + day = Math.ceil((_date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + return pad(day, null, 3); + } else if (_c === 'k') { + return pad(_date.getHours(), padding == null ? ' ' : padding); + } else if (_c === 'L') { + return pad(Math.floor(_timestamp % 1000), null, 3); + } else if (_c === 'l') { + return pad(hours12(_date), padding == null ? ' ' : padding); + } else if (_c === 'M') { + return pad(_date.getMinutes(), padding); + } else if (_c === 'm') { + return pad(_date.getMonth() + 1, padding); + } else if (_c === 'n') { + return '\n'; + } else if (_c === 'o') { + return String(_date.getDate()) + ordinal(_date.getDate()); + } else if (_c === 'P') { + return _date.getHours() < 12 ? _locale.am : _locale.pm; + } else if (_c === 'p') { + return _date.getHours() < 12 ? _locale.AM : _locale.PM; + } else if (_c === 'R') { + return _strftime(_locale.formats.R || '%H:%M', _date, _locale); + } else if (_c === 'r') { + return _strftime(_locale.formats.r || '%I:%M:%S %p', _date, _locale); + } else if (_c === 'S') { + return pad(_date.getSeconds(), padding); + } else if (_c === 's') { + return Math.floor(timestamp / 1000); + } else if (_c === 'T') { + return _strftime(_locale.formats.T || '%H:%M:%S', _date, _locale); + } else if (_c === 't') { + return '\t'; + } else if (_c === 'U') { + return pad(weekNumber(_date, 'sunday'), padding); + } else if (_c === 'u') { + day = _date.getDay(); + return day === 0 ? 7 : day; + } else if (_c === 'v') { + return _strftime(_locale.formats.v || '%e-%b-%Y', _date, _locale); + } else if (_c === 'W') { + return pad(weekNumber(_date, 'monday'), padding); + } else if (_c === 'w') { + return _date.getDay(); + } else if (_c === 'Y') { + return _date.getFullYear(); + } else if (_c === 'y') { + return String(_date.getFullYear()).slice(-2); + } else if (_c === 'Z') { + if (_options.utc) { + return 'GMT'; + } else { + var tzString = _date.toString().match(/\((\w+)\)/); + return tzString && tzString[1] || ''; + } + } else if (_c === 'z') { + if (_options.utc) { + return '+0000'; + } else { + var off = typeof _timeZone === 'number' ? _timeZone : -_date.getTimezoneOffset(); + return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); + } + } else { + return _c; + } } + // TODO: Did not inline quacksLikeDate called from _strftime (target requires context change). function quacksLikeDate(x) { var index = RequiredDateMethods.length; @@ -343,6 +472,10 @@ return true; } + function dateToUTC(d) { + new Date(d.getTime() + (d.getTimezoneOffset() || 0) * 60000); + } + // Default padding is '0' and default length is 2, both are optional. function pad(n, padding, length) { var _padding = padding == null ? '0' : padding; From b163521428c922b57bf6a346a06bc3469bb8b0b6 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Sat, 10 May 2014 23:46:54 +0400 Subject: [PATCH 04/15] Optimize for V8 --- strftime.js | 480 +++++++++++++++++----------------------------------- 1 file changed, 153 insertions(+), 327 deletions(-) diff --git a/strftime.js b/strftime.js index 4cb902d..c3689ed 100644 --- a/strftime.js +++ b/strftime.js @@ -51,37 +51,7 @@ var _locale; var _date; var _timeZone; - var _timestamp; - - function strftime(fmt, d, locale) { - return _strftime(fmt, d, locale); - } - - function strfTimeTZ(fmt, d, locale, timezone) { - var _locale = locale; - var _timezone = timezone; - - if ((typeof locale === 'number' || typeof locale === 'string') && timezone == null) { - _timezone = locale; - _locale = undefined; - } - - return _strftime(fmt, d, _locale, { - timezone: _timezone - }); - } - - function strftimeUTC(fmt, d, locale) { - return _strftime(fmt, d, locale, { - utc: true - }); - } - - function localizedStrftime(locale) { - return function (fmt, d, options) { - return _strftime(fmt, d, locale, options); - }; - } + var _padding; // d, locale, and options are optional, but you can't leave // holes in the argument list. If you pass options you have to pass @@ -103,10 +73,6 @@ } _date = _date || new Date(); - - // Hang on to this Unix timestamp because we might mess with it directly below. - _timestamp = _date.getTime(); - _locale = _locale || DefaultLocale; _locale.formats = _locale.formats || {}; @@ -116,7 +82,7 @@ _date = dateToUTC(_date); } - if (_timeZone) { + /*if (_timeZone) { // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. if (tzType === 'string') { @@ -128,338 +94,171 @@ } _date = new Date(_date.getTime() + (_timeZone * 60000)); - } + }*/ // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 // to pad with nothing, space, or zero (respectively). - /*var part; - var regExp = /%([-_0]?.)/g;*/ - // while (part = regExp.exec(format)) { - // var padding; - // var _c = part[1]; - // - // if (_c.length === 2) { - // switch (_c[0]) { - // // omit padding - // case '-': - // padding = ''; - // break; - // - // // pad with space - // case '_': - // padding = ' '; - // break; - // - // // pad with zero - // case '0': - // padding = '0'; - // break; - // - // // unrecognized, return the format - // default: - // return part[0]; - // } - // - // _c = _c[1]; - // } - // - // switch (_c) { - // // Examples for new Date(0) in GMT - // - // // 'Thursday' - // case 'A': - // return _locale.days[_d.getDay()]; - // - // // 'Thu' - // case 'a': - // return _locale.shortDays[_d.getDay()]; - // - // // 'January' - // case 'B': - // return _locale.months[_d.getMonth()]; - // - // // 'Jan' - // case 'b': - // return _locale.shortMonths[_d.getMonth()]; - // - // // '19' - // case 'C': - // return pad(Math.floor(_d.getFullYear() / 100), padding); - // - // // '01/01/70' - // case 'D': - // return _strftime(_locale.formats.D || '%m/%d/%y', _d, _locale); - // - // // '01' - // case 'd': - // return pad(_d.getDate(), padding); - // - // // '01' - // case 'e': - // return _d.getDate(); - // - // // '1970-01-01' - // case 'F': - // return _strftime(_locale.formats.F || '%Y-%m-%d', _d, _locale); - // - // // '00' - // case 'H': - // return pad(_d.getHours(), padding); - // - // // 'Jan' - // case 'h': - // return _locale.shortMonths[_d.getMonth()]; - // - // // '12' - // case 'I': - // return pad(hours12(_d), padding); - // - // // '000' - // case 'j': - // var y = new Date(_d.getFullYear(), 0, 1); - // var day = Math.ceil((_d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - // return pad(day, null, 3); - // - // // ' 0' - // case 'k': - // return pad(_d.getHours(), padding == null ? ' ' : padding); - // - // // '000' - // case 'L': - // return pad(Math.floor(timestamp % 1000), null, 3); - // - // // '12' - // case 'l': - // return pad(hours12(_d), padding == null ? ' ' : padding); - // - // // '00' - // case 'M': - // return pad(_d.getMinutes(), padding); - // - // // '01' - // case 'm': - // return pad(_d.getMonth() + 1, padding); - // - // // '\n' - // case 'n': - // return '\n'; - // - // // '1st' - // case 'o': - // return String(_d.getDate()) + ordinal(_d.getDate()); - // - // // 'am' - // case 'P': - // return _d.getHours() < 12 ? _locale.am : _locale.pm; - // - // // 'AM' - // case 'p': - // return _d.getHours() < 12 ? _locale.AM : _locale.PM; - // - // // '00:00' - // case 'R': - // return _strftime(_locale.formats.R || '%H:%M', _d, _locale); - // - // // '12:00:00 AM' - // case 'r': - // return _strftime(_locale.formats.r || '%I:%M:%S %p', _d, _locale); - // - // // '00' - // case 'S': - // return pad(_d.getSeconds(), padding); - // - // // '0' - // case 's': - // return Math.floor(timestamp / 1000); - // - // // '00:00:00' - // case 'T': - // return _strftime(_locale.formats.T || '%H:%M:%S', _d, _locale); - // - // // '\t' - // case 't': - // return '\t'; - // - // // '00' - // case 'U': - // return pad(weekNumber(_d, 'sunday'), padding); - // - // // '4' - // case 'u': - // var day = _d.getDay(); - // return day === 0 ? 7 : day; // 1 - 7, Monday is first day of the week - // - // // '1-Jan-1970' - // case 'v': - // return _strftime(_locale.formats.v || '%e-%b-%Y', _d, _locale); - // - // // '00' - // case 'W': - // return pad(weekNumber(_d, 'monday'), padding); - // - // // '4' - // case 'w': - // return _d.getDay(); // 0 - 6, Sunday is first day of the week - // - // // '1970' - // case 'Y': - // return _d.getFullYear(); - // - // // '70' - // case 'y': - // return String(_d.getFullYear()).slice(-2); - // - // // 'GMT' - // case 'Z': - // if (_options.utc) { - // return 'GMT'; - // } else { - // var tzString = _d.toString().match(/\((\w+)\)/); - // return tzString && tzString[1] || ''; - // } - // - // // '+0000' - // case 'z': - // if (_options.utc) { - // return '+0000'; - // } else { - // var off = typeof tz === 'number' ? tz : -_d.getTimezoneOffset(); - // return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); - // } - // - // default: - // return _c; - // } - // } - - return format.replace(/%([-_0]?.)/g, processing); + return format.replace(/%([-_0]?)(.)/g, processing); } - function processing(_, c) { - var padding; - var _c = c; - var day; - - if (_c.length === 2) { - switch (_c[0]) { - // omit padding - case '-': - padding = ''; - break; - - // pad with space - case '_': - padding = ' '; - break; - - // pad with zero - case '0': - padding = '0'; - break; - - // unrecognized, return the format - default: - return _; - } - - _c = _c[1]; - } - - if (_c === 'A') { + var mask = { + 'A': function () { return _locale.days[_date.getDay()]; - } else if (_c === 'a') { + }, + 'a': function () { return _locale.shortDays[_date.getDay()]; - } else if (_c === 'B') { + }, + 'B': function () { return _locale.months[_date.getMonth()]; - } else if (_c === 'b') { + }, + 'b': function () { return _locale.shortMonths[_date.getMonth()]; - } else if (_c === 'C') { - return pad(Math.floor(_date.getFullYear() / 100), padding); - } else if (_c === 'D') { + }, + 'C': function () { + return pad(Math.floor(_date.getFullYear() / 100), _padding); + }, + 'D': function () { return _strftime(_locale.formats.D || '%m/%d/%y', _date, _locale); - } else if (_c === 'd') { - return pad(_date.getDate(), padding); - } else if (_c === 'e') { + }, + 'd': function () { + return pad(_date.getDate(), _padding); + }, + 'e': function () { return _date.getDate(); - } else if (_c === 'F') { + }, + 'F': function () { return _strftime(_locale.formats.F || '%Y-%m-%d', _date, _locale); - } else if (_c === 'H') { - return pad(_date.getHours(), padding); - } else if (_c === 'h') { + }, + 'H': function () { + return pad(_date.getHours(), _padding); + }, + 'h': function () { return _locale.shortMonths[_date.getMonth()]; - } else if (_c === 'I') { - return pad(hours12(_date), padding); - } else if (_c === 'j') { + }, + 'I': function () { + return pad(hours12(_date), _padding); + }, + 'j': function () { var y = new Date(_date.getFullYear(), 0, 1); - day = Math.ceil((_date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + var day = Math.ceil((_date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); return pad(day, null, 3); - } else if (_c === 'k') { - return pad(_date.getHours(), padding == null ? ' ' : padding); - } else if (_c === 'L') { - return pad(Math.floor(_timestamp % 1000), null, 3); - } else if (_c === 'l') { - return pad(hours12(_date), padding == null ? ' ' : padding); - } else if (_c === 'M') { - return pad(_date.getMinutes(), padding); - } else if (_c === 'm') { - return pad(_date.getMonth() + 1, padding); - } else if (_c === 'n') { + }, + 'k': function () { + return pad(_date.getHours(), _padding == null ? ' ' : _padding); + }, + 'L': function () { + return pad(Math.floor(_date.getTime() % 1000), null, 3); + }, + 'l': function () { + return pad(hours12(_date), _padding == null ? ' ' : _padding); + }, + 'M': function () { + return pad(_date.getMinutes(), _padding); + }, + 'm': function () { + return pad(_date.getMonth() + 1, _padding); + }, + 'n': function () { return '\n'; - } else if (_c === 'o') { + }, + 'o': function () { return String(_date.getDate()) + ordinal(_date.getDate()); - } else if (_c === 'P') { + }, + 'P': function () { return _date.getHours() < 12 ? _locale.am : _locale.pm; - } else if (_c === 'p') { + }, + 'p': function () { return _date.getHours() < 12 ? _locale.AM : _locale.PM; - } else if (_c === 'R') { + }, + 'R': function () { return _strftime(_locale.formats.R || '%H:%M', _date, _locale); - } else if (_c === 'r') { + }, + 'r': function () { return _strftime(_locale.formats.r || '%I:%M:%S %p', _date, _locale); - } else if (_c === 'S') { - return pad(_date.getSeconds(), padding); - } else if (_c === 's') { - return Math.floor(timestamp / 1000); - } else if (_c === 'T') { + }, + 'S': function () { + return pad(_date.getSeconds(), _padding); + }, + 's': function () { + return Math.floor(_date.getTime() / 1000); + }, + 'T': function () { return _strftime(_locale.formats.T || '%H:%M:%S', _date, _locale); - } else if (_c === 't') { + }, + 't': function () { return '\t'; - } else if (_c === 'U') { - return pad(weekNumber(_date, 'sunday'), padding); - } else if (_c === 'u') { - day = _date.getDay(); + }, + 'U': function () { + return pad(weekNumber(_date, 'sunday'), _padding); + }, + 'u': function () { + var day = _date.getDay(); return day === 0 ? 7 : day; - } else if (_c === 'v') { + }, + 'v': function () { return _strftime(_locale.formats.v || '%e-%b-%Y', _date, _locale); - } else if (_c === 'W') { - return pad(weekNumber(_date, 'monday'), padding); - } else if (_c === 'w') { + }, + 'W': function () { + return pad(weekNumber(_date, 'monday'), _padding); + }, + 'w': function () { return _date.getDay(); - } else if (_c === 'Y') { + }, + 'Y': function () { return _date.getFullYear(); - } else if (_c === 'y') { + }, + 'y': function () { return String(_date.getFullYear()).slice(-2); - } else if (_c === 'Z') { + }, + 'Z': function () { if (_options.utc) { return 'GMT'; } else { var tzString = _date.toString().match(/\((\w+)\)/); return tzString && tzString[1] || ''; } - } else if (_c === 'z') { + }, + 'z': function () { if (_options.utc) { return '+0000'; } else { var off = typeof _timeZone === 'number' ? _timeZone : -_date.getTimezoneOffset(); return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); } - } else { - return _c; } + }; + + function processing(_, p, c) { + var _padding; + + if (p) { + switch (p) { + // omit padding + case '-': + _padding = ''; + break; + + // pad with space + case '_': + _padding = ' '; + break; + + // pad with zero + case '0': + _padding = '0'; + break; + + // unrecognized, return the format + default: + return _; + } + } + + return mask[c] ? mask[c]() : c; } - // TODO: Did not inline quacksLikeDate called from _strftime (target requires context change). function quacksLikeDate(x) { var index = RequiredDateMethods.length; @@ -473,7 +272,7 @@ } function dateToUTC(d) { - new Date(d.getTime() + (d.getTimezoneOffset() || 0) * 60000); + return new Date(d.getTime() + (d.getTimezoneOffset() || 0) * 60000); } // Default padding is '0' and default length is 2, both are optional. @@ -544,9 +343,36 @@ return Math.floor((yDay + 7 - wDay) / 7); } + function strftime(fmt, d, locale) { + return _strftime(fmt, d, locale); + } + namespace.strftime = strftime; - namespace.strftimeTZ = strftime.strftimeTZ = strfTimeTZ; - namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC; - namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime; + + namespace.strftimeTZ = strftime.strftimeTZ = function strfTimeTZ(fmt, d, locale, timezone) { + var _locale = locale; + var _timezone = timezone; + + if ((typeof locale === 'number' || typeof locale === 'string') && timezone == null) { + _timezone = locale; + _locale = undefined; + } + + return _strftime(fmt, d, _locale, { + timezone: _timezone + }); + }; + + namespace.strftimeUTC = strftime.strftimeUTC = function strftimeUTC(fmt, d, locale) { + return _strftime(fmt, d, locale, { + utc: true + }); + }; + + namespace.localizedStrftime = strftime.localizedStrftime = function localizedStrftime(locale) { + return function (fmt, d, options) { + return _strftime(fmt, d, locale, options); + }; + }; }()); From 4090402e2b837098f0f93fd11907ac4a6e24705b Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Sat, 10 May 2014 23:49:17 +0400 Subject: [PATCH 05/15] Optimize for V8 --- strftime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strftime.js b/strftime.js index c3689ed..9bbd55c 100644 --- a/strftime.js +++ b/strftime.js @@ -82,7 +82,7 @@ _date = dateToUTC(_date); } - /*if (_timeZone) { + if (_timeZone) { // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. if (tzType === 'string') { @@ -94,7 +94,7 @@ } _date = new Date(_date.getTime() + (_timeZone * 60000)); - }*/ + } // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 From 51af2bd52aff37c786b75079d17e60a3e4f815b3 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Sat, 10 May 2014 23:49:59 +0400 Subject: [PATCH 06/15] Refactoring --- strftime.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/strftime.js b/strftime.js index 9bbd55c..d0cc2e4 100644 --- a/strftime.js +++ b/strftime.js @@ -96,10 +96,6 @@ _date = new Date(_date.getTime() + (_timeZone * 60000)); } - // Most of the specifiers supported by C's strftime, and some from Ruby. - // Some other syntax extensions from Ruby are supported: %-, %_, and %0 - // to pad with nothing, space, or zero (respectively). - return format.replace(/%([-_0]?)(.)/g, processing); } @@ -233,6 +229,9 @@ function processing(_, p, c) { var _padding; + // Most of the specifiers supported by C's strftime, and some from Ruby. + // Some other syntax extensions from Ruby are supported: %-, %_, and %0 + // to pad with nothing, space, or zero (respectively). if (p) { switch (p) { // omit padding From a2c640098f4208c5bcb148dab039088332d58884 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Mon, 12 May 2014 22:56:59 +0400 Subject: [PATCH 07/15] Refactoring --- strftime.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/strftime.js b/strftime.js index d0cc2e4..0cc1192 100644 --- a/strftime.js +++ b/strftime.js @@ -96,7 +96,7 @@ _date = new Date(_date.getTime() + (_timeZone * 60000)); } - return format.replace(/%([-_0]?)(.)/g, processing); + return format.replace(/%([-_0]?)([AaBbCDdeFHhIjkLlMmnoPpRrSsTtUuvWwYyZz])/g, processing); } var mask = { @@ -160,7 +160,8 @@ return '\n'; }, 'o': function () { - return String(_date.getDate()) + ordinal(_date.getDate()); + var date = _date.getDate(); + return String(date) + ordinal(date); }, 'P': function () { return _date.getHours() < 12 ? _locale.am : _locale.pm; @@ -255,7 +256,7 @@ } } - return mask[c] ? mask[c]() : c; + return mask[c](); } function quacksLikeDate(x) { From caf5c77eb9166c2037912e9021029b1436c83bb4 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Mon, 12 May 2014 23:13:19 +0400 Subject: [PATCH 08/15] Fixed the reference on padding --- strftime.js | 55 ++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/strftime.js b/strftime.js index 0cc1192..18d18e5 100644 --- a/strftime.js +++ b/strftime.js @@ -51,7 +51,6 @@ var _locale; var _date; var _timeZone; - var _padding; // d, locale, and options are optional, but you can't leave // holes in the argument list. If you pass options you have to pass @@ -112,14 +111,14 @@ 'b': function () { return _locale.shortMonths[_date.getMonth()]; }, - 'C': function () { - return pad(Math.floor(_date.getFullYear() / 100), _padding); + 'C': function (padding) { + return pad(Math.floor(_date.getFullYear() / 100), padding); }, 'D': function () { return _strftime(_locale.formats.D || '%m/%d/%y', _date, _locale); }, - 'd': function () { - return pad(_date.getDate(), _padding); + 'd': function (padding) { + return pad(_date.getDate(), padding); }, 'e': function () { return _date.getDate(); @@ -127,34 +126,34 @@ 'F': function () { return _strftime(_locale.formats.F || '%Y-%m-%d', _date, _locale); }, - 'H': function () { - return pad(_date.getHours(), _padding); + 'H': function (padding) { + return pad(_date.getHours(), padding); }, 'h': function () { return _locale.shortMonths[_date.getMonth()]; }, - 'I': function () { - return pad(hours12(_date), _padding); + 'I': function (padding) { + return pad(hours12(_date), padding); }, 'j': function () { var y = new Date(_date.getFullYear(), 0, 1); var day = Math.ceil((_date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); return pad(day, null, 3); }, - 'k': function () { - return pad(_date.getHours(), _padding == null ? ' ' : _padding); + 'k': function (padding) { + return pad(_date.getHours(), padding == null ? ' ' : padding); }, 'L': function () { return pad(Math.floor(_date.getTime() % 1000), null, 3); }, - 'l': function () { - return pad(hours12(_date), _padding == null ? ' ' : _padding); + 'l': function (padding) { + return pad(hours12(_date), padding == null ? ' ' : padding); }, - 'M': function () { - return pad(_date.getMinutes(), _padding); + 'M': function (padding) { + return pad(_date.getMinutes(), padding); }, - 'm': function () { - return pad(_date.getMonth() + 1, _padding); + 'm': function (padding) { + return pad(_date.getMonth() + 1, padding); }, 'n': function () { return '\n'; @@ -175,8 +174,8 @@ 'r': function () { return _strftime(_locale.formats.r || '%I:%M:%S %p', _date, _locale); }, - 'S': function () { - return pad(_date.getSeconds(), _padding); + 'S': function (padding) { + return pad(_date.getSeconds(), padding); }, 's': function () { return Math.floor(_date.getTime() / 1000); @@ -187,8 +186,8 @@ 't': function () { return '\t'; }, - 'U': function () { - return pad(weekNumber(_date, 'sunday'), _padding); + 'U': function (padding) { + return pad(weekNumber(_date, 'sunday'), padding); }, 'u': function () { var day = _date.getDay(); @@ -197,8 +196,8 @@ 'v': function () { return _strftime(_locale.formats.v || '%e-%b-%Y', _date, _locale); }, - 'W': function () { - return pad(weekNumber(_date, 'monday'), _padding); + 'W': function (padding) { + return pad(weekNumber(_date, 'monday'), padding); }, 'w': function () { return _date.getDay(); @@ -228,7 +227,7 @@ }; function processing(_, p, c) { - var _padding; + var padding; // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 @@ -237,17 +236,17 @@ switch (p) { // omit padding case '-': - _padding = ''; + padding = ''; break; // pad with space case '_': - _padding = ' '; + padding = ' '; break; // pad with zero case '0': - _padding = '0'; + padding = '0'; break; // unrecognized, return the format @@ -256,7 +255,7 @@ } } - return mask[c](); + return mask[c](padding); } function quacksLikeDate(x) { From 13028bcde7bdd4aeb31259b3da18fb7a2b961a34 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 00:09:21 +0400 Subject: [PATCH 09/15] Fixed code style --- test/test.js | 216 +++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/test/test.js b/test/test.js index 195187f..7f509ac 100755 --- a/test/test.js +++ b/test/test.js @@ -12,43 +12,43 @@ var assert = require('assert') , lib = require(libFilename) // Tue, 07 Jun 2011 18:51:45 GMT - , Time = new Date(1307472705067) + , Time = new Date(1307472705067); assert.fn = function(value, msg) { assert.equal('function', typeof value, msg) -} +}; assert.format = function(format, expected, expectedUTC, time) { - time = time || Time + time = time || Time; function _assertFmt(expected, name) { - name = name || 'strftime' - var actual = lib[name](format, time) + name = name || 'strftime'; + var actual = lib[name](format, time); assert.equal(expected, actual, name + '("' + format + '", ' + time + ') is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected)) } - if (expected) _assertFmt(expected, 'strftime') + if (expected) _assertFmt(expected, 'strftime'); _assertFmt(expectedUTC || expected, 'strftimeUTC') -} +}; /// check exports -assert.fn(lib.strftime) -assert.fn(lib.strftimeUTC) -assert.fn(lib.localizedStrftime) -ok('Exports') +assert.fn(lib.strftime); +assert.fn(lib.strftimeUTC); +assert.fn(lib.localizedStrftime); +ok('Exports'); /// time zones if (process.env.TZ == 'America/Vancouver') { - testTimezone('P[DS]T') - assert.format('%C', '01', '01', new Date(100, 0, 1)) - assert.format('%j', '097', '098', new Date(1365390736236)) + testTimezone('P[DS]T'); + assert.format('%C', '01', '01', new Date(100, 0, 1)); + assert.format('%j', '097', '098', new Date(1365390736236)); ok('Time zones (' + process.env.TZ + ')') } else if (process.env.TZ == 'CET') { - testTimezone('CES?T') - assert.format('%C', '01', '00', new Date(100, 0, 1)) - assert.format('%j', '098', '098', new Date(1365390736236)) + testTimezone('CES?T'); + assert.format('%C', '01', '00', new Date(100, 0, 1)); + assert.format('%j', '098', '098', new Date(1365390736236)); ok('Time zones (' + process.env.TZ + ')') } else { @@ -56,56 +56,56 @@ else { } /// check all formats in GMT, most coverage -assert.format('%A', 'Tuesday') -assert.format('%a', 'Tue') -assert.format('%B', 'June') -assert.format('%b', 'Jun') -assert.format('%C', '20') -assert.format('%D', '06/07/11') -assert.format('%d', '07') -assert.format('%-d', '7') -assert.format('%_d', ' 7') -assert.format('%0d', '07') -assert.format('%e', '7') -assert.format('%F', '2011-06-07') -assert.format('%H', null, '18') -assert.format('%h', 'Jun') -assert.format('%I', null, '06') -assert.format('%-I', null, '6') -assert.format('%_I', null, ' 6') -assert.format('%0I', null, '06') -assert.format('%j', null, '158') -assert.format('%k', null, '18') -assert.format('%L', '067') -assert.format('%l', null, ' 6') -assert.format('%-l', null, '6') -assert.format('%_l', null, ' 6') -assert.format('%0l', null, '06') -assert.format('%M', null, '51') -assert.format('%m', '06') -assert.format('%n', '\n') -assert.format('%o', '7th') -assert.format('%P', null, 'pm') -assert.format('%p', null, 'PM') -assert.format('%R', null, '18:51') -assert.format('%r', null, '06:51:45 PM') -assert.format('%S', '45') -assert.format('%s', '1307472705') -assert.format('%T', null, '18:51:45') -assert.format('%t', '\t') -assert.format('%U', '23') -assert.format('%U', '24', null, new Date(+Time + 5 * 86400000)) -assert.format('%u', '2') -assert.format('%v', '7-Jun-2011') -assert.format('%W', '23') -assert.format('%W', '23', null, new Date(+Time + 5 * 86400000)) -assert.format('%w', '2') -assert.format('%Y', '2011') -assert.format('%y', '11') -assert.format('%Z', null, 'GMT') -assert.format('%z', null, '+0000') -assert.format('%%', '%') // any other char -ok('GMT') +assert.format('%A', 'Tuesday'); +assert.format('%a', 'Tue'); +assert.format('%B', 'June'); +assert.format('%b', 'Jun'); +assert.format('%C', '20'); +assert.format('%D', '06/07/11'); +assert.format('%d', '07'); +assert.format('%-d', '7'); +assert.format('%_d', ' 7'); +assert.format('%0d', '07'); +assert.format('%e', '7'); +assert.format('%F', '2011-06-07'); +assert.format('%H', null, '18'); +assert.format('%h', 'Jun'); +assert.format('%I', null, '06'); +assert.format('%-I', null, '6'); +assert.format('%_I', null, ' 6'); +assert.format('%0I', null, '06'); +assert.format('%j', null, '158'); +assert.format('%k', null, '18'); +assert.format('%L', '067'); +assert.format('%l', null, ' 6'); +assert.format('%-l', null, '6'); +assert.format('%_l', null, ' 6'); +assert.format('%0l', null, '06'); +assert.format('%M', null, '51'); +assert.format('%m', '06'); +assert.format('%n', '\n'); +assert.format('%o', '7th'); +assert.format('%P', null, 'pm'); +assert.format('%p', null, 'PM'); +assert.format('%R', null, '18:51'); +assert.format('%r', null, '06:51:45 PM'); +assert.format('%S', '45'); +assert.format('%s', '1307472705'); +assert.format('%T', null, '18:51:45'); +assert.format('%t', '\t'); +assert.format('%U', '23'); +assert.format('%U', '24', null, new Date(+Time + 5 * 86400000)); +assert.format('%u', '2'); +assert.format('%v', '7-Jun-2011'); +assert.format('%W', '23'); +assert.format('%W', '23', null, new Date(+Time + 5 * 86400000)); +assert.format('%w', '2'); +assert.format('%Y', '2011'); +assert.format('%y', '11'); +assert.format('%Z', null, 'GMT'); +assert.format('%z', null, '+0000'); +assert.format('%%', '%'); // any other char +ok('GMT'); /// locales @@ -127,54 +127,54 @@ var it_IT = , T: 'it$%H:%M:%S' , v: 'it$%e-%b-%Y' } -} +}; assert.format_it = function(format, expected, expectedUTC) { function _assertFmt(expected, name) { - name = name || 'strftime' - var actual = lib[name](format, Time, it_IT) + name = name || 'strftime'; + var actual = lib[name](format, Time, it_IT); assert.equal(expected, actual, name + '("' + format + '", Time) is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected)) } - if (expected) _assertFmt(expected, 'strftime') + if (expected) _assertFmt(expected, 'strftime'); _assertFmt(expectedUTC || expected, 'strftimeUTC') -} +}; -assert.format_it('%A', 'martedi') -assert.format_it('%a', 'mar') -assert.format_it('%B', 'giugno') -assert.format_it('%b', 'giu') -assert.format_it('%D', 'it$06/07/11') -assert.format_it('%F', 'it$2011-06-07') -assert.format_it('%p', null, 'it$PM') -assert.format_it('%P', null, 'it$pm') -assert.format_it('%R', null, 'it$18:51') -assert.format_it('%r', null, 'it$06:51:45 it$PM') -assert.format_it('%T', null, 'it$18:51:45') -assert.format_it('%v', 'it$7-giu-2011') -ok('Localization') +assert.format_it('%A', 'martedi'); +assert.format_it('%a', 'mar'); +assert.format_it('%B', 'giugno'); +assert.format_it('%b', 'giu'); +assert.format_it('%D', 'it$06/07/11'); +assert.format_it('%F', 'it$2011-06-07'); +assert.format_it('%p', null, 'it$PM'); +assert.format_it('%P', null, 'it$pm'); +assert.format_it('%R', null, 'it$18:51'); +assert.format_it('%r', null, 'it$06:51:45 it$PM'); +assert.format_it('%T', null, 'it$18:51:45'); +assert.format_it('%v', 'it$7-giu-2011'); +ok('Localization'); /// timezones assert.formatTZ = function(format, expected, tz, time) { time = time || Time; - var actual = lib.strftimeTZ(format, time, tz) + var actual = lib.strftimeTZ(format, time, tz); assert.equal( expected, actual, ('strftime("' + format + '", ' + time + ') is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected)) ) -} +}; -assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', 0) -assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', '+0000') -assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', 120) -assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', '+0200') -assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', -420) -assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', '-0700') -ok('Time zone offset') +assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', 0); +assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', '+0000'); +assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', 120); +assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', '+0200'); +assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', -420); +assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', '-0700'); +ok('Time zone offset'); /// helpers @@ -187,8 +187,8 @@ function ok(s) { console.log('[ \033[32mOK\033[0m ] ' + s) } // Don't pass GMT! Every date includes it and it will fail. // Be careful if you pass a regex, it has to quack like the default one. function testTimezone(regex) { - regex = typeof regex === 'string' ? RegExp('\\((' + regex + ')\\)$') : regex - var match = Time.toString().match(regex) + regex = typeof regex === 'string' ? new RegExp('\\((' + regex + ')\\)$') : regex; + var match = Time.toString().match(regex); if (match) { var off = Time.getTimezoneOffset() , hourOff = off / 60 @@ -207,18 +207,18 @@ function testTimezone(regex) { , ampm = hour12 == hour24 ? 'AM' : 'PM' , R = hour24 + ':' + mins , r = padZero12 + hour12 + ':' + mins + ':45 ' + ampm - , T = R + ':45' - assert.format('%H', padZero24 + hour24, '18') - assert.format('%I', padZero12 + hour12, '06') - assert.format('%k', padSpace24 + hour24, '18') - assert.format('%l', padSpace12 + hour12, ' 6') - assert.format('%M', mins) - assert.format('%P', ampm.toLowerCase(), 'pm') - assert.format('%p', ampm, 'PM') - assert.format('%R', R, '18:51') - assert.format('%r', r, '06:51:45 PM') - assert.format('%T', T, '18:51:45') - assert.format('%Z', tz, 'GMT') + , T = R + ':45'; + assert.format('%H', padZero24 + hour24, '18'); + assert.format('%I', padZero12 + hour12, '06'); + assert.format('%k', padSpace24 + hour24, '18'); + assert.format('%l', padSpace12 + hour12, ' 6'); + assert.format('%M', mins); + assert.format('%P', ampm.toLowerCase(), 'pm'); + assert.format('%p', ampm, 'PM'); + assert.format('%R', R, '18:51'); + assert.format('%r', r, '06:51:45 PM'); + assert.format('%T', T, '18:51:45'); + assert.format('%Z', tz, 'GMT'); assert.format('%z', sign + '0' + Math.abs(hourDiff) + '00', '+0000') } } From 895a3361b0b3a90f18d5ad465c8e6a00593a2520 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 01:56:37 +0400 Subject: [PATCH 10/15] Fixed tests --- strftime.js | 280 ++++++++++++++++++++++++++-------------------------- 1 file changed, 138 insertions(+), 142 deletions(-) diff --git a/strftime.js b/strftime.js index 18d18e5..22b418c 100644 --- a/strftime.js +++ b/strftime.js @@ -11,8 +11,9 @@ ; (function () { - //// Where to export the API + // Where to export the API var namespace; + var toString = Object.prototype.toString; try { // CommonJS / Node module @@ -34,24 +35,6 @@ pm: 'pm' }; - var RequiredDateMethods = [ - 'getTime', - 'getTimezoneOffset', - 'getDay', - 'getDate', - 'getMonth', - 'getFullYear', - 'getYear', - 'getHours', - 'getMinutes', - 'getSeconds' - ]; - - var _options; - var _locale; - var _date; - var _timeZone; - // d, locale, and options are optional, but you can't leave // holes in the argument list. If you pass options you have to pass // in all the preceding args as well. @@ -59,181 +42,174 @@ // options: // - locale [object] an object with the same structure as DefaultLocale // - timezone [number] timezone offset in minutes from GMT - function _strftime(format, d, locale, options) { - _options = options || {}; - _locale = locale; - _date = d; - _timeZone = _options.timezone; + function _strftime(format, date, locale, options) { + var matches; + var _options = options || {}; + var _locale = locale; + var _date = date; + var timestamp; + var index = 0; + var result = ''; + var regExp = /%([-_0]?)(.)/g; - // d and locale are optional so check if d is really the locale - if (_date && !quacksLikeDate(_date)) { + // date and locale are optional so check if date is really the locale + if (_date && !isDate(_date)) { _locale = _date; _date = undefined; } _date = _date || new Date(); + timestamp = _date.getTime(); _locale = _locale || DefaultLocale; _locale.formats = _locale.formats || {}; - var tzType = typeof _timeZone; + _date = fixTimeZone(_date, _options); - if (_options.utc || tzType === 'number' || tzType === 'string') { - _date = dateToUTC(_date); + while (matches = regExp.exec(format)) { + result += format.substring(index, matches.index) + processing(matches, _date, _locale, timestamp, _options); + index = matches.index + matches[0].length; } - if (_timeZone) { - // ISO 8601 format timezone string, [-+]HHMM - // Convert to the number of minutes and it'll be applied to the date below. - if (tzType === 'string') { - var sign = _timeZone[0] === '-' ? -1 : 1; - var hours = parseInt(_timeZone.slice(1, 3), 10); - var mins = parseInt(_timeZone.slice(3, 5), 10); - - _timeZone = sign * (60 * hours) + mins; - } - - _date = new Date(_date.getTime() + (_timeZone * 60000)); - } - - return format.replace(/%([-_0]?)([AaBbCDdeFHhIjkLlMmnoPpRrSsTtUuvWwYyZz])/g, processing); + return result; } var mask = { - 'A': function () { - return _locale.days[_date.getDay()]; + 'A': function (padding, date, locale) { + return locale.days[date.getDay()]; }, - 'a': function () { - return _locale.shortDays[_date.getDay()]; + 'a': function (padding, date, locale) { + return locale.shortDays[date.getDay()]; }, - 'B': function () { - return _locale.months[_date.getMonth()]; + 'B': function (padding, date, locale) { + return locale.months[date.getMonth()]; }, - 'b': function () { - return _locale.shortMonths[_date.getMonth()]; + 'b': function (padding, date, locale) { + return locale.shortMonths[date.getMonth()]; }, - 'C': function (padding) { - return pad(Math.floor(_date.getFullYear() / 100), padding); + 'C': function (padding, date) { + return pad(Math.floor(date.getFullYear() / 100), padding); }, - 'D': function () { - return _strftime(_locale.formats.D || '%m/%d/%y', _date, _locale); + 'D': function (padding, date, locale) { + return _strftime(locale.formats.D || '%m/%d/%y', date, locale); }, - 'd': function (padding) { - return pad(_date.getDate(), padding); + 'd': function (padding, date) { + return pad(date.getDate(), padding); }, - 'e': function () { - return _date.getDate(); + 'e': function (padding, date) { + return date.getDate(); }, - 'F': function () { - return _strftime(_locale.formats.F || '%Y-%m-%d', _date, _locale); + 'F': function (padding, date, locale) { + return _strftime(locale.formats.F || '%Y-%m-%d', date, locale); }, - 'H': function (padding) { - return pad(_date.getHours(), padding); + 'H': function (padding, date) { + return pad(date.getHours(), padding); }, - 'h': function () { - return _locale.shortMonths[_date.getMonth()]; + 'h': function (padding, date, locale) { + return locale.shortMonths[date.getMonth()]; }, - 'I': function (padding) { - return pad(hours12(_date), padding); + 'I': function (padding, date) { + return pad(hours12(date), padding); }, - 'j': function () { - var y = new Date(_date.getFullYear(), 0, 1); - var day = Math.ceil((_date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + 'j': function (padding, date) { + var y = new Date(date.getFullYear(), 0, 1); + var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); return pad(day, null, 3); }, - 'k': function (padding) { - return pad(_date.getHours(), padding == null ? ' ' : padding); + 'k': function (padding, date) { + return pad(date.getHours(), padding == null ? ' ' : padding); }, - 'L': function () { - return pad(Math.floor(_date.getTime() % 1000), null, 3); + 'L': function (padding, date, locale, timestamp) { + return pad(Math.floor(timestamp % 1000), null, 3); }, - 'l': function (padding) { - return pad(hours12(_date), padding == null ? ' ' : padding); + 'l': function (padding, date) { + return pad(hours12(date), padding == null ? ' ' : padding); }, - 'M': function (padding) { - return pad(_date.getMinutes(), padding); + 'M': function (padding, date) { + return pad(date.getMinutes(), padding); }, - 'm': function (padding) { - return pad(_date.getMonth() + 1, padding); + 'm': function (padding, date) { + return pad(date.getMonth() + 1, padding); }, 'n': function () { return '\n'; }, - 'o': function () { - var date = _date.getDate(); - return String(date) + ordinal(date); + 'o': function (padding, date) { + var _date = date.getDate(); + return String(_date) + ordinal(_date); }, - 'P': function () { - return _date.getHours() < 12 ? _locale.am : _locale.pm; + 'P': function (padding, date, locale) { + return date.getHours() < 12 ? locale.am : locale.pm; }, - 'p': function () { - return _date.getHours() < 12 ? _locale.AM : _locale.PM; + 'p': function (padding, date, locale) { + return date.getHours() < 12 ? locale.AM : locale.PM; }, - 'R': function () { - return _strftime(_locale.formats.R || '%H:%M', _date, _locale); + 'R': function (padding, date, locale) { + return _strftime(locale.formats.R || '%H:%M', date, locale); }, - 'r': function () { - return _strftime(_locale.formats.r || '%I:%M:%S %p', _date, _locale); + 'r': function (padding, date, locale) { + return _strftime(locale.formats.r || '%I:%M:%S %p', date, locale); }, - 'S': function (padding) { - return pad(_date.getSeconds(), padding); + 'S': function (padding, date) { + return pad(date.getSeconds(), padding); }, - 's': function () { - return Math.floor(_date.getTime() / 1000); + 's': function (padding, date, locale, timestamp) { + return Math.floor(timestamp / 1000); }, - 'T': function () { - return _strftime(_locale.formats.T || '%H:%M:%S', _date, _locale); + 'T': function (padding, date, locale) { + return _strftime(locale.formats.T || '%H:%M:%S', date, locale); }, 't': function () { return '\t'; }, - 'U': function (padding) { - return pad(weekNumber(_date, 'sunday'), padding); + 'U': function (padding, date) { + return pad(weekNumber(date, 'sunday'), padding); }, - 'u': function () { - var day = _date.getDay(); + 'u': function (padding, date) { + var day = date.getDay(); return day === 0 ? 7 : day; }, - 'v': function () { - return _strftime(_locale.formats.v || '%e-%b-%Y', _date, _locale); + 'v': function (padding, date, locale) { + return _strftime(locale.formats.v || '%e-%b-%Y', date, locale); }, - 'W': function (padding) { - return pad(weekNumber(_date, 'monday'), padding); + 'W': function (padding, date) { + return pad(weekNumber(date, 'monday'), padding); }, - 'w': function () { - return _date.getDay(); + 'w': function (padding, date) { + return date.getDay(); }, - 'Y': function () { - return _date.getFullYear(); + 'Y': function (padding, date) { + return date.getFullYear(); }, - 'y': function () { - return String(_date.getFullYear()).slice(-2); + 'y': function (padding, date) { + return String(date.getFullYear()).slice(-2); }, - 'Z': function () { - if (_options.utc) { + 'Z': function (padding, date, locale, timestamp, options) { + if (options.utc) { return 'GMT'; } else { - var tzString = _date.toString().match(/\((\w+)\)/); + var tzString = date.toString().match(/\((\w+)\)/); return tzString && tzString[1] || ''; } }, - 'z': function () { - if (_options.utc) { + 'z': function (padding, date, locale, timestamp, options) { + if (options.utc) { return '+0000'; } else { - var off = typeof _timeZone === 'number' ? _timeZone : -_date.getTimezoneOffset(); + var off = typeof options.timezone === 'number' ? options.timezone : -date.getTimezoneOffset(); return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); } } }; - function processing(_, p, c) { - var padding; + function processing(match, date, locale, timestamp, options) { + var padding = match[1]; + var char = match[2]; // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 // to pad with nothing, space, or zero (respectively). - if (p) { - switch (p) { + if (padding) { + switch (padding) { // omit padding case '-': padding = ''; @@ -246,28 +222,17 @@ // pad with zero case '0': - padding = '0'; break; // unrecognized, return the format default: - return _; + return match[0]; } + } else { + padding = null; } - return mask[c](padding); - } - - function quacksLikeDate(x) { - var index = RequiredDateMethods.length; - - while (index--) { - if (typeof x[RequiredDateMethods[index]] !== 'function') { - return false; - } - } - - return true; + return mask[char] ? mask[char](padding, date, locale, timestamp, options) : char; } function dateToUTC(d) { @@ -346,19 +311,50 @@ return _strftime(fmt, d, locale); } + function isDate(date) { + return toString.call(date) === '[object Date]'; + } + + // ISO 8601 format timezone string, [-+]HHMM + // Convert to the number of minutes and it'll be applied to the date below. + function fixTimeZone(date, options) { + var _date = date; + var timeZone = options.timezone; + var tzType = typeof timeZone; + + if (options.utc || tzType === 'number' || tzType === 'string') { + _date = dateToUTC(_date); + } + + if (timeZone) { + if (tzType === 'string') { + var sign = timeZone[0] === '-' ? -1 : 1; + var hours = parseInt(timeZone.slice(1, 3), 10); + var mins = parseInt(timeZone.slice(3, 5), 10); + + timeZone = sign * (60 * hours) + mins; + } + + _date = new Date(_date.getTime() + (timeZone * 60000)); + options.timezone = timeZone; + } + + return _date; + } + namespace.strftime = strftime; - namespace.strftimeTZ = strftime.strftimeTZ = function strfTimeTZ(fmt, d, locale, timezone) { + namespace.strftimeTZ = strftime.strftimeTZ = function strfTimeTZ(fmt, d, locale, timeZone) { var _locale = locale; - var _timezone = timezone; + var _timeZone = timeZone; - if ((typeof locale === 'number' || typeof locale === 'string') && timezone == null) { - _timezone = locale; + if ((typeof locale === 'number' || typeof locale === 'string') && timeZone == null) { + _timeZone = locale; _locale = undefined; } return _strftime(fmt, d, _locale, { - timezone: _timezone + timezone: _timeZone }); }; From 05700a7dedf871d0abf823f56cbe3c57f1f61d34 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 02:22:02 +0400 Subject: [PATCH 11/15] Optimization of inlining --- strftime.js | 102 +++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 58 deletions(-) diff --git a/strftime.js b/strftime.js index 22b418c..8f03250 100644 --- a/strftime.js +++ b/strftime.js @@ -43,34 +43,29 @@ // - locale [object] an object with the same structure as DefaultLocale // - timezone [number] timezone offset in minutes from GMT function _strftime(format, date, locale, options) { - var matches; - var _options = options || {}; - var _locale = locale; - var _date = date; - var timestamp; - var index = 0; - var result = ''; - var regExp = /%([-_0]?)(.)/g; + var m; + var o = options || {}; + var l = locale; + var d = date; + var i = 0; + var r = ''; + var re = /%([-_0]?)(.)/g; - // date and locale are optional so check if date is really the locale - if (_date && !isDate(_date)) { - _locale = _date; - _date = undefined; + if (d && !isDate(d)) { + l = d; + d = null; } - _date = _date || new Date(); - timestamp = _date.getTime(); - _locale = _locale || DefaultLocale; - _locale.formats = _locale.formats || {}; + l = l || DefaultLocale; + l.formats = l.formats || {}; + d = fixTimeZone(d || new Date(), o); - _date = fixTimeZone(_date, _options); - - while (matches = regExp.exec(format)) { - result += format.substring(index, matches.index) + processing(matches, _date, _locale, timestamp, _options); - index = matches.index + matches[0].length; + while (m = re.exec(format)) { + r += format.substring(i, m.index) + match(m, d, l, d.getTime(), o); + i = m.index + m[0].length; } - return result; + return r; } var mask = { @@ -201,38 +196,29 @@ } }; - function processing(match, date, locale, timestamp, options) { - var padding = match[1]; - var char = match[2]; + // Most of the specifiers supported by C's strftime, and some from Ruby. + // Some other syntax extensions from Ruby are supported: %-, %_, and %0 + // to pad with nothing, space, or zero (respectively). + function match(match, date, locale, timestamp, options) { + var p = match[1]; + var c = match[2]; - // Most of the specifiers supported by C's strftime, and some from Ruby. - // Some other syntax extensions from Ruby are supported: %-, %_, and %0 - // to pad with nothing, space, or zero (respectively). - if (padding) { - switch (padding) { - // omit padding + if (p) { + switch (p) { case '-': - padding = ''; + p = ''; break; - - // pad with space case '_': - padding = ' '; + p = ' '; break; - - // pad with zero case '0': break; - - // unrecognized, return the format default: return match[0]; } - } else { - padding = null; } - return mask[char] ? mask[char](padding, date, locale, timestamp, options) : char; + return mask[c] ? mask[c](p, date, locale, timestamp, options) : c; } function dateToUTC(d) { @@ -241,7 +227,7 @@ // Default padding is '0' and default length is 2, both are optional. function pad(n, padding, length) { - var _padding = padding == null ? '0' : padding; + var _padding = padding ? '0' : padding; var _n = String(n); var _length = length || 2; @@ -317,29 +303,29 @@ // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. - function fixTimeZone(date, options) { - var _date = date; - var timeZone = options.timezone; - var tzType = typeof timeZone; + function fixTimeZone(date, opt) { + var d = date; + var tz = opt.timezone; + var tzType = typeof tz; - if (options.utc || tzType === 'number' || tzType === 'string') { - _date = dateToUTC(_date); + if (opt.utc || tzType == 'number' || tzType == 'string') { + d = dateToUTC(d); } - if (timeZone) { - if (tzType === 'string') { - var sign = timeZone[0] === '-' ? -1 : 1; - var hours = parseInt(timeZone.slice(1, 3), 10); - var mins = parseInt(timeZone.slice(3, 5), 10); + if (tz) { + if (tzType == 'string') { + var s = tz[0] === '-' ? -1 : 1; + var h = parseInt(tz.slice(1, 3), 10); + var m = parseInt(tz.slice(3, 5), 10); - timeZone = sign * (60 * hours) + mins; + tz = s * (60 * h) + m; } - _date = new Date(_date.getTime() + (timeZone * 60000)); - options.timezone = timeZone; + d = new Date(d.getTime() + (tz * 60000)); + opt.timezone = tz; } - return _date; + return d; } namespace.strftime = strftime; From 0af3e1ed40cfee9183f89faf15b84c93934ffcde Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 02:28:07 +0400 Subject: [PATCH 12/15] Fixed tests --- strftime.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/strftime.js b/strftime.js index 8f03250..31f58f6 100644 --- a/strftime.js +++ b/strftime.js @@ -43,12 +43,10 @@ // - locale [object] an object with the same structure as DefaultLocale // - timezone [number] timezone offset in minutes from GMT function _strftime(format, date, locale, options) { - var m; var o = options || {}; var l = locale; var d = date; - var i = 0; - var r = ''; + var m, ts, i = 0, r = ''; var re = /%([-_0]?)(.)/g; if (d && !isDate(d)) { @@ -58,10 +56,12 @@ l = l || DefaultLocale; l.formats = l.formats || {}; - d = fixTimeZone(d || new Date(), o); + d = d || new Date(); + ts = d.getTime(); + d = fixTZ(d, o); while (m = re.exec(format)) { - r += format.substring(i, m.index) + match(m, d, l, d.getTime(), o); + r += format.substring(i, m.index) + match(m, d, l, ts, o); i = m.index + m[0].length; } @@ -216,6 +216,8 @@ default: return match[0]; } + } else { + p = null; } return mask[c] ? mask[c](p, date, locale, timestamp, options) : c; @@ -227,7 +229,7 @@ // Default padding is '0' and default length is 2, both are optional. function pad(n, padding, length) { - var _padding = padding ? '0' : padding; + var _padding = padding == null ? '0' : padding; var _n = String(n); var _length = length || 2; @@ -303,7 +305,7 @@ // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. - function fixTimeZone(date, opt) { + function fixTZ(date, opt) { var d = date; var tz = opt.timezone; var tzType = typeof tz; From 785611bffa7626f463bb8f3f9c39ed109a29ea3f Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 02:32:23 +0400 Subject: [PATCH 13/15] Refactoring --- strftime.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/strftime.js b/strftime.js index 31f58f6..eed559a 100644 --- a/strftime.js +++ b/strftime.js @@ -229,14 +229,14 @@ // Default padding is '0' and default length is 2, both are optional. function pad(n, padding, length) { - var _padding = padding == null ? '0' : padding; var _n = String(n); - var _length = length || 2; + var _p = padding == null ? '0' : padding; + var _l = length || 2; // padding may be an empty string, don't loop forever if it is - if (_padding) { - while (_n.length < _length) { - _n = _padding + _n; + if (_p) { + while (_n.length < _l) { + _n = _p + _n; } } From bef84d6988f6997e790173f1d48eb1e6a3c3aec1 Mon Sep 17 00:00:00 2001 From: B~Vladi Date: Thu, 15 May 2014 11:26:42 +0400 Subject: [PATCH 14/15] Bugfix --- strftime.js | 620 ++++++++++++++++++++++++++-------------------------- 1 file changed, 310 insertions(+), 310 deletions(-) diff --git a/strftime.js b/strftime.js index eed559a..df9aeb4 100644 --- a/strftime.js +++ b/strftime.js @@ -11,351 +11,351 @@ ; (function () { - // Where to export the API - var namespace; - var toString = Object.prototype.toString; +// Where to export the API +var namespace; +var toString = Object.prototype.toString; - try { - // CommonJS / Node module - namespace = module.exports = strftime; - } catch (error) { - // Browsers and other environments - // Get the global object. Works in ES3, ES5, and ES5 strict mode. - namespace = new Function('return this')(); +try { + // CommonJS / Node module + namespace = module.exports = strftime; +} catch (error) { + // Browsers and other environments + // Get the global object. Works in ES3, ES5, and ES5 strict mode. + namespace = new Function('return this')(); +} + +var DefaultLocale = { + days: 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), + shortDays: 'Sun Mon Tue Wed Thu Fri Sat'.split(' '), + months: 'January February March April May June July August September October November December'.split(' '), + shortMonths: 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), + AM: 'AM', + PM: 'PM', + am: 'am', + pm: 'pm' +}; + +// d, locale, and options are optional, but you can't leave +// holes in the argument list. If you pass options you have to pass +// in all the preceding args as well. +// +// options: +// - locale [object] an object with the same structure as DefaultLocale +// - timezone [number] timezone offset in minutes from GMT +function _strftime(format, date, locale, options) { + var o = options || {}; + var l = locale; + var d = date; + var m, ts, i = 0, r = ''; + var re = /%([-_0]?)(.)/g; + + if (d && !isDate(d)) { + l = d; + d = null; } - var DefaultLocale = { - days: 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), - shortDays: 'Sun Mon Tue Wed Thu Fri Sat'.split(' '), - months: 'January February March April May June July August September October November December'.split(' '), - shortMonths: 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), - AM: 'AM', - PM: 'PM', - am: 'am', - pm: 'pm' - }; + l = l || DefaultLocale; + l.formats = l.formats || {}; + d = d || new Date(); + ts = d.getTime(); + d = fixTZ(d, o); - // d, locale, and options are optional, but you can't leave - // holes in the argument list. If you pass options you have to pass - // in all the preceding args as well. - // - // options: - // - locale [object] an object with the same structure as DefaultLocale - // - timezone [number] timezone offset in minutes from GMT - function _strftime(format, date, locale, options) { - var o = options || {}; - var l = locale; - var d = date; - var m, ts, i = 0, r = ''; - var re = /%([-_0]?)(.)/g; - - if (d && !isDate(d)) { - l = d; - d = null; - } - - l = l || DefaultLocale; - l.formats = l.formats || {}; - d = d || new Date(); - ts = d.getTime(); - d = fixTZ(d, o); - - while (m = re.exec(format)) { - r += format.substring(i, m.index) + match(m, d, l, ts, o); - i = m.index + m[0].length; - } - - return r; + while (m = re.exec(format)) { + r += format.substring(i, m.index) + match(m, d, l, ts, o); + i = m.index + m[0].length; } - var mask = { - 'A': function (padding, date, locale) { - return locale.days[date.getDay()]; - }, - 'a': function (padding, date, locale) { - return locale.shortDays[date.getDay()]; - }, - 'B': function (padding, date, locale) { - return locale.months[date.getMonth()]; - }, - 'b': function (padding, date, locale) { - return locale.shortMonths[date.getMonth()]; - }, - 'C': function (padding, date) { - return pad(Math.floor(date.getFullYear() / 100), padding); - }, - 'D': function (padding, date, locale) { - return _strftime(locale.formats.D || '%m/%d/%y', date, locale); - }, - 'd': function (padding, date) { - return pad(date.getDate(), padding); - }, - 'e': function (padding, date) { - return date.getDate(); - }, - 'F': function (padding, date, locale) { - return _strftime(locale.formats.F || '%Y-%m-%d', date, locale); - }, - 'H': function (padding, date) { - return pad(date.getHours(), padding); - }, - 'h': function (padding, date, locale) { - return locale.shortMonths[date.getMonth()]; - }, - 'I': function (padding, date) { - return pad(hours12(date), padding); - }, - 'j': function (padding, date) { - var y = new Date(date.getFullYear(), 0, 1); - var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); - return pad(day, null, 3); - }, - 'k': function (padding, date) { - return pad(date.getHours(), padding == null ? ' ' : padding); - }, - 'L': function (padding, date, locale, timestamp) { - return pad(Math.floor(timestamp % 1000), null, 3); - }, - 'l': function (padding, date) { - return pad(hours12(date), padding == null ? ' ' : padding); - }, - 'M': function (padding, date) { - return pad(date.getMinutes(), padding); - }, - 'm': function (padding, date) { - return pad(date.getMonth() + 1, padding); - }, - 'n': function () { - return '\n'; - }, - 'o': function (padding, date) { - var _date = date.getDate(); - return String(_date) + ordinal(_date); - }, - 'P': function (padding, date, locale) { - return date.getHours() < 12 ? locale.am : locale.pm; - }, - 'p': function (padding, date, locale) { - return date.getHours() < 12 ? locale.AM : locale.PM; - }, - 'R': function (padding, date, locale) { - return _strftime(locale.formats.R || '%H:%M', date, locale); - }, - 'r': function (padding, date, locale) { - return _strftime(locale.formats.r || '%I:%M:%S %p', date, locale); - }, - 'S': function (padding, date) { - return pad(date.getSeconds(), padding); - }, - 's': function (padding, date, locale, timestamp) { - return Math.floor(timestamp / 1000); - }, - 'T': function (padding, date, locale) { - return _strftime(locale.formats.T || '%H:%M:%S', date, locale); - }, - 't': function () { - return '\t'; - }, - 'U': function (padding, date) { - return pad(weekNumber(date, 'sunday'), padding); - }, - 'u': function (padding, date) { - var day = date.getDay(); - return day === 0 ? 7 : day; - }, - 'v': function (padding, date, locale) { - return _strftime(locale.formats.v || '%e-%b-%Y', date, locale); - }, - 'W': function (padding, date) { - return pad(weekNumber(date, 'monday'), padding); - }, - 'w': function (padding, date) { - return date.getDay(); - }, - 'Y': function (padding, date) { - return date.getFullYear(); - }, - 'y': function (padding, date) { - return String(date.getFullYear()).slice(-2); - }, - 'Z': function (padding, date, locale, timestamp, options) { - if (options.utc) { - return 'GMT'; - } else { - var tzString = date.toString().match(/\((\w+)\)/); - return tzString && tzString[1] || ''; - } - }, - 'z': function (padding, date, locale, timestamp, options) { - if (options.utc) { - return '+0000'; - } else { - var off = typeof options.timezone === 'number' ? options.timezone : -date.getTimezoneOffset(); - return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); - } - } - }; + return r + format.substring(i); +} - // Most of the specifiers supported by C's strftime, and some from Ruby. - // Some other syntax extensions from Ruby are supported: %-, %_, and %0 - // to pad with nothing, space, or zero (respectively). - function match(match, date, locale, timestamp, options) { - var p = match[1]; - var c = match[2]; - - if (p) { - switch (p) { - case '-': - p = ''; - break; - case '_': - p = ' '; - break; - case '0': - break; - default: - return match[0]; - } +var mask = { + 'A': function (padding, date, locale) { + return locale.days[date.getDay()]; + }, + 'a': function (padding, date, locale) { + return locale.shortDays[date.getDay()]; + }, + 'B': function (padding, date, locale) { + return locale.months[date.getMonth()]; + }, + 'b': function (padding, date, locale) { + return locale.shortMonths[date.getMonth()]; + }, + 'C': function (padding, date) { + return pad(Math.floor(date.getFullYear() / 100), padding); + }, + 'D': function (padding, date, locale) { + return _strftime(locale.formats.D || '%m/%d/%y', date, locale); + }, + 'd': function (padding, date) { + return pad(date.getDate(), padding); + }, + 'e': function (padding, date) { + return date.getDate(); + }, + 'F': function (padding, date, locale) { + return _strftime(locale.formats.F || '%Y-%m-%d', date, locale); + }, + 'H': function (padding, date) { + return pad(date.getHours(), padding); + }, + 'h': function (padding, date, locale) { + return locale.shortMonths[date.getMonth()]; + }, + 'I': function (padding, date) { + return pad(hours12(date), padding); + }, + 'j': function (padding, date) { + var y = new Date(date.getFullYear(), 0, 1); + var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); + return pad(day, null, 3); + }, + 'k': function (padding, date) { + return pad(date.getHours(), padding == null ? ' ' : padding); + }, + 'L': function (padding, date, locale, timestamp) { + return pad(Math.floor(timestamp % 1000), null, 3); + }, + 'l': function (padding, date) { + return pad(hours12(date), padding == null ? ' ' : padding); + }, + 'M': function (padding, date) { + return pad(date.getMinutes(), padding); + }, + 'm': function (padding, date) { + return pad(date.getMonth() + 1, padding); + }, + 'n': function () { + return '\n'; + }, + 'o': function (padding, date) { + var _date = date.getDate(); + return String(_date) + ordinal(_date); + }, + 'P': function (padding, date, locale) { + return date.getHours() < 12 ? locale.am : locale.pm; + }, + 'p': function (padding, date, locale) { + return date.getHours() < 12 ? locale.AM : locale.PM; + }, + 'R': function (padding, date, locale) { + return _strftime(locale.formats.R || '%H:%M', date, locale); + }, + 'r': function (padding, date, locale) { + return _strftime(locale.formats.r || '%I:%M:%S %p', date, locale); + }, + 'S': function (padding, date) { + return pad(date.getSeconds(), padding); + }, + 's': function (padding, date, locale, timestamp) { + return Math.floor(timestamp / 1000); + }, + 'T': function (padding, date, locale) { + return _strftime(locale.formats.T || '%H:%M:%S', date, locale); + }, + 't': function () { + return '\t'; + }, + 'U': function (padding, date) { + return pad(weekNumber(date, 'sunday'), padding); + }, + 'u': function (padding, date) { + var day = date.getDay(); + return day === 0 ? 7 : day; + }, + 'v': function (padding, date, locale) { + return _strftime(locale.formats.v || '%e-%b-%Y', date, locale); + }, + 'W': function (padding, date) { + return pad(weekNumber(date, 'monday'), padding); + }, + 'w': function (padding, date) { + return date.getDay(); + }, + 'Y': function (padding, date) { + return date.getFullYear(); + }, + 'y': function (padding, date) { + return String(date.getFullYear()).slice(-2); + }, + 'Z': function (padding, date, locale, timestamp, options) { + if (options.utc) { + return 'GMT'; } else { - p = null; + var tzString = date.toString().match(/\((\w+)\)/); + return tzString && tzString[1] || ''; } + }, + 'z': function (padding, date, locale, timestamp, options) { + if (options.utc) { + return '+0000'; + } else { + var off = typeof options.timezone === 'number' ? options.timezone : -date.getTimezoneOffset(); + return (off < 0 ? '-' : '+') + pad(Math.abs(off / 60)) + pad(off % 60); + } + } +}; - return mask[c] ? mask[c](p, date, locale, timestamp, options) : c; +// Most of the specifiers supported by C's strftime, and some from Ruby. +// Some other syntax extensions from Ruby are supported: %-, %_, and %0 +// to pad with nothing, space, or zero (respectively). +function match(match, date, locale, timestamp, options) { + var p = match[1]; + var c = match[2]; + + if (p) { + switch (p) { + case '-': + p = ''; + break; + case '_': + p = ' '; + break; + case '0': + break; + default: + return match[0]; + } + } else { + p = null; } - function dateToUTC(d) { - return new Date(d.getTime() + (d.getTimezoneOffset() || 0) * 60000); - } + return mask[c] ? mask[c](p, date, locale, timestamp, options) : c; +} - // Default padding is '0' and default length is 2, both are optional. - function pad(n, padding, length) { - var _n = String(n); - var _p = padding == null ? '0' : padding; - var _l = length || 2; +function dateToUTC(d) { + return new Date(d.getTime() + (d.getTimezoneOffset() || 0) * 60000); +} - // padding may be an empty string, don't loop forever if it is - if (_p) { - while (_n.length < _l) { - _n = _p + _n; - } - } +// Default padding is '0' and default length is 2, both are optional. +function pad(n, padding, length) { + var _n = String(n); + var _p = padding == null ? '0' : padding; + var _l = length || 2; - return _n; - } - - function hours12(d) { - var hour = d.getHours(); - - if (hour === 0) { - hour = 12; - } else if (hour > 12) { - hour -= 12; - } - - return hour; - } - - // Get the ordinal suffix for a number: st, nd, rd, or th - function ordinal(n) { - var i = n % 10; - var ii = n % 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'; + // padding may be an empty string, don't loop forever if it is + if (_p) { + while (_n.length < _l) { + _n = _p + _n; } } - // firstWeekday: 'sunday' or 'monday', default is 'sunday' - // Pilfered & ported from Ruby's strftime implementation. - function weekNumber(d, firstWeekday) { - var firstDayOfYear = new Date(d.getFullYear(), 0, 1); - // This works by shifting the weekday back by one day if we - // are treating Monday as the first day of the week. - var wDay = d.getDay(); - var yDay = (d - firstDayOfYear) / 86400000; + return _n; +} - if (firstWeekday === 'monday') { - if (wDay === 0) { - // Sunday - wDay = 6; - } else { - wDay--; - } - } +function hours12(d) { + var hour = d.getHours(); - return Math.floor((yDay + 7 - wDay) / 7); + if (hour === 0) { + hour = 12; + } else if (hour > 12) { + hour -= 12; } - function strftime(fmt, d, locale) { - return _strftime(fmt, d, locale); + return hour; +} + +// Get the ordinal suffix for a number: st, nd, rd, or th +function ordinal(n) { + var i = n % 10; + var ii = n % 100; + + if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) { + return 'th'; } - function isDate(date) { - return toString.call(date) === '[object Date]'; + switch (i) { + case 1: + return 'st'; + case 2: + return 'nd'; + case 3: + return 'rd'; + } +} + +// firstWeekday: 'sunday' or 'monday', default is 'sunday' +// Pilfered & ported from Ruby's strftime implementation. +function weekNumber(d, firstWeekday) { + var firstDayOfYear = new Date(d.getFullYear(), 0, 1); + // This works by shifting the weekday back by one day if we + // are treating Monday as the first day of the week. + var wDay = d.getDay(); + var yDay = (d - firstDayOfYear) / 86400000; + + if (firstWeekday === 'monday') { + if (wDay === 0) { + // Sunday + wDay = 6; + } else { + wDay--; + } } - // ISO 8601 format timezone string, [-+]HHMM - // Convert to the number of minutes and it'll be applied to the date below. - function fixTZ(date, opt) { - var d = date; - var tz = opt.timezone; - var tzType = typeof tz; + return Math.floor((yDay + 7 - wDay) / 7); +} - if (opt.utc || tzType == 'number' || tzType == 'string') { - d = dateToUTC(d); - } +function strftime(fmt, d, locale) { + return _strftime(fmt, d, locale); +} - if (tz) { - if (tzType == 'string') { - var s = tz[0] === '-' ? -1 : 1; - var h = parseInt(tz.slice(1, 3), 10); - var m = parseInt(tz.slice(3, 5), 10); +function isDate(date) { + return toString.call(date) === '[object Date]'; +} - tz = s * (60 * h) + m; - } +// ISO 8601 format timezone string, [-+]HHMM +// Convert to the number of minutes and it'll be applied to the date below. +function fixTZ(date, opt) { + var d = date; + var tz = opt.timezone; + var tzType = typeof tz; - d = new Date(d.getTime() + (tz * 60000)); - opt.timezone = tz; - } - - return d; + if (opt.utc || tzType == 'number' || tzType == 'string') { + d = dateToUTC(d); } - namespace.strftime = strftime; + if (tz) { + if (tzType == 'string') { + var s = tz[0] === '-' ? -1 : 1; + var h = parseInt(tz.slice(1, 3), 10); + var m = parseInt(tz.slice(3, 5), 10); - namespace.strftimeTZ = strftime.strftimeTZ = function strfTimeTZ(fmt, d, locale, timeZone) { - var _locale = locale; - var _timeZone = timeZone; - - if ((typeof locale === 'number' || typeof locale === 'string') && timeZone == null) { - _timeZone = locale; - _locale = undefined; + tz = s * (60 * h) + m; } - return _strftime(fmt, d, _locale, { - timezone: _timeZone - }); - }; - - namespace.strftimeUTC = strftime.strftimeUTC = function strftimeUTC(fmt, d, locale) { - return _strftime(fmt, d, locale, { - utc: true - }); - }; - - namespace.localizedStrftime = strftime.localizedStrftime = function localizedStrftime(locale) { - return function (fmt, d, options) { - return _strftime(fmt, d, locale, options); - }; + d = new Date(d.getTime() + (tz * 60000)); + opt.timezone = tz; + } + + return d; +} + +namespace.strftime = strftime; + +namespace.strftimeTZ = strftime.strftimeTZ = function strfTimeTZ(fmt, d, locale, timeZone) { + var _locale = locale; + var _timeZone = timeZone; + + if ((typeof locale === 'number' || typeof locale === 'string') && timeZone == null) { + _timeZone = locale; + _locale = undefined; + } + + return _strftime(fmt, d, _locale, { + timezone: _timeZone + }); +}; + +namespace.strftimeUTC = strftime.strftimeUTC = function strftimeUTC(fmt, d, locale) { + return _strftime(fmt, d, locale, { + utc: true + }); +}; + +namespace.localizedStrftime = strftime.localizedStrftime = function localizedStrftime(locale) { + return function (fmt, d, options) { + return _strftime(fmt, d, locale, options); }; +}; }()); From 281ae01936835f88078c7fb1c021b570dcf9043c Mon Sep 17 00:00:00 2001 From: B-Vladi Date: Mon, 19 May 2014 21:42:30 +0400 Subject: [PATCH 15/15] Optimization for V8 --- strftime.js | 63 +++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/strftime.js b/strftime.js index df9aeb4..c331e35 100644 --- a/strftime.js +++ b/strftime.js @@ -24,7 +24,7 @@ try { namespace = new Function('return this')(); } -var DefaultLocale = { +var defaultLocale = { days: 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), shortDays: 'Sun Mon Tue Wed Thu Fri Sat'.split(' '), months: 'January February March April May June July August September October November December'.split(' '), @@ -35,37 +35,40 @@ var DefaultLocale = { pm: 'pm' }; +var defaultFormats = { + D: '%m/%d/%y', + F: '%Y-%m-%d', + R: '%H:%M', + r: '%I:%M:%S %p', + T: '%H:%M:%S', + v: '%e-%b-%Y' +}; + // d, locale, and options are optional, but you can't leave // holes in the argument list. If you pass options you have to pass // in all the preceding args as well. // // options: -// - locale [object] an object with the same structure as DefaultLocale +// - locale [object] an object with the same structure as defaultLocale // - timezone [number] timezone offset in minutes from GMT -function _strftime(format, date, locale, options) { +function _strftime(f, date, locale, options) { var o = options || {}; var l = locale; var d = date; - var m, ts, i = 0, r = ''; - var re = /%([-_0]?)(.)/g; + var ts; - if (d && !isDate(d)) { + if (d && toString.call(d) !== '[object Date]') { l = d; d = null; } - l = l || DefaultLocale; + l = l || defaultLocale; l.formats = l.formats || {}; d = d || new Date(); ts = d.getTime(); d = fixTZ(d, o); - while (m = re.exec(format)) { - r += format.substring(i, m.index) + match(m, d, l, ts, o); - i = m.index + m[0].length; - } - - return r + format.substring(i); + return match(f, d, l, ts, o); } var mask = { @@ -199,28 +202,34 @@ var mask = { // Most of the specifiers supported by C's strftime, and some from Ruby. // Some other syntax extensions from Ruby are supported: %-, %_, and %0 // to pad with nothing, space, or zero (respectively). -function match(match, date, locale, timestamp, options) { - var p = match[1]; - var c = match[2]; +function match(f, d, l, ts, o) { + var m, i = 0, r = '', c; + var re = /%([-_0]?)(.)/g; + while (m = re.exec(f)) { + c = m[2]; + r += f.substring(i, m.index) + (mask[c] ? mask[c](fixPadding(m[1], m[0]), d, l, ts, o) : c); + i = re.lastIndex; + } + + return r + f.substring(i); +} + +function fixPadding(p, c) { if (p) { switch (p) { case '-': - p = ''; - break; + return ''; case '_': - p = ' '; - break; + return ' '; case '0': - break; + return p; default: - return match[0]; + return c; } } else { - p = null; + return null; } - - return mask[c] ? mask[c](p, date, locale, timestamp, options) : c; } function dateToUTC(d) { @@ -299,10 +308,6 @@ function strftime(fmt, d, locale) { return _strftime(fmt, d, locale); } -function isDate(date) { - return toString.call(date) === '[object Date]'; -} - // ISO 8601 format timezone string, [-+]HHMM // Convert to the number of minutes and it'll be applied to the date below. function fixTZ(date, opt) {