7034,7045,7065,7066: back merge from trunk

Note: need to disable CAL.FRENCH in gramps_test.py,
otherwise they fail due to #7068

svn: r23159
This commit is contained in:
Vassilii Khachaturov 2013-09-18 09:55:54 +00:00
parent 03e800d808
commit 3f3fc5a84d
3 changed files with 283 additions and 151 deletions

View File

@ -23,7 +23,7 @@
"""
Provide calendar to sdn (serial date number) conversion.
"""
from __future__ import division
from __future__ import division, print_function
#-------------------------------------------------------------------------
#
# Python modules
@ -45,14 +45,15 @@ _JLN_SDN_OFFSET = 32083
_JLN_DAYS_PER_5_MONTHS = 153
_JLN_DAYS_PER_4_YEARS = 1461
_HBR_HALAKIM_PER_HOUR = 1080
_HBR_HALAKIM_PER_DAY = 25920
_HBR_HALAKIM_PER_LUNAR_CYCLE = 765433
_HBR_HALAKIM_PER_METONIC_CYCLE = 179876755
_HBR_HALAKIM_PER_LUNAR_CYCLE = 29 * _HBR_HALAKIM_PER_DAY + 13753
_HBR_HALAKIM_PER_METONIC_CYCLE = _HBR_HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7)
_HBR_SDN_OFFSET = 347997
_HBR_NEW_MOON_OF_CREATION = 31524
_HBR_NOON = 19440
_HBR_AM3_11_20 = 9924
_HBR_AM9_32_43 = 16789
_HBR_NOON = 18 * _HBR_HALAKIM_PER_HOUR
_HBR_AM3_11_20 = (9 * _HBR_HALAKIM_PER_HOUR) + 204
_HBR_AM9_32_43 = (15 * _HBR_HALAKIM_PER_HOUR) + 589
_HBR_SUNDAY = 0
_HBR_MONDAY = 1
@ -122,9 +123,9 @@ def _tishri_molad(input_day):
# really quite close.
while molad_day < (input_day - 6940 + 310):
metonic_cycle = metonic_cycle + 1
molad_halakim = molad_halakim + _HBR_HALAKIM_PER_METONIC_CYCLE
molad_day = molad_day + ( molad_halakim // _HBR_HALAKIM_PER_DAY)
metonic_cycle += 1
molad_halakim += _HBR_HALAKIM_PER_METONIC_CYCLE
molad_day += molad_halakim // _HBR_HALAKIM_PER_DAY
molad_halakim = molad_halakim % _HBR_HALAKIM_PER_DAY
# Find the molad of Tishri closest to this date.
@ -133,12 +134,11 @@ def _tishri_molad(input_day):
if molad_day > input_day - 74:
break
molad_halakim = molad_halakim + (_HBR_HALAKIM_PER_LUNAR_CYCLE
* _HBR_MONTHS_PER_YEAR[metonic_year])
molad_day = molad_day + (molad_halakim // _HBR_HALAKIM_PER_DAY)
molad_halakim += (_HBR_HALAKIM_PER_LUNAR_CYCLE
* _HBR_MONTHS_PER_YEAR[metonic_year])
molad_day += molad_halakim // _HBR_HALAKIM_PER_DAY
molad_halakim = molad_halakim % _HBR_HALAKIM_PER_DAY
else:
metonic_year += 1
return (metonic_cycle, metonic_year, molad_day, molad_halakim)
def _molad_of_metonic_cycle(metonic_cycle):
@ -161,10 +161,10 @@ def _molad_of_metonic_cycle(metonic_cycle):
# will be in d1.
d2 = r2 // _HBR_HALAKIM_PER_DAY
r2 = r2 - (d2 * _HBR_HALAKIM_PER_DAY)
r2 -= d2 * _HBR_HALAKIM_PER_DAY
r1 = (r2 << 16) | (r1 & 0xFFFF)
d1 = r1 // _HBR_HALAKIM_PER_DAY
r1 = r1 - ( d1 * _HBR_HALAKIM_PER_DAY)
r1 -= d1 * _HBR_HALAKIM_PER_DAY
molad_day = (d2 << 16) | d1
molad_halakim = r1
@ -261,9 +261,10 @@ def hebrew_sdn(year, month, day):
return sdn + _HBR_SDN_OFFSET
def hebrew_ymd(sdn):
"""Convert an SDN number to a Julian calendar date."""
"""Convert an SDN number to a Hebrew calendar date."""
input_day = sdn - _HBR_SDN_OFFSET
# TODO if input_day <= 0, the result is a date invalid in Hebrew calendar!
(metonic_cycle, metonic_year, day, halakim) = _tishri_molad(input_day)
tishri1 = _tishri1(metonic_year, day, halakim)
@ -284,9 +285,9 @@ def hebrew_ymd(sdn):
# We need the length of the year to figure this out, so find
# Tishri 1 of the next year.
halakim = halakim + (_HBR_HALAKIM_PER_LUNAR_CYCLE
* _HBR_MONTHS_PER_YEAR[metonic_year])
day = day + (halakim // _HBR_HALAKIM_PER_DAY)
halakim += (_HBR_HALAKIM_PER_LUNAR_CYCLE
* _HBR_MONTHS_PER_YEAR[metonic_year])
day += halakim // _HBR_HALAKIM_PER_DAY
halakim = halakim % _HBR_HALAKIM_PER_DAY
tishri1_after = _tishri1((metonic_year + 1) % 19, day, halakim)
else:
@ -320,24 +321,24 @@ def hebrew_ymd(sdn):
day = input_day - tishri1 + 207
if day > 0:
return (year, month, day)
month = month - 1
day = day + 30
month -= 1
day += 30
if day > 0:
return (year, month, day)
month = month - 1
day = day + 30
month -= 1
day += 30
else:
month = 6
day = input_day - tishri1 + 207
if day > 0:
return (year, month, day)
month = month - 1
day = day + 30
month -= 1
day += 30
if day > 0:
return (year, month, day)
month = month - 1
day = day + 29
month -= 1
day += 29
if day > 0:
return (year, month, day)
@ -348,25 +349,23 @@ def hebrew_ymd(sdn):
tishri1 = _tishri1(metonic_year, day, halakim)
year_length = tishri1_after - tishri1
cday = input_day - tishri1 - 29
day = input_day - tishri1 - 29
if year_length == 355 or year_length == 385 :
# Heshvan has 30 days
if day <= 30:
month = 2
day = cday
return (year, month, day)
day = day - 30
day -= 30
else:
# Heshvan has 29 days
if day <= 29:
month = 2
day = cday
return (year, month, day)
cday = cday - 29
day -= 29
# It has to be Kislev
return (year, 3, cday)
return (year, 3, day)
def julian_sdn(year, month, day):
"""Convert a Julian calendar date to an SDN number."""
@ -573,3 +572,16 @@ def swedish_ymd(sdn):
return gregorian_ymd(sdn)
else:
return julian_ymd(sdn)
try:
import sdn
hebrew_ymd = sdn.SdnToJewish # Fix bug# 7066
hebrew_sdn = sdn.JewishToSdn
#TODO maybe alias the other local invented wheels to Calendar convertors
except ImportError:
import logging
LOG = logging.getLogger(".calendar")
LOG.warn("sdn not available. "
"Install Calendar with pypi for native Hebrew calendar calculations.")

View File

@ -125,8 +125,7 @@ class Span(object):
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
#self.repr = "about " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
start, stop = self.date2.get_start_stop_range()
start = Date(*start)
stop = Date(*stop)
@ -157,8 +156,7 @@ class Span(object):
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
#self.repr = "about " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -184,8 +182,7 @@ class Span(object):
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.AFTER)
#self.repr = "more than about " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -211,14 +208,12 @@ class Span(object):
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
#self.repr = "about " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
v = self.date1.sortval - self.date2.sortval
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
#self.repr = "about " + self._format(self._diff(self.date1, self.date2))
elif (self.date1.get_modifier() == Date.MOD_RANGE or
self.date1.get_modifier() == Date.MOD_SPAN): # SPAN----------------------------
elif self.date1.is_compound():
if self.date2.get_modifier() == Date.MOD_NONE:
start, stop = self.date1.get_start_stop_range()
start = Date(*start)
@ -244,8 +239,7 @@ class Span(object):
self.sort = (v, -Span.ABOUT)
self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
#self.repr = "about " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
start1, stop1 = self.date1.get_start_stop_range()
start2, stop2 = self.date2.get_start_stop_range()
start1 = Date(*start1)
@ -319,8 +313,7 @@ class Span(object):
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
# TO_FIX: bug #5293 !
self.repr = _("age|about") + " " + self._format(self._diff(self.date1, self.date2)).format(precision=1)
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
start, stop = self.date2.get_start_stop_range()
start = Date(*start)
stop = Date(*stop)
@ -351,8 +344,7 @@ class Span(object):
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
self.repr = _("less than about") + " " + self._format(self._diff(self.date1, self.date2))
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
#v = self.date1.sortval - self.date2.sortval
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -378,8 +370,7 @@ class Span(object):
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.AFTER)
self.repr = _("more than about") + " " + self._format(self._diff(self.date1, self.date2)).format(precision=1)
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
#v = self.date1.sortval - self.date2.sortval
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
@ -405,14 +396,12 @@ class Span(object):
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
self.repr = _("age|about") + " " + self._format(self._diff(self.date1, self.date2)).format(precision=1)
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
#v = self.date1.sortval - self.date2.sortval
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
self.repr = _("age|about") + " " + self._format(self._diff(self.date1, self.date2)).format(precision=1)
elif (self.date1.get_modifier() == Date.MOD_RANGE or
self.date1.get_modifier() == Date.MOD_SPAN): # SPAN----------------------------
elif self.date1.is_compound():
if self.date2.get_modifier() == Date.MOD_NONE:
start, stop = self.date1.get_start_stop_range()
start = Date(*start)
@ -438,8 +427,7 @@ class Span(object):
#self.sort = (v, -Span.ABOUT)
#self.minmax = (v - Span.ABOUT, v + Span.ABOUT)
self.repr = _("age|about") + " " + self._format(self._diff(self.date1, self.date2)).format(precision=1)
elif (self.date2.get_modifier() == Date.MOD_RANGE or
self.date2.get_modifier() == Date.MOD_SPAN):
elif self.date2.is_compound():
start1, stop1 = self.date1.get_start_stop_range()
start2, stop2 = self.date2.get_start_stop_range()
start1 = Date(*start1)
@ -1039,7 +1027,7 @@ class Date(object):
(self.dateval[Date._POS_YR]) % 10,
self.dateval[Date._POS_MON],
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" % (
self.dateval[Date._POS_YR], self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY], self.dateval[Date._POS_RYR],
@ -1202,7 +1190,7 @@ class Date(object):
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:
if self.is_compound():
val = self.dateval[4:8]
else:
val = Date.EMPTY
@ -1232,7 +1220,7 @@ class Date(object):
"""
Return the item specified.
"""
if self.modifier == Date.MOD_SPAN or self.modifier == Date.MOD_RANGE:
if self.is_compound():
val = self.dateval[index]
else:
val = 0
@ -1279,89 +1267,91 @@ class Date(object):
"""
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.
@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)
dv[Date._POS_YR] = year
dv[Date._POS_MON] = month
dv[Date._POS_DAY] = day
self.dateval = tuple(dv)
if self.is_compound() and remove_stop_date is None:
raise DateError("Required parameter remove_stop_date not set!")
self.__set_yr_mon_day(year, month, day,
Date._POS_YR, Date._POS_MON, Date._POS_DAY)
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):
"""
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[Date._POS_RYR] = year
dv[Date._POS_RMON] = month
dv[Date._POS_RDAY] = day
if dv[pos_yr]:
dv[pos_yr] += year
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._calc_sort_value()
return (day != 0 or dv[pos_day] > 28)
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 dv[Date._POS_YR]:
dv[Date._POS_YR] += year
elif year:
dv[Date._POS_YR] = year
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))
if self.__set_yr_mon_day_offset(year, month, day,
Date._POS_YR, Date._POS_MON, Date._POS_DAY):
self.set_yr_mon_day(*self.offset(day), remove_stop_date = False)
if self.is_compound():
self.set2_yr_mon_day_offset(year, month, day)
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)
if dv[Date._POS_RYR]:
dv[Date._POS_RYR] += year
elif year:
dv[Date._POS_RYR] = year
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))
self._assert_compound()
if self.__set_yr_mon_day_offset(year, month, day,
Date._POS_RYR, Date._POS_RMON, Date._POS_RDAY):
stop = Date(self.get_stop_ymd())
self.set2_yr_mon_day(*stop.offset(day))
def copy_offset_ymd(self, year=0, month=0, day=0):
"""
@ -1374,24 +1364,19 @@ class Date(object):
new_date = self
retval = Date(new_date)
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:
return retval
else:
retval.convert_calendar(orig_cal)
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.
@param remove_stop_date Same as in set_yr_mon_day.
"""
retval = Date(self)
retval.set_yr_mon_day(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)
retval.set_yr_mon_day(year, month, day, remove_stop_date)
return retval
def set_year(self, year):
@ -1496,30 +1481,52 @@ class Date(object):
"""
return self.text
def set(self, quality, modifier, calendar, value, text=None,
newyear=0):
def _zero_adjust_ymd(self, y, m, d):
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.
Parameters are::
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
for more information)
Defaults to the previous value for the date.
calendar - The calendar associated with the date (see
get_calendar for more information).
Defaults to the previous value for the date.
value - A tuple representing the date information. For a
non-compound date, the format is (DD, MM, YY, slash)
and for a compound date the tuple stores data as
(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
or a comment relating to the date.
Defaults to the previous value for the date.
newyear - The newyear code, or tuple representing (month, day)
of newyear day.
of newyear day.
Defaults to 0.
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,
Date.MOD_AFTER, Date.MOD_ABOUT) and len(value) < 4:
@ -1545,11 +1552,12 @@ class Date(object):
self.calendar = calendar
self.dateval = value
self.set_new_year(newyear)
year = max(value[Date._POS_YR], 1)
month = max(value[Date._POS_MON], 1)
day = max(value[Date._POS_DAY], 1)
year, month, day = self._zero_adjust_ymd(
value[Date._POS_YR],
value[Date._POS_MON],
value[Date._POS_DAY])
if year == month == 0 and day == 0:
if year == month == day == 0:
self.sortval = 0
else:
func = Date._calendar_convert[calendar]
@ -1587,6 +1595,32 @@ class Date(object):
d2.set_calendar(self.calendar)
d2_val = d2.sortval
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:
self.text = text
@ -1603,26 +1637,30 @@ class Date(object):
"""
Calculate 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)
year, month, day = self._zero_adjust_ymd(
self.dateval[Date._POS_YR],
self.dateval[Date._POS_MON],
self.dateval[Date._POS_DAY])
if year == month == 0 and day == 0:
self.sortval = 0
else:
func = Date._calendar_convert[self.calendar]
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.
"""
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
(year, month, day) = Date._calendar_change[calendar](self.sortval)
if self.is_compound():
ryear = max(self.dateval[Date._POS_RYR], 1)
rmonth = max(self.dateval[Date._POS_RMON], 1)
rday = max(self.dateval[Date._POS_RDAY], 1)
ryear, rmonth, rday = self._zero_adjust_ymd(
self.dateval[Date._POS_RYR],
self.dateval[Date._POS_RMON],
self.dateval[Date._POS_RDAY])
sdn = Date._calendar_convert[self.calendar](ryear, rmonth, rday)
(nyear, nmonth, nday) = Date._calendar_change[calendar](sdn)
self.dateval = (day, month, year, False,
@ -1686,6 +1724,12 @@ class Date(object):
"""
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):
"""
Return (year, month, day) of this date +- value.

View File

@ -49,7 +49,7 @@ import config
import DateHandler
from DateHandler import parser as _dp
from DateHandler import displayer as _dd
from gen.lib.date import Date, Span
from gen.lib.date import Date, Span, DateError
gettext.textdomain("gramps")
gettext.install("gramps",loc,unicode=1)
@ -444,6 +444,82 @@ def suite4():
count += 1
return suite
class Test_set2(unittest.TestCase):
"""
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__":
unittest.TextTestRunner().run(suite())
unittest.TextTestRunner().run(suite2())