Comparing version 0.9.1 to 0.9.2
@@ -20,75 +20,143 @@ /** | ||
function phpdate( format, timestamp ) { | ||
var dt = _timestampToDt(timestamp); | ||
var i, output = ""; | ||
// small circular buffer to cache recent results | ||
var _dateCache = []; | ||
var _dateCacheIdx = 0; | ||
function _setCached( format, dt, output ) { | ||
// lru result cache, sorta like mysql | ||
_dateCache[_dateCacheIdx++] = {fmt: format, ts: dt.getTime(), gmt: dt.isGmt, res: output}; | ||
if (_dateCacheIdx >= 10) _dateCacheIdx = 0; | ||
} | ||
function _getCached( format, dt ) { | ||
var i, tm = dt.getTime(); | ||
for (i=0; i<_dateCache.length; i++) { | ||
var item = _dateCache[i]; | ||
if (item.ts === tm && item.fmt === format && item.gmt === dt.isGmt) { | ||
return item.res; | ||
} | ||
} | ||
return ""; | ||
} | ||
for (i=0; i<format.length; i++) { | ||
var c = format[i]; | ||
if (formatters[c]) output += formatters[c](dt); | ||
else if (c === '\\' && ++i < format.length) output += format[i]; | ||
else output += c; | ||
} | ||
function phpdate( format, dt ) { | ||
dt = _timestampToDt(dt); | ||
var output; | ||
// cache recent results to capture inter-millisecond redundancy | ||
if ((output = _getCached(format, dt))) return output; | ||
// use a custom function to typeset the date, avoid many small calls | ||
output = compileFormat(format)(dt); | ||
_setCached(format, dt, output); | ||
return output; | ||
} | ||
var _currentDate = null; | ||
function _timestampToDt( timestamp ) { | ||
return (typeof timestamp === 'number') ? new Date(timestamp) : timestamp ? timestamp : new Date(); | ||
// TODO: use process.hrtime() to capture microsecond timestamp, for 'u' format | ||
if (typeof timestamp === 'number') return new Date(timestamp); | ||
if (timestamp) return timestamp; | ||
if (_currentDate) return _currentDate; | ||
else { | ||
setTimeout(function(){ _currentDate = null; }, 1); | ||
// TODO: use process.hrtime() to capture microsecond timestamp, for 'u' format | ||
// NOTE: hrtime returns relative time, is not directly usable | ||
return _currentDate = new Date(); | ||
} | ||
// note: is it ever possible to get a stale _currentDate? (high load / blocking calls?) | ||
} | ||
// actions to concatenate the formatted Date now into ret | ||
// helper methods are in fmt | ||
// `now` is a Date object which will output timezone-corrected values | ||
// (either localtime, no correction, or gmtime, shifted ahead by tz.offs) | ||
var formatters = { | ||
d: function(now){ return pad2(now.getDate()); }, | ||
D: function(now){ return ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now.getDay()]; }, | ||
j: function(now){ return now.getDate(); }, | ||
l: function(now){ return ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][now.getDay()]; }, | ||
h: function(now){ return pad2(hour1to12(now)); }, | ||
N: function(now){ return iso8601day(now.getDay()); }, // Mon=1 .. Sun=7 | ||
S: function(now){ return dayNumberOrdinalSuffix(now); }, | ||
w: function(now){ return now.getDay(); }, | ||
z: function(now){ return weekdayOffset(now).year; }, | ||
var actions = { | ||
d: '{ ret += fmt.pad2(now.getDate()); }', | ||
D: "{ ret += ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][now.getDay()]; }", | ||
j: '{ ret += now.getDate(); }', | ||
l: "{ ret += ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][now.getDay()]; }", | ||
h: '{ ret += fmt.pad2(fmt.hour1to12(now)); }', | ||
N: '{ ret += fmt.iso8601day(now.getDay()); }', // Mon=1 .. Sun=7 | ||
S: '{ ret += fmt.dayNumberOrdinalSuffix(now); }', | ||
w: '{ ret += now.getDay(); }', | ||
z: '{ ret += fmt.weekdayOffset(now).year; }', | ||
// W: ISO 8601 week number of the year, weeks starting on Monday | ||
// AR: php pads with a leading zero if 1..9 | ||
W: function(now){ return pad2(iso8601week(now)); }, | ||
W: '{ ret += fmt.pad2(fmt.iso8601week(now)); }', | ||
F: function(now){ return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][now.getMonth()]; }, | ||
m: function(now){ return pad2(now.getMonth() + 1); }, | ||
M: function(now){ return ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now.getMonth()]; }, | ||
n: function(now){ return now.getMonth() + 1; }, | ||
t: function(now){ return weekdayOffset(now).mdays; }, | ||
F: "{ ret += ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][now.getMonth()]; }", | ||
m: '{ ret += fmt.pad2(now.getMonth() + 1); }', | ||
M: "{ ret += ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][now.getMonth()]; }", | ||
n: '{ ret += now.getMonth() + 1; }', | ||
t: '{ ret += fmt.weekdayOffset(now).mdays; }', | ||
L: function(now){ return weekdayOffset(now).leap ? 1 : 0; }, | ||
L: '{ ret += fmt.weekdayOffset(now).leap ? 1 : 0; }', | ||
// o: ISO 8601 year number | ||
o: function(now){ return pad4(iso8601year(now)); }, | ||
Y: function(now){ return pad4(now.getFullYear()); }, | ||
y: function(now){ return pad2(now.getFullYear() % 100); }, | ||
o: '{ ret += fmt.pad4(fmt.iso8601year(now)); }', | ||
Y: '{ ret += fmt.pad4(now.getFullYear()); }', | ||
y: '{ ret += fmt.pad2(now.getFullYear() % 100); }', | ||
a: function(now){ return now.getHours() < 12 ? "am" : "pm"; }, | ||
A: function(now){ return now.getHours() < 12 ? "AM" : "PM"; }, | ||
B: function(now){ var tz = tzInfo(now); | ||
a: '{ ret += now.getHours() < 12 ? "am" : "pm"; }', | ||
A: '{ ret += now.getHours() < 12 ? "AM" : "PM"; }', | ||
B: "{ var tz = fmt.tzInfo(now); " + | ||
// swatch time is GMT +01:00, ie timestamp 1 is 1am, '041' | ||
var tm = now.getTime() + 3600 * 1000; | ||
return pad3(Math.floor(1000 * (tm % 86400000) / 86400000)); }, | ||
g: function(now){ return hour1to12(now); }, | ||
G: function(now){ return (now.getHours()); }, | ||
H: function(now){ return pad2(now.getHours()); }, | ||
i: function(now){ return pad2(now.getMinutes()); }, | ||
s: function(now){ return pad2(now.getSeconds()); }, | ||
u: function(now){ return pad6(now.getTime() % 1000 * 1000); }, | ||
"var tm = now.getTime() + 3600 * 1000; " + | ||
"ret += fmt.pad3(Math.floor(1000 * (tm % 86400000) / 86400000)); }", | ||
g: '{ ret += fmt.hour1to12(now); }', | ||
G: '{ ret += (now.getHours()); }', | ||
H: '{ ret += fmt.pad2(now.getHours()); }', | ||
i: '{ ret += fmt.pad2(now.getMinutes()); }', | ||
s: '{ ret += fmt.pad2(now.getSeconds()); }', | ||
u: '{ ret += fmt.pad6(now.getTime() % 1000 * 1000); }', | ||
e: function(now){ var tz = tzInfo(now); return tz.tzname; }, | ||
I: function(now){ var tz = tzInfo(now); return tz.isDst; }, | ||
O: function(now){ var tz = tzInfo(now); return (!tz.offs ? '+0000' : tz.sign + pad2(tz.h) + pad2(tz.m)); }, | ||
P: function(now){ var tz = tzInfo(now); return (!tz.offs ? '+00:00' : tz.sign + pad2(tz.h) + ":" + pad2(tz.m)); }, | ||
T: function(now){ var tz = tzInfo(now); return tz.tz; }, | ||
Z: function(now){ var tz = tzInfo(now); return -tz.offs * 60; }, | ||
e: '{ var tz = fmt.tzInfo(now); ret += tz.tzname; }', | ||
I: '{ var tz = fmt.tzInfo(now); ret += tz.isDst; }', | ||
O: "{ var tz = fmt.tzInfo(now); ret += (!tz.offs ? '+0000' : tz.sign + fmt.pad2(tz.h) + fmt.pad2(tz.m)); }", | ||
P: "{ var tz = fmt.tzInfo(now); ret += (!tz.offs ? '+00:00' : tz.sign + fmt.pad2(tz.h) + ':' + fmt.pad2(tz.m)); }", | ||
T: '{ var tz = fmt.tzInfo(now); ret += tz.tz; }', | ||
Z: '{ var tz = fmt.tzInfo(now); ret += -tz.offs * 60; }', | ||
c: function(now){ return phpdate("Y-m-d\\TH:i:sP", now); }, | ||
r: function(now){ return phpdate("D, d M Y H:i:s O", now); }, | ||
U: function(now){ return Math.floor(now.getTime() / 1000); }, | ||
c: '{ ret += fmt.phpdate("Y-m-d\\\\TH:i:sP", now); }', | ||
r: '{ ret += fmt.phpdate("D, d M Y H:i:s O", now); }', | ||
U: '{ ret += Math.floor(now.getTime() / 1000); }', | ||
}; | ||
var fmtFuncs = { | ||
pad2: pad2, | ||
pad3: pad3, | ||
pad4: pad4, | ||
pad6: pad6, | ||
tzInfo: tzInfo, | ||
iso8601day: iso8601day, | ||
iso8601week: iso8601week, | ||
iso8601year: iso8601year, | ||
weekdayOffset: weekdayOffset, | ||
hour1to12: hour1to12, | ||
dayNumberOrdinalSuffix: dayNumberOrdinalSuffix, | ||
phpdate: phpdate, | ||
}; | ||
var _compiledFormats = {}; | ||
function compileFormat( format ) { | ||
if (_compiledFormats[format]) return _compiledFormats[format]; | ||
var i, body = "var ret = '';\n"; | ||
for (i=0; i<format.length; i++) { | ||
var c = format[i]; | ||
if (actions[c]) { | ||
body += actions[c] + '\n'; | ||
} | ||
else if (c === '\\' && ++i < format.length) { | ||
body += '{ ret += String.fromCharCode(' + format.charCodeAt(i) + ') }\n'; | ||
} | ||
else { | ||
body += '{ ret += String.fromCharCode(' + c.charCodeAt(0) + ') }\n'; | ||
} | ||
} | ||
body += "return ret;\n"; | ||
var func = Function("now, fmt", body); | ||
return _compiledFormats[format] = function(now) { | ||
return func(now, fmtFuncs); | ||
} | ||
} | ||
// Date is magic, and cannot inherit its prototype methods... so decorate it | ||
@@ -109,3 +177,3 @@ function GmtDate( dt ) { | ||
function gmdate( format, timestamp ) { | ||
var dt = _timestampToDt(timestamp); | ||
var dt = _timestampToDt(timestamp) | ||
return phpdate(format, new GmtDate(dt)); | ||
@@ -229,3 +297,6 @@ } | ||
var tzMap = { | ||
// see http://www.timeanddate.com/time/zones/na | ||
'0': ['GMT', 'GMT', 'UTC'], | ||
'180': ['WGT', 'WGST', 'Western_Greenland'], // also Pierre & Miquelon, islands off Nova Scotia | ||
'210': ['NST', 'NDT', 'Newfoundland'], // -03:30 | ||
'240': ['AST', 'ADT', 'Atlantic'], | ||
@@ -236,6 +307,5 @@ '300': ['EST', 'EDT', 'US/Eastern'], | ||
'480': ['PST', 'PDT', 'US/Pacific'], | ||
// Juneau | ||
// Hawaii | ||
'540': ['AKST', 'AKDT', 'US/Alaska'], | ||
'600': ['HAST', 'HADT', 'US/Hawaii'], // also US/Aleutian | ||
}; | ||
// FIXME: only detects a few timezone abbreviations, and presumes US | ||
// TODO: probe timezone abbreviations with | ||
@@ -242,0 +312,0 @@ // child_process.exec("date +%Z --date @0") and "date +%Z --date @1500000000" |
{ | ||
"name": "phpdate-js", | ||
"version": "0.9.1", | ||
"version": "0.9.2", | ||
"description": "php date() work-alike for nodejs", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
phpdate-js | ||
========== | ||
php date() work-alike for nodejs | ||
quick php date() work-alike for nodejs | ||
@@ -18,7 +18,13 @@ var phpdate = require('phpdate-js'); | ||
See php's [date](http://php.net/manual/en/function.date.php) for the supported | ||
conversions (as of php 5.5). Of them, W and o are not yet implemented. | ||
Supports all php 5.5 conversions, except the ISO 8601 W and o are not yet | ||
implemented. See php's [date](http://php.net/manual/en/function.date.php) for | ||
the descriptions. | ||
The conversion is quick, about half the speed of Date.toISOString(). | ||
The conversion is very quick, as fast as the "Ultra fast javascript strftime" | ||
(ultra-strftime)[http://nodejs.org/package/ultra-strftime]; | ||
faster than Date().toString(). | ||
Had I known about ultra-strftime it would have been tempting to implement | ||
phpdate as a format conversion preprocessor. | ||
### phpdate( format, [timestamp] ) | ||
@@ -28,11 +34,14 @@ | ||
conversions other than the ISO-8601 W and o, though timezone and localization | ||
support is rather lacking. North America timezones should work. | ||
support is rather limited. North America timezones should work. | ||
Format is the timestamp conversion spec. 'Y-m-d H:i:s' formats an SQL | ||
datetime (ISO 9075). The date and time are formatted for the current locale, | ||
with timezone and daylight savings adjustments applied. | ||
Format is the timestamp conversion specifier. Format control characters are | ||
replaced with formatted values; other characters are left as-is. Backslash | ||
escapes the special meaning of a character. For instance, 'Y-m-d H:i:s' | ||
formats an ISO 9075 SQL datetime such as '2014-01-02 12:34:56'. The date | ||
and time are formatted for the current locale, with timezone and daylight | ||
savings adjustments applied. | ||
The timestamp is optional; it can be a Date object or a JavaScript | ||
millisecond timestamp (milliseconds since the epoch). If not specified, | ||
the current date is used. | ||
The timestamp is optional. If omitted, the current date is used. If | ||
specified, it can be a Date object or a JavaScript millisecond timestamp | ||
(milliseconds since the epoch). | ||
@@ -55,5 +64,6 @@ var phpdate = require('phpdate-js'); | ||
- The T and e conversions (timezone abbreviation and timezone name) reverse | ||
engineer the timezone offset, and only support North American timezones. The | ||
date and time conversions rely on the built-in system timezone handling and | ||
should be correct in all locales. | ||
- The e conversion returns e.g. US/Eastern and not America/New_York | ||
engineer the timezone offset, and only support North American timezones. The | ||
date and time conversions rely on the built-in system timezone handling and | ||
should be correct in all locales. | ||
- The e conversion returns a generic timezone name like US/Eastern and not | ||
a locale-specific one such as America/New_York. |
28610
576
67