Display leap day anniversaries every year on calendars

Added a new anniversary method to dates which returns a day and
month on which to display leap day anniversaries on non-leap
years.

Added an option in the preferences display tab which allows the
user to specify on which day the anniversary should be displayed.
The options are: 28th Feb, 1st Mar or only on 29th Feb.

Fixes #12511
This commit is contained in:
SNoiraud 2021-12-26 10:05:48 +01:00 committed by Nick Hall
parent e2dbba4a62
commit f59e4d2889
8 changed files with 87 additions and 4 deletions

View File

@ -250,6 +250,7 @@ register('preferences.quick-backup-include-mode', False)
register('preferences.date-format', 0) register('preferences.date-format', 0)
register('preferences.calendar-format-report', 0) register('preferences.calendar-format-report', 0)
register('preferences.calendar-format-input', 0) register('preferences.calendar-format-input', 0)
register('preferences.february-29', 0) # 0: 02/28; 1: 03/01; 2: only the 02/29
register('preferences.cprefix', 'C%04d') register('preferences.cprefix', 'C%04d')
register('preferences.default-source', False) register('preferences.default-source', False)
register('preferences.tag-on-import', False) register('preferences.tag-on-import', False)

View File

@ -30,6 +30,7 @@
# #
# ------------------------------------------------------------------------ # ------------------------------------------------------------------------
import logging import logging
import calendar
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# #
@ -2064,6 +2065,24 @@ class Date:
self.dateval = tuple(dlist) self.dateval = tuple(dlist)
self._calc_sort_value() self._calc_sort_value()
def anniversary(self, year):
"""
If the date is February 29, you must choose the day to view it in the
event of a non-leap year.
This is usualy used in calendars.
"""
month = self.dateval[Date._POS_MON]
day = self.dateval[Date._POS_DAY]
if month == 2 and day == 29 and not calendar.isleap(year):
day_show = config.get('preferences.february-29')
if day_show == 0:
day = 28
elif day_show == 1:
month = 3
day = 1
# else: # In all other cases, keep the february 29 day
return (month, day)
year = property(get_year, set_year) year = property(get_year, set_year)

View File

@ -1232,6 +1232,41 @@ class EmptyDateTest(BaseDateTest):
d.set(value=(1, 1, 1900, False, 1, 1, 1910, False), modifier=Date.MOD_SPAN) d.set(value=(1, 1, 1900, False, 1, 1, 1910, False), modifier=Date.MOD_SPAN)
self.assertFalse(d.is_empty()) self.assertFalse(d.is_empty())
# -------------------------------------------------------------------------
#
# AnniversaryDateTest
#
# -------------------------------------------------------------------------
class AnniversaryDateTest(BaseDateTest):
"""
Tests for leap day anniversary dates.
"""
def test_leapyear_1(self):
config.set('preferences.february-29', 0)
d = Date(1904, 2, 29)
self.assertEqual(d.anniversary(1908), (2, 29))
def test_leapyear_2(self):
config.set('preferences.february-29', 1)
d = Date(1904, 2, 29)
self.assertEqual(d.anniversary(1908), (2, 29))
def test_nonleapyear_before(self):
config.set('preferences.february-29', 0)
d = Date(1904, 2, 29)
self.assertEqual(d.anniversary(1910), (2, 28))
def test_nonleapyear_after(self):
config.set('preferences.february-29', 1)
d = Date(1904, 2, 29)
self.assertEqual(d.anniversary(1910), (3, 1))
def test_nonleapyear_keep(self):
config.set('preferences.february-29', 2)
d = Date(1904, 2, 29)
self.assertEqual(d.anniversary(1910), (2, 29))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -1386,6 +1386,23 @@ class GrampsPreferences(ConfigureDialog):
grid.attach(lwidget, 1, row, 1, 1) grid.attach(lwidget, 1, row, 1, 1)
grid.attach(obox, 2, row, 2, 1) grid.attach(obox, 2, row, 2, 1)
row += 1
# Birthday on february 29
feb29 = Gtk.ComboBoxText()
show_on = [_("on the previous day"),
_("on the next day"),
_("only on leap years")]
list(map(feb29.append_text, show_on))
active = config.get('preferences.february-29')
feb29.set_active(active)
feb29.connect('changed', self.date_february_29_display_on)
ttip = _("For non leap years, anniversaries are displayed on either "
"February 28, March 1 or not at all in Gregorian calendars")
feb29.set_tooltip_text(ttip)
lwidget = BasicLabel(_("Show leap day anniversaries"))
grid.attach(lwidget, 1, row, 1, 1)
grid.attach(feb29, 2, row, 2, 1)
row += 1 row += 1
# Status bar: # Status bar:
obox = Gtk.ComboBoxText() obox = Gtk.ComboBoxText()
@ -1572,6 +1589,12 @@ class GrampsPreferences(ConfigureDialog):
""" """
config.set('preferences.calendar-format-input', obj.get_active()) config.set('preferences.calendar-format-input', obj.get_active())
def date_february_29_display_on(self, obj):
"""
Save "February 29 display on " option.
"""
config.set('preferences.february-29', obj.get_active())
def autobackup_changed(self, obj): def autobackup_changed(self, obj):
""" """
Save "Autobackup" option on change. Save "Autobackup" option on change.

