[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:
Lucas Perais (lpe)
2018-10-23 14:00:18 +00:00
parent 35bb8304ab
commit eeae211176
2 changed files with 35 additions and 7 deletions
+13 -7
View File
@@ -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):
+22
View File
@@ -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)