diff --git a/server/app/schemas/block.py b/server/app/schemas/block.py index c853718..8ad67e3 100644 --- a/server/app/schemas/block.py +++ b/server/app/schemas/block.py @@ -1,7 +1,7 @@ -from datetime import datetime +from datetime import datetime, timezone from enum import Enum -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, field_validator class BlockType(str, Enum): @@ -18,6 +18,15 @@ class BlockBase(BaseModel): block_type: BlockType # Use the Enum here transportation_type: str | None = None + @field_validator("start_time", "end_time") + @classmethod + def validate_timezone(cls, v: datetime | None) -> datetime | None: + if v is None: + return None + if v.tzinfo is not None: + v = v.astimezone(timezone.utc).replace(tzinfo=None) + return v + class BlockCreate(BlockBase): pass diff --git a/server/tests/schemas/test_block.py b/server/tests/schemas/test_block.py new file mode 100644 index 0000000..ecfb3cc --- /dev/null +++ b/server/tests/schemas/test_block.py @@ -0,0 +1,59 @@ +from datetime import datetime, timezone +from app.schemas.block import BlockCreate, BlockType + +def test_timezone_naive_input(): + # Naive string: 2023-01-01T10:00:00 + data = { + "title": "Naive", + "start_time": "2023-01-01T10:00:00", + "end_time": "2023-01-01T12:00:00", + "detail": "detail", + "block_type": "event", + } + block = BlockCreate(**data) + assert block.start_time.tzinfo is None + assert block.start_time == datetime(2023, 1, 1, 10, 0, 0) + assert block.end_time.tzinfo is None + assert block.end_time == datetime(2023, 1, 1, 12, 0, 0) + +def test_timezone_aware_utc_input(): + # Aware UTC string: 2023-01-01T10:00:00Z + data = { + "title": "Aware UTC", + "start_time": "2023-01-01T10:00:00Z", + "end_time": "2023-01-01T12:00:00Z", + "detail": "detail", + "block_type": "event", + } + block = BlockCreate(**data) + # Pydantic parses Z as UTC aware. The validator should convert to naive UTC. + assert block.start_time.tzinfo is None + assert block.start_time == datetime(2023, 1, 1, 10, 0, 0) + assert block.end_time.tzinfo is None + assert block.end_time == datetime(2023, 1, 1, 12, 0, 0) + +def test_timezone_aware_offset_input(): + # Aware Offset string: 2023-01-01T19:00:00+09:00 (JST) -> 10:00 UTC + data = { + "title": "Aware Offset", + "start_time": "2023-01-01T19:00:00+09:00", + "end_time": "2023-01-01T21:00:00+09:00", + "detail": "detail", + "block_type": "event", + } + block = BlockCreate(**data) + # The validator should convert to UTC and make naive. + assert block.start_time.tzinfo is None + assert block.start_time == datetime(2023, 1, 1, 10, 0, 0) + assert block.end_time.tzinfo is None + assert block.end_time == datetime(2023, 1, 1, 12, 0, 0) + +def test_optional_end_time(): + data = { + "title": "Optional End Time", + "start_time": "2023-01-01T10:00:00Z", + "detail": "detail", + "block_type": "event", + } + block = BlockCreate(**data) + assert block.end_time is None