Source code for tableauhyperapi.interval

import functools

from typing import Union

from .impl.util import c_modulus, c_divide


# TODO TFSID 922857: check limits
[docs] @functools.total_ordering class Interval: """ A Hyper INTERVAL. It is a union of logically independent months, days, and microseconds components, which expresses time intervals like "1 year" (which may mean 365 or 366 days depending on to which date it is added), "2 months" (which has variable number of days depending on the month), "3 days" (which may mean different number of hours if daylight savings changed), "4 hours", "5 seconds". :param months: the number of months. :param days: the number of days. :param microseconds: the number of microseconds. Examples: .. testsetup:: Interval.__init__ from tableauhyperapi import * .. doctest:: Interval.__init__ >>> print(Interval(1, 0, 0)) # 1 month P1M >>> print(Interval(0, 1, 0)) # 1 day P1D >>> print(Interval(0, 0, 1_000_000)) # 1 second PT1.0S :any:`Interval` values can be added and subtracted, which performs component-wise addition or subtraction. Comparison for :any:`Interval` values is lexicographic on their components, for instance 1 day does not equal 24 hours. Similarly 1 year compares greater than 400 days. """ __slots__ = ('months', 'days', 'microseconds') _MICROSECONDS_PER_HOUR = 60 * 60 * 1_000_000 _MICROSECONDS_PER_MINUTE = 60 * 1_000_000 _MICROSECONDS_PER_SECOND = 1_000_000 def __init__(self, months: int, days: int, microseconds: int): self.months = months """ The months component. """ self.days = days """ The days component. """ self.microseconds = microseconds """ The microseconds component. """ @staticmethod def _zero() -> 'Interval': """ Zero-length interval. """ return Interval(0, 0, 0) @staticmethod def _from_years_and_months(years: int, months: int) -> 'Interval': """ Returns an interval for the given number of years and months (which equals 12*years + months). :param years: the number of years. :param months: the number of months. :return: a new :any:`Interval` object. """ return Interval(years * 12 + months, 0, 0) @staticmethod def _from_days(days: int) -> 'Interval': """ Returns an interval for the given number of days. :param days: the number of days. :return: a new :any:`Interval` object. """ return Interval(0, days, 0) @staticmethod def _from_seconds(seconds: Union[int, float]) -> 'Interval': """ Returns an interval for the given number of seconds. :param seconds: the number of seconds, may be a floating-point number with microsecond precision. :return: a new :any:`Interval` object. """ mus = seconds * Interval._MICROSECONDS_PER_SECOND return Interval(0, 0, int(mus)) @staticmethod def _from_time_parts(hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0) -> 'Interval': """ Returns an interval for the given number of hours, minutes, seconds, and microseconds. :param hours: the number of hours. :param minutes: the number of minutes. :param seconds: the number of seconds. :param microseconds: the number of microseconds. :return: a new :any:`Interval` object. """ mus = hours * Interval._MICROSECONDS_PER_HOUR + minutes * Interval._MICROSECONDS_PER_MINUTE + \ seconds * Interval._MICROSECONDS_PER_SECOND + microseconds return Interval(0, 0, mus) @property def _year_part(self) -> int: """ The year part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_divide(self.months, 12) @property def _month_part(self) -> int: """ The month part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_modulus(self.months, 12) @property def _day_part(self) -> int: """ The day part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return self.days @property def _hour_part(self) -> int: """ The hour part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_divide(self.microseconds, Interval._MICROSECONDS_PER_HOUR) @property def _minute_part(self) -> int: """ The minute part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_modulus(c_divide(self.microseconds, Interval._MICROSECONDS_PER_MINUTE), 60) @property def _second_part(self) -> int: """ The second part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_modulus(c_divide(self.microseconds, Interval._MICROSECONDS_PER_SECOND), 60) @property def _microsecond_part(self) -> int: """ The microsecond part of the (years, months, days, hours, minutes, seconds, microseconds) representation. """ return c_modulus(self.microseconds, Interval._MICROSECONDS_PER_SECOND) def __repr__(self): return 'Interval({}, {}, {})'.format(self.months, self.days, self.microseconds) def __str__(self): """ ISO 8601 timestamp representation (https://en.wikipedia.org/wiki/ISO_8601#Durations) """ result = 'P' if self.months: year_part = self._year_part if year_part: result += '{0}Y'.format(year_part) month_part = self._month_part if month_part: result += '{0}M'.format(month_part) if self.days: result += '{0}D'.format(self.days) if self.microseconds: result += 'T' hour_part = self._hour_part minute_part = self._minute_part second_part = self._second_part microsecond_part = self._microsecond_part if hour_part: result += '{0}H'.format(hour_part) if minute_part: result += '{0}M'.format(minute_part) if second_part or microsecond_part: result += '{0}S'.format(second_part + microsecond_part / Interval._MICROSECONDS_PER_SECOND) elif len(result) == 1: result += 'T0S' return result def __as_tuple(self): return self.months, self.days, self.microseconds def __eq__(self, other): if not isinstance(other, Interval): return NotImplemented return self.__as_tuple() == other.__as_tuple() def __lt__(self, other): if not isinstance(other, Interval): return NotImplemented return self.__as_tuple() < other.__as_tuple() def __hash__(self): return hash(self.__as_tuple()) def __bool__(self): return bool(self.months or self.days or self.microseconds) def __add__(self, other): if not isinstance(other, Interval): return NotImplemented return Interval(self.months + other.months, self.days + other.days, self.microseconds + other.microseconds) def __sub__(self, other): if not isinstance(other, Interval): return NotImplemented return Interval(self.months - other.months, self.days - other.days, self.microseconds - other.microseconds) def __neg__(self): return Interval(-self.months, -self.days, -self.microseconds) def __pos__(self): return Interval(self.months, self.days, self.microseconds) def __abs__(self): return Interval(abs(self.months), abs(self.days), abs(self.microseconds)) def __mul__(self, other): if not isinstance(other, int): return NotImplemented return Interval(self.months * other, self.days * other, self.microseconds * other)