1414# FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
1515# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17- #
18- # $Id: date.py,v 1.68 2004-05-06 02:35:46 richard Exp $
17+ #
18+ # $Id: date.py,v 1.69 2004-05-19 17:12:18 a1s Exp $
1919
2020"""Date, time and time interval handling.
2121"""
2222__docformat__ = 'restructuredtext'
2323
2424import time , re , calendar , types
2525from types import *
26- from i18n import _
26+ import i18n
2727
2828def _add_granularity (src , order , value = 1 ):
2929 '''Increment first non-None value in src dictionary ordered by 'order'
@@ -43,7 +43,7 @@ class Date:
4343 "2000-06-24.13:03:59". We'll call this the "full date format". When
4444 Timestamp objects are printed as strings, they appear in the full date
4545 format with the time always given in GMT. The full date format is
46- always exactly 19 characters long.
46+ always exactly 19 characters long.
4747
4848 For user input, some partial forms are also permitted: the whole time
4949 or just the seconds may be omitted; and the whole date may be omitted
@@ -109,15 +109,21 @@ class Date:
109109 <Date 2003-07-01.00:00:0.000000>
110110 '''
111111
112- def __init__ (self , spec = '.' , offset = 0 , add_granularity = 0 ):
112+ def __init__ (self , spec = '.' , offset = 0 , add_granularity = 0 , translator = i18n ):
113113 """Construct a date given a specification and a time zone offset.
114114
115115 'spec'
116116 is a full date or a partial form, with an optional added or
117117 subtracted interval. Or a date 9-tuple.
118118 'offset'
119119 is the local time zone offset from GMT in hours.
120+ 'translator'
121+ is i18n module or one of gettext translation classes.
122+ It must have attributes 'gettext' and 'ngettext',
123+ serving as translation functions.
120124 """
125+ self ._ = translator .gettext
126+ self .ngettext = translator .ngettext
121127 if type (spec ) == type ('' ):
122128 self .set (spec , offset = offset , add_granularity = add_granularity )
123129 return
@@ -159,7 +165,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
159165 # not serialised data, try usual format
160166 m = date_re .match (spec )
161167 if m is None :
162- raise ValueError , _ ('Not a date spec: %s' % self .usagespec )
168+ raise ValueError , self . _ ('Not a date spec: %s' ) % self .usagespec
163169
164170 info = m .groupdict ()
165171
@@ -170,7 +176,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
170176 ts = time .time ()
171177 frac = ts - int (ts )
172178 y ,m ,d ,H ,M ,S ,x ,x ,x = time .gmtime (ts )
173- # gmtime loses the fractional seconds
179+ # gmtime loses the fractional seconds
174180 S = S + frac
175181
176182 if info ['y' ] is not None or info ['a' ] is not None :
@@ -197,7 +203,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
197203
198204 if add_granularity :
199205 S = S - 1
200-
206+
201207 # now handle the adjustment of hour
202208 frac = S - int (S )
203209 ts = calendar .timegm ((y ,m ,d ,H ,M ,S ,0 ,0 ,0 ))
@@ -210,7 +216,7 @@ def set(self, spec, offset=0, date_re=re.compile(r'''
210216 try :
211217 self .applyInterval (Interval (info ['o' ], allowdate = 0 ))
212218 except ValueError :
213- raise ValueError , _ ('%r not a date spec (%s)' )% (spec ,
219+ raise ValueError , self . _ ('%r not a date spec (%s)' )% (spec ,
214220 self .usagespec )
215221
216222 def addInterval (self , interval ):
@@ -249,11 +255,11 @@ def get_mdays(year, month):
249255
250256 while month < 1 or month > 12 or day < 1 or day > get_mdays (year ,month ):
251257 # now to day under/over
252- if day < 1 :
258+ if day < 1 :
253259 # When going backwards, decrement month, then increment days
254260 month -= 1
255261 day += get_mdays (year ,month )
256- elif day > get_mdays (year ,month ):
262+ elif day > get_mdays (year ,month ):
257263 # When going forwards, decrement days, then increment month
258264 day -= get_mdays (year ,month )
259265 month += 1
@@ -432,8 +438,12 @@ class Interval:
432438
433439 TODO: more examples, showing the order of addition operation
434440 '''
435- def __init__ (self , spec , sign = 1 , allowdate = 1 , add_granularity = 0 ):
441+ def __init__ (self , spec , sign = 1 , allowdate = 1 , add_granularity = 0 ,
442+ translator = i18n
443+ ):
436444 """Construct an interval given a specification."""
445+ self ._ = translator .gettext
446+ self .ngettext = translator .ngettext
437447 if type (spec ) in (IntType , FloatType , LongType ):
438448 self .from_seconds (spec )
439449 elif type (spec ) in (StringType , UnicodeType ):
@@ -474,8 +484,8 @@ def set(self, spec, allowdate=1, interval_re=re.compile('''
474484 if not m :
475485 m = interval_re .match (spec )
476486 if not m :
477- raise ValueError , _ ('Not an interval spec: [+-] [#y] [#m] [#w] '
478- '[#d] [[[H]H:MM]:SS] [date spec]' )
487+ raise ValueError , self . _ ('Not an interval spec:'
488+ ' [+-] [#y] [#m] [#w] [#d] [[[H]H:MM]:SS] [date spec]' )
479489 else :
480490 allowdate = 0
481491
@@ -493,8 +503,8 @@ def set(self, spec, allowdate=1, interval_re=re.compile('''
493503
494504 # make sure it's valid
495505 if not valid and not info ['D' ]:
496- raise ValueError , _ ('Not an interval spec: [+-] [#y] [#m] [#w] '
497- '[#d] [[[H]H:MM]:SS]' )
506+ raise ValueError , self . _ ('Not an interval spec:'
507+ ' [+-] [#y] [#m] [#w] [#d] [[[H]H:MM]:SS]' )
498508
499509 if self .week :
500510 self .day = self .day + self .week * 7
@@ -625,55 +635,61 @@ def __repr__(self):
625635 def pretty (self ):
626636 ''' print up the date date using one of these nice formats..
627637 '''
638+ _quarters = self .minute / 15
628639 if self .year :
629- if self .year == 1 :
630- s = _ ('1 year' )
631- else :
632- s = _ ('%(number)s years' )% {'number' : self .year }
633- elif self .month or self .day > 13 :
634- days = (self .month * 30 ) + self .day
635- if days > 28 :
636- if int (days / 30 ) > 1 :
637- s = _ ('%(number)s months' )% {'number' : int (days / 30 )}
638- else :
639- s = _ ('1 month' )
640- else :
641- s = _ ('%(number)s weeks' )% {'number' : int (days / 7 )}
640+ s = self .ngettext ("%(number)s year" , "%(number)s years" ,
641+ self .year ) % {'number' : self .year }
642+ elif self .month or self .day > 28 :
643+ _months = int (((self .month * 30 ) + self .day ) / 30 )
644+ s = self .ngettext ("%(number)s month" , "%(number)s months" ,
645+ _months ) % {'number' : _months }
642646 elif self .day > 7 :
643- s = _ ('1 week' )
647+ _weeks = int (self .day / 7 )
648+ s = self .ngettext ("%(number)s week" , "%(number)s weeks" ,
649+ _weeks ) % {'number' : _weeks }
644650 elif self .day > 1 :
645- s = _ ('%(number)s days' )% {'number' : self .day }
651+ # Note: singular form is not used
652+ s = self .ngettext ('%(number)s day' , '%(number)s days' ,
653+ self .day ) % {'number' : self .day }
646654 elif self .day == 1 or self .hour > 12 :
647655 if self .sign > 0 :
648- return _ ('tomorrow' )
656+ return self . _ ('tomorrow' )
649657 else :
650- return _ ('yesterday' )
658+ return self . _ ('yesterday' )
651659 elif self .hour > 1 :
652- s = _ ('%(number)s hours' )% {'number' : self .hour }
660+ # Note: singular form is not used
661+ s = self .ngettext ('%(number)s hour' , '%(number)s hours' ,
662+ self .hour ) % {'number' : self .hour }
653663 elif self .hour == 1 :
654664 if self .minute < 15 :
655- s = _ ('an hour' )
656- elif self . minute / 15 == 2 :
657- s = _ ('1 1/2 hours' )
665+ s = self . _ ('an hour' )
666+ elif _quarters == 2 :
667+ s = self . _ ('1 1/2 hours' )
658668 else :
659- s = _ ('1 %(number)s/4 hours' )% {'number' : self .minute / 15 }
669+ s = self .ngettext ('1 %(number)s/4 hours' ,
670+ '1 %(number)s/4 hours' , _quarters ) % {'number' : _quarters }
660671 elif self .minute < 1 :
661672 if self .sign > 0 :
662- return _ ('in a moment' )
673+ return self . _ ('in a moment' )
663674 else :
664- return _ ('just now' )
675+ return self . _ ('just now' )
665676 elif self .minute == 1 :
666- s = _ ('1 minute' )
677+ # Note: used in expressions "in 1 minute" or "1 minute ago"
678+ s = self ._ ('1 minute' )
667679 elif self .minute < 15 :
668- s = _ ('%(number)s minutes' )% {'number' : self .minute }
669- elif int (self .minute / 15 ) == 2 :
670- s = _ ('1/2 an hour' )
680+ # Note: used in expressions "in 2 minutes" or "2 minutes ago"
681+ s = self .ngettext ('%(number)s minute' , '%(number)s minutes' ,
682+ self .minute ) % {'number' : self .minute }
683+ elif _quarters == 2 :
684+ s = self ._ ('1/2 an hour' )
671685 else :
672- s = _ ('%(number)s/4 hour' )% {'number' : int (self .minute / 15 )}
673- if self .sign < 0 :
674- s = s + _ (' ago' )
686+ s = self .ngettext ('%(number)s/4 hours' , '%(number)s/4 hours' ,
687+ _quarters ) % {'number' : _quarters }
688+ # XXX this is internationally broken
689+ if self .sign < 0 :
690+ s = self ._ ('%s ago' ) % s
675691 else :
676- s = _ ('in ' ) + s
692+ s = self . _ ('in %s ' ) % s
677693 return s
678694
679695 def get_tuple (self ):
@@ -687,7 +703,7 @@ def serialise(self):
687703
688704 def as_seconds (self ):
689705 '''Calculate the Interval as a number of seconds.
690-
706+
691707 Months are counted as 30 days, years as 365 days. Returns a Long
692708 int.
693709 '''
@@ -756,7 +772,7 @@ def fixTimeOverflow(time):
756772class Range :
757773 """Represents range between two values
758774 Ranges can be created using one of theese two alternative syntaxes:
759-
775+
760776 1. Native english syntax::
761777
762778 [[From] <value>][ To <value>]
@@ -774,33 +790,33 @@ class Range:
774790
775791 >>> Range("from 2-12 to 4-2")
776792 <Range from 2003-02-12.00:00:00 to 2003-04-02.00:00:00>
777-
793+
778794 >>> Range("18:00 TO +2m")
779795 <Range from 2003-03-08.18:00:00 to 2003-05-08.20:07:48>
780-
796+
781797 >>> Range("12:00")
782798 <Range from 2003-03-08.12:00:00 to None>
783-
799+
784800 >>> Range("tO +3d")
785801 <Range from None to 2003-03-11.20:07:48>
786-
802+
787803 >>> Range("2002-11-10; 2002-12-12")
788804 <Range from 2002-11-10.00:00:00 to 2002-12-12.00:00:00>
789-
805+
790806 >>> Range("; 20:00 +1d")
791807 <Range from None to 2003-03-09.20:00:00>
792808
793809 """
794810 def __init__ (self , spec , Type , allow_granularity = 1 , ** params ):
795811 """Initializes Range of type <Type> from given <spec> string.
796-
812+
797813 Sets two properties - from_value and to_value. None assigned to any of
798814 this properties means "infinitum" (-infinitum to from_value and
799815 +infinitum to to_value)
800816
801817 The Type parameter here should be class itself (e.g. Date), not a
802818 class instance.
803-
819+
804820 """
805821 self .range_type = Type
806822 re_range = r'(?:^|from(.+?))(?:to(.+?)$|$)'
@@ -830,7 +846,7 @@ def __str__(self):
830846
831847 def __repr__ (self ):
832848 return "<Range %s>" % self .__str__ ()
833-
849+
834850def test_range ():
835851 rspecs = ("from 2-12 to 4-2" , "from 18:00 TO +2m" , "12:00;" , "tO +3d" ,
836852 "2002-11-10; 2002-12-12" , "; 20:00 +1d" , '2002-10-12' )
0 commit comments