6
from twisted.trial import unittest
8
from epsilon import extime
10
# This is the implementation of 'mkgmtime' used to derive the values below. It
11
# is perhaps instructive to read, but it remains commented out to avoid the
12
# temptation to actually call it. If have a GMT time-tuple, just use
13
# Time.fromStructTime(gmtt).asPOSIXTimestamp() to convert it; this was only
14
# written as an alternative implementation to test that code path.
17
# 'convert GMT time-tuple to local time'
18
# if time.daylight and gmtt[-1]:
21
# zone = time.timezone
22
# return time.mktime(gmtt) - zone
25
class TestTime(unittest.TestCase):
26
class MST(datetime.tzinfo):
29
def utcoffset(self, dt):
30
return datetime.timedelta(hours = -7)
32
return datetime.timedelta(0)
34
class CET(datetime.tzinfo):
37
def utcoffset(self, dt):
38
return datetime.timedelta(hours = 1)
40
return datetime.timedelta(0)
42
reference = datetime.datetime(2004, 12, 6, 14, 15, 16)
43
awareReference = datetime.datetime(2004, 12, 6, 14, 15, 16, tzinfo=extime.FixedOffset(0, 0))
45
def _checkReference(self, timeInstance, reference=None):
47
Check timeInstance against self.reference.
49
self.assertEquals(timeInstance._time, reference or self.reference)
51
def _createReference(self, reference=None):
53
Return a reference instance.
55
return extime.Time.fromDatetime(reference or self.reference)
57
def test_pytzWeirdness(self):
59
pytz weirdness; RT ticket #2755
64
raise unittest.SkipTest, 'pytz could not be imported'
65
tz = pytz.timezone('America/Detroit')
66
time = extime.Time.fromRFC2822('Wed, 06 Apr 2005 23:12:27 -0400')
67
dtime = time.asDatetime(tz)
68
self.assertEquals(dtime.hour, 23)
69
self.assertEquals(dtime.minute, 12)
74
self.assertEquals(extime.Time.fromStructTime(now), extime.Time.fromStructTime(now))
76
extime.Time.fromStructTime(now),
77
extime.Time.fromStructTime(now) + datetime.timedelta(seconds=42))
78
self.assertNotEquals(extime.Time.fromStructTime(now), 13)
80
aTime = extime.Time.fromStructTime(now)
81
for op in 'lt', 'le', 'gt', 'ge':
82
self.assertRaises(TypeError, getattr(operator, op), aTime, now)
85
def test_fromNow(self):
86
diff = datetime.datetime.utcnow() - extime.Time()._time
87
if diff < datetime.timedelta():
89
self.failUnless(diff.days == 0 and diff.seconds <= 5, 'Time created now is %r away from now' % (diff,))
91
def test_insignificantTimezones(self):
93
Timezones should be insignificant when the resolution is >= 1 day
95
def testEqual(creator, input):
96
self.assertEquals(creator(input), creator(input, tzinfo=self.MST()))
98
def testNotEqual(creator, input):
99
self.assertNotEquals(creator(input), creator(input, tzinfo=self.MST()))
101
testEqual(extime.Time.fromHumanly, 'sunday')
102
testEqual(extime.Time.fromISO8601TimeAndDate, '2005')
103
testEqual(extime.Time.fromISO8601TimeAndDate, '2005-02')
104
testEqual(extime.Time.fromISO8601TimeAndDate, '2005-02-10')
106
testNotEqual(extime.Time.fromISO8601TimeAndDate, '2005-02-10T12')
107
testNotEqual(extime.Time.fromISO8601TimeAndDate, '2005-02-10T12:10')
108
testNotEqual(extime.Time.fromISO8601TimeAndDate, '2005-02-10T12:10:03')
110
def test_fromHumanly(self):
111
def test(input, expected, tzinfo=None):
112
time = extime.Time.fromHumanly(
115
self._createReference())
118
time.asISO8601TimeAndDate(),
123
def testMalformed(input):
124
self.assertRaises(ValueError, extime.Time.fromHumanly, input)
126
def testDay(input, expected, tzinfo=None):
127
time = test(input, expected, tzinfo)
128
self.assert_(time.isAllDay())
130
def testMinute(input, expected, tzinfo=None):
131
time = test(input, expected, tzinfo)
132
self.assertEquals(time.resolution, datetime.timedelta(minutes=1))
134
def testMicrosecond(input, expected, tzinfo=None):
135
time = test(input, expected, tzinfo)
136
self.assertEquals(time.resolution, datetime.timedelta(microseconds=1))
138
# 'now' is Monday, 2004-12-06 14:15:16 UTC
139
testDay('yesterday', '2004-12-05')
140
testDay(' ToDaY ', '2004-12-06')
141
testDay(' TuESDaY ', '2004-12-07')
142
testDay(' ToMoRroW ', '2004-12-07')
143
testDay('wednesday', '2004-12-08')
144
testDay('This wednesday', '2004-12-08')
145
testDay('neXt wednesday', '2004-12-08')
146
testDay('thursday', '2004-12-09')
147
testDay('friday', '2004-12-10')
148
testDay('saturday', '2004-12-11')
149
testDay('sunday', '2004-12-12')
150
testDay('sunday', '2004-12-12', self.MST()) # timezone is insignificant for dates with resolution >= 1 day
151
testDay('monday', '2004-12-13')
153
testMinute('15:00', '2004-12-06T15:00+00:00')
154
testMinute('8:00', '2004-12-06T15:00+00:00', self.MST())
155
testMinute(' 14:00 ', '2004-12-07T14:00+00:00')
156
testMinute(' 2:00 pm ', '2004-12-07T14:00+00:00')
157
testMinute(' 02:00 pm ', '2004-12-07T14:00+00:00')
158
testMinute(' noon ', '2004-12-07T12:00+00:00')
159
testMinute('midnight', '2004-12-07T00:00+00:00')
161
testMicrosecond('now', '2004-12-06T14:15:16+00:00')
162
testMicrosecond(' noW ', '2004-12-06T14:15:16+00:00')
164
testMalformed('24:01')
165
testMalformed('24:00') # this one might be considered valid by some people, but it's just dumb.
166
testMalformed('13:00pm')
167
testMalformed('13:00am')
169
# these are perfectly reasonable cases, but are totally broken. Good enough for demo work.
170
testMalformed('13:00 tomorrow')
171
testMalformed('13:00 next thursday')
172
testMalformed('last monday')
174
def test_fromISO8601DateAndTime(self):
175
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-12-06T14:15:16') )
176
self._checkReference( extime.Time.fromISO8601TimeAndDate('20041206T141516') )
177
self._checkReference( extime.Time.fromISO8601TimeAndDate('20041206T091516-0500') )
178
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-12-06T07:15:16', self.MST()) )
179
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-12-06T14:15:16Z', self.MST()) )
180
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-12-06T14:15:16-0000', self.MST()) )
181
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-12-06T14:15:16-0000') )
182
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-W50-1T14:15:16') )
183
self._checkReference( extime.Time.fromISO8601TimeAndDate('2004-341T14:15:16') )
185
self.assertRaises( ValueError, extime.Time.fromISO8601TimeAndDate, '2005-W53' )
186
self.assertRaises( ValueError, extime.Time.fromISO8601TimeAndDate, '2004-367' )
188
extime.Time.fromISO8601TimeAndDate('2004-366')
190
raise unittest.FailTest, 'leap years should have 366 days'
193
extime.Time.fromISO8601TimeAndDate('2004-123T14-0600')
194
extime.Time.fromISO8601TimeAndDate('2004-123T14:13-0600')
195
extime.Time.fromISO8601TimeAndDate('2004-123T14:13:51-0600')
197
raise unittest.FailTest, 'timezone should be allowed if time with *any* resolution is specified'
199
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2005').resolution, datetime.timedelta(days=365) )
200
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004').resolution, datetime.timedelta(days=366) )
201
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02').resolution, datetime.timedelta(days=29) )
202
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29').resolution, datetime.timedelta(days=1) )
203
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13').resolution, datetime.timedelta(hours=1) )
204
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13:10').resolution, datetime.timedelta(minutes=1) )
205
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13:10:05').resolution, datetime.timedelta(seconds=1) )
206
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13:10:05.010').resolution, datetime.timedelta(microseconds=1000) )
207
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13:10:05.010000').resolution, datetime.timedelta(microseconds=1) )
208
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-02-29T13:10:05.010000123').resolution, datetime.timedelta(microseconds=1) )
209
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-W11').resolution, datetime.timedelta(days=7) )
210
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-W11-3').resolution, datetime.timedelta(days=1) )
211
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-W11-3T14:16:21').resolution, datetime.timedelta(seconds=1) )
212
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-123').resolution, datetime.timedelta(days=1) )
213
self.assertEquals( extime.Time.fromISO8601TimeAndDate('2004-123T14:16:21').resolution, datetime.timedelta(seconds=1) )
215
def test_fromStructTime(self):
216
self._checkReference( extime.Time.fromStructTime((2004, 12, 6, 14, 15, 16, 0, 0, 0)) )
217
self._checkReference( extime.Time.fromStructTime((2004, 12, 6, 7, 15, 16, 0, 0, 0), self.MST()) )
218
self._checkReference( extime.Time.fromStructTime((2004, 12, 6, 15, 15, 16, 0, 0, 0), self.CET()) )
219
self._checkReference( extime.Time.fromStructTime(time.struct_time((2004, 12, 6, 7, 15, 16, 0, 0, 0)), self.MST()) )
221
def test_sanitizeStructTime(self):
223
Ensure that sanitizeStructTime does not modify valid times and
224
rounds down invalid ones.
226
t1 = (2004, 12, 6, 14, 15, 16, 0, 0, 0)
227
t2 = (2004, 12, 33, 14, 15, 61, 1, 2, 3)
228
cleanT2 = (2004, 12, 31, 14, 15, 59, 1, 2, 3)
229
self.assertEqual(extime.sanitizeStructTime(t1), t1)
230
self.assertEqual(extime.sanitizeStructTime(t2), cleanT2)
232
t3 = (2004, -12, 33, 14, 15, 61, 1, 2, 3)
233
cleanT3 = (2004, 1, 31, 14, 15, 59, 1, 2, 3)
234
self.assertEqual(extime.sanitizeStructTime(t3), cleanT3)
236
def test_fromDatetime(self):
237
self._checkReference( extime.Time.fromDatetime(datetime.datetime(2004, 12, 6, 14, 15, 16)) )
238
self._checkReference( extime.Time.fromDatetime(datetime.datetime(2004, 12, 6, 7, 15, 16, tzinfo=self.MST())) )
239
self._checkReference( extime.Time.fromDatetime(datetime.datetime(2004, 12, 6, 15, 15, 16, tzinfo=self.CET())) )
241
def test_fromPOSIXTimestamp(self):
242
# if there were an 'mkgmtime', it would do this:
243
# mkgmtime((2004, 12, 6, 14, 15, 16, 0, 0, 0))) = 1102342516.0
244
self._checkReference( extime.Time.fromPOSIXTimestamp(1102342516.0))
246
def test_fromRFC2822(self):
247
self._checkReference( extime.Time.fromRFC2822('Mon, 6 Dec 2004 14:15:16 -0000') )
248
self._checkReference( extime.Time.fromRFC2822('Mon, 6 Dec 2004 9:15:16 -0500') )
249
self._checkReference( extime.Time.fromRFC2822('6 Dec 2004 9:15:16 -0500') )
250
self._checkReference( extime.Time.fromRFC2822('Mon,6 Dec 2004 9:15:16 -0500') )
251
self._checkReference( extime.Time.fromRFC2822('Mon,6 Dec 2004 9:15 -0500'), datetime.datetime(2004, 12, 6, 14, 15) )
252
self._checkReference( extime.Time.fromRFC2822('Mon,6 Dec 2004 9:15:16 EST') )
253
self._checkReference( extime.Time.fromRFC2822('Monday,6 December 2004 9:15:16 EST') )
254
self._checkReference( extime.Time.fromRFC2822('Monday,6 December 2004 14:15:16') )
255
self.assertRaises( ValueError, extime.Time.fromRFC2822, 'some invalid date' )
257
def test_twentyThirtyEightBug_RFC2822(self):
259
Verify that we can parse RFC2822 timestamps after the One Terrible
262
In other words, make sure that we don't round trip through a platform
263
time_t, because those will overflow on 32-bit platforms in 2038.
266
extime.Time.fromRFC2822(
267
'Fri, 19 Jan 2038 03:14:08 -0000'
268
).asPOSIXTimestamp(),
271
extime.Time.fromRFC2822(
272
'Fri, 13 Dec 1901 20:45:52 -0000'
273
).asPOSIXTimestamp(),
276
def test_twentyThirtyEightBug_POSIXTimestamp(self):
278
Verify that we can load POSIX timestamps after the One Terrible Moment
281
In other words, make sure that we don't round trip through a platform
282
time_t, because those will overflow on 32-bit platforms in 2038.
285
extime.Time.fromPOSIXTimestamp(
287
).asPOSIXTimestamp(),
290
extime.Time.fromPOSIXTimestamp(
292
).asPOSIXTimestamp(),
296
def test_obsoleteRFC2822(self):
297
self._checkReference( extime.Time.fromRFC2822('Monday,6 December (i hate this month) 2004 9:15:16 R') )
299
test_obsoleteRFC2822.todo = '''\
300
email.Utils implementation does not handle obsoleted military style
301
timezones, nor does it handle obsoleted comments in the header'''
303
def test_asPOSIXTimestamp(self):
304
self.assertEquals( self._createReference().asPOSIXTimestamp(), 1102342516 )
306
def test_asRFC2822(self):
307
self.assertEquals( self._createReference().asRFC2822(), 'Mon, 6 Dec 2004 14:15:16 -0000' )
308
self.assertEquals( self._createReference().asRFC2822(self.MST()), 'Mon, 6 Dec 2004 07:15:16 -0700' )
309
self.assertEquals( self._createReference().asRFC2822(self.CET()), 'Mon, 6 Dec 2004 15:15:16 +0100' )
311
def test_asISO8601TimeAndDate(self):
313
self._createReference().asISO8601TimeAndDate(),
314
'2004-12-06T14:15:16+00:00' )
316
self._createReference(reference=datetime.datetime(2004, 12, 6, 14, 15, 16, 43210)).asISO8601TimeAndDate(),
317
'2004-12-06T14:15:16.04321+00:00' )
319
self._createReference().asISO8601TimeAndDate(tzinfo=self.MST()),
320
'2004-12-06T07:15:16-07:00' )
322
self._createReference().asISO8601TimeAndDate(tzinfo=self.CET()),
323
'2004-12-06T15:15:16+01:00' )
325
self._createReference().asISO8601TimeAndDate(includeTimezone=False),
326
'2004-12-06T14:15:16' )
328
self._createReference(reference=datetime.datetime(2004, 12, 6, 14, 15, 16, 43210)).asISO8601TimeAndDate(includeTimezone=False),
329
'2004-12-06T14:15:16.04321' )
331
self._createReference().asISO8601TimeAndDate(tzinfo=self.MST(), includeTimezone=False),
332
'2004-12-06T07:15:16' )
334
self._createReference().asISO8601TimeAndDate(tzinfo=self.CET(), includeTimezone=False),
335
'2004-12-06T15:15:16' )
337
def test_asStructTime(self):
338
self.assertEquals( self._createReference().asStructTime(), (2004, 12, 06, 14, 15, 16, 0, 341, 0) )
339
self.assertEquals( self._createReference().asStructTime(tzinfo=self.MST()), (2004, 12, 06, 7, 15, 16, 0, 341, 0) )
340
self.assertEquals( self._createReference().asStructTime(tzinfo=self.CET()), (2004, 12, 06, 15, 15, 16, 0, 341, 0) )
342
def test_asNaiveDatetime(self):
344
return self.awareReference.astimezone(tzinfo).replace(tzinfo=None)
346
self.assertEquals( self._createReference().asNaiveDatetime(), self.reference )
347
self.assertEquals( self._createReference().asNaiveDatetime(tzinfo=self.MST()), ref(self.MST()))
348
self.assertEquals( self._createReference().asNaiveDatetime(tzinfo=self.CET()), ref(self.CET()))
350
def test_asDatetime(self):
351
self.assertEquals( self._createReference().asDatetime(), self.awareReference )
352
self.assertEquals( self._createReference().asDatetime(tzinfo=self.MST()), self.awareReference )
353
self.assertEquals( self._createReference().asDatetime(tzinfo=self.CET()), self.awareReference )
355
def test_asHumanlySameDay(self):
357
L{Time.asHumanly} should return a string which provides only enough
358
context to identify the time being formatted. It should include only
359
the time of day, when formatting times in the same day as now.
361
sameDay = extime.Time.fromStructTime((2004, 12, 6, 14, 15, 16, 0, 0, 0))
363
self._createReference().asHumanly(now=sameDay),
366
self._createReference().asHumanly(tzinfo=self.MST(), now=sameDay),
369
self._createReference().asHumanly(tzinfo=self.CET(), now=sameDay),
372
allDay = extime.Time.fromISO8601TimeAndDate('2005-123')
373
self.assertEquals(allDay.asHumanly(now=allDay), 'all day')
376
def test_asHumanlyDifferentDay(self):
378
L{Time.asHumanly} should include the month and day, when formatting
379
times in a different day (but the same year) as now.
381
nextDay = extime.Time.fromStructTime((2004, 12, 7, 14, 15, 16, 0, 0, 0))
383
self._createReference().asHumanly(now=nextDay),
386
self._createReference().asHumanly(tzinfo=self.MST(), now=nextDay),
389
self._createReference().asHumanly(tzinfo=self.CET(), now=nextDay),
392
allDay = extime.Time.fromISO8601TimeAndDate('2005-123')
393
allDayNextDay = extime.Time.fromISO8601TimeAndDate('2005-124')
394
self.assertEquals(allDay.asHumanly(now=allDayNextDay), '3 May')
397
def test_asHumanlyDifferentYear(self):
399
L{Time.asHumanly} should include the year, when formatting times in a
400
different year than now.
402
nextYear = extime.Time.fromStructTime((2005, 12, 6, 14, 15, 16, 0, 0, 0))
404
self._createReference().asHumanly(now=nextYear),
405
'6 Dec 2004, 02:15 pm' )
407
self._createReference().asHumanly(tzinfo=self.MST(), now=nextYear),
408
'6 Dec 2004, 07:15 am' )
410
self._createReference().asHumanly(tzinfo=self.CET(), now=nextYear),
411
'6 Dec 2004, 03:15 pm' )
413
allDay = extime.Time.fromISO8601TimeAndDate('2005-123')
414
allDayNextYear = extime.Time.fromISO8601TimeAndDate('2006-123')
415
self.assertEquals(allDay.asHumanly(now=allDayNextYear), '3 May 2005')
418
def test_asHumanlyValidPrecision(self):
420
L{Time.asHumanly} should return the time in minutes by default, and
421
in the specified precision when the precision parameter is given.
422
The precision behavior should be identical for both same day and
423
different day code paths.
425
sameDay = extime.Time.fromStructTime((2004, 12, 6, 14, 15, 16, 0, 0, 0))
426
nextDay = extime.Time.fromStructTime((2004, 12, 7, 14, 15, 16, 0, 0, 0))
427
self.assertEquals(self._createReference().asHumanly(now=sameDay),
429
self.assertEquals(self._createReference().asHumanly(now=sameDay,
430
precision=extime.Time.Precision.SECONDS), '02:15:16 pm' )
431
self.assertEquals(self._createReference().asHumanly(now=nextDay),
433
self.assertEquals(self._createReference().asHumanly(now=nextDay,
434
precision=extime.Time.Precision.SECONDS), '6 Dec, 02:15:16 pm' )
437
def test_asHumanlyInvalidPrecision(self):
439
L{Time.asHumanly} should raise an L{InvalidPrecision} exception if
440
passed a value for precision other than L{Time.Precision.MINUTES} or
441
L{Time.Precision.SECONDS}.
443
self.assertRaises(extime.InvalidPrecision,
444
extime.Time().asHumanly,
445
**{'precision': '%H:%M'})
448
def test_inverse(self):
454
'ISO8601TimeAndDate']:
455
parse = getattr(extime.Time, 'from'+style)
456
format = getattr(extime.Time, 'as'+style)
457
self.assertEquals( self._createReference(), parse(format(self._createReference())), '%s() is not the inverse of %s()' % (style, style))
459
def test_evalRepr(self):
460
evalns = {'datetime': datetime,
463
self.assertEquals( now, eval(repr(now), evalns, evalns) )
465
def test_containment(self):
466
makeTime = extime.Time.fromISO8601TimeAndDate
468
self.assertIn(makeTime('2004-05'), makeTime('2004'))
469
self.assertNotIn(makeTime('2005-01'), makeTime('2004'))
471
def test_arithmetic(self):
473
Verify that L{datetime.timedelta} objects can be added to and
474
subtracted from L{Time} instances and that L{Time} instances can be
475
subtracted from each other.
477
time1 = extime.Time.fromISO8601TimeAndDate('2004-12-03T14:15:16')
478
time2 = extime.Time.fromISO8601TimeAndDate('2004-12-09T14:15:16')
479
offset = datetime.timedelta(days=6)
481
# Supported operations
482
self.assertEqual(time1 + offset, time2)
483
self.assertEqual(time2 - offset, time1)
484
self.assertEqual(time2 - time1, offset)
486
# Make sure unsupported types give back a TypeError
487
self.assertRaises(TypeError, lambda: time1 + 1)
488
self.assertRaises(TypeError, lambda: time1 - 1)
491
def test_oneDay(self):
492
day = self._createReference().oneDay()
493
self.assertEquals(day._time, datetime.datetime(2004, 12, 6, 0, 0, 0))
494
self.assertEquals(day.resolution, datetime.timedelta(days=1))
496
def test_isAllDay(self):
497
self.failIf(self._createReference().isAllDay())
498
self.failUnless(extime.Time.fromISO8601TimeAndDate('2005-123').isAllDay())