chore: Replace pytz with zoneinfo for UTC handling - Part 1 (#37523)

First PR to replace pytz with zoneinfo for UTC handling across codebase.

This PR migrates all UTC timezone handling from pytz to Python’s standard
library zoneinfo. The pytz library is now deprecated, and its documentation
recommends using zoneinfo for all new code. This update modernizes our
codebase, removes legacy pytz usage, and ensures compatibility with
current best practices for timezone management in Python 3.9+. No functional
changes to timezone logic - just a direct replacement for UTC handling.

https://github.com/openedx/edx-platform/issues/33980
This commit is contained in:
Tarun Tak
2025-10-29 01:53:22 +05:30
committed by GitHub
parent 6deb4f8d05
commit 18d5abb2f6
70 changed files with 263 additions and 234 deletions

View File

@@ -6,9 +6,9 @@ Tests for Bookmarks models.
import datetime
from contextlib import contextmanager
from unittest import mock
from zoneinfo import ZoneInfo
import ddt
import pytz
from freezegun import freeze_time
from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
@@ -352,7 +352,7 @@ class BookmarkModelTests(BookmarksTestsBase):
bookmark, __ = Bookmark.create(bookmark_data)
assert bookmark.xblock_cache is not None
modification_datetime = datetime.datetime.now(pytz.utc) + datetime.timedelta(seconds=seconds_delta)
modification_datetime = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(seconds=seconds_delta)
with freeze_time(modification_datetime):
bookmark.xblock_cache.paths = paths
bookmark.xblock_cache.save()

View File

@@ -5,6 +5,7 @@ import datetime
import logging
import uuid
from typing import TYPE_CHECKING, Any, List, Union
from zoneinfo import ZoneInfo
import pycountry
import requests
@@ -13,7 +14,6 @@ from django.core.exceptions import ObjectDoesNotExist
from edx_rest_api_client.auth import SuppliedJwtAuth
from edx_rest_api_client.client import USER_AGENT
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from common.djangoapps.entitlements.utils import is_course_run_entitlement_fulfillable
from common.djangoapps.student.models import CourseEnrollment
@@ -593,7 +593,7 @@ def get_fulfillable_course_runs_for_entitlement(entitlement, course_runs):
enrollable_sessions = []
# Only retrieve list of published course runs that can still be enrolled and upgraded
search_time = datetime.datetime.now(UTC)
search_time = datetime.datetime.now(ZoneInfo("UTC"))
for course_run in course_runs:
course_id = CourseKey.from_string(course_run.get("key"))
(user_enrollment_mode, is_active) = CourseEnrollment.enrollment_mode_for_user(

View File

@@ -4,9 +4,9 @@ Unit tests for the API module
import datetime
from unittest import mock
from urllib import parse
from zoneinfo import ZoneInfo
import pytest
import pytz
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
@@ -44,10 +44,10 @@ class APIsTestCase(SharedModuleStoreTestCase):
# Create a course outline
start = datetime.datetime(
2010, 5, 12, 2, 42, tzinfo=pytz.UTC
2010, 5, 12, 2, 42, tzinfo=ZoneInfo("UTC")
)
due = datetime.datetime(
2010, 7, 7, 0, 0, tzinfo=pytz.UTC
2010, 7, 7, 0, 0, tzinfo=ZoneInfo("UTC")
)
cls.chapters = [

View File

@@ -7,8 +7,8 @@ import json
import logging
from datetime import datetime
from urllib.parse import urlparse, urlunparse
from zoneinfo import ZoneInfo
import pytz
from ccx_keys.locator import CCXLocator
from config_models.models import ConfigurationModel
from django.conf import settings
@@ -705,7 +705,7 @@ class CourseOverview(TimeStampedModel):
course_overviews = course_overviews.filter(**filter_)
if active_only:
course_overviews = course_overviews.filter(
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=pytz.UTC))
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=ZoneInfo("UTC")))
)
return course_overviews
@@ -737,11 +737,11 @@ class CourseOverview(TimeStampedModel):
"""
if active_only:
return course_overviews.filter(
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=pytz.UTC))
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=ZoneInfo("UTC")))
)
if archived_only:
return course_overviews.filter(
end__lt=datetime.now().replace(tzinfo=pytz.UTC)
end__lt=datetime.now().replace(tzinfo=ZoneInfo("UTC"))
)
return course_overviews

View File

@@ -10,8 +10,9 @@ import pytest
import datetime # lint-amnesty, pylint: disable=wrong-import-order
import itertools # lint-amnesty, pylint: disable=wrong-import-order
import math # lint-amnesty, pylint: disable=wrong-import-order
from zoneinfo import ZoneInfo
import ddt
import pytz
from django.conf import settings
from django.db.utils import IntegrityError
from django.test.utils import override_settings
@@ -93,7 +94,7 @@ class CourseOverviewTestCase(CatalogIntegrationMixin, ModuleStoreTestCase, Cache
"""
if date_time is None:
return None
epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=ZoneInfo("UTC"))
return math.floor((date_time - epoch).total_seconds())
# Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache

View File

