Skip to content

Fix intervals #302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed
- Validate `tarantool.Interval` limits with the same rules as in Tarantool.

### Fixed
- `tarantool.Interval` arithmetic with weeks
- `tarantool.Interval` weeks display in `str()` and `repr()`

## 1.1.0 - 2023-06-30

### Added
Expand Down Expand Up @@ -198,7 +207,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support pandas way to build datetime from timestamp (PR #252).

`timestamp_since_utc_epoch` is a parameter to set timestamp
convertion behavior for timezone-aware datetimes.
conversion behavior for timezone-aware datetimes.

If ``False`` (default), behaves similar to Tarantool `datetime.new()`:

Expand All @@ -218,7 +227,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Thus, if ``False``, datetime is computed from timestamp
since epoch and then timezone is applied without any
convertion. In that case, `dt.timestamp` won't be equal to
conversion. In that case, `dt.timestamp` won't be equal to
initialization `timestamp` for all timezones with non-zero offset.

If ``True``, behaves similar to `pandas.Timestamp`:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ install:


PYTHON_FILES=tarantool test setup.py docs/source/conf.py
TEXT_FILES=README.rst docs/source/*.rst
TEXT_FILES=README.rst CHANGELOG.md docs/source/*.rst
.PHONY: lint
lint:
python3 -m pylint --recursive=y $(PYTHON_FILES)
Expand Down
68 changes: 64 additions & 4 deletions tarantool/msgpack_ext/types/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,60 @@
8: 'adjust',
}

# https://github.com/tarantool/tarantool/blob/ff57f990f359f6d7866c1947174d8ba0e97b1ea6/src/lua/datetime.lua#L112-L146
SECS_PER_DAY = 86400

MIN_DATE_YEAR = -5879610
MIN_DATE_MONTH = 6
MIN_DATE_DAY = 22
MAX_DATE_YEAR = 5879611
MAX_DATE_MONTH = 7
MAX_DATE_DAY = 11

AVERAGE_DAYS_YEAR = 365.25
AVERAGE_WEEK_YEAR = AVERAGE_DAYS_YEAR / 7
INT_MAX = 2147483647
MAX_YEAR_RANGE = MAX_DATE_YEAR - MIN_DATE_YEAR
MAX_MONTH_RANGE = MAX_YEAR_RANGE * 12
MAX_WEEK_RANGE = MAX_YEAR_RANGE * AVERAGE_WEEK_YEAR
MAX_DAY_RANGE = MAX_YEAR_RANGE * AVERAGE_DAYS_YEAR
MAX_HOUR_RANGE = MAX_DAY_RANGE * 24
MAX_MIN_RANGE = MAX_HOUR_RANGE * 60
MAX_SEC_RANGE = MAX_DAY_RANGE * SECS_PER_DAY
MAX_NSEC_RANGE = INT_MAX

max_val = {
'year': MAX_YEAR_RANGE,
'month': MAX_MONTH_RANGE,
'week': MAX_WEEK_RANGE,
'day': MAX_DAY_RANGE,
'hour': MAX_HOUR_RANGE,
'minute': MAX_MIN_RANGE,
'sec': MAX_SEC_RANGE,
'nsec': MAX_NSEC_RANGE,
}


def verify_range(intv):
"""
Check allowed values. Approach is the same as in tarantool/tarantool.

:param intv: Raw interval to verify.
:type intv: :class:`~tarantool.Interval`

:raise: :exc:`ValueError`

:meta private:
"""

for field_name, range_max in max_val.items():
val = getattr(intv, field_name)
# Tarantool implementation has a bug
# https://github.com/tarantool/tarantool/issues/8878
if (val > range_max) or (val < -range_max):
raise ValueError(f"value {val} of {field_name} is out of "
f"allowed range [{-range_max}, {range_max}]")


# https://github.com/tarantool/c-dt/blob/cec6acebb54d9e73ea0b99c63898732abd7683a6/dt_arithmetic.h#L34
class Adjust(Enum):
Expand Down Expand Up @@ -49,8 +103,8 @@ class Interval():

.. code-block:: python

di = tarantool.Interval(year=-1, month=2, day=3,
hour=4, minute=-5, sec=6,
di = tarantool.Interval(year=-1, month=2, week=-3,
day=4, hour=5, minute=-6, sec=7,
nsec=308543321,
adjust=tarantool.IntervalAdjust.NONE)

Expand Down Expand Up @@ -92,6 +146,8 @@ def __init__(self, *, year=0, month=0, week=0,
:param adjust: Interval adjustment rule. Refer to
:meth:`~tarantool.Datetime.__add__`.
:type adjust: :class:`~tarantool.IntervalAdjust`, optional

:raise: :exc:`ValueError`
"""

self.year = year
Expand All @@ -104,6 +160,8 @@ def __init__(self, *, year=0, month=0, week=0,
self.nsec = nsec
self.adjust = adjust

verify_range(self)

def __add__(self, other):
"""
Valid operations:
Expand Down Expand Up @@ -145,6 +203,7 @@ def __add__(self, other):
return Interval(
year=self.year + other.year,
month=self.month + other.month,
week=self.week + other.week,
day=self.day + other.day,
hour=self.hour + other.hour,
minute=self.minute + other.minute,
Expand Down Expand Up @@ -194,6 +253,7 @@ def __sub__(self, other):
return Interval(
year=self.year - other.year,
month=self.month - other.month,
week=self.week - other.week,
day=self.day - other.day,
hour=self.hour - other.hour,
minute=self.minute - other.minute,
Expand Down Expand Up @@ -231,8 +291,8 @@ def __eq__(self, other):
return True

def __repr__(self):
return f'tarantool.Interval(year={self.year}, month={self.month}, day={self.day}, ' + \
f'hour={self.hour}, minute={self.minute}, sec={self.sec}, ' + \
return f'tarantool.Interval(year={self.year}, month={self.month}, week={self.week}, ' + \
f'day={self.day}, hour={self.hour}, minute={self.minute}, sec={self.sec}, ' + \
f'nsec={self.nsec}, adjust={self.adjust})'

__str__ = __repr__
6 changes: 6 additions & 0 deletions test/suites/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,12 @@ def test_tarantool_datetime_subtraction_different_timezones(self):
'res_add': tarantool.Datetime(year=2009, month=3, day=31),
'res_sub': tarantool.Datetime(year=2009, month=1, day=31),
},
'week': {
'arg_1': tarantool.Datetime(year=2008, month=2, day=3),
'arg_2': tarantool.Interval(week=1),
'res_add': tarantool.Datetime(year=2008, month=2, day=10),
'res_sub': tarantool.Datetime(year=2008, month=1, day=27),
},
}

def test_python_interval_addition(self):
Expand Down
Loading