chore: use zoneinfo instead of pytz

This commit is contained in:
Tarun Tak
2025-10-28 06:15:57 +00:00
committed by Feanil Patel
parent 7f1f8767a8
commit f841763640
27 changed files with 126 additions and 122 deletions

View File

@@ -5,9 +5,9 @@ Classes representing asset metadata.
import json import json
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo
import dateutil.parser import dateutil.parser
import pytz
from lxml import etree from lxml import etree
from opaque_keys.edx.keys import AssetKey, CourseKey from opaque_keys.edx.keys import AssetKey, CourseKey
@@ -80,7 +80,7 @@ class AssetMetadata:
self.thumbnail = thumbnail self.thumbnail = thumbnail
self.curr_version = curr_version self.curr_version = curr_version
self.prev_version = prev_version self.prev_version = prev_version
now = datetime.now(pytz.utc) now = datetime.now(ZoneInfo("UTC"))
self.edited_by = edited_by self.edited_by = edited_by
self.edited_by_email = edited_by_email self.edited_by_email = edited_by_email
self.edited_on = edited_on or now self.edited_on = edited_on or now

View File

@@ -23,10 +23,10 @@ from copy import deepcopy
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from xml.sax.saxutils import unescape from xml.sax.saxutils import unescape
from zoneinfo import ZoneInfo
from django.conf import settings from django.conf import settings
from lxml import etree from lxml import etree
from pytz import UTC
from openedx.core.djangolib.markup import HTML, Text from openedx.core.djangolib.markup import HTML, Text
from openedx.core.lib.safe_lxml.xmlparser import XML from openedx.core.lib.safe_lxml.xmlparser import XML
@@ -437,7 +437,10 @@ class LoncapaProblem: # pylint: disable=too-many-public-methods,too-many-instan
if self.correct_map.is_queued(answer_id) if self.correct_map.is_queued(answer_id)
] ]
queuetimes = [ queuetimes = [
datetime.strptime(qt_str, xqueue_interface.DATEFORMAT).replace(tzinfo=UTC) for qt_str in queuetime_strs datetime.strptime(qt_str, xqueue_interface.DATEFORMAT).replace(
tzinfo=ZoneInfo("UTC")
)
for qt_str in queuetime_strs
] ]
return max(queuetimes) return max(queuetimes)

View File

@@ -20,6 +20,7 @@ from cmath import isnan
from collections import namedtuple from collections import namedtuple
from datetime import datetime from datetime import datetime
from sys import float_info from sys import float_info
from zoneinfo import ZoneInfo
import html5lib import html5lib
import numpy import numpy
@@ -33,7 +34,6 @@ from django.utils import html
from lxml import etree from lxml import etree
from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME? from lxml.html.soupparser import fromstring as fromstring_bs # uses Beautiful Soup!!! FIXME?
from pyparsing import ParseException from pyparsing import ParseException
from pytz import UTC
from shapely.geometry import MultiPoint, Point from shapely.geometry import MultiPoint, Point
from six.moves import map, range, zip from six.moves import map, range, zip
from symmath import symmath_check from symmath import symmath_check
@@ -2653,7 +2653,7 @@ class CodeResponse(LoncapaResponse):
# ------------------------------------------------------------ # ------------------------------------------------------------
qinterface = self.capa_system.xqueue.interface qinterface = self.capa_system.xqueue.interface
qtime = datetime.strftime(datetime.now(UTC), DATEFORMAT) qtime = datetime.strftime(datetime.now(ZoneInfo("UTC")), DATEFORMAT)
anonymous_student_id = self.capa_system.anonymous_student_id anonymous_student_id = self.capa_system.anonymous_student_id

View File

@@ -11,13 +11,13 @@ import unittest
import zipfile import zipfile
from datetime import datetime from datetime import datetime
from unittest import mock from unittest import mock
from zoneinfo import ZoneInfo
import calc import calc
import pyparsing import pyparsing
import pytest import pytest
import random2 as random import random2 as random
import requests import requests
from pytz import UTC
from xmodule.capa.correctmap import CorrectMap from xmodule.capa.correctmap import CorrectMap
from xmodule.capa.responsetypes import ( from xmodule.capa.responsetypes import (
@@ -999,7 +999,7 @@ class CodeResponseTest(ResponseTest):
# Now we queue the LCP # Now we queue the LCP
cmap = CorrectMap() cmap = CorrectMap()
for i, answer_id in enumerate(answer_ids): for i, answer_id in enumerate(answer_ids):
queuestate = CodeResponseTest.make_queuestate(i, datetime.now(UTC)) queuestate = CodeResponseTest.make_queuestate(i, datetime.now(ZoneInfo("UTC")))
cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate)) cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate))
self.problem.correct_map.update(cmap) self.problem.correct_map.update(cmap)
@@ -1015,7 +1015,7 @@ class CodeResponseTest(ResponseTest):
old_cmap = CorrectMap() old_cmap = CorrectMap()
for i, answer_id in enumerate(answer_ids): for i, answer_id in enumerate(answer_ids):
queuekey = 1000 + i queuekey = 1000 + i
queuestate = CodeResponseTest.make_queuestate(queuekey, datetime.now(UTC)) queuestate = CodeResponseTest.make_queuestate(queuekey, datetime.now(ZoneInfo("UTC")))
old_cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate)) old_cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate))
# Message format common to external graders # Message format common to external graders
@@ -1083,14 +1083,14 @@ class CodeResponseTest(ResponseTest):
cmap = CorrectMap() cmap = CorrectMap()
for i, answer_id in enumerate(answer_ids): for i, answer_id in enumerate(answer_ids):
queuekey = 1000 + i queuekey = 1000 + i
latest_timestamp = datetime.now(UTC) latest_timestamp = datetime.now(ZoneInfo("UTC"))
queuestate = CodeResponseTest.make_queuestate(queuekey, latest_timestamp) queuestate = CodeResponseTest.make_queuestate(queuekey, latest_timestamp)
cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate)) cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate))
self.problem.correct_map.update(cmap) self.problem.correct_map.update(cmap)
# Queue state only tracks up to second # Queue state only tracks up to second
latest_timestamp = datetime.strptime(datetime.strftime(latest_timestamp, DATEFORMAT), DATEFORMAT).replace( latest_timestamp = datetime.strptime(datetime.strftime(latest_timestamp, DATEFORMAT), DATEFORMAT).replace(
tzinfo=UTC tzinfo=ZoneInfo("UTC")
) )
assert self.problem.get_recentmost_queuetime() == latest_timestamp assert self.problem.get_recentmost_queuetime() == latest_timestamp
@@ -1153,7 +1153,7 @@ class CodeResponseTest(ResponseTest):
old_cmap = CorrectMap() old_cmap = CorrectMap()
for i, answer_id in enumerate(answer_ids): for i, answer_id in enumerate(answer_ids):
queuekey = 1000 + i queuekey = 1000 + i
queuestate = CodeResponseTest.make_queuestate(queuekey, datetime.now(UTC)) queuestate = CodeResponseTest.make_queuestate(queuekey, datetime.now(ZoneInfo("UTC")))
old_cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate)) old_cmap.update(CorrectMap(answer_id=answer_id, queuestate=queuestate))
for grader_msg in valid_grader_msgs: for grader_msg in valid_grader_msgs:

View File