@@ -6,10 +6,10 @@ Tests for the course_overviews app's signal functionality.
import datetime
from unittest.mock import patch
from collections import namedtuple
from zoneinfo import ZoneInfo
import pytest
import ddt
from pytz import UTC
from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore import ModuleStoreEnum
@@ -33,7 +33,7 @@ class CourseOverviewSignalsTestCase(ImmediateOnCommitMixin, ModuleStoreTestCase)
"""
MODULESTORE = TEST_DATA_ONLY_SPLIT_MODULESTORE_DRAFT_PREFERRED
ENABLED_SIGNALS = ['course_deleted', 'course_published']
TODAY = datetime.datetime.utcnow().replace(tzinfo=UTC)
TODAY = datetime.datetime.utcnow().replace(tzinfo=ZoneInfo("UTC"))
NEXT_WEEK = TODAY + datetime.timedelta(days=7)
def assert_changed_signal_sent(self, changes, mock_signal):

View File

@@ -12,12 +12,13 @@ import logging
import shlex
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import dateutil.parser
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from openedx.core.djangoapps.credentials.models import NotifyCredentialsConfig
from openedx.core.djangoapps.credentials.tasks.v1.tasks import handle_notify_credentials
@@ -32,7 +33,7 @@ log = logging.getLogger(__name__)
def parsetime(timestr):
dt = dateutil.parser.parse(timestr)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=UTC)
dt = dt.replace(tzinfo=ZoneInfo("UTC"))
return dt

View File

@@ -7,7 +7,7 @@ import datetime
import logging
import uuid
import pytz
from zoneinfo import ZoneInfo
from django.db import transaction
from edx_proctoring.api import get_last_exam_completion_date
@@ -296,7 +296,7 @@ def create_credit_request(course_key, provider_id, username):
parameters = {
"request_uuid": credit_request.uuid,
"timestamp": to_timestamp(datetime.datetime.now(pytz.UTC)),
"timestamp": to_timestamp(datetime.datetime.now(ZoneInfo("UTC"))),
"course_org": course_key.org,
"course_num": course_key.course,
"course_run": course_key.run,

View File

@@ -10,7 +10,7 @@ import datetime
import logging
from collections import defaultdict
import pytz
from zoneinfo import ZoneInfo
from config_models.models import ConfigurationModel
from django.conf import settings
from django.core.cache import cache
@@ -536,7 +536,7 @@ def default_deadline_for_credit_eligibility():
"""
The default deadline to use when creating a new CreditEligibility model.
"""
return datetime.datetime.now(pytz.UTC) + datetime.timedelta(
return datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(
days=getattr(settings, "CREDIT_ELIGIBILITY_EXPIRATION_DAYS", 365)
)
@@ -617,7 +617,7 @@ class CreditEligibility(TimeStampedModel):
return cls.objects.filter(
username=username,
course__enabled=True,
deadline__gt=datetime.datetime.now(pytz.UTC)
deadline__gt=datetime.datetime.now(ZoneInfo("UTC"))
).select_related('course')
@classmethod
@@ -636,7 +636,7 @@ class CreditEligibility(TimeStampedModel):
course__course_key=course_key,
course__enabled=True,
username=username,
deadline__gt=datetime.datetime.now(pytz.UTC),
deadline__gt=datetime.datetime.now(ZoneInfo("UTC")),
).exists()
def __str__(self):

View File

@@ -4,7 +4,7 @@
import datetime
import logging
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied
@@ -78,7 +78,7 @@ class CreditProviderCallbackSerializer(serializers.Serializer): # pylint:disabl
log.warning(msg)
raise serializers.ValidationError(msg)
elapsed = (datetime.datetime.now(pytz.UTC) - date_time).total_seconds()
elapsed = (datetime.datetime.now(ZoneInfo("UTC")) - date_time).total_seconds()
if elapsed > settings.CREDIT_PROVIDER_TIMESTAMP_EXPIRATION:
msg = f'[{value}] is too far in the past (over [{elapsed}] seconds).'
log.warning(msg)

View File

@@ -7,7 +7,7 @@ import uuid
import factory
from factory.fuzzy import FuzzyText
import pytz
from zoneinfo import ZoneInfo
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from openedx.core.djangoapps.credit.models import (
@@ -80,7 +80,7 @@ class CreditRequestFactory(factory.django.DjangoModelFactory):
obj.parameters = json.dumps({
"request_uuid": obj.uuid,
"timestamp": to_timestamp(datetime.datetime.now(pytz.UTC)),
"timestamp": to_timestamp(datetime.datetime.now(ZoneInfo("UTC"))),
"course_org": course_key.org,
"course_num": course_key.course,
"course_run": course_key.run,

View File

@@ -9,7 +9,7 @@ from unittest import mock
import pytest
import ddt
import httpretty
import pytz
from zoneinfo import ZoneInfo
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core import mail
from django.db import connection
@@ -400,7 +400,7 @@ class CreditRequirementApiTests(CreditApiTestBase):
CreditEligibility.objects.create(
course=credit_course,
username="staff",
deadline=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=1)
deadline=datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=1)
)
# The user should NOT be eligible for credit
@@ -960,7 +960,7 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase):
# Validate the timestamp
assert 'timestamp' in parameters
parsed_date = from_timestamp(parameters['timestamp'])
assert parsed_date < datetime.datetime.now(pytz.UTC)
assert parsed_date < datetime.datetime.now(ZoneInfo("UTC"))
# Validate course information
assert parameters['course_org'] == self.course_key.org

View File

@@ -7,7 +7,7 @@ from unittest import mock
from uuid import uuid4
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.test.client import RequestFactory
from opaque_keys.edx.keys import UsageKey
from openedx_events.data import EventsMetadata
@@ -47,8 +47,8 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
satisfied. But if student grade is less than and deadline is passed then
user will be marked as failed.
"""
VALID_DUE_DATE = datetime.now(pytz.UTC) + timedelta(days=20)
EXPIRED_DUE_DATE = datetime.now(pytz.UTC) - timedelta(days=20)
VALID_DUE_DATE = datetime.now(ZoneInfo("UTC")) + timedelta(days=20)
EXPIRED_DUE_DATE = datetime.now(ZoneInfo("UTC")) - timedelta(days=20)
DATES = {
'valid': VALID_DUE_DATE,

View File

@@ -7,7 +7,7 @@ import datetime
import json
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.test import Client, TestCase
from django.test.utils import override_settings
@@ -523,7 +523,7 @@ class CreditProviderCallbackViewTests(UserMixin, TestCase):
"""
provider_id = kwargs.get('provider_id', self.provider.provider_id)
secret_key = kwargs.get('secret_key', '931433d583c84ca7ba41784bad3232e6')
timestamp = kwargs.get('timestamp', to_timestamp(datetime.datetime.now(pytz.UTC)))
timestamp = kwargs.get('timestamp', to_timestamp(datetime.datetime.now(ZoneInfo("UTC"))))
keys = kwargs.get('keys', {self.provider.provider_id: secret_key})
url = reverse('credit:provider_callback', args=[provider_id])
@@ -577,7 +577,7 @@ class CreditProviderCallbackViewTests(UserMixin, TestCase):
if timedelta == 'invalid':
timestamp = timedelta
else:
timestamp = to_timestamp(datetime.datetime.now(pytz.UTC) + timedelta)
timestamp = to_timestamp(datetime.datetime.now(ZoneInfo("UTC")) + timedelta)
request_uuid = self._create_credit_request_and_get_uuid()
response = self._credit_provider_callback(request_uuid, 'approved', timestamp=timestamp)
assert response.status_code == 400
@@ -585,7 +585,7 @@ class CreditProviderCallbackViewTests(UserMixin, TestCase):
def test_post_with_string_timestamp(self):
""" Verify the endpoint supports timestamps transmitted as strings instead of integers. """
request_uuid = self._create_credit_request_and_get_uuid()
timestamp = str(to_timestamp(datetime.datetime.now(pytz.UTC)))
timestamp = str(to_timestamp(datetime.datetime.now(ZoneInfo("UTC"))))
response = self._credit_provider_callback(request_uuid, 'approved', timestamp=timestamp)
assert response.status_code == 200

View File

@@ -6,7 +6,7 @@ Views for the credit Django app.
import datetime
import logging
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
@@ -166,7 +166,7 @@ class CreditEligibilityView(generics.ListAPIView):
return queryset.filter(
username=username,
course__course_key=course_key,
deadline__gt=datetime.datetime.now(pytz.UTC)
deadline__gt=datetime.datetime.now(ZoneInfo("UTC"))
)

View File

@@ -8,7 +8,7 @@ from unittest.mock import patch
import ddt
import pytest
from pytz import UTC
from zoneinfo import ZoneInfo
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
@@ -369,7 +369,7 @@ class EnrollmentDataTest(ModuleStoreTestCase):
def _update_verified_mode_as_expired(self, course_id):
"""Dry method to change verified mode expiration."""
mode = CourseMode.objects.get(course_id=course_id, mode_slug=CourseMode.VERIFIED)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=UTC)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=ZoneInfo("UTC"))
mode.save()
def assert_enrollment_modes(self, expected_modes, include_expired):

View File

@@ -12,7 +12,7 @@ from urllib.parse import quote
import ddt
import httpretty
import pytest
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
@@ -356,8 +356,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
@ddt.unpack
def test_force_enrollment(self, course_modes, enrollment_mode, force_enrollment):
# Create the course modes (if any) required for this test case
start_date = datetime.datetime(2021, 12, 1, 5, 0, 0, tzinfo=pytz.UTC)
end_date = datetime.datetime(2022, 12, 1, 5, 0, 0, tzinfo=pytz.UTC)
start_date = datetime.datetime(2021, 12, 1, 5, 0, 0, tzinfo=ZoneInfo("UTC"))
end_date = datetime.datetime(2022, 12, 1, 5, 0, 0, tzinfo=ZoneInfo("UTC"))
self.course = CourseFactory.create(
emit_signals=True,
start=start_date,
@@ -658,11 +658,11 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
# enforced at the data layer, so we need to handle the case
# in which no dates are specified.
(None, None, None, None),
(datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=pytz.UTC), None, "2015-01-02T03:04:05Z", None),
(None, datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=pytz.UTC), None, "2015-01-02T03:04:05Z"),
(datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=ZoneInfo("UTC")), None, "2015-01-02T03:04:05Z", None),
(None, datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=ZoneInfo("UTC")), None, "2015-01-02T03:04:05Z"),
(
datetime.datetime(2014, 6, 7, 8, 9, 10, tzinfo=pytz.UTC),
datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=pytz.UTC),
datetime.datetime(2014, 6, 7, 8, 9, 10, tzinfo=ZoneInfo("UTC")),
datetime.datetime(2015, 1, 2, 3, 4, 5, tzinfo=ZoneInfo("UTC")),
"2014-06-07T08:09:10Z",
"2015-01-02T03:04:05Z",
),
@@ -1078,7 +1078,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
# Change verified mode expiration.
mode = CourseMode.objects.get(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=pytz.utc)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=ZoneInfo("UTC"))
mode.save()
# Deactivate enrollment.
@@ -1198,7 +1198,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
# Change verified mode expiration.
mode = CourseMode.objects.get(course_id=self.course.id, mode_slug=CourseMode.VERIFIED)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=pytz.utc)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=ZoneInfo("UTC"))
mode.save()
self.assert_enrollment_status(
as_server=using_api_key,
@@ -1784,7 +1784,7 @@ class CourseEnrollmentsApiListTest(APITestCase, ModuleStoreTestCase):
"""
Test the course enrollments list API.
"""
CREATED_DATA = datetime.datetime(2018, 1, 1, 0, 0, 1, tzinfo=pytz.UTC)
CREATED_DATA = datetime.datetime(2018, 1, 1, 0, 0, 1, tzinfo=ZoneInfo("UTC"))
def setUp(self):
super().setUp()

View File

@@ -7,7 +7,7 @@ import datetime
from django.test import override_settings
import pytest
import ddt
from pytz import UTC
from zoneinfo import ZoneInfo
from django.conf import settings
from xmodule.modulestore import ModuleStoreEnum
@@ -86,13 +86,13 @@ class CourseDetailsTestCase(ModuleStoreTestCase):
jsondetails.self_paced = True
assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).self_paced ==\
jsondetails.self_paced
jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC)
jsondetails.start_date = datetime.datetime(2010, 10, 1, 0, tzinfo=ZoneInfo("UTC"))
assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).start_date ==\
jsondetails.start_date
jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=UTC)
jsondetails.end_date = datetime.datetime(2011, 10, 1, 0, tzinfo=ZoneInfo("UTC"))
assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).end_date ==\
jsondetails.end_date
jsondetails.certificate_available_date = datetime.datetime(2010, 10, 1, 0, tzinfo=UTC)
jsondetails.certificate_available_date = datetime.datetime(2010, 10, 1, 0, tzinfo=ZoneInfo("UTC"))
assert CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user)\
.certificate_available_date == jsondetails.certificate_available_date
jsondetails.course_image_name = "an_image.jpg"
@@ -126,7 +126,7 @@ class CourseDetailsTestCase(ModuleStoreTestCase):
jsondetails.instructor_info
def test_toggle_pacing_during_course_run(self):
self.course.start = datetime.datetime.now(UTC)
self.course.start = datetime.datetime.now(ZoneInfo("UTC"))
self.store.update_item(self.course, self.user.id)
details = CourseDetails.fetch(self.course.id)

View File

@@ -10,7 +10,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from edx_django_utils.monitoring import set_code_owner_attribute
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from zoneinfo import ZoneInfo
from openedx.core.djangoapps.notifications.audience_filters import NotificationFilter
from openedx.core.djangoapps.notifications.base_notification import (
@@ -75,7 +75,7 @@ def delete_expired_notifications():
This task deletes all expired notifications
"""
batch_size = settings.EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE
expiry_date = datetime.now(UTC) - timedelta(days=settings.NOTIFICATIONS_EXPIRY)
expiry_date = datetime.now(ZoneInfo("UTC")) - timedelta(days=settings.NOTIFICATIONS_EXPIRY)
start_time = datetime.now()
total_deleted = 0
delete_count = None

View File

@@ -6,7 +6,7 @@ import ddt
import unittest
from unittest.mock import MagicMock, patch
from datetime import datetime
from pytz import utc
from zoneinfo import ZoneInfo
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.notifications.grouping_notifications import (
@@ -128,7 +128,7 @@ class TestGroupUserNotifications(ModuleStoreTestCase):
self.assertFalse(old_notification.save.called)
@ddt.data(datetime(2023, 1, 1, tzinfo=utc), None)
@ddt.data(datetime(2023, 1, 1, tzinfo=ZoneInfo("UTC")), None)
def test_not_grouped_when_notification_is_seen(self, last_seen):
"""
Notification is not grouped if the notification is marked as seen
@@ -172,11 +172,11 @@ class TestGetUserExistingNotifications(unittest.TestCase):
# Mock the notification objects returned by the filter
mock_notification1 = MagicMock(spec=Notification)
mock_notification1.user_id = 1
mock_notification1.created = datetime(2023, 9, 1, tzinfo=utc)
mock_notification1.created = datetime(2023, 9, 1, tzinfo=ZoneInfo("UTC"))
mock_notification2 = MagicMock(spec=Notification)
mock_notification2.user_id = 1
mock_notification2.created = datetime(2023, 9, 2, tzinfo=utc)
mock_notification2.created = datetime(2023, 9, 2, tzinfo=ZoneInfo("UTC"))
mock_filter.return_value = [mock_notification1, mock_notification2]

View File

@@ -11,7 +11,7 @@ from django.core.cache import cache
from django.test.utils import override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from pytz import UTC
from zoneinfo import ZoneInfo
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
@@ -192,7 +192,7 @@ class NotificationListAPIViewTest(APITestCase):
"""
Test that the view can filter notifications by expiry date.
"""
today = datetime.now(UTC)
today = datetime.now(ZoneInfo("UTC"))
# Create two notifications for the user, one with current date and other with expiry date.
Notification.objects.create(

View File

@@ -8,7 +8,7 @@ from django.db.models import Count
from django_ratelimit.core import is_ratelimited
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext as _
from pytz import UTC
from zoneinfo import ZoneInfo
from rest_framework import generics, status
from rest_framework.decorators import api_view
from rest_framework.generics import UpdateAPIView
@@ -80,7 +80,7 @@ class NotificationListAPIView(generics.ListAPIView):
"""
Override the get_queryset method to filter the queryset by app name, request.user and created
"""
expiry_date = datetime.now(UTC) - timedelta(days=settings.NOTIFICATIONS_EXPIRY)
expiry_date = datetime.now(ZoneInfo("UTC")) - timedelta(days=settings.NOTIFICATIONS_EXPIRY)
app_name = self.request.query_params.get('app_name')
if self.request.query_params.get('tray_opened'):
@@ -212,7 +212,7 @@ class NotificationReadAPIView(APIView):
- 404: Not Found status code if the notification was not found.
"""
notification_id = request.data.get('notification_id', None)
read_at = datetime.now(UTC)
read_at = datetime.now(ZoneInfo("UTC"))
if notification_id:
notification = get_object_or_404(Notification, pk=notification_id, user=request.user)

View File

@@ -11,7 +11,7 @@ from django.dispatch import receiver
from oauth2_provider.models import AccessToken
from oauth2_provider.oauth2_validators import OAuth2Validator
from oauth2_provider.scopes import get_scopes_backend
from pytz import utc
from zoneinfo import ZoneInfo
from ..models import RestrictedApplication
# pylint: disable=W0223
@@ -23,7 +23,7 @@ def on_access_token_presave(sender, instance, *args, **kwargs): # pylint: disab
Mark AccessTokens as expired for 'restricted applications' if required.
"""
if RestrictedApplication.should_expire_access_token(instance.application):
instance.expires = datetime(1970, 1, 1, tzinfo=utc)
instance.expires = datetime(1970, 1, 1, tzinfo=ZoneInfo("UTC"))
class EdxOAuth2Validator(OAuth2Validator):
@@ -152,4 +152,4 @@ def _get_utc_now():
"""
Return current time in UTC.
"""
return datetime.utcnow().replace(tzinfo=utc)
return datetime.utcnow().replace(tzinfo=ZoneInfo("UTC"))

View File

@@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
from django_mysql.models import ListCharField
from oauth2_provider.settings import oauth2_settings
from organizations.models import Organization
from pytz import utc
from zoneinfo import ZoneInfo
from openedx.core.djangolib.markup import HTML
from openedx.core.lib.request_utils import get_request_or_stub
@@ -53,7 +53,7 @@ class RestrictedApplication(models.Model):
For access_tokens for RestrictedApplications, make sure that the expiry date
is set at the beginning of the epoch which is Jan. 1, 1970
"""
return access_token.expires == datetime(1970, 1, 1, tzinfo=utc)
return access_token.expires == datetime(1970, 1, 1, tzinfo=ZoneInfo("UTC"))
class ApplicationAccess(models.Model):

View File

@@ -4,7 +4,7 @@
from datetime import datetime, timedelta
import factory
import pytz
from zoneinfo import ZoneInfo
from factory.django import DjangoModelFactory
from factory.fuzzy import FuzzyText
from oauth2_provider.models import AccessToken, Application, RefreshToken
@@ -39,7 +39,7 @@ class AccessTokenFactory(DjangoModelFactory):
django_get_or_create = ('user', 'application')
token = FuzzyText(length=32)
expires = datetime.now(pytz.UTC) + timedelta(days=1)
expires = datetime.now(ZoneInfo("UTC")) + timedelta(days=1)
class RefreshTokenFactory(DjangoModelFactory):

View File

@@ -4,7 +4,7 @@ Utilities for enforcing and tracking compliance with password policy rules.
from datetime import datetime
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.utils.translation import gettext as _
@@ -69,7 +69,7 @@ def enforce_compliance_on_login(user, password):
if deadline is None:
return
now = datetime.now(pytz.UTC)
now = datetime.now(ZoneInfo("UTC"))
if now >= deadline: # lint-amnesty, pylint: disable=no-else-raise
raise NonCompliantPasswordException(
HTML(_(

View File

@@ -6,7 +6,7 @@ from datetime import datetime, timedelta
from unittest.mock import patch
import pytest
import pytz
from zoneinfo import ZoneInfo
from dateutil.parser import parse as parse_date
from django.test import TestCase, override_settings
@@ -75,7 +75,7 @@ class TestCompliance(TestCase):
mock_check_user_compliance.return_value = False
with patch('openedx.core.djangoapps.password_policy.compliance._get_compliance_deadline_for_user') as \
mock_get_compliance_deadline_for_user:
mock_get_compliance_deadline_for_user.return_value = datetime.now(pytz.UTC) - timedelta(1)
mock_get_compliance_deadline_for_user.return_value = datetime.now(ZoneInfo("UTC")) - timedelta(1)
pytest.raises(NonCompliantPasswordException, enforce_compliance_on_login, user, password)
# Test deadline is in the future
@@ -84,7 +84,7 @@ class TestCompliance(TestCase):
mock_check_user_compliance.return_value = False
with patch('openedx.core.djangoapps.password_policy.compliance._get_compliance_deadline_for_user') as \
mock_get_compliance_deadline_for_user:
mock_get_compliance_deadline_for_user.return_value = datetime.now(pytz.UTC) + timedelta(1)
mock_get_compliance_deadline_for_user.return_value = datetime.now(ZoneInfo("UTC")) + timedelta(1)
assert pytest.raises(NonCompliantPasswordWarning, enforce_compliance_on_login, user, password)
def test_check_user_compliance(self):

View File

@@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
import datetime # lint-amnesty, pylint: disable=wrong-import-order
from pytz import UTC
from zoneinfo import ZoneInfo
from django.urls import reverse
from django.http import HttpResponse
@@ -30,8 +30,8 @@ from ..views import LOG_MESSAGE_CREATE, LOG_MESSAGE_DELETE
from .helpers import make_image_file
TEST_PASSWORD = "test"
TEST_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=UTC)
TEST_UPLOAD_DT2 = datetime.datetime(2003, 1, 9, 15, 43, 1, tzinfo=UTC)
TEST_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=ZoneInfo("UTC"))
TEST_UPLOAD_DT2 = datetime.datetime(2003, 1, 9, 15, 43, 1, tzinfo=ZoneInfo("UTC"))
class ProfileImageEndpointMixin(UserSettingsEventTestMixin):

View File

@@ -11,7 +11,7 @@ from contextlib import closing
from django.utils.translation import gettext as _
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser
from pytz import UTC
from zoneinfo import ZoneInfo
from rest_framework import permissions, status
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.response import Response
@@ -38,7 +38,7 @@ def _make_upload_dt():
Generate a server-side timestamp for the upload. This is in a separate
function so its behavior can be overridden in tests.
"""
return datetime.datetime.utcnow().replace(tzinfo=UTC)
return datetime.datetime.utcnow().replace(tzinfo=ZoneInfo("UTC"))
class ProfileImageView(DeveloperErrorViewMixin, APIView):

View File

@@ -10,7 +10,7 @@ from unittest import mock
import ddt
import httpretty
import pytest
import pytz
from zoneinfo import ZoneInfo
import requests
from celery.exceptions import MaxRetriesExceededError
from django.conf import settings
@@ -520,7 +520,7 @@ class AwardCourseCertificatesTestCase(CredentialsApiConfigMixin, TestCase):
def setUp(self):
super().setUp()
self.available_date = datetime.now(pytz.UTC) + timedelta(days=1)
self.available_date = datetime.now(ZoneInfo("UTC")) + timedelta(days=1)
self.course = CourseOverviewFactory.create(
self_paced=True, # Any option to allow the certificate to be viewable for the course
certificate_available_date=self.available_date,
@@ -1023,7 +1023,7 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(CredentialsApiConfigM
def setUp(self):
super().setUp()
self.end_date = datetime.now(pytz.UTC) + timedelta(days=90)
self.end_date = datetime.now(ZoneInfo("UTC")) + timedelta(days=90)
self.credentials_api_config = self.create_credentials_config(enabled=False)
def tearDown(self):
@@ -1135,7 +1135,7 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(CredentialsApiConfigM
explicitly set as part of the course overview.
"""
self._update_credentials_api_config(True)
certificate_available_date = datetime.now(pytz.UTC) + timedelta(days=120)
certificate_available_date = datetime.now(ZoneInfo("UTC")) + timedelta(days=120)
course_overview = self._create_course_overview(
False,
@@ -1168,7 +1168,7 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(CredentialsApiConfigM
invalid data is set in a course overview, we don't pass it to Credentials.
"""
self._update_credentials_api_config(True)
certificate_available_date = datetime.now(pytz.UTC) + timedelta(days=120)
certificate_available_date = datetime.now(ZoneInfo("UTC")) + timedelta(days=120)
course_overview = self._create_course_overview(
True,

View File

@@ -15,7 +15,7 @@ from django.test.utils import override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_switch
from opaque_keys.edx.keys import CourseKey # lint-amnesty, pylint: disable=wrong-import-order
from pytz import utc
from zoneinfo import ZoneInfo
from testfixtures import LogCapture
from common.djangoapps.course_modes.models import CourseMode
@@ -209,7 +209,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
CourseEntitlementFactory.create(
user=self.user,
course_uuid=course_uuid,
expired_at=datetime.datetime.now(utc),
expired_at=datetime.datetime.now(ZoneInfo("UTC")),
mode=CourseMode.VERIFIED,
enrollment_course_run=enrollment
@@ -308,7 +308,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
the right type for which the upgrade deadline has not passed.
"""
course_run_key = generate_course_run_key()
now = datetime.datetime.now(utc)
now = datetime.datetime.now(ZoneInfo("UTC"))
upgrade_deadline = None if not offset else str(now + datetime.timedelta(days=offset))
required_seat = SeatFactory(type=CourseMode.VERIFIED, upgrade_deadline=upgrade_deadline)
enrolled_seat = SeatFactory(type=CourseMode.AUDIT)
@@ -488,7 +488,7 @@ class TestProgramProgressMeter(ModuleStoreTestCase):
def test_simulate_progress(self, mock_get_programs): # lint-amnesty, pylint: disable=too-many-statements
"""Simulate the entirety of a user's progress through a program."""
today = datetime.datetime.now(utc)
today = datetime.datetime.now(ZoneInfo("UTC"))
two_days_ago = today - datetime.timedelta(days=2)
three_days_ago = today - datetime.timedelta(days=3)
yesterday = today - datetime.timedelta(days=1)
@@ -862,8 +862,8 @@ def _create_course(self, course_price, course_run_count=1, make_entitlement=Fals
course_runs = []
for x in range(course_run_count):
course = ModuleStoreCourseFactory.create(run='Run_' + str(x))
course.start = datetime.datetime.now(utc) - datetime.timedelta(days=1)
course.end = datetime.datetime.now(utc) + datetime.timedelta(days=1)
course.start = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=1)
course.end = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=1)
course.instructor_info = self.instructors
course = self.update_course(course, self.user.id)
@@ -899,8 +899,8 @@ class TestProgramDataExtender(ModuleStoreTestCase):
super().setUp()
self.course = ModuleStoreCourseFactory()
self.course.start = datetime.datetime.now(utc) - datetime.timedelta(days=1)
self.course.end = datetime.datetime.now(utc) + datetime.timedelta(days=1)
self.course.start = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=1)
self.course.end = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=1)
self.course = self.update_course(self.course, self.user.id)
self.course_run = CourseRunFactory(key=str(self.course.id))
@@ -941,7 +941,7 @@ class TestProgramDataExtender(ModuleStoreTestCase):
Verify that changes to the course run end date do not affect our
assessment of the course run being open for enrollment.
"""
self.course.end = datetime.datetime.now(utc) + datetime.timedelta(days=days_offset)
self.course.end = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=days_offset)
self.course = self.update_course(self.course, self.user.id)
data = ProgramDataExtender(self.program, self.user).extend()
@@ -1022,8 +1022,8 @@ class TestProgramDataExtender(ModuleStoreTestCase):
"""
Verify that course run enrollment status is reflected correctly.
"""
self.course.enrollment_start = datetime.datetime.now(utc) - datetime.timedelta(days=start_offset)
self.course.enrollment_end = datetime.datetime.now(utc) - datetime.timedelta(days=end_offset)
self.course.enrollment_start = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=start_offset)
self.course.enrollment_end = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=end_offset)
self.course = self.update_course(self.course, self.user.id)
@@ -1040,7 +1040,7 @@ class TestProgramDataExtender(ModuleStoreTestCase):
Verify that a closed course run with no explicit enrollment start date
doesn't cause an error. Regression test for ECOM-4973.
"""
self.course.enrollment_end = datetime.datetime.now(utc) - datetime.timedelta(days=1)
self.course.enrollment_end = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=1)
self.course = self.update_course(self.course, self.user.id)
data = ProgramDataExtender(self.program, self.user).extend()

View File

@@ -14,7 +14,7 @@ from django.core.cache import cache
from django.urls import reverse
from django.utils.functional import cached_property
from opaque_keys.edx.keys import CourseKey
from pytz import utc
from zoneinfo import ZoneInfo
from requests.exceptions import RequestException
from common.djangoapps.course_modes.api import get_paid_modes_for_course
@@ -43,7 +43,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from xmodule.modulestore.django import modulestore
# The datetime module's strftime() methods require a year >= 1900.
DEFAULT_ENROLLMENT_START_DATE = datetime.datetime(1900, 1, 1, tzinfo=utc)
DEFAULT_ENROLLMENT_START_DATE = datetime.datetime(1900, 1, 1, tzinfo=ZoneInfo("UTC"))
log = logging.getLogger(__name__)
@@ -286,7 +286,7 @@ class ProgramProgressMeter:
list of dict, each containing information about a user's progress
towards completing a program.
"""
now = datetime.datetime.now(utc)
now = datetime.datetime.now(ZoneInfo("UTC"))
progress = []
programs = programs or self.engaged_programs
@@ -598,15 +598,17 @@ class ProgramDataExtender:
run_mode["enrollment_open_date"] = strftime_localized(self.enrollment_start, "SHORT_DATE")
def _attach_course_run_is_course_ended(self, run_mode):
end_date = self.course_overview.end or datetime.datetime.max.replace(tzinfo=utc)
run_mode["is_course_ended"] = end_date < datetime.datetime.now(utc)
end_date = self.course_overview.end or datetime.datetime.max.replace(tzinfo=ZoneInfo("UTC"))
run_mode["is_course_ended"] = end_date < datetime.datetime.now(ZoneInfo("UTC"))
def _attach_course_run_is_enrolled(self, run_mode):
run_mode["is_enrolled"] = CourseEnrollment.is_enrolled(self.user, self.course_run_key)
def _attach_course_run_is_enrollment_open(self, run_mode):
enrollment_end = self.course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=utc)
run_mode["is_enrollment_open"] = self.enrollment_start <= datetime.datetime.now(utc) < enrollment_end
enrollment_end = self.course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=ZoneInfo("UTC"))
run_mode["is_enrollment_open"] = (
self.enrollment_start <= datetime.datetime.now(ZoneInfo("UTC")) < enrollment_end
)
def _attach_course_run_advertised_start(self, run_mode):
"""

View File

@@ -5,7 +5,7 @@ Base management command for sending emails
import datetime
import pytz
from zoneinfo import ZoneInfo
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
@@ -62,7 +62,7 @@ class SendEmailBaseCommand(PrefixedDebugLoggerMixin, BaseCommand): # lint-amnes
current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],
tzinfo=pytz.UTC
tzinfo=ZoneInfo("UTC")
)
self.log_debug('Current date = %s', current_date.isoformat())
override_recipient_email = options.get('override_recipient_email')

View File

@@ -3,7 +3,7 @@ Management command to send Schedule course updates
"""
import datetime
import pytz
from zoneinfo import ZoneInfo
from textwrap import dedent # lint-amnesty, pylint: disable=wrong-import-order
from django.contrib.sites.models import Site
@@ -23,7 +23,7 @@ class Command(SendEmailBaseCommand):
def handle(self, *args, ** options):
current_date = datetime.datetime(
*[int(x) for x in options['date'].split('-')],
tzinfo=pytz.UTC
tzinfo=ZoneInfo("UTC")
)
site = Site.objects.get(domain__iexact=options['site_domain_name'])

View File

@@ -7,7 +7,7 @@ import datetime
from textwrap import dedent
import factory
import pytz
from zoneinfo import ZoneInfo
from django.contrib.sites.models import Site
from django.core.management.base import BaseCommand
@@ -26,29 +26,29 @@ class ThreeDayNudgeSchedule(ScheduleFactory):
"""
A ScheduleFactory that creates a Schedule set up for a 3-day nudge email.
"""
start_date = factory.Faker('date_time_between', start_date='-3d', end_date='-3d', tzinfo=pytz.UTC)
start_date = factory.Faker('date_time_between', start_date='-3d', end_date='-3d', tzinfo=ZoneInfo("UTC"))
class TenDayNudgeSchedule(ScheduleFactory):
"""
A ScheduleFactory that creates a Schedule set up for a 10-day nudge email.
"""
start_date = factory.Faker('date_time_between', start_date='-10d', end_date='-10d', tzinfo=pytz.UTC)
start_date = factory.Faker('date_time_between', start_date='-10d', end_date='-10d', tzinfo=ZoneInfo("UTC"))
class UpgradeReminderSchedule(ScheduleFactory):
"""
A ScheduleFactory that creates a Schedule set up for a 2-days-remaining upgrade reminder.
"""
start_date = factory.Faker('past_datetime', tzinfo=pytz.UTC)
upgrade_deadline = factory.Faker('date_time_between', start_date='+2d', end_date='+2d', tzinfo=pytz.UTC)
start_date = factory.Faker('past_datetime', tzinfo=ZoneInfo("UTC"))
upgrade_deadline = factory.Faker('date_time_between', start_date='+2d', end_date='+2d', tzinfo=ZoneInfo("UTC"))
class ContentHighlightSchedule(ScheduleFactory):
"""
A ScheduleFactory that creates a Schedule set up for a course highlights email.
"""
start_date = factory.Faker('date_time_between', start_date='-7d', end_date='-7d', tzinfo=pytz.UTC)
start_date = factory.Faker('date_time_between', start_date='-7d', end_date='-7d', tzinfo=ZoneInfo("UTC"))
experience = factory.RelatedFactory(ScheduleExperienceFactory, 'schedule', experience_type=ScheduleExperience.EXPERIENCES.course_updates) # lint-amnesty, pylint: disable=line-too-long

View File

@@ -11,7 +11,7 @@ from unittest.mock import Mock, patch
import attr
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.db.models import Max
@@ -119,7 +119,7 @@ class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): # lint-amnesty, pyli
return max_user_id + num_bins - (max_user_id % num_bins)
def _get_dates(self, offset=None): # lint-amnesty, pylint: disable=missing-function-docstring
current_day = _get_datetime_beginning_of_day(datetime.datetime.now(pytz.UTC))
current_day = _get_datetime_beginning_of_day(datetime.datetime.now(ZoneInfo("UTC")))
offset = offset or self.expected_offsets[0]
target_day = current_day + datetime.timedelta(days=offset)
if self.resolver.schedule_date_field == 'upgrade_deadline':
@@ -148,7 +148,7 @@ class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): # lint-amnesty, pyli
CourseModeFactory(
course_id=course_id,
mode_slug=CourseMode.VERIFIED,
expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
expiration_datetime=datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=30),
)
self._courses_with_verified_modes.add(course_id)
return schedule
@@ -158,7 +158,7 @@ class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): # lint-amnesty, pyli
Updates the schedule config model by making sure the new entry
has a later timestamp.
"""
later_time = datetime.datetime.now(pytz.UTC) + datetime.timedelta(minutes=1)
later_time = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(minutes=1)
with freeze_time(later_time):
ScheduleConfigFactory.create(**schedule_config_kwargs)
@@ -167,7 +167,7 @@ class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): # lint-amnesty, pyli
def test_handle(self):
with patch.object(self.command, 'async_send_task') as mock_send:
test_day = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
test_day = datetime.datetime(2017, 8, 1, tzinfo=ZoneInfo("UTC"))
self.command().handle(date='2017-08-01', site_domain_name=self.site_config.site.domain)
for offset in self.expected_offsets:
@@ -287,7 +287,7 @@ class ScheduleSendEmailTestMixin(FilteredQueryCountMixin): # lint-amnesty, pyli
}
self._update_schedule_config(schedule_config_kwargs)
current_datetime = datetime.datetime(2017, 8, 1, tzinfo=pytz.UTC)
current_datetime = datetime.datetime(2017, 8, 1, tzinfo=ZoneInfo("UTC"))
with patch.object(self.task, 'apply_async') as mock_apply_async:
self.task.enqueue(self.site_config.site, current_datetime, 3)

View File

@@ -8,7 +8,7 @@ from unittest import skipUnless
from unittest.mock import DEFAULT, Mock, patch
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.contrib.sites.models import Site
@@ -33,7 +33,7 @@ class TestSendEmailBaseCommand(CacheIsolationTestCase): # lint-amnesty, pylint:
self.command.handle(site_domain_name=self.site.domain, date='2017-09-29')
send_emails.assert_called_once_with(
self.site,
datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC),
datetime.datetime(2017, 9, 29, tzinfo=ZoneInfo("UTC")),
None,
None
)
@@ -45,7 +45,7 @@ class TestSendEmailBaseCommand(CacheIsolationTestCase): # lint-amnesty, pylint:
for expected_site in expected_sites:
send_emails.assert_any_call(
expected_site,
datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC),
datetime.datetime(2017, 9, 29, tzinfo=ZoneInfo("UTC")),
None,
None
)

View File

@@ -4,7 +4,7 @@ Factories for schedules tests
import factory
import pytz
from zoneinfo import ZoneInfo
from openedx.core.djangoapps.schedules import models
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
@@ -22,8 +22,8 @@ class ScheduleFactory(factory.django.DjangoModelFactory): # lint-amnesty, pylin
class Meta:
model = models.Schedule
start_date = factory.Faker('future_datetime', tzinfo=pytz.UTC)
upgrade_deadline = factory.Faker('future_datetime', tzinfo=pytz.UTC)
start_date = factory.Faker('future_datetime', tzinfo=ZoneInfo("UTC"))
upgrade_deadline = factory.Faker('future_datetime', tzinfo=ZoneInfo("UTC"))
enrollment = factory.SubFactory(CourseEnrollmentFactory)
experience = factory.RelatedFactory(ScheduleExperienceFactory, 'schedule')

View File

@@ -8,7 +8,7 @@ from unittest.mock import Mock
import crum
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.test import TestCase
from django.test.client import RequestFactory
from django.test.utils import override_settings
@@ -123,7 +123,7 @@ class TestBinnedSchedulesBaseResolver(SchedulesResolverTestMixin, TestCase):
# experiment. Note that the experiment waffle is currently inactive, but they should still be excluded because
# they were bucketed at enrollment time.
bin_num = BinnedSchedulesBaseResolver.bin_num_for_user_id(user.id)
resolver = BinnedSchedulesBaseResolver(None, self.site, datetime.datetime.now(pytz.UTC), 0, bin_num)
resolver = BinnedSchedulesBaseResolver(None, self.site, datetime.datetime.now(ZoneInfo("UTC")), 0, bin_num)
resolver.schedule_date_field = 'created'
schedules = resolver.get_schedules_with_target_date_by_bin_and_orgs()
@@ -235,7 +235,7 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
def setUp(self):
super().setUp()
self.today = datetime.datetime.utcnow()
self.today = datetime.datetime.now(ZoneInfo("UTC"))
self.yesterday = self.today - datetime.timedelta(days=1)
self.course = CourseFactory.create(
highlights_enabled_for_messaging=True, self_paced=True,

View File

@@ -8,7 +8,7 @@ from unittest.mock import patch
import ddt
import pytest
from pytz import utc
from zoneinfo import ZoneInfo
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
@@ -188,7 +188,7 @@ def _create_course_run(self_paced=True, start_day_offset=-1):
Both audit and verified `CourseMode` objects will be created for the course run.
"""
now = datetime.datetime.now(utc)
now = datetime.datetime.now(ZoneInfo("UTC"))
start = now + datetime.timedelta(days=start_day_offset)
course = CourseFactory.create(start=start, self_paced=self_paced)

View File

@@ -5,7 +5,7 @@ Tests for schedules utils
import datetime
import ddt
from pytz import utc
from zoneinfo import ZoneInfo
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -26,7 +26,7 @@ class ResetSelfPacedScheduleTests(SharedModuleStoreTestCase):
# pylint: disable=attribute-defined-outside-init
self.config = ScheduleConfigFactory()
start = datetime.datetime.now(utc) + datetime.timedelta(days=course_start_offset)
start = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=course_start_offset)
self.course = CourseFactory.create(start=start, self_paced=True)
self.enrollment = CourseEnrollmentFactory(

View File

@@ -3,7 +3,7 @@
import datetime
import logging
import pytz
from zoneinfo import ZoneInfo
from django.db import transaction
from openedx.core.djangoapps.schedules.models import Schedule
@@ -59,7 +59,7 @@ def reset_self_paced_schedule(user, course_key, use_enrollment_date=False):
if use_enrollment_date:
new_start_date = schedule.enrollment.created
else:
new_start_date = datetime.datetime.now(pytz.utc)
new_start_date = datetime.datetime.now(ZoneInfo("UTC"))
# Make sure we don't start the clock on the learner's schedule before the course even starts
new_start_date = max(new_start_date, schedule.enrollment.course.start)

View File

@@ -12,7 +12,7 @@ from django.core.validators import ValidationError, validate_email
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from eventtracking import tracker
from pytz import UTC
from zoneinfo import ZoneInfo
from common.djangoapps.student import views as student_views
from common.djangoapps.student.models import (
@@ -375,7 +375,7 @@ def _store_old_name_if_needed(old_name, user_profile, requesting_user):
meta['old_names'].append([
old_name,
f"Name change requested through account API by {requesting_user.username}",
datetime.datetime.now(UTC).isoformat()
datetime.datetime.now(ZoneInfo("UTC")).isoformat()
])
user_profile.set_meta(meta)
user_profile.save()

View File

@@ -6,7 +6,7 @@ Helpers for testing retirement functionality
import datetime
import pytest
import pytz
from zoneinfo import ZoneInfo
from django.test import TestCase
from social_django.models import UserSocialAuth
@@ -67,7 +67,7 @@ def create_retirement_status(user, state=None, create_datetime=None):
Assumes that retirement states have been setup before calling.
"""
if create_datetime is None:
create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=8)
create_datetime = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=8)
retirement = UserRetirementStatus.create_retirement(user)
if state:

View File

@@ -17,7 +17,7 @@ from django.http import HttpResponse
from django.test import TestCase
from django.test.client import RequestFactory
from django.urls import reverse
from pytz import UTC
from zoneinfo import ZoneInfo
from social_django.models import UserSocialAuth
from common.djangoapps.student.models import (
@@ -381,7 +381,7 @@ class TestAccountApi(UserSettingsEventTestMixin, EmailTemplateTagMixin, CreateAc
meta['old_names'] = []
for num in range(3):
meta['old_names'].append(
[f'old_name_{num}', 'test', datetime.datetime.now(UTC).isoformat()]
[f'old_name_{num}', 'test', datetime.datetime.now(ZoneInfo("UTC")).isoformat()]
)
user_profile.set_meta(meta)
user_profile.save()

View File

@@ -8,7 +8,7 @@ import hashlib
from unittest.mock import patch
from django.test import TestCase
from pytz import UTC
from zoneinfo import ZoneInfo
from openedx.core.djangolib.testing.utils import skip_unless_lms
from common.djangoapps.student.tests.factories import UserFactory
@@ -16,7 +16,7 @@ from common.djangoapps.student.tests.factories import UserFactory
from ..image_helpers import get_profile_image_urls_for_user
TEST_SIZES = {'full': 50, 'small': 10}
TEST_PROFILE_IMAGE_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=UTC)
TEST_PROFILE_IMAGE_UPLOAD_DT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=ZoneInfo("UTC"))
@patch.dict('django.conf.settings.PROFILE_IMAGE_SIZES_MAP', TEST_SIZES, clear=True)

