[FIX] calendar: recurring event around DST
Be in timezone Europe/Brussels, where, in 2018, the DST switch happens on 2018-10-28. Create a recurring event starting on 2018-10-27 until 2018-10-29 at say, 8am For the days before the 28, the time of the event will be correct Before this commit though, for the days after the 28, the meeting time will be shifted by one hour This is because the rrule took a TZ aware datetime, and generated recurring event dates with that same TZ, i.e. the one in STD time After, this commit, all the meetings are well scheduled at 8am, OPW 1895315 closes odoo/odoo#27985
This commit is contained in:
@@ -609,13 +609,16 @@ class Meeting(models.Model):
|
||||
event_date = datetime.now()
|
||||
|
||||
use_naive_datetime = self.allday and self.rrule and 'UNTIL' in self.rrule and 'Z' not in self.rrule
|
||||
if use_naive_datetime:
|
||||
rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True)
|
||||
else:
|
||||
if not use_naive_datetime:
|
||||
# Convert the event date to saved timezone (or context tz) as it'll
|
||||
# define the correct hour/day asked by the user to repeat for recurrence.
|
||||
event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone
|
||||
rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date, forceset=True, tzinfos={})
|
||||
event_date = event_date.astimezone(timezone)
|
||||
|
||||
# The start date is naive
|
||||
# the timezone will be applied, if necessary, at the very end of the process
|
||||
# to allow for DST timezone reevaluation
|
||||
rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date.replace(tzinfo=None), forceset=True, ignoretz=True)
|
||||
|
||||
recurring_meetings = self.search([('recurrent_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)])
|
||||
|
||||
# We handle a maximum of 50,000 meetings at a time, and clear the cache at each step to
|
||||
@@ -629,12 +632,15 @@ class Meeting(models.Model):
|
||||
if use_naive_datetime:
|
||||
recurring_date = recurring_date.replace(tzinfo=None)
|
||||
else:
|
||||
recurring_date = todate(meeting.recurrent_id_date)
|
||||
recurring_date = todate(meeting.recurrent_id_date).replace(tzinfo=None)
|
||||
if date_field == "stop":
|
||||
recurring_date += timedelta(hours=self.duration)
|
||||
rset1.exdate(recurring_date)
|
||||
invalidate = True
|
||||
return [d.astimezone(pytz.UTC) if d.tzinfo else d for d in rset1 if d.year < MAXYEAR]
|
||||
|
||||
def naive_tz_to_utc(d):
|
||||
return timezone.localize(d).astimezone(pytz.UTC)
|
||||
return [naive_tz_to_utc(d) if not use_naive_datetime else d for d in rset1 if d.year < MAXYEAR]
|
||||
|
||||
@api.multi
|
||||
def _get_recurrency_end_date(self):
|
||||
|
||||
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests.common import TransactionCase
|
||||
import pytz
|
||||
|
||||
|
||||
class TestCalendar(TransactionCase):
|
||||
@@ -292,3 +293,24 @@ class TestCalendar(TransactionCase):
|
||||
|
||||
self.assertEqual(event.start, '2018-10-16 08:00:00')
|
||||
self.assertEqual(event.stop, '2018-10-18 18:00:00')
|
||||
|
||||
def test_recurring_around_dst(self):
|
||||
m = self.CalendarEvent.create({
|
||||
'name': "wheee",
|
||||
'start': '2018-10-27 14:30:00',
|
||||
'allday': False,
|
||||
'rrule': u'FREQ=DAILY;INTERVAL=1;COUNT=4',
|
||||
'duration': 2,
|
||||
'stop': '2018-10-27 16:30:00',
|
||||
})
|
||||
|
||||
start_recurring_dates = m.with_context({'tz': 'Europe/Brussels'})._get_recurrent_date_by_event()
|
||||
self.assertEqual(len(start_recurring_dates), 4)
|
||||
|
||||
for d in start_recurring_dates:
|
||||
self.assertEqual(d.tzinfo, pytz.UTC)
|
||||
if d.day < 28: # DST switch happens between 2018-10-27 and 2018-10-28
|
||||
self.assertEqual(d.hour, 14)
|
||||
else:
|
||||
self.assertEqual(d.hour, 15)
|
||||
self.assertEqual(d.minute, 30)
|
||||
|
||||
Reference in New Issue
Block a user