datatest
You can compare :pydate <datetime.date>
and :pydatetime
<datetime.datetime>
objects just as you would any other quantitative value:
from datetime import date, datetime
from datatest import validate
validate(date(2020, 12, 25), date(2020, 12, 25))
validate(datetime(2020, 12, 25, 9, 30), datetime(2020, 12, 25, 9, 30))
Compare mappings of date objects:
from datetime import date
from datatest import validate
data = {
'A': date(2020, 12, 24),
'B': date(2020, 12, 25),
'C': date(2020, 12, 26),
}
requirement = {
'A': date(2020, 12, 24),
'B': date(2020, 12, 25),
'C': date(2020, 12, 26),
}
validate(data, requirement)
We can use validate.interval
to check that date and datetime values are within a given range.
Check that dates fall within the month of December 2020:
from datetime import date
from datatest import validate
data = [
date(2020, 12, 4),
date(2020, 12, 11),
date(2020, 12, 18),
date(2020, 12, 25),
]
validate.interval(data, min=date(2020, 12, 1), max=date(2020, 12, 31))
Check that dates are one week old or newer:
from datetime import date, timedelta
from datatest import validate
data = {
'A': date(2020, 12, 24),
'B': date(2020, 12, 25),
'C': date(2020, 12, 26),
}
one_week_ago = date.today() - timedelta(days=7)
validate.interval(data, min=one_week_ago, msg='one week old or newer')
When validation fails, a Deviation is generated containing a :pytimedelta <datetime.timedelta>
that represents the difference between two dates or times. You can accept time differences with accepted.tolerance
.
Failure
from datetime import date
from datatest import validate
validate(date(2020, 12, 27), date(2020, 12, 25))
Traceback (most recent call last):
File "example.py", line 4, in <module>
validate(date(2020, 12, 27), date(2020, 12, 25))
datatest.ValidationError: does not satisfy `datetime.date(2020, 12, 25)` (1 difference): [
Deviation(timedelta(days=+2), date(2020, 12, 25)),
]
Acceptance
from datetime import date, timedelta
from datatest import validate, accepted
with accepted.tolerance(timedelta(days=2)): # accepts ±2 days
validate(date(2020, 12, 27), date(2020, 12, 25))
Failure
from datetime import date
from datatest import validate
data = [
date(2020, 11, 26),
date(2020, 12, 11),
date(2020, 12, 25),
date(2021, 1, 4),
]
validate.interval(data, min=date(2020, 12, 1), max=date(2020, 12, 31))
Traceback (most recent call last):
File "example.py", line 11, in <module>
validate.interval(data, min=date(2020, 12, 1), max=date(2020, 12, 31))
datatest.ValidationError: elements `x` do not satisfy `datetime.date(2020,
12, 1) <= x <= datetime.date(2020, 12, 31)` (2 differences): [
Deviation(timedelta(days=-5), date(2020, 12, 1)),
Deviation(timedelta(days=+4), date(2020, 12, 31)),
]
Acceptance
from datetime import date, timedelta
from datatest import validate, accepted
data = [
date(2020, 11, 26),
date(2020, 12, 11),
date(2020, 12, 25),
date(2021, 1, 4),
]
with accepted.tolerance(timedelta(days=5)): # accepts ±5 days
validate.interval(data, min=date(2020, 12, 1), max=date(2020, 12, 31))
Failure
from datetime import datetime
from datatest import validate
remote_log = {
'job165': datetime(2020, 11, 27, hour=3, minute=29, second=55),
'job382': datetime(2020, 11, 27, hour=3, minute=36, second=33),
'job592': datetime(2020, 11, 27, hour=3, minute=42, second=14),
'job720': datetime(2020, 11, 27, hour=3, minute=50, second=7),
'job826': datetime(2020, 11, 27, hour=3, minute=54, second=12),
}
master_log = {
'job165': datetime(2020, 11, 27, hour=3, minute=28, second=1),
'job382': datetime(2020, 11, 27, hour=3, minute=36, second=51),
'job592': datetime(2020, 11, 27, hour=3, minute=40, second=18),
'job720': datetime(2020, 11, 27, hour=3, minute=49, second=39),
'job826': datetime(2020, 11, 27, hour=3, minute=55, second=20),
}
validate(remote_log, master_log)
Traceback (most recent call last):
File "example.py", line 20, in <module>
validate(remote_log, master_log)
datatest.ValidationError: does not satisfy mapping requirements (5 differences): {
'job165': Deviation(timedelta(seconds=+114), datetime(2020, 11, 27, 3, 28, 1)),
'job382': Deviation(timedelta(seconds=-18), datetime(2020, 11, 27, 3, 36, 51)),
'job592': Deviation(timedelta(seconds=+116), datetime(2020, 11, 27, 3, 40, 18)),
'job720': Deviation(timedelta(seconds=+28), datetime(2020, 11, 27, 3, 49, 39)),
'job826': Deviation(timedelta(seconds=-68), datetime(2020, 11, 27, 3, 55, 20)),
}
Acceptance
from datetime import datetime, timedelta
from datatest import validate, accepted
remote_log = {
'job165': datetime(2020, 11, 27, hour=3, minute=29, second=55),
'job382': datetime(2020, 11, 27, hour=3, minute=36, second=33),
'job592': datetime(2020, 11, 27, hour=3, minute=42, second=14),
'job720': datetime(2020, 11, 27, hour=3, minute=50, second=7),
'job826': datetime(2020, 11, 27, hour=3, minute=54, second=12),
}
master_log = {
'job165': datetime(2020, 11, 27, hour=3, minute=28, second=1),
'job382': datetime(2020, 11, 27, hour=3, minute=36, second=51),
'job592': datetime(2020, 11, 27, hour=3, minute=40, second=18),
'job720': datetime(2020, 11, 27, hour=3, minute=49, second=39),
'job826': datetime(2020, 11, 27, hour=3, minute=55, second=20),
}
with accepted.tolerance(timedelta(seconds=120)): # accepts ±120 seconds
validate(remote_log, master_log)
Note
The Deviation
's repr (its printable representation) does some tricks with :pydatetime
objects to improve readability and help users understand their data errors. Compare the representations of the following datetime objects against their representations when included inside a Deviation:
>>> from datetime import timedelta, date
>>> from datatest import Deviation
>>> timedelta(days=2)
datetime.timedelta(days=2)
>>> date(2020, 12, 25)
datetime.date(2020, 12, 25)
>>> Deviation(timedelta(days=2), date(2020, 12, 25))
Deviation(timedelta(days=+2), date(2020, 12, 25))
And below, we see a negative-value timedelta with a particularly surprising native repr. The Deviation repr modifies this to be more readable:
>>> from datetime import timedelta, datetime
>>> from datatest import Deviation
>>> timedelta(seconds=-3)
datetime.timedelta(days=-1, seconds=86397)
>>> datetime(2020, 12, 25, 9, 30)
datetime.datetime(2020, 12, 25, 9, 30)
>>> Deviation(timedelta(seconds=-3), datetime(2020, 12, 25, 9, 30))
Deviation(timedelta(seconds=-3), datetime(2020, 12, 25, 9, 30))
While the timedelta reprs do differ, they are legitimate constructors for creating objects of equal value:
>>> from datetime import timedelta
>>> timedelta(days=+2) == timedelta(days=2)
True
>>> timedelta(seconds=-3) == timedelta(days=-1, seconds=86397)
True