Skip to content

Commit 88c60d7

Browse files
committed
Remove all uses of utcfromtimestamp
utcfromtimestamp is deprecated, replace with datetime.fromtimestamp(timestamp, timezone.utc). Note that the former returns naive datetime objects whereas the latter returns timezone aware objects.
1 parent f9fceb5 commit 88c60d7

12 files changed

+68
-81
lines changed

Diff for: listenbrainz/db/external_service_oauth.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22
from typing import List, Optional, Union
33

44
from sqlalchemy import text
55

66
from data.model.external_service import ExternalServiceType
7-
from listenbrainz import db, utils
87
import sqlalchemy
98

109

@@ -32,7 +31,7 @@ def save_token(db_conn, user_id: int, service: ExternalServiceType, access_token
3231
# to use the new values. any column which does not have a new value to be set should
3332
# be explicitly set to the default value (which would have been used if the row was
3433
# inserted instead).
35-
token_expires = utils.unix_timestamp_to_datetime(token_expires_ts) if token_expires_ts else None
34+
token_expires = datetime.fromtimestamp(token_expires_ts, timezone.utc) if token_expires_ts else None
3635
result = db_conn.execute(sqlalchemy.text("""
3736
INSERT INTO external_service_oauth AS eso
3837
(user_id, external_user_id, service, access_token, refresh_token, token_expires, scopes)
@@ -121,7 +120,7 @@ def update_token(db_conn, user_id: int, service: ExternalServiceType, access_tok
121120
refresh_token: the new token used to refresh access tokens, if omitted the old token in the database remains unchanged
122121
expires_at: the unix timestamp at which the access token expires
123122
"""
124-
token_expires = utils.unix_timestamp_to_datetime(expires_at)
123+
token_expires = datetime.fromtimestamp(expires_at, timezone.utc)
125124
params = {
126125
"access_token": access_token,
127126
"token_expires": token_expires,

Diff for: listenbrainz/db/listens_importer.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import datetime
1+
from datetime import datetime, timezone
22
from typing import Optional, Union
33

44
from sqlalchemy import text
55

66
from data.model.external_service import ExternalServiceType
7-
from listenbrainz import utils
87
import sqlalchemy
98

109

@@ -51,12 +50,12 @@ def update_latest_listened_at(db_conn, user_id: int, service: ExternalServiceTyp
5150
"""), {
5251
'user_id': user_id,
5352
'service': service.value,
54-
'timestamp': utils.unix_timestamp_to_datetime(timestamp),
53+
'timestamp': datetime.fromtimestamp(timestamp, timezone.utc),
5554
})
5655
db_conn.commit()
5756

5857

59-
def get_latest_listened_at(db_conn, user_id: int, service: ExternalServiceType) -> Optional[datetime.datetime]:
58+
def get_latest_listened_at(db_conn, user_id: int, service: ExternalServiceType) -> Optional[datetime]:
6059
""" Returns the timestamp of the last listen imported for the user with
6160
specified LB user ID from the given service.
6261

Diff for: listenbrainz/db/pinned_recording.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import sqlalchemy
2-
from datetime import datetime
2+
from datetime import datetime, timezone
33

4-
from listenbrainz import db
54
from listenbrainz.db.model.pinned_recording import PinnedRecording, WritablePinnedRecording
65
from typing import List, Iterable
76

@@ -223,8 +222,8 @@ def get_pins_for_feed(db_conn, user_ids: Iterable[int], min_ts: int, max_ts: int
223222
LIMIT :count
224223
""".format(columns=','.join(PINNED_REC_GET_COLUMNS))), {
225224
"user_ids": tuple(user_ids),
226-
"min_ts": datetime.utcfromtimestamp(min_ts),
227-
"max_ts": datetime.utcfromtimestamp(max_ts),
225+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
226+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
228227
"count": count,
229228
})
230229
return [PinnedRecording(**row) for row in result.mappings()]

Diff for: listenbrainz/db/user_relationship.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# with this program; if not, write to the Free Software Foundation, Inc.,
1717
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818

19-
from datetime import datetime
19+
from datetime import datetime, timezone
2020
from typing import List, Iterable
2121

2222
import sqlalchemy
@@ -158,8 +158,8 @@ def get_follow_events(db_conn, user_ids: Iterable[int], min_ts: float, max_ts: f
158158
LIMIT :count
159159
"""), {
160160
"user_ids": tuple(user_ids),
161-
"min_ts": datetime.utcfromtimestamp(min_ts),
162-
"max_ts": datetime.utcfromtimestamp(max_ts),
161+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
162+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
163163
"count": count
164164
})
165165

Diff for: listenbrainz/db/user_timeline_event.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import sqlalchemy
2020
import orjson
2121

22-
from datetime import datetime
22+
from datetime import datetime, timezone
2323

2424
from sqlalchemy import text
2525

@@ -34,9 +34,8 @@
3434
HiddenUserTimelineEvent,
3535
PersonalRecordingRecommendationMetadata, WritePersonalRecordingRecommendationMetadata
3636
)
37-
from listenbrainz import db
3837
from listenbrainz.db.exceptions import DatabaseException
39-
from typing import List, Tuple, Iterable
38+
from typing import List, Iterable
4039

4140
from listenbrainz.db.model.review import CBReviewTimelineMetadata
4241

@@ -196,8 +195,8 @@ def get_user_timeline_events(
196195
LIMIT :count
197196
"""), {
198197
"user_ids": tuple(user_ids),
199-
"min_ts": datetime.utcfromtimestamp(min_ts),
200-
"max_ts": datetime.utcfromtimestamp(max_ts),
198+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
199+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
201200
"event_type": event_type.value,
202201
"count": count,
203202
})
@@ -255,8 +254,8 @@ def get_personal_recommendation_events_for_feed(db_conn, user_id: int, min_ts: i
255254
LIMIT :count
256255
"""), {
257256
"user_id": user_id,
258-
"min_ts": datetime.utcfromtimestamp(min_ts),
259-
"max_ts": datetime.utcfromtimestamp(max_ts),
257+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
258+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
260259
"count": count,
261260
"event_type": UserTimelineEventType.PERSONAL_RECORDING_RECOMMENDATION.value,
262261
})
@@ -297,8 +296,8 @@ def get_thanks_events_for_feed(db_conn, user_id: int, min_ts: int, max_ts: int,
297296
LIMIT :count
298297
"""), {
299298
"user_id": user_id,
300-
"min_ts": datetime.utcfromtimestamp(min_ts),
301-
"max_ts": datetime.utcfromtimestamp(max_ts),
299+
"min_ts": datetime.fromtimestamp(min_ts, timezone.utc),
300+
"max_ts": datetime.fromtimestamp(max_ts, timezone.utc),
302301
"count": count,
303302
"event_type": UserTimelineEventType.THANKS.value,
304303
})

Diff for: listenbrainz/listen.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
# coding=utf-8
2-
import calendar
31
from copy import deepcopy
4-
from datetime import datetime
2+
from datetime import datetime, timezone
53

64
import orjson
75

@@ -76,7 +74,7 @@ def __init__(self, user_id=None, user_name=None, timestamp=None, recording_msid=
7674
# determine the type of timestamp and do the right thing
7775
if isinstance(timestamp, int) or isinstance(timestamp, float):
7876
self.ts_since_epoch = int(timestamp)
79-
self.timestamp = datetime.utcfromtimestamp(self.ts_since_epoch)
77+
self.timestamp = datetime.fromtimestamp(self.ts_since_epoch, timezone.utc)
8078
else:
8179
if timestamp:
8280
self.timestamp = timestamp
@@ -105,12 +103,12 @@ def from_json(cls, j):
105103
"""Factory to make Listen() objects from a dict"""
106104
# Let's go play whack-a-mole with our lovely whicket of timestamp fields. Hopefully one will work!
107105
try:
108-
j['listened_at'] = datetime.utcfromtimestamp(float(j['listened_at']))
106+
j['listened_at'] = datetime.fromtimestamp(float(j['listened_at']), timezone.utc)
109107
except KeyError:
110108
try:
111-
j['listened_at'] = datetime.utcfromtimestamp(float(j['timestamp']))
109+
j['listened_at'] = datetime.fromtimestamp(float(j['timestamp']), timezone.utc)
112110
except KeyError:
113-
j['listened_at'] = datetime.utcfromtimestamp(float(j['ts_since_epoch']))
111+
j['listened_at'] = datetime.fromtimestamp(float(j['ts_since_epoch']), timezone.utc)
114112

115113
return cls(
116114
user_id=j.get('user_id'),

Diff for: listenbrainz/listenstore/tests/test_timescale_utils.py

+31-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22

33
from sqlalchemy import text
44

@@ -35,8 +35,8 @@ def _get_count_and_timestamp(self, user):
3535
row = result.fetchone()
3636
return {
3737
"count": row.count,
38-
"min_listened_at": row.min_listened_at.replace(tzinfo=None),
39-
"max_listened_at": row.max_listened_at.replace(tzinfo=None)
38+
"min_listened_at": row.min_listened_at,
39+
"max_listened_at": row.max_listened_at
4040
}
4141

4242
def test_delete_listens_update_metadata(self):
@@ -49,35 +49,51 @@ def test_delete_listens_update_metadata(self):
4949
update_user_listen_data()
5050

5151
metadata_1 = self._get_count_and_timestamp(user_1)
52-
self.assertEqual(metadata_1["min_listened_at"], datetime.utcfromtimestamp(1400000000))
53-
self.assertEqual(metadata_1["max_listened_at"], datetime.utcfromtimestamp(1400000200))
52+
self.assertEqual(metadata_1["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
53+
self.assertEqual(metadata_1["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
5454
self.assertEqual(metadata_1["count"], 5)
5555

5656
metadata_2 = self._get_count_and_timestamp(user_2)
57-
self.assertEqual(metadata_2["min_listened_at"], datetime.utcfromtimestamp(1400000000))
58-
self.assertEqual(metadata_2["max_listened_at"], datetime.utcfromtimestamp(1400000200))
57+
self.assertEqual(metadata_2["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
58+
self.assertEqual(metadata_2["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
5959
self.assertEqual(metadata_2["count"], 5)
6060

6161
# to test the case when the update script has not run since delete, so metadata in listen_user_metadata does
6262
# account for this listen and deleting should not affect it either.
6363
self._create_test_data(user_1, "timescale_listenstore_test_listens_2.json")
64-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000500), user_1["id"], "4269ddbc-9241-46da-935d-4fa9e0f7f371")
64+
self.ls.delete_listen(
65+
datetime.fromtimestamp(1400000500, timezone.utc),
66+
user_1["id"],
67+
"4269ddbc-9241-46da-935d-4fa9e0f7f371"
68+
)
6569

6670
# test min_listened_at is updated if that listen is deleted for a user
67-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000000), user_1["id"], "4269ddbc-9241-46da-935d-4fa9e0f7f371")
71+
self.ls.delete_listen(
72+
datetime.fromtimestamp(1400000000, timezone.utc),
73+
user_1["id"],
74+
"4269ddbc-9241-46da-935d-4fa9e0f7f371"
75+
)
6876
# test max_listened_at is updated if that listen is deleted for a user
69-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000200), user_1["id"], "db072fa7-0c7f-4f55-b90f-a88da531b219")
77+
self.ls.delete_listen(
78+
datetime.fromtimestamp(1400000200, timezone.utc),
79+
user_1["id"],
80+
"db072fa7-0c7f-4f55-b90f-a88da531b219"
81+
)
7082
# test normal listen delete updates correctly
71-
self.ls.delete_listen(datetime.utcfromtimestamp(1400000100), user_2["id"], "08ade1eb-800e-4ad8-8184-32941664ac02")
83+
self.ls.delete_listen(
84+
datetime.fromtimestamp(1400000100, timezone.utc),
85+
user_2["id"],
86+
"08ade1eb-800e-4ad8-8184-32941664ac02"
87+
)
7288

7389
delete_listens()
7490

7591
metadata_1 = self._get_count_and_timestamp(user_1)
76-
self.assertEqual(metadata_1["min_listened_at"], datetime.utcfromtimestamp(1400000050))
77-
self.assertEqual(metadata_1["max_listened_at"], datetime.utcfromtimestamp(1400000150))
92+
self.assertEqual(metadata_1["min_listened_at"], datetime.fromtimestamp(1400000050, timezone.utc))
93+
self.assertEqual(metadata_1["max_listened_at"], datetime.fromtimestamp(1400000150, timezone.utc))
7894
self.assertEqual(metadata_1["count"], 3)
7995

8096
metadata_2 = self._get_count_and_timestamp(user_2)
81-
self.assertEqual(metadata_2["min_listened_at"], datetime.utcfromtimestamp(1400000000))
82-
self.assertEqual(metadata_2["max_listened_at"], datetime.utcfromtimestamp(1400000200))
97+
self.assertEqual(metadata_2["min_listened_at"], datetime.fromtimestamp(1400000000, timezone.utc))
98+
self.assertEqual(metadata_2["max_listened_at"], datetime.fromtimestamp(1400000200, timezone.utc))
8399
self.assertEqual(metadata_2["count"], 4)

Diff for: listenbrainz/tests/unit/test_utils.py

-9
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@
1616
# with this program; if not, write to the Free Software Foundation, Inc.,
1717
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818

19-
import listenbrainz.utils as utils
20-
import time
2119
import unittest
2220
import uuid
2321

24-
from datetime import datetime
2522
from listenbrainz.webserver import create_app
2623
from listenbrainz.webserver.views.api_tools import is_valid_uuid
2724

@@ -31,12 +28,6 @@ class ListenBrainzUtilsTestCase(unittest.TestCase):
3128
def setUp(self):
3229
self.app = create_app(debug=True) # create an app for config value access
3330

34-
def test_unix_timestamp_to_datetime(self):
35-
t = int(time.time())
36-
x = utils.unix_timestamp_to_datetime(t)
37-
self.assertIsInstance(x, datetime)
38-
self.assertEqual(int(x.strftime('%s')), t)
39-
4031
def test_valid_uuid(self):
4132
self.assertTrue(is_valid_uuid(str(uuid.uuid4())))
4233
self.assertFalse(is_valid_uuid('hjjkghjk'))

Diff for: listenbrainz/tests/utils.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# coding=utf-8
22

3-
from datetime import datetime, date
3+
from datetime import datetime, timezone
44
import time
5-
import sys
6-
import os
75

86
from listenbrainz.listen import Listen
97
import uuid
@@ -21,7 +19,7 @@ def generate_data(db_conn, from_date, num_records, user_name):
2119
item = Listen(
2220
user_id=user['id'],
2321
user_name=user_name,
24-
timestamp=datetime.utcfromtimestamp(current_date),
22+
timestamp=datetime.fromtimestamp(current_date, timezone.utc),
2523
recording_msid=str(uuid.uuid4()),
2624
data={
2725
'artist_name': 'Test Artist Pls ignore',

Diff for: listenbrainz/utils.py

-12
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ def create_channel_to_consume(connection, exchange: str, queue: str, callback_fu
4242
return ch
4343

4444

45-
def unix_timestamp_to_datetime(timestamp):
46-
""" Converts expires_at timestamp received from Spotify to a datetime object
47-
48-
Args:
49-
timestamp (int): the unix timestamp to be converted to datetime
50-
51-
Returns:
52-
A datetime object with timezone UTC corresponding to the provided timestamp
53-
"""
54-
return datetime.utcfromtimestamp(timestamp).replace(tzinfo=timezone.utc)
55-
56-
5745
def get_fallback_connection_name():
5846
""" Get a connection name friendlier than docker gateway ip during connecting
5947
to services like redis, rabbitmq etc."""

Diff for: listenbrainz/webserver/views/api.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ def delete_listen():
499499
if "listened_at" not in data:
500500
log_raise_400("Listen timestamp missing.")
501501
try:
502-
listened_at = datetime.utcfromtimestamp(int(data["listened_at"]))
502+
listened_at = datetime.fromtimestamp(int(data["listened_at"]), timezone.utc)
503503
except ValueError:
504504
log_raise_400("%s: Listen timestamp invalid." % data["listened_at"])
505505

Diff for: listenbrainz/webserver/views/user_timeline_event_api.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1818

1919
import time
20-
from datetime import datetime, timedelta
20+
from datetime import datetime, timedelta, timezone
2121
from typing import List, Dict, Iterable
2222

2323
import pydantic
@@ -838,14 +838,14 @@ def get_listen_events(
838838
# if neither is set, use current time as max_ts and subtract window
839839
# length to get min_ts.
840840
if min_ts and max_ts:
841-
min_ts = datetime.utcfromtimestamp(min_ts)
842-
max_ts = datetime.utcfromtimestamp(max_ts)
841+
min_ts = datetime.fromtimestamp(min_ts, timezone.utc)
842+
max_ts = datetime.fromtimestamp(max_ts, timezone.utc)
843843
else:
844844
if min_ts:
845-
min_ts = datetime.utcfromtimestamp(min_ts)
845+
min_ts = datetime.fromtimestamp(min_ts, timezone.utc)
846846
max_ts = min_ts + DEFAULT_LISTEN_EVENT_WINDOW
847847
elif max_ts:
848-
max_ts = datetime.utcfromtimestamp(max_ts)
848+
max_ts = datetime.fromtimestamp(max_ts, timezone.utc)
849849
min_ts = max_ts - DEFAULT_LISTEN_EVENT_WINDOW
850850
else:
851851
max_ts = datetime.utcnow()
@@ -892,19 +892,19 @@ def get_all_listen_events(
892892
# if neither is set, use current time as max_ts and subtract window
893893
# length to get min_ts.
894894
if min_ts and max_ts:
895-
temp = datetime.utcfromtimestamp(min_ts)
896-
max_ts = datetime.utcfromtimestamp(max_ts)
895+
temp = datetime.fromtimestamp(min_ts, timezone.utc)
896+
max_ts = datetime.fromtimestamp(max_ts, timezone.utc)
897897
if max_ts - temp < DEFAULT_LISTEN_EVENT_WINDOW_NEW:
898898
min_ts = temp
899899
else:
900900
# If the given interval for search is greater than :DEFAULT_LISTEN_EVENT_WINDOW_NEW:,
901901
# then we must limit the search interval to :DEFAULT_LISTEN_EVENT_WINDOW_NEW:.
902902
min_ts = max_ts - DEFAULT_LISTEN_EVENT_WINDOW_NEW
903903
elif min_ts:
904-
min_ts = datetime.utcfromtimestamp(min_ts)
904+
min_ts = datetime.fromtimestamp(min_ts, timezone.utc)
905905
max_ts = min_ts + DEFAULT_LISTEN_EVENT_WINDOW_NEW
906906
elif max_ts:
907-
max_ts = datetime.utcfromtimestamp(max_ts)
907+
max_ts = datetime.fromtimestamp(max_ts, timezone.utc)
908908
min_ts = max_ts - DEFAULT_LISTEN_EVENT_WINDOW_NEW
909909
else:
910910
max_ts = datetime.utcnow()

0 commit comments

Comments
 (0)