diff --git a/bin/check-copyright b/bin/check-copyright index 6698e3fda..13cbcd858 100755 --- a/bin/check-copyright +++ b/bin/check-copyright @@ -162,7 +162,7 @@ def get_first_commit(path): else: pass except OSError: - rev, who, when = None, None, datetime.datetime.now() + rev, who, when = None, None, datetime.datetime.now(datetime.timezone.utc) return { path: { 'rev': rev, 'who': who, 'date': when.strftime('%Y-%m-%d %H:%M:%S'), }, } diff --git a/ietf/bin/send-review-reminders b/ietf/bin/send-review-reminders index e74694c8a..317db4376 100755 --- a/ietf/bin/send-review-reminders +++ b/ietf/bin/send-review-reminders @@ -26,8 +26,9 @@ from ietf.review.utils import ( send_unavailability_period_ending_reminder, send_reminder_all_open_reviews, send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments) from ietf.utils.log import log +from ietf.utils.timezone import date_today, DEADLINE_TZINFO -today = datetime.date.today() +today = date_today(DEADLINE_TZINFO) for assignment in review_assignments_needing_reviewer_reminder(today): email_reviewer_reminder(assignment) diff --git a/ietf/doc/expire.py b/ietf/doc/expire.py index a2a4a714f..af48827cf 100644 --- a/ietf/doc/expire.py +++ b/ietf/doc/expire.py @@ -18,7 +18,7 @@ from ietf.person.models import Person from ietf.meeting.models import Meeting from ietf.doc.utils import add_state_change_event, update_action_holders from ietf.mailtrigger.utils import gather_address_lists -from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO +from ietf.utils.timezone import date_today, datetime_today, DEADLINE_TZINFO nonexpirable_states: Optional[List[State]] = None @@ -173,7 +173,7 @@ def expire_draft(doc): def clean_up_draft_files(): """Move unidentified and old files out of the Internet Draft directory.""" - cut_off = datetime.date.today() + cut_off = date_today() pattern = os.path.join(settings.INTERNET_DRAFT_PATH, "draft-*.*") filename_re = re.compile(r'^(.*)-(\d\d)$') diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index d209eb0a2..372c43ab0 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -15,6 +15,7 @@ from ietf.person.fields import SearchablePersonField, SearchablePersonsField from ietf.person.models import Email, Person from ietf.name.models import ExtResourceName +from ietf.utils.timezone import date_today from ietf.utils.validators import validate_external_resource_value class TelechatForm(forms.Form): @@ -34,7 +35,7 @@ class TelechatForm(forms.Form): for d in dates: self.page_count[d] = telechat_page_count(date=d).for_approval choice_display[d] = '%s (%s pages)' % (d.strftime("%Y-%m-%d"),self.page_count[d]) - if d-datetime.date.today() < datetime.timedelta(days=13): + if d - date_today() < datetime.timedelta(days=13): choice_display[d] += ' : WARNING - this may not leave enough time for directorate reviews!' self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, choice_display[d]) for d in dates] diff --git a/ietf/doc/lastcall.py b/ietf/doc/lastcall.py index fab3001b1..852e1c98c 100644 --- a/ietf/doc/lastcall.py +++ b/ietf/doc/lastcall.py @@ -10,6 +10,8 @@ from ietf.person.models import Person from ietf.doc.utils import add_state_change_event, update_action_holders from ietf.doc.mails import generate_ballot_writeup, generate_approval_mail, generate_last_call_announcement from ietf.doc.mails import send_last_call_request, email_last_call_expired, email_last_call_expired_with_downref +from ietf.utils.timezone import date_today, DEADLINE_TZINFO + def request_last_call(request, doc): if not doc.latest_event(type="changed_ballot_writeup_text"): @@ -33,7 +35,7 @@ def request_last_call(request, doc): e.save() def get_expired_last_calls(): - today = datetime.date.today() + today = date_today(DEADLINE_TZINFO) for d in Document.objects.filter(Q(states__type="draft-iesg", states__slug="lc") | Q(states__type="statchg", states__slug="in-lc")): e = d.latest_event(LastCallDocEvent, type="sent_last_call") diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index ddc669ed8..54e0f47e2 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -25,6 +25,7 @@ from ietf.doc.utils_bofreq import bofreq_editors, bofreq_responsible from ietf.group.models import Role from ietf.doc.models import Document from ietf.mailtrigger.utils import gather_address_lists +from ietf.utils.timezone import date_today, DEADLINE_TZINFO def email_state_changed(request, doc, text, mailtrigger_id=None): @@ -192,7 +193,7 @@ def generate_ballot_rfceditornote(request, doc): return e def generate_last_call_announcement(request, doc): - expiration_date = datetime.date.today() + datetime.timedelta(days=14) + expiration_date = date_today(DEADLINE_TZINFO) + datetime.timedelta(days=14) if doc.group.type_id in ("individ", "area"): group = "an individual submitter" expiration_date += datetime.timedelta(days=14) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 56d03adca..84957e9a8 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -36,7 +36,7 @@ from ietf.utils.decorators import memoize from ietf.utils.validators import validate_no_control_chars from ietf.utils.mail import formataddr from ietf.utils.models import ForeignKey -from ietf.utils.timezone import RPC_TZINFO +from ietf.utils.timezone import date_today, RPC_TZINFO if TYPE_CHECKING: # importing other than for type checking causes errors due to cyclic imports from ietf.meeting.models import ProceedingsMaterial, Session @@ -831,16 +831,20 @@ class Document(DocumentInfo): def telechat_date(self, e=None): if not e: e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat") - return e.telechat_date if e and e.telechat_date and e.telechat_date >= datetime.date.today() else None + return e.telechat_date if e and e.telechat_date and e.telechat_date >= date_today(settings.TIME_ZONE) else None def past_telechat_date(self): "Return the latest telechat date if it isn't in the future; else None" e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat") - return e.telechat_date if e and e.telechat_date and e.telechat_date < datetime.date.today() else None + return e.telechat_date if e and e.telechat_date and e.telechat_date < date_today(settings.TIME_ZONE) else None def previous_telechat_date(self): "Return the most recent telechat date in the past, if any (even if there's another in the future)" - e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat", telechat_date__lt=timezone.now()) + e = self.latest_event( + TelechatDocEvent, + type="scheduled_for_telechat", + telechat_date__lt=date_today(settings.TIME_ZONE), + ) return e.telechat_date if e else None def request_closed_time(self, review_req): @@ -906,8 +910,14 @@ class Document(DocumentInfo): def future_presentations(self): """ returns related SessionPresentation objects for meetings that have not yet ended. This implementation allows for 2 week meetings """ - candidate_presentations = self.sessionpresentation_set.filter(session__meeting__date__gte=datetime.date.today()-datetime.timedelta(days=15)) - return sorted([pres for pres in candidate_presentations if pres.session.meeting.end_date()>=datetime.date.today()], key=lambda x:x.session.meeting.date) + candidate_presentations = self.sessionpresentation_set.filter( + session__meeting__date__gte=date_today() - datetime.timedelta(days=15) + ) + return sorted( + [pres for pres in candidate_presentations + if pres.session.meeting.end_date() >= date_today()], + key=lambda x:x.session.meeting.date, + ) def last_presented(self): """ returns related SessionPresentation objects for the most recent meeting in the past""" diff --git a/ietf/doc/templatetags/ballot_icon.py b/ietf/doc/templatetags/ballot_icon.py index 6d3ddba44..0de81f2e1 100644 --- a/ietf/doc/templatetags/ballot_icon.py +++ b/ietf/doc/templatetags/ballot_icon.py @@ -38,6 +38,7 @@ import debug # pyflakes:ignore from django import template from django.urls import reverse as urlreverse from django.db.models import Q +from django.utils import timezone from django.utils.safestring import mark_safe from ietf.ietfauth.utils import user_is_person, has_role @@ -173,17 +174,17 @@ def state_age_colored(doc): if iesg_state in ["dead", "watching", "pub", "idexists"]: return "" try: - state_date = ( + state_datetime = ( doc.docevent_set.filter( Q(type="started_iesg_process") | Q(type="changed_state", statedocevent__state_type="draft-iesg") ) .order_by("-time")[0] - .time.date() + .time ) except IndexError: - state_date = datetime.date(1990, 1, 1) - days = (datetime.date.today() - state_date).days + state_datetime = datetime.datetime(1990, 1, 1, tzinfo=datetime.timezone.utc) + days = (timezone.now() - state_datetime).days # loosely based on # https://trac.ietf.org/trac/iesg/wiki/PublishPath if iesg_state == "lc": diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 4c769b0d8..66f84f630 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2226,7 +2226,7 @@ class DocumentMeetingTests(TestCase): self.other_chair = PersonFactory() self.other_group.role_set.create(name_id='chair',person=self.other_chair,email=self.other_chair.email()) - today = datetime.date.today() + today = datetime_today() cut_days = settings.MEETING_MATERIALS_DEFAULT_SUBMISSION_CORRECTION_DAYS self.past_cutoff = SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=1+cut_days)) self.past = SessionFactory.create(meeting__type_id='ietf',group=self.group,meeting__date=today-datetime.timedelta(days=cut_days/2)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index a4cb02f11..92883e048 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -640,7 +640,7 @@ class ExpireIDsTests(DraftFileMixin, TestCase): meeting = Meeting.objects.create(number="123", type=MeetingTypeName.objects.get(slug="ietf"), - date=datetime.date.today()) + date=date_today()) second_cut_off = meeting.get_second_cut_off() ietf_monday = meeting.get_ietf_monday() diff --git a/ietf/doc/tests_irsg_ballot.py b/ietf/doc/tests_irsg_ballot.py index 97074197a..1a0f07e8e 100644 --- a/ietf/doc/tests_irsg_ballot.py +++ b/ietf/doc/tests_irsg_ballot.py @@ -324,7 +324,7 @@ class BaseManipulationTests(): def test_issue_ballot(self): draft = RgDraftFactory() url = urlreverse('ietf.doc.views_ballot.issue_irsg_ballot',kwargs=dict(name=draft.name)) - due = datetime.date.today()+datetime.timedelta(days=14) + due = datetime_today(DEADLINE_TZINFO)+datetime.timedelta(days=14) empty_outbox() login_testing_unauthorized(self, self.username , url) @@ -445,7 +445,7 @@ class IRSGMemberTests(TestCase): def test_cant_issue_irsg_ballot(self): draft = RgDraftFactory() - due = datetime.date.today()+datetime.timedelta(days=14) + due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=14) url = urlreverse('ietf.doc.views_ballot.close_irsg_ballot', kwargs=dict(name=draft.name)) self.client.login(username = self.username, password = self.username+'+password') diff --git a/ietf/doc/tests_review.py b/ietf/doc/tests_review.py index 6ff804af3..cdfb64aed 100644 --- a/ietf/doc/tests_review.py +++ b/ietf/doc/tests_review.py @@ -38,7 +38,7 @@ from ietf.utils.mail import outbox, empty_outbox, parseaddr, on_behalf_of, get_p from ietf.utils.test_utils import login_testing_unauthorized, reload_db_objects from ietf.utils.test_utils import TestCase from ietf.utils.text import strip_prefix, xslugify -from ietf.utils.timezone import DEADLINE_TZINFO +from ietf.utils.timezone import date_today, DEADLINE_TZINFO from django.utils.html import escape class ReviewTests(TestCase): @@ -78,7 +78,7 @@ class ReviewTests(TestCase): r = self.client.get(url) self.assertEqual(r.status_code, 200) - deadline = datetime.date.today() + datetime.timedelta(days=10) + deadline = date_today() + datetime.timedelta(days=10) empty_outbox() @@ -268,7 +268,7 @@ class ReviewTests(TestCase): team=review_req.team, state_id='assigned', requested_rev="01", - deadline=datetime.date.today() - datetime.timedelta(days=80), + deadline=date_today() - datetime.timedelta(days=80), ) ReviewAssignmentFactory( review_request = req, @@ -526,7 +526,7 @@ class ReviewTests(TestCase): messages = r.json()["messages"] self.assertEqual(len(messages), 2) - today = datetime.date.today() + today = date_today() self.assertEqual(messages[0]["url"], "https://www.example.com/testmessage") self.assertTrue("John Doe" in messages[0]["content"]) diff --git a/ietf/doc/utils_charter.py b/ietf/doc/utils_charter.py index 43982deab..d14684d42 100644 --- a/ietf/doc/utils_charter.py +++ b/ietf/doc/utils_charter.py @@ -25,6 +25,8 @@ from ietf.utils.mail import parse_preformatted from ietf.mailtrigger.utils import gather_address_lists from ietf.utils.log import log from ietf.group.utils import save_group_in_history +from ietf.utils.timezone import date_today + def charter_name_for_group(group): if group.type_id == "rg": @@ -216,7 +218,7 @@ def default_review_text(group, charter, by): parent_ads=group.parent.role_set.filter(name='ad'), techadv=group.role_set.filter(name="techadv"), milestones=group.groupmilestone_set.filter(state="charter"), - review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(), + review_date=(date_today() + datetime.timedelta(weeks=1)).isoformat(), review_type="new" if group.state_id in ["proposed","bof"] else "recharter", to=addrs.to, cc=addrs.cc, diff --git a/ietf/doc/views_ballot.py b/ietf/doc/views_ballot.py index bd6c67737..207b8b972 100644 --- a/ietf/doc/views_ballot.py +++ b/ietf/doc/views_ballot.py @@ -1084,7 +1084,7 @@ def make_last_call(request, name): return HttpResponseRedirect(doc.get_absolute_url()) else: initial = {} - initial["last_call_sent_date"] = datetime.date.today() + initial["last_call_sent_date"] = date_today() if doc.type.slug == 'draft': # This logic is repeated in the code that edits last call text - why? expire_days = 14 @@ -1095,7 +1095,7 @@ def make_last_call(request, name): expire_days=28 templ = 'doc/status_change/make_last_call.html' - initial["last_call_expiration_date"] = datetime.date.today() + datetime.timedelta(days=expire_days) + initial["last_call_expiration_date"] = date_today() + datetime.timedelta(days=expire_days) form = MakeLastCallForm(initial=initial) @@ -1192,7 +1192,10 @@ def irsg_ballot_status(request): ballot = doc.active_ballot() if ballot: doc.ballot = ballot - doc.duedate=datetime.datetime.strftime(ballot.irsgballotdocevent.duedate, '%Y-%m-%d') + doc.duedate=datetime.datetime.strftime( + ballot.irsgballotdocevent.duedate.astimezone(DEADLINE_TZINFO), + '%Y-%m-%d', + ) docs.append(doc) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 926b06ac4..2831d2e81 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -84,6 +84,7 @@ from ietf.utils import markup_txt, log, markdown from ietf.utils.draft import PlaintextDraft from ietf.utils.response import permission_denied from ietf.utils.text import maybe_split +from ietf.utils.timezone import date_today def render_document_top(request, doc, tab, name): @@ -185,7 +186,7 @@ def document_main(request, name, rev=None): telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat") - if telechat and (not telechat.telechat_date or telechat.telechat_date < datetime.date.today()): + if telechat and (not telechat.telechat_date or telechat.telechat_date < date_today(settings.TIME_ZONE)): telechat = None @@ -1386,11 +1387,12 @@ def telechat_date(request, name): warnings = [] if e and e.telechat_date and doc.type.slug != 'charter': - if e.telechat_date==datetime.date.today(): + today = date_today(settings.TIME_ZONE) + if e.telechat_date == today: warnings.append( "This document is currently scheduled for today's telechat. " +"Please set the returning item bit carefully.") - elif e.telechat_date