@@ -15,6 +15,7 @@ import re
import struct import struct
import sys import sys
import traceback import traceback
from zoneinfo import ZoneInfo
import nh3 import nh3
from django.conf import settings from django.conf import settings
@@ -23,7 +24,6 @@ from django.template.loader import render_to_string
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.functional import cached_property from django.utils.functional import cached_property
from lxml import etree from lxml import etree
from pytz import utc
from web_fragments.fragment import Fragment from web_fragments.fragment import Fragment
from xblock.core import XBlock from xblock.core import XBlock
from xblock.exceptions import NotFoundError, ProcessingError from xblock.exceptions import NotFoundError, ProcessingError
@@ -931,7 +931,7 @@ class _BuiltInProblemBlock( # pylint: disable=too-many-public-methods,too-many-
""" """
Set the module's last submission time (when the problem was submitted) Set the module's last submission time (when the problem was submitted)
""" """
self.last_submission_time = datetime.datetime.now(utc) self.last_submission_time = datetime.datetime.now(ZoneInfo("UTC"))
def get_progress(self): def get_progress(self):
""" """
@@ -1448,7 +1448,7 @@ class _BuiltInProblemBlock( # pylint: disable=too-many-public-methods,too-many-
""" """
Is it now past this problem's due date, including grace period? Is it now past this problem's due date, including grace period?
""" """
return self.close_date is not None and datetime.datetime.now(utc) > self.close_date return self.close_date is not None and datetime.datetime.now(ZoneInfo("UTC")) > self.close_date
def closed(self): def closed(self):
""" """
@@ -1774,7 +1774,7 @@ class _BuiltInProblemBlock( # pylint: disable=too-many-public-methods,too-many-
event_info["answers"] = answers_without_files event_info["answers"] = answers_without_files
# Can override current time # Can override current time
current_time = datetime.datetime.now(utc) current_time = datetime.datetime.now(ZoneInfo("UTC"))
if override_time is not False: if override_time is not False:
current_time = override_time current_time = override_time

View File

@@ -6,6 +6,7 @@ Django module container for classes and operations related to the "Course Block"
import json import json
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import dateutil.parser import dateutil.parser
import requests import requests
@@ -15,7 +16,6 @@ from edx_toggles.toggles import SettingDictToggle
from lazy import lazy from lazy import lazy
from lxml import etree from lxml import etree
from path import Path as path from path import Path as path
from pytz import utc
from xblock.fields import Boolean, Date, Dict, Float, Integer, List, Scope, String from xblock.fields import Boolean, Date, Dict, Float, Integer, List, Scope, String
from openedx.core.djangoapps.video_pipeline.models import VideoUploadsEnabledByDefault from openedx.core.djangoapps.video_pipeline.models import VideoUploadsEnabledByDefault
from openedx.core.djangoapps.video_config.sharing import ( from openedx.core.djangoapps.video_config.sharing import (
@@ -163,7 +163,7 @@ class Textbook: # lint-amnesty, pylint: disable=missing-class-docstring
# see if we already fetched this # see if we already fetched this
if toc_url in _cached_toc: if toc_url in _cached_toc:
(table_of_contents, timestamp) = _cached_toc[toc_url] (table_of_contents, timestamp) = _cached_toc[toc_url]
age = datetime.now(utc) - timestamp age = datetime.now(ZoneInfo("UTC")) - timestamp
# expire every 10 minutes # expire every 10 minutes
if age.seconds < 600: if age.seconds < 600:
return table_of_contents return table_of_contents
@@ -1485,7 +1485,7 @@ class CourseBlock(
blackouts = self.get_discussion_blackout_datetimes() blackouts = self.get_discussion_blackout_datetimes()
posting_restrictions = self.discussions_settings.get('posting_restrictions', 'disabled') posting_restrictions = self.discussions_settings.get('posting_restrictions', 'disabled')
now = datetime.now(utc) now = datetime.now(ZoneInfo("UTC"))
if posting_restrictions == 'enabled': if posting_restrictions == 'enabled':
return False return False
@@ -1601,7 +1601,7 @@ class CourseBlock(
""" """
if not self.start: if not self.start:
return False return False
return datetime.now(utc) <= self.start return datetime.now(ZoneInfo("UTC")) <= self.start
class CourseSummary: class CourseSummary:
@@ -1674,5 +1674,5 @@ class CourseSummary:
course_id=str(self.id), end_date=self.end, err=e course_id=str(self.id), end_date=self.end, err=e
) )
) )
modified_end = self.end.replace(tzinfo=utc) modified_end = self.end.replace(tzinfo=ZoneInfo("UTC"))
return course_metadata_utils.has_course_ended(modified_end) return course_metadata_utils.has_course_ended(modified_end)

View File

@@ -7,14 +7,15 @@ classes, which both need these type of functions.
""" """
import dateutil.parser
from base64 import b32encode from base64 import b32encode
from datetime import datetime, timedelta from datetime import datetime, timedelta
from math import exp from math import exp
from zoneinfo import ZoneInfo
import dateutil.parser
from pytz import utc
DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=utc) DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=ZoneInfo("UTC"))
""" """
Default grading policy for a course run. Default grading policy for a course run.
@@ -95,7 +96,7 @@ def has_course_started(start_date):
start_date (datetime): The start datetime of the course in question. start_date (datetime): The start datetime of the course in question.
""" """
# TODO: This will throw if start_date is None... consider changing this behavior? # TODO: This will throw if start_date is None... consider changing this behavior?
return datetime.now(utc) > start_date return datetime.now(ZoneInfo("UTC")) > start_date
def has_course_ended(end_date): def has_course_ended(end_date):
@@ -107,7 +108,7 @@ def has_course_ended(end_date):
Arguments: Arguments:
end_date (datetime): The end datetime of the course in question. end_date (datetime): The end datetime of the course in question.
""" """
return datetime.now(utc) > end_date if end_date is not None else False return datetime.now(ZoneInfo("UTC")) > end_date if end_date is not None else False
def is_enrollment_open(enrollment_start_date, enrollment_end_date): def is_enrollment_open(enrollment_start_date, enrollment_end_date):
@@ -118,9 +119,9 @@ def is_enrollment_open(enrollment_start_date, enrollment_end_date):
enrollment_start_date (datetime): The enrollment start datetime of the course. enrollment_start_date (datetime): The enrollment start datetime of the course.
enrollment_end_date (datetime): The enrollment end datetime of the course. enrollment_end_date (datetime): The enrollment end datetime of the course.
""" """
now = datetime.now(utc) now = datetime.now(ZoneInfo("UTC"))
enrollment_start_date = enrollment_start_date or datetime.min.replace(tzinfo=utc) enrollment_start_date = enrollment_start_date or datetime.min.replace(tzinfo=ZoneInfo("UTC"))
enrollment_end_date = enrollment_end_date or datetime.max.replace(tzinfo=utc) enrollment_end_date = enrollment_end_date or datetime.max.replace(tzinfo=ZoneInfo("UTC"))
return enrollment_start_date < now < enrollment_end_date return enrollment_start_date < now < enrollment_end_date
@@ -133,7 +134,7 @@ def course_starts_within(start_date, look_ahead_days):
start_date (datetime): The start datetime of the course in question. start_date (datetime): The start datetime of the course in question.
look_ahead_days (int): number of days to see in future for course start date. look_ahead_days (int): number of days to see in future for course start date.
""" """
return datetime.now(utc) + timedelta(days=look_ahead_days) > start_date return datetime.now(ZoneInfo("UTC")) + timedelta(days=look_ahead_days) > start_date
def course_start_date_is_default(start, advertised_start): def course_start_date_is_default(start, advertised_start):
@@ -179,10 +180,10 @@ def sorting_dates(start, advertised_start, announcement):
try: try:
start = dateutil.parser.parse(advertised_start) start = dateutil.parser.parse(advertised_start)
if start.tzinfo is None: if start.tzinfo is None:
start = start.replace(tzinfo=utc) start = start.replace(tzinfo=ZoneInfo("UTC"))
except (TypeError, ValueError, AttributeError): except (TypeError, ValueError, AttributeError):
start = start # lint-amnesty, pylint: disable=self-assigning-variable start = start # lint-amnesty, pylint: disable=self-assigning-variable
now = datetime.now(utc) now = datetime.now(ZoneInfo("UTC"))
return announcement, start, now return announcement, start, now

View File

@@ -62,6 +62,7 @@ import textwrap
from unittest import mock from unittest import mock
from urllib import parse from urllib import parse
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from zoneinfo import ZoneInfo
import nh3 import nh3
import oauthlib.oauth1 import oauthlib.oauth1
@@ -69,7 +70,6 @@ from django.conf import settings
from lxml import etree from lxml import etree
from oauthlib.oauth1.rfc5849 import signature from oauthlib.oauth1.rfc5849 import signature
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from web_fragments.fragment import Fragment from web_fragments.fragment import Fragment
from webob import Response from webob import Response
from xblock.core import List, Scope, String, XBlock from xblock.core import List, Scope, String, XBlock
@@ -989,7 +989,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'}
close_date = due_date + self.graceperiod # pylint: disable=no-member close_date = due_date + self.graceperiod # pylint: disable=no-member
else: else:
close_date = due_date close_date = due_date
return close_date is not None and datetime.datetime.now(UTC) > close_date return close_date is not None and datetime.datetime.now(ZoneInfo("UTC")) > close_date
LTIBlock = ( LTIBlock = (

View File

@@ -12,11 +12,11 @@ from abc import ABCMeta, abstractmethod
from collections import defaultdict from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from operator import itemgetter from operator import itemgetter
from django.db import transaction from zoneinfo import ZoneInfo
from django.db import transaction
from opaque_keys.edx.keys import AssetKey, CourseKey from opaque_keys.edx.keys import AssetKey, CourseKey
from opaque_keys.edx.locations import Location # For import backwards compatibility from opaque_keys.edx.locations import Location # For import backwards compatibility
from pytz import UTC
from sortedcontainers import SortedKeyList from sortedcontainers import SortedKeyList
from xblock.core import XBlock from xblock.core import XBlock
from xblock.plugin import default_select from xblock.plugin import default_select
@@ -719,7 +719,7 @@ class ModuleStoreAssetWriteInterface(ModuleStoreAssetBase):
)) ))
continue continue
if not import_only: if not import_only:
asset_md.update({'edited_by': user_id, 'edited_on': datetime.datetime.now(UTC)}) asset_md.update({'edited_by': user_id, 'edited_on': datetime.datetime.now(ZoneInfo("UTC"))})
asset_type = asset_md.asset_id.asset_type asset_type = asset_md.asset_id.asset_type
all_assets = assets_by_type[asset_type] all_assets = assets_by_type[asset_type]
all_assets.insert_or_update(asset_md) all_assets.insert_or_update(asset_md)
@@ -862,7 +862,7 @@ class ModuleStoreRead(ModuleStoreAssetBase, metaclass=ABCMeta):
For substring matching: For substring matching:
pass a regex object. pass a regex object.
For arbitrary function comparison such as date time comparison: For arbitrary function comparison such as date time comparison:
pass the function as in start=lambda x: x < datetime.datetime(2014, 1, 1, 0, tzinfo=pytz.UTC) pass the function as in start=lambda x: x < datetime.datetime(2014, 1, 1, 0, tzinfo=ZoneInfo("UTC"))
Args: Args:
block (dict, XBlock, or BlockData): either the BlockData (transformed from the db) -or- block (dict, XBlock, or BlockData): either the BlockData (transformed from the db) -or-

