3939 (?P<n>\.)? # .
4040 (((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d?(\.\d+)?))?)? # hh:mm:ss
4141 (?P<o>[\d\smywd\-+]+)? # offset
42+ (?P<tz>[+-]\d{4})? # time-zone offset
4243$''' , re .VERBOSE )
4344serialised_date_re = re .compile (r'''
4445 (\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d?(\.\d+)?)
@@ -158,6 +159,19 @@ def _local_to_utc(y,m,d,H,M,S,tz):
158159 y ,m ,d ,H ,M ,S = TZ .localize (dt ).utctimetuple ()[:6 ]
159160 return (y ,m ,d ,H ,M ,S )
160161
162+ def test_ini (t ):
163+ """ Monkey-patch to make doctest think it's always time t:
164+ """
165+ u = Date .now
166+ d = datetime .datetime .strptime (t , '%Y-%m-%d.%H:%M:%S.%f' )
167+ Date .now = lambda x : d
168+ return u
169+
170+ def test_fin (u ):
171+ """ Undo monkey patch above
172+ """
173+ Date .now = u
174+
161175class Date :
162176 '''
163177 As strings, date-and-time stamps are specified with the date in
@@ -166,7 +180,7 @@ class Date:
166180 and are fairly readable when printed. An example of a valid stamp is
167181 "2000-06-24.13:03:59". We'll call this the "full date format". When
168182 Timestamp objects are printed as strings, they appear in the full date
169- format with the time always given in GMT . The full date format is
183+ format with the time always given in UTC . The full date format is
170184 always exactly 19 characters long.
171185
172186 For user input, some partial forms are also permitted: the whole time
@@ -197,40 +211,46 @@ class Date:
197211 2000-08-04 (rather than trying to decide whether 1m 10d means 38 or 40
198212 or 41 days). Example usage::
199213
214+ make doctest think it's always 2000-06-26.00:34:02:
215+ >>> u = test_ini('2000-06-26.00:34:02.0')
216+
200217 >>> Date(".")
201- <Date 2000-06-26.00:34:02>
218+ <Date 2000-06-26.00:34:02.000 >
202219 >>> _.local(-5)
203- " 2000-06-25.19:34:02"
220+ <Date 2000-06-25.19:34:02.000>
204221 >>> Date(". + 2d")
205- <Date 2000-06-28.00:34:02>
222+ <Date 2000-06-28.00:34:02.000 >
206223 >>> Date("1997-04-17", -5)
207- <Date 1997-04-17.00 :00:00>
224+ <Date 1997-04-17.05 :00:00.000 >
208225 >>> Date("01-25", -5)
209- <Date 2000-01-25.00 :00:00>
226+ <Date 2000-01-25.05 :00:00.000 >
210227 >>> Date("08-13.22:13", -5)
211- <Date 2000-08-14.03:13:00>
228+ <Date 2000-08-14.03:13:00.000 >
212229 >>> Date("14:25", -5)
213- <Date 2000-06-25 .19:25:00>
230+ <Date 2000-06-26 .19:25:00.000 >
214231
215232 The date format 'yyyymmddHHMMSS' (year, month, day, hour,
216233 minute, second) is the serialisation format returned by the serialise()
217234 method, and is accepted as an argument on instatiation.
218235
219236 The date class handles basic arithmetic::
220237
238+ >>> x=test_ini('2004-04-06.22:04:20.766830')
221239 >>> d1=Date('.')
222240 >>> d1
223- <Date 2004-04-06.22:04:20.766830 >
241+ <Date 2004-04-06.22:04:20.767 >
224242 >>> d2=Date('2003-07-01')
225243 >>> d2
226- <Date 2003-07-01.00:00:0.000000 >
244+ <Date 2003-07-01.00:00:00.000 >
227245 >>> d1-d2
228246 <Interval + 280d 22:04:20>
229247 >>> i1=_
230248 >>> d2+i1
231- <Date 2004-04-06.22:04:20.000000 >
249+ <Date 2004-04-06.22:04:20.000 >
232250 >>> d1-i1
233- <Date 2003-07-01.00:00:0.000000>
251+ <Date 2003-07-01.00:00:00.000>
252+
253+ >>> test_fin(u)
234254 '''
235255
236256 def __init__ (self , spec = '.' , offset = 0 , add_granularity = False ,
@@ -281,6 +301,11 @@ def __init__(self, spec='.', offset=0, add_granularity=False,
281301 except :
282302 raise ValueError , 'Unknown spec %r' % (spec ,)
283303
304+ def now (self ):
305+ """ To be able to override for testing
306+ """
307+ return datetime .datetime .utcnow ()
308+
284309 def set (self , spec , offset = 0 , date_re = date_re ,
285310 serialised_re = serialised_date_re , add_granularity = False ):
286311 ''' set the date to the value in spec
@@ -298,9 +323,9 @@ def set(self, spec, offset=0, date_re=date_re,
298323 # not serialised data, try usual format
299324 m = date_re .match (spec )
300325 if m is None :
301- raise ValueError , self ._ ('Not a date spec: '
302- '"yyyy-mm-dd", "mm-dd", "HH:MM", "HH:MM:SS" or '
303- '"yyyy-mm-dd.HH:MM:SS.SSS"' )
326+ raise ValueError , self ._ ('Not a date spec: %r '
327+ '( "yyyy-mm-dd", "mm-dd", "HH:MM", "HH:MM:SS" or '
328+ '"yyyy-mm-dd.HH:MM:SS.SSS")' % spec )
304329
305330 info = m .groupdict ()
306331
@@ -324,7 +349,7 @@ def set(self, spec, offset=0, date_re=date_re,
324349 raise ValueError (self ._ ('Could not determine granularity' ))
325350
326351 # get the current date as our default
327- dt = datetime . datetime . utcnow ()
352+ dt = self . now ()
328353 y ,m ,d ,H ,M ,S ,x ,x ,x = dt .timetuple ()
329354 S += dt .microsecond / 1000000.
330355
@@ -603,10 +628,13 @@ class Interval:
603628 "0:04:33" means four minutes and 33 seconds
604629
605630 Example usage:
631+ make doctest think it's always 2000-06-26.00:34:02:
632+ >>> u = test_ini('2000-06-26.00:34:02.0')
633+
606634 >>> Interval(" 3w 1 d 2:00")
607635 <Interval + 22d 2:00>
608636 >>> Date(". + 2d") + Interval("- 3w")
609- <Date 2000-06-07.00:34:02>
637+ <Date 2000-06-07.00:34:02.000 >
610638 >>> Interval('1:59:59') + Interval('00:00:01')
611639 <Interval + 2:00>
612640 >>> Interval('2:00') + Interval('- 00:00:01')
@@ -615,10 +643,16 @@ class Interval:
615643 <Interval + 6m>
616644 >>> Interval('1:00')/2
617645 <Interval + 0:30>
646+
647+ [number of days between 2000-06-26.00:34:02 and 2003-03-18
618648 >>> Interval('2003-03-18')
619- <Interval + [number of days between now and 2003-03-18]>
649+ <Interval - 995d>
650+
651+ [number of days between 2000-06-26.00:34:02 and 2003-03-14
620652 >>> Interval('-4d 2003-03-18')
621- <Interval + [number of days between now and 2003-03-14]>
653+ <Interval - 991d>
654+
655+ >>> test_fin(u)
622656
623657 Interval arithmetic is handled in a couple of special ways, trying
624658 to cater for the most common cases. Fundamentally, Intervals which
@@ -1011,24 +1045,40 @@ class Range:
10111045
10121046 Examples (consider local time is Sat Mar 8 22:07:48 EET 2003)::
10131047
1014- >>> Range("from 2-12 to 4-2")
1048+ make doctest think it's always 2000-06-26.00:34:02:
1049+ >>> u = test_ini('2003-03-08.20:07:48.0')
1050+
1051+ >>> Range("from 2-12 to 4-2", Date)
10151052 <Range from 2003-02-12.00:00:00 to 2003-04-02.00:00:00>
10161053
1017- >>> Range("18:00 TO +2m")
1054+ >>> Range("18:00 to +2m", Date )
10181055 <Range from 2003-03-08.18:00:00 to 2003-05-08.20:07:48>
10191056
1020- >>> Range("12:00")
1057+ >>> Range("tO +3d", Date)
1058+ <Range from None to 2003-03-11.20:07:48>
1059+
1060+ >>> Range("12:00 to", Date)
10211061 <Range from 2003-03-08.12:00:00 to None>
10221062
1023- >>> Range("tO +3d" )
1024- <Range from None to 2003-03-11.20:07:48 >
1063+ >>> Range("12:00;", Date )
1064+ <Range from 2003-03-08.12:00:00 to None >
10251065
1026- >>> Range("2002-11-10; 2002-12-12")
1066+ >>> Range("2002-11-10; 2002-12-12", Date )
10271067 <Range from 2002-11-10.00:00:00 to 2002-12-12.00:00:00>
10281068
1029- >>> Range("; 20:00 +1d")
1069+ >>> Range("; 20:00 +1d", Date )
10301070 <Range from None to 2003-03-09.20:00:00>
10311071
1072+ Granularity tests:
1073+
1074+ >>> Range("12:00", Date)
1075+ <Range from 2003-03-08.12:00:00 to 2003-03-08.12:00:59>
1076+
1077+ >>> Range("2003-03-08", Date)
1078+ <Range from 2003-03-08.00:00:00 to 2003-03-08.23:59:59>
1079+
1080+ >>> test_fin(u)
1081+
10321082 """
10331083 def __init__ (self , spec , Type , allow_granularity = True , ** params ):
10341084 """Initializes Range of type <Type> from given <spec> string.
@@ -1041,8 +1091,8 @@ def __init__(self, spec, Type, allow_granularity=True, **params):
10411091 class instance.
10421092 """
10431093 self .range_type = Type
1044- re_range = r'(?:^| from(.+?))(?: to(.+?)$|$) '
1045- re_geek_range = r'(?:^| (.+?));(?:( .+?)$|$) '
1094+ re_range = r'^ (?:from)? (.+?)? to(.+?)?$ '
1095+ re_geek_range = r'^ (.+?)?;( .+?)?$ '
10461096 # Check which syntax to use
10471097 if ';' in spec :
10481098 # Geek
0 commit comments