View File

@ -28,6 +28,7 @@
import datetime import datetime
import time import time
from functools import partial from functools import partial
import calendar
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
@ -374,6 +375,7 @@ class Calendar(Report):
day = birth_date.get_day() day = birth_date.get_day()
prob_alive_date = Date(self.year, month, day) prob_alive_date = Date(self.year, month, day)
month, day = birth_date.anniversary(self.year)
nyears = self.year - year nyears = self.year - year
short_name = self.get_name( short_name = self.get_name(

View File

@ -27,6 +27,7 @@
# #
#------------------------------------------------------------------------ #------------------------------------------------------------------------
import datetime, time import datetime, time
import calendar
#------------------------------------------------------------------------ #------------------------------------------------------------------------
# #
@ -37,8 +38,9 @@ from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext _ = glocale.translation.gettext
from gramps.gen.const import URL_HOMEPAGE from gramps.gen.const import URL_HOMEPAGE
from gramps.gen.errors import ReportError from gramps.gen.errors import ReportError
from gramps.gen.config import config
from gramps.gen.lib import NameType, EventType, Name, Date, Person, Surname from gramps.gen.lib import NameType, EventType, Name, Date, Person, Surname
from gramps.gen.lib.date import gregorian from gramps.gen.lib.date import gregorian, Today
from gramps.gen.relationship import get_relationship_calculator from gramps.gen.relationship import get_relationship_calculator
from gramps.gen.plug.docgen import (FontStyle, ParagraphStyle, GraphicsStyle, from gramps.gen.plug.docgen import (FontStyle, ParagraphStyle, GraphicsStyle,
FONT_SERIF, PARA_ALIGN_RIGHT, FONT_SERIF, PARA_ALIGN_RIGHT,
@ -291,7 +293,7 @@ class BirthdayReport(Report):
day = birth_date.get_day() day = birth_date.get_day()
prob_alive_date = Date(self.year, month, day) prob_alive_date = Date(self.year, month, day)
month, day = birth_date.anniversary(self.year)
nyears = self.year - year nyears = self.year - year
# add some things to handle maiden name: # add some things to handle maiden name:
father_lastname = None # husband, actually father_lastname = None # husband, actually

View File

@ -968,7 +968,7 @@ class CalendarPage(BasePage):
# current year of calendar, month nd day is their birth # current year of calendar, month nd day is their birth
# month and birth day # month and birth day
prob_alive_date = Date(this_year, month, day) prob_alive_date = Date(this_year, month, day)
month, day = birth_date.anniversary(this_year)
# add some things to handle maiden name: # add some things to handle maiden name:
father_surname = None # husband, actually father_surname = None # husband, actually
if person.gender == Person.FEMALE: if person.gender == Person.FEMALE:
@ -1093,6 +1093,7 @@ class CalendarPage(BasePage):
month = event_date.get_month() month = event_date.get_month()
day = event_date.get_day() day = event_date.get_day()
month, day = event_date.anniversary(this_year)
# date to figure if someone is still alive # date to figure if someone is still alive
prob_alive_date = Date(this_year, prob_alive_date = Date(this_year,
month, day) month, day)

View File

@ -1370,7 +1370,7 @@ return false;
# current year of calendar, month nd day is their birth # current year of calendar, month nd day is their birth
# month and birth day # month and birth day
prob_alive_date = Date(this_year, month, day) prob_alive_date = Date(this_year, month, day)
month, day = birth_date.anniversary(this_year)
# add some things to handle maiden name: # add some things to handle maiden name:
father_surname = None # husband, actually father_surname = None # husband, actually
if person.gender == Person.FEMALE: if person.gender == Person.FEMALE: