More timezone aware fixes

This commit is contained in:
Kovid Goyal 2010-02-15 12:15:01 -07:00
parent 137d83c0e2
commit c82f3e4138
11 changed files with 76 additions and 63 deletions

View file

@ -7,12 +7,11 @@
Fetch metadata using Amazon AWS
'''
import sys, re
from datetime import datetime
from lxml import etree
from calibre import browser
from calibre.utils.date import parse_date
from calibre.utils.date import parse_date, utcnow
from calibre.ebooks.metadata import MetaInformation, string_to_authors
AWS_NS = 'http://webservices.amazon.com/AWSECommerceService/2005-10-05'
@ -44,7 +43,7 @@ def get_social_metadata(title, authors, publisher, isbn):
try:
d = root.findtext('.//'+AWS('PublicationDate'))
if d:
default = datetime.utcnow().replace(day=15)
default = utcnow().replace(day=15)
d = parse_date(d[0].text, assume_utc=True, default=default)
mi.pubdate = d
except:

View file

@ -6,14 +6,13 @@
import sys, textwrap
from urllib import urlencode
from functools import partial
from datetime import datetime
from lxml import etree
from calibre import browser, preferred_encoding
from calibre.ebooks.metadata import MetaInformation
from calibre.utils.config import OptionParser
from calibre.utils.date import parse_date
from calibre.utils.date import parse_date, utcnow
NAMESPACES = {
'openSearch':'http://a9.com/-/spec/opensearchrss/1.0/',
@ -156,7 +155,7 @@ def get_date(self, entry, verbose):
try:
d = date(entry)
if d:
default = datetime.utcnow().replace(day=15)
default = utcnow().replace(day=15)
d = parse_date(d[0].text, assume_utc=True, default=default)
else:
d = None

View file

@ -11,11 +11,11 @@
from struct import pack, unpack
from cStringIO import StringIO
from datetime import datetime
from calibre.ebooks.mobi import MobiError
from calibre.ebooks.mobi.writer import rescale_image, MAX_THUMB_DIMEN
from calibre.ebooks.mobi.langcodes import iana2mobi
from calibre.utils.date import now as nowf
class StreamSlicer(object):
@ -331,7 +331,7 @@ def pop_exth_record(exth_id):
recs.append((106, self.timestamp))
pop_exth_record(106)
else:
recs.append((106, str(datetime.now()).encode(self.codec, 'replace')))
recs.append((106, nowf().isoformat().encode(self.codec, 'replace')))
pop_exth_record(106)
if self.cover_record is not None:
recs.append((201, pack('>I', self.cover_rindex)))

View file

@ -4,13 +4,11 @@
Read data from .mobi files
'''
import datetime
import functools
import os
import re
import struct
import textwrap
import cStringIO
try:
@ -23,6 +21,7 @@
from calibre import entity_to_unicode, CurrentDir
from calibre.utils.filenames import ascii_filename
from calibre.utils.date import parse_date
from calibre.ptempfile import TemporaryDirectory
from calibre.ebooks import DRMError
from calibre.ebooks.chardet import ENCODING_PATS
@ -96,8 +95,7 @@ def process_metadata(self, id, content, codec):
self.mi.tags = list(set(self.mi.tags))
elif id == 106:
try:
self.mi.publish_date = datetime.datetime.strptime(
content, '%Y-%m-%d', ).date()
self.mi.pubdate = parse_date(content, as_utc=False)
except:
pass
elif id == 108:

View file