View File

@@ -7,7 +7,7 @@ import json
from unittest import mock
import ddt
import pytz
from zoneinfo import ZoneInfo
from consent.models import DataSharingConsent
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.contrib.sites.models import Site
@@ -516,7 +516,7 @@ class TestPartnerReportingList(ModuleStoreTestCase):
self.headers = build_jwt_headers(self.test_superuser)
self.url = reverse('accounts_retirement_partner_report')
self.maxDiff = None
self.test_created_datetime = datetime.datetime(2018, 1, 1, tzinfo=pytz.UTC)
self.test_created_datetime = datetime.datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
ExternalIdType.objects.get_or_create(name=ExternalIdType.CALIPER)
def get_user_dict(self, user, enrollments):
@@ -769,7 +769,7 @@ class TestAccountRetirementList(RetirementTestCase):
# retirements = [2018-04-10..., 2018-04-09..., 2018-04-08...]
pending_state = RetirementState.objects.get(state_name='PENDING')
for days_back in range(1, days_back_to_test, -1):
create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_back)
create_datetime = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=days_back)
retirements.append(create_retirement_status(
UserFactory(),
state=pending_state,
@@ -927,12 +927,12 @@ class TestAccountRetirementsByStatusAndDate(RetirementTestCase):
# Create retirements for the last 10 days
for days_back in range(0, 10): # lint-amnesty, pylint: disable=simplifiable-range
create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=days_back)
create_datetime = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=days_back)
ret = create_retirement_status(UserFactory(), state=complete_state, create_datetime=create_datetime)
retirements.append(self._retirement_to_dict(ret))
# Go back in time adding days to the query, assert the correct retirements are present
end_date = datetime.datetime.now(pytz.UTC)
end_date = datetime.datetime.now(ZoneInfo("UTC"))
for days_back in range(1, 11):
retirement_dicts = retirements[:days_back]
start_date = end_date - datetime.timedelta(days=days_back - 1)

View File

@@ -10,7 +10,7 @@ from unittest import mock
from urllib.parse import quote
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.test.testcases import TransactionTestCase
@@ -44,7 +44,7 @@ from openedx.features.name_affirmation_api.utils import get_name_affirmation_ser
from .. import ALL_USERS_VISIBILITY, CUSTOM_VISIBILITY, PRIVATE_VISIBILITY
TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=pytz.UTC)
TEST_PROFILE_IMAGE_UPLOADED_AT = datetime.datetime(2002, 1, 9, 15, 43, 1, tzinfo=ZoneInfo("UTC"))
# this is used in one test to check the behavior of profile image url
# generation with a relative url in the config.
@@ -304,7 +304,7 @@ class TestCancelAccountRetirementStatusView(UserAPITestCase):
current_state=retirement_state,
last_state=retirement_state,
original_email=self.user.email,
created=datetime.datetime.now(pytz.UTC)
created=datetime.datetime.now(ZoneInfo("UTC"))
)
url = reverse("cancel_account_retirement")
response = client.post(url, data={'retirement_id': user_retirement_status.id})
@@ -329,7 +329,7 @@ class TestCancelAccountRetirementStatusView(UserAPITestCase):
current_state=retirement_state,
last_state=retirement_state,
original_email=self.user.email,
created=datetime.datetime.now(pytz.UTC)
created=datetime.datetime.now(ZoneInfo("UTC"))
)
user_retirement_status.user.set_unusable_password()
assert UserRetirementStatus.objects.count() == 1
@@ -585,8 +585,8 @@ class TestAccountsAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAPITe
@mock.patch('openedx.core.djangoapps.user_api.accounts.views.is_email_retired')
@ddt.data(
(datetime.datetime.now(pytz.UTC), True),
(datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=15), False)
(datetime.datetime.now(ZoneInfo("UTC")), True),
(datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=15), False)
)
@ddt.unpack
def test_search_emails_retired_before_cooloff_period(self, created_date, can_cancel, mock_is_email_retired):

View File

