Repository: fluent/fluent-bit (2026-04-06T08:47:42Z)

Issue:
Time_System_Timezone assigns wrong date to logs after midnight
## Bug Report

**Describe the bug**
When parsing logs that only contain time (`HH:MM:SS`) with no date component, using `Time_System_Timezone on` with a UTC+1/+2 timezone, the parser incorrectly assigns timestamps to the wrong date around midnight. Times just after midnight (00:00–02:00) are assigned to the previous day instead of the current one.

**To Reproduce**

**Input log:**
```
[23:58:00] [Server thread/INFO]: Before midnight
[23:59:00] [Server thread/INFO]: Just before
[00:00:00] [Server thread/INFO]: After midnight
[00:01:00] [Server thread/INFO]: Still wrong
[02:00:00] [Server thread/INFO]: Back to normal
```

**Parser configuration:**
```
[PARSER]
    Name        paper
    Format      regex
    Regex       ^\[(?<time>\d{2}:\d{2}:\d{2})\] \[(?<thread>[^/]+)/(?<level>\w+)\]: (?<message>.*)
    Time_Key    time
    Time_Format %H:%M:%S
    Time_System_Timezone on
```

**Steps:**
1. Create test log file with the input above
2. Configure Fluent Bit parser as shown
3. Set `TZ=Europe/Stockholm` in environment
4. Run and observe output timestamps

**Expected behavior**
Timestamps should be increasing and assigned to the correct date (March 30 in this case, not March 29).

**Actual behavior**
The 00:00 and 00:01 entries receive unix timestamps earlier than the 23:58 and 23:59 entries, indicating assignment to the previous day.

**Output example:**
```
[0] [[1774907880.000000000, ...], "Before midnight"]      # March 30, 23:58 - Correct
[1] [[1774907940.000000000, ...], "Just before"]          # March 30, 23:59 - Correct
[2] [[1774821600.000000000, ...], "After midnight"]       # March 29, 00:00 - Incorrect
[3] [[1774821660.000000000, ...], "Still wrong"]          # March 29, 00:01 - Incorrect
[4] [[1774828800.000000000, ...], "Back to normal"]       # March 30, 02:00 - Correct
```

**Your Environment**
* **Version used:** 4.2.3
* **Configuration:** Classic format (shown above)
* **Environment:** Docker container with `TZ=Europe/Stockholm`
* **Operating System:** macOS (Docker)
* **Timezone:** Europe/Stockholm (UTC+2, CEST)

**Additional context**
This affects parsing of logs that only contain time information without date. The bug becomes apparent when logs are parsed around the midnight boundary, making logs between 00:00–02:00 appear on the wrong date in downstream systems like OpenSearch.


Discussion:

Patch:
diff --git a/src/flb_parser.c b/src/flb_parser.c
index f13243f580f..c4b6b2d6036 100644
--- a/src/flb_parser.c
+++ b/src/flb_parser.c
@@ -1199,7 +1199,12 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize,
             time_now = now;
         }
 
-        gmtime_r(&time_now, &tmy);
+        if (parser->time_system_timezone == FLB_TRUE) {
+            localtime_r(&time_now, &tmy);
+        }
+        else {
+            gmtime_r(&time_now, &tmy);
+        }
 
         /* Make the timestamp default to today */
         tm->tm.tm_mon = tmy.tm_mon;


Test patch:
diff --git a/tests/internal/parser.c b/tests/internal/parser.c
index b7b4eab876b..8337e4f89fd 100644
--- a/tests/internal/parser.c
+++ b/tests/internal/parser.c
@@ -517,12 +517,84 @@ void test_mysql_unquoted()
 
 }
 
+void test_parser_time_system_timezone_midnight()
+{
+    int ret;
+    time_t now;
+    time_t epoch;
+    struct tm now_tm;
+    struct tm expected_tm;
+    struct flb_tm tm;
+    double ns;
+    char *orig_tz;
+    struct flb_parser *parser;
+    struct flb_config *config;
+
+    orig_tz = getenv("TZ");
+    if (orig_tz != NULL) {
+        orig_tz = flb_strdup(orig_tz);
+    }
+
+    setenv("TZ", "Europe/Stockholm", 1);
+    tzset();
+
+    memset(&now_tm, 0, sizeof(struct tm));
+    now_tm.tm_year = 2025 - 1900;
+    now_tm.tm_mon = 3;
+    now_tm.tm_mday = 15;
+    now_tm.tm_hour = 0;
+    now_tm.tm_min = 30;
+    now_tm.tm_sec = 0;
+    now_tm.tm_isdst = -1;
+    now = mktime(&now_tm);
+    TEST_CHECK(now != (time_t) -1);
+
+    config = flb_config_init();
+    parser = flb_parser_create("time_only_tz", "regex", "^(?<time>.*)$",
+                               FLB_FALSE, "%H:%M:%S", "time",
+                               NULL, FLB_FALSE, FLB_TRUE, FLB_TRUE,
+                               FLB_FALSE, NULL, 0, NULL, config);
+    TEST_CHECK(parser != NULL);
+
+    memset(&tm, 0, sizeof(struct flb_tm));
+    ret = flb_parser_time_lookup("00:01:00", strlen("00:01:00"), now,
+                                 parser, &tm, &ns);
+    TEST_CHECK(ret == 0);
+    TEST_CHECK(ns == 0);
+
+    memset(&expected_tm, 0, sizeof(struct tm));
+    expected_tm.tm_year = 2025 - 1900;
+    expected_tm.tm_mon = 3;
+    expected_tm.tm_mday = 15;
+    expected_tm.tm_hour = 0;
+    expected_tm.tm_min = 1;
+    expected_tm.tm_sec = 0;
+    expected_tm.tm_isdst = -1;
+    epoch = mktime(&expected_tm);
+    TEST_CHECK(epoch != (time_t) -1);
+    TEST_CHECK(epoch == flb_parser_tm2time(&tm, FLB_TRUE));
+
+    if (orig_tz != NULL) {
+        setenv("TZ", orig_tz, 1);
+        flb_free(orig_tz);
+    }
+    else {
+        unsetenv("TZ");
+    }
+    tzset();
+
+    flb_parser_destroy(parser);
+    flb_parser_exit(config);
+    flb_config_exit(config);
+}
+
 
 TEST_LIST = {
     { "tzone_offset", test_parser_tzone_offset},
     { "time_lookup", test_parser_time_lookup},
     { "json_time_lookup", test_json_parser_time_lookup},
     { "regex_time_lookup", test_regex_parser_time_lookup},
+    { "time_system_timezone_midnight", test_parser_time_system_timezone_midnight},
     { "mysql_unquoted" , test_mysql_unquoted },
     { 0 }
 };