mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-01 00:05:43 +08:00
fix(common/time_utils): correct None/empty timestamp fallback and ISO 8601 parsing (#16483)
Recovery PR for #16173 after the fork branch was accidentally reset during rewrite-cleanup. Cherry-picked onto current `main`: - fix(common/time_utils): correct fallback timestamp and ISO-8601 normalization - fix(common/time_utils): preserve zero timestamps and mark regression tests - test(common/time_utils): make fallback assertions deterministic Supersedes closed #16173 — same branch `Harsh23Kashyap/fix/time-utils-edgecases`, rebuilt per @yuzhichang recovery steps in https://github.com/infiniflow/ragflow/pull/16173#issuecomment-4829663835 --------- Co-authored-by: Harsh Kashyap <harshkashyap@Harshs-MacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,8 +46,9 @@ def timestamp_to_date(timestamp, format_string="%Y-%m-%d %H:%M:%S"):
|
||||
>>> timestamp_to_date(1704067200000)
|
||||
'2024-01-01 08:00:00'
|
||||
"""
|
||||
if not timestamp:
|
||||
timestamp = time.time()
|
||||
if timestamp is None or timestamp == "":
|
||||
timestamp = current_timestamp()
|
||||
logging.debug("timestamp_to_date received empty timestamp; using current_timestamp() fallback")
|
||||
timestamp = int(timestamp) / 1000
|
||||
time_array = time.localtime(timestamp)
|
||||
str_date = time.strftime(format_string, time_array)
|
||||
@@ -144,11 +145,8 @@ def format_iso_8601_to_ymd_hms(time_str: str) -> str:
|
||||
from dateutil import parser
|
||||
|
||||
try:
|
||||
if parser.isoparse(time_str):
|
||||
dt = datetime.datetime.fromisoformat(time_str.replace("Z", "+00:00"))
|
||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
return time_str
|
||||
dt = parser.isoparse(time_str)
|
||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
except Exception as e:
|
||||
logging.error(str(e))
|
||||
return time_str
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import time
|
||||
import datetime
|
||||
import pytest
|
||||
from common.time_utils import current_timestamp, timestamp_to_date, date_string_to_timestamp, datetime_format, delta_seconds
|
||||
from common.time_utils import current_timestamp, timestamp_to_date, date_string_to_timestamp, datetime_format, delta_seconds, format_iso_8601_to_ymd_hms
|
||||
|
||||
|
||||
class TestCurrentTimestamp:
|
||||
@@ -650,4 +650,65 @@ class TestDeltaSeconds:
|
||||
# If we're testing on the first day of month
|
||||
date_string = "2024-01-31 12:00:00" # Use a known past date
|
||||
result = delta_seconds(date_string)
|
||||
assert result > 0
|
||||
assert result > 0
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
class TestTimestampToDateCurrentTimeFallback:
|
||||
"""Regression tests for the None/empty fallback of timestamp_to_date.
|
||||
|
||||
The docstring promises "If None or empty, uses current time", but the
|
||||
fallback assigned ``time.time()`` (seconds) and then divided by 1000 again,
|
||||
producing a date around 1970-01-21 instead of now. The existing
|
||||
``test_return_type_always_string`` only checks the return type, so it never
|
||||
caught this. These tests pin the behaviour by value.
|
||||
"""
|
||||
|
||||
def test_none_uses_current_time(self, monkeypatch):
|
||||
"""None input must resolve to current_timestamp() fallback."""
|
||||
fixed_ms = 1704067200123
|
||||
monkeypatch.setattr("common.time_utils.current_timestamp", lambda: fixed_ms)
|
||||
assert timestamp_to_date(None) == time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(fixed_ms / 1000)
|
||||
)
|
||||
|
||||
def test_empty_string_uses_current_time(self, monkeypatch):
|
||||
"""Empty-string input must resolve to current_timestamp() fallback."""
|
||||
fixed_ms = 1704067200123
|
||||
monkeypatch.setattr("common.time_utils.current_timestamp", lambda: fixed_ms)
|
||||
assert timestamp_to_date("") == time.strftime(
|
||||
"%Y-%m-%d %H:%M:%S", time.localtime(fixed_ms / 1000)
|
||||
)
|
||||
|
||||
def test_zero_timestamp_is_not_treated_as_empty(self):
|
||||
"""Zero timestamp should map to Unix epoch, not fallback to current time."""
|
||||
assert timestamp_to_date(0) == time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(0))
|
||||
|
||||
|
||||
@pytest.mark.p2
|
||||
class TestFormatIso8601ToYmdHms:
|
||||
"""Test cases for format_iso_8601_to_ymd_hms function."""
|
||||
|
||||
def test_standard_utc_z(self):
|
||||
"""Standard UTC timestamp with trailing Z."""
|
||||
assert format_iso_8601_to_ymd_hms("2024-01-01T12:00:00Z") == "2024-01-01 12:00:00"
|
||||
|
||||
def test_explicit_utc_offset(self):
|
||||
"""Timestamp with an explicit +00:00 offset."""
|
||||
assert format_iso_8601_to_ymd_hms("2024-01-01T12:00:00+00:00") == "2024-01-01 12:00:00"
|
||||
|
||||
def test_ordinal_date_extended(self):
|
||||
"""ISO 8601 ordinal date (day-of-year), extended form.
|
||||
|
||||
dateutil.isoparse accepts it but datetime.fromisoformat rejects it,
|
||||
which previously made the function silently return the input unchanged.
|
||||
"""
|
||||
assert format_iso_8601_to_ymd_hms("2024-001T12:00:00Z") == "2024-01-01 12:00:00"
|
||||
|
||||
def test_ordinal_date_basic(self):
|
||||
"""ISO 8601 ordinal date (day-of-year), basic form."""
|
||||
assert format_iso_8601_to_ymd_hms("2024001T120000Z") == "2024-01-01 12:00:00"
|
||||
|
||||
def test_invalid_string_returns_original(self):
|
||||
"""Unparseable input is returned unchanged."""
|
||||
assert format_iso_8601_to_ymd_hms("not-a-date") == "not-a-date"
|
||||
Reference in New Issue
Block a user