@@ -9,7 +9,7 @@ import datetime
import logging
from functools import wraps
import pytz
from zoneinfo import ZoneInfo
from consent.models import DataSharingConsent
from django.apps import apps
from django.conf import settings
@@ -198,11 +198,11 @@ class AccountViewSet(ViewSet):
if is_email_retired(user_email):
can_cancel_retirement = True
retirement_id = None
earliest_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=settings.COOL_OFF_DAYS)
earliest_datetime = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=settings.COOL_OFF_DAYS)
try:
retirement_status = UserRetirementStatus.objects.get(
created__gt=earliest_datetime,
created__lt=datetime.datetime.now(pytz.UTC),
created__lt=datetime.datetime.now(ZoneInfo("UTC")),
original_email=user_email,
)
retirement_id = retirement_status.id
@@ -891,7 +891,7 @@ class AccountRetirementStatusView(ViewSet):
status=status.HTTP_400_BAD_REQUEST,
)
earliest_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=cool_off_days)
earliest_datetime = datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=cool_off_days)
retirements = (
UserRetirementStatus.objects.select_related("user", "current_state", "last_state")
@@ -921,9 +921,12 @@ class AccountRetirementStatusView(ViewSet):
so to get one day you would set both dates to that day.
"""
try:
start_date = datetime.datetime.strptime(request.GET["start_date"], "%Y-%m-%d").replace(tzinfo=pytz.UTC)
end_date = datetime.datetime.strptime(request.GET["end_date"], "%Y-%m-%d").replace(tzinfo=pytz.UTC)
now = datetime.datetime.now(pytz.UTC)
start_date = (
datetime.datetime.strptime(request.GET["start_date"], "%Y-%m-%d")
.replace(tzinfo=ZoneInfo("UTC"))
)
end_date = datetime.datetime.strptime(request.GET["end_date"], "%Y-%m-%d").replace(tzinfo=ZoneInfo("UTC"))
now = datetime.datetime.now(ZoneInfo("UTC"))
if start_date > now or end_date > now or start_date > end_date:
raise RetirementStateError("Dates must be today or earlier, and start must be earlier than end.")

View File

@@ -20,7 +20,7 @@ from enterprise.models import (
)
from integrated_channels.sap_success_factors.models import SapSuccessFactorsLearnerDataTransmissionAudit
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from zoneinfo import ZoneInfo
from common.djangoapps.entitlements.models import CourseEntitlement, CourseEntitlementSupportDetail
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
@@ -82,7 +82,7 @@ class Command(BaseCommand):
user.save()
# UserProfile
profile_image_uploaded_date = datetime(2018, 5, 3, tzinfo=UTC)
profile_image_uploaded_date = datetime(2018, 5, 3, tzinfo=ZoneInfo("UTC"))
user_profile, __ = UserProfile.objects.get_or_create(
user=user
)

View File

@@ -6,7 +6,7 @@ from enum import Enum
from unittest.mock import patch
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from oauth2_provider import models as dot_models
from rest_framework import status
@@ -42,7 +42,7 @@ def utcnow():
"""
Helper function to return the current UTC time localized to the UTC timezone.
"""
return datetime.now(pytz.UTC)
return datetime.now(ZoneInfo("UTC"))
@ddt.ddt

View File

@@ -26,7 +26,7 @@ from edx_django_utils.monitoring import set_custom_attribute
from openedx_events.learning.data import UserData, UserPersonalData
from openedx_events.learning.signals import STUDENT_REGISTRATION_COMPLETED
from openedx_filters.learning.filters import StudentRegistrationRequested
from pytz import UTC
from zoneinfo import ZoneInfo
from django_ratelimit.decorators import ratelimit
from requests import HTTPError
from rest_framework.response import Response
@@ -371,7 +371,7 @@ def _track_user_registration(user, profile, params, third_party_provider, regist
'name': profile.name,
# Mailchimp requires the age & yearOfBirth to be integers, we send a sane integer default if falsey.
'age': profile.age or -1,
'yearOfBirth': profile.year_of_birth or datetime.datetime.now(UTC).year,
'yearOfBirth': profile.year_of_birth or datetime.datetime.now(ZoneInfo("UTC")).year,
'education': profile.level_of_education_display,
'address': profile.mailing_address,
'gender': profile.gender_display,
@@ -530,7 +530,9 @@ def _record_utm_registration_attribution(request, user):
# We divide by 1000 here because the javascript timestamp generated is in milliseconds not seconds.
# PYTHON: time.time() => 1475590280.823698
# JS: new Date().getTime() => 1475590280823
created_at_datetime = datetime.datetime.fromtimestamp(int(created_at_unixtime) / float(1000), tz=UTC)
created_at_datetime = datetime.datetime.fromtimestamp(
int(created_at_unixtime) / float(1000), tz=ZoneInfo("UTC")
)
UserAttribute.set_user_attribute(
user,
REGISTRATION_UTM_CREATED_AT,

View File

@@ -19,7 +19,7 @@ from django.urls import reverse
from freezegun import freeze_time
from oauth2_provider.models import AccessToken as dot_access_token
from oauth2_provider.models import RefreshToken as dot_refresh_token
from pytz import UTC
from zoneinfo import ZoneInfo
from testfixtures import LogCapture
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
@@ -319,7 +319,7 @@ class TestPasswordChange(CreateAccountMixin, CacheIsolationTestCase):
# now reset the time to 1 min from now in future and change the email and
# verify that it will allow another request from same IP
reset_time = datetime.now(UTC) + timedelta(seconds=61)
reset_time = datetime.now(ZoneInfo("UTC")) + timedelta(seconds=61)
with freeze_time(reset_time):
response = self._change_password(email=self.OLD_EMAIL)
assert response.status_code == 200

View File

@@ -16,7 +16,7 @@ from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from openedx_events.tests.utils import OpenEdxEventsTestMixin
from pytz import UTC
from zoneinfo import ZoneInfo
from social_django.models import Partial, UserSocialAuth
from testfixtures import LogCapture
@@ -949,7 +949,7 @@ class RegistrationViewTestV1(
)
def test_register_form_year_of_birth(self):
this_year = datetime.now(UTC).year
this_year = datetime.now(ZoneInfo("UTC")).year
year_options = (
[
{

View File

@@ -24,7 +24,7 @@ from django.urls import reverse
from django.utils.http import int_to_base36
from freezegun import freeze_time
from oauth2_provider import models as dot_models
from pytz import UTC
from zoneinfo import ZoneInfo
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
@@ -267,7 +267,7 @@ class ResetPasswordTests(EventTestMixin, CacheIsolationTestCase):
self.request_password_reset(200)
# now reset the time to 1 min from now in future and change the email and
# verify that it will allow another request from same IP
reset_time = datetime.now(UTC) + timedelta(seconds=61)
reset_time = datetime.now(ZoneInfo("UTC")) + timedelta(seconds=61)
with freeze_time(reset_time):
for status in [200, 403]:
self.request_password_reset(status)

View File

@@ -3,7 +3,7 @@
from datetime import datetime
from pytz import UTC
from zoneinfo import ZoneInfo
from openedx.core.djangoapps.course_groups.models import CourseUserGroupPartitionGroup
from openedx.core.djangoapps.course_groups.tests.helpers import CohortFactory
@@ -32,7 +32,7 @@ class ContentGroupTestCase(ModuleStoreTestCase):
# This test needs to use a course that has already started --
# discussion topics only show up if the course has already started,
# and the default start date for courses is Jan 1, 2030.
start=datetime(2012, 2, 3, tzinfo=UTC),
start=datetime(2012, 2, 3, tzinfo=ZoneInfo("UTC")),
user_partitions=[
UserPartition(
0,

View File

@@ -5,7 +5,7 @@ Tests for verified_track_content/partition_scheme.py.
from datetime import datetime, timedelta
import pytz
from zoneinfo import ZoneInfo
import pytest
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.models import CourseEnrollment
@@ -38,7 +38,7 @@ class EnrollmentTrackUserPartitionTest(SharedModuleStoreTestCase):
# Note that the verified mode is expired-- this is intentional.
create_mode(
self.course, CourseMode.VERIFIED, "Verified Enrollment Track", min_price=1,
expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=-1)
expiration_datetime=datetime.now(ZoneInfo("UTC")) + timedelta(days=-1)
)
# Note that the credit mode is not selectable-- this is intentional so we
# can test that it is filtered out.
@@ -128,7 +128,7 @@ class EnrollmentTrackPartitionSchemeTest(SharedModuleStoreTestCase):
def test_enrolled_in_expired(self):
create_mode(
self.course, CourseMode.VERIFIED, "Verified Enrollment Track",
min_price=1, expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=-1)
min_price=1, expiration_datetime=datetime.now(ZoneInfo("UTC")) + timedelta(days=-1)
)
CourseEnrollment.enroll(self.student, self.course.id, mode=CourseMode.VERIFIED)
assert 'Verified Enrollment Track' == self._get_user_group().name
@@ -153,7 +153,7 @@ class EnrollmentTrackPartitionSchemeTest(SharedModuleStoreTestCase):
# the upgrade deadline has passed (see EDUCATOR-1511 for why this matters).
create_mode(
self.course, CourseMode.VERIFIED, "Verified Enrollment Track", min_price=1,
expiration_datetime=datetime.now(pytz.UTC) + timedelta(days=-1)
expiration_datetime=datetime.now(ZoneInfo("UTC")) + timedelta(days=-1)
)
assert 'Verified Enrollment Track' == self._get_user_group().name

View File

@@ -19,7 +19,7 @@ from django.utils.html import escape
from edx_django_utils.plugins import pluggable_override
from lxml import etree, html
from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2
from pytz import UTC
from zoneinfo import ZoneInfo
from web_fragments.fragment import Fragment
from xblock.core import XBlock
from xblock.exceptions import InvalidScopeError
@@ -310,7 +310,7 @@ def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context)
# Useful to indicate to staff if problem has been released or not.
# TODO (ichuang): use _has_access_block.can_load in lms.courseware.access,
# instead of now>mstart comparison here.
now = datetime.datetime.now(UTC)
now = datetime.datetime.now(ZoneInfo("UTC"))
is_released = "unknown"
mstart = block.start

View File

@@ -2,7 +2,7 @@
from datetime import datetime, timedelta
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.utils.translation import gettext as _
from icalendar import Calendar, Event, vCalAddress, vText
@@ -59,7 +59,7 @@ def generate_ics_files_for_user_course(course, user, user_calendar_sync_config_i
assignments = get_course_assignments(course.id, user)
platform_name = get_value('platform_name', settings.PLATFORM_NAME)
platform_email = get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
now = datetime.now(pytz.utc)
now = datetime.now(ZoneInfo("UTC"))
site_config = SiteConfiguration.get_configuration_for_org(course.org)
ics_files = {}

View File

@@ -3,7 +3,7 @@
from datetime import datetime, timedelta
from unittest.mock import patch
import pytz
from zoneinfo import ZoneInfo
from django.test import RequestFactory, TestCase
from freezegun import freeze_time
@@ -21,7 +21,7 @@ class TestIcsGeneration(TestCase):
def setUp(self):
super().setUp()
freezer = freeze_time(datetime(2013, 10, 3, 8, 24, 55, tzinfo=pytz.utc))
freezer = freeze_time(datetime(2013, 10, 3, 8, 24, 55, tzinfo=ZoneInfo("UTC")))
self.addCleanup(freezer.stop)
freezer.start()
@@ -103,7 +103,7 @@ END:VCALENDAR
def test_generate_ics_for_user_course(self):
""" Tests that a simple sample set of course assignments is generated correctly """
now = datetime.now(pytz.utc)
now = datetime.now(ZoneInfo("UTC"))
day1 = now + timedelta(1)
day2 = now + timedelta(1)

View File

@@ -10,7 +10,7 @@ import datetime
import logging
import crum
import pytz
from zoneinfo import ZoneInfo
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from web_fragments.fragment import Fragment
@@ -88,7 +88,7 @@ class ContentTypeGatingPartition(UserPartition):
return None
expiration_datetime = verified_mode.expiration_datetime
if expiration_datetime and expiration_datetime < datetime.datetime.now(pytz.UTC):
if expiration_datetime and expiration_datetime < datetime.datetime.now(ZoneInfo("UTC")):
ecommerce_checkout_link = None
else:
ecommerce_checkout_link = self._get_checkout_link(user, verified_mode.sku, str(course_key))

View File

@@ -6,7 +6,7 @@ import itertools # lint-amnesty, pylint: disable=wrong-import-order
from datetime import datetime, timedelta # lint-amnesty, pylint: disable=wrong-import-order
import ddt
import pytz
from zoneinfo import ZoneInfo
from django.utils import timezone
from edx_django_utils.cache import RequestCache
from unittest.mock import Mock # lint-amnesty, pylint: disable=wrong-import-order
@@ -217,17 +217,17 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase): # pylint: disable=mi
# Point-test some of the final configurations
assert all_configs[CourseLocator('7-True', 'test_course', 'run-None')] == {
'enabled': (True, Provenance.org),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")), Provenance.run),
'studio_override_enabled': (None, Provenance.default)
}
assert all_configs[CourseLocator('7-True', 'test_course', 'run-False')] == {
'enabled': (False, Provenance.run),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")), Provenance.run),
'studio_override_enabled': (None, Provenance.default)
}
assert all_configs[CourseLocator('7-None', 'test_course', 'run-None')] == {
'enabled': (True, Provenance.site),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")), Provenance.run),
'studio_override_enabled': (None, Provenance.default)
}

View File

@@ -8,7 +8,7 @@ import ddt
from crum import set_current_request
from django.test import RequestFactory
from django.utils import timezone
from pytz import UTC
from zoneinfo import ZoneInfo
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from common.djangoapps.course_modes.models import CourseMode
@@ -34,9 +34,12 @@ class TestAccess(ModuleStoreTestCase):
def setUp(self):
super().setUp() # lint-amnesty, pylint: disable=super-with-arguments
CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=UTC))
CourseDurationLimitConfig.objects.create(
enabled=True,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
self.course = CourseOverviewFactory.create(start=datetime(2018, 1, 1, tzinfo=UTC), self_paced=True)
self.course = CourseOverviewFactory.create(start=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC")), self_paced=True)
def assertDateInMessage(self, date, message): # lint-amnesty, pylint: disable=missing-function-docstring
# First, check that the formatted version is in there
@@ -148,7 +151,7 @@ class TestAccess(ModuleStoreTestCase):
course_id=enrollment.course.id,
mode_slug=CourseMode.AUDIT,
)
Schedule.objects.update(start_date=datetime(2017, 1, 1, tzinfo=UTC))
Schedule.objects.update(start_date=datetime(2017, 1, 1, tzinfo=ZoneInfo("UTC")))
content_availability_date = max(enrollment.created, enrollment.course.start)
access_duration = get_user_course_duration(enrollment.user, enrollment.course)

View File

@@ -8,7 +8,7 @@ from unittest.mock import Mock
import ddt
import pytest
import pytz
from zoneinfo import ZoneInfo
from django.utils import timezone
from edx_django_utils.cache import RequestCache
from opaque_keys.edx.locator import CourseLocator
@@ -178,13 +178,18 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
def test_all_current_course_configs(self):
# Set up test objects
for global_setting in (True, False, None):
CourseDurationLimitConfig.objects.create(enabled=global_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
CourseDurationLimitConfig.objects.create(
enabled=global_setting,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
for site_setting in (True, False, None):
test_site_cfg = SiteConfigurationFactory.create(
site_values={'course_org_filter': []}
)
CourseDurationLimitConfig.objects.create(
site=test_site_cfg.site, enabled=site_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)
site=test_site_cfg.site,
enabled=site_setting,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
for org_setting in (True, False, None):
@@ -193,7 +198,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
test_site_cfg.save()
CourseDurationLimitConfig.objects.create(
org=test_org, enabled=org_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)
org=test_org, enabled=org_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
for course_setting in (True, False, None):
@@ -202,7 +207,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
id=CourseLocator(test_org, 'test_course', f'run-{course_setting}')
)
CourseDurationLimitConfig.objects.create(
course=test_course, enabled=course_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC) # lint-amnesty, pylint: disable=line-too-long
course=test_course, enabled=course_setting, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC")) # lint-amnesty, pylint: disable=line-too-long
)
with self.assertNumQueries(4):
@@ -216,22 +221,25 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
# Point-test some of the final configurations
assert all_configs[CourseLocator('7-True', 'test_course', 'run-None')] == {
'enabled': (True, Provenance.org),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")),
Provenance.run)
}
assert all_configs[CourseLocator('7-True', 'test_course', 'run-False')] == {
'enabled': (False, Provenance.run),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")),
Provenance.run)
}
assert all_configs[CourseLocator('7-None', 'test_course', 'run-None')] == {
'enabled': (True, Provenance.site),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC),
'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=ZoneInfo("UTC")),
Provenance.run)
}
def test_caching_global(self):
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC))
global_config = CourseDurationLimitConfig(
enabled=True,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
global_config.save()
RequestCache.clear_all_namespaces()
@@ -257,7 +265,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
def test_caching_site(self):
site_cfg = SiteConfigurationFactory()
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
site_config.save()
RequestCache.clear_all_namespaces()
@@ -281,7 +289,10 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(1):
assert not CourseDurationLimitConfig.current(site=site_cfg.site).enabled
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC))
global_config = CourseDurationLimitConfig(
enabled=True,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
global_config.save()
RequestCache.clear_all_namespaces()
@@ -295,7 +306,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
site_cfg = SiteConfigurationFactory.create(
site_values={'course_org_filter': course.org}
)
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
org_config.save()
RequestCache.clear_all_namespaces()
@@ -319,7 +330,10 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(2):
assert not CourseDurationLimitConfig.current(org=course.org).enabled
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC))
global_config = CourseDurationLimitConfig(
enabled=True,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
global_config.save()
RequestCache.clear_all_namespaces()
@@ -328,7 +342,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(0):
assert not CourseDurationLimitConfig.current(org=course.org).enabled
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
site_config.save()
RequestCache.clear_all_namespaces()
@@ -342,7 +356,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
site_cfg = SiteConfigurationFactory.create(
site_values={'course_org_filter': course.org}
)
course_config = CourseDurationLimitConfig(course=course, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
course_config = CourseDurationLimitConfig(course=course, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
course_config.save()
RequestCache.clear_all_namespaces()
@@ -366,7 +380,10 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(2):
assert not CourseDurationLimitConfig.current(course_key=course.id).enabled
global_config = CourseDurationLimitConfig(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC))
global_config = CourseDurationLimitConfig(
enabled=True,
enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))
)
global_config.save()
RequestCache.clear_all_namespaces()
@@ -375,7 +392,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(0):
assert not CourseDurationLimitConfig.current(course_key=course.id).enabled
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
site_config = CourseDurationLimitConfig(site=site_cfg.site, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
site_config.save()
RequestCache.clear_all_namespaces()
@@ -384,7 +401,7 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase):
with self.assertNumQueries(0):
assert not CourseDurationLimitConfig.current(course_key=course.id).enabled
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=pytz.UTC)) # lint-amnesty, pylint: disable=line-too-long
org_config = CourseDurationLimitConfig(org=course.org, enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC"))) # lint-amnesty, pylint: disable=line-too-long
org_config.save()
RequestCache.clear_all_namespaces()

View File

@@ -5,7 +5,7 @@ Tests for the course updates page.
from datetime import datetime
from django.urls import reverse
from pytz import UTC
from zoneinfo import ZoneInfo
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
@@ -41,7 +41,7 @@ class TestCourseUpdatesPage(BaseCourseUpdatesTestCase):
self.assertContains(response, 'Second Message')
def test_queries(self):
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=UTC))
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=ZoneInfo("UTC")))
self.create_course_update('First Message')
# Pre-fetch the view to populate any caches

View File

@@ -11,7 +11,7 @@ not other discounts like coupons or enterprise/program offers configured in ecom
from datetime import datetime, timedelta
import pytz
from zoneinfo import ZoneInfo
from crum import get_current_request, impersonate
from django.conf import settings
from django.utils import timezone
@@ -197,7 +197,7 @@ def _is_in_holdback_and_bucket(user):
Return whether the specified user is in the first-purchase-discount holdback group.
This will also stable bucket the user.
"""
if datetime(2020, 8, 1, tzinfo=pytz.UTC) <= datetime.now(tz=pytz.UTC):
if datetime(2020, 8, 1, tzinfo=ZoneInfo("UTC")) <= datetime.now(tz=ZoneInfo("UTC")):
return False
# Holdback is 10%

View File

@@ -6,7 +6,7 @@ from unittest.mock import Mock, patch
import ddt
import pytest
import pytz
from zoneinfo import ZoneInfo
from django.contrib.sites.models import Site
from django.utils.timezone import now
from edx_toggles.toggles.testutils import override_waffle_flag
@@ -39,7 +39,7 @@ class TestApplicability(ModuleStoreTestCase):
self.user = UserFactory.create()
self.course = CourseFactory.create(run='test', display_name='test')
CourseModeFactory.create(course_id=self.course.id, mode_slug='verified')
now_time = datetime.now(tz=pytz.UTC).strftime("%Y-%m-%d %H:%M:%S%z")
now_time = datetime.now(tz=ZoneInfo("UTC")).strftime("%Y-%m-%d %H:%M:%S%z")
ExperimentData.objects.create(
user=self.user, experiment_id=REV1008_EXPERIMENT_ID, key=str(self.course.id), value=now_time
)
@@ -175,6 +175,6 @@ class TestApplicability(ModuleStoreTestCase):
with patch('openedx.features.discounts.applicability.stable_bucketing_hash_group', return_value=0):
with patch(
'openedx.features.discounts.applicability.datetime',
Mock(now=Mock(return_value=datetime(2020, 8, 1, 0, 1, tzinfo=pytz.UTC)), wraps=datetime),
Mock(now=Mock(return_value=datetime(2020, 8, 1, 0, 1, tzinfo=ZoneInfo("UTC"))), wraps=datetime),
):
assert not _is_in_holdback_and_bucket(self.user)

View File

@@ -4,7 +4,7 @@ Utility functions for working with discounts and discounted pricing.
from datetime import datetime
import pytz
from zoneinfo import ZoneInfo
from django.conf import settings
from django.utils.translation import get_language
from django.utils.translation import gettext as _
@@ -89,7 +89,7 @@ def generate_offer_data(user, course):
ExperimentData.objects.get_or_create(
user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course),
defaults={
'value': datetime.now(tz=pytz.UTC).strftime('%Y-%m-%d %H:%M:%S%z'),
'value': datetime.now(tz=ZoneInfo("UTC")).strftime('%Y-%m-%d %H:%M:%S%z'),
},
)

