@@ -272,4 +272,171 @@ describe('PrettyStream', function () {
272272 } ) ) ;
273273 } ) ;
274274 } ) ;
275+
276+ describe ( 'timezone handling' , function ( ) {
277+ it ( 'should display provided timestamps consistently' , function ( done ) {
278+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
279+ var writeStream = new Writable ( ) ;
280+
281+ writeStream . _write = function ( data ) {
282+ data = data . toString ( ) ;
283+ // The timestamp should be formatted consistently
284+ data . should . containEql ( '[2016-07-01 00:00:00]' ) ;
285+ data . should . containEql ( 'INFO' ) ;
286+ data . should . containEql ( 'Test message' ) ;
287+ done ( ) ;
288+ } ;
289+
290+ ghostPrettyStream . pipe ( writeStream ) ;
291+
292+ // Write with an explicit timestamp
293+ ghostPrettyStream . write ( JSON . stringify ( {
294+ time : '2016-07-01 00:00:00' ,
295+ level : 30 ,
296+ msg : 'Test message'
297+ } ) ) ;
298+ } ) ;
299+
300+ it ( 'should handle ISO 8601 timestamps and convert to local time' , function ( done ) {
301+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
302+ var writeStream = new Writable ( ) ;
303+
304+ writeStream . _write = function ( data ) {
305+ data = data . toString ( ) ;
306+ // ISO timestamp should be parsed and converted to local time
307+ // Extract the timestamp to verify format
308+ const timestampMatch = data . match ( / ^ \[ ( \d { 4 } - \d { 2 } - \d { 2 } \d { 2 } : \d { 2 } : \d { 2 } ) \] / ) ;
309+ timestampMatch . should . not . be . null ( ) ;
310+
311+ // Verify the timestamp represents the correct moment
312+ // 2016-07-01T00:00:00.000Z in local time
313+ const parsedTime = new Date ( timestampMatch [ 1 ] ) ;
314+ const expectedTime = new Date ( '2016-07-01T00:00:00.000Z' ) ;
315+
316+ // The displayed local time should represent the same moment as the UTC time
317+ // Allow for some tolerance due to date parsing
318+ Math . abs ( parsedTime . getTime ( ) - expectedTime . getTime ( ) ) . should . be . below ( 24 * 60 * 60 * 1000 ) ;
319+
320+ data . should . containEql ( 'INFO' ) ;
321+ data . should . containEql ( 'ISO timestamp test' ) ;
322+ done ( ) ;
323+ } ;
324+
325+ ghostPrettyStream . pipe ( writeStream ) ;
326+
327+ // Write with an ISO 8601 timestamp
328+ ghostPrettyStream . write ( JSON . stringify ( {
329+ time : '2016-07-01T00:00:00.000Z' ,
330+ level : 30 ,
331+ msg : 'ISO timestamp test'
332+ } ) ) ;
333+ } ) ;
334+
335+ it ( 'should handle timestamps with timezone offsets' , function ( done ) {
336+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
337+ var writeStream = new Writable ( ) ;
338+
339+ writeStream . _write = function ( data ) {
340+ data = data . toString ( ) ;
341+ // Timestamp with timezone offset should be converted to local time for display
342+ const timestampMatch = data . match ( / ^ \[ ( \d { 4 } - \d { 2 } - \d { 2 } \d { 2 } : \d { 2 } : \d { 2 } ) \] / ) ;
343+ timestampMatch . should . not . be . null ( ) ;
344+
345+ data . should . containEql ( 'INFO' ) ;
346+ data . should . containEql ( 'Timezone offset test' ) ;
347+ done ( ) ;
348+ } ;
349+
350+ ghostPrettyStream . pipe ( writeStream ) ;
351+
352+ // Write with a timestamp that includes timezone offset
353+ ghostPrettyStream . write ( JSON . stringify ( {
354+ time : '2016-07-01T00:00:00+02:00' ,
355+ level : 30 ,
356+ msg : 'Timezone offset test'
357+ } ) ) ;
358+ } ) ;
359+
360+ it ( 'should use current local time when no timestamp is provided' , function ( done ) {
361+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
362+ var writeStream = new Writable ( ) ;
363+
364+ // Capture the time before the test
365+ const beforeTime = new Date ( ) ;
366+
367+ writeStream . _write = function ( data ) {
368+ data = data . toString ( ) ;
369+
370+ // Extract the timestamp from the output
371+ const timestampMatch = data . match ( / ^ \[ ( \d { 4 } - \d { 2 } - \d { 2 } \d { 2 } : \d { 2 } : \d { 2 } ) \] / ) ;
372+ timestampMatch . should . not . be . null ( ) ;
373+
374+ const loggedTime = new Date ( timestampMatch [ 1 ] ) ;
375+ const afterTime = new Date ( ) ;
376+
377+ // The logged time should be between beforeTime and afterTime
378+ loggedTime . getTime ( ) . should . be . aboveOrEqual ( beforeTime . getTime ( ) ) ;
379+ loggedTime . getTime ( ) . should . be . belowOrEqual ( afterTime . getTime ( ) ) ;
380+
381+ done ( ) ;
382+ } ;
383+
384+ ghostPrettyStream . pipe ( writeStream ) ;
385+
386+ // Write without a timestamp
387+ ghostPrettyStream . write ( JSON . stringify ( {
388+ level : 30 ,
389+ msg : 'No timestamp test'
390+ } ) ) ;
391+ } ) ;
392+
393+ it ( 'should work correctly in different timezones' , function ( done ) {
394+ // This test verifies that string timestamps are displayed as-is
395+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
396+ var writeStream = new Writable ( ) ;
397+
398+ writeStream . _write = function ( data ) {
399+ data = data . toString ( ) ;
400+ // String timestamp should be displayed exactly as provided
401+ data . should . containEql ( '[2016-07-01 00:00:00]' ) ;
402+ data . should . containEql ( 'String timestamp' ) ;
403+ done ( ) ;
404+ } ;
405+
406+ ghostPrettyStream . pipe ( writeStream ) ;
407+
408+ // Test with string timestamp - should be displayed as-is
409+ ghostPrettyStream . write ( JSON . stringify ( {
410+ time : '2016-07-01 00:00:00' ,
411+ level : 30 ,
412+ msg : 'String timestamp'
413+ } ) ) ;
414+ } ) ;
415+
416+ it ( 'regression test: string timestamps should not be affected by timezone offset' , function ( done ) {
417+ // This test ensures the bug from commit be5ddf2 doesn't resurface
418+ // String timestamps like '2016-07-01 00:00:00' should be displayed exactly as provided
419+ // regardless of the system timezone
420+ var ghostPrettyStream = new PrettyStream ( { mode : 'short' } ) ;
421+ var writeStream = new Writable ( ) ;
422+
423+ writeStream . _write = function ( data ) {
424+ data = data . toString ( ) ;
425+ // The exact string '2016-07-01 00:00:00' should appear in the output
426+ // It should NOT be shifted by timezone offset (e.g., NOT '2016-06-30 23:00:00')
427+ data . should . match ( / ^ \[ 2 0 1 6 - 0 7 - 0 1 0 0 : 0 0 : 0 0 \] / ) ;
428+ data . should . containEql ( 'Regression test' ) ;
429+ done ( ) ;
430+ } ;
431+
432+ ghostPrettyStream . pipe ( writeStream ) ;
433+
434+ // This timestamp format was causing issues in non-UTC timezones
435+ ghostPrettyStream . write ( JSON . stringify ( {
436+ time : '2016-07-01 00:00:00' ,
437+ level : 30 ,
438+ msg : 'Regression test'
439+ } ) ) ;
440+ } ) ;
441+ } ) ;
275442} ) ;
0 commit comments