From 59dea09c8a1a7ba8e7dc6509f9692cdc7c0714f6 Mon Sep 17 00:00:00 2001 From: Romain Bignon Date: Sat, 30 Mar 2013 12:30:33 +0100 Subject: [PATCH] fix problem with strftime on date.year<1900 --- weboob/capabilities/base.py | 11 +++++ weboob/tools/date.py | 86 ++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/weboob/capabilities/base.py b/weboob/capabilities/base.py index 6755c6dd..a1f2024f 100644 --- a/weboob/capabilities/base.py +++ b/weboob/capabilities/base.py @@ -24,6 +24,7 @@ from decimal import Decimal from copy import deepcopy, copy from weboob.tools.misc import to_unicode +from weboob.tools.date import new_date, new_datetime from weboob.tools.ordereddict import OrderedDict @@ -228,6 +229,16 @@ class DateField(Field): def __init__(self, doc, **kwargs): Field.__init__(self, doc, datetime.date, datetime.datetime, **kwargs) + def __setattr__(self, name, value): + if name == 'value': + # Force use of our date and datetime types, to fix bugs in python2 + # with strftime on year<1900. + if type(value) is datetime.datetime: + value = new_datetime(value) + if type(value) is datetime.date: + value = new_date(value) + return object.__setattr__(self, name, value) + class TimeField(Field): """ diff --git a/weboob/tools/date.py b/weboob/tools/date.py index 21144c81..a4fc1f42 100644 --- a/weboob/tools/date.py +++ b/weboob/tools/date.py @@ -18,14 +18,16 @@ # along with weboob. If not, see . -from datetime import date, timedelta +from datetime import date as real_date, datetime as real_datetime, timedelta +import time +import re try: from dateutil import tz except ImportError: raise ImportError('Please install python-dateutil') -__all__ = ['local2utc', 'utc2local', 'LinearDateGuesser'] +__all__ = ['local2utc', 'utc2local', 'LinearDateGuesser', 'date', 'datetime', 'new_date', 'new_datetime'] def local2utc(dateobj): @@ -40,6 +42,86 @@ def utc2local(dateobj): return dateobj +class date(real_date): + def strftime(self, fmt): + return strftime(self, fmt) + + +class datetime(real_datetime): + def strftime(self, fmt): + return strftime(self, fmt) + def combine(self, date, time): + return datetime(date.year, date.month, date.day, time.hour, time.minute, time.microsecond, time.tzinfo) + def date(self): + return date(self.year, self.month, self.day) + + +def new_date(d): + """ Generate a safe date from a datetime.date object """ + return date(d.year, d.month, d.day) + + +def new_datetime(d): + """ + Generate a safe datetime from a datetime.date or datetime.datetime object + """ + kw = [d.year, d.month, d.day] + if isinstance(d, real_datetime): + kw.extend([d.hour, d.minute, d.second, d.microsecond, d.tzinfo]) + return datetime(*kw) + + +# No support for strftime's "%s" or "%y". +# Allowed if there's an even number of "%"s because they are escaped. +_illegal_formatting = re.compile(r"((^|[^%])(%%)*%[sy])") + +def _findall(text, substr): + # Also finds overlaps + sites = [] + i = 0 + while 1: + j = text.find(substr, i) + if j == -1: + break + sites.append(j) + i=j+1 + return sites + +def strftime(dt, fmt): + if dt.year >= 1900: + return super(type(dt), dt).strftime(fmt) + illegal_formatting = _illegal_formatting.search(fmt) + if illegal_formatting: + raise TypeError("strftime of dates before 1900 does not handle" + illegal_formatting.group(0)) + + year = dt.year + # For every non-leap year century, advance by + # 6 years to get into the 28-year repeat cycle + delta = 2000 - year + off = 6*(delta // 100 + delta // 400) + year = year + off + + # Move to around the year 2000 + year = year + ((2000 - year)//28)*28 + timetuple = dt.timetuple() + s1 = time.strftime(fmt, (year,) + timetuple[1:]) + sites1 = _findall(s1, str(year)) + + s2 = time.strftime(fmt, (year+28,) + timetuple[1:]) + sites2 = _findall(s2, str(year+28)) + + sites = [] + for site in sites1: + if site in sites2: + sites.append(site) + + s = s1 + syear = "%4d" % (dt.year,) + for site in sites: + s = s[:site] + syear + s[site+4:] + return s + + class LinearDateGuesser(object): """ The aim of this class is to guess the exact date object from