View File

@@ -11,7 +11,7 @@ from completion import handlers
from completion.models import BlockCompletion
from completion.test_utils import CompletionSetUpMixin
from django.test import TestCase
from pytz import utc
from zoneinfo import ZoneInfo
from xblock.completable import XBlockCompletionMode
from xblock.core import XBlock
@@ -66,7 +66,7 @@ class ScorableCompletionHandlerTestCase(CompletionSetUpMixin, TestCase):
usage_id=str(block_key),
weighted_earned=0.0,
weighted_possible=3.0,
modified=datetime.utcnow().replace(tzinfo=utc),
modified=datetime.utcnow().replace(tzinfo=ZoneInfo("UTC")),
score_db_table='submissions',
**params
)
@@ -127,7 +127,7 @@ class ScorableCompletionHandlerTestCase(CompletionSetUpMixin, TestCase):
usage_id=str(self.block_key),
weighted_earned=0.0,
weighted_possible=3.0,
modified=datetime.utcnow().replace(tzinfo=utc),
modified=datetime.utcnow().replace(tzinfo=ZoneInfo("UTC")),
score_db_table='submissions',
)
mock_handler.assert_called()
@@ -153,7 +153,7 @@ class DisabledCompletionHandlerTestCase(CompletionSetUpMixin, TestCase):
usage_id=str(self.block_key),
weighted_earned=0.0,
weighted_possible=3.0,
modified=datetime.utcnow().replace(tzinfo=utc),
modified=datetime.utcnow().replace(tzinfo=ZoneInfo("UTC")),
score_db_table='submissions',
)
with pytest.raises(BlockCompletion.DoesNotExist):

View File

@@ -44,7 +44,7 @@ from datetime import datetime, timedelta
import html
from unittest import mock
import pytz
from zoneinfo import ZoneInfo
from bs4 import BeautifulSoup
from django.conf import settings
from django.urls import reverse
@@ -199,7 +199,7 @@ class GradePublishTestMixin:
'score': score,
'max_score': max_score})
# Shim a return time, defaults to 1 hour before now
return datetime.now().replace(tzinfo=pytz.UTC) - timedelta(hours=1)
return datetime.now().replace(tzinfo=ZoneInfo("UTC")) - timedelta(hours=1)
self.scores = []
patcher = mock.patch("lms.djangoapps.grades.signals.handlers.set_score", capture_score)