diff --git a/package.js b/package.js new file mode 100644 index 0000000..4e6173c --- /dev/null +++ b/package.js @@ -0,0 +1,22 @@ +Package.describe({ + name: 'kalarani:strftime', + version: '0.0.1', + // Brief, one-line summary of the package. + summary: 'strftime for JavaScript', + // URL to the Git repository containing the source code for this package. + git: 'git://github.com/samsonjs/strftime.git', + // By default, Meteor will default to using README.md for documentation. + // To avoid submitting documentation, set this field to null. + documentation: 'README.md' +}); + +Package.onUse(function(api) { + api.versionsFrom('1.1.0.2'); + api.addFiles('strftime.js'); +}); + +Package.onTest(function(api) { + api.use('peerlibrary:assert'); + api.use('kalarani:strftime'); + api.addFiles('strftime-tests.js'); +}); diff --git a/strftime-tests.js b/strftime-tests.js new file mode 100644 index 0000000..737abe3 --- /dev/null +++ b/strftime-tests.js @@ -0,0 +1,258 @@ + +// Based on CoffeeScript by andrewschaaf on github +// +// TODO: +// - past and future dates, especially < 1900 and > 2100 +// - look for edge cases + +var Time = new Date(1307470705067); // Tue, 07 Jun 2011 18:18:25 GMT + +assert.fn = function(value, msg) { + assert.equal('function', typeof value, msg); +}; + +function assertFormat(time, format, expected, name, strftime) { + var actual = strftime(format, time); + assert.equal(expected, actual, name + '("' + format + '", ' + time + ') is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected)); +} + +assert.format = function(format, expected, expectedUTC, time) { + time = time || Time; + if (expected) { assertFormat(time, format, expected, 'strftime', strftime); } + assertFormat(time, format, expectedUTC || expected, 'strftime.utc()', strftimeUTC); +}; + +/// check deprecated exports +assert.fn(strftime); +assert.fn(strftimeTZ); +assert.fn(strftimeUTC); +assert.fn(localizedStrftime); +ok('Deprecated exports'); + +/// check exports +assert.fn(strftime.localize); +assert.fn(strftime.timezone); +assert.fn(strftime.utc); +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('%X', '11:51:45', '18:51:45'); + assert.format('%c', 'Tue Jun 07 11:51:45 2011', 'Tue Jun 07 18:51:45 2011'); + assert.format('%j', '097', '098', new Date(1365390736236)); + assert.format('%x', '06/07/11'); + assert.format('%U', '12', null, new Date('2017-03-25 00:00:00 +0000')); + assert.format('%U', '12', '13', new Date('2017-03-26 00:00:00 +0000')); + assert.format('%U', '13', null, new Date('2017-03-27 00:00:00 +0000')); + assert.format('%U', '13', '14', new Date('2017-04-02 00:00:00 +0000')); + 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('%X', '20:51:45', '18:51:45'); + assert.format('%c', 'Tue Jun 07 20:51:45 2011', 'Tue Jun 07 18:51:45 2011'); + assert.format('%j', '098', '098', new Date(1365390736236)); + assert.format('%x', '06/07/11'); + assert.format('%U', '12', null, new Date('2017-03-25 00:00:00 +0000')); + assert.format('%U', '13', null, new Date('2017-03-26 00:00:00 +0000')); + assert.format('%U', '13', null, new Date('2017-03-27 00:00:00 +0000')); + assert.format('%U', '14', null, new Date('2017-04-02 00:00:00 +0000')); + ok('Time zones (' + process.env.TZ + ')'); +} +else { + console.log('(Current timezone has no tests: ' + (process.env.TZ || 'none') + ')'); +} + +/// 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, '18'); +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:18'); +assert.format('%r', null, '06:18:25 PM'); +assert.format('%S', '25'); +assert.format('%s', '1307470705'); +assert.format('%T', null, '18:18:25'); +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('%:z', null, '+00:00'); +assert.format('%%', '%'); // any other char +assert.format('%F %T', null, '1970-01-01 00:00:00', new Date(0)); +assert.format('%U', null, '12', new Date('2017-03-25 00:00:00 +0000')); +assert.format('%U', null, '13', new Date('2017-03-26 00:00:00 +0000')); +assert.format('%U', null, '13', new Date('2017-03-27 00:00:00 +0000')); +assert.format('%U', null, '14', new Date('2017-04-02 00:00:00 +0000')); +ok('GMT'); + + +/// locales + +var it_IT = { + days: words('domenica lunedi martedi mercoledi giovedi venerdi sabato'), + shortDays: words('dom lun mar mer gio ven sab'), + months: words('gennaio febbraio marzo aprile maggio giugno luglio agosto settembre ottobre novembre dicembre'), + shortMonths: words('gen feb mar apr mag giu lug ago set ott nov dic'), + AM: 'it$AM', + PM: 'it$PM', + am: 'it$am', + pm: 'it$pm', + formats: { + D: 'it$%m/%d/%y', + F: 'it$%Y-%m-%d', + R: 'it$%H:%M', + T: 'it$%H:%M:%S', + X: '%T', + c: '%a %b %d %X %Y', + r: 'it$%I:%M:%S %p', + v: 'it$%e-%b-%Y', + x: '%D' + } + }; + +var strftimeIT = strftime.localize(it_IT), + strftimeITUTC = strftimeIT.utc(); +assert.format_it = function(format, expected, expectedUTC) { + if (expected) { assertFormat(Time, format, expected, 'strftime.localize(it_IT)', strftimeIT); } + assertFormat(Time, format, expectedUTC || expected, 'strftime.localize(it_IT).utc()', strftimeITUTC); +}; + +assert.format_it('%A', 'martedi'); +assert.format_it('%a', 'mar'); +assert.format_it('%B', 'giugno'); +assert.format_it('%b', 'giu'); +assert.format_it('%c', null, 'mar giu 07 it$18:18:25 2011'); +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:18'); +assert.format_it('%r', null, 'it$06:18:25 it$PM'); +assert.format_it('%T', null, 'it$18:18:25'); +assert.format_it('%v', 'it$ 7-giu-2011'); +assert.format_it('%x', null, 'it$06/07/11'); +assert.format_it('%X', null, 'it$18:18:25'); +ok('Localization'); + + +/// timezones + +assert.formatTZ = function(format, expected, tz, time) { + assertFormat(time || Time, format, expected, 'strftime.timezone(' + tz + ')', strftime.timezone(tz)); +}; + +assert.formatTZ('%F %r %z', '2011-06-07 06:18:25 PM +0000', 0); +assert.formatTZ('%F %r %z', '2011-06-07 06:18:25 PM +0000', '+0000'); +assert.formatTZ('%F %r %z', '2011-06-07 08:18:25 PM +0200', 120); +assert.formatTZ('%F %r %z', '2011-06-07 08:18:25 PM +0200', '+0200'); +assert.formatTZ('%F %r %z', '2011-06-07 11:18:25 AM -0700', -420); +assert.formatTZ('%F %r %z', '2011-06-07 11:18:25 AM -0700', '-0700'); +assert.formatTZ('%F %r %z', '2011-06-07 10:48:25 AM -0730', '-0730'); +assert.formatTZ('%F %r %:z', '2011-06-07 10:48:25 AM -07:30', '-0730'); +ok('Time zone offset'); + +/// caching +(function() { + // this test fails when the 2 calls cross a millisecond boundary, so try a number of times + var CacheAttempts = 10; + var MaxFailures = 1; + var failures = 0; + for (var i = 0; i < CacheAttempts; ++i) { + var expectedMillis = strftime('%L'); + var millis = strftime('%L'); + if (expectedMillis != millis) { + ++failures; + } + } + if (failures > MaxFailures) { + assert.fail('timestamp caching appears to be broken (' + failures + ' failed attempts out of ' + CacheAttempts + ')'); + } +}()); +ok('Cached timestamps'); + + +/// helpers + +function words(s) { return (s || '').split(' '); } + +function ok(s) { console.log('[ \033[32mOK\033[0m ] ' + s); } + +// Pass a regex or string that matches the timezone abbrev, e.g. %Z above. +// 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); + if (match) { + var off = Time.getTimezoneOffset(), + hourOff = off / 60, + hourDiff = Math.floor(hourOff), + hours = 18 - hourDiff, + padSpace24 = hours < 10 ? ' ' : '', + padZero24 = hours < 10 ? '0' : '', + hour24 = String(hours), + padSpace12 = (hours % 12) < 10 ? ' ' : '', + padZero12 = (hours % 12) < 10 ? '0' : '', + hour12 = String(hours % 12), + sign = hourDiff < 0 ? '+' : '-', + minDiff = Time.getTimezoneOffset() - (hourDiff * 60), + mins = String(51 - minDiff), + tz = match[1], + 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'); + assert.format('%z', sign + '0' + Math.abs(hourDiff) + '00', '+0000'); + assert.format('%:z', sign + '0' + Math.abs(hourDiff) + ':00', '+00:00'); + } +}