View File

@@ -19,6 +19,7 @@ import sys
from datetime import datetime from datetime import datetime
from importlib import import_module from importlib import import_module
from uuid import uuid4 from uuid import uuid4
from zoneinfo import ZoneInfo
import pymongo import pymongo
from bson.son import SON from bson.son import SON
@@ -26,7 +27,6 @@ from fs.osfs import OSFS
from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator
from path import Path as path from path import Path as path
from pytz import UTC
from xblock.exceptions import InvalidScopeError from xblock.exceptions import InvalidScopeError
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope, ScopeIds from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope, ScopeIds
from xblock.runtime import KvsFieldData from xblock.runtime import KvsFieldData
@@ -252,7 +252,7 @@ class OldModuleStoreRuntime(ModuleStoreRuntime, EditInfoRuntimeMixin): # pylint
if raw_metadata.get('published_date'): if raw_metadata.get('published_date'):
block._edit_info['published_date'] = datetime( block._edit_info['published_date'] = datetime(
*raw_metadata.get('published_date')[0:6] *raw_metadata.get('published_date')[0:6]
).replace(tzinfo=UTC) ).replace(tzinfo=ZoneInfo("UTC"))
block._edit_info['published_by'] = raw_metadata.get('published_by') block._edit_info['published_by'] = raw_metadata.get('published_by')
for wrapper in self.modulestore.xblock_field_data_wrappers: for wrapper in self.modulestore.xblock_field_data_wrappers:

View File

@@ -11,12 +11,12 @@ import re
import zlib import zlib
from contextlib import contextmanager from contextlib import contextmanager
from time import time from time import time
from zoneinfo import ZoneInfo
from ccx_keys.locator import CCXLocator from ccx_keys.locator import CCXLocator
from django.core.cache import caches, InvalidCacheBackendError from django.core.cache import caches, InvalidCacheBackendError
from django.db.transaction import TransactionManagementError from django.db.transaction import TransactionManagementError
import pymongo import pymongo
import pytz
# Import this just to export it # Import this just to export it
from pymongo.errors import DuplicateKeyError # pylint: disable=unused-import from pymongo.errors import DuplicateKeyError # pylint: disable=unused-import
from edx_django_utils import monitoring from edx_django_utils import monitoring
@@ -488,7 +488,7 @@ class MongoPersistenceBackend:
with TIMER.timer("insert_course_index", course_context): with TIMER.timer("insert_course_index", course_context):
# Set last_update which is used to avoid collisions, unless a subclass already set it before calling super() # Set last_update which is used to avoid collisions, unless a subclass already set it before calling super()
if not self.with_mysql_subclass: if not self.with_mysql_subclass:
course_index['last_update'] = datetime.datetime.now(pytz.utc) course_index['last_update'] = datetime.datetime.now(ZoneInfo("UTC"))
# Insert the new index: # Insert the new index:
self.course_index.insert_one(course_index) self.course_index.insert_one(course_index)
@@ -515,7 +515,7 @@ class MongoPersistenceBackend:
} }
# Set last_update which is used to avoid collisions, unless a subclass already set it before calling super() # Set last_update which is used to avoid collisions, unless a subclass already set it before calling super()
if not self.with_mysql_subclass: if not self.with_mysql_subclass:
course_index['last_update'] = datetime.datetime.now(pytz.utc) course_index['last_update'] = datetime.datetime.now(ZoneInfo("UTC"))
# Update the course index: # Update the course index:
result = self.course_index.replace_one(query, course_index, upsert=False,) result = self.course_index.replace_one(query, course_index, upsert=False,)
if result.modified_count == 0: if result.modified_count == 0:
@@ -729,7 +729,7 @@ class DjangoFlexPersistenceBackend(MongoPersistenceBackend):
# This is a relatively large hammer for the problem, but we mostly only use one course at a time. # This is a relatively large hammer for the problem, but we mostly only use one course at a time.
RequestCache(namespace="course_index_cache").clear() RequestCache(namespace="course_index_cache").clear()
course_index['last_update'] = datetime.datetime.now(pytz.utc) course_index['last_update'] = datetime.datetime.now(ZoneInfo("UTC"))
new_index = SplitModulestoreCourseIndex(**SplitModulestoreCourseIndex.fields_from_v1_schema(course_index)) new_index = SplitModulestoreCourseIndex(**SplitModulestoreCourseIndex.fields_from_v1_schema(course_index))
new_index.save() new_index.save()
# Also write to MongoDB, so we can switch back to using it if this new MySQL version doesn't work well. # Also write to MongoDB, so we can switch back to using it if this new MySQL version doesn't work well.
@@ -751,7 +751,7 @@ class DjangoFlexPersistenceBackend(MongoPersistenceBackend):
# This code is just copying the behavior of the existing MongoPersistenceBackend # This code is just copying the behavior of the existing MongoPersistenceBackend
# See https://github.com/openedx/edx-platform/pull/5200 for context # See https://github.com/openedx/edx-platform/pull/5200 for context
RequestCache(namespace="course_index_cache").clear() RequestCache(namespace="course_index_cache").clear()
course_index['last_update'] = datetime.datetime.now(pytz.utc) course_index['last_update'] = datetime.datetime.now(ZoneInfo("UTC"))
# Find the SplitModulestoreCourseIndex entry that we'll be updating: # Find the SplitModulestoreCourseIndex entry that we'll be updating:
index_obj = SplitModulestoreCourseIndex.objects.get(objectid=course_index["_id"]) index_obj = SplitModulestoreCourseIndex.objects.get(objectid=course_index["_id"])

