From 128efd80623f6a276f717712cad947c793eae97c Mon Sep 17 00:00:00 2001 From: Furkan Coban Date: Sun, 1 Nov 2026 13:03:59 +0000 Subject: [PATCH] fix: normalize Go zero time in BackupListReturn and ExportStatusReturn completed_at (#2216) --- test/test_backup.py | 26 ++++++++++++++++++++++++++ weaviate/backup/backup.py | 9 ++++++++- weaviate/export/export.py | 9 ++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/test/test_backup.py b/test/test_backup.py index 225588581..8321fe2b4 100644 --- a/test/test_backup.py +++ b/test/test_backup.py @@ -39,3 +39,29 @@ def test_list_return_defaults_incremental_base_backup_id_to_none(entry: Dict[str # The server omits the field for non-incremental backups, and only populates # it for callers it has confirmed as root, so absent must not be an error. assert BackupListReturn(**entry).incremental_base_backup_id is None + + +def test_running_backup_and_export_zero_completed_at_is_none() -> None: + from weaviate.export.export import ExportStatusReturn + + backup = BackupListReturn( + **_list_entry(status="STARTED", completedAt="0001-01-01T00:00:00.000Z") + ) + assert backup.completed_at is None + + completed = BackupListReturn( + **_list_entry(status="SUCCESS", completedAt="2026-08-01T10:05:00.000Z") + ) + assert completed.completed_at is not None + assert completed.completed_at.year == 2026 + + export_status = ExportStatusReturn( + id="exp1", + backend="filesystem", + path="/tmp/exports/exp1", + status="TRANSFERRING", + startedAt="2026-10-21T06:50:47.995Z", + completedAt="0001-01-01T00:00:00.000Z", + classes=["Article"], + ) + assert export_status.completed_at is None diff --git a/weaviate/backup/backup.py b/weaviate/backup/backup.py index f00a4aed8..d31bda3f5 100644 --- a/weaviate/backup/backup.py +++ b/weaviate/backup/backup.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Any, Dict, List, Optional, cast -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from weaviate.backup.backup_location import _BackupLocationConfig @@ -107,3 +107,10 @@ class BackupListReturn(BaseModel): # None when the backup is not incremental, and also when the server omits the # field: it is only returned to callers Weaviate has confirmed as root. incremental_base_backup_id: Optional[str] = Field(default=None) + + @field_validator("completed_at", mode="after") + @classmethod + def _normalize_zero_completed_at(cls, v: Optional[datetime]) -> Optional[datetime]: + if v is not None and v.year <= 1: + return None + return v diff --git a/weaviate/export/export.py b/weaviate/export/export.py index fe94afa5c..3bd37fdaf 100644 --- a/weaviate/export/export.py +++ b/weaviate/export/export.py @@ -4,7 +4,7 @@ from enum import Enum from typing import Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, field_validator from weaviate.backup.backup import BackupStorage @@ -69,3 +69,10 @@ class ExportStatusReturn(ExportCreateReturn): ) error: Optional[str] = None took_in_ms: Optional[int] = Field(alias="tookInMs", default=None) + + @field_validator("completed_at", mode="after") + @classmethod + def _normalize_zero_completed_at(cls, v: Optional[datetime]) -> Optional[datetime]: + if v is not None and v.year <= 1: + return None + return v