diff --git a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py index c648b2e5b875..0ae745399c78 100644 --- a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py @@ -3,7 +3,6 @@ import datetime import ddt -import pytz from django.test import RequestFactory from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole @@ -11,6 +10,7 @@ from openedx.core.lib.courses import course_image_url from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo from ...serializers.course_runs import CourseRunSerializer from ..utils import serialize_datetime @@ -22,7 +22,7 @@ class CourseRunSerializerTests(ModuleStoreTestCase): # lint-amnesty, pylint: di def setUp(self): super().setUp() - self.course_start = datetime.datetime.now(pytz.UTC) + self.course_start = datetime.datetime.now(ZoneInfo("UTC")) self.course_end = self.course_start + datetime.timedelta(days=30) self.request = RequestFactory().get('') diff --git a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py index 78261a421424..ea0afb3d4bee 100644 --- a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py @@ -5,7 +5,6 @@ from unittest.mock import patch # lint-amnesty, pylint: disable=unused-import import ddt -import pytz from django.core.files.uploadedfile import SimpleUploadedFile from django.test import RequestFactory, override_settings from django.urls import reverse @@ -22,6 +21,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory, ToyCourseFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo from ...serializers.course_runs import CourseRunSerializer from ..utils import serialize_datetime @@ -117,7 +117,7 @@ def test_update(self): assert CourseAccessRole.objects.filter(course_id=course_run.id).count() == 0 url = reverse('api:v1:course_run-detail', kwargs={'pk': str(course_run.id)}) - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) end = start + datetime.timedelta(days=30) title = 'A New Testing Strategy' user = UserFactory() @@ -165,7 +165,7 @@ def test_update_with_pacing_type(self): """ Test that update run updates the pacing type """ - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) course_run = CourseFactory(start=start, end=None, self_paced=False) data = { 'pacing_type': 'self_paced', @@ -183,7 +183,7 @@ def test_update_with_instructor_role(self): Test that update creates a new instructor role only if it does not exist """ instructor_role = 'instructor' - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) new_user = UserFactory() course_run = CourseFactory(start=start, end=None, self_paced=False) assert CourseAccessRole.objects.filter(course_id=course_run.id).count() == 0 @@ -214,7 +214,7 @@ def test_update_with_multiple_roles(self): """ staff_role = 'staff' instructor_role = 'instructor' - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) course_run = CourseFactory(start=start, end=None, self_paced=False) existing_user = UserFactory() @@ -255,7 +255,7 @@ def test_update_with_multiple_roles(self): def test_create(self, pacing_type, expected_self_paced_value): """Tests successful course run creation""" user = UserFactory() - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) end = start + datetime.timedelta(days=30) role = 'staff' data = self.get_course_run_data(user, start, end, pacing_type, role) @@ -280,7 +280,7 @@ def test_create_with_invalid_course_team(self): with expected validation message """ user = UserFactory() - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) end = start + datetime.timedelta(days=30) data = self.get_course_run_data(user, start, end, 'self-paced') data['team'] = [{'user': 'invalid-username'}] @@ -334,7 +334,7 @@ def test_rerun(self, pacing_type, expected_self_paced_value, number): 'short_name': original_course_run.id.org, # lint-amnesty, pylint: disable=no-member 'description': 'Testing Organization Description', }) - start = datetime.datetime.now(pytz.UTC).replace(microsecond=0) + start = datetime.datetime.now(ZoneInfo("UTC")).replace(microsecond=0) end = start + datetime.timedelta(days=30) user = UserFactory() role = 'instructor' diff --git a/cms/djangoapps/contentstore/api/views/course_validation.py b/cms/djangoapps/contentstore/api/views/course_validation.py index be5208430a18..867d6c915453 100644 --- a/cms/djangoapps/contentstore/api/views/course_validation.py +++ b/cms/djangoapps/contentstore/api/views/course_validation.py @@ -2,7 +2,6 @@ import logging import dateutil -from pytz import UTC from rest_framework.generics import GenericAPIView from rest_framework.response import Response @@ -13,6 +12,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from .utils import course_author_access_required, get_bool_param +from zoneinfo import ZoneInfo log = logging.getLogger(__name__) @@ -260,34 +260,34 @@ def _get_open_responses(self, course, graded_only): def _has_date_before_start(self, ora, start): # lint-amnesty, pylint: disable=missing-function-docstring if ora.submission_start: - if dateutil.parser.parse(ora.submission_start).replace(tzinfo=UTC) < start: + if dateutil.parser.parse(ora.submission_start).replace(tzinfo=ZoneInfo("UTC")) < start: return True if ora.submission_due: - if dateutil.parser.parse(ora.submission_due).replace(tzinfo=UTC) < start: + if dateutil.parser.parse(ora.submission_due).replace(tzinfo=ZoneInfo("UTC")) < start: return True for assessment in ora.rubric_assessments: if assessment['start']: - if dateutil.parser.parse(assessment['start']).replace(tzinfo=UTC) < start: + if dateutil.parser.parse(assessment['start']).replace(tzinfo=ZoneInfo("UTC")) < start: return True if assessment['due']: - if dateutil.parser.parse(assessment['due']).replace(tzinfo=UTC) < start: + if dateutil.parser.parse(assessment['due']).replace(tzinfo=ZoneInfo("UTC")) < start: return True return False def _has_date_after_end(self, ora, end): # lint-amnesty, pylint: disable=missing-function-docstring if ora.submission_start: - if dateutil.parser.parse(ora.submission_start).replace(tzinfo=UTC) > end: + if dateutil.parser.parse(ora.submission_start).replace(tzinfo=ZoneInfo("UTC")) > end: return True if ora.submission_due: - if dateutil.parser.parse(ora.submission_due).replace(tzinfo=UTC) > end: + if dateutil.parser.parse(ora.submission_due).replace(tzinfo=ZoneInfo("UTC")) > end: return True for assessment in ora.rubric_assessments: if assessment['start']: - if dateutil.parser.parse(assessment['start']).replace(tzinfo=UTC) > end: + if dateutil.parser.parse(assessment['start']).replace(tzinfo=ZoneInfo("UTC")) > end: return True if assessment['due']: - if dateutil.parser.parse(assessment['due']).replace(tzinfo=UTC) > end: + if dateutil.parser.parse(assessment['due']).replace(tzinfo=ZoneInfo("UTC")) > end: return True return False diff --git a/cms/djangoapps/contentstore/management/commands/tests/test_clean_stale_certificate_availability_dates.py b/cms/djangoapps/contentstore/management/commands/tests/test_clean_stale_certificate_availability_dates.py index 42b3ca408aa2..bf335e42c58b 100644 --- a/cms/djangoapps/contentstore/management/commands/tests/test_clean_stale_certificate_availability_dates.py +++ b/cms/djangoapps/contentstore/management/commands/tests/test_clean_stale_certificate_availability_dates.py @@ -4,7 +4,6 @@ from datetime import datetime, timedelta from django.core.management import CommandError, call_command -import pytz from cms.djangoapps.contentstore.models import CleanStaleCertificateAvailabilityDatesConfig from openedx.core.lib.courses import get_course_by_id @@ -12,6 +11,7 @@ from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +from zoneinfo import ZoneInfo class CleanStaleCertAvailableDateTests(ModuleStoreTestCase): @@ -26,7 +26,7 @@ def setUp(self): self.instructor_paced_course1 = CourseFactory() # set the self-paced courses to self-paced, create and insert an invalid certificate available date for them - self.certificate_available_date = datetime.now(pytz.UTC) + timedelta(days=30) + self.certificate_available_date = datetime.now(ZoneInfo("UTC")) + timedelta(days=30) self.self_paced_course1.self_paced = True self.self_paced_course1.certificate_available_date = self.certificate_available_date self.self_paced_course2.self_paced = True diff --git a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py index cd7592c46629..626101b3ea4d 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views/tests/test_home.py @@ -5,7 +5,6 @@ from datetime import datetime, timedelta import ddt -import pytz from django.conf import settings from django.test import override_settings from django.urls import reverse @@ -21,6 +20,7 @@ from cms.djangoapps.modulestore_migrator.tests.factories import ModulestoreSourceFactory from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory from openedx.core.djangoapps.content_libraries import api as lib_api +from zoneinfo import ZoneInfo @ddt.ddt @@ -197,7 +197,7 @@ def test_filter_and_ordering_courses( display_name="Course (Demo)", id=archived_course_key, org=archived_course_key.org, - end=(datetime.now() - timedelta(days=365)).replace(tzinfo=pytz.UTC), + end=(datetime.now() - timedelta(days=365)).replace(tzinfo=ZoneInfo("UTC")), ) active_course_key = self.store.make_course_key("sample-org", "sample-number", "sample-run") CourseOverviewFactory.create( diff --git a/cms/djangoapps/contentstore/rest_api/v2/views/tests/test_home.py b/cms/djangoapps/contentstore/rest_api/v2/views/tests/test_home.py index 6a51610ac9f2..615aadf6e174 100644 --- a/cms/djangoapps/contentstore/rest_api/v2/views/tests/test_home.py +++ b/cms/djangoapps/contentstore/rest_api/v2/views/tests/test_home.py @@ -6,7 +6,6 @@ from datetime import datetime, timedelta import ddt -import pytz from django.conf import settings from django.urls import reverse from rest_framework import status @@ -14,6 +13,7 @@ from cms.djangoapps.contentstore.tests.utils import CourseTestCase from cms.djangoapps.contentstore.utils import reverse_course_url from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory +from zoneinfo import ZoneInfo @ddt.ddt @@ -36,7 +36,7 @@ def setUp(self): display_name="Demo Course (Sample)", id=archived_course_key, org=archived_course_key.org, - end=(datetime.now() - timedelta(days=365)).replace(tzinfo=pytz.UTC), + end=(datetime.now() - timedelta(days=365)).replace(tzinfo=ZoneInfo("UTC")), ) self.non_staff_client, _ = self.create_non_staff_authed_user_client() @@ -256,7 +256,7 @@ def test_filter_and_ordering_courses( display_name="Course (Demo)", id=archived_course_key, org=archived_course_key.org, - end=(datetime.now() - timedelta(days=365)).replace(tzinfo=pytz.UTC), + end=(datetime.now() - timedelta(days=365)).replace(tzinfo=ZoneInfo("UTC")), ) active_course_key = self.store.make_course_key("foo-org", "foo-number", "foo-run") CourseOverviewFactory.create( diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index e28cbf313acb..d9a4dd32bb4f 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -29,7 +29,6 @@ XBLOCK_DELETED, XBLOCK_UPDATED, ) -from pytz import UTC from cms.djangoapps.contentstore.courseware_index import ( CourseAboutSearchIndexer, @@ -45,6 +44,7 @@ from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import SignalHandler, modulestore from xmodule.modulestore.exceptions import ItemNotFoundError +from zoneinfo import ZoneInfo from ..models import ComponentLink, ContainerLink from ..tasks import ( @@ -156,7 +156,9 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable= # Kick off a courseware indexing action after the data is ready if CoursewareSearchIndexer.indexing_is_enabled() and CourseAboutSearchIndexer.indexing_is_enabled(): - transaction.on_commit(lambda: update_search_index.delay(course_key_str, datetime.now(UTC).isoformat())) + transaction.on_commit( + lambda: update_search_index.delay(course_key_str, datetime.now(ZoneInfo("UTC")).isoformat()) + ) update_discussions_settings_from_course_task.apply_async( args=[course_key_str], @@ -186,7 +188,7 @@ def listen_for_library_update(sender, library_key, **kwargs): # pylint: disable # import here, because signal is registered at startup, but items in tasks are not yet able to be loaded from cms.djangoapps.contentstore.tasks import update_library_index - update_library_index.delay(str(library_key), datetime.now(UTC).isoformat()) + update_library_index.delay(str(library_key), datetime.now(ZoneInfo("UTC")).isoformat()) @receiver(SignalHandler.item_deleted) diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index 13c72ff3391a..4cea64eb05ca 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -42,7 +42,6 @@ from organizations.exceptions import InvalidOrganizationException from organizations.models import Organization from path import Path as path -from pytz import UTC from user_tasks.models import UserTaskArtifact, UserTaskStatus from user_tasks.tasks import UserTask @@ -93,6 +92,7 @@ from xmodule.modulestore.xml_importer import CourseImportException, import_course_from_xml, import_library_from_xml from xmodule.tabs import StaticTab from xmodule.util.keys import BlockKey +from zoneinfo import ZoneInfo from .models import ComponentLink, ContainerLink, LearningContextLinksStatus, LearningContextLinksStatusChoices from .outlines import update_outline_from_modulestore @@ -249,7 +249,7 @@ def _parse_time(time_isoformat): # remove the +00:00 from the end of the formats generated within the system time_isoformat.split('+')[0], "%Y-%m-%dT%H:%M:%S.%f" - ).replace(tzinfo=UTC) + ).replace(tzinfo=ZoneInfo("UTC")) @shared_task diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 9afba26b32e5..350775e2a7b6 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -26,7 +26,6 @@ from edx_toggles.toggles.testutils import override_waffle_flag from milestones.models import MilestoneRelationshipType from milestones.tests.utils import MilestonesTestCaseMixin -from pytz import UTC from cms.djangoapps.contentstore import toggles from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url @@ -52,6 +51,7 @@ from xmodule.fields import Date # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo from .utils import AjaxEnabledTestClient, CourseTestCase @@ -84,7 +84,7 @@ def test_pre_1900_date(self): doesn't work for these dates. """ details = CourseDetails.fetch(self.course.id) - pre_1900 = datetime.datetime(1564, 4, 23, 1, 1, 1, tzinfo=UTC) + pre_1900 = datetime.datetime(1564, 4, 23, 1, 1, 1, tzinfo=ZoneInfo("UTC")) details.enrollment_start = pre_1900 dumped_jsondetails = json.dumps(details, cls=CourseSettingsEncoder) loaded_jsondetails = json.loads(dumped_jsondetails) @@ -97,7 +97,7 @@ def test_ooc_encoder(self): details = { 'number': 1, 'string': 'string', - 'datetime': datetime.datetime.now(UTC) + 'datetime': datetime.datetime.now(ZoneInfo("UTC")) } jsondetails = json.dumps(details, cls=CourseSettingsEncoder) jsondetails = json.loads(jsondetails) @@ -256,12 +256,13 @@ def test_update_and_fetch(self): resp = self.client.get_json(url) self.compare_details_with_encoding(json.loads(resp.content.decode('utf-8')), details.__dict__, "virgin get") - self.alter_field(url, details, 'start_date', datetime.datetime(2012, 11, 12, 1, 30, tzinfo=UTC)) - self.alter_field(url, details, 'start_date', datetime.datetime(2012, 11, 1, 13, 30, tzinfo=UTC)) - self.alter_field(url, details, 'end_date', datetime.datetime(2013, 2, 12, 1, 30, tzinfo=UTC)) - self.alter_field(url, details, 'enrollment_start', datetime.datetime(2012, 10, 12, 1, 30, tzinfo=UTC)) - - self.alter_field(url, details, 'enrollment_end', datetime.datetime(2012, 11, 15, 1, 30, tzinfo=UTC)) + self.alter_field(url, details, 'start_date', datetime.datetime(2012, 11, 12, 1, 30, tzinfo=ZoneInfo("UTC"))) + self.alter_field(url, details, 'start_date', datetime.datetime(2012, 11, 1, 13, 30, tzinfo=ZoneInfo("UTC"))) + self.alter_field(url, details, 'end_date', datetime.datetime(2013, 2, 12, 1, 30, tzinfo=ZoneInfo("UTC"))) + self.alter_field( + url, details, 'enrollment_start', datetime.datetime(2012, 10, 12, 1, 30, tzinfo=ZoneInfo("UTC")) + ) + self.alter_field(url, details, 'enrollment_end', datetime.datetime(2012, 11, 15, 1, 30, tzinfo=ZoneInfo("UTC"))) self.alter_field(url, details, 'short_description', "Short Description") self.alter_field(url, details, 'about_sidebar_html', "About Sidebar HTML") self.alter_field(url, details, 'overview', "Overview") @@ -1501,7 +1502,7 @@ def test_validate_update_does_not_allow_proctoring_provider_changes_after_course Only admin users may update the provider if the course has started. """ field_name = "proctoring_provider" - course = CourseFactory.create(start=datetime.datetime.now(UTC) - datetime.timedelta(days=1)) + course = CourseFactory.create(start=datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=1)) user = UserFactory.create(is_staff=staff_user) did_validate, errors, test_model = CourseMetadata.validate_and_update_from_json( diff --git a/cms/djangoapps/contentstore/tests/test_courseware_index.py b/cms/djangoapps/contentstore/tests/test_courseware_index.py index 3ab3fa373f81..38313af694b0 100644 --- a/cms/djangoapps/contentstore/tests/test_courseware_index.py +++ b/cms/djangoapps/contentstore/tests/test_courseware_index.py @@ -11,7 +11,6 @@ import pytest from django.conf import settings from lazy.lazy import lazy -from pytz import UTC from search.search_engine_base import SearchEngine from cms.djangoapps.contentstore.courseware_index import ( @@ -37,6 +36,7 @@ ) from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory, LibraryFactory # lint-amnesty, pylint: disable=wrong-import-order from xmodule.partitions.partitions import UserPartition # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo COURSE_CHILD_STRUCTURE = { "course": "chapter", @@ -56,7 +56,7 @@ def create_children(store, parent, category, load_factor): display_name=f"{category} {child_index} {time.clock()}", # lint-amnesty, pylint: disable=no-member modulestore=store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) created_count += 1 @@ -72,7 +72,7 @@ def create_large_course(store, load_factor): load_factor ^ 4 - e.g. load_factor of 10 => 10 chapters, 100 sequentials, 1000 verticals, 10000 html blocks """ - course = CourseFactory.create(modulestore=store, start=datetime(2015, 3, 1, tzinfo=UTC)) + course = CourseFactory.create(modulestore=store, start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC"))) with store.bulk_operations(course.id): child_count = create_children(store, course, COURSE_CHILD_STRUCTURE["course"], load_factor) return course, child_count @@ -148,7 +148,7 @@ def setup_course_base(self, store): """ self.course = CourseFactory.create( modulestore=store, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), display_name="Search Index Test Course" ) @@ -158,7 +158,7 @@ def setup_course_base(self, store): display_name="Week 1", modulestore=store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.sequential = BlockFactory.create( parent_location=self.chapter.location, @@ -166,7 +166,7 @@ def setup_course_base(self, store): display_name="Lesson 1", modulestore=store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.vertical = BlockFactory.create( parent_location=self.sequential.location, @@ -174,7 +174,7 @@ def setup_course_base(self, store): display_name='Subsection 1', modulestore=store, publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) # unspecified start - should inherit from container self.html_unit = BlockFactory.create( @@ -193,7 +193,7 @@ def reindex_course(self, store): def index_recent_changes(self, store, since_time): """ index course using recent changes """ - trigger_time = datetime.now(UTC) + trigger_time = datetime.now(ZoneInfo("UTC")) return CoursewareSearchIndexer.index( store, self.course.id, @@ -322,7 +322,7 @@ def _test_time_based_index(self, store): display_name='Section 2', modulestore=store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) # add a new vertical @@ -341,7 +341,7 @@ def _test_time_based_index(self, store): modulestore=store, ) - before_time = datetime.now(UTC) + before_time = datetime.now(ZoneInfo("UTC")) self.publish_item(store, vertical2.location) # index based on time, will include an index of the origin sequential # because it is in a common subtree but not of the original vertical @@ -432,7 +432,7 @@ def _test_course_location_null(self, store): display_name=None, modulestore=store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) # add a new vertical vertical2 = BlockFactory.create( @@ -505,7 +505,7 @@ def test_delete_course_from_search_index_after_course_deletion(self): self._test_delete_course_from_search_index_after_course_deletion(self.store) def test_empty_course(self): - empty_course = CourseFactory.create(modulestore=self.store, start=datetime(2015, 3, 1, tzinfo=UTC)) + empty_course = CourseFactory.create(modulestore=self.store, start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC"))) added_to_index = CoursewareSearchIndexer.do_course_reindex(self.store, empty_course.id) assert added_to_index == 0 @@ -605,28 +605,28 @@ def setUpClass(cls): super().setUpClass() SignalHandler.course_published.disconnect(listen_for_course_publish) SignalHandler.library_updated.disconnect(listen_for_library_update) - cls.course = CourseFactory.create(start=datetime(2015, 3, 1, tzinfo=UTC)) + cls.course = CourseFactory.create(start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC"))) cls.chapter = BlockFactory.create( parent_location=cls.course.location, category='chapter', display_name="Week 1", publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) cls.sequential = BlockFactory.create( parent_location=cls.chapter.location, category='sequential', display_name="Lesson 1", publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) cls.vertical = BlockFactory.create( parent_location=cls.sequential.location, category='vertical', display_name='Subsection 1', publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) # unspecified start - should inherit from container cls.html_unit = BlockFactory.create( @@ -881,7 +881,7 @@ def _setup_course_with_content(self): display_name="Week 1", modulestore=self.store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.sequential = BlockFactory.create( @@ -890,7 +890,7 @@ def _setup_course_with_content(self): display_name="Lesson 1", modulestore=self.store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.sequential2 = BlockFactory.create( @@ -899,7 +899,7 @@ def _setup_course_with_content(self): display_name="Lesson 2", modulestore=self.store, publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.vertical = BlockFactory.create( @@ -908,7 +908,7 @@ def _setup_course_with_content(self): display_name='Subsection 1', modulestore=self.store, publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) self.vertical2 = BlockFactory.create( @@ -917,7 +917,7 @@ def _setup_course_with_content(self): display_name='Subsection 2', modulestore=self.store, publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) self.vertical3 = BlockFactory.create( @@ -926,7 +926,7 @@ def _setup_course_with_content(self): display_name='Subsection 3', modulestore=self.store, publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) # unspecified start - should inherit from container @@ -1091,7 +1091,7 @@ def _html_group_result(self, html_unit, content_groups): 'content_type': 'Text', 'org': self.course.org, 'content_groups': content_groups, - 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=UTC) + 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=ZoneInfo("UTC")) } def _html_experiment_group_result(self, html_unit, content_groups): @@ -1111,7 +1111,7 @@ def _html_experiment_group_result(self, html_unit, content_groups): 'content_type': 'Text', 'org': self.course.org, 'content_groups': content_groups, - 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=UTC) + 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=ZoneInfo("UTC")) } def _vertical_experiment_group_result(self, vertical, content_groups): @@ -1119,7 +1119,7 @@ def _vertical_experiment_group_result(self, vertical, content_groups): Return object with arguments and content group for split_test vertical. """ return { - 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=UTC), + 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=ZoneInfo("UTC")), 'content': {'display_name': vertical.display_name}, 'course': str(self.course.id), 'location': [ @@ -1151,7 +1151,7 @@ def _html_nogroup_result(self, html_unit): 'content_type': 'Text', 'org': self.course.org, 'content_groups': None, - 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=UTC) + 'start_date': datetime(2015, 4, 1, 0, 0, tzinfo=ZoneInfo("UTC")) } def _get_index_values_from_call_args(self, mock_index): diff --git a/cms/djangoapps/contentstore/tests/test_exams.py b/cms/djangoapps/contentstore/tests/test_exams.py index 798b5e51fd80..7b9fdc321bac 100644 --- a/cms/djangoapps/contentstore/tests/test_exams.py +++ b/cms/djangoapps/contentstore/tests/test_exams.py @@ -9,12 +9,12 @@ from django.conf import settings from edx_toggles.toggles.testutils import override_waffle_flag from freezegun import freeze_time -from pytz import utc from cms.djangoapps.contentstore.signals.handlers import listen_for_course_publish from openedx.core.djangoapps.course_apps.toggles import EXAMS_IDA from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory +from zoneinfo import ZoneInfo @ddt.ddt @@ -54,7 +54,7 @@ def setUp(self): display_name='Homework 1', graded=True, is_time_limited=False, - due=datetime.now(utc) + timedelta(minutes=60), + due=datetime.now(ZoneInfo("UTC")) + timedelta(minutes=60), ) def _get_exams_url(self, course_id): @@ -93,7 +93,7 @@ def test_publishing_exam(self, is_proctored_exam, is_practice_exam, When a course is published it will register all exams sections with the exams service """ default_time_limit_minutes = 10 - due_date = datetime.now(utc) + timedelta(minutes=default_time_limit_minutes + 1) + due_date = datetime.now(ZoneInfo("UTC")) + timedelta(minutes=default_time_limit_minutes + 1) sequence = BlockFactory.create( parent=self.chapter, category='sequential', @@ -244,7 +244,7 @@ def test_subsection_due_date_prioritized(self, is_self_paced, proctoring_provide self.course.proctoring_provider = proctoring_provider self.course = self.update_course(self.course, 1) - sequential_due_date = datetime.now(utc) + timedelta(minutes=60) + sequential_due_date = datetime.now(ZoneInfo("UTC")) + timedelta(minutes=60) sequence = BlockFactory.create( parent=self.chapter, category='sequential', diff --git a/cms/djangoapps/contentstore/tests/test_filters.py b/cms/djangoapps/contentstore/tests/test_filters.py index 4011ae728b34..7426d1ac347e 100644 --- a/cms/djangoapps/contentstore/tests/test_filters.py +++ b/cms/djangoapps/contentstore/tests/test_filters.py @@ -4,7 +4,6 @@ from datetime import datetime from urllib.parse import urljoin -from pytz import UTC from django.test import override_settings from cms.djangoapps.contentstore import asset_storage_handlers @@ -12,6 +11,7 @@ from openedx_filters import PipelineStep from xmodule.contentstore.content import StaticContent from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from zoneinfo import ZoneInfo class TestPageURLRequestedPipelineStep(PipelineStep): @@ -38,7 +38,7 @@ class LMSPageURLRequestedFiltersTest(ModuleStoreTestCase): def setUp(self): # pylint: disable=arguments-differ super().setUp() - self.upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) + self.upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=ZoneInfo("UTC")) self.content_type = 'image/jpg' self.course_key = CourseLocator('org', 'class', 'run') self.location = self.course_key.make_asset_key('asset', 'my_file_name.jpg') diff --git a/cms/djangoapps/contentstore/tests/test_proctoring.py b/cms/djangoapps/contentstore/tests/test_proctoring.py index d9b83a811ef0..0f5d032c5891 100644 --- a/cms/djangoapps/contentstore/tests/test_proctoring.py +++ b/cms/djangoapps/contentstore/tests/test_proctoring.py @@ -9,13 +9,13 @@ import ddt from django.conf import settings from edx_proctoring.api import get_all_exams_for_course, get_review_policy_by_exam_id -from pytz import UTC from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory from cms.djangoapps.contentstore.signals.handlers import listen_for_course_publish from common.djangoapps.student.tests.factories import UserFactory +from zoneinfo import ZoneInfo @ddt.ddt @@ -97,7 +97,7 @@ def test_onboarding_exam_is_practice_exam(self, is_practice_exam, is_onboarding_ default_time_limit_minutes=default_time_limit_minutes, is_proctored_enabled=is_proctored_exam, is_practice_exam=is_practice_exam, - due=datetime.now(UTC) + timedelta(minutes=default_time_limit_minutes + 1), + due=datetime.now(ZoneInfo("UTC")) + timedelta(minutes=default_time_limit_minutes + 1), exam_review_rules="allow_use_of_paper", hide_after_due=True, is_onboarding_exam=is_onboarding_exam, @@ -132,7 +132,7 @@ def test_publishing_exam(self, is_proctored_exam, default_time_limit_minutes=default_time_limit_minutes, is_proctored_enabled=is_proctored_exam, is_practice_exam=is_practice_exam, - due=datetime.now(UTC) + timedelta(minutes=default_time_limit_minutes + 1), + due=datetime.now(ZoneInfo("UTC")) + timedelta(minutes=default_time_limit_minutes + 1), exam_review_rules="allow_use_of_paper", hide_after_due=hide_after_due, is_onboarding_exam=False, @@ -300,7 +300,7 @@ def test_self_paced_no_due_dates(self): default_time_limit_minutes=60, is_proctored_enabled=False, is_practice_exam=False, - due=datetime.now(UTC) + timedelta(minutes=60), + due=datetime.now(ZoneInfo("UTC")) + timedelta(minutes=60), exam_review_rules="allow_use_of_paper", hide_after_due=True, is_onboarding_exam=False, diff --git a/cms/djangoapps/contentstore/tests/test_utils.py b/cms/djangoapps/contentstore/tests/test_utils.py index a46d9831d4e5..52ab712c86ab 100644 --- a/cms/djangoapps/contentstore/tests/test_utils.py +++ b/cms/djangoapps/contentstore/tests/test_utils.py @@ -14,7 +14,6 @@ from opaque_keys.edx.locator import CourseLocator, LibraryLocator from openedx_events.tests.utils import OpenEdxEventsTestMixin from path import Path as path -from pytz import UTC from rest_framework import status from user_tasks.models import UserTaskArtifact, UserTaskStatus @@ -39,6 +38,7 @@ CourseFactory, ) from xmodule.partitions.partitions import Group, UserPartition +from zoneinfo import ZoneInfo class LMSLinksTestCase(TestCase): @@ -120,8 +120,8 @@ def setUpClass(cls): super().setUpClass() cls.dummy_user = ModuleStoreEnum.UserID.test - cls.past = datetime(1970, 1, 1, tzinfo=UTC) - cls.future = datetime.now(UTC) + timedelta(days=1) + cls.past = datetime(1970, 1, 1, tzinfo=ZoneInfo("UTC")) + cls.future = datetime.now(ZoneInfo("UTC")) + timedelta(days=1) cls.course = CourseFactory.create() def test_private_unreleased_xblock(self): @@ -201,8 +201,8 @@ def setUp(self): self.sequential = self.store.get_item(self.sequential.location) self.vertical = self.store.get_item(self.vertical.location) - self.date_one = datetime(1980, 1, 1, tzinfo=UTC) - self.date_two = datetime(2020, 1, 1, tzinfo=UTC) + self.date_one = datetime(1980, 1, 1, tzinfo=ZoneInfo("UTC")) + self.date_two = datetime(2020, 1, 1, tzinfo=ZoneInfo("UTC")) def _update_release_dates(self, chapter_start, sequential_start, vertical_start): """Sets the release dates of the chapter, sequential, and vertical""" diff --git a/cms/djangoapps/contentstore/tests/test_video_utils.py b/cms/djangoapps/contentstore/tests/test_video_utils.py index e65e4f6638c6..c42550e9dd5a 100644 --- a/cms/djangoapps/contentstore/tests/test_video_utils.py +++ b/cms/djangoapps/contentstore/tests/test_video_utils.py @@ -8,7 +8,6 @@ from unittest import mock import ddt -import pytz import requests from django.conf import settings from django.core.files.base import ContentFile @@ -26,6 +25,7 @@ validate_video_image ) from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file +from zoneinfo import ZoneInfo class ValidateVideoImageTestCase(TestCase): @@ -63,7 +63,7 @@ def setUp(self): super().setUp() course_ids = [str(self.course.id)] profiles = ['youtube'] - created = datetime.now(pytz.utc) + created = datetime.now(ZoneInfo("UTC")) previous_uploads = [ { 'edx_video_id': 'test1', diff --git a/cms/djangoapps/contentstore/tests/tests.py b/cms/djangoapps/contentstore/tests/tests.py index d151b1d58526..f8e1ff15e9e2 100644 --- a/cms/djangoapps/contentstore/tests/tests.py +++ b/cms/djangoapps/contentstore/tests/tests.py @@ -19,7 +19,6 @@ 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 cms.djangoapps.contentstore import toggles from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase @@ -27,6 +26,7 @@ from cms.djangoapps.contentstore.utils import get_studio_home_url from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo class ContentStoreTestCase(ModuleStoreTestCase): @@ -218,7 +218,7 @@ def set_blackout_dates(self, blackout_dates): ] def test_blackouts(self): - now = datetime.datetime.now(UTC) + now = datetime.datetime.now(ZoneInfo("UTC")) times1 = [ (now - datetime.timedelta(days=14), now - datetime.timedelta(days=11)), (now + datetime.timedelta(days=24), now + datetime.timedelta(days=30)) diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index a80fffec7551..a6bc2d09400c 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -31,7 +31,6 @@ from openedx_events.content_authoring.signals import XBLOCK_DUPLICATED from openedx_events.learning.data import CourseNotificationData from openedx_events.learning.signals import COURSE_NOTIFICATION_REQUESTED -from pytz import UTC from rest_framework.fields import BooleanField from xblock.fields import Scope @@ -113,6 +112,7 @@ ) from xmodule.services import ConfigurationService, SettingsService, TeamsConfigurationService from xmodule.util.keys import BlockKey +from zoneinfo import ZoneInfo from .models import ComponentLink, ContainerLink @@ -599,7 +599,7 @@ def is_currently_visible_to_students(xblock): # Check start date if 'detached' not in published._class_tags and published.start is not None: # lint-amnesty, pylint: disable=protected-access - return datetime.now(UTC) > published.start + return datetime.now(ZoneInfo("UTC")) > published.start # No start date, so it's always visible return True diff --git a/cms/djangoapps/contentstore/video_storage_handlers.py b/cms/djangoapps/contentstore/video_storage_handlers.py index 87086c9951ac..c87931cec178 100644 --- a/cms/djangoapps/contentstore/video_storage_handlers.py +++ b/cms/djangoapps/contentstore/video_storage_handlers.py @@ -45,7 +45,6 @@ from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from path import Path as path -from pytz import UTC from rest_framework import status as rest_status from rest_framework.response import Response from tempfile import NamedTemporaryFile, mkdtemp @@ -61,6 +60,7 @@ ) from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo from .models import VideoUploadConfig from .toggles import use_new_video_uploads_page, use_mock_video_uploads @@ -597,7 +597,7 @@ def convert_video_status(video, is_video_encodes_ready=False): * `YouTube Duplicate` if status is `invalid_token` * user-friendly video status """ - now = datetime.now(video.get('created', datetime.now().replace(tzinfo=UTC)).tzinfo) + now = datetime.now(video.get('created', datetime.now().replace(tzinfo=ZoneInfo("UTC"))).tzinfo) if video['status'] == 'upload' and (now - video['created']) > timedelta(hours=MAX_UPLOAD_HOURS): new_status = 'upload_failed' diff --git a/cms/djangoapps/contentstore/views/tests/test_assets.py b/cms/djangoapps/contentstore/views/tests/test_assets.py index e7dbcfe9f55a..5f0bf9016dc8 100644 --- a/cms/djangoapps/contentstore/views/tests/test_assets.py +++ b/cms/djangoapps/contentstore/views/tests/test_assets.py @@ -15,7 +15,6 @@ from opaque_keys.edx.keys import AssetKey from opaque_keys.edx.locator import CourseLocator from PIL import Image -from pytz import UTC from cms.djangoapps.contentstore.tests.utils import CourseTestCase from cms.djangoapps.contentstore.utils import reverse_course_url @@ -29,6 +28,7 @@ from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.xml_importer import import_course_from_xml # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE +from zoneinfo import ZoneInfo TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT @@ -227,7 +227,7 @@ def test_mocked_filtered_response(self, mock_get_all_content_for_course): """ asset_key = self.course.id.make_asset_key( AssetMetadata.GENERAL_ASSET_TYPE, 'test.jpg') - upload_date = datetime(2015, 1, 12, 10, 30, tzinfo=UTC) + upload_date = datetime(2015, 1, 12, 10, 30, tzinfo=ZoneInfo("UTC")) thumbnail_location = [ 'c4x', 'edX', 'toy', 'thumbnail', 'test_thumb.jpg', None] @@ -426,7 +426,7 @@ class AssetToJsonTestCase(AssetsTestCase): """ @override_settings(LMS_ROOT_URL="https://lms_root_url") def test_basic(self): - upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) + upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=ZoneInfo("UTC")) content_type = 'image/jpg' course_key = CourseLocator('org', 'class', 'run') location = course_key.make_asset_key('asset', 'my_file_name.jpg') @@ -472,7 +472,7 @@ def post_asset_update(lock, course): """ Helper method for posting asset update. """ content_type = 'application/txt' course_key = CourseLocator('org', 'class', 'run') - upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC) + upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=ZoneInfo("UTC")) asset_location = course.id.make_asset_key('asset', 'sample_static.html') url = reverse_course_url( 'assets_handler', course.id, kwargs={'asset_key_string': str(asset_location)} diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py index 2b21a9b9b970..eb331057e3d4 100644 --- a/cms/djangoapps/contentstore/views/tests/test_block.py +++ b/cms/djangoapps/contentstore/views/tests/test_block.py @@ -23,7 +23,6 @@ from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from pyquery import PyQuery -from pytz import UTC from bs4 import BeautifulSoup from web_fragments.fragment import Fragment from webob import Response @@ -87,6 +86,7 @@ create_xblock_info, ) from common.test.utils import assert_dict_contains_subset +from zoneinfo import ZoneInfo class AsideTest(XBlockAside): @@ -637,11 +637,11 @@ def test_create_block_negative(self): self.assertEqual(resp.status_code, 200) def test_create_with_future_date(self): - self.assertEqual(self.course.start, datetime(2030, 1, 1, tzinfo=UTC)) + self.assertEqual(self.course.start, datetime(2030, 1, 1, tzinfo=ZoneInfo("UTC"))) resp = self.create_xblock(category="chapter") usage_key = self.response_usage_key(resp) obj = self.get_item_from_modulestore(usage_key) - self.assertEqual(obj.start, datetime(2030, 1, 1, tzinfo=UTC)) + self.assertEqual(obj.start, datetime(2030, 1, 1, tzinfo=ZoneInfo("UTC"))) def test_static_tabs_initialization(self): """ @@ -1987,13 +1987,13 @@ def test_date_fields(self): self.seq_update_url, data={"metadata": {"due": "2010-11-22T04:00Z"}} ) sequential = self.get_item_from_modulestore(self.seq_usage_key) - self.assertEqual(sequential.due, datetime(2010, 11, 22, 4, 0, tzinfo=UTC)) + self.assertEqual(sequential.due, datetime(2010, 11, 22, 4, 0, tzinfo=ZoneInfo("UTC"))) self.client.ajax_post( self.seq_update_url, data={"metadata": {"start": "2010-09-12T14:00Z"}} ) sequential = self.get_item_from_modulestore(self.seq_usage_key) - self.assertEqual(sequential.due, datetime(2010, 11, 22, 4, 0, tzinfo=UTC)) - self.assertEqual(sequential.start, datetime(2010, 9, 12, 14, 0, tzinfo=UTC)) + self.assertEqual(sequential.due, datetime(2010, 11, 22, 4, 0, tzinfo=ZoneInfo("UTC"))) + self.assertEqual(sequential.start, datetime(2010, 9, 12, 14, 0, tzinfo=ZoneInfo("UTC"))) @ddt.data( "1000-01-01T00:00Z", @@ -2308,7 +2308,7 @@ def _make_draft_content_different_from_published(self): self.problem_update_url, data={"metadata": {"due": "2077-10-10T04:00Z"}} ) updated_draft = self.get_item_from_modulestore(self.problem_usage_key) - self.assertEqual(updated_draft.due, datetime(2077, 10, 10, 4, 0, tzinfo=UTC)) + self.assertEqual(updated_draft.due, datetime(2077, 10, 10, 4, 0, tzinfo=ZoneInfo("UTC"))) self.assertIsNone(published.due) # Fetch the published version again to make sure the due date is still unset. published = modulestore().get_item( @@ -2323,7 +2323,7 @@ def test_make_public_with_update(self): data={"metadata": {"due": "2077-10-10T04:00Z"}, "publish": "make_public"}, ) published = self.get_item_from_modulestore(self.problem_usage_key) - self.assertEqual(published.due, datetime(2077, 10, 10, 4, 0, tzinfo=UTC)) + self.assertEqual(published.due, datetime(2077, 10, 10, 4, 0, tzinfo=ZoneInfo("UTC"))) def test_published_and_draft_contents_with_update(self): """Create a draft and publish it then modify the draft and check that published content is not modified""" @@ -3442,7 +3442,7 @@ def test_validate_start_date(self): parent_location=course.location, category='chapter', display_name='Week 1' ) - chapter.start = datetime(year=1899, month=1, day=1, tzinfo=UTC) + chapter.start = datetime(year=1899, month=1, day=1, tzinfo=ZoneInfo("UTC")) xblock_info = create_xblock_info( chapter, @@ -4122,7 +4122,7 @@ def test_published_unit(self): sequential = self._create_child(chapter, "sequential", "Test Sequential") self._create_child(sequential, "vertical", "Published Unit", publish_item=True) self._create_child(sequential, "vertical", "Staff Only Unit", staff_only=True) - self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1)) + self._set_release_date(chapter.location, datetime.now(ZoneInfo("UTC")) + timedelta(days=1)) xblock_info = self._get_xblock_info(chapter.location) self._verify_visibility_state(xblock_info, VisibilityState.ready) self._verify_visibility_state( @@ -4143,7 +4143,7 @@ def test_released_unit(self): sequential = self._create_child(chapter, "sequential", "Test Sequential") self._create_child(sequential, "vertical", "Published Unit", publish_item=True) self._create_child(sequential, "vertical", "Staff Only Unit", staff_only=True) - self._set_release_date(chapter.location, datetime.now(UTC) - timedelta(days=1)) + self._set_release_date(chapter.location, datetime.now(ZoneInfo("UTC")) - timedelta(days=1)) xblock_info = self._get_xblock_info(chapter.location) self._verify_visibility_state(xblock_info, VisibilityState.live) self._verify_visibility_state( @@ -4187,11 +4187,11 @@ def test_partially_released_section(self): released_sequential = self._create_child(chapter, 'sequential', "Released Sequential") self._create_child(released_sequential, 'vertical', "Released Unit", publish_item=True) self._create_child(released_sequential, 'vertical', "Staff Only Unit 1", staff_only=True) - self._set_release_date(chapter.location, datetime.now(UTC) - timedelta(days=1)) + self._set_release_date(chapter.location, datetime.now(ZoneInfo("UTC")) - timedelta(days=1)) published_sequential = self._create_child(chapter, 'sequential', "Published Sequential") self._create_child(published_sequential, 'vertical', "Published Unit", publish_item=True) self._create_child(published_sequential, 'vertical', "Staff Only Unit 2", staff_only=True) - self._set_release_date(published_sequential.location, datetime.now(UTC) + timedelta(days=1)) + self._set_release_date(published_sequential.location, datetime.now(ZoneInfo("UTC")) + timedelta(days=1)) xblock_info = self._get_xblock_info(chapter.location) # Verify the state of the released sequential @@ -4352,7 +4352,7 @@ def test_unscheduled_section_with_live_subsection(self): self._create_child(sequential, "vertical", "Published Unit", publish_item=True) self._create_child(sequential, "vertical", "Staff Only Unit", staff_only=True) self._set_release_date( - sequential.location, datetime.now(UTC) - timedelta(days=1) + sequential.location, datetime.now(ZoneInfo("UTC")) - timedelta(days=1) ) xblock_info = self._get_xblock_info(chapter.location) self._verify_visibility_state(xblock_info, VisibilityState.needs_attention) @@ -4371,9 +4371,9 @@ def test_unreleased_section_with_live_subsection(self): sequential = self._create_child(chapter, "sequential", "Test Sequential") self._create_child(sequential, "vertical", "Published Unit", publish_item=True) self._create_child(sequential, "vertical", "Staff Only Unit", staff_only=True) - self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1)) + self._set_release_date(chapter.location, datetime.now(ZoneInfo("UTC")) + timedelta(days=1)) self._set_release_date( - sequential.location, datetime.now(UTC) - timedelta(days=1) + sequential.location, datetime.now(ZoneInfo("UTC")) - timedelta(days=1) ) xblock_info = self._get_xblock_info(chapter.location) self._verify_visibility_state(xblock_info, VisibilityState.needs_attention) @@ -4431,7 +4431,7 @@ def test_self_paced_item_visibility_state(self): # Create course, chapter and setup future release date to make chapter in scheduled state course = CourseFactory.create() chapter = self._create_child(course, "chapter", "Test Chapter") - self._set_release_date(chapter.location, datetime.now(UTC) + timedelta(days=1)) + self._set_release_date(chapter.location, datetime.now(ZoneInfo("UTC")) + timedelta(days=1)) # Check that chapter has scheduled state xblock_info = self._get_xblock_info(chapter.location) @@ -4474,7 +4474,7 @@ def create_source_block(self, course): parent=course, category="course_info", display_name="Source Block", - metadata={"due": datetime(2010, 11, 22, 4, 0, tzinfo=UTC)}, + metadata={"due": datetime(2010, 11, 22, 4, 0, tzinfo=ZoneInfo("UTC"))}, ) def_id = self.runtime.id_generator.create_definition("html") @@ -4494,7 +4494,7 @@ def create_source_block(self, course): # quick sanity checks source_block = self.store.get_item(source_block.location) - self.assertEqual(source_block.due, datetime(2010, 11, 22, 4, 0, tzinfo=UTC)) + self.assertEqual(source_block.due, datetime(2010, 11, 22, 4, 0, tzinfo=ZoneInfo("UTC"))) self.assertEqual(source_block.display_name, "Source Block") self.assertEqual( source_block.runtime.get_asides(source_block)[0].field11, "html_new_value1" @@ -4553,7 +4553,7 @@ def test_update_clobbers(self): parent=course, category="course_info", display_name="Destination Chapter", - metadata={"due": datetime(2025, 10, 21, 6, 5, tzinfo=UTC)}, + metadata={"due": datetime(2025, 10, 21, 6, 5, tzinfo=ZoneInfo("UTC"))}, ) def_id = self.runtime.id_generator.create_definition("html") diff --git a/cms/djangoapps/contentstore/views/tests/test_container_page.py b/cms/djangoapps/contentstore/views/tests/test_container_page.py index e6b58257b69d..ac0b87f247c5 100644 --- a/cms/djangoapps/contentstore/views/tests/test_container_page.py +++ b/cms/djangoapps/contentstore/views/tests/test_container_page.py @@ -11,7 +11,6 @@ from django.test.client import RequestFactory from django.urls import reverse from edx_toggles.toggles.testutils import override_waffle_flag -from pytz import UTC from urllib.parse import quote import cms.djangoapps.contentstore.views.component as views @@ -20,6 +19,7 @@ from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo from .utils import StudioPageTestCase @@ -41,8 +41,8 @@ def setUp(self): self.video = self._create_block(self.child_vertical, "video", "My Video") self.store = modulestore() - past = datetime.datetime(1970, 1, 1, tzinfo=UTC) - future = datetime.datetime.now(UTC) + datetime.timedelta(days=1) + past = datetime.datetime(1970, 1, 1, tzinfo=ZoneInfo("UTC")) + future = datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=1) self.released_private_vertical = self._create_block( parent=self.sequential, category='vertical', display_name='Released Private Unit', start=past) diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index 58c425c601a3..8ce081643cf2 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -8,7 +8,6 @@ from unittest import mock, skip import ddt -import pytz from django.core.exceptions import PermissionDenied from django.utils.translation import gettext as _ from search.api import perform_search @@ -28,6 +27,7 @@ from ..course import _deprecated_blocks_info, course_outline_initial_state, reindex_course_and_check_access from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import VisibilityState, create_xblock_info +from zoneinfo import ZoneInfo @ddt.ddt @@ -250,7 +250,7 @@ def setUp(self): super().setUp() - self.course.start = datetime.datetime(2014, 1, 1, tzinfo=pytz.utc) + self.course.start = datetime.datetime(2014, 1, 1, tzinfo=ZoneInfo("UTC")) modulestore().update_item(self.course, self.user.id) self.chapter = BlockFactory.create( diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py index 2d17229f59c1..8642a2d7a8c7 100644 --- a/cms/djangoapps/contentstore/views/tests/test_videos.py +++ b/cms/djangoapps/contentstore/views/tests/test_videos.py @@ -14,7 +14,6 @@ import dateutil.parser from common.djangoapps.student.tests.factories import UserFactory import ddt -import pytz from django.test import TestCase from django.conf import settings from django.test.utils import override_settings @@ -56,6 +55,7 @@ storage_service_key, PUBLIC_VIDEO_SHARE ) +from zoneinfo import ZoneInfo class VideoUploadTestBase: @@ -86,7 +86,7 @@ def setUp(self): # course ids for videos course_ids = [str(self.course.id), str(self.course2.id)] - created = datetime.now(pytz.utc) + created = datetime.now(ZoneInfo("UTC")) self.profiles = ["profile1", "profile2"] self.previous_uploads = [ @@ -742,12 +742,12 @@ def test_convert_video_status(self): video = self.previous_uploads[0] # video status should be failed if it's in upload state for more than 24 hours - video['created'] = datetime(2016, 1, 1, 10, 10, 10, 0, pytz.UTC) + video['created'] = datetime(2016, 1, 1, 10, 10, 10, 0, ZoneInfo("UTC")) status = convert_video_status(video) self.assertEqual(status, StatusDisplayStrings.get('upload_failed')) # `invalid_token` should be converted to `youtube_duplicate` - video['created'] = datetime.now(pytz.UTC) + video['created'] = datetime.now(ZoneInfo("UTC")) video['status'] = 'invalid_token' status = convert_video_status(video) self.assertEqual(status, StatusDisplayStrings.get('youtube_duplicate')) diff --git a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py index 78c393532305..4b617e403e0f 100644 --- a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py +++ b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py @@ -30,7 +30,6 @@ from edx_proctoring.exceptions import ProctoredExamNotFoundException from help_tokens.core import HelpUrlExpert from opaque_keys.edx.locator import LibraryUsageLocator, LibraryUsageLocatorV2 -from pytz import UTC from xblock.core import XBlock from xblock.fields import Scope from .xblock_helpers import get_block_key_string @@ -64,6 +63,7 @@ from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError from xmodule.modulestore.inheritance import own_metadata from xmodule.tabs import CourseTabList +from zoneinfo import ZoneInfo from ..utils import ( ancestor_has_staff_lock, @@ -1207,7 +1207,7 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements "studio_url": xblock_studio_url(xblock, parent_xblock), "lms_url": xblock_lms_url(xblock), "embed_lms_url": xblock_embed_lms_url(xblock), - "released_to_students": datetime.now(UTC) > xblock.start, + "released_to_students": datetime.now(ZoneInfo("UTC")) > xblock.start, "release_date": release_date, "visibility_state": visibility_state, "has_explicit_staff_lock": xblock.fields[ @@ -1552,7 +1552,7 @@ def _compute_visibility_state( return VisibilityState.needs_attention is_unscheduled = xblock.start == DEFAULT_START_DATE - is_live = is_course_self_paced or datetime.now(UTC) > xblock.start + is_live = is_course_self_paced or datetime.now(ZoneInfo("UTC")) > xblock.start if child_info and child_info.get("children", []): # pylint: disable=too-many-nested-blocks all_staff_only = True all_unscheduled = True diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index 1cd87ed5a0a1..69f4dab436b4 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -6,7 +6,6 @@ import logging from datetime import datetime -import pytz from django.conf import settings from django.core.exceptions import ValidationError from django.utils.translation import gettext as _ @@ -24,6 +23,7 @@ from xmodule.modulestore.exceptions import InvalidProctoringProvider # lint-amnesty, pylint: disable=wrong-import-order from xmodule.partitions.partitions import MINIMUM_UNUSED_PARTITION_ID from xmodule.partitions.partitions_service import get_all_partitions_for_course +from zoneinfo import ZoneInfo LOGGER = logging.getLogger(__name__) @@ -470,7 +470,7 @@ def validate_proctoring_settings(cls, block, settings_dict, user): cls._has_requested_proctoring_provider_changed( block.proctoring_provider, proctoring_provider_model.get('value') ) and - datetime.now(pytz.UTC) > block.start + datetime.now(ZoneInfo("UTC")) > block.start ): message = ( 'The proctoring provider cannot be modified after a course has started.' diff --git a/cms/lib/xblock/tagging/test.py b/cms/lib/xblock/tagging/test.py index 173523452d54..6139001c0f5c 100644 --- a/cms/lib/xblock/tagging/test.py +++ b/cms/lib/xblock/tagging/test.py @@ -11,7 +11,6 @@ from django.test.client import RequestFactory from lxml import etree from opaque_keys.edx.asides import AsideUsageKeyV1, AsideUsageKeyV2 -from pytz import UTC from xblock.fields import ScopeIds from xblock.runtime import DictKeyValueStore, KvsFieldData from xblock.test.tools import TestRuntime @@ -27,6 +26,7 @@ from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory # lint-amnesty, pylint: disable=wrong-import-order +from zoneinfo import ZoneInfo @ddt.ddt @@ -56,21 +56,21 @@ def setUp(self): category='chapter', display_name="Week 1", publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.sequential = BlockFactory.create( parent_location=self.chapter.location, category='sequential', display_name="Lesson 1", publish_item=True, - start=datetime(2015, 3, 1, tzinfo=UTC), + start=datetime(2015, 3, 1, tzinfo=ZoneInfo("UTC")), ) self.vertical = BlockFactory.create( parent_location=self.sequential.location, category='vertical', display_name='Subsection 1', publish_item=True, - start=datetime(2015, 4, 1, tzinfo=UTC), + start=datetime(2015, 4, 1, tzinfo=ZoneInfo("UTC")), ) self.problem = BlockFactory.create( category="problem", diff --git a/cms/lib/xblock/test/test_upstream_sync.py b/cms/lib/xblock/test/test_upstream_sync.py index 94ee69e5f1f3..c67b1990e0f3 100644 --- a/cms/lib/xblock/test/test_upstream_sync.py +++ b/cms/lib/xblock/test/test_upstream_sync.py @@ -6,7 +6,6 @@ import ddt from organizations.api import ensure_organization from organizations.models import Organization -from pytz import utc from cms.lib.xblock.upstream_sync import ( BadDownstream, @@ -24,6 +23,7 @@ from openedx.core.djangoapps.xblock import api as xblock from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory +from zoneinfo import ZoneInfo @ddt.ddt @@ -292,7 +292,7 @@ def test_sync_updates_to_downstream_only_fields(self): # Modifing downstream-only fields are "safe" customizations downstream.display_name = "Downstream Title Override" downstream.attempts_before_showanswer_button = 2 - downstream.due = datetime.datetime(2025, 2, 2, tzinfo=utc) + downstream.due = datetime.datetime(2025, 2, 2, tzinfo=ZoneInfo("UTC")) downstream.force_save_button = True downstream.graceperiod = '2d' downstream.grading_method = 'last_score' @@ -320,7 +320,7 @@ def test_sync_updates_to_downstream_only_fields(self): # but "safe" customizations survive assert downstream.display_name == "Downstream Title Override" assert downstream.attempts_before_showanswer_button == 2 - assert downstream.due == datetime.datetime(2025, 2, 2, tzinfo=utc) + assert downstream.due == datetime.datetime(2025, 2, 2, tzinfo=ZoneInfo("UTC")) assert downstream.force_save_button assert downstream.graceperiod == '2d' assert downstream.grading_method == 'last_score'