@ -10,7 +10,6 @@
import re
import time
import traceback
from datetime import datetime, timedelta
from PyQt4.Qt import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate, \
QPixmap, QListWidgetItem, QDialog
@ -29,6 +28,7 @@
from calibre import islinux
from calibre.ebooks.metadata.meta import get_metadata
from calibre.utils.config import prefs, tweaks
from calibre.utils.date import qt_to_dt
from calibre.customize.ui import run_plugins_on_import, get_isbndb_key
from calibre.gui2.dialogs.config.social import SocialMetadata
@ -354,12 +354,9 @@ def __init__(self, window, row, db, accepted_callback=None, cancel_all=False):
self.comments.setPlainText(comments if comments else '')
cover = self.db.cover(row)
pubdate = db.pubdate(self.id, index_is_id=True)
self.local_timezone_offset = timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
pubdate = pubdate - self.local_timezone_offset
self.pubdate.setDate(QDate(pubdate.year, pubdate.month,
pubdate.day))
timestamp = db.timestamp(self.id, index_is_id=True)
timestamp = timestamp - self.local_timezone_offset
self.date.setDate(QDate(timestamp.year, timestamp.month,
timestamp.day))
@ -583,7 +580,6 @@ def fetch_metadata(self):
if book.isbn: self.isbn.setText(book.isbn)
if book.pubdate:
d = book.pubdate
d = d - self.local_timezone_offset
self.pubdate.setDate(QDate(d.year, d.month, d.day))
summ = book.comments
if summ:
@ -656,12 +652,10 @@ def accept(self):
self.db.set_series_index(self.id, self.series_index.value(), notify=False)
self.db.set_comment(self.id, qstring_to_unicode(self.comments.toPlainText()), notify=False)
d = self.pubdate.date()
d = datetime(d.year(), d.month(), d.day())
d = d + self.local_timezone_offset
d = qt_to_dt(d)
self.db.set_pubdate(self.id, d)
d = self.date.date()
d = datetime(d.year(), d.month(), d.day())
d = d + self.local_timezone_offset
d = qt_to_dt(d)
self.db.set_timestamp(self.id, d)
if self.cover_changed:

View file

@ -1,8 +1,7 @@
from calibre.ebooks.metadata import authors_to_string
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, textwrap, traceback, time, re
from datetime import timedelta, datetime
import os, textwrap, traceback, re
from operator import attrgetter
from math import cos, sin, pi
@ -25,6 +24,7 @@
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
from calibre.ebooks.metadata import string_to_authors, fmt_sidx
from calibre.utils.config import tweaks
from calibre.utils.date import dt_factory, qt_to_dt, isoformat
class LibraryDelegate(QItemDelegate):
COLOR = QColor("blue")
@ -567,13 +567,11 @@ def authors(r):
def timestamp(r):
dt = self.db.data[r][tmdx]
if dt:
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
return QDate(dt.year, dt.month, dt.day)
def pubdate(r):
dt = self.db.data[r][pddx]
if dt:
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
return QDate(dt.year, dt.month, dt.day)
def rating(r):
@ -670,13 +668,11 @@ def setData(self, index, value, role):
elif column == 'timestamp':
if val.isNull() or not val.isValid():
return False
dt = datetime(val.year(), val.month(), val.day()) + timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
self.db.set_timestamp(id, dt)
self.db.set_timestamp(id, qt_to_dt(val, as_utc=False))
elif column == 'pubdate':
if val.isNull() or not val.isValid():
return False
dt = datetime(val.year(), val.month(), val.day()) + timedelta(seconds=time.timezone) - timedelta(hours=time.daylight)
self.db.set_pubdate(id, dt)
self.db.set_pubdate(id, qt_to_dt(val, as_utc=False))
else:
self.db.set(row, column, val)
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
@ -1032,7 +1028,8 @@ def _strcmp(x, y):
def datecmp(x, y):
x = self.db[x].datetime
y = self.db[y].datetime
return cmp(datetime(*x[0:6]), datetime(*y[0:6]))
return cmp(dt_factory(x, assume_utc=True), dt_factory(y,
assume_utc=True))
def sizecmp(x, y):
x, y = int(self.db[x].size), int(self.db[y].size)
return cmp(x, y)
@ -1081,10 +1078,8 @@ def current_changed(self, current, previous):
type = ext[1:].lower()
data[_('Format')] = type
data[_('Path')] = item.path
dt = item.datetime
dt = datetime(*dt[0:6])
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
data[_('Timestamp')] = strftime('%a %b %d %H:%M:%S %Y', dt.timetuple())
dt = dt_factory(item.datetime, assume_utc=True)
data[_('Timestamp')] = isoformat(dt, sep=' ', as_utc=False)
data[_('Tags')] = ', '.join(item.tags)
self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data)
@ -1119,8 +1114,7 @@ def data(self, index, role):
return QVariant(BooksView.human_readable(size))
elif col == 3:
dt = self.db[self.map[row]].datetime
dt = datetime(*dt[0:6])
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
dt = dt_factory(dt, assume_utc=True, as_utc=False)
return QVariant(strftime(BooksView.TIME_FMT, dt.timetuple()))
elif col == 4:
tags = self.db[self.map[row]].tags

View file

@ -11,7 +11,6 @@
import __builtin__
from itertools import repeat
from logging.handlers import RotatingFileHandler
from datetime import datetime
from threading import Thread
import cherrypy
@ -31,15 +30,16 @@
from calibre.utils.mdns import publish as publish_zeroconf, \
stop_server as stop_zeroconf
from calibre.ebooks.metadata import fmt_sidx, title_sort
from calibre.utils.date import now as nowf, fromtimestamp
def strftime(fmt='%Y/%m/%d %H:%M:%S', dt=None):
if not hasattr(dt, 'timetuple'):
dt = datetime.now()
dt = nowf()
dt = dt.timetuple()
try:
return _strftime(fmt, dt)
except:
return _strftime(fmt, datetime.now().timetuple())
return _strftime(fmt, nowf().timetuple())
def expose(func):
@ -351,7 +351,7 @@ def __init__(self, db, opts, embedded=False, show_tracebacks=True):
map(int, self.opts.max_cover.split('x'))
self.max_stanza_items = opts.max_opds_items
path = P('content_server')
self.build_time = datetime.fromtimestamp(os.stat(path).st_mtime)
self.build_time = fromtimestamp(os.stat(path).st_mtime)
self.default_cover = open(P('content_server/default_cover.jpg'), 'rb').read()
cherrypy.config.update({
@ -429,7 +429,7 @@ def get_cover(self, id, thumbnail=False):
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
cherrypy.response.timeout = 3600
path = getattr(cover, 'name', False)
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime) if path and \
updated = fromtimestamp(os.stat(path).st_mtime) if path and \
os.access(path, os.R_OK) else self.build_time
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
try:
@ -476,7 +476,7 @@ def get_format(self, id, format):
cherrypy.response.timeout = 3600
path = getattr(fmt, 'name', None)
if path and os.path.exists(path):
updated = datetime.utcfromtimestamp(os.stat(path).st_mtime)
updated = fromtimestamp(os.stat(path).st_mtime)
cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
return fmt.read()
@ -841,7 +841,7 @@ def static(self, name):
if not os.path.exists(path):
raise cherrypy.HTTPError(404, '%s not found'%name)
if self.opts.develop:
lm = datetime.fromtimestamp(os.stat(path).st_mtime)
lm = fromtimestamp(os.stat(path).st_mtime)
cherrypy.response.headers['Last-Modified'] = self.last_modified(lm)
return open(path, 'rb').read()

View file

@ -11,8 +11,8 @@
from dateutil.parser import parse
from dateutil.tz import tzlocal, tzutc
_utc_tz = tzutc()
_local_tz = tzlocal()
utc_tz = _utc_tz = tzutc()
local_tz = _local_tz = tzlocal()
def parse_date(date_string, assume_utc=False, as_utc=True, default=None):
'''
@ -34,17 +34,48 @@ def parse_date(date_string, assume_utc=False, as_utc=True, default=None):
dt = parse(date_string, default=default)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz)
dt = dt.astimezone(_utc_tz if as_utc else _local_tz)
return dt.astimezone(_utc_tz if as_utc else _local_tz)
def strptime(val, fmt, assume_utc=False, as_utc=True):
dt = datetime.strptime(val, fmt)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz)
return dt.astimezone(_utc_tz if as_utc else _local_tz)
def dt_factory(time_t, assume_utc=False, as_utc=True):
dt = datetime(time_t[0:6])
if dt.tzinfo is None:
dt = dt.replace(tzinfo=_utc_tz if assume_utc else _local_tz)
return dt.astimezone(_utc_tz if as_utc else _local_tz)
def qt_to_dt(qdate_or_qdatetime, as_utc=True):
from PyQt4.Qt import Qt
o = qdate_or_qdatetime
if hasattr(o, 'toUTC'):
# QDateTime
o = unicode(o.toUTC().toString(Qt.ISODate))
return parse_date(o, assume_utc=True, as_utc=as_utc)
dt = datetime(o.year(), o.month(), o.day()).replace(tzinfo=_local_tz)
return dt.astimezone(_utc_tz if as_utc else _local_tz)
def fromtimestamp(ctime, as_utc=True):
dt = datetime.utcfromtimestamp().replace(tzinfo=_utc_tz)
if not as_utc:
dt = dt.astimezone(_local_tz)
return dt
def isoformat(date_time, assume_utc=False, as_utc=True):
def fromordinal(day, as_utc=True):
return datetime.fromordinal(day).replace(
tzinfo=_utc_tz if as_utc else _local_tz)
def isoformat(date_time, assume_utc=False, as_utc=True, sep='T'):
if not hasattr(date_time, 'tzinfo'):
return unicode(date_time.isoformat())
if date_time.tzinfo is None:
date_time = date_time.replace(tzinfo=_utc_tz if assume_utc else
_local_tz)
date_time = date_time.astimezone(_utc_tz if as_utc else _local_tz)
return unicode(date_time.isoformat())
return unicode(date_time.isoformat(sep))
def now():
return datetime.now().replace(tzinfo=_local_tz)

View file

@ -6,17 +6,16 @@
Contains the logic for parsing feeds.
'''
import time, traceback, copy, re
from datetime import datetime
from lxml import html
from calibre.web.feeds.feedparser import parse
from calibre.utils.logging import default_log
from calibre import entity_to_unicode
from lxml import html
from calibre.utils.date import dt_factory, utcnow, local_tz
class Article(object):
time_offset = datetime.now() - datetime.utcnow()
def __init__(self, id, title, url, author, summary, published, content):
self.downloaded = False
self.id = id
@ -48,8 +47,8 @@ def __init__(self, id, title, url, author, summary, published, content):
self.author = author
self.content = content
self.date = published
self.utctime = datetime(*self.date[:6])
self.localtime = self.utctime + self.time_offset
self.utctime = dt_factory(self.date, assume_utc=True, as_utc=True)
self.localtime = self.utctime.astimezone(local_tz)
@dynamic_property
def title(self):
@ -146,7 +145,7 @@ def populate_from_preparsed_feed(self, title, articles, oldest_article=7,
content = item.get('content', '')
author = item.get('author', '')
article = Article(id, title, link, author, description, published, content)
delta = datetime.utcnow() - article.utctime
delta = utcnow() - article.utctime
if delta.days*24*3600 + delta.seconds <= 24*3600*self.oldest_article:
self.articles.append(article)
else:
@ -183,7 +182,7 @@ def parse_article(self, item):
if not link and not content:
return
article = Article(id, title, link, author, description, published, content)
delta = datetime.utcnow() - article.utctime
delta = utcnow() - article.utctime
if delta.days*24*3600 + delta.seconds <= 24*3600*self.oldest_article:
self.articles.append(article)
else:

View file

@ -11,7 +11,6 @@
from collections import defaultdict
from functools import partial
from contextlib import nested, closing
from datetime import datetime
from calibre import browser, __appname__, iswindows, \
@ -29,7 +28,7 @@
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
from calibre.ptempfile import PersistentTemporaryFile, \
PersistentTemporaryDirectory
from calibre.utils.date import now as nowf
class BasicNewsRecipe(Recipe):
'''
@ -1080,11 +1079,11 @@ def create_opf(self, feeds, dir=None):
mi.publisher = __appname__
mi.author_sort = __appname__
mi.publication_type = 'periodical:'+self.publication_type
mi.timestamp = datetime.now()
mi.timestamp = nowf()
mi.comments = self.description
if not isinstance(mi.comments, unicode):
mi.comments = mi.comments.decode('utf-8', 'replace')
mi.pubdate = datetime.now()
mi.pubdate = nowf()
opf_path = os.path.join(dir, 'index.opf')
ncx_path = os.path.join(dir, 'index.ncx')

View file

@ -8,14 +8,14 @@
import os, calendar
from threading import RLock
from datetime import datetime, timedelta
from datetime import timedelta
from lxml import etree
from lxml.builder import ElementMaker
from calibre import browser
from calibre.utils.date import parse_date, now as nowf, utcnow, tzlocal, \
isoformat
isoformat, fromordinal
NS = 'http://calibre-ebook.com/recipe_collection'
E = ElementMaker(namespace=NS, nsmap={None:NS})
@ -163,7 +163,7 @@ def schedule_recipe(self, recipe, schedule_type, schedule, last_downloaded=None)
self.root.remove(x)
break
if last_downloaded is None:
last_downloaded = datetime.fromordinal(1)
last_downloaded = fromordinal(1)
sr = E.scheduled_recipe({
'id' : recipe.get('id'),
'title': recipe.get('title'),