View File

@@ -60,6 +60,7 @@ import datetime
import logging import logging
from collections import defaultdict from collections import defaultdict
from importlib import import_module from importlib import import_module
from zoneinfo import ZoneInfo
from bson.objectid import ObjectId from bson.objectid import ObjectId
from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator from ccx_keys.locator import CCXBlockUsageLocator, CCXLocator
@@ -72,7 +73,6 @@ from opaque_keys.edx.locator import (
LocalId, LocalId,
) )
from path import Path as path from path import Path as path
from pytz import UTC
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope from xblock.fields import Reference, ReferenceList, ReferenceValueDict, Scope
@@ -481,7 +481,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin):
new_structure['_id'] = ObjectId() new_structure['_id'] = ObjectId()
new_structure['previous_version'] = structure['_id'] new_structure['previous_version'] = structure['_id']
new_structure['edited_by'] = user_id new_structure['edited_by'] = user_id
new_structure['edited_on'] = datetime.datetime.now(UTC) new_structure['edited_on'] = datetime.datetime.now(ZoneInfo("UTC"))
new_structure['schema_version'] = self.SCHEMA_VERSION new_structure['schema_version'] = self.SCHEMA_VERSION
# If we're in a bulk write, update the structure used there, and mark it as dirty # If we're in a bulk write, update the structure used there, and mark it as dirty
@@ -499,7 +499,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin):
original_usage = block_data.edit_info.original_usage original_usage = block_data.edit_info.original_usage
original_usage_version = block_data.edit_info.original_usage_version original_usage_version = block_data.edit_info.original_usage_version
block_data.edit_info.edited_on = datetime.datetime.now(UTC) block_data.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
block_data.edit_info.edited_by = user_id block_data.edit_info.edited_by = user_id
block_data.edit_info.previous_version = block_data.edit_info.update_version block_data.edit_info.previous_version = block_data.edit_info.update_version
block_data.edit_info.update_version = update_version block_data.edit_info.update_version = update_version
@@ -1499,7 +1499,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
"fields": new_def_data, "fields": new_def_data,
"edit_info": { "edit_info": {
"edited_by": user_id, "edited_by": user_id,
"edited_on": datetime.datetime.now(UTC), "edited_on": datetime.datetime.now(ZoneInfo("UTC")),
"previous_version": None, "previous_version": None,
"original_version": new_id, "original_version": new_id,
}, },
@@ -1547,7 +1547,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
new_definition['_id'] = ObjectId() new_definition['_id'] = ObjectId()
new_definition['fields'] = new_def_data new_definition['fields'] = new_def_data
new_definition['edit_info']['edited_by'] = user_id new_definition['edit_info']['edited_by'] = user_id
new_definition['edit_info']['edited_on'] = datetime.datetime.now(UTC) new_definition['edit_info']['edited_on'] = datetime.datetime.now(ZoneInfo("UTC"))
# previous version id # previous version id
new_definition['edit_info']['previous_version'] = old_definition['_id'] new_definition['edit_info']['previous_version'] = old_definition['_id']
new_definition['schema_version'] = self.SCHEMA_VERSION new_definition['schema_version'] = self.SCHEMA_VERSION
@@ -1886,7 +1886,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
new_fields.update(definition_fields) new_fields.update(definition_fields)
definition_id = self._update_definition_from_data(locator, old_def, new_fields, user_id).definition_id definition_id = self._update_definition_from_data(locator, old_def, new_fields, user_id).definition_id
root_block.definition = definition_id root_block.definition = definition_id
root_block.edit_info.edited_on = datetime.datetime.now(UTC) root_block.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
root_block.edit_info.edited_by = user_id root_block.edit_info.edited_by = user_id
root_block.edit_info.previous_version = root_block.edit_info.update_version root_block.edit_info.previous_version = root_block.edit_info.update_version
root_block.edit_info.update_version = new_id root_block.edit_info.update_version = new_id
@@ -1906,7 +1906,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
'course': get_library_or_course_attribute(locator), 'course': get_library_or_course_attribute(locator),
'run': locator.run, 'run': locator.run,
'edited_by': user_id, 'edited_by': user_id,
'edited_on': datetime.datetime.now(UTC), 'edited_on': datetime.datetime.now(ZoneInfo("UTC")),
'versions': versions_dict, 'versions': versions_dict,
'schema_version': self.SCHEMA_VERSION, 'schema_version': self.SCHEMA_VERSION,
'search_targets': search_targets or {}, 'search_targets': search_targets or {},
@@ -2414,7 +2414,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
dest_info.edit_info.previous_version = dest_info.edit_info.update_version dest_info.edit_info.previous_version = dest_info.edit_info.update_version
dest_info.edit_info.update_version = old_dest_structure_version dest_info.edit_info.update_version = old_dest_structure_version
dest_info.edit_info.edited_by = user_id dest_info.edit_info.edited_by = user_id
dest_info.edit_info.edited_on = datetime.datetime.now(UTC) dest_info.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
orphans = orig_descendants - new_descendants orphans = orig_descendants - new_descendants
for orphan in orphans: for orphan in orphans:
@@ -2483,7 +2483,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
# from draft to published as part of publishing workflow. # from draft to published as part of publishing workflow.
# Setting it to the source_block_info structure version here breaks split_draft's has_changes() method. # Setting it to the source_block_info structure version here breaks split_draft's has_changes() method.
new_block_info.edit_info.edited_by = user_id new_block_info.edit_info.edited_by = user_id
new_block_info.edit_info.edited_on = datetime.datetime.now(UTC) new_block_info.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
new_block_info.edit_info.original_usage = str(usage_key.replace(branch=None, version_guid=None)) new_block_info.edit_info.original_usage = str(usage_key.replace(branch=None, version_guid=None))
new_block_info.edit_info.original_usage_version = source_block_info.edit_info.update_version new_block_info.edit_info.original_usage_version = source_block_info.edit_info.update_version
dest_structure['blocks'][new_block_key] = new_block_info dest_structure['blocks'][new_block_key] = new_block_info
@@ -2541,7 +2541,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
for parent_block_key in parent_block_keys: for parent_block_key in parent_block_keys:
parent_block = new_blocks[parent_block_key] parent_block = new_blocks[parent_block_key]
parent_block.fields['children'].remove(block_key) parent_block.fields['children'].remove(block_key)
parent_block.edit_info.edited_on = datetime.datetime.now(UTC) parent_block.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
parent_block.edit_info.edited_by = user_id parent_block.edit_info.edited_by = user_id
parent_block.edit_info.previous_version = parent_block.edit_info.update_version parent_block.edit_info.previous_version = parent_block.edit_info.update_version
parent_block.edit_info.update_version = new_id parent_block.edit_info.update_version = new_id
@@ -3056,7 +3056,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
'previous_version': None, 'previous_version': None,
'original_version': new_id, 'original_version': new_id,
'edited_by': user_id, 'edited_by': user_id,
'edited_on': datetime.datetime.now(UTC), 'edited_on': datetime.datetime.now(ZoneInfo("UTC")),
'blocks': blocks, 'blocks': blocks,
'schema_version': self.SCHEMA_VERSION, 'schema_version': self.SCHEMA_VERSION,
} }
@@ -3122,7 +3122,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
destination_block.edit_info.previous_version = previous_version destination_block.edit_info.previous_version = previous_version
destination_block.edit_info.update_version = destination_version destination_block.edit_info.update_version = destination_version
destination_block.edit_info.edited_by = user_id destination_block.edit_info.edited_by = user_id
destination_block.edit_info.edited_on = datetime.datetime.now(UTC) destination_block.edit_info.edited_on = datetime.datetime.now(ZoneInfo("UTC"))
else: else:
destination_block = self._new_block( destination_block = self._new_block(
user_id, new_block.block_type, user_id, new_block.block_type,
@@ -3196,7 +3196,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
'fields': block_fields, 'fields': block_fields,
'asides': asides, 'asides': asides,
'edit_info': { 'edit_info': {
'edited_on': datetime.datetime.now(UTC), 'edited_on': datetime.datetime.now(ZoneInfo("UTC")),
'edited_by': user_id, 'edited_by': user_id,
'previous_version': None, 'previous_version': None,
'update_version': new_id 'update_version': new_id

View File

@@ -12,9 +12,9 @@ from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from uuid import uuid4 from uuid import uuid4
from unittest.mock import patch from unittest.mock import patch
from zoneinfo import ZoneInfo
import pymongo.message import pymongo.message
import pytz
from factory import Factory, Sequence, lazy_attribute, lazy_attribute_sequence from factory import Factory, Sequence, lazy_attribute, lazy_attribute_sequence
from factory.errors import CyclicDefinitionError from factory.errors import CyclicDefinitionError
from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.keys import UsageKey
@@ -207,7 +207,7 @@ class ToyCourseFactory(SampleCourseFactory):
'graded': True, 'graded': True,
'discussion_topics': {"General": {"id": "i4x-edX-toy-course-2012_Fall"}}, 'discussion_topics': {"General": {"id": "i4x-edX-toy-course-2012_Fall"}},
'graceperiod': datetime.timedelta(days=2, seconds=21599), 'graceperiod': datetime.timedelta(days=2, seconds=21599),
'start': datetime.datetime(2015, 7, 17, 12, tzinfo=pytz.utc), 'start': datetime.datetime(2015, 7, 17, 12, tzinfo=ZoneInfo("UTC")),
'xml_attributes': {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]}, 'xml_attributes': {"filename": ["course/2012_Fall.xml", "course/2012_Fall.xml"]},
'pdf_textbooks': [ 'pdf_textbooks': [
{ {

View File

@@ -8,7 +8,7 @@ import unittest
from datetime import datetime, timedelta from datetime import datetime, timedelta
import pytest import pytest
import ddt import ddt
import pytz from zoneinfo import ZoneInfo
from django.test import TestCase from django.test import TestCase
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
@@ -30,7 +30,7 @@ class AssetStoreTestData:
""" """
Shared data for constructing test assets. Shared data for constructing test assets.
""" """
now = datetime.now(pytz.utc) now = datetime.now(ZoneInfo("UTC"))
user_id = 144 user_id = 144
user_id_long = int(user_id) user_id_long = int(user_id)
@@ -131,7 +131,7 @@ class TestMongoAssetMetadataStorage(TestCase):
""" """
Make a single test asset metadata. Make a single test asset metadata.
""" """
now = datetime.now(pytz.utc) now = datetime.now(ZoneInfo("UTC"))
return AssetMetadata( return AssetMetadata(
asset_loc, internal_name='EKMND332DDBK', asset_loc, internal_name='EKMND332DDBK',
pathname='pictures/historical', contenttype='image/jpeg', pathname='pictures/historical', contenttype='image/jpeg',
@@ -322,13 +322,13 @@ class TestMongoAssetMetadataStorage(TestCase):
('curr_version', 'v1.01'), ('curr_version', 'v1.01'),
('prev_version', 'v1.0'), ('prev_version', 'v1.0'),
('edited_by', 'Mork'), ('edited_by', 'Mork'),
('edited_on', datetime(1969, 1, 1, tzinfo=pytz.utc)), ('edited_on', datetime(1969, 1, 1, tzinfo=ZoneInfo("UTC"))),
) )
DISALLOWED_ATTRS = ( DISALLOWED_ATTRS = (
('asset_id', 'IAmBogus'), ('asset_id', 'IAmBogus'),
('created_by', 'Smith'), ('created_by', 'Smith'),
('created_on', datetime.now(pytz.utc)), ('created_on', datetime.now(ZoneInfo("UTC"))),
) )
UNKNOWN_ATTRS = ( UNKNOWN_ATTRS = (

View File

@@ -13,6 +13,7 @@ from shutil import rmtree
from tempfile import mkdtemp from tempfile import mkdtemp
from uuid import uuid4 from uuid import uuid4
from unittest.mock import Mock, call, patch from unittest.mock import Mock, call, patch
from zoneinfo import ZoneInfo
import ddt import ddt
from openedx_events.content_authoring.data import CourseData, XBlockData from openedx_events.content_authoring.data import CourseData, XBlockData
@@ -32,7 +33,6 @@ import pytest
from django.conf import settings from django.conf import settings
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator # pylint: disable=unused-import from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator, LibraryLocator # pylint: disable=unused-import
from pytz import UTC
from web_fragments.fragment import Fragment from web_fragments.fragment import Fragment
from xblock.core import XBlockAside from xblock.core import XBlockAside
from xblock.fields import Scope, ScopeIds, String from xblock.fields import Scope, ScopeIds, String
@@ -2043,7 +2043,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
'problem' 'problem'
) )
assert self.user_id == block.edited_by assert self.user_id == block.edited_by
assert datetime.datetime.now(UTC) > block.edited_on assert datetime.datetime.now(ZoneInfo("UTC")) > block.edited_on
@ddt.data(ModuleStoreEnum.Type.split) @ddt.data(ModuleStoreEnum.Type.split)
def test_create_item_populates_subtree_edited_info(self, default_ms): def test_create_item_populates_subtree_edited_info(self, default_ms):
@@ -2054,7 +2054,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
'problem' 'problem'
) )
assert self.user_id == block.subtree_edited_by assert self.user_id == block.subtree_edited_by
assert datetime.datetime.now(UTC) > block.subtree_edited_on assert datetime.datetime.now(ZoneInfo("UTC")) > block.subtree_edited_on
# Split: wildcard search of draft (find) and split (mysql) # Split: wildcard search of draft (find) and split (mysql)
@ddt.data((ModuleStoreEnum.Type.split, 1, 1, 0)) @ddt.data((ModuleStoreEnum.Type.split, 1, 1, 0))
@@ -2202,7 +2202,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
block_id='test_html_no_change' block_id='test_html_no_change'
) )
after_create = datetime.datetime.now(UTC) after_create = datetime.datetime.now(ZoneInfo("UTC"))
# Verify that all nodes were last edited in the past by create_user # Verify that all nodes were last edited in the past by create_user
for block in [component, child, sibling]: for block in [component, child, sibling]:
check_node(block.location, None, after_create, self.user_id, None, after_create, self.user_id) check_node(block.location, None, after_create, self.user_id, None, after_create, self.user_id)
@@ -2213,7 +2213,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
editing_user = self.user_id - 2 editing_user = self.user_id - 2
with self.store.bulk_operations(test_course.id): # TNL-764 bulk ops disabled ancestor updates with self.store.bulk_operations(test_course.id): # TNL-764 bulk ops disabled ancestor updates
component = self.store.update_item(component, editing_user) component = self.store.update_item(component, editing_user)
after_edit = datetime.datetime.now(UTC) after_edit = datetime.datetime.now(ZoneInfo("UTC"))
check_node(component.location, after_create, after_edit, editing_user, after_create, after_edit, editing_user) check_node(component.location, after_create, after_edit, editing_user, after_create, after_edit, editing_user)
# but child didn't change # but child didn't change
check_node(child.location, None, after_create, self.user_id, None, after_create, self.user_id) check_node(child.location, None, after_create, self.user_id, None, after_create, self.user_id)
@@ -2223,7 +2223,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
child.display_name = 'Changed Display Name' child.display_name = 'Changed Display Name'
self.store.update_item(child, user_id=editing_user) self.store.update_item(child, user_id=editing_user)
after_edit = datetime.datetime.now(UTC) after_edit = datetime.datetime.now(ZoneInfo("UTC"))
# Verify that child was last edited between after_create and after_edit by edit_user # Verify that child was last edited between after_create and after_edit by edit_user
check_node(child.location, after_create, after_edit, editing_user, after_create, after_edit, editing_user) check_node(child.location, after_create, after_edit, editing_user, after_create, after_edit, editing_user)
@@ -2283,7 +2283,7 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup):
) )
# Store the current time, then publish # Store the current time, then publish
old_time = datetime.datetime.now(UTC) old_time = datetime.datetime.now(ZoneInfo("UTC"))
self.store.publish(component.location, publish_user) self.store.publish(component.location, publish_user)
updated_component = self.store.get_item(component.location) updated_component = self.store.get_item(component.location)

