mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix editing dates with days sometimes off by one day. Fixes #2042815 [problems with dates](https://bugs.launchpad.net/calibre/+bug/2042815)
The problem seems to be some change in PyQt that causes auto-conversion of python datetime objects to QDateTime to lose timezone information. So we convert manually preserving that information.
This commit is contained in:
parent
378e21b907
commit
f3b37944c4
@ -11,7 +11,7 @@ import threading
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import lru_cache
|
||||
from qt.core import (
|
||||
QApplication, QBuffer, QByteArray, QColor, QDateTime, QDesktopServices, QDialog,
|
||||
QApplication, QBuffer, QByteArray, QColor, QDesktopServices, QDialog,
|
||||
QDialogButtonBox, QEvent, QFile, QFileDialog, QFileIconProvider, QFileInfo, QFont,
|
||||
QFontDatabase, QFontInfo, QFontMetrics, QGuiApplication, QIcon, QImageReader,
|
||||
QImageWriter, QIODevice, QLocale, QNetworkProxyFactory, QObject, QPalette,
|
||||
@ -37,7 +37,7 @@ from calibre.gui2.qt_file_dialogs import FileDialog
|
||||
from calibre.ptempfile import base_dir
|
||||
from calibre.utils.config import Config, ConfigProxy, JSONConfig, dynamic
|
||||
from calibre.utils.config_base import tweaks
|
||||
from calibre.utils.date import UNDEFINED_DATE
|
||||
from calibre.utils.date import UNDEFINED_DATE, qt_from_dt
|
||||
from calibre.utils.file_type_icons import EXT_MAP
|
||||
from calibre.utils.img import set_image_allocation_limit
|
||||
from calibre.utils.localization import get_lang
|
||||
@ -428,7 +428,7 @@ create_defs()
|
||||
del create_defs
|
||||
# }}}
|
||||
|
||||
UNDEFINED_QDATETIME = QDateTime(UNDEFINED_DATE)
|
||||
UNDEFINED_QDATETIME = qt_from_dt(UNDEFINED_DATE, as_utc=True)
|
||||
QT_HIDDEN_CLEAR_ACTION = '_q_qlineeditclearaction'
|
||||
ALL_COLUMNS = ['title', 'ondevice', 'authors', 'size', 'timestamp', 'rating', 'publisher',
|
||||
'tags', 'series', 'pubdate']
|
||||
|
@ -9,7 +9,7 @@ import os
|
||||
from collections import OrderedDict
|
||||
from functools import partial
|
||||
from qt.core import (
|
||||
QApplication, QCheckBox, QComboBox, QDateTime, QDialog, QDoubleSpinBox, QGridLayout,
|
||||
QApplication, QCheckBox, QComboBox, QDialog, QDoubleSpinBox, QGridLayout,
|
||||
QGroupBox, QHBoxLayout, QIcon, QLabel, QLineEdit, QMessageBox, QPlainTextEdit,
|
||||
QSizePolicy, QSpacerItem, QSpinBox, QStyle, Qt, QToolButton, QUrl, QVBoxLayout,
|
||||
QWidget,
|
||||
@ -18,15 +18,16 @@ from qt.core import (
|
||||
from calibre.ebooks.metadata import title_sort
|
||||
from calibre.gui2 import UNDEFINED_QDATETIME, elided_text, error_dialog, gprefs
|
||||
from calibre.gui2.comments_editor import Editor as CommentsEditor
|
||||
from calibre.gui2.markdown_editor import Editor as MarkdownEditor
|
||||
from calibre.gui2.complete2 import EditWithComplete as EWC
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.gui2.library.delegates import ClearingDoubleSpinBox, ClearingSpinBox
|
||||
from calibre.gui2.markdown_editor import Editor as MarkdownEditor
|
||||
from calibre.gui2.widgets2 import DateTimeEdit as DateTimeEditBase, RatingEditor
|
||||
from calibre.library.comments import comments_to_html
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import (
|
||||
as_local_time, as_utc, internal_iso_format_string, is_date_undefined, now, qt_to_dt,
|
||||
as_local_time, as_utc, internal_iso_format_string, is_date_undefined, now,
|
||||
qt_from_dt, qt_to_dt,
|
||||
)
|
||||
from calibre.utils.icu import lower as icu_lower, sort_key
|
||||
|
||||
@ -349,7 +350,7 @@ class DateTimeEdit(DateTimeEditBase):
|
||||
DateTimeEditBase.focusOutEvent(self, x)
|
||||
|
||||
def set_to_today(self):
|
||||
self.setDateTime(now())
|
||||
self.setDateTime(qt_from_dt(now()))
|
||||
|
||||
def set_to_clear(self):
|
||||
self.setDateTime(UNDEFINED_QDATETIME)
|
||||
@ -400,12 +401,12 @@ class DateTime(Base):
|
||||
if val is None:
|
||||
val = self.dte.minimumDateTime()
|
||||
else:
|
||||
val = QDateTime(val)
|
||||
val = qt_from_dt(val)
|
||||
self.dte.setDateTime(val)
|
||||
|
||||
def getter(self):
|
||||
val = self.dte.dateTime()
|
||||
if val <= UNDEFINED_QDATETIME:
|
||||
if is_date_undefined(val):
|
||||
val = None
|
||||
else:
|
||||
val = qt_to_dt(val)
|
||||
@ -1251,13 +1252,13 @@ class BulkDateTime(BulkBase):
|
||||
if val is None:
|
||||
val = self.main_widget.minimumDateTime()
|
||||
else:
|
||||
val = QDateTime(val)
|
||||
val = qt_from_dt(val)
|
||||
self.main_widget.setDateTime(val)
|
||||
self.ignore_change_signals = False
|
||||
|
||||
def getter(self):
|
||||
val = self.main_widget.dateTime()
|
||||
if val <= UNDEFINED_QDATETIME:
|
||||
if is_date_undefined(val):
|
||||
val = None
|
||||
else:
|
||||
val = qt_to_dt(val)
|
||||
|
@ -15,8 +15,8 @@ import traceback
|
||||
from collections import defaultdict, namedtuple
|
||||
from itertools import groupby
|
||||
from qt.core import (
|
||||
QAbstractTableModel, QApplication, QColor, QDateTime, QFont, QFontMetrics, QIcon,
|
||||
QImage, QModelIndex, QPainter, QPixmap, Qt, pyqtSignal,
|
||||
QAbstractTableModel, QApplication, QColor, QFont, QFontMetrics, QIcon, QImage,
|
||||
QModelIndex, QPainter, QPixmap, Qt, pyqtSignal,
|
||||
)
|
||||
|
||||
from calibre import (
|
||||
@ -34,7 +34,7 @@ from calibre.library.save_to_disk import find_plugboard
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import device_prefs, prefs, tweaks
|
||||
from calibre.utils.date import (
|
||||
UNDEFINED_DATE, as_local_time, dt_factory, is_date_undefined, qt_to_dt,
|
||||
UNDEFINED_DATE, dt_factory, is_date_undefined, qt_from_dt, qt_to_dt,
|
||||
)
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.localization import calibre_langcode_to_name, ngettext
|
||||
@ -919,7 +919,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
elif dt == 'datetime':
|
||||
def func(idx):
|
||||
val = fffunc(field_obj, idfunc(idx), default_value=UNDEFINED_DATE)
|
||||
return None if is_date_undefined(val) else QDateTime(as_local_time(val))
|
||||
return None if is_date_undefined(val) else qt_from_dt(val)
|
||||
elif dt == 'rating':
|
||||
rating_fields[field] = m['display'].get('allow_half_stars', False)
|
||||
|
||||
|
@ -36,7 +36,9 @@ from calibre.gui2.comments_editor import Editor
|
||||
from calibre.gui2.complete2 import EditWithComplete
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.gui2.languages import LanguagesEdit as LE
|
||||
from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView, LineEditIndicators
|
||||
from calibre.gui2.widgets import (
|
||||
EnLineEdit, FormatList as _FormatList, ImageView, LineEditIndicators,
|
||||
)
|
||||
from calibre.gui2.widgets2 import (
|
||||
DateTimeEdit, Dialog, RatingEditor, RightClickButton, access_key,
|
||||
populate_standard_spinbox_context_menu,
|
||||
@ -46,7 +48,7 @@ from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
from calibre.utils.date import (
|
||||
UNDEFINED_DATE, as_local_time, internal_iso_format_string, is_date_undefined,
|
||||
local_tz, parse_only_date, qt_to_dt, utcfromtimestamp,
|
||||
local_tz, parse_only_date, qt_from_dt, qt_to_dt, utcfromtimestamp,
|
||||
)
|
||||
from calibre.utils.filenames import make_long_path_useable
|
||||
from calibre.utils.icu import sort_key, strcmp
|
||||
@ -188,6 +190,8 @@ def make_undoable(spinbox):
|
||||
if hasattr(self, 'setDateTime'):
|
||||
if isinstance(val, date) and not isinstance(val, datetime) and not is_date_undefined(val):
|
||||
val = parse_only_date(val.isoformat(), assume_utc=False, as_utc=False)
|
||||
if isinstance(val, datetime):
|
||||
val = qt_from_dt(val)
|
||||
self.setDateTime(val)
|
||||
elif hasattr(self, 'setValue'):
|
||||
self.setValue(val)
|
||||
|
@ -178,6 +178,17 @@ def qt_to_dt(qdate_or_qdatetime, as_utc=True):
|
||||
return dt.astimezone(_utc_tz if as_utc else _local_tz)
|
||||
|
||||
|
||||
def qt_from_dt(d, as_utc=False, assume_utc=False):
|
||||
from qt.core import QDateTime, QTimeZone
|
||||
if d.tzinfo is None:
|
||||
d = d.replace(tzinfo=utc_tz if assume_utc else local_tz)
|
||||
d = d.astimezone(utc_tz)
|
||||
ans = QDateTime.fromMSecsSinceEpoch(int(d.timestamp() * 1000), QTimeZone.utc())
|
||||
if not as_utc:
|
||||
ans = ans.toLocalTime()
|
||||
return ans
|
||||
|
||||
|
||||
def fromtimestamp(ctime, as_utc=True):
|
||||
dt = datetime.utcfromtimestamp(ctime).replace(tzinfo=_utc_tz)
|
||||
if not as_utc:
|
||||
|
Loading…
x
Reference in New Issue
Block a user