7034,7045,7065: back-merge my fixes from trunk

Back-merge from trunk changes to date.py and date_test.py
from the following commits (cumulative, clean apply).

Tests pass (but need to block the CAL_FRENCH on date_test.py:199,
because of bug# 7068 -- skipping it wasn't back-ported in this commit as
it is about the fully fixed issues only!)

	commit fa49752824bd58802773439b35faa39f2d34b151
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 15:44:04 2013 +0000

		provide sensible defautls for all Date.set params

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23126 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 3f32597699f3b372324ad87e6f7a04abac6d19e7
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 15:11:09 2013 +0000

		7045: Setting an invalid date does not raise

		do the sanity checks on a separate date object,
		so that the uncertainty expressed with 0 d/m isn't removed

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23124 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 12edf7e97626e01931c4063b2d94bec3b299a2ed
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 14:23:58 2013 +0000

		7065: Calendar conversion broken for negative date

		fixed, repro steps work as expected now
		date_test still broken due to further blocking issues,
		see #7045

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23123 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 091d4461e9715ab06f1ef6ab3b67517d6608daf3
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 13:24:40 2013 +0000

		7045: Date.set on invalid date does not raise

		refactor _zero_adjust_ymd out of 3 cut-and-paste cases
		the bug with the code inside it remains -- the negative years
		should not be clamped to positive ones!!!!

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23122 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 5987046ac4cac407a4be506da9242f7a5000d878
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 13:00:19 2013 +0000

		7045: Date.set on invalid date does not raise

		Now it does, but another test breaks:

		Traceback (most recent call last):
		  File "/usr/lib/python2.7/unittest/loader.py", line 252, in _find_tests
			module = self._get_module_from_name(name)
		  File "/usr/lib/python2.7/unittest/loader.py", line 230, in _get_module_from_name
			__import__(name)
		  File "/home/vassilii/Gramps/gramps/gen/lib/test/date_test.py", line 136, in <module>
			d.set(quality,modifier,calendar,(4,11,-90,False),"Text comment")
		  File "/home/vassilii/Gramps/gramps/gen/lib/date.py", line 1600, in set
			format(original, value))
		DateError: Invalid year -90 passed in value (4, 11, -90, False)

		because the corresponding year gets adjusted from -90 to 1...

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23121 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit d8876cceb64629ce0a025ff714e4875768ab88a6
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sat Sep 14 11:50:58 2013 +0000

		7034: fix test_copy_ymd_preserves_orig

		broken in r23083

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23120 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 7c163636c8e48149a5b09c211ff3dc146ebd84b2
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Tue Sep 10 17:19:16 2013 +0000

		7034: add remove_stop_date parameter

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23083 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit b45e20da3bd67d864420f99cf59fbb9929c58851
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Mon Sep 9 19:31:13 2013 +0000

		7034: probably_alive() failing when no birth-death

		further refactoring of set_.../set2_... common code
		added accessor get_stop_ymd analogous to get_ymd

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23068 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit f13a3fc23e7f0763c49e605b428b6a175c3f9eeb
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Mon Sep 9 19:31:00 2013 +0000

		7034: probably_alive() failing when no birth-death

		docstring update

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23067 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 9ee312d7ed02520b99d2ca1b28f75c87846aa3c6
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:35:15 2013 +0000

		refactor test

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23059 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 07ca997ebd885ad4d1b205907a00509099ac8f9a
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:23:23 2013 +0000

		consistency between offset and non-offset setters

		added ugly parameter _update2 to set_yr_mon_day, needs refactoring

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23058 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 67a904c529642668fbe34bfc97ef2915278ecbdb
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:23:12 2013 +0000

		fix set_yr_mon_day_offset for compound dates

		now calls set2_yr_mon_day_offset

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23057 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 3db06c36d6449ec75cde49b433349cddad40d596
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:23:02 2013 +0000

		refactor set_yr_mon_day and set2_yr_mon_day

		refactor common base

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23056 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 4192680c72cc0028c22fa207fe3f1ff0940358b3
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:22:52 2013 +0000

		raise DateError in set2_... if not is_compound()

		refactor Date to always use is_compound instead of repeating
		its logic everywhere

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23055 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 44195ede18c5a887d4440b4132bd5321f76ce5ff
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:22:40 2013 +0000

		Add some UT for Date.set2_... and fix bugs

		Cut and paste is evil ;-) fix bugs before I refactor the code...

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23054 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

	commit 14f6e3a3f35e0ff7b67006bf4996ce63ae665098
	Author: Vassilii Khachaturov <vassilii@tarunz.org>
	Date:   Sun Sep 8 19:22:30 2013 +0000

		7034: probably_alive() failing when no birth-death

		docstring fix

		git-svn-id: svn+ssh://svn.code.sf.net/p/gramps/code/trunk@23053 4ae1f11a-8b86-4847-b8af-ab372f36d1fd

