jenkins-bot submitted this change.

View Change


Approvals: Matěj Suchánek: Looks good to me, but someone else must approve Xqt: Looks good to me, approved jenkins-bot: Verified
[Feature] Allow normalization of WbTime objects

* Makes a new WbTime object that omits any parameters that
are not represented by the precision
* Handles precisions lower than "year"

Bug: T123888
Change-Id: Iabb820ad20e6b76f9aec8cacd6134f6e135e4a81
---
M pywikibot/__init__.py
M tests/wikibase_tests.py
2 files changed, 194 insertions(+), 0 deletions(-)

diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index db7bdfe..f4e7889 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -486,6 +486,75 @@
timezone=timezone, calendarmodel=calendarmodel,
site=site)

+ def normalize(self) -> 'WbTime':
+ """Normalizes the WbTime object to account for precision.
+
+ Normalization is needed because WbTime objects can have hidden
+ values that affect naive comparisons, such as an object set to
+ a precision of YEAR but containing a month and day value.
+
+ This function returns a new normalized object and does not do
+ any modification in place.
+
+ Note: Normalized WbTime objects can only be compared to other
+ normalized WbTime objects of the same precision. Normalization
+ might make a WbTime object that was less than another WbTime object
+ before normalization, greater than it after normalization, or vice
+ versa.
+ """
+ year = self.year
+ # This is going to get messy.
+ if self.PRECISION['1000000000'] <= self.precision <= self.PRECISION['10000']: # noqa: E501
+ # 1000000000 == 10^9
+ power_of_10 = 10 ** (9 - self.precision)
+ # Wikidata rounds the number based on the first non-decimal digit.
+ # Python's round function will round -15.5 to -16, and +15.5 to +16
+ # so we don't need to do anything complicated like the other
+ # examples.
+ year = round(year / power_of_10) * power_of_10
+ elif self.precision == self.PRECISION['millenia']:
+ # Similar situation with centuries
+ year_float = year / 1000
+ if year_float < 0:
+ year = math.floor(year_float)
+ else:
+ year = math.ceil(year_float)
+ year *= 1000
+ elif self.precision == self.PRECISION['century']:
+ # For century, -1301 is the same century as -1400 but not -1401.
+ # Similar for 1901 and 2000 vs 2001.
+ year_float = year / 100
+ if year_float < 0:
+ year = math.floor(year_float)
+ else:
+ year = math.ceil(year_float)
+ year *= 100
+ elif self.precision == self.PRECISION['decade']:
+ # For decade, -1340 is the same decade as -1349 but not -1350.
+ # Similar for 2010 and 2019 vs 2020
+ year_float = year / 10
+ year = math.trunc(year_float)
+ year *= 10
+ kwargs = {
+ 'precision': self.precision,
+ 'before': self.before,
+ 'after': self.after,
+ 'timezone': self.timezone,
+ 'calendarmodel': self.calendarmodel,
+ 'year': year
+ }
+ if self.precision >= self.PRECISION['month']:
+ kwargs['month'] = self.month
+ if self.precision >= self.PRECISION['day']:
+ kwargs['day'] = self.day
+ if self.precision >= self.PRECISION['hour']:
+ kwargs['hour'] = self.hour
+ if self.precision >= self.PRECISION['minute']:
+ kwargs['minute'] = self.minute
+ if self.precision >= self.PRECISION['second']:
+ kwargs['second'] = self.second
+ return type(self)(**kwargs)
+
def toTimestr(self, force_iso: bool = False) -> str:
"""
Convert the data to a UTC date/time string.
diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py
index c97f6f7..3e7bfdb 100755
--- a/tests/wikibase_tests.py
+++ b/tests/wikibase_tests.py
@@ -343,6 +343,117 @@
hour=0, minute=5, site=repo))
self.assertEqual(t9.precision, pywikibot.WbTime.PRECISION['minute'])

+ def test_WbTime_normalization(self):
+ """Test WbTime normalization."""
+ repo = self.get_repo()
+ # flake8 is being annoying, so to reduce line length, I'll make
+ # some aliases here
+ decade = pywikibot.WbTime.PRECISION['decade']
+ century = pywikibot.WbTime.PRECISION['century']
+ millenia = pywikibot.WbTime.PRECISION['millenia']
+ t = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12)
+ t2 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['second'])
+ t3 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['minute'])
+ t4 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['hour'])
+ t5 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['day'])
+ t6 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['month'])
+ t7 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=pywikibot.WbTime.PRECISION['year'])
+ t8 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=decade)
+ t9 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=century)
+ t10 = pywikibot.WbTime(site=repo, year=2010, month=1, day=1, hour=12,
+ minute=43, second=12,
+ precision=millenia)
+ self.assertEqual(t.normalize(), t)
+ self.assertEqual(t2.normalize(), t.normalize())
+ self.assertEqual(t3.normalize(),
+ pywikibot.WbTime(site=repo, year=2010, month=1,
+ day=1, hour=12, minute=43))
+ self.assertEqual(t4.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ month=1, day=1, hour=12))
+ self.assertEqual(t5.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ month=1, day=1))
+ self.assertEqual(t6.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ month=1))
+ self.assertEqual(
+ t7.normalize(), pywikibot.WbTime(site=repo, year=2010))
+ self.assertEqual(t8.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ precision=decade))
+ self.assertEqual(t9.normalize(),
+ pywikibot.WbTime(site=repo, year=2100,
+ precision=century))
+ self.assertEqual(t9.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ precision=century).normalize())
+ self.assertEqual(t10.normalize(),
+ pywikibot.WbTime(site=repo, year=3000,
+ precision=millenia))
+ self.assertEqual(t10.normalize(),
+ pywikibot.WbTime(site=repo, year=2010,
+ precision=millenia).normalize())
+
+ def test_WbTime_normalization_very_low_precision(self):
+ """Test WbTime normalization with very low precision."""
+ repo = self.get_repo()
+ # flake8 is being annoying, so to reduce line length, I'll make
+ # some aliases here
+ year_10000 = pywikibot.WbTime.PRECISION['10000']
+ year_100000 = pywikibot.WbTime.PRECISION['100000']
+ year_1000000 = pywikibot.WbTime.PRECISION['1000000']
+ year_10000000 = pywikibot.WbTime.PRECISION['10000000']
+ year_100000000 = pywikibot.WbTime.PRECISION['100000000']
+ year_1000000000 = pywikibot.WbTime.PRECISION['1000000000']
+ t = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_10000)
+ t2 = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_100000)
+ t3 = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_1000000)
+ t4 = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_10000000)
+ t5 = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_100000000)
+ t6 = pywikibot.WbTime(site=repo, year=-3124684989,
+ precision=year_1000000000)
+ self.assertEqual(t.normalize(),
+ pywikibot.WbTime(site=repo, year=-3124680000,
+ precision=year_10000))
+ self.assertEqual(t2.normalize(),
+ pywikibot.WbTime(site=repo, year=-3124700000,
+ precision=year_100000))
+ self.assertEqual(t3.normalize(),
+ pywikibot.WbTime(site=repo, year=-3125000000,
+ precision=year_1000000))
+ self.assertEqual(t4.normalize(),
+ pywikibot.WbTime(site=repo, year=-3120000000,
+ precision=year_10000000))
+ self.assertEqual(t5.normalize(),
+ pywikibot.WbTime(site=repo, year=-3100000000,
+ precision=year_100000000))
+ self.assertEqual(t6.normalize(),
+ pywikibot.WbTime(site=repo, year=-3000000000,
+ precision=year_1000000000))
+
def test_WbTime_timestamp(self):
"""Test timestamp functions of WbTime."""
repo = self.get_repo()

To view, visit change 868173. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Iabb820ad20e6b76f9aec8cacd6134f6e135e4a81
Gerrit-Change-Number: 868173
Gerrit-PatchSet: 5
Gerrit-Owner: RPI2026F1 <sarkaraoyan+rpi2026f1@gmail.com>
Gerrit-Reviewer: JJMC89 <JJMC89.Wikimedia@gmail.com>
Gerrit-Reviewer: Matěj Suchánek <matejsuchanek97@gmail.com>
Gerrit-Reviewer: RPI2026F1 <sarkaraoyan+rpi2026f1@gmail.com>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged