Fix handling of auto-checkout around DST switch

This commit is contained in:
Raphael Michel
2021-04-29 17:39:23 +02:00
parent a45f4cce4f
commit 552d13ef6c
2 changed files with 51 additions and 3 deletions

View File

@@ -32,11 +32,12 @@
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
from datetime import timedelta
from datetime import datetime, timedelta
from functools import partial, reduce
import dateutil
import dateutil.parser
import pytz
from django.core.files import File
from django.db import transaction
from django.db.models import (
@@ -46,7 +47,7 @@ from django.db.models import (
from django.db.models.functions import Coalesce, TruncDate
from django.dispatch import receiver
from django.utils.functional import cached_property
from django.utils.timezone import now, override
from django.utils.timezone import make_aware, now, override
from django.utils.translation import gettext as _
from django_scopes import scope, scopes_disabled
@@ -523,5 +524,15 @@ def process_exit_all(sender, **kwargs):
position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at
)
checkin_created.send(cl.event, checkin=ci)
cl.exit_all_at = cl.exit_all_at + timedelta(days=1)
d = cl.exit_all_at.astimezone(cl.event.timezone)
if cl.event.settings.get(f'autocheckin_dst_hack_{cl.pk}'): # move time back if yesterday was DST switch
d -= timedelta(hours=1)
cl.event.settings.delete(f'autocheckin_dst_hack_{cl.pk}')
try:
cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone)
except pytz.exceptions.NonExistentTimeError:
cl.event.settings.set(f'autocheckin_dst_hack_{cl.pk}', True)
d += timedelta(hours=1)
cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone)
# AmbiguousTimeError shouldn't be possible since d.time() includes fold=0
cl.save(update_fields=['exit_all_at'])

View File

@@ -687,3 +687,40 @@ def test_auto_check_out_only_if_checked_in(event, position, clist):
with freeze_time("2020-01-03 03:05:00+01:00"):
process_exit_all(sender=None)
assert position.checkins.count() == 2
@pytest.mark.django_db(transaction=True)
def test_auto_check_out_dst(event, position, clist):
event.settings.timezone = 'Europe/Berlin'
# Survive across a shift that doesn't affect the time in question
clist.exit_all_at = event.timezone.localize(datetime(2021, 3, 28, 1, 0))
clist.save()
with freeze_time(clist.exit_all_at + timedelta(minutes=5)):
process_exit_all(sender=None)
clist.refresh_from_db()
assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 29, 1, 0))
# Survive across a shift that makes the time in question ambigous
clist.exit_all_at = event.timezone.localize(datetime(2021, 10, 28, 2, 30))
clist.save()
with freeze_time(clist.exit_all_at + timedelta(minutes=5)):
process_exit_all(sender=None)
clist.refresh_from_db()
assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 10, 29, 2, 30))
# Doesn't survive across a shift that makes the time in question non-existant
clist.exit_all_at = event.timezone.localize(datetime(2021, 3, 27, 2, 30))
clist.save()
with freeze_time(clist.exit_all_at + timedelta(minutes=5)):
process_exit_all(sender=None)
clist.refresh_from_db()
assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 28, 2, 30))
with freeze_time(clist.exit_all_at + timedelta(minutes=5)):
process_exit_all(sender=None)
clist.refresh_from_db()
assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 29, 2, 30))
with freeze_time(clist.exit_all_at + timedelta(minutes=5)):
process_exit_all(sender=None)
clist.refresh_from_db()
assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 30, 2, 30))