Django

Code

Ticket #1443: datetime_safe.diff

File datetime_safe.diff, 13.6 kB (added by SmileyChris, 9 months ago)

New and improved!

  • django/db/models/manipulators.py

    old new  
    99from django.utils.text import capfirst 
    1010from django.utils.encoding import smart_str 
    1111from django.utils.translation import ugettext as _ 
     12from django.utils import datetime_safe 
    1213 
    1314def add_manipulators(sender): 
    1415    cls = sender 
     
    332333            pass 
    333334        else: 
    334335            format_string = (lookup_type == 'date') and '%B %d, %Y' or '%B %Y' 
     336            date_val = datetime_safe.new_datetime(date_val) 
    335337            raise validators.ValidationError, "Please enter a different %s. The one you entered is already being used for %s." % \ 
    336338                (from_field.verbose_name, date_val.strftime(format_string)) 
  • django/db/models/fields/__init__.py

    old new  
    2020from django.utils.translation import ugettext_lazy, ugettext as _ 
    2121from django.utils.encoding import smart_unicode, force_unicode, smart_str 
    2222from django.utils.maxlength import LegacyMaxlength 
     23from django.utils import datetime_safe 
    2324 
    2425class NOT_PROVIDED: 
    2526    pass 
     
    530531        if lookup_type == 'range': 
    531532            value = [smart_unicode(v) for v in value] 
    532533        elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'): 
    533             value = value.strftime('%Y-%m-%d') 
     534            value = datetime_safe.new_date(value).strftime('%Y-%m-%d') 
    534535        else: 
    535536            value = smart_unicode(value) 
    536537        return Field.get_db_prep_lookup(self, lookup_type, value) 
     
    562563        # Casts dates into string format for entry into database. 
    563564        if value is not None: 
    564565            try: 
    565                 value = value.strftime('%Y-%m-%d') 
     566                value = datetime_safe.new_date(value).strftime('%Y-%m-%d') 
    566567            except AttributeError: 
    567568                # If value is already a string it won't have a strftime method, 
    568569                # so we'll just let it pass through. 
     
    574575 
    575576    def flatten_data(self, follow, obj=None): 
    576577        val = self._get_val_from_obj(obj) 
    577         return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')} 
     578        if val is None: 
     579            data = '' 
     580        else: 
     581            data = datetime_safe.new_date(val).strftime('%Y-%m-%d') 
     582        return {self.attname: data} 
    578583 
    579584    def formfield(self, **kwargs): 
    580585        defaults = {'form_class': forms.DateField} 
     
    641646    def flatten_data(self,follow, obj = None): 
    642647        val = self._get_val_from_obj(obj) 
    643648        date_field, time_field = self.get_manipulator_field_names('') 
    644         return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), 
    645                 time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 
     649        if val is None: 
     650            date_data = '' 
     651            time_data = '' 
     652        else: 
     653            d = datetime_safe.new_datetime(val) 
     654            date_data = d.strftime('%Y-%m-%d') 
     655            time_data = d.strftime('%H:%M:%S') 
     656        return {date_field: date_data, time_field: time_data} 
    646657 
    647658    def formfield(self, **kwargs): 
    648659        defaults = {'form_class': forms.DateTimeField} 
     
    842853        self.path, self.match, self.recursive = path, match, recursive 
    843854        kwargs['max_length'] = kwargs.get('max_length', 100) 
    844855        Field.__init__(self, verbose_name, name, **kwargs) 
    845      
     856 
    846857    def formfield(self, **kwargs): 
    847858        defaults = { 
    848859            'path': self.path, 
  • django/core/serializers/json.py

    old new  
    66from django.utils import simplejson 
    77from django.core.serializers.python import Serializer as PythonSerializer 
    88from django.core.serializers.python import Deserializer as PythonDeserializer 
     9from django.utils import datetime_safe 
    910try: 
    1011    from cStringIO import StringIO 
    1112except ImportError: 
     
    2021    Convert a queryset to JSON. 
    2122    """ 
    2223    internal_use_only = False 
    23      
     24 
    2425    def end_serialization(self): 
    2526        self.options.pop('stream', None) 
    2627        self.options.pop('fields', None) 
     
    5152 
    5253    def default(self, o): 
    5354        if isinstance(o, datetime.datetime): 
    54             return o.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT)) 
     55            d = datetime_safe.new_datetime(o) 
     56            return d.strftime("%s %s" % (self.DATE_FORMAT, self.TIME_FORMAT)) 
    5557        elif isinstance(o, datetime.date): 
    56             return o.strftime(self.DATE_FORMAT) 
     58            d = datetime_safe.new_date(o) 
     59            return d.strftime(self.DATE_FORMAT) 
    5760        elif isinstance(o, datetime.time): 
    5861            return o.strftime(self.TIME_FORMAT) 
    5962        elif isinstance(o, decimal.Decimal): 
  • django/core/serializers/base.py

    old new  
    88    from StringIO import StringIO 
    99from django.db import models 
    1010from django.utils.encoding import smart_str, smart_unicode 
     11from django.utils import datetime_safe 
    1112 
    1213class SerializationError(Exception): 
    1314    """Something bad happened during serialization.""" 
     
    5960        Convert a field's value to a string. 
    6061        """ 
    6162        if isinstance(field, models.DateTimeField): 
    62             value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S") 
     63            d = datetime_safe.new_datetime(getattr(obj, field.name)) 
     64            value = d.strftime("%Y-%m-%d %H:%M:%S") 
    6365        else: 
    6466            value = field.flatten_data(follow=None, obj=obj).get(field.name, "") 
    6567        return smart_unicode(value) 
  • django/core/validators.py

    old new  
    141141    # Could use time.strptime here and catch errors, but datetime.date below 
    142142    # produces much friendlier error messages. 
    143143    year, month, day = map(int, date_string.split('-')) 
    144     # This check is needed because strftime is used when saving the date 
    145     # value to the database, and strftime requires that the year be >=1900. 
    146     if year < 1900: 
    147         raise ValidationError, _('Year must be 1900 or later.') 
    148144    try: 
    149145        date(year, month, day) 
    150146    except ValueError, e: 
     
    407403    """ 
    408404    Usage: If you create an instance of the IsPowerOf validator: 
    409405        v = IsAPowerOf(2) 
    410      
     406 
    411407    The following calls will succeed: 
    412         v(4, None)  
     408        v(4, None) 
    413409        v(8, None) 
    414410        v(16, None) 
    415      
     411 
    416412    But this call: 
    417413        v(17, None) 
    418414    will raise "django.core.validators.ValidationError: ['This value must be a power of 2.']" 
  • django/utils/datetime_safe.py

    old new  
     1# Python's datetime strftime doesn't handle dates before 1900. 
     2# These classes override date and datetime to support the formatting of a date 
     3# through its full "proleptic Gregorian" date range. 
     4# 
     5# Based on code submitted to comp.lang.python by Andrew Dalke 
     6# 
     7# >>> datetime_safe.date(1850, 8, 2).strftime("%Y/%M/%d was a %A") 
     8# '1850/08/02 was a Friday' 
     9 
     10from datetime import date as real_date, datetime as real_datetime 
     11import time 
     12import re 
     13 
     14 
     15class date(real_date): 
     16    def strftime(self, fmt): 
     17        return strftime(self, fmt) 
     18 
     19 
     20class datetime(real_datetime): 
     21    def strftime(self, fmt): 
     22        return strftime(self, fmt) 
     23    def combine(self, date, time): 
     24        return datetime(date.year, date.month, date.day, time.hour, time.minute, time.microsecond, time.tzinfo) 
     25    def date(self): 
     26        return date(self.year, self.month, self.day) 
     27 
     28 
     29def new_date(d): 
     30    """ Generate a safe date from a datetime.date object """ 
     31    return date(d.year, d.month, d.day) 
     32 
     33 
     34def new_datetime(d): 
     35    """ 
     36    Generate a safe datetime from a datetime.date or datetime.datetime object 
     37    """ 
     38    kw = [d.year, d.month, d.day] 
     39    if isinstance(d, real_datetime): 
     40        kw.extend([d.hour, d.minute, d.second, d.microsecond, d.tzinfo]) 
     41    return datetime(*kw) 
     42 
     43 
     44# No support for strftime's "%s" or "%y". 
     45# Allowed if there's an even number of "%"s because they are escaped. 
     46_illegal_formatting = re.compile(r"((^|[^%])(%%)*%[sy])") 
     47 
     48def _findall(text, substr): 
     49     # Also finds overlaps 
     50     sites = [] 
     51     i = 0 
     52     while 1: 
     53         j = text.find(substr, i) 
     54         if j == -1: 
     55             break 
     56         sites.append(j) 
     57         i=j+1 
     58     return sites 
     59 
     60def strftime(dt, fmt): 
     61    if dt.year >= 1900: 
     62        return super(type(dt), dt).strftime(fmt) 
     63    illegal_formatting = _illegal_formatting.search(fmt) 
     64    if illegal_formatting: 
     65        raise TypeError("strftime of dates before 1900 does not handle" + illegal_formatting.group(0)) 
     66 
     67    year = dt.year 
     68    # For every non-leap year century, advance by 
     69    # 6 years to get into the 28-year repeat cycle 
     70    delta = 2000 - year 
     71    off = 6*(delta // 100 + delta // 400) 
     72    year = year + off 
     73 
     74    # Move to around the year 2000 
     75    year = year + ((2000 - year)//28)*28 
     76    timetuple = dt.timetuple() 
     77    s1 = time.strftime(fmt, (year,) + timetuple[1:]) 
     78    sites1 = _findall(s1, str(year)) 
     79 
     80    s2 = time.strftime(fmt, (year+28,) + timetuple[1:]) 
     81    sites2 = _findall(s2, str(year+28)) 
     82 
     83    sites = [] 
     84    for site in sites1: 
     85        if site in sites2: 
     86            sites.append(site) 
     87 
     88    s = s1 
     89    syear = "%4d" % (dt.year,) 
     90    for site in sites: 
     91        s = s[:site] + syear + s[site+4:] 
     92    return s 
  • django/contrib/databrowse/plugins/calendars.py

    old new  
    88from django.utils.encoding import force_unicode 
    99from django.utils.safestring import mark_safe 
    1010from django.views.generic import date_based 
     11from django.utils import datetime_safe 
    1112 
    1213class CalendarPlugin(DatabrowsePlugin): 
    1314    def __init__(self, field_names=None): 
     
    3334 
    3435    def urls(self, plugin_name, easy_instance_field): 
    3536        if isinstance(easy_instance_field.field, models.DateField): 
     37            d = easy_instance_field.raw_value 
    3638            return [mark_safe(u'%s%s/%s/%s/%s/%s/' % ( 
    3739                easy_instance_field.model.url(), 
    3840                plugin_name, easy_instance_field.field.name, 
    39                 easy_instance_field.raw_value.year, 
    40                 easy_instance_field.raw_value.strftime('%b').lower(), 
    41                 easy_instance_field.raw_value.day))] 
     41                d.year, 
     42                datetime_safe.new_date(d).strftime('%b').lower(), 
     43                d.day))] 
    4244 
    4345    def model_view(self, request, model_databrowse, url): 
    4446        self.model, self.site = model_databrowse.model, model_databrowse.site 
  • django/newforms/widgets.py

    old new  
    1515from django.utils.translation import ugettext 
    1616from django.utils.encoding import StrAndUnicode, force_unicode 
    1717from django.utils.safestring import mark_safe 
     18from django.utils import datetime_safe 
    1819from util import flatatt 
    1920 
    2021__all__ = ( 
     
    170171        if value is None: 
    171172            value = '' 
    172173        elif hasattr(value, 'strftime'): 
     174            value = datetime_safe.new_datetime(value) 
    173175            value = value.strftime(self.format) 
    174176        return super(DateTimeInput, self).render(name, value, attrs) 
    175177 
  • tests/regressiontests/datetime_safe/tests.py

    old new  
     1r""" 
     2>>> original_datetime(*more_recent) == datetime(*more_recent) 
     3True 
     4>>> original_datetime(*really_old) == datetime(*really_old) 
     5True 
     6>>> original_date(*more_recent) == date(*more_recent) 
     7True 
     8>>> original_date(*really_old) == date(*really_old) 
     9True 
     10 
     11>>> original_date(*just_safe).strftime('%Y-%m-%d') == date(*just_safe).strftime('%Y-%m-%d') 
     12True 
     13>>> original_datetime(*just_safe).strftime('%Y-%m-%d') == datetime(*just_safe).strftime('%Y-%m-%d') 
     14True 
     15 
     16>>> date(*just_unsafe[:3]).strftime('%Y-%m-%d (weekday %w)') 
     17'1899-12-31 (weekday 0)' 
     18>>> date(*just_safe).strftime('%Y-%m-%d (weekday %w)') 
     19'1900-01-01 (weekday 1)' 
     20 
     21>>> datetime(*just_unsafe).strftime('%Y-%m-%d %H:%M:%S (weekday %w)') 
     22'1899-12-31 23:59:59 (weekday 0)' 
     23>>> datetime(*just_safe).strftime('%Y-%m-%d %H:%M:%S (weekday %w)') 
     24'1900-01-01 00:00:00 (weekday 1)' 
     25 
     26>>> date(*just_safe).strftime('%y')   # %y will error before this date 
     27'00' 
     28>>> datetime(*just_safe).strftime('%y') 
     29'00' 
     30""" 
     31 
     32from datetime import date as original_date, datetime as original_datetime 
     33from django.utils.datetime_pg import date, datetime 
     34 
     35just_safe = (1900, 1, 1) 
     36just_unsafe = (1899, 12, 31, 23, 59, 59) 
     37really_old = (20, 1, 1) 
     38more_recent = (2006, 1, 1)