View File

@@ -11,11 +11,11 @@ import logging
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
from django.conf import settings from django.conf import settings
from zoneinfo import ZoneInfo
from edx_django_utils.monitoring import set_custom_attribute from edx_django_utils.monitoring import set_custom_attribute
from lxml import etree from lxml import etree
from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.keys import UsageKey
from pytz import UTC
from web_fragments.fragment import Fragment from web_fragments.fragment import Fragment
from xblock.completable import XBlockCompletionMode from xblock.completable import XBlockCompletionMode
from xblock.core import XBlock from xblock.core import XBlock
@@ -393,7 +393,7 @@ class SequenceBlock(
return ( return (
not date or not date or
not hide_after_date or not hide_after_date or
datetime.now(UTC) < date datetime.now(ZoneInfo("UTC")) < date
) )
def gate_entire_sequence_if_it_is_a_timed_exam_and_contains_content_type_gated_problems(self): def gate_entire_sequence_if_it_is_a_timed_exam_and_contains_content_type_gated_problems(self):

View File

@@ -10,6 +10,7 @@ import random
import textwrap import textwrap
import unittest import unittest
from unittest.mock import DEFAULT, Mock, PropertyMock, patch from unittest.mock import DEFAULT, Mock, PropertyMock, patch
from zoneinfo import ZoneInfo
import ddt import ddt
import pytest import pytest
@@ -21,7 +22,6 @@ from django.test import override_settings
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from lxml import etree from lxml import etree
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from pytz import UTC
from webob.multidict import MultiDict from webob.multidict import MultiDict
from xblock.exceptions import NotFoundError from xblock.exceptions import NotFoundError
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
@@ -213,7 +213,7 @@ class ProblemBlockTest(unittest.TestCase): # pylint: disable=too-many-public-me
def setUp(self): def setUp(self):
super().setUp() super().setUp()
now = datetime.datetime.now(UTC) now = datetime.datetime.now(ZoneInfo("UTC"))
day_delta = datetime.timedelta(days=1) day_delta = datetime.timedelta(days=1)
self.yesterday_str = str(now - day_delta) self.yesterday_str = str(now - day_delta)
self.today_str = str(now) self.today_str = str(now)
@@ -731,11 +731,11 @@ class ProblemBlockTest(unittest.TestCase): # pylint: disable=too-many-public-me
# Utility to create a datetime object in the past # Utility to create a datetime object in the past
def past_datetime(days): def past_datetime(days):
return datetime.datetime.now(UTC) - datetime.timedelta(days=days) return datetime.datetime.now(ZoneInfo("UTC")) - datetime.timedelta(days=days)
# Utility to create a datetime object in the future # Utility to create a datetime object in the future
def future_datetime(days): def future_datetime(days):
return datetime.datetime.now(UTC) + datetime.timedelta(days=days) return datetime.datetime.now(ZoneInfo("UTC")) + datetime.timedelta(days=days)
block = CapaFactory.create(max_attempts="1", attempts="0") block = CapaFactory.create(max_attempts="1", attempts="0")
@@ -1258,7 +1258,7 @@ class ProblemBlockTest(unittest.TestCase): # pylint: disable=too-many-public-me
) )
with multipatch as values: with multipatch as values:
values["is_queued"].return_value = True values["is_queued"].return_value = True
values["get_recentmost_queuetime"].return_value = datetime.datetime.now(UTC) values["get_recentmost_queuetime"].return_value = datetime.datetime.now(ZoneInfo("UTC"))
get_request_dict = {CapaFactory.input_key(): "3.14"} get_request_dict = {CapaFactory.input_key(): "3.14"}
result = block.submit_problem(get_request_dict) result = block.submit_problem(get_request_dict)

View File

@@ -6,6 +6,7 @@ import unittest
from datetime import datetime, timedelta from datetime import datetime, timedelta
import sys import sys
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from zoneinfo import ZoneInfo
import ddt import ddt
from dateutil import parser from dateutil import parser
@@ -14,7 +15,6 @@ from django.test import override_settings
from fs.memoryfs import MemoryFS from fs.memoryfs import MemoryFS
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
import pytest import pytest
from pytz import utc
from xblock.runtime import DictKeyValueStore, KvsFieldData from xblock.runtime import DictKeyValueStore, KvsFieldData
from openedx.core.lib.teams_config import TeamsConfig, DEFAULT_COURSE_RUN_MAX_TEAM_SIZE from openedx.core.lib.teams_config import TeamsConfig, DEFAULT_COURSE_RUN_MAX_TEAM_SIZE
@@ -27,9 +27,9 @@ from xmodule.modulestore.exceptions import InvalidProctoringProvider
ORG = 'test_org' ORG = 'test_org'
COURSE = 'test_course' COURSE = 'test_course'
NOW = datetime.strptime('2013-01-01T01:00:00', '%Y-%m-%dT%H:%M:00').replace(tzinfo=utc) NOW = datetime.strptime('2013-01-01T01:00:00', '%Y-%m-%dT%H:%M:00').replace(tzinfo=ZoneInfo("UTC"))
_TODAY = datetime.now(utc) _TODAY = datetime.now(ZoneInfo("UTC"))
_LAST_WEEK = _TODAY - timedelta(days=7) _LAST_WEEK = _TODAY - timedelta(days=7)
_NEXT_WEEK = _TODAY + timedelta(days=7) _NEXT_WEEK = _TODAY + timedelta(days=7)