svn: r23156
This commit is contained in:
Vassilii Khachaturov 2013-09-17 18:58:06 +00:00
parent 75ca7e1f8c
commit 63f022348b
2 changed files with 237 additions and 118 deletions

View File

@ -122,8 +122,7 @@ class Span(object):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
start, stop = self.date2.get_start_stop_range() start, stop = self.date2.get_start_stop_range()
start = Date(*start) start = Date(*start)
stop = Date(*stop) stop = Date(*stop)
@ -148,8 +147,7 @@ class Span(object):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -170,8 +168,7 @@ class Span(object):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.AFTER) self.minmax = (v - Span.ABOUT, v + Span.AFTER)
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -192,13 +189,11 @@ class Span(object):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
elif (self.date1.get_modifier() == Date.MOD_RANGE or elif self.date1.is_compound():
self.date1.get_modifier() == Date.MOD_SPAN): # SPAN----------------------------
if self.date2.get_modifier() == Date.MOD_NONE: if self.date2.get_modifier() == Date.MOD_NONE:
start, stop = self.date1.get_start_stop_range() start, stop = self.date1.get_start_stop_range()
start = Date(*start) start = Date(*start)
@ -219,8 +214,7 @@ class Span(object):
v = self.date1.sortval - self.date2.sortval v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT) self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT) self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
start1, stop1 = self.date1.get_start_stop_range() start1, stop1 = self.date1.get_start_stop_range()
start2, stop2 = self.date2.get_start_stop_range() start2, stop2 = self.date2.get_start_stop_range()
start1 = Date(*start1) start1 = Date(*start1)
@ -314,8 +308,7 @@ class Span(object):
_repr = trans_text("less than") + " " + fdate12 _repr = trans_text("less than") + " " + fdate12
elif self.date2.get_modifier() == Date.MOD_ABOUT: elif self.date2.get_modifier() == Date.MOD_ABOUT:
_repr = trans_text("age|about") + " " + fdate12p1 _repr = trans_text("age|about") + " " + fdate12p1
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
start, stop = self.date2.get_start_stop_range() start, stop = self.date2.get_start_stop_range()
start = Date(*start) start = Date(*start)
stop = Date(*stop) stop = Date(*stop)
@ -330,8 +323,7 @@ class Span(object):
_repr = trans_text("less than") + " " + fdate12 _repr = trans_text("less than") + " " + fdate12
elif self.date2.get_modifier() == Date.MOD_ABOUT: elif self.date2.get_modifier() == Date.MOD_ABOUT:
_repr = trans_text("less than about") + " " + fdate12 _repr = trans_text("less than about") + " " + fdate12
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
_repr = trans_text("less than") + " " + fdate12 _repr = trans_text("less than") + " " + fdate12
elif self.date1.get_modifier() == Date.MOD_AFTER: # AFTER---------------------------- elif self.date1.get_modifier() == Date.MOD_AFTER: # AFTER----------------------------
if self.date2.get_modifier() == Date.MOD_NONE: if self.date2.get_modifier() == Date.MOD_NONE:
@ -342,8 +334,7 @@ class Span(object):
_repr = self._format((-1, -1 , -1)) _repr = self._format((-1, -1 , -1))
elif self.date2.get_modifier() == Date.MOD_ABOUT: elif self.date2.get_modifier() == Date.MOD_ABOUT:
_repr = trans_text("more than about") + " " + fdate12p1 _repr = trans_text("more than about") + " " + fdate12p1
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
_repr = trans_text("more than") + " " + fdate12 _repr = trans_text("more than") + " " + fdate12
elif self.date1.get_modifier() == Date.MOD_ABOUT: # ABOUT---------------------------- elif self.date1.get_modifier() == Date.MOD_ABOUT: # ABOUT----------------------------
if self.date2.get_modifier() == Date.MOD_NONE: if self.date2.get_modifier() == Date.MOD_NONE:
@ -354,11 +345,9 @@ class Span(object):
_repr = trans_text("less than about") + " " + fdate12p1 _repr = trans_text("less than about") + " " + fdate12p1
elif self.date2.get_modifier() == Date.MOD_ABOUT: elif self.date2.get_modifier() == Date.MOD_ABOUT:
_repr = trans_text("age|about") + " " + fdate12p1 _repr = trans_text("age|about") + " " + fdate12p1
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
_repr = trans_text("age|about") + " " + fdate12p1 _repr = trans_text("age|about") + " " + fdate12p1
elif (self.date1.get_modifier() == Date.MOD_RANGE or elif self.date1.is_compound():
self.date1.get_modifier() == Date.MOD_SPAN): # SPAN----------------------------
if self.date2.get_modifier() == Date.MOD_NONE: if self.date2.get_modifier() == Date.MOD_NONE:
start, stop = self.date1.get_start_stop_range() start, stop = self.date1.get_start_stop_range()
start = Date(*start) start = Date(*start)
@ -371,8 +360,7 @@ class Span(object):
_repr = trans_text("less than") + " " + fdate12 _repr = trans_text("less than") + " " + fdate12
elif self.date2.get_modifier() == Date.MOD_ABOUT: elif self.date2.get_modifier() == Date.MOD_ABOUT:
_repr = trans_text("age|about") + " " + fdate12p1 _repr = trans_text("age|about") + " " + fdate12p1
elif (self.date2.get_modifier() == Date.MOD_RANGE or elif self.date2.is_compound():
self.date2.get_modifier() == Date.MOD_SPAN):
start1, stop1 = self.date1.get_start_stop_range() start1, stop1 = self.date1.get_start_stop_range()
start2, stop2 = self.date2.get_start_stop_range() start2, stop2 = self.date2.get_start_stop_range()
start1 = Date(*start1) start1 = Date(*start1)
@ -1045,7 +1033,7 @@ class Date(object):
(self.dateval[Date._POS_YR]) % 10, (self.dateval[Date._POS_YR]) % 10,
self.dateval[Date._POS_MON], self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY]) self.dateval[Date._POS_DAY])
elif self.modifier == Date.MOD_RANGE or self.modifier == Date.MOD_SPAN: elif self.is_compound():
val = "%04d-%02d-%02d - %04d-%02d-%02d" % ( val = "%04d-%02d-%02d - %04d-%02d-%02d" % (
self.dateval[Date._POS_YR], self.dateval[Date._POS_MON], self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY], self.dateval[Date._POS_RYR], self.dateval[Date._POS_DAY], self.dateval[Date._POS_RYR],
@ -1208,7 +1196,7 @@ class Date(object):
of (0, 0, 0, False) is returned. Otherwise, a date of (DD, MM, YY, slash) 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. 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: if self.is_compound():
val = self.dateval[4:8] val = self.dateval[4:8]
else: else:
val = Date.EMPTY val = Date.EMPTY
@ -1238,7 +1226,7 @@ class Date(object):
""" """
Return the item specified. Return the item specified.
""" """
if self.modifier == Date.MOD_SPAN or self.modifier == Date.MOD_RANGE: if self.is_compound():
val = self.dateval[index] val = self.dateval[index]
else: else:
val = 0 val = 0
@ -1285,89 +1273,91 @@ class Date(object):
""" """
self.newyear = value self.newyear = value
def set_yr_mon_day(self, year, month, day): def __set_yr_mon_day(self, year, month, day, pos_yr, pos_mon, pos_day):
dv = list(self.dateval)
dv[pos_yr] = year
dv[pos_mon] = month
dv[pos_day] = day
self.dateval = tuple(dv)
def set_yr_mon_day(self, year, month, day, remove_stop_date = None):
""" """
Set the year, month, and day values. Set the year, month, and day values.
@param remove_stop_date Required parameter for a compound date.
When True, the stop date is changed to the same date as well.
When False, the stop date is not changed.
""" """
dv = list(self.dateval) if self.is_compound() and remove_stop_date is None:
dv[Date._POS_YR] = year raise DateError("Required parameter remove_stop_date not set!")
dv[Date._POS_MON] = month
dv[Date._POS_DAY] = day self.__set_yr_mon_day(year, month, day,
self.dateval = tuple(dv) Date._POS_YR, Date._POS_MON, Date._POS_DAY)
self._calc_sort_value() self._calc_sort_value()
if remove_stop_date and self.is_compound():
self.set2_yr_mon_day(year, month, day)
def _assert_compound(self):
if not self.is_compound():
raise DateError("Operation allowed for compound dates only!")
def set2_yr_mon_day(self, year, month, day): def set2_yr_mon_day(self, year, month, day):
""" """
Set the year, month, and day values. Set the year, month, and day values in the 2nd part of
a compound date (range or span).
""" """
self._assert_compound()
self.__set_yr_mon_day(year, month, day,
Date._POS_RYR, Date._POS_RMON, Date._POS_RDAY)
def __set_yr_mon_day_offset(self, year, month, day, pos_yr, pos_mon, pos_day):
dv = list(self.dateval) dv = list(self.dateval)
dv[Date._POS_RYR] = year if dv[pos_yr]:
dv[Date._POS_RMON] = month dv[pos_yr] += year
dv[Date._POS_RDAY] = day elif year:
dv[pos_yr] = year
if dv[pos_mon]:
dv[pos_mon] += month
elif month:
if month < 0:
dv[pos_mon] = 1 + month
else:
dv[pos_mon] = month
# Fix if month out of bounds:
if month != 0: # only check if changed
if dv[pos_mon] == 0: # subtraction
dv[pos_mon] = 12
dv[pos_yr] -= 1
elif dv[pos_mon] < 0: # subtraction
dv[pos_yr] -= int((-dv[pos_mon]) // 12) + 1
dv[pos_mon] = (dv[pos_mon] % 12)
elif dv[pos_mon] > 12 or dv[pos_mon] < 1:
dv[pos_yr] += int(dv[pos_mon] // 12)
dv[pos_mon] = dv[pos_mon] % 12
self.dateval = tuple(dv) self.dateval = tuple(dv)
self._calc_sort_value()
return (day != 0 or dv[pos_day] > 28)
def set_yr_mon_day_offset(self, year=0, month=0, day=0): def set_yr_mon_day_offset(self, year=0, month=0, day=0):
""" """
Set the year, month, and day values by offset. Offset the date by the given year, month, and day values.
""" """
dv = list(self.dateval) if self.__set_yr_mon_day_offset(year, month, day,
if dv[Date._POS_YR]: Date._POS_YR, Date._POS_MON, Date._POS_DAY):
dv[Date._POS_YR] += year self.set_yr_mon_day(*self.offset(day), remove_stop_date = False)
elif year: if self.is_compound():
dv[Date._POS_YR] = year self.set2_yr_mon_day_offset(year, month, day)
if dv[Date._POS_MON]:
dv[Date._POS_MON] += month
elif month:
if month < 0:
dv[Date._POS_MON] = 1 + month
else:
dv[Date._POS_MON] = month
# Fix if month out of bounds:
if month != 0: # only check if changed
if dv[Date._POS_MON] == 0: # subtraction
dv[Date._POS_MON] = 12
dv[Date._POS_YR] -= 1
elif dv[Date._POS_MON] < 0: # subtraction
dv[Date._POS_YR] -= int((-dv[Date._POS_MON]) // 12) + 1
dv[Date._POS_MON] = (dv[Date._POS_MON] % 12)
elif dv[Date._POS_MON] > 12 or dv[Date._POS_MON] < 1:
dv[Date._POS_YR] += int(dv[Date._POS_MON] // 12)
dv[Date._POS_MON] = dv[Date._POS_MON] % 12
self.dateval = tuple(dv)
self._calc_sort_value()
if day != 0 or dv[Date._POS_DAY] > 28:
self.set_yr_mon_day(*self.offset(day))
def set2_yr_mon_day_offset(self, year=0, month=0, day=0): def set2_yr_mon_day_offset(self, year=0, month=0, day=0):
""" """
Set the year, month, and day values by offset. Set the year, month, and day values by offset in the 2nd part
of a compound date (range or span).
""" """
dv = list(self.dateval) self._assert_compound()
if dv[Date._POS_RYR]: if self.__set_yr_mon_day_offset(year, month, day,
dv[Date._POS_RYR] += year Date._POS_RYR, Date._POS_RMON, Date._POS_RDAY):
elif year: stop = Date(self.get_stop_ymd())
dv[Date._POS_RYR] = year self.set2_yr_mon_day(*stop.offset(day))
if dv[Date._POS_RMON]:
dv[Date._POS_RMON] += month
elif month:
if month < 0:
dv[Date._POS_RMON] = 1 + month
else:
dv[Date._POS_RMON] = month
# Fix if month out of bounds:
if month != 0: # only check if changed
if dv[Date._POS_RMON] == 0: # subtraction
dv[Date._POS_RMON] = 12
dv[Date._POS_RYR] -= 1
elif dv[Date._POS_RMON] < 0: # subtraction
dv[Date._POS_RYR] -= int((-dv[Date._POS_RMON]) / 12) + 1
dv[Date._POS_RMON] = (dv[Date._POS_RMON] % 12)
elif dv[Date._POS_RMON] > 12 or dv[Date._POS_RMON] < 1:
dv[Date._POS_RYR] += int(dv[Date._POS_RMON] / 12)
dv[Date._POS_RMON] = dv[Date._POS_RMON] % 12
self.dateval = tuple(dv)
if day != 0 or dv[Date._POS_RDAY] > 28:
self.set2_yr_mon_day(*self.offset(day))
def copy_offset_ymd(self, year=0, month=0, day=0): def copy_offset_ymd(self, year=0, month=0, day=0):
""" """
@ -1380,24 +1370,19 @@ class Date(object):
new_date = self new_date = self
retval = Date(new_date) retval = Date(new_date)
retval.set_yr_mon_day_offset(year, month, day) retval.set_yr_mon_day_offset(year, month, day)
if (self.get_modifier() == Date.MOD_RANGE or
self.get_modifier() == Date.MOD_SPAN):
retval.set2_yr_mon_day_offset(year, month, day)
if orig_cal == 0: if orig_cal == 0:
return retval return retval
else: else:
retval.convert_calendar(orig_cal) retval.convert_calendar(orig_cal)
return retval return retval
def copy_ymd(self, year=0, month=0, day=0): def copy_ymd(self, year=0, month=0, day=0, remove_stop_date=None):
""" """
Return a Date copy with year, month, and day set. Return a Date copy with year, month, and day set.
@param remove_stop_date Same as in set_yr_mon_day.
""" """
retval = Date(self) retval = Date(self)
retval.set_yr_mon_day(year, month, day) retval.set_yr_mon_day(year, month, day, remove_stop_date)
if (self.get_modifier() == Date.MOD_RANGE or
self.get_modifier() == Date.MOD_SPAN):
retval.set2_yr_mon_day_offset(year, month, day)
return retval return retval
def set_year(self, year): def set_year(self, year):
@ -1502,30 +1487,52 @@ class Date(object):
""" """
return self.text return self.text
def set(self, quality, modifier, calendar, value, text=None, def _zero_adjust_ymd(self, y, m, d):
newyear=0): year = y if y != 0 else 1
month = max(m, 1)
day = max(d, 1)
return (year, month, day)
def set(self, quality=None, modifier=None, calendar=None,
value=None,
text=None, newyear=0):
""" """
Set the date to the specified value. Set the date to the specified value.
Parameters are:: Parameters are::
quality - The date quality for the date (see get_quality quality - The date quality for the date (see get_quality
for more information) for more information).
Defaults to the previous value for the date.
modified - The date modifier for the date (see get_modifier modified - The date modifier for the date (see get_modifier
for more information) for more information)
Defaults to the previous value for the date.
calendar - The calendar associated with the date (see calendar - The calendar associated with the date (see
get_calendar for more information). get_calendar for more information).
Defaults to the previous value for the date.
value - A tuple representing the date information. For a value - A tuple representing the date information. For a
non-compound date, the format is (DD, MM, YY, slash) non-compound date, the format is (DD, MM, YY, slash)
and for a compound date the tuple stores data as and for a compound date the tuple stores data as
(DD, MM, YY, slash1, DD, MM, YY, slash2) (DD, MM, YY, slash1, DD, MM, YY, slash2)
Defaults to the previous value for the date.
text - A text string holding either the verbatim user input text - A text string holding either the verbatim user input
or a comment relating to the date. or a comment relating to the date.
Defaults to the previous value for the date.
newyear - The newyear code, or tuple representing (month, day) newyear - The newyear code, or tuple representing (month, day)
of newyear day. of newyear day.
Defaults to 0.
The sort value is recalculated. The sort value is recalculated.
""" """
if quality is None:
quality = self.quality
if modifier is None:
modifier = self.modifier
if calendar is None:
calendar = self.calendar
if value is None:
value = self.value
if modifier in (Date.MOD_NONE, Date.MOD_BEFORE, if modifier in (Date.MOD_NONE, Date.MOD_BEFORE,
Date.MOD_AFTER, Date.MOD_ABOUT) and len(value) < 4: Date.MOD_AFTER, Date.MOD_ABOUT) and len(value) < 4:
@ -1551,11 +1558,12 @@ class Date(object):
self.calendar = calendar self.calendar = calendar
self.dateval = value self.dateval = value
self.set_new_year(newyear) self.set_new_year(newyear)
year = max(value[Date._POS_YR], 1) year, month, day = self._zero_adjust_ymd(
month = max(value[Date._POS_MON], 1) value[Date._POS_YR],
day = max(value[Date._POS_DAY], 1) value[Date._POS_MON],
value[Date._POS_DAY])
if year == month == 0 and day == 0: if year == month == day == 0:
self.sortval = 0 self.sortval = 0
else: else:
func = Date._calendar_convert[calendar] func = Date._calendar_convert[calendar]
@ -1593,6 +1601,32 @@ class Date(object):
d2.set_calendar(self.calendar) d2.set_calendar(self.calendar)
d2_val = d2.sortval d2_val = d2.sortval
self.sortval += (d1_val - d2_val) + 1 self.sortval += (d1_val - d2_val) + 1
if modifier != Date.MOD_TEXTONLY:
sanity = Date(self)
sanity.convert_calendar(self.calendar, known_valid = False)
# We don't do the roundtrip conversion on self, becaue
# it would remove uncertainty on day/month expressed with zeros
# Did the roundtrip change the date value?!
if sanity.dateval != value:
# Maybe it is OK because of undetermined value adjustment?
zl = zip(sanity.dateval, value)
# Loop over all values present, whether compound or not
for d,m,y,sl in zip(*[iter(zl)]*4):
# each of d,m,y,sl is a pair from dateval and value, to compare
for adjusted,original in d,m:
if adjusted != original and not(original == 0 and adjusted == 1):
raise DateError("Invalid day/month {} passed in value {}".
format(original, value))
adjusted,original = y
if adjusted != original:
raise DateError("Invalid year {} passed in value {}".
format(original, value))
# ignore slash difference
if text: if text:
self.text = text self.text = text
@ -1609,26 +1643,30 @@ class Date(object):
""" """
Calculate the numerical sort value associated with the date. Calculate the numerical sort value associated with the date.
""" """
year = max(self.dateval[Date._POS_YR], 1) year, month, day = self._zero_adjust_ymd(
month = max(self.dateval[Date._POS_MON], 1) self.dateval[Date._POS_YR],
day = max(self.dateval[Date._POS_DAY], 1) self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY])
if year == month == 0 and day == 0: if year == month == 0 and day == 0:
self.sortval = 0 self.sortval = 0
else: else:
func = Date._calendar_convert[self.calendar] func = Date._calendar_convert[self.calendar]
self.sortval = func(year, month, day) self.sortval = func(year, month, day)
def convert_calendar(self, calendar): def convert_calendar(self, calendar, known_valid=True):
""" """
Convert the date from the current calendar to the specified calendar. Convert the date from the current calendar to the specified calendar.
""" """
if calendar == self.calendar and self.newyear == Date.NEWYEAR_JAN1: if (known_valid # if not known valid, round-trip convert anyway
and calendar == self.calendar
and self.newyear == Date.NEWYEAR_JAN1):
return return
(year, month, day) = Date._calendar_change[calendar](self.sortval) (year, month, day) = Date._calendar_change[calendar](self.sortval)
if self.is_compound(): if self.is_compound():
ryear = max(self.dateval[Date._POS_RYR], 1) ryear, rmonth, rday = self._zero_adjust_ymd(
rmonth = max(self.dateval[Date._POS_RMON], 1) self.dateval[Date._POS_RYR],
rday = max(self.dateval[Date._POS_RDAY], 1) self.dateval[Date._POS_RMON],
self.dateval[Date._POS_RDAY])
sdn = Date._calendar_convert[self.calendar](ryear, rmonth, rday) sdn = Date._calendar_convert[self.calendar](ryear, rmonth, rday)
(nyear, nmonth, nday) = Date._calendar_change[calendar](sdn) (nyear, nmonth, nday) = Date._calendar_change[calendar](sdn)
self.dateval = (day, month, year, False, self.dateval = (day, month, year, False,
@ -1692,6 +1730,12 @@ class Date(object):
""" """
return (self.get_year(), self.get_month(), self.get_day()) return (self.get_year(), self.get_month(), self.get_day())
def get_stop_ymd(self):
"""
Return (year, month, day) of the stop date, or all-zeros if it's not defined.
"""
return (self.get_stop_year(), self.get_stop_month(), self.get_stop_day())
def offset(self, value): def offset(self, value):
""" """
Return (year, month, day) of this date +- value. Return (year, month, day) of this date +- value.

View File

@ -38,7 +38,7 @@ from ...config import config
from ...datehandler import get_date_formats, set_format from ...datehandler import get_date_formats, set_format
from ...datehandler import parser as _dp from ...datehandler import parser as _dp
from ...datehandler import displayer as _dd from ...datehandler import displayer as _dd
from ...lib.date import Date from ...lib.date import Date, DateError
date_tests = {} date_tests = {}
@ -193,7 +193,7 @@ for calendar in (Date.CAL_JULIAN,
d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment") d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment")
dates.append( d) dates.append( d)
for calendar in (Date.CAL_HEBREW, Date.CAL_FRENCH): for calendar in (Date.CAL_HEBREW, Date.CAL_HEBREW):
for month in range(1,14): for month in range(1,14):
d = Date() d = Date()
d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment") d.set(quality,modifier,calendar,(4,month,1789,False),"Text comment")
@ -422,6 +422,81 @@ class SwedishDateTest(BaseDateTest):
self.assertEqual(date.sortval, self.assertEqual(date.sortval,
date.to_calendar('gregorian').sortval) date.to_calendar('gregorian').sortval)
class Test_set2(BaseDateTest):
"""
Test the Date.set2_... setters -- the ones to manipulate the 2nd date
of a compound date
"""
def setUp(self):
self.date = d = Date()
d.set(modifier=Date.MOD_RANGE,
#d m y sl--d m y sl
value=(1, 1, 2000, 0, 1, 1, 2010, 0))
def testStartStopSanity(self):
start,stop = self.date.get_start_stop_range()
self.assertEqual(start, (2000, 1, 1))
self.assertEqual(stop, (2010, 1, 1))
def test_set2_ymd_overrides_stop_date(self):
self.date.set2_yr_mon_day(2013, 2, 2)
start,stop = self.date.get_start_stop_range()
self.assertEqual(start, (2000, 1, 1))
self.assertEqual(stop, (2013, 2, 2))
def test_set_ymd_overrides_both_dates(self):
self.date.set_yr_mon_day(2013, 2, 2, remove_stop_date = True)
start,stop = self.date.get_start_stop_range()
self.assertEqual(start, stop)
self.assertEqual(stop, (2013, 2, 2))
def test_set_ymd_offset_updates_both_ends(self):
self.date.set_yr_mon_day_offset(+2, +2, +2)
start,stop = self.date.get_start_stop_range()
self.assertEqual(start, (2002, 3, 3))
self.assertEqual(stop, (2012, 3, 3))
def test_set2_ymd_offset_updates_stop_date(self):
self.date.set2_yr_mon_day_offset(+7, +5, +5)
start,stop = self.date.get_start_stop_range()
self.assertEqual(start, (2000, 1, 1))
self.assertEqual(stop, (2017, 6, 6))
def test_copy_offset_ymd_preserves_orig(self):
copied = self.date.copy_offset_ymd(year=-1)
self.testStartStopSanity()
start,stop = copied.get_start_stop_range()
self.assertEqual(start, (1999, 1, 1))
self.assertEqual(stop, (2009, 1, 1))
def test_copy_ymd_preserves_orig(self):
copied = self.date.copy_ymd(year=1000, month=10, day=10,
remove_stop_date=True)
self.testStartStopSanity()
start,stop = copied.get_start_stop_range()
self.assertEqual(start, (1000, 10, 10))
self.assertEqual(stop, (1000, 10, 10))
def _test_set2_function_raises_error_unless_compound(self, function):
for mod in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER,
Date.MOD_ABOUT,
Date.MOD_TEXTONLY):
self.date.set_modifier(mod)
try:
function(self.date)
self.assertTrue(False,
"Modifier: {}, dateval: {} - exception expected!".format(
mod, self.date.dateval))
except DateError:
pass
def test_set2_ymd_raises_error_unless_compound(self):
self._test_set2_function_raises_error_unless_compound(
lambda date: date.set2_yr_mon_day(2013, 2, 2))
def test_set2_ymd_offset_raises_error_unless_compound(self):
self._test_set2_function_raises_error_unless_compound(
lambda date: date.set2_yr_mon_day_offset(year=-1))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()