diff --git a/ietf/bin/iana-changes-updates b/ietf/bin/iana-changes-updates index 817f7e533..b0ea6712e 100755 --- a/ietf/bin/iana-changes-updates +++ b/ietf/bin/iana-changes-updates @@ -18,6 +18,7 @@ django.setup() from django.conf import settings from optparse import OptionParser +from zoneinfo import ZoneInfo parser = OptionParser() parser.add_option("-f", "--from", dest="start", @@ -38,13 +39,16 @@ CLOCK_SKEW_COMPENSATION = 5 # seconds MAX_INTERVAL_ACCEPTED_BY_IANA = datetime.timedelta(hours=23) +local_tzinfo = ZoneInfo(settings.TIME_ZONE) start = datetime.datetime.now() - datetime.timedelta(hours=23) + datetime.timedelta(seconds=CLOCK_SKEW_COMPENSATION) if options.start: start = datetime.datetime.strptime(options.start, "%Y-%m-%d %H:%M:%S") +start = start.replace(tzinfo=local_tzinfo).astimezone(datetime.timezone.utc) end = start + datetime.timedelta(hours=23) if options.end: - end = datetime.datetime.strptime(options.end, "%Y-%m-%d %H:%M:%S") + end = datetime.datetime.strptime(options.end, "%Y-%m-%d %H:%M:%S").replace(tzinfo=local_tzinfo) +end = end.astimezone(datetime.timezone.utc) syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) @@ -52,7 +56,13 @@ syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) from ietf.sync.iana import fetch_changes_json, parse_changes_json, update_history_with_changes -syslog.syslog("Updating history log with new changes from IANA from %s, period %s - %s" % (settings.IANA_SYNC_CHANGES_URL, start, end)) +syslog.syslog( + "Updating history log with new changes from IANA from %s, period %s - %s" % ( + settings.IANA_SYNC_CHANGES_URL, + start.astimezone(local_tzinfo), + end.astimezone(local_tzinfo), + ) +) t = start while t < end: diff --git a/ietf/sync/iana.py b/ietf/sync/iana.py index 22190fb36..978b49507 100644 --- a/ietf/sync/iana.py +++ b/ietf/sync/iana.py @@ -24,7 +24,6 @@ from ietf.doc.utils import add_state_change_event from ietf.person.models import Person from ietf.utils.log import log from ietf.utils.mail import parseaddr, get_payload_text -from ietf.utils.timezone import local_timezone_to_utc #PROTOCOLS_URL = "https://www.iana.org/protocols/" @@ -67,8 +66,8 @@ def update_rfc_log_from_protocol_page(rfc_names, rfc_must_published_later_than): def fetch_changes_json(url, start, end): - url += "?start=%s&end=%s" % (urlquote(local_timezone_to_utc(start).strftime("%Y-%m-%d %H:%M:%S")), - urlquote(local_timezone_to_utc(end).strftime("%Y-%m-%d %H:%M:%S"))) + url += "?start=%s&end=%s" % (urlquote(start.astimezone(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S")), + urlquote(end.astimezone(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S"))) # HTTP basic auth username = "ietfsync" password = settings.IANA_SYNC_PASSWORD diff --git a/ietf/utils/timezone.py b/ietf/utils/timezone.py index 149e471a3..19c4153a4 100644 --- a/ietf/utils/timezone.py +++ b/ietf/utils/timezone.py @@ -1,6 +1,6 @@ -import pytz import datetime +from typing import Union from zoneinfo import ZoneInfo from django.conf import settings @@ -19,60 +19,58 @@ DEADLINE_TZINFO = ZoneInfo('PST8PDT') RPC_TZINFO = ZoneInfo('PST8PDT') -def make_aware(dt, tzinfo): +def _tzinfo(tz: Union[str, datetime.tzinfo, None]): + """Helper to convert a tz param into a tzinfo + + Accepts Defaults to UTC. + """ + if tz is None: + return datetime.timezone.utc + elif isinstance(tz, datetime.tzinfo): + return tz + else: + return ZoneInfo(tz) + + +def make_aware(dt, tz): """Assign timezone to a naive datetime Helper to deal with both pytz and zoneinfo type time zones. Can go away when pytz is removed. """ + tzinfo = _tzinfo(tz) if hasattr(tzinfo, 'localize'): return tzinfo.localize(dt) # pytz-style else: return dt.replace(tzinfo=tzinfo) # zoneinfo- / datetime.timezone-style -def local_timezone_to_utc(d): - """Takes a naive datetime in the local timezone and returns a - naive datetime with the corresponding UTC time.""" - local_timezone = pytz.timezone(settings.TIME_ZONE) - - d = local_timezone.localize(d).astimezone(pytz.utc) - - return d.replace(tzinfo=None) - - -def datetime_from_date(date, tz=pytz.utc): +def datetime_from_date(date, tz=None): """Get datetime at midnight on a given date""" # accept either pytz or zoneinfo tzinfos until we get rid of pytz - return make_aware(datetime.datetime(date.year, date.month, date.day), tz) + return make_aware(datetime.datetime(date.year, date.month, date.day), _tzinfo(tz)) -def datetime_today(tzinfo=None): +def datetime_today(tz=None): """Get a timezone-aware datetime representing midnight today For use with datetime fields representing a date. """ - if tzinfo is None: - tzinfo = pytz.utc - return timezone.now().astimezone(tzinfo).replace(hour=0, minute=0, second=0, microsecond=0) + return timezone.now().astimezone(_tzinfo(tz)).replace(hour=0, minute=0, second=0, microsecond=0) -def date_today(tzinfo=None): +def date_today(tz=None): """Get the date corresponding to the current moment Note that Dates are not themselves timezone aware. """ - if tzinfo is None: - tzinfo = pytz.utc - return timezone.now().astimezone(tzinfo).date() + return timezone.now().astimezone(_tzinfo(tz)).date() -def time_now(tzinfo=None): +def time_now(tz=None): """Get the "wall clock" time corresponding to the current moment The value returned by this data is a Time with no tzinfo attached. (Time objects have only limited timezone support, even if tzinfo is filled in, and may not behave correctly when daylight savings time shifts are relevant.) """ - if tzinfo is None: - tzinfo = pytz.utc - return timezone.now().astimezone(tzinfo).time() + return timezone.now().astimezone(_tzinfo(tz)).time()