gramps/src/RelLib/_Date.py

873 lines
31 KiB
Python
Raw Normal View History

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 Donald N. Allingham
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
2006-04-08 11:26:31 +05:30
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
"""Support for dates
"""
__author__ = "Donald N. Allingham"
2006-04-08 11:26:31 +05:30
__revision__ = "$Revision$"
#------------------------------------------------------------------------
#
# Python modules
#
#------------------------------------------------------------------------
from gettext import gettext as _
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
import logging
log = logging.getLogger(".Date")
#-------------------------------------------------------------------------
#
# Gnome/GTK modules
#
#-------------------------------------------------------------------------
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from _CalSdn import *
#------------------------------------------------------------------------
#
# Constants
#
#------------------------------------------------------------------------
#obtain the ranges once, they do not change!
try:
import Config
_DATE_BEFORE_RANGE = Config.get(Config.DATE_BEFORE_RANGE)
_DATE_AFTER_RANGE = Config.get(Config.DATE_AFTER_RANGE)
_DATE_ABOUT_RANGE = Config.get(Config.DATE_ABOUT_RANGE)
except ImportError:
# RelLib used as module not part of GRAMPS
_DATE_BEFORE_RANGE = 9999
_DATE_AFTER_RANGE = 9999
_DATE_ABOUT_RANGE = 10
#-------------------------------------------------------------------------
#
# DateError exception
#
#-------------------------------------------------------------------------
class DateError(Exception):
"""Error used to report Date errors"""
2006-04-08 11:26:31 +05:30
def __init__(self, value=""):
Exception.__init__(self)
self.value = value
def __str__(self):
return self.value
#-------------------------------------------------------------------------
#
# Date class
#
#-------------------------------------------------------------------------
class Date:
"""
2006-04-08 11:26:31 +05:30
The core date handling class for GRAMPs. Supports partial dates,
compound dates and alternate calendars.
"""
MOD_NONE = 0
MOD_BEFORE = 1
MOD_AFTER = 2
MOD_ABOUT = 3
MOD_RANGE = 4
MOD_SPAN = 5
MOD_TEXTONLY = 6
2006-04-08 11:26:31 +05:30
QUAL_NONE = 0
QUAL_ESTIMATED = 1
QUAL_CALCULATED = 2
CAL_GREGORIAN = 0
CAL_JULIAN = 1
CAL_HEBREW = 2
CAL_FRENCH = 3
CAL_PERSIAN = 4
CAL_ISLAMIC = 5
2006-04-08 11:26:31 +05:30
EMPTY = (0, 0, 0, False)
_POS_DAY = 0
_POS_MON = 1
_POS_YR = 2
_POS_SL = 3
_POS_RDAY = 4
_POS_RMON = 5
_POS_RYR = 6
_POS_RSL = 7
_calendar_convert = [
2006-04-08 11:26:31 +05:30
gregorian_sdn,
julian_sdn,
hebrew_sdn,
french_sdn,
persian_sdn,
islamic_sdn,
]
_calendar_change = [
2006-04-08 11:26:31 +05:30
gregorian_ymd,
julian_ymd,
hebrew_ymd,
french_ymd,
persian_ymd,
islamic_ymd,
]
2006-04-08 11:26:31 +05:30
calendar_names = ["Gregorian",
"Julian",
"Hebrew",
"French Republican",
"Persian",
"Islamic"]
2006-04-08 11:26:31 +05:30
ui_calendar_names = [_("Gregorian"),
_("Julian"),
_("Hebrew"),
_("French Republican"),
_("Persian"),
_("Islamic")]
2006-04-08 11:26:31 +05:30
def __init__(self, source=None):
"""
Creates a new Date instance.
"""
if source:
self.calendar = source.calendar
self.modifier = source.modifier
self.quality = source.quality
self.dateval = source.dateval
self.text = source.text
self.sortval = source.sortval
else:
self.calendar = Date.CAL_GREGORIAN
self.modifier = Date.MOD_NONE
self.quality = Date.QUAL_NONE
self.dateval = Date.EMPTY
self.text = u""
self.sortval = 0
def serialize(self, no_text_date=False):
2006-04-08 11:26:31 +05:30
"""
Convert to a series of tuples for data storage
"""
if no_text_date:
text = u''
else:
text = self.text
2006-04-08 11:26:31 +05:30
return (self.calendar, self.modifier, self.quality,
self.dateval, text, self.sortval)
2006-04-08 11:26:31 +05:30
def unserialize(self, data):
"""
Load from the format created by serialize
"""
(self.calendar, self.modifier, self.quality,
self.dateval, self.text, self.sortval) = data
return self
2006-04-08 11:26:31 +05:30
def copy(self, source):
"""
Copy all the attributes of the given Date instance
to the present instance, without creating a new object.
"""
self.calendar = source.calendar
self.modifier = source.modifier
self.quality = source.quality
self.dateval = source.dateval
self.text = source.text
self.sortval = source.sortval
2006-04-08 11:26:31 +05:30
def __cmp__(self, other):
"""
Comparison function. Allows the usage of equality tests.
This allows you do run statements like 'date1 <= date2'
"""
2006-04-08 11:26:31 +05:30
if isinstance(other, Date):
return cmp(self.sortval, other.sortval)
else:
return -1
2006-04-08 11:26:31 +05:30
def is_equal(self, other):
"""
Return 1 if the given Date instance is the same as the present
instance IN ALL REGARDS. Needed, because the __cmp__ only looks
at the sorting value, and ignores the modifiers/comments.
"""
if self.modifier == other.modifier \
and self.modifier == Date.MOD_TEXTONLY:
value = self.text == other.text
else:
value = (self.calendar == other.calendar and
self.modifier == other.modifier and
self.quality == other.quality and
self.dateval == other.dateval)
return value
def get_start_stop_range(self):
"""
Returns the minimal start_date, and a maximal
stop_date corresponding to this date, given in Gregorian calendar.
Useful in doing range overlap comparisons between different dates.
Note that we stay in (YR,MON,DAY)
"""
def yr_mon_day(dateval):
""" Local function to swap order for easy comparisons,
and correct year of slash date.
Slash date is given as year1/year2, where year1 is Julian
year, and year2=year1+1 the Gregorian year
"""
if dateval[Date._POS_SL] :
return (dateval[Date._POS_YR]+1, dateval[Date._POS_MON],
dateval[Date._POS_DAY])
else :
return (dateval[Date._POS_YR], dateval[Date._POS_MON],
dateval[Date._POS_DAY])
def date_offset(dateval, offset):
""" Local function to do date arithmetic: add the offset,
return (year,month,day) in the Gregorian calendar
"""
new_date = Date()
new_date.set_yr_mon_day(dateval[0], dateval[1], dateval[2])
return Date._calendar_change[Date.CAL_GREGORIAN](
new_date.sortval + offset)
datecopy = Date(self)
#we do all calculation in Gregorian calendar
datecopy.convert_calendar(Date.CAL_GREGORIAN)
start = yr_mon_day(datecopy.get_start_date())
stop = yr_mon_day(datecopy.get_stop_date())
if stop == (0, 0, 0):
stop = start
stopmax = list(stop)
if stopmax[0] == 0: # then use start_year, if one
stopmax[0] = start[Date._POS_YR]
if stopmax[1] == 0:
stopmax[1] = 12
if stopmax[2] == 0:
stopmax[2] = 31
startmin = list(start)
if startmin[1] == 0:
startmin[1] = 1
if startmin[2] == 0:
startmin[2] = 1
# if BEFORE, AFTER, or ABOUT/EST, adjust:
if self.modifier == Date.MOD_BEFORE:
stopmax = date_offset(startmin, -1)
fdiff = _DATE_BEFORE_RANGE
startmin = (stopmax[0] - fdiff, stopmax[1], stopmax[2])
elif self.modifier == Date.MOD_AFTER:
startmin = date_offset(stopmax, 1)
fdiff = _DATE_AFTER_RANGE
stopmax = (startmin[0] + fdiff, startmin[1], startmin[2])
elif (self.modifier == Date.MOD_ABOUT or
self.quality == Date.QUAL_ESTIMATED):
fdiff = _DATE_ABOUT_RANGE
startmin = (startmin[0] - fdiff, startmin[1], startmin[2])
stopmax = (stopmax[0] + fdiff, stopmax[1], stopmax[2])
# return tuples not lists, for comparisons
return (tuple(startmin), tuple(stopmax))
def match(self, other_date):
"""
The other comparisons for Date don't actually look for anything
other than a straight match, or a simple comparison of the sortval.
This method allows a more sophisticated comparison looking for
any overlap between two possible dates, date spans, and qualities.
Returns True if part of other_date matches part of the date-span
defined by self
"""
if (other_date.modifier == Date.MOD_TEXTONLY or
self.modifier == Date.MOD_TEXTONLY):
###from DateHandler import displayer
# If either date is just text, then we can only compare textual
# representations
# Use text as originally given or display date to format
# in preferences? That is use self.text or displayer ?
# It is unclean to import DateHandler in RelLib !
###self_text = displayer.display(self)
self_text = self.text
##DEBUG: print ' TEXT COMPARE ONLY '
return (self_text.upper().find(other_date.text.upper()) != -1)
# Obtain minimal start and maximal stop in Gregorian calendar
other_start, other_stop = other_date.get_start_stop_range()
self_start, self_stop = self.get_start_stop_range()
##DEBUG print " date compare:", self_start, self_stop, other_start,
##DEBUG other_stop
# If some overlap then match is True, otherwise False.
if ((self_start <= other_start <= self_stop) or
(self_start <= other_stop <= self_stop) or
(other_start <= self_start <= other_stop) or
(other_start <= self_stop <= other_stop)):
return True
else:
return False
def __str__(self):
"""
Produces a string representation of the Date object. If the
date is not valid, the text representation is displayed. If
the date is a range or a span, a string in the form of
'YYYY-MM-DD - YYYY-MM-DD' is returned. Otherwise, a string in
the form of 'YYYY-MM-DD' is returned.
"""
if self.quality == Date.QUAL_ESTIMATED:
qual = "est "
elif self.quality == Date.QUAL_CALCULATED:
qual = "calc "
else:
qual = ""
if self.modifier == Date.MOD_BEFORE:
pref = "bef "
elif self.modifier == Date.MOD_AFTER:
pref = "aft "
elif self.modifier == Date.MOD_ABOUT:
pref = "abt "
else:
pref = ""
if self.calendar != Date.CAL_GREGORIAN:
cal = " (%s)" % Date.calendar_names[self.calendar]
else:
cal = ""
if self.modifier == Date.MOD_TEXTONLY:
val = self.text
elif self.modifier == Date.MOD_RANGE or self.modifier == Date.MOD_SPAN:
val = "%04d-%02d-%02d - %04d-%02d-%02d" % (
2006-04-08 11:26:31 +05:30
self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY], self.dateval[Date._POS_RYR],
self.dateval[Date._POS_RMON], self.dateval[Date._POS_RDAY])
else:
val = "%04d-%02d-%02d" % (
2006-04-08 11:26:31 +05:30
self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY])
2006-04-08 11:26:31 +05:30
return "%s%s%s%s" % (qual, pref, val, cal)
def get_sort_value(self):
"""
Returns the sort value of Date object. If the value is a
text string, 0 is returned. Otherwise, the calculated sort
date is returned. The sort date is rebuilt on every assignment.
The sort value is an integer representing the value. A date of
March 5, 1990 would have the value of 19900305.
"""
return self.sortval
def get_modifier(self):
"""
Returns an integer indicating the calendar selected. The valid
values are::
MOD_NONE = no modifier (default)
MOD_BEFORE = before
MOD_AFTER = after
MOD_ABOUT = about
MOD_RANGE = date range
MOD_SPAN = date span
MOD_TEXTONLY = text only
"""
return self.modifier
2006-04-08 11:26:31 +05:30
def set_modifier(self, val):
"""
Sets the modifier for the date.
"""
2006-04-08 11:26:31 +05:30
if val not in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER,
Date.MOD_ABOUT, Date.MOD_RANGE, Date.MOD_SPAN,
Date.MOD_TEXTONLY):
raise DateError("Invalid modifier")
self.modifier = val
def get_quality(self):
"""
Returns an integer indicating the calendar selected. The valid
values are::
QUAL_NONE = normal (default)
QUAL_ESTIMATED = estimated
QUAL_CALCULATED = calculated
"""
return self.quality
2006-04-08 11:26:31 +05:30
def set_quality(self, val):
"""
Sets the quality selected for the date.
"""
2006-04-08 11:26:31 +05:30
if val not in (Date.QUAL_NONE, Date.QUAL_ESTIMATED,
Date.QUAL_CALCULATED):
raise DateError("Invalid quality")
self.quality = val
def get_calendar(self):
"""
Returns an integer indicating the calendar selected. The valid
values are::
CAL_GREGORIAN - Gregorian calendar
CAL_JULIAN - Julian calendar
CAL_HEBREW - Hebrew (Jewish) calendar
CAL_FRENCH - French Republican calendar
CAL_PERSIAN - Persian calendar
CAL_ISLAMIC - Islamic calendar
"""
return self.calendar
2006-04-08 11:26:31 +05:30
def set_calendar(self, val):
"""
Sets the calendar selected for the date.
"""
2006-04-08 11:26:31 +05:30
if val not in (Date.CAL_GREGORIAN, Date.CAL_JULIAN, Date.CAL_HEBREW,
Date.CAL_FRENCH, Date.CAL_PERSIAN, Date.CAL_ISLAMIC):
raise DateError("Invalid calendar")
self.calendar = val
def get_start_date(self):
"""
Returns a tuple representing the start date. If the date is a
compound date (range or a span), it is the first part of the
compound date. If the date is a text string, a tuple of
2006-04-08 11:26:31 +05:30
(0, 0, 0, False) is returned. Otherwise, a date of (DD, MM, YY, slash)
is returned. If slash is True, then the date is in the form of 1530/1.
"""
if self.modifier == Date.MOD_TEXTONLY:
val = Date.EMPTY
else:
val = self.dateval[0:4]
return val
def get_stop_date(self):
"""
Returns a tuple representing the second half of a compound date.
If the date is not a compound date, (including text strings) a tuple
2006-04-08 11:26:31 +05:30
of (0, 0, 0, False) is returned. Otherwise, a date of (DD, MM, YY, slash)
is returned. If slash is True, then the date is in the form of 1530/1.
"""
if self.modifier == Date.MOD_RANGE or self.modifier == Date.MOD_SPAN:
val = self.dateval[4:8]
else:
val = Date.EMPTY
return val
2006-04-08 11:26:31 +05:30
def _get_low_item(self, index):
"""
Returns the item specified
"""
if self.modifier == Date.MOD_TEXTONLY:
val = 0
else:
val = self.dateval[index]
return val
2006-04-08 11:26:31 +05:30
def _get_low_item_valid(self, index):
"""
Determines if the item specified is valid
"""
if self.modifier == Date.MOD_TEXTONLY:
val = False
else:
val = self.dateval[index] != 0
return val
2006-04-08 11:26:31 +05:30
def _get_high_item(self, index):
"""
Returns the item specified
"""
if self.modifier == Date.MOD_SPAN or self.modifier == Date.MOD_RANGE:
val = self.dateval[index]
else:
val = 0
return val
def get_year(self):
"""
Returns the year associated with the date. If the year is
not defined, a zero is returned. If the date is a compound
date, the lower date year is returned.
"""
return self._get_low_item(Date._POS_YR)
def set_yr_mon_day(self, year, month, day):
"""
Sets the year, month, and day values
"""
dv = list(self.dateval)
dv[Date._POS_YR] = year
dv[Date._POS_MON] = month
dv[Date._POS_DAY] = day
self.dateval = tuple(dv)
self._calc_sort_value()
2006-04-08 11:26:31 +05:30
def set_year(self, year):
"""
2006-04-08 11:26:31 +05:30
Sets the year value
"""
2006-04-08 11:26:31 +05:30
self.dateval = self.dateval[0:2] + (year, ) + self.dateval[3:]
self._calc_sort_value()
def get_year_valid(self):
2006-04-08 11:26:31 +05:30
"""
Returns true if the year is valid
"""
return self._get_low_item_valid(Date._POS_YR)
def get_month(self):
"""
Returns the month associated with the date. If the month is
not defined, a zero is returned. If the date is a compound
date, the lower date month is returned.
"""
return self._get_low_item(Date._POS_MON)
def get_month_valid(self):
2006-04-08 11:26:31 +05:30
"""
Returns true if the month is valid
"""
return self._get_low_item_valid(Date._POS_MON)
def get_day(self):
"""
Returns the day of the month associated with the date. If
the day is not defined, a zero is returned. If the date is
a compound date, the lower date day is returned.
"""
return self._get_low_item(Date._POS_DAY)
def get_day_valid(self):
2006-04-08 11:26:31 +05:30
"""
Returns true if the day is valid
"""
return self._get_low_item_valid(Date._POS_DAY)
def get_valid(self):
""" Returns true if any part of the date is valid"""
return self.modifier != Date.MOD_TEXTONLY
def get_stop_year(self):
"""
Returns the day of the year associated with the second
part of a compound date. If the year is not defined, a zero
is returned.
"""
return self._get_high_item(Date._POS_RYR)
def get_stop_month(self):
"""
Returns the month of the month associated with the second
part of a compound date. If the month is not defined, a zero
is returned.
"""
return self._get_high_item(Date._POS_RMON)
def get_stop_day(self):
"""
Returns the day of the month associated with the second
part of a compound date. If the day is not defined, a zero
is returned.
"""
return self._get_high_item(Date._POS_RDAY)
def get_high_year(self):
"""
Returns the high year estimate. For compound dates with non-zero
stop year, the stop year is returned. Otherwise, the start year
is returned.
"""
if self.is_compound():
ret = self.get_stop_year()
if ret:
return ret
else:
return self.get_year()
def get_text(self):
"""
Returns the text value associated with an invalid date.
"""
return self.text
2006-04-08 11:26:31 +05:30
def set(self, quality, modifier, calendar, value, text=None):
"""
Sets the date to the specified value. Parameters are::
quality - The date quality for the date (see get_quality
for more information)
modified - The date modifier for the date (see get_modifier
for more information)
calendar - The calendar associated with the date (see
get_calendar for more information).
value - A tuple representing the date information. For a
2006-04-08 11:26:31 +05:30
non-compound date, the format is (DD, MM, YY, slash)
and for a compound date the tuple stores data as
2006-04-08 11:26:31 +05:30
(DD, MM, YY, slash1, DD, MM, YY, slash2)
text - A text string holding either the verbatim user input
or a comment relating to the date.
The sort value is recalculated.
"""
2006-04-08 11:26:31 +05:30
if modifier in (Date.MOD_NONE, Date.MOD_BEFORE,
Date.MOD_AFTER, Date.MOD_ABOUT) and len(value) < 4:
raise DateError("Invalid value. Should be: (DD, MM, YY, slash)")
if modifier in (Date.MOD_RANGE, Date.MOD_SPAN) and len(value) < 8:
raise DateError("Invalid value. Should be: (DD, MM, "
"YY, slash1, DD, MM, YY, slash2)")
if modifier not in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER,
Date.MOD_ABOUT, Date.MOD_RANGE, Date.MOD_SPAN,
Date.MOD_TEXTONLY):
raise DateError("Invalid modifier")
2006-04-08 11:26:31 +05:30
if quality not in (Date.QUAL_NONE, Date.QUAL_ESTIMATED,
Date.QUAL_CALCULATED):
raise DateError("Invalid quality")
2006-04-08 11:26:31 +05:30
if calendar not in (Date.CAL_GREGORIAN, Date.CAL_JULIAN,
Date.CAL_HEBREW, Date.CAL_FRENCH,
Date.CAL_PERSIAN, Date.CAL_ISLAMIC):
raise DateError("Invalid calendar")
self.quality = quality
self.modifier = modifier
self.calendar = calendar
self.dateval = value
2006-04-08 11:26:31 +05:30
year = max(value[Date._POS_YR], 1)
month = max(value[Date._POS_MON], 1)
day = max(value[Date._POS_DAY], 1)
if year == 0 and month == 0 and day == 0:
self.sortval = 0
else:
2006-04-08 11:26:31 +05:30
func = Date._calendar_convert[calendar]
self.sortval = func(year, month, day)
if text:
self.text = text
2006-04-08 11:26:31 +05:30
def _calc_sort_value(self):
"""
Calculates the numerical sort value associated with the date
"""
year = max(self.dateval[Date._POS_YR], 1)
month = max(self.dateval[Date._POS_MON], 1)
day = max(self.dateval[Date._POS_DAY], 1)
if year == 0 and month == 0 and day == 0:
self.sortval = 0
else:
2006-04-08 11:26:31 +05:30
func = Date._calendar_convert[self.calendar]
self.sortval = func(year, month, day)
2006-04-08 11:26:31 +05:30
def convert_calendar(self, calendar):
"""
Converts the date from the current calendar to the specified
calendar.
"""
if calendar == self.calendar:
return
2006-04-08 11:26:31 +05:30
(year, month, day) = Date._calendar_change[calendar](self.sortval)
if self.is_compound():
2006-04-08 11:26:31 +05:30
ryear = max(self.dateval[Date._POS_RYR], 1)
rmonth = max(self.dateval[Date._POS_RMON], 1)
rday = max(self.dateval[Date._POS_RDAY], 1)
sdn = Date._calendar_convert[self.calendar](ryear, rmonth, rday)
(nyear, nmonth, nday) = Date._calendar_change[calendar](sdn)
self.dateval = (day, month, year, self.dateval[Date._POS_SL],
nday, nmonth, nyear, self.dateval[Date._POS_RSL])
else:
2006-04-08 11:26:31 +05:30
self.dateval = (day, month, year, self.dateval[Date._POS_SL])
self.calendar = calendar
2006-04-08 11:26:31 +05:30
def set_as_text(self, text):
"""
Sets the day to a text string, and assigns the sort value
to zero.
"""
self.modifier = Date.MOD_TEXTONLY
self.text = text
self.sortval = 0
2006-04-08 11:26:31 +05:30
def set_text_value(self, text):
"""
Sets the text string to a given text.
"""
self.text = text
def is_empty(self):
"""
Returns True if the date contains no information (empty text).
"""
return (self.modifier == Date.MOD_TEXTONLY and not self.text) or \
(self.get_start_date()==Date.EMPTY
and self.get_stop_date()==Date.EMPTY)
def is_compound(self):
"""
Returns True if the date is a date range or a date span.
"""
return self.modifier == Date.MOD_RANGE \
or self.modifier == Date.MOD_SPAN
def is_regular(self):
"""
Returns True if the date is a regular date.
The regular date is a single exact date, i.e. not text-only, not
a range or a span, not estimated/calculated, not about/before/after
date, and having year, month, and day all non-zero.
"""
return self.modifier == Date.MOD_NONE \
and self.quality == Date.QUAL_NONE \
and self.get_year_valid() and self.get_month_valid() \
and self.get_day_valid()
if __name__ == "__main__":
# Test function. Call it as follows from the command line (so as to find
# imported modules):
# export PYTHONPATH=/path/to/gramps/src python src/RelLib/_Date.py
#
from DateHandler import _DateParser
df = _DateParser.DateParser() # date factory
def test_date(d1, d2, expected1, expected2 = None):
if expected2 == None:
expected2 = expected1
pos1 = 1
if expected1 :
pos1 = 0
pos2 = 1
if expected2 :
pos2 = 0
date1 = df.parse(d1)
date2 = df.parse(d2)
wrong = 0
print "Testing '%s' and '%s'" % (d1, d2)
val = date2.match(date1)
try:
assert(val == expected1)
print [" correct: they match!"
," correct: they do not match!"][pos1]
except:
print " Wrong! got %s" % (not expected1)
wrong += 1
val = date1.match(date2)
try:
assert(val == expected2)
print [" correct: they match!"
," correct: they do not match!"][pos2]
except:
print " Wrong! got %s" % (not expected2)
wrong += 1
return {"incorrect": wrong, "correct": 2 - wrong }
stats = {'incorrect':0, 'correct':0}
# create a bunch of tests:
# most are symmetric: #date1, date2, does d1 match d2? does d2 match d1?
tests = [("before 1960", "before 1961", True),
("before 1960", "before 1960", True),
("before 1961", "before 1961", True),
("jan 1, 1960", "jan 1, 1960", True),
("dec 31, 1959", "dec 31, 1959", True),
("before 1960", "jan 1, 1960", False),
("before 1960", "dec 31, 1959", True),
("abt 1960", "1960", True),
("abt 1960", "before 1960", True),
("1960", "1960", True),
("1960", "after 1960", False),
("1960", "before 1960", False),
("abt 1960", "abt 1960", True),
("before 1960", "after 1960", False),
("after jan 1, 1900", "jan 2, 1900", True),
("abt jan 1, 1900", "jan 1, 1900", True),
("from 1950 to 1955", "1950", True),
("from 1950 to 1955", "1951", True),
("from 1950 to 1955", "1952", True),
("from 1950 to 1955", "1953", True),
("from 1950 to 1955", "1954", True),
("from 1950 to 1955", "1955", True),
("from 1950 to 1955", "1956", False),
("from 1950 to 1955", "dec 31, 1955", True),
("from 1950 to 1955", "jan 1, 1955", True),
("from 1950 to 1955", "dec 31, 1949", False),
("from 1950 to 1955", "jan 1, 1956", False),
("after jul 4, 1980", "jul 4, 1980", False),
("after jul 4, 1980", "before jul 4, 1980", False),
("after jul 4, 1980", "about jul 4, 1980", True),
("after jul 4, 1980", "after jul 4, 1980", True),
("between 1750 and 1752", "1750", True),
("between 1750 and 1752", "about 1750", True),
("between 1750 and 1752", "between 1749 and 1750", True),
("between 1750 and 1752", "1749", False),
("invalid date", "invalid date", True),
("invalid date", "invalid", False, True),
("invalid date 1", "invalid date 2", False),
("abt jan 1, 2000", "dec 31, 1999", True),
("jan 1, 2000", "dec 31, 1999", False),
("aft jan 1, 2000", "dec 31, 1999", False),
("after jan 1, 2000", "after dec 31, 1999", True),
("after dec 31, 1999", "after jan 1, 2000", True),
("1 31, 2000", "jan 1, 2000", False),
("dec 31, 1999", "jan 1, 2000", False),
("jan 1, 2000", "before dec 31, 1999", False),
("aft jan 1, 2000", "before dec 31, 1999", False),
("before jan 1, 2000", "after dec 31, 1999", False),
("jan 1, 2000/1", "jan 1, 2000", False),
("jan 1, 2000/1", "jan 1, 2001", True),
("about 1984", "about 2005", False),
("about 1990", "about 2005", True),
("about 2007", "about 2006", True),
("about 1995", "after 2000", True),
("about 1995", "after 2005", False),
("about 2007", "about 2003", True),
("before 2007", "2000", True),
# different calendar, same date
("Aug 3, 1982", "14 Thermidor 190 (French Republican)", True),
("after Aug 3, 1982",
"before 14 Thermidor 190 (French Republican)", False),
("ab cd", "54 ab cd 2000", True, False),
]
# test them:
for testdata in tests:
results = test_date(*testdata)
for result in results:
stats[result] += results[result]
for result in stats:
print result, stats[result]