View File

@@ -6,8 +6,8 @@ Tests for course_metadata_utils.
from collections import namedtuple from collections import namedtuple
from datetime import datetime, timedelta from datetime import datetime, timedelta
from unittest import TestCase from unittest import TestCase
from zoneinfo import ZoneInfo
from pytz import utc
import pytest import pytest
from xmodule.block_metadata_utils import ( from xmodule.block_metadata_utils import (
display_name_with_default, display_name_with_default,
@@ -29,7 +29,7 @@ from xmodule.modulestore.tests.utils import (
VersioningModulestoreBuilder VersioningModulestoreBuilder
) )
_TODAY = datetime.now(utc) _TODAY = datetime.now(ZoneInfo("UTC"))
_LAST_WEEK = _TODAY - timedelta(days=7) _LAST_WEEK = _TODAY - timedelta(days=7)
_NEXT_WEEK = _TODAY + timedelta(days=7) _NEXT_WEEK = _TODAY + timedelta(days=7)
@@ -111,7 +111,7 @@ class CourseMetadataUtilsTestCase(TestCase):
"""Dummy implementation of gettext, so we don't need Django.""" """Dummy implementation of gettext, so we don't need Django."""
return text return text
test_datetime = datetime(1945, 2, 6, 4, 20, 00, tzinfo=utc) test_datetime = datetime(1945, 2, 6, 4, 20, 00, tzinfo=ZoneInfo("UTC"))
advertised_start_parsable = "2038-01-19 03:14:07" advertised_start_parsable = "2038-01-19 03:14:07"
FunctionTest = namedtuple('FunctionTest', 'function scenarios') FunctionTest = namedtuple('FunctionTest', 'function scenarios')

View File

@@ -12,10 +12,10 @@ import datetime
import textwrap import textwrap
import unittest import unittest
from unittest.mock import Mock from unittest.mock import Mock
from zoneinfo import ZoneInfo
import pytest import pytest
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from pytz import UTC
from xblock.exceptions import NotFoundError from xblock.exceptions import NotFoundError
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds from xblock.fields import ScopeIds
@@ -163,7 +163,7 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime.now(UTC), last_submission_time=datetime.datetime.now(ZoneInfo("UTC")),
submission_wait_seconds=0 submission_wait_seconds=0
) )
# Successfully submitted and answered # Successfully submitted and answered
@@ -176,7 +176,7 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime.now(UTC), last_submission_time=datetime.datetime.now(ZoneInfo("UTC")),
submission_wait_seconds=123 submission_wait_seconds=123
) )
# You should get a dialog that tells you to wait # You should get a dialog that tells you to wait
@@ -189,9 +189,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 18, 36, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 18, 36, tzinfo=ZoneInfo("UTC"))
) )
# You should get a dialog that tells you to wait 2 minutes # You should get a dialog that tells you to wait 2 minutes
# Also, the number of attempts should not be incremented # Also, the number of attempts should not be incremented
@@ -203,9 +203,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 20, 35, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 20, 35, tzinfo=ZoneInfo("UTC"))
) )
# You should get a dialog that tells you to wait 2 minutes # You should get a dialog that tells you to wait 2 minutes
# Also, the number of attempts should not be incremented # Also, the number of attempts should not be incremented
@@ -217,9 +217,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 20, 36, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 20, 36, tzinfo=ZoneInfo("UTC"))
) )
# Successfully submitted and answered # Successfully submitted and answered
# Also, the number of attempts should increment by 1 # Also, the number of attempts should increment by 1
@@ -231,9 +231,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=ZoneInfo("UTC"))
) )
# Successfully submitted and answered # Successfully submitted and answered
# Also, the number of attempts should increment by 1 # Also, the number of attempts should increment by 1
@@ -247,17 +247,17 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
with pytest.raises(NotFoundError): with pytest.raises(NotFoundError):
(block, unused_result) = self.create_and_check( (block, unused_result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=ZoneInfo("UTC"))
) )
# Now try it without the submit_problem # Now try it without the submit_problem
(block, unused_result) = self.create_and_check( (block, unused_result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=180, submission_wait_seconds=180,
considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=UTC), considered_now=datetime.datetime(2013, 12, 6, 0, 24, 0, tzinfo=ZoneInfo("UTC")),
skip_submit_problem=True skip_submit_problem=True
) )
# Expect that number of attempts NOT incremented # Expect that number of attempts NOT incremented
@@ -268,9 +268,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=60 * 60 * 2, submission_wait_seconds=60 * 60 * 2,
considered_now=datetime.datetime(2013, 12, 6, 2, 15, 35, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 2, 15, 35, tzinfo=ZoneInfo("UTC"))
) )
# You should get a dialog that tells you to wait 2 minutes # You should get a dialog that tells you to wait 2 minutes
# Also, the number of attempts should not be incremented # Also, the number of attempts should not be incremented
@@ -282,9 +282,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=60 * 60 * 2 + 63, submission_wait_seconds=60 * 60 * 2 + 63,
considered_now=datetime.datetime(2013, 12, 6, 1, 15, 40, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 1, 15, 40, tzinfo=ZoneInfo("UTC"))
) )
# You should get a dialog that tells you to wait 2 minutes # You should get a dialog that tells you to wait 2 minutes
# Also, the number of attempts should not be incremented # Also, the number of attempts should not be incremented
@@ -296,9 +296,9 @@ class XModuleQuizAttemptsDelayTest(unittest.TestCase):
num_attempts = 1 num_attempts = 1
(block, result) = self.create_and_check( (block, result) = self.create_and_check(
num_attempts=num_attempts, num_attempts=num_attempts,
last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC), last_submission_time=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC")),
submission_wait_seconds=60, submission_wait_seconds=60,
considered_now=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=UTC) considered_now=datetime.datetime(2013, 12, 6, 0, 17, 36, tzinfo=ZoneInfo("UTC"))
) )
# You should get a dialog that tells you to wait 2 minutes # You should get a dialog that tells you to wait 2 minutes
# Also, the number of attempts should not be incremented # Also, the number of attempts should not be incremented

View File

@@ -9,11 +9,11 @@ from datetime import datetime, timedelta, tzinfo
from tempfile import mkdtemp from tempfile import mkdtemp
from textwrap import dedent from textwrap import dedent
from unittest import mock from unittest import mock
from zoneinfo import ZoneInfo
import pytest import pytest
import ddt import ddt
import lxml.etree import lxml.etree
import pytz
from django.utils.translation import gettext_lazy from django.utils.translation import gettext_lazy
from fs.osfs import OSFS from fs.osfs import OSFS
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
@@ -196,7 +196,7 @@ class TestEdxJsonEncoder(unittest.TestCase):
assert '2013-05-03T10:20:30' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30)) assert '2013-05-03T10:20:30' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30))
def test_encode_utc_datetime(self): def test_encode_utc_datetime(self):
assert '2013-05-03T10:20:30+00:00' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30, 0, pytz.UTC)) assert '2013-05-03T10:20:30+00:00' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30, 0, ZoneInfo("UTC")))
assert '2013-05-03T10:20:30+04:00' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30, 0, self.offset_tz)) assert '2013-05-03T10:20:30+04:00' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30, 0, self.offset_tz))

View File

