From e93a323959b09c1791348e59995cf1a622c194c7 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Sun, 6 Aug 2023 16:29:56 +0200 Subject: [PATCH] Replace pytz code --- src/newsreader/news/collection/feed.py | 5 ++--- src/newsreader/news/collection/forms/feed.py | 21 +++++++++++++----- .../news/collection/forms/reddit.py | 6 ++--- .../news/collection/forms/twitter.py | 6 ++--- src/newsreader/news/collection/models.py | 19 ++++++++++++---- src/newsreader/news/collection/reddit.py | 5 +++-- .../tests/endpoints/rule/list/tests.py | 10 ++++----- .../collection/tests/feed/builder/tests.py | 11 +++++----- .../collection/tests/feed/collector/tests.py | 13 +++++------ .../collection/tests/reddit/builder/tests.py | 6 ++--- .../tests/reddit/collector/tests.py | 9 ++++---- .../collection/tests/twitter/builder/tests.py | 12 +++++----- .../tests/twitter/collector/tests.py | 9 ++++---- .../news/collection/tests/views/test_crud.py | 8 +++---- .../tests/views/test_subreddit_views.py | 10 ++++----- .../tests/views/test_twitter_views.py | 10 ++++----- src/newsreader/news/collection/twitter.py | 9 ++++---- src/newsreader/news/collection/utils.py | 4 ++-- src/newsreader/news/collection/views/base.py | 8 ++++--- .../tests/endpoints/category/list/tests.py | 22 +++++++++---------- .../core/tests/endpoints/post/list/tests.py | 10 ++++----- src/newsreader/news/core/tests/factories.py | 5 +++-- 22 files changed, 117 insertions(+), 101 deletions(-) diff --git a/src/newsreader/news/collection/feed.py b/src/newsreader/news/collection/feed.py index 2730ca0..6a0ff3b 100644 --- a/src/newsreader/news/collection/feed.py +++ b/src/newsreader/news/collection/feed.py @@ -2,12 +2,11 @@ import logging from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import timedelta +from zoneinfo import ZoneInfo from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.utils import timezone -import pytz - from feedparser import parse from newsreader.news.collection.base import ( @@ -58,7 +57,7 @@ class FeedBuilder(PostBuilder): "published_parsed": "publication_date", "author": "author", } - tz = pytz.timezone(self.stream.rule.timezone) + tz = ZoneInfo(self.stream.rule.timezone) data = {"rule_id": self.stream.rule.pk} for field, model_field in field_mapping.items(): diff --git a/src/newsreader/news/collection/forms/feed.py b/src/newsreader/news/collection/forms/feed.py index 4a22a2e..1ebec91 100644 --- a/src/newsreader/news/collection/forms/feed.py +++ b/src/newsreader/news/collection/forms/feed.py @@ -1,19 +1,30 @@ +from datetime import timezone as python_timezone +from zoneinfo import available_timezones + from django import forms from django.utils.translation import gettext_lazy as _ -import pytz - from newsreader.core.forms import CheckboxInput from newsreader.news.collection.forms.base import CollectionRuleForm from newsreader.news.collection.models import CollectionRule +def get_timezones(): + return [ + ( + _timezone, + _timezone, + ) + for _timezone in available_timezones() + ] + + class FeedForm(CollectionRuleForm): timezone = forms.ChoiceField( - widget=forms.Select(attrs={"size": len(pytz.all_timezones)}), - choices=((timezone, timezone) for timezone in pytz.all_timezones), + widget=forms.Select(attrs={"size": len(get_timezones())}), + choices=get_timezones(), help_text=_("The timezone which the feed uses"), - initial=pytz.utc, + initial=python_timezone.utc, ) class Meta: diff --git a/src/newsreader/news/collection/forms/reddit.py b/src/newsreader/news/collection/forms/reddit.py index b4d1090..6e1a780 100644 --- a/src/newsreader/news/collection/forms/reddit.py +++ b/src/newsreader/news/collection/forms/reddit.py @@ -1,10 +1,10 @@ +from datetime import timezone + from django import forms from django.core.exceptions import ValidationError from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -import pytz - from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.forms.base import CollectionRuleForm from newsreader.news.collection.models import CollectionRule @@ -36,7 +36,7 @@ class SubRedditForm(CollectionRuleForm): instance = super().save(commit=False) instance.type = RuleTypeChoices.subreddit - instance.timezone = str(pytz.utc) + instance.timezone = str(timezone.utc) if commit: instance.save() diff --git a/src/newsreader/news/collection/forms/twitter.py b/src/newsreader/news/collection/forms/twitter.py index 902652b..85401e7 100644 --- a/src/newsreader/news/collection/forms/twitter.py +++ b/src/newsreader/news/collection/forms/twitter.py @@ -1,8 +1,8 @@ +from datetime import timezone + from django import forms from django.utils.translation import gettext_lazy as _ -import pytz - from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.forms.base import CollectionRuleForm from newsreader.news.collection.models import CollectionRule @@ -21,7 +21,7 @@ class TwitterTimelineForm(CollectionRuleForm): instance = super().save(commit=False) instance.type = RuleTypeChoices.twitter_timeline - instance.timezone = str(pytz.utc) + instance.timezone = str(timezone.utc) instance.url = f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name={instance.screen_name}&tweet_mode=extended" if commit: diff --git a/src/newsreader/news/collection/models.py b/src/newsreader/news/collection/models.py index 907de11..66b7ac2 100644 --- a/src/newsreader/news/collection/models.py +++ b/src/newsreader/news/collection/models.py @@ -1,13 +1,24 @@ +from datetime import timezone +from zoneinfo import available_timezones + from django.db import models from django.urls import reverse from django.utils.translation import gettext as _ -import pytz - from newsreader.core.models import TimeStampedModel from newsreader.news.collection.choices import RuleTypeChoices +def get_timezones(): + return [ + ( + _timezone, + _timezone, + ) + for _timezone in available_timezones() + ] + + class CollectionRuleQuerySet(models.QuerySet): def enabled(self): return self.filter(enabled=True) @@ -26,9 +37,9 @@ class CollectionRule(TimeStampedModel): favicon = models.URLField(blank=True, null=True) timezone = models.CharField( - choices=((timezone, timezone) for timezone in pytz.all_timezones), + choices=get_timezones(), max_length=100, - default=str(pytz.utc), + default=str(timezone.utc), ) category = models.ForeignKey( diff --git a/src/newsreader/news/collection/reddit.py b/src/newsreader/news/collection/reddit.py index 0c0eae2..e674b50 100644 --- a/src/newsreader/news/collection/reddit.py +++ b/src/newsreader/news/collection/reddit.py @@ -2,6 +2,7 @@ import logging from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime, timedelta +from datetime import timezone as _timezone from html import unescape from json.decoder import JSONDecodeError from urllib.parse import urlencode @@ -12,7 +13,6 @@ from django.core.cache import cache from django.utils import timezone from django.utils.html import format_html -import pytz import requests from newsreader.news.collection.base import ( @@ -215,7 +215,8 @@ class RedditBuilder(PostBuilder): try: parsed_date = datetime.fromtimestamp(entry_data["created_utc"]) - created_date = pytz.utc.localize(parsed_date) + + created_date = parsed_date.replace(tzinfo=_timezone.utc) except (OverflowError, OSError) as e: raise BuilderParseException(payload=entry) from e except KeyError as e: diff --git a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py index 5a34dbc..a1acb23 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py @@ -1,12 +1,10 @@ import json -from datetime import date, datetime, time +from datetime import date, datetime, time, timezone from django.test import TestCase from django.urls import reverse -import pytz - from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -154,21 +152,21 @@ class NestedRuleListViewTestCase(TestCase): title="I'm the first post", rule=rule, publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc + date(2019, 5, 20), time(hour=16, minute=7, second=37), timezone.utc ), ), FeedPostFactory( title="I'm the second post", rule=rule, publication_date=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc + date(2019, 7, 20), time(hour=18, minute=7, second=37), timezone.utc ), ), FeedPostFactory( title="I'm the third post", rule=rule, publication_date=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc + date(2019, 7, 20), time(hour=16, minute=7, second=37), timezone.utc ), ), ] diff --git a/src/newsreader/news/collection/tests/feed/builder/tests.py b/src/newsreader/news/collection/tests/feed/builder/tests.py index 7f4edf0..c40356b 100644 --- a/src/newsreader/news/collection/tests/feed/builder/tests.py +++ b/src/newsreader/news/collection/tests/feed/builder/tests.py @@ -1,11 +1,10 @@ from datetime import datetime +from datetime import timezone as _timezone from unittest.mock import Mock from django.test import TestCase from django.utils import timezone -import pytz - from freezegun import freeze_time from newsreader.news.collection.feed import FeedBuilder @@ -35,7 +34,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] publication_date = datetime( - 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=pytz.utc + 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=_timezone.utc ) self.assertEqual( @@ -59,7 +58,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] publication_date = datetime( - 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=_timezone.utc ) self.assertEqual( @@ -94,7 +93,7 @@ class FeedBuilderTestCase(TestCase): post = posts[0] publication_date = datetime( - 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc + 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=_timezone.utc ) self.assertEqual(post.publication_date, publication_date) @@ -109,7 +108,7 @@ class FeedBuilderTestCase(TestCase): post = posts[1] publication_date = datetime( - 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=pytz.utc + 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=_timezone.utc ) self.assertEqual(post.publication_date, publication_date) diff --git a/src/newsreader/news/collection/tests/feed/collector/tests.py b/src/newsreader/news/collection/tests/feed/collector/tests.py index ae10918..5dd6d8f 100644 --- a/src/newsreader/news/collection/tests/feed/collector/tests.py +++ b/src/newsreader/news/collection/tests/feed/collector/tests.py @@ -1,12 +1,11 @@ from datetime import date, datetime, time +from datetime import timezone as _timezone from time import struct_time from unittest.mock import Mock, patch from django.test import TestCase from django.utils import timezone -import pytz - from freezegun import freeze_time from newsreader.news.collection.exceptions import ( @@ -100,7 +99,7 @@ class FeedCollectorTestCase(TestCase): def test_forbidden(self): self.mocked_fetch.side_effect = StreamForbiddenException - old_run = pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) + old_run = datetime(2019, 10, 30, 12, 30, tzinfo=_timezone.utc) rule = FeedFactory(last_run=old_run) collector = FeedCollector() @@ -130,7 +129,7 @@ class FeedCollectorTestCase(TestCase): self.assertEquals(rule.succeeded, False) self.assertEquals(rule.error, "Stream timed out") self.assertEquals( - rule.last_run, pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) + rule.last_run, datetime(2019, 10, 30, 12, 30, tzinfo=_timezone.utc) ) def test_duplicates(self): @@ -139,7 +138,7 @@ class FeedCollectorTestCase(TestCase): rule = FeedFactory() aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc + struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), _timezone.utc ) first_post = FeedPostFactory( @@ -152,7 +151,7 @@ class FeedCollectorTestCase(TestCase): ) aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc + struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), _timezone.utc ) second_post = FeedPostFactory( @@ -165,7 +164,7 @@ class FeedCollectorTestCase(TestCase): ) aware_datetime = build_publication_date( - struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc + struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), _timezone.utc ) third_post = FeedPostFactory( diff --git a/src/newsreader/news/collection/tests/reddit/builder/tests.py b/src/newsreader/news/collection/tests/reddit/builder/tests.py index 3434972..0cd88f9 100644 --- a/src/newsreader/news/collection/tests/reddit/builder/tests.py +++ b/src/newsreader/news/collection/tests/reddit/builder/tests.py @@ -1,10 +1,8 @@ -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import Mock from django.test import TestCase -import pytz - from newsreader.news.collection.reddit import RedditBuilder from newsreader.news.collection.tests.factories import SubredditFactory from newsreader.news.collection.tests.reddit.builder.mocks import * @@ -59,7 +57,7 @@ class RedditBuilderTestCase(TestCase): "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)) + post.publication_date, datetime(2020, 7, 6, 6, 11, 22, tzinfo=timezone.utc) ) def test_empty_data(self): diff --git a/src/newsreader/news/collection/tests/reddit/collector/tests.py b/src/newsreader/news/collection/tests/reddit/collector/tests.py index fa2f5d4..d901e5e 100644 --- a/src/newsreader/news/collection/tests/reddit/collector/tests.py +++ b/src/newsreader/news/collection/tests/reddit/collector/tests.py @@ -1,12 +1,11 @@ from datetime import datetime +from datetime import timezone as _timezone from unittest.mock import patch from uuid import uuid4 from django.test import TestCase from django.utils import timezone -import pytz - from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.exceptions import ( StreamDeniedException, @@ -82,7 +81,8 @@ class RedditCollectorTestCase(TestCase): ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 7, 11, 22, 23, 24)) + post.publication_date, + datetime(2020, 7, 11, 22, 23, 24, tzinfo=_timezone.utc), ) self.assertEquals(post.author, "HannahB888") @@ -99,7 +99,8 @@ class RedditCollectorTestCase(TestCase): ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 7, 12, 10, 29, 10)) + post.publication_date, + datetime(2020, 7, 12, 10, 29, 10, tzinfo=_timezone.utc), ) self.assertEquals(post.author, "Sebaron") diff --git a/src/newsreader/news/collection/tests/twitter/builder/tests.py b/src/newsreader/news/collection/tests/twitter/builder/tests.py index 2e9ecc0..09e84cb 100644 --- a/src/newsreader/news/collection/tests/twitter/builder/tests.py +++ b/src/newsreader/news/collection/tests/twitter/builder/tests.py @@ -1,11 +1,9 @@ -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import Mock from django.test import TestCase from django.utils.safestring import mark_safe -import pytz - from ftfy import fix_text from newsreader.news.collection.tests.factories import TwitterTimelineFactory @@ -69,7 +67,7 @@ class TwitterBuilderTestCase(TestCase): post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1291528756373286914" ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 8, 7, 0, 17, 5)) + post.publication_date, datetime(2020, 8, 7, 0, 17, 5, tzinfo=timezone.utc) ) post = posts["1288550304095416320"] @@ -85,7 +83,7 @@ class TwitterBuilderTestCase(TestCase): post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1288550304095416320" ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 7, 29, 19, 1, 47)) + post.publication_date, datetime(2020, 7, 29, 19, 1, 47, tzinfo=timezone.utc) ) # note that only one media type can be uploaded to an Tweet @@ -114,7 +112,7 @@ class TwitterBuilderTestCase(TestCase): post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1269039237166321664" ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 6, 5, 22, 51, 46)) + post.publication_date, datetime(2020, 6, 5, 22, 51, 46, tzinfo=timezone.utc) ) self.assertInHTML( @@ -179,7 +177,7 @@ class TwitterBuilderTestCase(TestCase): post.url, f"{TWITTER_URL}/RobertsSpaceInd/status/1291080532361527296" ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 8, 5, 18, 36, 0)) + post.publication_date, datetime(2020, 8, 5, 18, 36, 0, tzinfo=timezone.utc) ) self.assertIn(full_text, post.body) diff --git a/src/newsreader/news/collection/tests/twitter/collector/tests.py b/src/newsreader/news/collection/tests/twitter/collector/tests.py index ab589ff..bcc2c77 100644 --- a/src/newsreader/news/collection/tests/twitter/collector/tests.py +++ b/src/newsreader/news/collection/tests/twitter/collector/tests.py @@ -1,12 +1,11 @@ from datetime import datetime +from datetime import timezone as _timezone from unittest.mock import Mock, patch from uuid import uuid4 from django.test import TestCase from django.utils import timezone -import pytz - from freezegun import freeze_time from ftfy import fix_text @@ -67,7 +66,8 @@ class TwitterCollectorTestCase(TestCase): ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 20, 32, 22)) + post.publication_date, + datetime(2020, 9, 18, 20, 32, 22, tzinfo=_timezone.utc), ) title = truncate_text( @@ -89,7 +89,8 @@ class TwitterCollectorTestCase(TestCase): ) self.assertEquals( - post.publication_date, pytz.utc.localize(datetime(2020, 9, 18, 18, 50, 11)) + post.publication_date, + datetime(2020, 9, 18, 18, 50, 11, tzinfo=_timezone.utc), ) body = fix_text( diff --git a/src/newsreader/news/collection/tests/views/test_crud.py b/src/newsreader/news/collection/tests/views/test_crud.py index 7da241d..c36f354 100644 --- a/src/newsreader/news/collection/tests/views/test_crud.py +++ b/src/newsreader/news/collection/tests/views/test_crud.py @@ -1,8 +1,8 @@ +from datetime import timezone + from django.test import TestCase from django.urls import reverse -import pytz - from django_celery_beat.models import PeriodicTask from newsreader.news.collection.choices import RuleTypeChoices @@ -21,7 +21,7 @@ class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase): self.form_data.update( name="new rule", url="https://www.rss.com/rss", - timezone=pytz.utc, + timezone=str(timezone.utc), category=str(self.category.pk), ) @@ -34,7 +34,7 @@ class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase): self.assertEquals(rule.type, RuleTypeChoices.feed) self.assertEquals(rule.url, "https://www.rss.com/rss") - self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.timezone, str(timezone.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) diff --git a/src/newsreader/news/collection/tests/views/test_subreddit_views.py b/src/newsreader/news/collection/tests/views/test_subreddit_views.py index a644800..7295421 100644 --- a/src/newsreader/news/collection/tests/views/test_subreddit_views.py +++ b/src/newsreader/news/collection/tests/views/test_subreddit_views.py @@ -1,9 +1,9 @@ +from datetime import timezone + from django.test import TestCase from django.urls import reverse from django.utils.translation import gettext as _ -import pytz - from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL @@ -38,7 +38,7 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase): self.assertEquals(rule.type, RuleTypeChoices.subreddit) self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") - self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.timezone, str(timezone.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) @@ -70,7 +70,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): "name": self.rule.name, "url": self.rule.url, "category": str(self.category.pk), - "timezone": pytz.utc, + "timezone": str(timezone.utc), "reddit_allow_nfsw": False, "reddit_allow_spoiler": False, "reddit_allow_viewed": True, @@ -125,7 +125,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.assertEquals(rule.type, RuleTypeChoices.subreddit) self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") - self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.timezone, str(timezone.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) diff --git a/src/newsreader/news/collection/tests/views/test_twitter_views.py b/src/newsreader/news/collection/tests/views/test_twitter_views.py index d9afa26..62b7081 100644 --- a/src/newsreader/news/collection/tests/views/test_twitter_views.py +++ b/src/newsreader/news/collection/tests/views/test_twitter_views.py @@ -1,8 +1,8 @@ +from datetime import timezone + from django.test import TestCase from django.urls import reverse -import pytz - from django_celery_beat.models import PeriodicTask from newsreader.news.collection.choices import RuleTypeChoices @@ -37,7 +37,7 @@ class TwitterTimelineCreateViewTestCase(CollectionRuleViewTestCase, TestCase): rule.url, f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name=RobertsSpaceInd&tweet_mode=extended", ) - self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.timezone, str(timezone.utc)) self.assertEquals(rule.favicon, None) self.assertEquals(rule.category.pk, self.category.pk) self.assertEquals(rule.user.pk, self.user.pk) @@ -70,7 +70,7 @@ class TwitterTimelineUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): "name": self.rule.name, "screen_name": self.rule.screen_name, "category": str(self.category.pk), - "timezone": pytz.utc, + "timezone": str(timezone.utc), } def test_name_change(self): @@ -123,7 +123,7 @@ class TwitterTimelineUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): self.rule.url, f"{TWITTER_API_URL}/statuses/user_timeline.json?screen_name=CyberpunkGame&tweet_mode=extended", ) - self.assertEquals(self.rule.timezone, str(pytz.utc)) + self.assertEquals(self.rule.timezone, str(timezone.utc)) self.assertEquals(self.rule.favicon, None) self.assertEquals(self.rule.category.pk, self.category.pk) self.assertEquals(self.rule.user.pk, self.user.pk) diff --git a/src/newsreader/news/collection/twitter.py b/src/newsreader/news/collection/twitter.py index 5167d22..2907297 100644 --- a/src/newsreader/news/collection/twitter.py +++ b/src/newsreader/news/collection/twitter.py @@ -2,6 +2,7 @@ import logging from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime +from datetime import timezone as _timezone from json import JSONDecodeError from django.conf import settings @@ -10,8 +11,6 @@ from django.utils import timezone from django.utils.html import format_html, urlize from django.utils.translation import gettext as _ -import pytz - from ftfy import fix_text from requests_oauthlib import OAuth1 as OAuth @@ -82,9 +81,11 @@ class TwitterBuilder(PostBuilder): Post, "title", self.sanitize_fragment(data["full_text"]) ) - publication_date = pytz.utc.localize( - datetime.strptime(data["created_at"], "%a %b %d %H:%M:%S +0000 %Y") + parsed_date = datetime.strptime( + data["created_at"], "%a %b %d %H:%M:%S +0000 %Y" ) + + publication_date = parsed_date.replace(tzinfo=_timezone.utc) except KeyError as e: raise BuilderMissingDataException(payload=data) from e except (OverflowError, OSError) as e: diff --git a/src/newsreader/news/collection/utils.py b/src/newsreader/news/collection/utils.py index 0eb1dc0..1ada2e9 100644 --- a/src/newsreader/news/collection/utils.py +++ b/src/newsreader/news/collection/utils.py @@ -1,10 +1,10 @@ from datetime import datetime +from datetime import timezone as _timezone from django.conf import settings from django.db.models.fields import CharField, TextField from django.utils import timezone -import pytz import requests from requests.exceptions import RequestException @@ -22,7 +22,7 @@ def build_publication_date(dt, tz): except (TypeError, ValueError): return timezone.now() - return published_parsed.astimezone(pytz.utc) + return published_parsed.astimezone(_timezone.utc) def fetch(url, auth=None, headers={}): diff --git a/src/newsreader/news/collection/views/base.py b/src/newsreader/news/collection/views/base.py index d7a3a4d..f90d4ea 100644 --- a/src/newsreader/news/collection/views/base.py +++ b/src/newsreader/news/collection/views/base.py @@ -1,8 +1,8 @@ import json -from django.urls import reverse_lazy +from zoneinfo import available_timezones -import pytz +from django.urls import reverse_lazy from django_celery_beat.models import IntervalSchedule, PeriodicTask @@ -25,7 +25,9 @@ class CollectionRuleDetailMixin: context_data = super().get_context_data(**kwargs) categories = Category.objects.filter(user=self.request.user).order_by("name") - timezones = [timezone for timezone in pytz.all_timezones] + + _available_timezones = available_timezones() + timezones = [timezone for timezone in _available_timezones] context_data["categories"] = categories context_data["timezones"] = timezones diff --git a/src/newsreader/news/core/tests/endpoints/category/list/tests.py b/src/newsreader/news/core/tests/endpoints/category/list/tests.py index c822950..dcf27f1 100644 --- a/src/newsreader/news/core/tests/endpoints/category/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/list/tests.py @@ -1,12 +1,10 @@ import json -from datetime import datetime +from datetime import datetime, timezone from django.test import TestCase from django.urls import reverse -import pytz - from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -29,15 +27,15 @@ class CategoryListViewTestCase(TestCase): def test_ordering(self): categories = [ CategoryFactory( - created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), + created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), user=self.user, ), CategoryFactory( - created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc), + created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc), user=self.user, ), CategoryFactory( - created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc), + created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc), user=self.user, ), ] @@ -430,12 +428,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Reuters post", rule=reuters_rule, - publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 21, 15, tzinfo=timezone.utc), ), FeedPostFactory.create( title="First Reuters post", rule=reuters_rule, - publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 12, tzinfo=timezone.utc), ), ] @@ -443,12 +441,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Guardian post", rule=guardian_rule, - publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 21, 14, tzinfo=timezone.utc), ), FeedPostFactory.create( title="First Guardian post", rule=guardian_rule, - publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 11, tzinfo=timezone.utc), ), ] @@ -456,12 +454,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second BBC post", rule=bbc_rule, - publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 21, 16, tzinfo=timezone.utc), ), FeedPostFactory.create( title="First BBC post", rule=bbc_rule, - publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 13, tzinfo=timezone.utc), ), ] diff --git a/src/newsreader/news/core/tests/endpoints/post/list/tests.py b/src/newsreader/news/core/tests/endpoints/post/list/tests.py index 37f83b0..627eae6 100644 --- a/src/newsreader/news/core/tests/endpoints/post/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/post/list/tests.py @@ -1,10 +1,8 @@ -from datetime import datetime +from datetime import datetime, timezone from django.test import TestCase from django.urls import reverse -import pytz - from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory @@ -32,17 +30,17 @@ class PostListViewTestCase(TestCase): FeedPostFactory( title="I'm the first post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=timezone.utc), ), FeedPostFactory( title="I'm the second post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc), ), FeedPostFactory( title="I'm the third post", rule=rule, - publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=pytz.utc), + publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=timezone.utc), ), ] diff --git a/src/newsreader/news/core/tests/factories.py b/src/newsreader/news/core/tests/factories.py index 520f940..d3b62f0 100644 --- a/src/newsreader/news/core/tests/factories.py +++ b/src/newsreader/news/core/tests/factories.py @@ -1,6 +1,7 @@ +from datetime import timezone + import factory import factory.fuzzy -import pytz from newsreader.accounts.tests.factories import UserFactory from newsreader.news.collection.reddit import REDDIT_API_URL @@ -19,7 +20,7 @@ class PostFactory(factory.django.DjangoModelFactory): title = factory.Faker("sentence") body = factory.Faker("paragraph") author = factory.Faker("name") - publication_date = factory.Faker("date_time_this_year", tzinfo=pytz.utc) + publication_date = factory.Faker("date_time_this_year", tzinfo=timezone.utc) url = factory.Faker("url") remote_identifier = factory.Faker("uuid4")