Skip to content
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a02ba98
chore!: replace pytz with zoneinfo
ttak-apphelix Aug 25, 2025
e63caf6
fix: update query count in credit requirement API
ttak-apphelix Aug 25, 2025
dacbe28
Merge branch 'openedx:master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Aug 25, 2025
8f00d36
fix: update audience query count in email digest tests
ttak-apphelix Aug 25, 2025
4497b44
fix: update error response detail format
ttak-apphelix Aug 25, 2025
69d5133
Merge remote-tracking branch 'upstream/master' into ttak-apphelix/rem…
ttak-apphelix Sep 23, 2025
1428d81
fix: resolve merge conflict in saml.py
ttak-apphelix Sep 23, 2025
f158196
fix: replace pytz with get_utc_timezone
ttak-apphelix Sep 24, 2025
e3c555e
Merge branch 'master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Sep 24, 2025
75335ab
feat: add timezone utility functions with toggle support for ZoneInfo…
ttak-apphelix Sep 24, 2025
08e79b6
fix: format code for consistency in test_time_zone_utils
ttak-apphelix Sep 24, 2025
f81d754
fix: format code
ttak-apphelix Sep 24, 2025
f70029a
fix: update imports and use get_common_timezones in tests
ttak-apphelix Sep 25, 2025
58a7adb
Merge branch 'master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Sep 30, 2025
7555d8d
Merge branch 'master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Oct 8, 2025
4b0c1ba
fix: updated timezone utilities
ttak-apphelix Oct 9, 2025
bcbfba4
fix: clean up whitespace in time zone utility tests
ttak-apphelix Oct 9, 2025
a6a565d
fix: clean up whitespace in time zone utility tests
ttak-apphelix Oct 9, 2025
ce5675c
fix: clean up whitespace in time zone utility tests
ttak-apphelix Oct 9, 2025
bd30c7c
Merge branch 'master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Oct 9, 2025
0767b67
Merge branch 'master' into ttak-apphelix/remove-pytz-part1
ttak-apphelix Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/bookmarks/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from unittest import mock

import ddt
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from freezegun import freeze_time
from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
Expand Down Expand Up @@ -352,7 +352,7 @@ def test_path(self, seconds_delta, paths, get_path_call_count, mock_get_path):
bookmark, __ = Bookmark.create(bookmark_data)
assert bookmark.xblock_cache is not None

modification_datetime = datetime.datetime.now(pytz.utc) + datetime.timedelta(seconds=seconds_delta)
modification_datetime = datetime.datetime.now(get_utc_timezone()) + datetime.timedelta(seconds=seconds_delta)
with freeze_time(modification_datetime):
bookmark.xblock_cache.paths = paths
bookmark.xblock_cache.save()
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/catalog/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from edx_rest_api_client.auth import SuppliedJwtAuth
from edx_rest_api_client.client import USER_AGENT
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from openedx.core.lib.time_zone_utils import get_utc_timezone

from common.djangoapps.entitlements.utils import is_course_run_entitlement_fulfillable
from common.djangoapps.student.models import CourseEnrollment
Expand Down Expand Up @@ -593,7 +593,7 @@ def get_fulfillable_course_runs_for_entitlement(entitlement, course_runs):
enrollable_sessions = []

# Only retrieve list of published course runs that can still be enrolled and upgraded
search_time = datetime.datetime.now(UTC)
search_time = datetime.datetime.now(get_utc_timezone())
for course_run in course_runs:
course_id = CourseKey.from_string(course_run.get("key"))
(user_enrollment_mode, is_active) = CourseEnrollment.enrollment_mode_for_user(
Expand Down
6 changes: 3 additions & 3 deletions openedx/core/djangoapps/ccxcon/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from urllib import parse

import pytest
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
Expand Down Expand Up @@ -44,10 +44,10 @@ def setUpClass(cls):

# Create a course outline
start = datetime.datetime(
2010, 5, 12, 2, 42, tzinfo=pytz.UTC
2010, 5, 12, 2, 42, tzinfo=get_utc_timezone()
)
due = datetime.datetime(
2010, 7, 7, 0, 0, tzinfo=pytz.UTC
2010, 7, 7, 0, 0, tzinfo=get_utc_timezone()
)

cls.chapters = [
Expand Down
8 changes: 4 additions & 4 deletions openedx/core/djangoapps/content/course_overviews/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import datetime
from urllib.parse import urlparse, urlunparse

import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from ccx_keys.locator import CCXLocator
from config_models.models import ConfigurationModel
from django.conf import settings
Expand Down Expand Up @@ -705,7 +705,7 @@ def get_all_courses(cls, orgs=None, filter_=None, active_only=False, course_keys
course_overviews = course_overviews.filter(**filter_)
if active_only:
course_overviews = course_overviews.filter(
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=pytz.UTC))
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=get_utc_timezone()))
)

return course_overviews
Expand Down Expand Up @@ -737,11 +737,11 @@ def get_courses_by_status(cls, active_only, archived_only, course_overviews):
"""
if active_only:
return course_overviews.filter(
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=pytz.UTC))
Q(end__isnull=True) | Q(end__gte=datetime.now().replace(tzinfo=get_utc_timezone()))
)
if archived_only:
return course_overviews.filter(
end__lt=datetime.now().replace(tzinfo=pytz.UTC)
end__lt=datetime.now().replace(tzinfo=get_utc_timezone())
)
return course_overviews

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import itertools # lint-amnesty, pylint: disable=wrong-import-order
import math # lint-amnesty, pylint: disable=wrong-import-order
import ddt
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.conf import settings
from django.db.utils import IntegrityError
from django.test.utils import override_settings
Expand Down Expand Up @@ -93,7 +93,7 @@ def get_seconds_since_epoch(date_time):
"""
if date_time is None:
return None
epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
epoch = datetime.datetime.fromtimestamp(0, tz=get_utc_timezone())
return math.floor((date_time - epoch).total_seconds())

# Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pytest
import ddt
from pytz import UTC
from openedx.core.lib.time_zone_utils import get_utc_timezone

from xmodule.data import CertificatesDisplayBehaviors
from xmodule.modulestore import ModuleStoreEnum
Expand All @@ -29,7 +29,7 @@ class CourseOverviewSignalsTestCase(ModuleStoreTestCase):
"""
MODULESTORE = TEST_DATA_ONLY_SPLIT_MODULESTORE_DRAFT_PREFERRED
ENABLED_SIGNALS = ['course_deleted', 'course_published']
TODAY = datetime.datetime.utcnow().replace(tzinfo=UTC)
TODAY = datetime.datetime.utcnow().replace(tzinfo=get_utc_timezone())
NEXT_WEEK = TODAY + datetime.timedelta(days=7)

def assert_changed_signal_sent(self, changes, mock_signal):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from django.core.management.base import BaseCommand, CommandError
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from pytz import UTC
from openedx.core.lib.time_zone_utils import get_utc_timezone

from openedx.core.djangoapps.credentials.models import NotifyCredentialsConfig
from openedx.core.djangoapps.credentials.tasks.v1.tasks import handle_notify_credentials
Expand All @@ -32,7 +32,7 @@
def parsetime(timestr):
dt = dateutil.parser.parse(timestr)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=UTC)
dt = dt.replace(tzinfo=get_utc_timezone())
return dt


Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/credit/api/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import logging
import uuid

import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.db import transaction
from edx_proctoring.api import get_last_exam_completion_date

Expand Down Expand Up @@ -296,7 +296,7 @@ def create_credit_request(course_key, provider_id, username):

parameters = {
"request_uuid": credit_request.uuid,
"timestamp": to_timestamp(datetime.datetime.now(pytz.UTC)),
"timestamp": to_timestamp(datetime.datetime.now(get_utc_timezone())),
"course_org": course_key.org,
"course_num": course_key.course,
"course_run": course_key.run,
Expand Down
8 changes: 4 additions & 4 deletions openedx/core/djangoapps/credit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import logging
from collections import defaultdict

import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from config_models.models import ConfigurationModel
from django.conf import settings
from django.core.cache import cache
Expand Down Expand Up @@ -536,7 +536,7 @@ def default_deadline_for_credit_eligibility():
"""
The default deadline to use when creating a new CreditEligibility model.
"""
return datetime.datetime.now(pytz.UTC) + datetime.timedelta(
return datetime.datetime.now(get_utc_timezone()) + datetime.timedelta(
days=getattr(settings, "CREDIT_ELIGIBILITY_EXPIRATION_DAYS", 365)
)

Expand Down Expand Up @@ -617,7 +617,7 @@ def get_user_eligibilities(cls, username):
return cls.objects.filter(
username=username,
course__enabled=True,
deadline__gt=datetime.datetime.now(pytz.UTC)
deadline__gt=datetime.datetime.now(get_utc_timezone())
).select_related('course')

@classmethod
Expand All @@ -636,7 +636,7 @@ def is_user_eligible_for_credit(cls, course_key, username):
course__course_key=course_key,
course__enabled=True,
username=username,
deadline__gt=datetime.datetime.now(pytz.UTC),
deadline__gt=datetime.datetime.now(get_utc_timezone()),
).exists()

def __str__(self):
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/credit/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import datetime
import logging

import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.conf import settings
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied
Expand Down Expand Up @@ -78,7 +78,7 @@ def validate_timestamp(self, value):
log.warning(msg)
raise serializers.ValidationError(msg)

elapsed = (datetime.datetime.now(pytz.UTC) - date_time).total_seconds()
elapsed = (datetime.datetime.now(get_utc_timezone()) - date_time).total_seconds()
if elapsed > settings.CREDIT_PROVIDER_TIMESTAMP_EXPIRATION:
msg = f'[{value}] is too far in the past (over [{elapsed}] seconds).'
log.warning(msg)
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/credit/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import factory
from factory.fuzzy import FuzzyText
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user

from openedx.core.djangoapps.credit.models import (
Expand Down Expand Up @@ -80,7 +80,7 @@ def post(obj, create, extracted, **kwargs):

obj.parameters = json.dumps({
"request_uuid": obj.uuid,
"timestamp": to_timestamp(datetime.datetime.now(pytz.UTC)),
"timestamp": to_timestamp(datetime.datetime.now(get_utc_timezone())),
"course_org": course_key.org,
"course_num": course_key.course,
"course_run": course_key.run,
Expand Down
8 changes: 4 additions & 4 deletions openedx/core/djangoapps/credit/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pytest
import ddt
import httpretty
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core import mail
from django.db import connection
Expand Down Expand Up @@ -400,7 +400,7 @@ def test_eligibility_expired(self):
CreditEligibility.objects.create(
course=credit_course,
username="staff",
deadline=datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=1)
deadline=datetime.datetime.now(get_utc_timezone()) - datetime.timedelta(days=1)
)

# The user should NOT be eligible for credit
Expand Down Expand Up @@ -651,7 +651,7 @@ def test_satisfy_all_requirements(self):
api.set_credit_requirements(self.course_key, requirements)

# Satisfy one of the requirements, but not the other
with self.assertNumQueries(11):
with self.assertNumQueries(12):
api.set_credit_requirement_status(
user,
self.course_key,
Expand Down Expand Up @@ -960,7 +960,7 @@ def test_credit_request(self):
# Validate the timestamp
assert 'timestamp' in parameters
parsed_date = from_timestamp(parameters['timestamp'])
assert parsed_date < datetime.datetime.now(pytz.UTC)
assert parsed_date < datetime.datetime.now(get_utc_timezone())

# Validate course information
assert parameters['course_org'] == self.course_key.org
Expand Down
6 changes: 3 additions & 3 deletions openedx/core/djangoapps/credit/tests/test_signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from uuid import uuid4

import ddt
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.test.client import RequestFactory
from opaque_keys.edx.keys import UsageKey
from openedx_events.data import EventsMetadata
Expand Down Expand Up @@ -47,8 +47,8 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
satisfied. But if student grade is less than and deadline is passed then
user will be marked as failed.
"""
VALID_DUE_DATE = datetime.now(pytz.UTC) + timedelta(days=20)
EXPIRED_DUE_DATE = datetime.now(pytz.UTC) - timedelta(days=20)
VALID_DUE_DATE = datetime.now(get_utc_timezone()) + timedelta(days=20)
EXPIRED_DUE_DATE = datetime.now(get_utc_timezone()) - timedelta(days=20)

DATES = {
'valid': VALID_DUE_DATE,
Expand Down
8 changes: 4 additions & 4 deletions openedx/core/djangoapps/credit/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import json

import ddt
import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.conf import settings
from django.test import Client, TestCase
from django.test.utils import override_settings
Expand Down Expand Up @@ -523,7 +523,7 @@ def _credit_provider_callback(self, request_uuid, status, **kwargs):
"""
provider_id = kwargs.get('provider_id', self.provider.provider_id)
secret_key = kwargs.get('secret_key', '931433d583c84ca7ba41784bad3232e6')
timestamp = kwargs.get('timestamp', to_timestamp(datetime.datetime.now(pytz.UTC)))
timestamp = kwargs.get('timestamp', to_timestamp(datetime.datetime.now(get_utc_timezone())))
keys = kwargs.get('keys', {self.provider.provider_id: secret_key})

url = reverse('credit:provider_callback', args=[provider_id])
Expand Down Expand Up @@ -577,15 +577,15 @@ def test_post_with_invalid_timestamp(self, timedelta):
if timedelta == 'invalid':
timestamp = timedelta
else:
timestamp = to_timestamp(datetime.datetime.now(pytz.UTC) + timedelta)
timestamp = to_timestamp(datetime.datetime.now(get_utc_timezone()) + timedelta)
request_uuid = self._create_credit_request_and_get_uuid()
response = self._credit_provider_callback(request_uuid, 'approved', timestamp=timestamp)
assert response.status_code == 400

def test_post_with_string_timestamp(self):
""" Verify the endpoint supports timestamps transmitted as strings instead of integers. """
request_uuid = self._create_credit_request_and_get_uuid()
timestamp = str(to_timestamp(datetime.datetime.now(pytz.UTC)))
timestamp = str(to_timestamp(datetime.datetime.now(get_utc_timezone())))
response = self._credit_provider_callback(request_uuid, 'approved', timestamp=timestamp)
assert response.status_code == 200

Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/credit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import datetime
import logging

import pytz
from openedx.core.lib.time_zone_utils import get_utc_timezone
from django.conf import settings
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
Expand Down Expand Up @@ -166,7 +166,7 @@ def filter_queryset(self, queryset):
return queryset.filter(
username=username,
course__course_key=course_key,
deadline__gt=datetime.datetime.now(pytz.UTC)
deadline__gt=datetime.datetime.now(get_utc_timezone())
)


Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/enrollments/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import ddt
import pytest
from pytz import UTC
from openedx.core.lib.time_zone_utils import get_utc_timezone

from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
Expand Down Expand Up @@ -369,7 +369,7 @@ def test_get_course_without_expired_mode_included(self):
def _update_verified_mode_as_expired(self, course_id):
"""Dry method to change verified mode expiration."""
mode = CourseMode.objects.get(course_id=course_id, mode_slug=CourseMode.VERIFIED)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=UTC)
mode.expiration_datetime = datetime.datetime(year=1970, month=1, day=1, tzinfo=get_utc_timezone())
mode.save()

def assert_enrollment_modes(self, expected_modes, include_expired):
Expand Down
Loading
Loading