@@ -4,6 +4,7 @@
import datetime import datetime
from tempfile import mkdtemp from tempfile import mkdtemp
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from zoneinfo import ZoneInfo
import ddt import ddt
from django.test import TestCase from django.test import TestCase
@@ -11,7 +12,6 @@ from fs.osfs import OSFS
from lxml import etree from lxml import etree
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from pytz import UTC
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import Date, Integer, Scope, String from xblock.fields import Date, Integer, Scope, String
from xblock.runtime import DictKeyValueStore, KvsFieldData from xblock.runtime import DictKeyValueStore, KvsFieldData
@@ -334,7 +334,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
assert child.due is None assert child.due is None
# Check that the child hasn't started yet # Check that the child hasn't started yet
assert datetime.datetime.now(UTC) <= child.start assert datetime.datetime.now(ZoneInfo("UTC")) <= child.start
def override_metadata_check(self, block, child, course_due, child_due): def override_metadata_check(self, block, child, course_due, child_due):
""" """

View File

@@ -5,8 +5,8 @@ import datetime
import textwrap import textwrap
import unittest import unittest
from unittest.mock import Mock from unittest.mock import Mock
from zoneinfo import ZoneInfo
from pytz import UTC
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xmodule.lti_2_util import LTIError from xmodule.lti_2_util import LTIError
@@ -380,7 +380,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
Test that we get a 404 when accept_grades_past_due is False and it is past due Test that we get a 404 when accept_grades_past_due is False and it is past due
""" """
self.setup_system_xblock_mocks_for_lti20_request_test() self.setup_system_xblock_mocks_for_lti20_request_test()
self.xblock.due = datetime.datetime.now(UTC) self.xblock.due = datetime.datetime.now(ZoneInfo("UTC"))
self.xblock.accept_grades_past_due = False self.xblock.accept_grades_past_due = False
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT) mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
response = self.xblock.lti_2_0_result_rest_handler(mock_request, "user/abcd") response = self.xblock.lti_2_0_result_rest_handler(mock_request, "user/abcd")

View File

@@ -6,6 +6,7 @@ import textwrap
from copy import copy from copy import copy
from unittest.mock import Mock, PropertyMock, patch from unittest.mock import Mock, PropertyMock, patch
from urllib import parse from urllib import parse
from zoneinfo import ZoneInfo
import pytest import pytest
@@ -14,7 +15,6 @@ from django.test import TestCase, override_settings
from lxml import etree from lxml import etree
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import BlockUsageLocator from opaque_keys.edx.locator import BlockUsageLocator
from pytz import UTC
from webob.request import Request from webob.request import Request
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds, Timedelta from xblock.fields import ScopeIds, Timedelta
@@ -198,7 +198,7 @@ class LTIBlockTest(TestCase):
Should fail if we do not accept past due grades, and it is past due. Should fail if we do not accept past due grades, and it is past due.
""" """
self.xblock.accept_grades_past_due = False self.xblock.accept_grades_past_due = False
self.xblock.due = datetime.datetime.now(UTC) self.xblock.due = datetime.datetime.now(ZoneInfo("UTC"))
self.xblock.graceperiod = Timedelta().from_json("0 seconds") self.xblock.graceperiod = Timedelta().from_json("0 seconds")
request = Request(self.environ) request = Request(self.environ)
request.body = self.get_request_body() request.body = self.get_request_body()

View File

@@ -9,9 +9,9 @@ import json
from collections import namedtuple from collections import namedtuple
from datetime import datetime, timedelta from datetime import datetime, timedelta
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from zoneinfo import ZoneInfo
import ddt import ddt
import pytz
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.test import override_settings from django.test import override_settings
from fs.memoryfs import MemoryFS from fs.memoryfs import MemoryFS
@@ -213,7 +213,7 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
Test the rendering of the student and public view. Test the rendering of the student and public view.
""" """
self.course.runtime._services['bookmarks'] = Mock() self.course.runtime._services['bookmarks'] = Mock()
now = datetime.now(pytz.UTC) now = datetime.now(ZoneInfo("UTC"))
self.vertical.due = now + timedelta(days=days) self.vertical.due = now + timedelta(days=days)
if view == STUDENT_VIEW: if view == STUDENT_VIEW:
self.course.runtime._services['user'] = StubUserService(user=Mock(username=self.username)) self.course.runtime._services['user'] = StubUserService(user=Mock(username=self.username))
@@ -253,7 +253,7 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
self.course.runtime._services['user'] = StubUserService(user=Mock()) self.course.runtime._services['user'] = StubUserService(user=Mock())
self.course.runtime._services['completion'] = StubCompletionService(enabled=True, completion_value=0) self.course.runtime._services['completion'] = StubCompletionService(enabled=True, completion_value=0)
now = datetime.now(pytz.UTC) now = datetime.now(ZoneInfo("UTC"))
self.vertical.due = now + timedelta(days=-1) self.vertical.due = now + timedelta(days=-1)
self.problem_block.has_score = has_score self.problem_block.has_score = has_score
@@ -273,7 +273,7 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
""" Tests access denied blocks are not rendered when hide_access_error_blocks is True """ """ Tests access denied blocks are not rendered when hide_access_error_blocks is True """
self.course.runtime._services['bookmarks'] = Mock() self.course.runtime._services['bookmarks'] = Mock()
self.course.runtime._services['user'] = StubUserService(user=Mock()) self.course.runtime._services['user'] = StubUserService(user=Mock())
self.vertical.due = datetime.now(pytz.UTC) + timedelta(days=-1) self.vertical.due = datetime.now(ZoneInfo("UTC")) + timedelta(days=-1)
self.problem_block.has_access_error = node_has_access_error self.problem_block.has_access_error = node_has_access_error
self.nested_problem_block.has_access_error = child_has_access_error self.nested_problem_block.has_access_error = child_has_access_error

View File

@@ -7,8 +7,8 @@ import logging
from copy import copy from copy import copy
from datetime import datetime from datetime import datetime
from functools import reduce from functools import reduce
from zoneinfo import ZoneInfo
import pytz
from django.conf import settings from django.conf import settings
from lxml import etree from lxml import etree
from openedx_filters.learning.filters import VerticalBlockChildRenderStarted, VerticalBlockRenderCompleted from openedx_filters.learning.filters import VerticalBlockChildRenderStarted, VerticalBlockRenderCompleted
@@ -137,7 +137,7 @@ class VerticalBlock(
}) })
completed = self.is_block_complete_for_assignments(completion_service) completed = self.is_block_complete_for_assignments(completion_service)
past_due = completed is False and self.due and self.due < datetime.now(pytz.UTC) past_due = completed is False and self.due and self.due < datetime.now(ZoneInfo("UTC"))
cta_service = self.runtime.service(self, 'call_to_action') cta_service = self.runtime.service(self, 'call_to_action')
vertical_banner_ctas = cta_service.get_ctas(self, 'vertical_banner', completed) if cta_service else [] vertical_banner_ctas = cta_service.get_ctas(self, 'vertical_banner', completed) if cta_service else []

View File

@@ -8,8 +8,8 @@ import json
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import pytz
from django.conf import settings from django.conf import settings
from .video_utils import set_query_parameter from .video_utils import set_query_parameter
@@ -48,7 +48,7 @@ def is_bumper_enabled(video):
bool. bool.
""" """
bumper_last_view_date = getattr(video, 'bumper_last_view_date', None) bumper_last_view_date = getattr(video, 'bumper_last_view_date', None)
utc_now = datetime.utcnow().replace(tzinfo=pytz.utc) utc_now = datetime.now(ZoneInfo("UTC"))
periodicity = settings.FEATURES.get('SHOW_BUMPER_PERIODICITY', 0) periodicity = settings.FEATURES.get('SHOW_BUMPER_PERIODICITY', 0)
has_viewed = any([ has_viewed = any([
video.bumper_do_not_show_again, video.bumper_do_not_show_again,