jenkins-bot submitted this change.

View Change


Approvals: Matěj Suchánek: Looks good to me, but someone else must approve RPI2026F1: Looks good to me, but someone else must approve Xqt: Looks good to me, approved jenkins-bot: Verified
[Bugfix] Implement comparison for WbTime object

- I used changeset 316220 as a basis for the tests
- For the sake of simplicity, all of the other changes
in the changeset were omitted
- This implementation does not change any current attributes
- This implementation factors in the correct number of days
in a month
- This implementation factors in leap years and the calendar model
when calculating leap years
- Note the difference in behavior of __ge__/__le__ and __eq__/__ne__

Bug: T148280
Change-Id: I694886fd4b264e97c351a8b2a5aefd2b5878be69
---
M pywikibot/__init__.py
M tests/wikibase_tests.py
2 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index db7bdfe..9202523 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -319,6 +319,21 @@
_items = ('year', 'month', 'day', 'hour', 'minute', 'second',
'precision', 'before', 'after', 'timezone', 'calendarmodel')

+ _month_offset = {
+ 1: 0,
+ 2: 31, # Jan -> Feb: 31 days
+ 3: 59, # Feb -> Mar: 28 days, plus 31 days in Jan -> Feb
+ 4: 90, # Mar -> Apr: 31 days, plus 59 days in Jan -> Mar
+ 5: 120, # Apr -> May: 30 days, plus 90 days in Jan -> Apr
+ 6: 151, # May -> Jun: 31 days, plus 120 days in Jan -> May
+ 7: 181, # Jun -> Jul: 30 days, plus 151 days in Jan -> Jun
+ 8: 212, # Jul -> Aug: 31 days, plus 181 days in Jan -> Jul
+ 9: 243, # Aug -> Sep: 31 days, plus 212 days in Jan -> Aug
+ 10: 273, # Sep -> Oct: 30 days, plus 243 days in Jan -> Sep
+ 11: 304, # Oct -> Nov: 31 days, plus 273 days in Jan -> Oct
+ 12: 334, # Nov -> Dec: 30 days, plus 304 days in Jan -> Nov
+ }
+
def __init__(self,
year: Optional[int] = None,
month: Optional[int] = None,
@@ -349,6 +364,12 @@
of the event, in the range −180° to 180°, multiplied by 4 to convert
to minutes.

+ Comparison information: When using the greater than or equal to
+ operator, or the less than or equal to operator, two different time
+ objects with the same UTC time after factoring in timezones are
+ considered equal. However, the equality operator will return false
+ if the timezone is different.
+
:param year: The year as a signed integer of between 1 and 16 digits.
:param month: Month of the timestamp, if it exists.
:param day: Day of the timestamp, if it exists.
@@ -406,7 +427,6 @@
.format(Site()))
calendarmodel = site.calendarmodel()
self.calendarmodel = calendarmodel
-
# if precision is given it overwrites the autodetection above
if precision is not None:
if (isinstance(precision, int)
@@ -418,6 +438,61 @@
else:
raise ValueError(f'Invalid precision: "{precision}"')

+ def _getSecondsAdjusted(self) -> int:
+ """Return an internal representation of the time object as seconds.
+
+ The value adjusts itself for timezones. It is not compatible with
+ before/after.
+
+ This value should *only* be used for comparisons, and
+ its value may change without warning.
+
+ :return: An integer roughly representing the number of seconds
+ since January 1, 0000 AD, adjusted for leap years.
+ """
+ # This function ignores leap seconds. Since it is not required
+ # to correlate to an actual UNIX timestamp, this is acceptable.
+
+ # We are always required to have a year.
+ elapsed_seconds = int(self.year * 365.25 * 24 * 60 * 60)
+ if self.month > 1:
+ elapsed_seconds += self._month_offset[self.month] * 24 * 60 * 60
+ # The greogrian calendar
+ if self.calendarmodel == 'http://www.wikidata.org/entity/Q1985727':
+ if (self.year % 400 == 0
+ or (self.year % 4 == 0 and self.year % 100 != 0)
+ and self.month > 2):
+ elapsed_seconds += 24 * 60 * 60 # Leap year
+ # The julian calendar
+ if self.calendarmodel == 'http://www.wikidata.org/entity/Q1985786':
+ if self.year % 4 == 0 and self.month > 2:
+ elapsed_seconds += 24 * 60 * 60
+ if self.day > 1:
+ # Days start at 1, not 0.
+ elapsed_seconds += (self.day - 1) * 24 * 60 * 60
+ elapsed_seconds += self.hour * 60 * 60
+ elapsed_seconds += self.minute * 60
+ elapsed_seconds += self.second
+ if self.timezone is not None:
+ elapsed_seconds += self.timezone * 60
+ return elapsed_seconds
+
+ def __lt__(self, other):
+ """Compare if self is less than other."""
+ return self._getSecondsAdjusted() < other._getSecondsAdjusted()
+
+ def __le__(self, other):
+ """Compare if self is less equals other."""
+ return self._getSecondsAdjusted() <= other._getSecondsAdjusted()
+
+ def __gt__(self, other):
+ """Compare if self is greater than other."""
+ return self._getSecondsAdjusted() > other._getSecondsAdjusted()
+
+ def __ge__(self, other):
+ """Compare if self is greater equals other."""
+ return self._getSecondsAdjusted() >= other._getSecondsAdjusted()
+
@classmethod
def fromTimestr(cls: Type['WbTime'],
datetimestr: str,
diff --git a/tests/wikibase_tests.py b/tests/wikibase_tests.py
index c97f6f7..3867219 100755
--- a/tests/wikibase_tests.py
+++ b/tests/wikibase_tests.py
@@ -378,6 +378,37 @@
with self.assertRaisesRegex(ValueError, regex):
pywikibot.WbTime(site=repo, precision='invalid_precision')

+ def test_comparison(self):
+ """Test WbTime comparison."""
+ repo = self.get_repo()
+ t1 = pywikibot.WbTime(site=repo, year=2010, hour=12, minute=43)
+ t2 = pywikibot.WbTime(site=repo, year=-2005, hour=16, minute=45)
+ self.assertEqual(t1.precision, pywikibot.WbTime.PRECISION['minute'])
+ self.assertEqual(t1, t1)
+ self.assertGreaterEqual(t1, t1)
+ self.assertGreaterEqual(t1, t2)
+ self.assertGreater(t1, t2)
+ self.assertEqual(t1.year, 2010)
+ self.assertEqual(t2.year, -2005)
+ self.assertEqual(t1.month, 1)
+ self.assertEqual(t2.month, 1)
+ self.assertEqual(t1.day, 1)
+ self.assertEqual(t2.day, 1)
+ self.assertEqual(t1.hour, 12)
+ self.assertEqual(t2.hour, 16)
+ self.assertEqual(t1.minute, 43)
+ self.assertEqual(t2.minute, 45)
+ self.assertEqual(t1.second, 0)
+ self.assertEqual(t2.second, 0)
+ self.assertEqual(t1.toTimestr(), '+00000002010-01-01T12:43:00Z')
+ self.assertEqual(t2.toTimestr(), '-00000002005-01-01T16:45:00Z')
+ self.assertRaises(ValueError, pywikibot.WbTime, site=repo,
+ precision=15)
+ self.assertRaises(ValueError, pywikibot.WbTime, site=repo,
+ precision='invalid_precision')
+ self.assertIsInstance(t1.toTimestamp(), pywikibot.Timestamp)
+ self.assertRaises(ValueError, t2.toTimestamp)
+

class TestWbQuantity(WbRepresentationTestCase):


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

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I694886fd4b264e97c351a8b2a5aefd2b5878be69
Gerrit-Change-Number: 863328
Gerrit-PatchSet: 17
Gerrit-Owner: RPI2026F1 <sarkaraoyan+rpi2026f1@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