From 205574fa2cb23cb8d46d071253b2f374c7547562 Mon Sep 17 00:00:00 2001 From: Donkie Date: Mon, 2 Oct 2023 21:35:46 +0200 Subject: [PATCH] Fixed timezone being returned incorrectly Happened if you've set TZ to non-UTC. Apparently astimezone and replace doesn't do the same thing even if tzinfo is None. --- spoolman/api/v1/models.py | 2 +- spoolman/database/spool.py | 8 ++++---- .../docker-compose-cockroachdb.yml | 2 ++ tests_integration/docker-compose-mariadb.yml | 2 ++ tests_integration/docker-compose-postgres.yml | 3 +++ tests_integration/docker-compose-sqlite.yml | 1 + tests_integration/tests/spool/test_use.py | 17 +++++++++++++++++ 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/spoolman/api/v1/models.py b/spoolman/api/v1/models.py index 7075692..9364c26 100644 --- a/spoolman/api/v1/models.py +++ b/spoolman/api/v1/models.py @@ -13,7 +13,7 @@ from spoolman.math import length_from_weight def datetime_to_str(dt: datetime) -> str: """Convert a datetime object to a string.""" if dt.tzinfo is None: - dt = dt.astimezone(tz=timezone.utc) + dt = dt.replace(tzinfo=timezone.utc) return dt.isoformat().replace("+00:00", "Z") diff --git a/spoolman/database/spool.py b/spoolman/database/spool.py index db20086..da9148f 100644 --- a/spoolman/database/spool.py +++ b/spoolman/database/spool.py @@ -230,8 +230,8 @@ async def use_weight(db: AsyncSession, spool_id: int, weight: float) -> models.S spool = await get_by_id(db, spool_id) if spool.first_used is None: - spool.first_used = datetime.utcnow() - spool.last_used = datetime.utcnow() + spool.first_used = datetime.utcnow().replace(microsecond=0) + spool.last_used = datetime.utcnow().replace(microsecond=0) await db.commit() await spool_changed(spool) @@ -275,8 +275,8 @@ async def use_length(db: AsyncSession, spool_id: int, length: float) -> models.S spool = await get_by_id(db, spool_id) if spool.first_used is None: - spool.first_used = datetime.utcnow() - spool.last_used = datetime.utcnow() + spool.first_used = datetime.utcnow().replace(microsecond=0) + spool.last_used = datetime.utcnow().replace(microsecond=0) await db.commit() await spool_changed(spool) diff --git a/tests_integration/docker-compose-cockroachdb.yml b/tests_integration/docker-compose-cockroachdb.yml index bc1ce04..e549bd6 100644 --- a/tests_integration/docker-compose-cockroachdb.yml +++ b/tests_integration/docker-compose-cockroachdb.yml @@ -6,6 +6,7 @@ services: environment: - COCKROACH_USER=john - COCKROACH_DATABASE=spoolman + - TZ=Asia/Seoul healthcheck: test: [ @@ -26,6 +27,7 @@ services: - SPOOLMAN_DB_NAME=spoolman - SPOOLMAN_DB_USERNAME=john - SPOOLMAN_LOGGING_LEVEL=INFO + - TZ=Europe/Stockholm depends_on: db: condition: service_healthy diff --git a/tests_integration/docker-compose-mariadb.yml b/tests_integration/docker-compose-mariadb.yml index 517503c..abd1e83 100644 --- a/tests_integration/docker-compose-mariadb.yml +++ b/tests_integration/docker-compose-mariadb.yml @@ -8,6 +8,7 @@ services: - MARIADB_PASSWORD=abc - MARIADB_DATABASE=spoolman - MARIADB_MYSQL_LOCALHOST_USER=true + - TZ=Asia/Seoul healthcheck: test: [ @@ -30,6 +31,7 @@ services: - SPOOLMAN_DB_USERNAME=john - SPOOLMAN_DB_PASSWORD=abc - SPOOLMAN_LOGGING_LEVEL=INFO + - TZ=Europe/Stockholm depends_on: db: condition: service_healthy diff --git a/tests_integration/docker-compose-postgres.yml b/tests_integration/docker-compose-postgres.yml index f1e0980..68ef5b4 100644 --- a/tests_integration/docker-compose-postgres.yml +++ b/tests_integration/docker-compose-postgres.yml @@ -4,6 +4,8 @@ services: image: postgres:11-alpine environment: - POSTGRES_PASSWORD=abc + - TZ='GMT+4' + - PGTZ='GMT+4' spoolman: image: donkie/spoolman:test environment: @@ -14,6 +16,7 @@ services: - SPOOLMAN_DB_USERNAME=postgres - SPOOLMAN_DB_PASSWORD=abc - SPOOLMAN_LOGGING_LEVEL=INFO + - TZ=Europe/Stockholm depends_on: - db tester: diff --git a/tests_integration/docker-compose-sqlite.yml b/tests_integration/docker-compose-sqlite.yml index f2eafdc..b98c56b 100644 --- a/tests_integration/docker-compose-sqlite.yml +++ b/tests_integration/docker-compose-sqlite.yml @@ -4,6 +4,7 @@ services: image: donkie/spoolman:test environment: - SPOOLMAN_LOGGING_LEVEL=INFO + - TZ=Europe/Stockholm tester: image: donkie/spoolman-tester:latest volumes: diff --git a/tests_integration/tests/spool/test_use.py b/tests_integration/tests/spool/test_use.py index 42a5d5d..103d0e2 100644 --- a/tests_integration/tests/spool/test_use.py +++ b/tests_integration/tests/spool/test_use.py @@ -2,6 +2,7 @@ import asyncio import math +from datetime import datetime, timezone from typing import Any import httpx @@ -41,6 +42,14 @@ def test_use_spool_weight(random_filament: dict[str, Any], use_weight: float): assert spool["used_weight"] == pytest.approx(max(use_weight, 0)) assert spool["remaining_weight"] == pytest.approx(min(max(start_weight - use_weight, 0), filament_net_weight)) + # Verify that first_used has been updated + diff = abs((datetime.now(tz=timezone.utc) - datetime.fromisoformat(spool["first_used"])).total_seconds()) + assert diff < 60 + + # Verify that last_used has been updated + diff = abs((datetime.now(tz=timezone.utc) - datetime.fromisoformat(spool["last_used"])).total_seconds()) + assert diff < 60 + # Clean up httpx.delete(f"{URL}/api/v1/spool/{spool['id']}").raise_for_status() @@ -56,6 +65,7 @@ def test_use_spool_length(random_filament: dict[str, Any], use_length: float): json={ "filament_id": random_filament["id"], "remaining_weight": start_weight, + "first_used": "2023-01-01T00:00:00Z", }, ) result.raise_for_status() @@ -79,6 +89,13 @@ def test_use_spool_length(random_filament: dict[str, Any], use_length: float): assert spool["used_weight"] == pytest.approx(max(use_weight, 0)) assert spool["remaining_weight"] == pytest.approx(min(max(start_weight - use_weight, 0), filament_net_weight)) + # Verify that first_used hasn't been updated since it was already set + assert spool["first_used"] == "2023-01-01T00:00:00Z" + + # Verify that last_used has been updated + diff = abs((datetime.now(tz=timezone.utc) - datetime.fromisoformat(spool["last_used"])).total_seconds()) + assert diff < 60 + # Clean up httpx.delete(f"{URL}/api/v1/spool/{spool['id']}").raise_for_status()