[books] Allow timezone changes when importing from KOReader

Turns out you need a city-based timezone for DST stuff to work properly.
The US/Eastern timezone doesn't mess with DST because it can be so wonky
in different regions. So while we fix timezone defaulting to a
DST-friendly timezone too.
This commit is contained in:
2025-07-19 01:54:27 -04:00
parent 7c6e895ae4
commit 4db8793d5c
6 changed files with 126 additions and 55 deletions

View File

@ -1,4 +1,8 @@
import pytz
from zoneinfo import ZoneInfo
import pendulum
from datetime import datetime
from django.utils import timezone
import logging
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
@ -10,6 +14,7 @@ from profiles.constants import PRETTY_TIMEZONE_CHOICES
User = get_user_model()
BNULL = {"blank": True, "null": True}
logger = logging.getLogger(__name__)
class UserProfile(TimeStampedModel):
user = models.OneToOneField(
@ -18,6 +23,7 @@ class UserProfile(TimeStampedModel):
timezone = models.CharField(
max_length=255, choices=PRETTY_TIMEZONE_CHOICES, default="UTC"
)
timezone_change_log = models.TextField(**BNULL)
lastfm_username = models.CharField(max_length=255, **BNULL)
lastfm_password = EncryptedField(**BNULL)
lastfm_auto_import = models.BooleanField(default=False)
@ -59,7 +65,51 @@ class UserProfile(TimeStampedModel):
@property
def tzinfo(self):
return pytz.timezone(self.timezone)
return ZoneInfo(self.timezone)
def save(self, *args, **kwargs):
if not self._state.adding:
old_instance = UserProfile.objects.get(pk=self.pk)
is_timezone_change = self.timezone != old_instance.timezone
if is_timezone_change:
logger.info("Updating timezone changelog for user", extra={"profile_id": self.id})
previous_changes = old_instance.timezone_change_log
now = timezone.now().replace(microsecond=0)
new_log = f"{self.timezone} - {now}"
if previous_changes:
new_log = previous_changes + f"\n{new_log}"
self.timezone_change_log = new_log
super(UserProfile, self).save(*args, **kwargs)
@property
def historic_timezone_changes(self) -> list:
"""Return a list of datetimes with timezones for the specific changed time"""
history = [pendulum.datetime(1900, 1, 1, 0, 0, 0, tz=self.tzinfo.key)]
if self.timezone_change_log:
for change in self.timezone_change_log.split("\n"):
if " - " in change:
tz, date = change.split(" - ")
history.append(pendulum.parse(date).in_timezone(tz))
return history
def get_timestamp_with_tz(self, timestamp):
timezone = self.tzinfo
if self.timezone_change_log:
change_list = self.historic_timezone_changes
for idx, start in enumerate(change_list):
try:
end = change_list[idx+1]
except IndexError:
end = None
if end:
if start <= timestamp.replace(tzinfo=end.timezone) <= end:
timezone = start.timezone
else:
if start <= timestamp.replace(tzinfo=start.timezone):
timezone = start.timezone
return timestamp.replace(tzinfo=timezone)
@cached_property
def task_context_tags(self) -> list[str]: