diff --git a/lib/fluent/time.rb b/lib/fluent/time.rb index 8a4c12ac93..3fcddce259 100644 --- a/lib/fluent/time.rb +++ b/lib/fluent/time.rb @@ -363,6 +363,8 @@ def parse_float(value) class TimeFormatter def initialize(format = nil, localtime = true, timezone = nil) + # An empty cache slot is marked by a nil string, not by its key: 0 is a + # valid timestamp, so a key can never say whether the slot is filled. @tc1 = 0 @tc1_str = nil @tc2 = 0 @@ -390,9 +392,9 @@ def initialize(format = nil, localtime = true, timezone = nil) end def format_without_subsec(time) - if @tc1 == time + if @tc1_str && @tc1 == time return @tc1_str - elsif @tc2 == time + elsif @tc2_str && @tc2 == time return @tc2_str else str = format_nocache(time) @@ -408,9 +410,9 @@ def format_without_subsec(time) end def format_with_subsec(time) - if Fluent::EventTime.eq?(@tc1, time) + if @tc1_str && Fluent::EventTime.eq?(@tc1, time) return @tc1_str - elsif Fluent::EventTime.eq?(@tc2, time) + elsif @tc2_str && Fluent::EventTime.eq?(@tc2, time) return @tc2_str else str = format_nocache(time) diff --git a/test/test_time_formatter.rb b/test/test_time_formatter.rb index 0938e2fb5b..8db58fff58 100644 --- a/test/test_time_formatter.rb +++ b/test/test_time_formatter.rb @@ -196,6 +196,29 @@ def test_format_with_subsec assert_equal("20140927 0000.000000000", formatter.format(time)) end + # The Unix epoch used to match an empty cache slot, whose string is nil. + def test_format_epoch + formatter = Fluent::TimeFormatter.new(nil, false, nil) + assert_equal("1970-01-01T00:00:00Z", formatter.format(Fluent::EventTime.new(0))) + + formatter = Fluent::TimeFormatter.new(nil, false, nil) + assert_equal("1970-01-01T00:00:00Z", formatter.format(0)) + assert_equal("1970-01-01T00:00:01Z", formatter.format(Fluent::EventTime.new(1))) + assert_equal("1970-01-01T00:00:00Z", formatter.format(Fluent::EventTime.new(0))) + end + + # Here the slot is compared with EventTime.eq?, which falls back to comparing + # seconds alone, so the whole first second of the epoch matched an empty slot. + def test_format_epoch_with_subsec + formatter = Fluent::TimeFormatter.new("%Y%m%d %H%M%S.%N", false, nil) + assert_equal("19700101 000000.000000000", formatter.format(Fluent::EventTime.new(0, 0))) + assert_equal("19700101 000000.123456789", formatter.format(Fluent::EventTime.new(0, 123456789))) + assert_equal("19700101 000000.000000000", formatter.format(Fluent::EventTime.new(0, 0))) + + formatter = Fluent::TimeFormatter.new("%Y%m%d %H%M%S.%N", false, nil) + assert_equal("19700101 000000.999999999", formatter.format(Fluent::EventTime.new(0, 999999999))) + end + sub_test_case 'TimeMixin::Formatter' do class DummyForTimeFormatter include Fluent::Configurable