mirror of
https://codeberg.org/Freedium-cfd/web.git
synced 2026-03-11 09:04:37 +00:00
feat(datetime): add utility for timestamp conversion
This commit is contained in:
parent
7f9f21d1ba
commit
e6684c71c0
3 changed files with 170 additions and 0 deletions
|
|
@ -13,3 +13,14 @@ repos:
|
|||
- id: check-added-large-files
|
||||
- id: check-ast
|
||||
- id: check-merge-conflict
|
||||
|
||||
# - repo: local
|
||||
# hooks:
|
||||
# - id: pytest-check
|
||||
# name: pytest-check
|
||||
# entry: bash -c 'cd freedium-library && pdm run pytest'
|
||||
# language: system
|
||||
# types: [python]
|
||||
# pass_filenames: false
|
||||
# always_run: true
|
||||
# verbose: true
|
||||
|
|
|
|||
34
freedium-library/src/freedium_library/utils/datetime.py
Normal file
34
freedium-library/src/freedium_library/utils/datetime.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from datetime import datetime
|
||||
from typing import Optional, Union
|
||||
|
||||
import pendulum
|
||||
|
||||
|
||||
class DateTimeUtils:
|
||||
@staticmethod
|
||||
def to_milliseconds(
|
||||
dt_input: Optional[Union[str, datetime]] = None, tz: str = "UTC"
|
||||
) -> int:
|
||||
"""
|
||||
Convert input to milliseconds with timezone support.
|
||||
|
||||
Args:
|
||||
dt_input: String, datetime, or None for current time
|
||||
tz: Timezone string (default: 'UTC')
|
||||
|
||||
Returns:
|
||||
Timestamp in milliseconds
|
||||
"""
|
||||
try:
|
||||
if dt_input is None:
|
||||
return pendulum.now(tz).int_timestamp * 1000
|
||||
|
||||
if isinstance(dt_input, str):
|
||||
dt = pendulum.parse(dt_input, tz=tz)
|
||||
else:
|
||||
dt = pendulum.instance(dt_input).in_timezone(tz)
|
||||
|
||||
return dt.int_timestamp * 1000
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid datetime input: {e}") # type: NoReturn
|
||||
125
freedium-library/src/freedium_library/utils/datetime_test.py
Normal file
125
freedium-library/src/freedium_library/utils/datetime_test.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
from datetime import datetime
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from freezegun import freeze_time
|
||||
|
||||
from freedium_library.utils.datetime import DateTimeUtils
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fixed_datetime() -> datetime:
|
||||
return datetime(2024, 1, 1, 12, 0, 0)
|
||||
|
||||
|
||||
@freeze_time("2024-01-01 12:00:00")
|
||||
def test_current_time() -> None:
|
||||
# Should return current time when no input is provided
|
||||
result = DateTimeUtils.to_milliseconds()
|
||||
expected = 1704110400000 # 2024-01-01 12:00:00 UTC in milliseconds
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_string_input() -> None:
|
||||
# Test various string formats
|
||||
cases: list[Tuple[str, int]] = [
|
||||
("2024-01-01 12:00:00", 1704110400000),
|
||||
("2024-01-01T12:00:00Z", 1704110400000),
|
||||
("2024-01-01", 1704067200000), # Start of day
|
||||
]
|
||||
|
||||
for input_str, expected in cases:
|
||||
assert DateTimeUtils.to_milliseconds(input_str) == expected
|
||||
|
||||
|
||||
def test_datetime_input(fixed_datetime: datetime) -> None:
|
||||
result = DateTimeUtils.to_milliseconds(fixed_datetime)
|
||||
expected = 1704110400000 # 2024-01-01 12:00:00 UTC
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_timezone_handling() -> None:
|
||||
dt_str: str = "2024-01-01 12:00:00"
|
||||
|
||||
utc_result = DateTimeUtils.to_milliseconds(dt_str, tz="UTC")
|
||||
est_result = DateTimeUtils.to_milliseconds(dt_str, tz="America/New_York")
|
||||
|
||||
# When it's 12:00 in EST, it's 17:00 in UTC (EST+5)
|
||||
assert est_result == utc_result + (5 * 3600 * 1000)
|
||||
|
||||
|
||||
def test_invalid_inputs() -> None:
|
||||
invalid_inputs: list[str] = [
|
||||
"not a date",
|
||||
"2024-13-01", # Invalid month
|
||||
"2024-01-32", # Invalid day
|
||||
]
|
||||
|
||||
for invalid_input in invalid_inputs:
|
||||
with pytest.raises(ValueError):
|
||||
DateTimeUtils.to_milliseconds(invalid_input)
|
||||
|
||||
|
||||
def test_comprehensive_timezone_handling() -> None:
|
||||
dt_str: str = "2024-01-01 12:00:00"
|
||||
|
||||
# Test various timezone offsets
|
||||
tokyo_result = DateTimeUtils.to_milliseconds(dt_str, tz="Asia/Tokyo")
|
||||
utc_result = DateTimeUtils.to_milliseconds(dt_str, tz="UTC")
|
||||
la_result = DateTimeUtils.to_milliseconds(dt_str, tz="America/Los_Angeles")
|
||||
|
||||
# Tokyo is UTC+9, LA is UTC-8
|
||||
assert tokyo_result == utc_result - (9 * 3600 * 1000)
|
||||
assert la_result == utc_result + (8 * 3600 * 1000)
|
||||
|
||||
|
||||
def test_dst_handling() -> None:
|
||||
# Test during DST period (July)
|
||||
summer_dt: str = "2024-07-01 12:00:00"
|
||||
ny_summer = DateTimeUtils.to_milliseconds(summer_dt, tz="America/New_York")
|
||||
utc_summer = DateTimeUtils.to_milliseconds(summer_dt, tz="UTC")
|
||||
|
||||
# During DST, NY is UTC-4
|
||||
assert ny_summer == utc_summer + (4 * 3600 * 1000)
|
||||
|
||||
# Test outside DST period (January)
|
||||
winter_dt: str = "2024-01-01 12:00:00"
|
||||
ny_winter = DateTimeUtils.to_milliseconds(winter_dt, tz="America/New_York")
|
||||
utc_winter = DateTimeUtils.to_milliseconds(winter_dt, tz="UTC")
|
||||
|
||||
# Outside DST, NY is UTC-5
|
||||
assert ny_winter == utc_winter + (5 * 3600 * 1000)
|
||||
|
||||
|
||||
def test_timezone_aware_string_input() -> None:
|
||||
cases: list[Tuple[str, str, int]] = [
|
||||
# ISO format with timezone
|
||||
(
|
||||
"2024-01-01T12:00:00+09:00",
|
||||
"UTC",
|
||||
1704078000000,
|
||||
), # Tokyo time converted to UTC
|
||||
(
|
||||
"2024-01-01T12:00:00-05:00",
|
||||
"UTC",
|
||||
1704128400000,
|
||||
), # EST time converted to UTC
|
||||
# Named timezone in string
|
||||
("2024-01-01T12:00:00-00:00", "UTC", 1704110400000),
|
||||
]
|
||||
|
||||
for input_str, target_tz, expected in cases:
|
||||
assert int(DateTimeUtils.to_milliseconds(input_str, tz=target_tz)) == int(
|
||||
expected
|
||||
)
|
||||
|
||||
|
||||
def test_date_boundary_cases() -> None:
|
||||
# Test date boundary cases across timezones
|
||||
dt_str: str = "2024-01-01 00:00:00"
|
||||
|
||||
tokyo = DateTimeUtils.to_milliseconds(dt_str, tz="Asia/Tokyo")
|
||||
la = DateTimeUtils.to_milliseconds(dt_str, tz="America/Los_Angeles")
|
||||
|
||||
# When it's midnight in Tokyo, it's still previous day in LA
|
||||
assert la - tokyo == 17 * 3600 * 1000 # 17 hours difference
|
||||
Loading…
Reference in a new issue