From 8df86bccf06fab2db71c450cd39cff86994b7f55 Mon Sep 17 00:00:00 2001 From: "David M. Lee" Date: Thu, 15 Oct 2015 22:17:53 -0400 Subject: [PATCH] Improved time formatting and conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bunyan CLI was not handling timezone conversion properly when set to use local time. This patch uses [Moment.js][] to fix those issues. * Timezone conversions work properly across DST conversions * The timezone, when shown, is correctly shown as `±hh:mm` * The timzeone is omitted on short output, since it isn't that short. Except when UTC is used, since that can be indicated by the single character `Z` Fixes #245 [Moment.js]: http://momentjs.com/ --- Makefile | 3 ++- bin/bunyan | 38 +++++++++++++++++++++++++------------- package.json | 3 +++ test/cli.test.js | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index d2f6358..0e52310 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ ifeq ($(shell uname -s),Darwin) endif NODEOPT ?= $(HOME)/opt - +# Run tests in a set timezone, so local time is predictable +export TZ=Pacific/Honolulu #---- Files diff --git a/bin/bunyan b/bin/bunyan index b5e23ec..e2c6d3d 100755 --- a/bin/bunyan +++ b/bin/bunyan @@ -25,6 +25,7 @@ var child_process = require('child_process'), exec = child_process.exec, execFile = child_process.execFile; var assert = require('assert'); +var moment = require('moment'); var nodeSpawnSupportsStdio = ( Number(process.version.split('.')[0]) >= 0 || @@ -87,6 +88,16 @@ Object.keys(levelFromName).forEach(function (name) { TIME_UTC = 1; // the default, bunyan's native format TIME_LOCAL = 2; +// Timestamp formats. Timezone is added at format time +UTC_FORMATS = { + long: 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]', + short: 'HH:mm:ss.SSS[Z]' +}; +LOCAL_FORMATS = { + long: 'YYYY-MM-DD[T]HH:mm:ss.SSSZ', + short: 'HH:mm:ss.SSS' +}; + var timezoneOffsetMs; // used for TIME_LOCAL display @@ -752,25 +763,26 @@ function emitRecord(rec, line, opts, stylize) { delete rec.v; - var time = rec.time; + var time = moment(rec.time); + // default to UTC + var zoneFormats; switch (opts.timeFormat) { - case TIME_UTC: - break; case TIME_LOCAL: - if (!timezoneOffsetMs) { - timezoneOffsetMs - = (new Date(time)).getTimezoneOffset() * 60 * 1000; - } - time = new Date( - (new Date(time)).getTime() - timezoneOffsetMs).toISOString() + zoneFormats = LOCAL_FORMATS; + break; + default: + // default to UTC + zoneFormats = UTC_FORMATS; + time.utc(); break; } - if (short && rec.time[10] === 'T') { - // Presuming `time` is ISO8601 formatted, i.e. safe to drop date. - time = stylize(time.substr(11), 'XXX'); + if (short) { + time = time.format(zoneFormats.short); } else { - time = stylize('[' + time + ']', 'XXX'); + // Is there any way to put square brackets in a moment format? + time = '[' + time.format(zoneFormats.long) + ']'; } + time = stylize(time, 'XXX'); delete rec.time; var nameStr = rec.name; diff --git a/package.json b/package.json index 93726b7..edca77c 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,8 @@ "scripts": { "test": "make test" + }, + "dependencies": { + "moment": "^2.10.6" } } diff --git a/test/cli.test.js b/test/cli.test.js index c26c7a0..61175e6 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -86,6 +86,46 @@ test('cat simple.log', function (t) { } ); }); +test('simple.log local long', function (t) { + exec(_('%s -o long -L %s/corpus/simple.log', BUNYAN, __dirname), + function (err, stdout, stderr) { + t.ifError(err) + t.equal(stdout, + '[2012-02-08T12:56:52.856-10:00] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + }); +}); +test('simple.log utc long', function (t) { + exec(_('%s -o long %s/corpus/simple.log', BUNYAN, __dirname), + function (err, stdout, stderr) { + t.ifError(err) + t.equal(stdout, + '[2012-02-08T22:56:52.856Z] INFO: myservice/123 on example.com: ' + + 'My message\n'); + t.end(); + }); +}); +test('simple.log local short', function (t) { + exec(_('%s -o short -L %s/corpus/simple.log', BUNYAN, __dirname), + function (err, stdout, stderr) { + t.ifError(err) + t.equal(stdout, + '12:56:52.856 INFO myservice: ' + + 'My message\n'); + t.end(); + }); +}); +test('simple.log utc short', function (t) { + exec(_('%s -o short %s/corpus/simple.log', BUNYAN, __dirname), + function (err, stdout, stderr) { + t.ifError(err) + t.equal(stdout, + '22:56:52.856Z INFO myservice: ' + + 'My message\n'); + t.end(); + }); +}); test('simple.log with color', function (t) { exec(_('%s --color %s/corpus/simple.log', BUNYAN, __dirname), function (err, stdout, stderr) {