Skip to content

Commit 1a3fe90

Browse files
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
1 parent 57c2616 commit 1a3fe90

4 files changed

Lines changed: 67 additions & 74 deletions

File tree

src/jupyterhub_cost_monitoring/date_utils.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
from dataclasses import dataclass
6-
from datetime import datetime, timedelta, timezone
6+
from datetime import UTC, datetime, timedelta
77

88

99
def ensure_utc_datetime(date_str: str) -> datetime:
@@ -19,10 +19,10 @@ def ensure_utc_datetime(date_str: str) -> datetime:
1919
dt = datetime.fromisoformat(date_str)
2020
if dt.tzinfo is None:
2121
# Assume UTC if no timezone provided
22-
dt = dt.replace(tzinfo=timezone.utc)
22+
dt = dt.replace(tzinfo=UTC)
2323
else:
2424
# Convert to UTC if timezone-aware
25-
dt = dt.astimezone(timezone.utc)
25+
dt = dt.astimezone(UTC)
2626
return dt
2727

2828

@@ -153,8 +153,7 @@ def parse_from_to_in_query_params(
153153
# Apply validation rules to prevent API errors
154154

155155
# Prevent "end date past the beginning of next month" errors from AWS
156-
if to_date > now_date:
157-
to_date = now_date
156+
to_date = min(to_date, now_date)
158157

159158
# Prevent "Start date (and hour) should be before end date (and hour)" errors
160159
if from_date >= now_date:
@@ -165,7 +164,5 @@ def parse_from_to_in_query_params(
165164

166165
def get_now_date():
167166
"""Get current date at midnight UTC for consistent date boundaries"""
168-
now_date = datetime.now(timezone.utc).replace(
169-
hour=0, minute=0, second=0, microsecond=0
170-
)
167+
now_date = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0)
171168
return now_date

src/jupyterhub_cost_monitoring/query_usage.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import os
66
from collections import defaultdict
7-
from datetime import datetime, timedelta, timezone
7+
from datetime import UTC, datetime, timedelta
88

99
import escapism
1010
import requests
@@ -127,7 +127,7 @@ def _process_response(
127127
hub = data["metric"]["namespace"]
128128
user = data["metric"]["username"]
129129
date = [
130-
datetime.fromtimestamp(value[0], tz=timezone.utc).strftime("%Y-%m-%d")
130+
datetime.fromtimestamp(value[0], tz=UTC).strftime("%Y-%m-%d")
131131
for value in data["values"]
132132
]
133133
usage = [float(value[1]) for value in data["values"]]

tests/conftest.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
22
import os
3-
from datetime import datetime, timezone
3+
from datetime import UTC, datetime
44
from unittest.mock import patch
55

66
import boto3
@@ -193,50 +193,50 @@ def mock_ce():
193193
@pytest.fixture
194194
def sample_utc_datetime():
195195
"""Sample UTC datetime for testing."""
196-
return datetime(2024, 1, 15, 12, 30, 45, tzinfo=timezone.utc)
196+
return datetime(2024, 1, 15, 12, 30, 45, tzinfo=UTC)
197197

198198

199199
@pytest.fixture
200200
def sample_date_range():
201201
"""Sample DateRange for testing."""
202202
from src.jupyterhub_cost_monitoring.date_utils import DateRange
203203

204-
start = datetime(2025, 9, 1, tzinfo=timezone.utc)
205-
end = datetime(2025, 9, 3, tzinfo=timezone.utc)
204+
start = datetime(2025, 9, 1, tzinfo=UTC)
205+
end = datetime(2025, 9, 3, tzinfo=UTC)
206206
return DateRange(start_date=start, end_date=end)
207207

208208

209209
@pytest.fixture
210210
def mock_current_time():
211211
"""Mock current time for consistent testing."""
212-
return datetime(2024, 2, 15, 10, 0, 0, tzinfo=timezone.utc)
212+
return datetime(2024, 2, 15, 10, 0, 0, tzinfo=UTC)
213213

214214

215215
@pytest.fixture
216216
def timezone_test_cases():
217217
"""Test cases for different timezone conversions."""
218218
return [
219219
# (input_string, expected_utc_datetime)
220-
("2024-01-15", datetime(2024, 1, 15, tzinfo=timezone.utc)),
220+
("2024-01-15", datetime(2024, 1, 15, tzinfo=UTC)),
221221
(
222222
"2024-01-15T10:00:00-05:00",
223-
datetime(2024, 1, 15, 15, 0, 0, tzinfo=timezone.utc),
223+
datetime(2024, 1, 15, 15, 0, 0, tzinfo=UTC),
224224
),
225225
(
226226
"2024-01-15T18:00:00+09:00",
227-
datetime(2024, 1, 15, 9, 0, 0, tzinfo=timezone.utc),
227+
datetime(2024, 1, 15, 9, 0, 0, tzinfo=UTC),
228228
),
229229
(
230230
"2024-01-15T12:00:00+00:00",
231-
datetime(2024, 1, 15, 12, 0, 0, tzinfo=timezone.utc),
231+
datetime(2024, 1, 15, 12, 0, 0, tzinfo=UTC),
232232
),
233233
]
234234

235235

236236
@pytest.fixture
237237
def date_validation_test_cases():
238238
"""Test cases for date validation scenarios."""
239-
base_time = datetime(2024, 2, 15, tzinfo=timezone.utc)
239+
base_time = datetime(2024, 2, 15, tzinfo=UTC)
240240
return {
241241
"current_time": base_time,
242242
"future_date": "2024-03-01", # Future end date

tests/test_date_utils.py

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
API-specific formatting, and caching integration.
66
"""
77

8-
from datetime import datetime, timedelta, timezone
8+
from datetime import UTC, datetime, timedelta
99
from unittest.mock import patch
1010

1111
import pytest
@@ -24,45 +24,43 @@ class TestEnsureUTCDateTime:
2424
def test_timezone_naive_string_assumes_utc(self):
2525
"""Test that timezone-naive strings are assumed to be UTC."""
2626
result = ensure_utc_datetime("2025-01-15")
27-
expected = datetime(2025, 1, 15, tzinfo=timezone.utc)
27+
expected = datetime(2025, 1, 15, tzinfo=UTC)
2828
assert result == expected
29-
assert result.tzinfo == timezone.utc
29+
assert result.tzinfo == UTC
3030

3131
def test_timezone_aware_string_converts_to_utc(self):
3232
"""Test that timezone-aware strings are converted to UTC."""
3333
# Test with EST (UTC-5)
3434
result = ensure_utc_datetime("2025-01-15T10:00:00-05:00")
35-
expected = datetime(
36-
2025, 1, 15, 15, 0, 0, tzinfo=timezone.utc
37-
) # 10 AM EST = 3 PM UTC
35+
expected = datetime(2025, 1, 15, 15, 0, 0, tzinfo=UTC) # 10 AM EST = 3 PM UTC
3836
assert result == expected
39-
assert result.tzinfo == timezone.utc
37+
assert result.tzinfo == UTC
4038

4139
def test_iso_formats(self):
4240
"""Test various ISO format inputs."""
4341
test_cases = [
44-
("2025-01-15", datetime(2025, 1, 15, tzinfo=timezone.utc)),
45-
("2025-01-15T00:00:00", datetime(2025, 1, 15, tzinfo=timezone.utc)),
42+
("2025-01-15", datetime(2025, 1, 15, tzinfo=UTC)),
43+
("2025-01-15T00:00:00", datetime(2025, 1, 15, tzinfo=UTC)),
4644
(
4745
"2025-01-15T12:30:45",
48-
datetime(2025, 1, 15, 12, 30, 45, tzinfo=timezone.utc),
46+
datetime(2025, 1, 15, 12, 30, 45, tzinfo=UTC),
4947
),
5048
# Z suffix indicates UTC timezone (Zulu time)
5149
(
5250
"2025-01-15T12:30:45Z",
53-
datetime(2025, 1, 15, 12, 30, 45, tzinfo=timezone.utc),
51+
datetime(2025, 1, 15, 12, 30, 45, tzinfo=UTC),
5452
),
5553
]
5654

5755
for input_str, expected in test_cases:
5856
result = ensure_utc_datetime(input_str)
5957
assert result == expected
60-
assert result.tzinfo == timezone.utc
58+
assert result.tzinfo == UTC
6159

6260
def test_utc_string_unchanged(self):
6361
"""Test that UTC strings remain unchanged."""
6462
result = ensure_utc_datetime("2025-01-15T12:00:00+00:00")
65-
expected = datetime(2025, 1, 15, 12, 0, 0, tzinfo=timezone.utc)
63+
expected = datetime(2025, 1, 15, 12, 0, 0, tzinfo=UTC)
6664
assert result == expected
6765

6866
def test_different_timezones(self):
@@ -71,17 +69,17 @@ def test_different_timezones(self):
7169
# PST (UTC-8) to UTC
7270
(
7371
"2025-01-15T08:00:00-08:00",
74-
datetime(2025, 1, 15, 16, 0, 0, tzinfo=timezone.utc),
72+
datetime(2025, 1, 15, 16, 0, 0, tzinfo=UTC),
7573
),
7674
# JST (UTC+9) to UTC
7775
(
7876
"2025-01-15T18:00:00+09:00",
79-
datetime(2025, 1, 15, 9, 0, 0, tzinfo=timezone.utc),
77+
datetime(2025, 1, 15, 9, 0, 0, tzinfo=UTC),
8078
),
8179
# CET (UTC+1) to UTC
8280
(
8381
"2025-01-15T13:00:00+01:00",
84-
datetime(2025, 1, 15, 12, 0, 0, tzinfo=timezone.utc),
82+
datetime(2025, 1, 15, 12, 0, 0, tzinfo=UTC),
8583
),
8684
]
8785

@@ -95,30 +93,30 @@ class TestDateRange:
9593

9694
def test_daterange_creation(self):
9795
"""Test basic DateRange object creation."""
98-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
99-
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
96+
start = datetime(2025, 1, 1, tzinfo=UTC)
97+
end = datetime(2025, 1, 31, tzinfo=UTC)
10098
dr = DateRange(start_date=start, end_date=end)
10199

102100
assert dr.start_date == start
103101
assert dr.end_date == end
104102

105103
def test_daterange_immutability(self):
106104
"""Test that DateRange is immutable (frozen dataclass)."""
107-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
108-
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
105+
start = datetime(2025, 1, 1, tzinfo=UTC)
106+
end = datetime(2025, 1, 31, tzinfo=UTC)
109107
dr = DateRange(start_date=start, end_date=end)
110108

111109
# Attempting to modify should raise AttributeError
112110
with pytest.raises(AttributeError):
113-
dr.start_date = datetime(2025, 2, 1, tzinfo=timezone.utc)
111+
dr.start_date = datetime(2025, 2, 1, tzinfo=UTC)
114112

115113
with pytest.raises(AttributeError):
116-
dr.end_date = datetime(2025, 2, 28, tzinfo=timezone.utc)
114+
dr.end_date = datetime(2025, 2, 28, tzinfo=UTC)
117115

118116
def test_daterange_hashability(self):
119117
"""Test that DateRange objects are hashable and can be used as dict keys."""
120-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
121-
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
118+
start = datetime(2025, 1, 1, tzinfo=UTC)
119+
end = datetime(2025, 1, 31, tzinfo=UTC)
122120

123121
dr1 = DateRange(start_date=start, end_date=end)
124122
dr2 = DateRange(start_date=start, end_date=end)
@@ -132,22 +130,20 @@ def test_daterange_hashability(self):
132130

133131
def test_daterange_equality(self):
134132
"""Test DateRange equality comparison."""
135-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
136-
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
133+
start = datetime(2025, 1, 1, tzinfo=UTC)
134+
end = datetime(2025, 1, 31, tzinfo=UTC)
137135

138136
dr1 = DateRange(start_date=start, end_date=end)
139137
dr2 = DateRange(start_date=start, end_date=end)
140-
dr3 = DateRange(
141-
start_date=start, end_date=datetime(2025, 2, 1, tzinfo=timezone.utc)
142-
)
138+
dr3 = DateRange(start_date=start, end_date=datetime(2025, 2, 1, tzinfo=UTC))
143139

144140
assert dr1 == dr2
145141
assert dr1 != dr3
146142

147143
def test_aws_range_formatting(self):
148144
"""Test AWS date range formatting (exclusive end date, YYYY-MM-DD format)."""
149-
start = datetime(2025, 1, 15, 12, 30, 45, tzinfo=timezone.utc)
150-
end = datetime(2025, 1, 31, 8, 15, 30, tzinfo=timezone.utc)
145+
start = datetime(2025, 1, 15, 12, 30, 45, tzinfo=UTC)
146+
end = datetime(2025, 1, 31, 8, 15, 30, tzinfo=UTC)
151147
dr = DateRange(start_date=start, end_date=end)
152148

153149
aws_from, aws_to = dr.aws_range
@@ -159,8 +155,8 @@ def test_aws_range_formatting(self):
159155

160156
def test_prometheus_range_formatting(self):
161157
"""Test Prometheus date range formatting (inclusive dates, ISO format)."""
162-
start = datetime(2025, 1, 15, 12, 30, 45, tzinfo=timezone.utc)
163-
end = datetime(2025, 1, 31, 8, 15, 30, tzinfo=timezone.utc)
158+
start = datetime(2025, 1, 15, 12, 30, 45, tzinfo=UTC)
159+
end = datetime(2025, 1, 31, 8, 15, 30, tzinfo=UTC)
164160
dr = DateRange(start_date=start, end_date=end)
165161

166162
prom_from, prom_to = dr.prometheus_range
@@ -172,8 +168,8 @@ def test_prometheus_range_formatting(self):
172168
def test_same_logical_range_different_formats(self):
173169
"""Test that AWS and Prometheus formats represent the same logical date range."""
174170
# Create range for January 1-31, 2025
175-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
176-
end = datetime(2025, 1, 31, 23, 59, 59, tzinfo=timezone.utc)
171+
start = datetime(2025, 1, 1, tzinfo=UTC)
172+
end = datetime(2025, 1, 31, 23, 59, 59, tzinfo=UTC)
177173
dr = DateRange(start_date=start, end_date=end)
178174

179175
aws_from, aws_to = dr.aws_range
@@ -195,7 +191,7 @@ def test_default_date_range(self):
195191
"""Test default 30-day range behavior."""
196192
with patch("src.jupyterhub_cost_monitoring.date_utils.datetime") as mock_dt:
197193
# Mock current time as 2025-02-15 midnight UTC
198-
mock_now = datetime(2025, 2, 15, tzinfo=timezone.utc)
194+
mock_now = datetime(2025, 2, 15, tzinfo=UTC)
199195
mock_dt.now.return_value = mock_now
200196
mock_dt.side_effect = lambda *args, **kwargs: datetime(*args, **kwargs)
201197

@@ -218,10 +214,10 @@ def test_timezone_handling_in_parsing(self):
218214

219215
# Should be converted to UTC
220216
expected_start = datetime(
221-
2025, 1, 1, 15, 0, 0, tzinfo=timezone.utc
217+
2025, 1, 1, 15, 0, 0, tzinfo=UTC
222218
) # 10 AM EST = 3 PM UTC
223219
expected_end = datetime(
224-
2025, 1, 31, 13, 0, 0, tzinfo=timezone.utc
220+
2025, 1, 31, 13, 0, 0, tzinfo=UTC
225221
) # 3 PM CET = 1 PM UTC
226222

227223
assert result.start_date == expected_start
@@ -242,8 +238,8 @@ def cached_function(date_range: DateRange) -> str:
242238
call_count += 1
243239
return f"Result for {date_range.start_date.date()}"
244240

245-
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
246-
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
241+
start = datetime(2025, 1, 1, tzinfo=UTC)
242+
end = datetime(2025, 1, 31, tzinfo=UTC)
247243

248244
dr1 = DateRange(start_date=start, end_date=end)
249245
dr2 = DateRange(start_date=start, end_date=end) # Identical range
@@ -267,12 +263,12 @@ def cached_function(date_range: DateRange) -> str:
267263
call_count += 1
268264
return f"Call #{call_count} for {date_range.start_date.date()}"
269265

270-
start1 = datetime(2025, 1, 1, tzinfo=timezone.utc)
271-
end1 = datetime(2025, 1, 31, tzinfo=timezone.utc)
266+
start1 = datetime(2025, 1, 1, tzinfo=UTC)
267+
end1 = datetime(2025, 1, 31, tzinfo=UTC)
272268
january_range = DateRange(start_date=start1, end_date=end1)
273269

274-
start2 = datetime(2025, 2, 1, tzinfo=timezone.utc)
275-
end2 = datetime(2025, 2, 28, tzinfo=timezone.utc)
270+
start2 = datetime(2025, 2, 1, tzinfo=UTC)
271+
end2 = datetime(2025, 2, 28, tzinfo=UTC)
276272
february_range = DateRange(start_date=start2, end_date=end2)
277273

278274
result1 = cached_function(january_range)
@@ -293,12 +289,12 @@ def cached_function(date_range: DateRange) -> str:
293289
return f"Call #{call_count} for {date_range.start_date.date()}"
294290

295291
# Same dates but different times
296-
start1 = datetime(2025, 1, 1, 8, 30, 15, tzinfo=timezone.utc)
297-
end1 = datetime(2025, 1, 31, 14, 45, 22, tzinfo=timezone.utc)
292+
start1 = datetime(2025, 1, 1, 8, 30, 15, tzinfo=UTC)
293+
end1 = datetime(2025, 1, 31, 14, 45, 22, tzinfo=UTC)
298294
morning_range = DateRange(start_date=start1, end_date=end1)
299295

300-
start2 = datetime(2025, 1, 1, 16, 20, 55, tzinfo=timezone.utc)
301-
end2 = datetime(2025, 1, 31, 9, 12, 8, tzinfo=timezone.utc)
296+
start2 = datetime(2025, 1, 1, 16, 20, 55, tzinfo=UTC)
297+
end2 = datetime(2025, 1, 31, 9, 12, 8, tzinfo=UTC)
302298
evening_range = DateRange(start_date=start2, end_date=end2)
303299

304300
result1 = cached_function(morning_range)
@@ -311,8 +307,8 @@ def cached_function(date_range: DateRange) -> str:
311307
def test_daterange_normalization(self):
312308
"""Test that DateRange normalizes times for consistent caching."""
313309
# Test start date normalization to 00:00:00
314-
start_with_time = datetime(2025, 1, 1, 15, 30, 45, tzinfo=timezone.utc)
315-
end_with_time = datetime(2025, 1, 31, 8, 15, 30, tzinfo=timezone.utc)
310+
start_with_time = datetime(2025, 1, 1, 15, 30, 45, tzinfo=UTC)
311+
end_with_time = datetime(2025, 1, 31, 8, 15, 30, tzinfo=UTC)
316312
time_specific_range = DateRange(
317313
start_date=start_with_time, end_date=end_with_time
318314
)
@@ -336,12 +332,12 @@ def test_daterange_normalization(self):
336332
def test_daterange_hash_consistency_across_times(self):
337333
"""Test that DateRange objects with same dates but different times have same hash."""
338334
# Same dates, different times
339-
start1 = datetime(2025, 1, 1, 2, 30, tzinfo=timezone.utc)
340-
end1 = datetime(2025, 1, 31, 18, 45, tzinfo=timezone.utc)
335+
start1 = datetime(2025, 1, 1, 2, 30, tzinfo=UTC)
336+
end1 = datetime(2025, 1, 31, 18, 45, tzinfo=UTC)
341337
early_range = DateRange(start_date=start1, end_date=end1)
342338

343-
start2 = datetime(2025, 1, 1, 22, 15, tzinfo=timezone.utc)
344-
end2 = datetime(2025, 1, 31, 6, 20, tzinfo=timezone.utc)
339+
start2 = datetime(2025, 1, 1, 22, 15, tzinfo=UTC)
340+
end2 = datetime(2025, 1, 31, 6, 20, tzinfo=UTC)
345341
late_range = DateRange(start_date=start2, end_date=end2)
346342

347343
# Should have same hash despite different input times

0 commit comments

Comments
 (0)