src.util.datelabel module

Classes and utility methods for dealing with dates as expressed in filenames and paths. Intended use case is, eg, determining if a file contains data for a given year from the filename, without having to open it and parse the header.

Note

These classes should not be used for calendar math! We currently implement and test comparison logic only, not anything more (eg addition, subtraction).

Note

These classes are based on the datetime standard library, and as such assume a proleptic Gregorian calendar for all dates.

Note

Timezone support is not currently implemented.

class src.util.datelabel.AtomicInterval(left, lower, upper, right)[source]

Bases: object

This class represents an atomic interval. An atomic interval is a single interval, with a lower and upper bounds, and two (closed or open) boundaries.

CLOSED = True
OPEN = False
_left
_lower
_upper
_right
property left

Boolean indicating whether the left boundary is inclusive (True) or exclusive (False).

property lower

Lower bound value.

property upper

Upper bound value.

property right

Boolean indicating whether the right boundary is inclusive (True) or exclusive (False).

is_empty()[source]

Test interval emptiness. :return: True if interval is empty, False otherwise.

replace(left=None, lower=None, upper=None, right=None, ignore_inf=True)[source]

Create a new interval based on the current one and the provided values. Callable can be passed instead of values. In that case, it is called with the current corresponding value except if ignore_inf if set (default) and the corresponding bound is an infinity.

Parameters
  • left – (a function of) left boundary.

  • lower – (a function of) value of the lower bound.

  • upper – (a function of) value of the upper bound.

  • right – (a function of) right boundary.

  • ignore_inf – ignore infinities if functions are provided (default is True).

Returns: an Interval instance

overlaps(other, adjacent=False)[source]

Test if intervals have any overlapping value. If ‘adjacent’ is set to True (default is False), then it returns True for adjacent intervals as well (e.g., [1, 2) and [2, 3], but not [1, 2) and (2, 3]). :param other: an atomic interval. :param adjacent: set to True to accept adjacent intervals as well. :return: True if intervals overlap, False otherwise.

intersection(other)[source]

Return the intersection of two intervals. :param other: an interval. :return: the intersection of the intervals.

union(other)[source]

Return the union of two intervals. If the union cannot be represented using a single atomic interval, return an Interval instance (which corresponds to an union of atomic intervals). :param other: an interval. :return: the union of the intervals.

contains(item)[source]

Test if given item is contained in this interval. This method accepts atomic intervals, intervals and arbitrary values. :param item: an atomic interval, an interval or any arbitrary value. :return: True if given item is contained, False otherwise.

adjoins_left(other)[source]
adjoins_right(other)[source]
adjoins(other)[source]
classmethod span(*args)[source]
classmethod contiguous_span(*args)[source]
class src.util.datelabel.DatePrecision(value)[source]

Bases: enum.IntEnum

IntEnum to encode the recognized levels of precision for date intervals. For example, Date(‘200012’) has DatePrecision.MONTH since the length of the corresponding interval is a month.

STATIC = -1
YEAR = 1
MONTH = 2
DAY = 3
HOUR = 4
MINUTE = 5
SECOND = 6
class src.util.datelabel._DateMixin[source]

Bases: object

Utility methods for dealing with dates.

static date_format(dt, precision=None)[source]

Print date in YYYYMMDDHHMMSS format, with length being set automatically from precision.

Note

strftime() is broken for dates prior to 1900 in python < 3.3, see https://bugs.python.org/issue1777412 and https://stackoverflow.com/q/10263956. For this reason, the workaround implemented here should be used instead.

classmethod increment(dt, precision)[source]

Return a copy of dt advanced by one time unit as specified by the precision attribute.

classmethod decrement(dt, precision)[source]

Return a copy of Date moved back by one time unit as specified by the precision attribute.

static _inc_dec_common(dt, precision, delta)[source]
class src.util.datelabel.DateRange(start, end=None, precision=None, log=<Logger src.util.datelabel (WARNING)>)[source]

Bases: src.util.datelabel.AtomicInterval, src.util.datelabel._DateMixin

Class representing a range of variable-precision dates.

Note

This is defined as a closed interval (containing both endpoints). Eg, DateRange(‘1990-1999’) starts at 0:00 on 1 Jan 1990 and ends at 23:59 on 31 Dec 1999.

_range_sep = '-'
property is_static

Property indicating time-independent data (eg, ‘fx’ in CMIP6 DRS.)

static _precision_check(*args)[source]
static _coerce_to_datetime(dt, is_lower)[source]
classmethod _coerce_to_self(item, precision=None)[source]
property start_datetime
property start
property end_datetime
property end
classmethod from_contiguous_span(*args)[source]

Given multiple DateRanges, return interval containing them ONLY IF ranges are continguous and nonoverlapping.

classmethod from_date_span(*args)[source]

Return a DateRange coresponding to the interval containing a set of Dates. Differs from from_contiguous_span() in that we don’t expect intervals to be contiguous.

format(precision=None)[source]
contains(item)

Override AtomicInterval.__contains__() to handle differences in datelabel precision. Finite precision means that the interval endpoints are ranges, not points (which is why Date inherits from DateRange and not vice-versa). We replace strict equality of endpoints (==) with appropriate conditions on the overlap of these ranges.

overlaps(item)[source]

Test if intervals have any overlapping value. If ‘adjacent’ is set to True (default is False), then it returns True for adjacent intervals as well (e.g., [1, 2) and [2, 3], but not [1, 2) and (2, 3]). :param other: an atomic interval. :param adjacent: set to True to accept adjacent intervals as well. :return: True if intervals overlap, False otherwise.

intersection(item, precision=None)[source]

Return the intersection of two intervals. :param other: an interval. :return: the intersection of the intervals.

_date_range_compare_common(other, func_name)[source]
_left
_lower
_upper
_right
class src.util.datelabel.Date(*args, **kwargs)[source]

Bases: src.util.datelabel.DateRange

Define a date with variable level precision.

Note

Date objects are mapped to datetimes representing the start of the interval implied by their precision, eg. DateTime(‘2000-05’) maps to 0:00 on 1 May 2000.

_datetime_attrs = ('year', 'month', 'day', 'hour', 'minute', 'second')
classmethod _parse_datetime(dt)[source]
classmethod _parse_input_string(s)[source]

Parse date strings in YYYY-MM-DD or YYYYMMDDHH formats.

format(precision=None)[source]
isoformat()[source]
_tuple_compare(other, func)[source]
_left
_lower
_upper
_right
class src.util.datelabel.StaticTimeDependenceBase[source]

Bases: object

Dummy class to label sentinel objects for use in describing static data with no time dependence.

property is_static

Property indicating time-independent data (eg, ‘fx’ in CMIP6 DRS.)

classmethod _coerce_to_self(item)[source]
format(precision=None)[source]
isoformat(precision=None)
static date_format(dt, precision=None)[source]
class src.util.datelabel._FXDateMin[source]

Bases: src.util.datelabel.StaticTimeDependenceBase, src.util.datelabel.Date

property start
property end
_left
_lower
_upper
_right
class src.util.datelabel._FXDateMax[source]

Bases: src.util.datelabel.StaticTimeDependenceBase, src.util.datelabel.Date

property start
property end
_left
_lower
_upper
_right
class src.util.datelabel._FXDateRange[source]

Bases: src.util.datelabel.StaticTimeDependenceBase, src.util.datelabel.DateRange

Singleton placeholder/sentinel object for use in describing static data with no time dependence.

property start
property end
_left
_lower
_upper
_right
class src.util.datelabel.DateFrequency(quantity, unit=None)[source]

Bases: datetime.timedelta

Class representing a date frequency or period.

Warning

Period lengths are not defined accurately, eg. a year is taken as 365 days and a month is taken as 30 days.

property is_static

Property indicating time-independent data (eg, ‘fx’ in CMIP6 DRS.)

classmethod from_struct(str_)[source]

Workaround for object creation, using the method mdtf_dataclass is looking for.

classmethod _parse_input_string(quantity, unit)[source]
classmethod _get_timedelta_kwargs(q, s)[source]
format()[source]
format_local()[source]

Format frequency as used in framework’s local directory hierarchy (defined in src.data_manager.DataManager.dest_path().)

class src.util.datelabel._FXDateFrequency[source]

Bases: src.util.datelabel.DateFrequency, src.util.datelabel.StaticTimeDependenceBase

Singleton placeholder/sentinel object for use in describing static data with no time dependence.

property is_static

Property indicating time-independent data (eg, ‘fx’ in CMIP6 DRS.)

class src.util.datelabel.AbstractDateRange[source]

Bases: abc.ABC

Defines interface (set of attributes) for DMDimension objects.

_abc_impl = <_abc_data object>
class src.util.datelabel.AbstractDate[source]

Bases: abc.ABC

Defines interface (set of attributes) for DMDimension objects.

_abc_impl = <_abc_data object>
class src.util.datelabel.AbstractDateFrequency[source]

Bases: abc.ABC

Defines interface (set of attributes) for DMDimension objects.

_abc_impl = <_abc_data object>