Verified Commit 220d86d2 authored by VAN ZUIJLEN Nils's avatar VAN ZUIJLEN Nils
Browse files

Fix timezone-related bug in RecurrentEvents

Use local timezone to fix the hour to a certain value

Fixes #71



Signed-off-by: VAN ZUIJLEN Nils's avatarNils VAN ZUIJLEN <nils.van-zuijlen@mailo.com>
parent 6767bded
from zoneinfo import ZoneInfo
from django.conf import settings
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from events.models import RecurrentEvent, Event
from django.utils import timezone from django.utils import timezone
import datetime import datetime
from events.models import RecurrentEvent
class Command(BaseCommand): class Command(BaseCommand):
help = 'Create events based on recurrent events.' help = "Create events based on recurrent events."
def handle(self, *args, **options): def handle(self, *args, **options) -> None:
for event in RecurrentEvent.objects.all(): """Create instances for recurrent events for at least the next 14 days"""
while True: for rec_event in RecurrentEvent.objects.all():
if event.last_created is None or event.last_created < timezone.now() + datetime.timedelta(days=14): while (
self.update_event(event) rec_event.last_created is None # Première création
else: or rec_event.last_created < timezone.now() + datetime.timedelta(days=14)
break ):
self.stdout.write('Successfully created events') # Tant que la dernière instance de l'évenement est
# dans moins de 14 jours
def update_event(self, event): # Créer une nouvelle instance
delta = datetime.timedelta(days=event.delay) self.update_rec_event(rec_event)
self.stdout.write("Successfully created events")
if event.last_created is None: # First creation
e = Event.objects.create(**{field: value for field, value in event.__dict__.items() if field in [field.column for field in Event._meta.fields if field.column not in ['id', 'model']]}) def update_rec_event(self, rec_event: RecurrentEvent) -> None:
delta = datetime.timedelta(days=rec_event.delay)
if rec_event.last_created is None: # First creation
e = rec_event.create_instance()
e.save() e.save()
event.last_created = e.start_time rec_event.last_created = e.start_time
event.save() rec_event.save()
self.stdout.write("First creation of %s, start = %s (delay=%d)" % (event, event.start_time, event.delay)) self.stdout.write(
f"First creation of {rec_event}, "
f"start = {rec_event.start_time} "
f"(delay={rec_event.delay})"
)
else: else:
delta_dates = event.last_created - event.start_time + delta delta_dates = rec_event.last_created - rec_event.start_time + delta
e = Event.objects.create(**{field: value for field, value in event.__dict__.items() if field in [field.column for field in Event._meta.fields if field.column not in ['id', 'model']]}) e = rec_event.create_instance()
e.start_time += delta_dates e.start_time += delta_dates
e.start_time = e.start_time.replace(hour=event.start_time.hour)
event.last_created = e.start_time
e.end_time += delta_dates e.end_time += delta_dates
e.end_time = e.end_time.replace(hour=event.end_time.hour)
e.end_inscriptions += delta_dates e.end_inscriptions += delta_dates
e.end_inscriptions = e.end_inscriptions.replace(hour=event.end_inscriptions.hour)
if e.invitations_start is not None: if e.invitations_start is not None:
e.invitations_start += delta_dates e.invitations_start += delta_dates
e.invitations_start = e.invitations_start.replace(hour=event.invitations_start.hour)
if settings.USE_TZ:
# If we use timezones, fix the hours in the local timezone
localtz = ZoneInfo(settings.TIME_ZONE)
e.start_time = e.start_time.astimezone(localtz).replace(
hour=rec_event.start_time.astimezone(localtz).hour
)
e.end_time = e.end_time.astimezone(localtz).replace(
hour=rec_event.end_time.astimezone(localtz).hour
)
e.end_inscriptions = e.end_inscriptions.astimezone(localtz).replace(
hour=rec_event.end_inscriptions.astimezone(localtz).hour
)
if e.invitations_start is not None:
e.invitations_start = e.invitations_start.astimezone(
localtz
).replace(hour=rec_event.invitations_start.astimezone(localtz).hour)
e.save() e.save()
event.save() rec_event.last_created = e.start_time
self.stdout.write("Creating event %s for date %s" % (event, event.last_created)) rec_event.save()
self.stdout.write(
f"Creating event {rec_event} for date {rec_event.last_created}"
)
...@@ -126,7 +126,7 @@ class Event(models.Model): ...@@ -126,7 +126,7 @@ class Event(models.Model):
class RecurrentEvent(Event): class RecurrentEvent(Event):
delay = models.IntegerField(default=1) delay = models.IntegerField(default=1) # In days
last_created = models.DateTimeField(null=True, blank=True, default=None) last_created = models.DateTimeField(null=True, blank=True, default=None)
class Meta: class Meta:
...@@ -134,6 +134,21 @@ class RecurrentEvent(Event): ...@@ -134,6 +134,21 @@ class RecurrentEvent(Event):
('manage_recurrent_event', 'Can manage recurrent event (add/del/edit'), ('manage_recurrent_event', 'Can manage recurrent event (add/del/edit'),
) )
def create_instance(self) -> Event:
"""Create an Event instance of the RecurrentEvent"""
return Event.objects.create(
**{
field: value
for field, value in self.__dict__.items()
if field
in { # Use sets for faster `in` checks
field.column
for field in Event._meta.fields
if field.column not in {"id", "model"}
}
}
)
class ExternLink(models.Model): class ExternLink(models.Model):
event = models.ForeignKey(Event, related_name="extern_links", on_delete=models.CASCADE) event = models.ForeignKey(Event, related_name="extern_links", on_delete=models.CASCADE)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment