Skip to content
This repository was archived by the owner on Jun 26, 2022. It is now read-only.

Commit 52e9be0

Browse files
authored
Improve datetime string parsing (#8)
* improve datetime string parsing. * Update test_models.py * Update models.py * Update test_models.py * Update models.py * Update models.py * Update models.py * Update models.py * Update models.py * Update test_models.py * Update test_models.py * Update models.py * Update test_models.py * Update models.py * Update test_models.py * Update test_models.py
1 parent aef4dc3 commit 52e9be0

File tree

2 files changed

+54
-19
lines changed

2 files changed

+54
-19
lines changed

sonarr/models.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@
77
from .exceptions import SonarrError
88

99

10+
def dt_str_to_dt(dt_str: str) -> datetime:
11+
"""Convert ISO-8601 datetime string to datetime object."""
12+
utc = False
13+
14+
if "Z" in dt_str:
15+
utc = True
16+
dt_str = dt_str[:-1]
17+
18+
if "." in dt_str:
19+
# Python doesn't support long microsecond values
20+
ts_bits = dt_str.split(".", 1)
21+
dt_str = "{}.{}".format(ts_bits[0], ts_bits[1][:2])
22+
fmt = "%Y-%m-%dT%H:%M:%S.%f"
23+
else:
24+
fmt = "%Y-%m-%dT%H:%M:%S"
25+
26+
if utc:
27+
dt_str += "Z"
28+
fmt += "%z"
29+
30+
return datetime.strptime(dt_str, fmt)
31+
32+
1033
@dataclass(frozen=True)
1134
class Disk:
1235
"""Object holding disk information from Sonarr."""
@@ -85,15 +108,15 @@ def from_dict(data: dict):
85108
"""Return Series object from Sonarr API response."""
86109
premiere = data.get("firstAired", None)
87110
if premiere is not None:
88-
premiere = datetime.strptime(premiere, "%Y-%m-%dT%H:%M:%S%z")
111+
premiere = dt_str_to_dt(premiere)
89112

90113
added = data.get("added", None)
91114
if added is not None:
92-
added = datetime.strptime(added, "%Y-%m-%dT%H:%M:%S.%f%z")
115+
added = dt_str_to_dt(added)
93116

94117
synced = data.get("lastInfoSync", None)
95118
if synced is not None:
96-
synced = datetime.strptime(synced, "%Y-%m-%dT%H:%M:%S.%f%z")
119+
synced = dt_str_to_dt(synced)
97120

98121
poster = None
99122
for image in data.get("images", []):
@@ -151,7 +174,7 @@ def from_dict(data: dict):
151174
"""Return Episode object from Sonarr API response."""
152175
airs = data.get("airDateUtc", None)
153176
if airs is not None:
154-
airs = datetime.strptime(airs, "%Y-%m-%dT%H:%M:%S%z")
177+
airs = dt_str_to_dt(airs)
155178

156179
episode_number = data.get("episodeNumber", 0)
157180
season_number = data.get("seasonNumber", 0)
@@ -215,14 +238,14 @@ def from_dict(data: dict):
215238
queued = started
216239

217240
if started is not None:
218-
started = datetime.strptime(started, "%Y-%m-%dT%H:%M:%S.%f%z")
241+
started = dt_str_to_dt(started)
219242

220243
if queued is not None:
221-
queued = datetime.strptime(queued, "%Y-%m-%dT%H:%M:%S.%f%z")
244+
queued = dt_str_to_dt(queued)
222245

223246
changed = data.get("stateChangeTime", None)
224247
if changed is not None:
225-
changed = datetime.strptime(changed, "%Y-%m-%dT%H:%M:%S.%f%z")
248+
changed = dt_str_to_dt(changed)
226249

227250
return CommandItem(
228251
command_id=data.get("id", 0),
@@ -264,7 +287,7 @@ def from_dict(data: dict):
264287

265288
eta = data.get("estimatedCompletionTime", None)
266289
if eta is not None:
267-
eta = datetime.strptime(eta, "%Y-%m-%dT%H:%M:%S.%f%z")
290+
eta = dt_str_to_dt(eta)
268291

269292
return QueueItem(
270293
queue_id=data.get("id", 0),

tests/test_models.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ def test_application_no_data() -> None:
4040
models.Application({})
4141

4242

43+
def test_dt_str_to_dt() -> None:
44+
"""Test the dt_str_to_dt method."""
45+
dt = models.dt_str_to_dt("2018-05-14T19:02:13.101496Z")
46+
assert dt == datetime(2018, 5, 14, 19, 2, 13, 100000, tzinfo=timezone.utc)
47+
48+
49+
def test_dt_str_to_dt_long_microseconds() -> None:
50+
"""Test the dt_str_to_dt method with long microseconds."""
51+
dt = models.dt_str_to_dt("2018-05-14T19:02:13.1014986Z")
52+
assert dt == datetime(2018, 5, 14, 19, 2, 13, 100000, tzinfo=timezone.utc)
53+
54+
4355
def test_info() -> None:
4456
"""Test the Info model."""
4557
info = models.Info.from_dict(INFO)
@@ -59,9 +71,9 @@ def test_command_item() -> None:
5971
assert item.state == "started"
6072
assert item.priority == "normal"
6173
assert item.trigger == "manual"
62-
assert item.started == datetime(2020, 4, 6, 16, 54, 6, 421322, tzinfo=timezone.utc)
63-
assert item.queued == datetime(2020, 4, 6, 16, 54, 6, 419450, tzinfo=timezone.utc)
64-
assert item.changed == datetime(2020, 4, 6, 16, 54, 6, 421322, tzinfo=timezone.utc)
74+
assert item.started == datetime(2020, 4, 6, 16, 54, 6, 420000, tzinfo=timezone.utc)
75+
assert item.queued == datetime(2020, 4, 6, 16, 54, 6, 410000, tzinfo=timezone.utc)
76+
assert item.changed == datetime(2020, 4, 6, 16, 54, 6, 420000, tzinfo=timezone.utc)
6577

6678
item = models.CommandItem.from_dict(COMMAND[1])
6779

@@ -71,9 +83,9 @@ def test_command_item() -> None:
7183
assert item.state == "started"
7284
assert item.priority == "unknown"
7385
assert item.trigger == "unknown"
74-
assert item.started == datetime(2020, 4, 6, 16, 57, 51, 406504, tzinfo=timezone.utc)
75-
assert item.queued == datetime(2020, 4, 6, 16, 57, 51, 406504, tzinfo=timezone.utc)
76-
assert item.changed == datetime(2020, 4, 6, 16, 57, 51, 417931, tzinfo=timezone.utc)
86+
assert item.started == datetime(2020, 4, 6, 16, 57, 51, 400000, tzinfo=timezone.utc)
87+
assert item.queued == datetime(2020, 4, 6, 16, 57, 51, 400000, tzinfo=timezone.utc)
88+
assert item.changed == datetime(2020, 4, 6, 16, 57, 51, 410000, tzinfo=timezone.utc)
7789

7890

7991
def test_episode() -> None:
@@ -125,7 +137,7 @@ def test_queue_item() -> None:
125137
assert item.protocol == "usenet"
126138
assert item.size == 4472186820
127139
assert item.size_remaining == 0
128-
assert item.eta == datetime(2016, 2, 5, 22, 46, 52, 440104, tzinfo=timezone.utc)
140+
assert item.eta == datetime(2016, 2, 5, 22, 46, 52, 440000, tzinfo=timezone.utc)
129141
assert item.time_remaining == "00:00:00"
130142

131143
assert item.episode
@@ -177,10 +189,10 @@ def test_series() -> None:
177189
assert series.certification == "TV-14"
178190
assert series.genres == ["Animation", "Comedy"]
179191
assert series.added == datetime(
180-
2011, 1, 26, 19, 25, 55, 455594, tzinfo=timezone.utc
192+
2011, 1, 26, 19, 25, 55, 450000, tzinfo=timezone.utc
181193
)
182194
assert series.synced == datetime(
183-
2014, 1, 26, 19, 25, 55, 455594, tzinfo=timezone.utc
195+
2014, 1, 26, 19, 25, 55, 450000, tzinfo=timezone.utc
184196
)
185197

186198

@@ -228,10 +240,10 @@ def test_series_item() -> None:
228240
assert item.series.certification == "TV-G"
229241
assert item.series.genres == ["Comedy"]
230242
assert item.series.added == datetime(
231-
2020, 4, 5, 20, 40, 20, 50044, tzinfo=timezone.utc
243+
2020, 4, 5, 20, 40, 20, 50000, tzinfo=timezone.utc
232244
)
233245
assert item.series.synced == datetime(
234-
2020, 4, 5, 20, 40, 21, 545669, tzinfo=timezone.utc
246+
2020, 4, 5, 20, 40, 21, 540000, tzinfo=timezone.utc
235247
)
236248

237249
assert item.seasons

0 commit comments

Comments
 (0)