timetools

This module specifies the handling of dates and periods in HydPy projects.

Module timetools implements the following members:

  • TOY0 Time of year handler.

  • TypeDate Type variable.

  • TypePeriod Type variable.

  • TypeTOY Type variable.

  • TypeTimegrid Type variable.

  • Date Handles a single date.

  • Period Handles a single period.

  • Timegrid Defines an arbitrary number of equidistant dates via the first date, the last date, and the step size between subsequent dates.

  • Timegrids Handles the “initialisation”, the “simulation”, and the “evaluation Timegrid object of a HydPy project.

  • TOY Time of year handler.


class hydpy.core.timetools.Date(date: Date | datetime | str)[source]

Bases: object

Handles a single date.

We built class the Date on top of the Python module datetime. It wraps datetime objects and specialise this general class on the needs of HydPy users.

Date objects can be initialised via datetime objects directly:

>>> import datetime
>>> date = datetime.datetime(1996, 11, 1, 0, 0, 0)
>>> from hydpy import Date
>>> Date(date)
Date("1996-11-01 00:00:00")

Date objects do not store time zone information. The Date object prepared above refers to zero o’clock in the time zone defined by utcoffset (UTC+01:00 by default). When the initialisation argument provides other time zone information, its date information is adjusted, which we show in the following examples, where the prepared datetime objects refer to UTC 00:00 and UTC-01:00:

>>> date = datetime.datetime(1996, 11, 1, 0, 0, 0,
...     tzinfo=datetime.timezone(datetime.timedelta(0)))
>>> Date(date)
Date("1996-11-01 01:00:00")
>>> date = datetime.datetime(1996, 11, 1, 0, 0, 0,
...     tzinfo=datetime.timezone(datetime.timedelta(hours=-1)))
>>> Date(date)
Date("1996-11-01 02:00:00")

One can change utcoffset, but this does not affect already existing Date objects:

>>> from hydpy import pub
>>> pub.options.utcoffset = 0
>>> temp = Date(date)
>>> temp
Date("1996-11-01 01:00:00")
>>> pub.options.utcoffset = 60
>>> temp
Date("1996-11-01 01:00:00")

Class Date accepts str objects as alternative constructor arguments. These are often more rapidly defined and allow to set the style property by the way (see the documentation on method from_string() for more examples):

>>> Date("1996-11-01")
Date("1996-11-01 00:00:00")
>>> Date("1996.11.01")
Date("1996.11.01 00:00:00")

Invalid arguments types result in the following error:

>>> Date(1)
Traceback (most recent call last):
...
TypeError: While trying to initialise a `Date` object based on argument `1`, the following error occurred: The supplied argument must be either an instance of `Date`, `datetime.datetime`, or `str`.  The given arguments type is `int`.

In contrast to class datetime, class Date is mutable:

>>> date = Date("1996-11-01")
>>> date.hour = 12
>>> date
Date("1996-11-01 12:00:00")

Unplausible values assigned to property hour and its related properties result in error messages like the following:

>>> date.hour = 24
Traceback (most recent call last):
...
ValueError: While trying to change the hour of the current Date object, the following error occurred: hour must be in 0..23

You can do some math with Date objects. First, you can add Period objects to shift the date:

>>> date = Date("2000.01.01")
>>> date + "1d"
Date("2000.01.02 00:00:00")
>>> date += "12h"
>>> date
Date("2000.01.01 12:00:00")

Second, you can subtract both Period and other Date objects to shift the date or determine the time delta, respectively:

>>> date - "1s"
Date("2000.01.01 11:59:59")
>>> date -= "12h"
>>> date
Date("2000.01.01 00:00:00")
>>> date - "2000-01-05"
Period("-4d")
>>> "2000.01.01 00:00:30" - date
Period("30s")

To try to subtract objects neither interpretable as a Date nor Period object results in the following error:

>>> date - "1"
Traceback (most recent call last):
...
TypeError: Object `1` of type `str` cannot be substracted from a `Date` instance.

The comparison operators work as expected:

>>> d1, d2 = Date("2000-1-1"), Date("2001-1-1")
>>> d1 < d2, d1 < "2000-1-1", "2001-1-2" < d1
(True, False, False)
>>> d1 <= d2, d1 <= "2000-1-1", "2001-1-2" <= d1
(True, True, False)
>>> d1 == d2, d1 == "2000-1-1", "2001-1-2" == d1, d1 == "1d"
(False, True, False, False)
>>> d1 != d2, d1 != "2000-1-1", "2001-1-2" != d1, d1 != "1d"
(True, False, True, True)
>>> d1 >= d2, d1 >= "2000-1-1", "2001-1-2" >= d1
(False, True, True)
>>> d1 > d2, d1 > "2000-1-1", "2001-1-2" > d1
(False, False, True)
formatstrings = {'din1': '%d.%m.%Y %H:%M:%S', 'din2': '%Y.%m.%d %H:%M:%S', 'iso1': '%Y-%m-%dT%H:%M:%S', 'iso2': '%Y-%m-%d %H:%M:%S', 'os': '%Y_%m_%d_%H_%M_%S', 'raw': '%Y%m%d%H%M%S'}
datetime: datetime
classmethod from_date(date: Date) TypeDate[source]

Create a new Date object based on another Date object and return it.

Initialisation from other Date objects preserves their style information:

>>> from hydpy import Date
>>> date1 = Date("2000.01.01")
>>> date2 = Date(date1)
>>> date1.style = "iso2"
>>> date3 = Date.from_date(date1)
>>> date2
Date("2000.01.01 00:00:00")
>>> date3
Date("2000-01-01 00:00:00")
classmethod from_datetime(date: datetime) TypeDate[source]

Create a new Date object based on a datetime object and return it.

Initialisation from datetime does not modify the default style information:

>>> from hydpy import Date
>>> from datetime import datetime, timedelta, timezone
>>> Date.from_datetime(datetime(2000, 1, 1))
Date("2000-01-01 00:00:00")

Be aware of the different minimum time resolution of class datetime (microseconds) and class Date (seconds):

>>> Date.from_datetime(datetime(2000, 1, 1, microsecond=2))
Traceback (most recent call last):
...
ValueError: For `Date` instances, the microsecond must be zero, but for the given `datetime` object it is `2` instead.

Due to a different kind of handling time zone information, the time zone awareness of datetime objects is removed (see the main documentation on class Date for further information:

>>> date = Date.from_datetime(
...     datetime(2000, 11, 1, tzinfo=timezone(timedelta(0))))
>>> date
Date("2000-11-01 01:00:00")
>>> date.datetime
datetime.datetime(2000, 11, 1, 1, 0)
classmethod from_string(date: str) TypeDate[source]

Create a new Date object based on a datetime object and return it.

The given string needs to match one of the following style patterns.

The os style is applied in text files and folder names and does not include any empty spaces or colons:

>>> Date.from_string("1997_11_01_00_00_00").style
'os'

The iso styles are more legible and come in two flavours. iso1 follows ISO 8601, and iso2 (which is the default style) omits the T between the date and the time:

>>> Date.from_string("1997-11-01T00:00:00").style
'iso1'
>>> Date.from_string("1997-11-01 00:00:00").style
'iso2'

The din styles rely on points instead of hyphens. The difference between the available flavours lies in the order of the date literals (DIN refers to a German norm):

>>> Date("01.11.1997 00:00:00").style
'din1'
>>> Date("1997.11.01 00:00:00").style
'din2'

The raw style avoids any unnecessary characters:

>>> Date("19971101000000").style
'raw'

You are allowed to abbreviate the input strings:

>>> for string in ("1996-11-01 00:00:00",
...                "1996-11-01 00:00",
...                "1996-11-01 00",
...                "1996-11-01"):
...     print(Date.from_string(string))
1996-11-01 00:00:00
1996-11-01 00:00:00
1996-11-01 00:00:00
1996-11-01 00:00:00

You can combine all styles with ISO time zone identifiers:

>>> Date.from_string("1997-11-01T00:00:00Z")
Date("1997-11-01T01:00:00")
>>> Date.from_string("1997-11-01 00:00:00-11:00")
Date("1997-11-01 12:00:00")
>>> Date.from_string("1997-11-01 +13")
Date("1997-10-31 12:00:00")
>>> Date.from_string("1997-11-01 +1330")
Date("1997-10-31 11:30:00")
>>> Date.from_string("01.11.1997 00-500")
Date("01.11.1997 06:00:00")

Poorly formatted date strings result in the following or comparable error messages:

>>> Date.from_string("1997/11/01")
Traceback (most recent call last):
...
ValueError: The given string `1997/11/01` does not agree with any of the supported format styles.
>>> Date.from_string("1997111")
Traceback (most recent call last):
...
ValueError: The given string `1997111` does not agree with any of the supported format styles.
>>> Date.from_string("1997-11-01 +0000001")
Traceback (most recent call last):
...
ValueError: While trying to apply the time zone offset defined by string `1997-11-01 +0000001`, the following error occurred: wrong number of offset characters
>>> Date.from_string("1997-11-01 +0X:00")
Traceback (most recent call last):
...
ValueError: While trying to apply the time zone offset defined by string `1997-11-01 +0X:00`, the following error occurred: invalid literal for int() with base 10: '0X'
classmethod from_array(array: ndarray[Any, dtype[float64]]) TypeDate[source]

Return a Date instance based on date information (year, month, day, hour, minute, second) stored as the first entries of the successive rows of a ndarray.

>>> from hydpy import Date
>>> import numpy
>>> array1d = numpy.array([1992, 10, 8, 15, 15, 42, 999])
>>> Date.from_array(array1d)
Date("1992-10-08 15:15:42")
>>> array3d = numpy.zeros((7, 2, 2))
>>> array3d[:, 0, 0] = array1d
>>> Date.from_array(array3d)
Date("1992-10-08 15:15:42")

Note

The date defined by the given ndarray cannot include any time zone information and corresponds to utcoffset, which defaults to UTC+01:00.

to_array() ndarray[source]

Return a 1-dimensional numpy ndarray with six entries defining the actual date (year, month, day, hour, minute, second).

>>> from hydpy import Date, print_vector
>>> print_vector(Date("1992-10-8 15:15:42").to_array())
1992.0, 10.0, 8.0, 15.0, 15.0, 42.0

Note

The date defined by the returned ndarray does not include any time zone information and corresponds to utcoffset, which defaults to UTC+01:00.

classmethod from_cfunits(units: str) TypeDate[source]

Return a Date object representing the reference date of the given units string agreeing with the NetCDF-CF conventions.

We took the following example string from the Time Coordinate chapter of the NetCDF-CF conventions documentation (modified). Note that method from_cfunits() ignores the first entry (the unit) and assumes UTC+00 for strings without time zone identifiers (as opposed to the usual HydPy convention that dates without time zone identifiers correspond to the local time defined by the option utcoffset):

>>> from hydpy import Date
>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42 -6:00")
Date("1992-10-08 22:15:42")
>>> Date.from_cfunits(" day since 1992-10-8 15:15:00")
Date("1992-10-08 16:15:00")
>>> Date.from_cfunits("seconds since 1992-10-8 -6:00")
Date("1992-10-08 07:00:00")
>>> Date.from_cfunits("m since 1992-10-8")
Date("1992-10-08 01:00:00")

One can also pass the unmodified example string from Time Coordinate as long as one omits any decimal fractions of a second different from zero:

>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42.")
Date("1992-10-08 16:15:42")
>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42.00")
Date("1992-10-08 16:15:42")
>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42. -6:00")
Date("1992-10-08 22:15:42")
>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42.0 -6:00")
Date("1992-10-08 22:15:42")
>>> Date.from_cfunits("seconds since 1992-10-8 15:15:42.005 -6:00")
Traceback (most recent call last):
...
ValueError: While trying to parse the date of the NetCDF-CF "units" string `seconds since 1992-10-8 15:15:42.005 -6:00`, the following error occurred: No other decimal fraction of a second than "0" allowed.
to_cfunits(unit: str = 'hours', utcoffset: int | None = None) str[source]

Return a units string agreeing with the NetCDF-CF conventions.

By default, method to_cfunits() uses hours as the time unit and takes the value of utcoffset as time zone information:

>>> from hydpy import Date
>>> date = Date("1992-10-08 15:15:42")
>>> date.to_cfunits()
'hours since 1992-10-08 15:15:42 +01:00'

You can define arbitrary strings to describe the time unit:

>>> date.to_cfunits(unit="minutes")
'minutes since 1992-10-08 15:15:42 +01:00'

For changing the time zone, pass the corresponding offset in minutes:

>>> date.to_cfunits(unit="sec", utcoffset=-60)
'sec since 1992-10-08 13:15:42 -01:00'
property style: str

Date format style to be applied in printing.

Initially, style corresponds to the format style of the string used as the initialisation object of a Date object:

>>> from hydpy import Date
>>> date = Date("01.11.1997 00:00:00")
>>> date.style
'din1'
>>> date
Date("01.11.1997 00:00:00")

However, you are allowed to change it:

>>> date.style = "iso1"
>>> date
Date("1997-11-01T00:00:00")

The default style is iso2:

>>> from datetime import datetime
>>> date = Date(datetime(2000, 1, 1))
>>> date
Date("2000-01-01 00:00:00")
>>> date.style
'iso2'

Trying to set a non-existing style results in the following error message:

>>> date.style = "iso"
Traceback (most recent call last):
...
AttributeError: Date format style `iso` is not available.
property second: int

The actual second.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.second
0
>>> date.second = 30
>>> date.second
30
property minute: int

The actual minute.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.minute
0
>>> date.minute = 30
>>> date.minute
30
property hour: int

The actual hour.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.hour
0
>>> date.hour = 12
>>> date.hour
12
property day: int

The actual day.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.day
1
>>> date.day = 15
>>> date.day
15
property month: int

The actual month.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.month
1
>>> date.month = 7
>>> date.month
7
property year: int

The actual year.

>>> from hydpy import Date
>>> date = Date("2000-01-01 00:00:00")
>>> date.year
2000
>>> date.year = 1   # smallest possible value
>>> date.year
1
>>> date.year = 9999   # highest possible value
>>> date.year
9999
refmonth

The first month of the hydrological year.

The default value is 11 (November which is the German reference month):

>>> from hydpy import Date
>>> date1 = Date("2000-01-01")
>>> date1.refmonth
11

Setting it, for example, to 10 (October is another typical reference month in different countries) affects all Date instances, no matter if already existing or created afterwards:

>>> date2 = Date("2010-01-01")
>>> date1.refmonth = 10
>>> date1.refmonth
10
>>> date2.refmonth
10
>>> Date("2010-01-01").refmonth
10

Alternatively, you can pass an appropriate string (the first three characters count):

>>> date1.refmonth = "January"
>>> date1.refmonth
1
>>> date1.refmonth = "feb"
>>> date1.refmonth
2

Wrong arguments result in the following error messages:

>>> date1.refmonth = 0
Traceback (most recent call last):
...
ValueError: The reference month must be a value between one (January) and twelve (December) but `0` is given
>>> date1.refmonth = "wrong"
Traceback (most recent call last):
...
ValueError: The given argument `wrong` cannot be interpreted as a month.
>>> date1.refmonth = 11
property wateryear: int

The actual hydrological year according to the selected reference month.

Property refmonth defaults to November:

>>> october = Date("1996.10.01")
>>> november = Date("1996.11.01")
>>> october.wateryear
1996
>>> november.wateryear
1997

Note that changing refmonth affects all Date objects:

>>> october.refmonth = 10
>>> october.wateryear
1997
>>> november.wateryear
1997
>>> october.refmonth = "November"
>>> october.wateryear
1996
>>> november.wateryear
1997
property dayofyear: int

The day of the year as an integer value.

>>> from hydpy import Date
>>> Date("2003-03-01").dayofyear
60
>>> Date("2004-03-01").dayofyear
61
property leapyear: bool

Return whether the actual date falls in a leap year or not.

>>> from hydpy import Date
>>> Date("2003-03-01").leapyear
False
>>> Date("2004-03-01").leapyear
True
>>> Date("2000-03-01").leapyear
True
>>> Date("2100-03-01").leapyear
False
property beginning_next_month: Date

The first possible date of the next month after the month of the current Date object.

>>> from hydpy import Date
>>> Date("2001-01-01 00:00:00").beginning_next_month
Date("2001-02-01 00:00:00")
>>> Date("2001-01-31 12:30:30").beginning_next_month
Date("2001-02-01 00:00:00")
>>> Date("2001-12-01 00:00:00").beginning_next_month
Date("2002-01-01 00:00:00")
>>> Date("2001-12-31 12:30:30").beginning_next_month
Date("2002-01-01 00:00:00")
to_string(style: str | None = None, utcoffset: int | None = None) str[source]

Return a str object representing the actual date following the given style and the eventually given UTC offset (in minutes).

Without any input arguments, the actual style is used to return a date string in your local time zone:

>>> from hydpy import Date
>>> date = Date("01.11.1997 00:00:00")
>>> date.to_string()
'01.11.1997 00:00:00'

Passing a style string affects the returned str object but not the style property:

>>> date.style
'din1'
>>> date.to_string(style="iso2")
'1997-11-01 00:00:00'
>>> date.style
'din1'

When passing the utcoffset in minutes, method to_string() appends the offset string:

>>> date.to_string(style="iso2", utcoffset=60)
'1997-11-01 00:00:00+01:00'

If the given offset does not correspond to your local offset defined by utcoffset (which defaults to UTC+01:00), the date string is adapted:

>>> date.to_string(style="iso1", utcoffset=0)
'1997-10-31T23:00:00+00:00'
to_repr(style: str | None = None, utcoffset: int | None = None) str[source]

Similar to method to_string(), but returns a proper string representation instead.

See method to_string() for explanations on the following examples:

>>> from hydpy import Date
>>> date = Date("01.11.1997 00:00:00")
>>> date.to_repr()
'Date("01.11.1997 00:00:00")'
>>> date.to_repr("iso1", utcoffset=0)
'Date("1997-10-31T23:00:00+00:00")'
class hydpy.core.timetools.Period(period: Period | timedelta | str | None = None)[source]

Bases: object

Handles a single period.

We built the class Period on top of the Python module datetime. It wraps timedelta objects and specialises this general class on the needs of HydPy users.

Be aware of the different minimum time resolution of module datetime (microseconds) and module timetools (seconds).

You can initialise Period directly via timedelta objects (see the documentation on method from_timedelta() for more information):

>>> from hydpy import Period
>>> from datetime import timedelta
>>> Period(timedelta(1))
Period("1d")

Alternatively, one can initialise from str objects. These must consist of some characters defining an integer value followed by a single character defining the unit (see the documentation on method from_timedelta() for more information):

>>> Period("30s")
Period("30s")

In case you need an “empty” period object, pass nothing or None:

>>> Period()
Period()
>>> Period(None)
Period()

All other types result in the following error:

>>> Period(1)
Traceback (most recent call last):
...
TypeError: While trying to initialise a `Period` object based argument `1`, the following error occurred: The supplied argument must be either an instance of `Period`, `datetime.timedelta`, or `str`, but the given type is `int`.

Class Period supports some mathematical operations. Depending on the operation, the second operand can be either a number or an object interpretable as a date or period.

First, one can add two Period objects or add a Period object to an object representing a date:

>>> period = Period("1m")
>>> period + "2m"
Period("3m")
>>> "30s" + period
Period("90s")
>>> period += "4m"
>>> period
Period("5m")
>>> "2000-01-01" + period
Date("2000-01-01 00:05:00")
>>> period + "wrong"
Traceback (most recent call last):
...
TypeError: Object `wrong` of type `str` cannot be added to a `Period` instance.

Subtraction works much like addition:

>>> period = Period("4d")
>>> period - "1d"
Period("3d")
>>> "1d" - period
Period("-3d")
>>> period -= "2d"
>>> period
Period("2d")
>>> "2000-01-10" - period
Date("2000-01-08 00:00:00")
>>> "wrong" - period
Traceback (most recent call last):
...
TypeError: A `Period` instance cannot be subtracted from object `wrong` of type `str`.

Use multiplication with a number to change the length of a Period object:

>>> period * 2.0
Period("4d")
>>> 0.5 * period
Period("1d")
>>> period *= 1.5
>>> period
Period("3d")

Division is possible in combination numbers and objects interpretable as periods:

>>> period / 3.0
Period("1d")
>>> period / "36h"
2.0
>>> "6d" / period
2.0
>>> period /= 1.5
>>> period
Period("2d")

Floor division and calculation of the remainder are also supported:

>>> period // "20h"
2
>>> period % "20h"
Period("8h")
>>> "3d" // period
1
>>> timedelta(3) % period
Period("1d")

You can change the sign in the following manners:

>>> period = -period
>>> period
Period("-2d")
>>> +period
Period("-2d")
>>> abs(period)
Period("2d")

The comparison operators work as expected:

>>> p1, p3 = Period("1d"), Period("3d")
>>> p1 < "2d", p1 < "1d", "2d" < p1
(True, False, False)
>>> p1 <= p3, p1 <= "1d", "2d" <= p1
(True, True, False)
>>> p1 == p3, p1 == "1d", "2d" == p1, p1 == "2000-01-01"
(False, True, False, False)
>>> p1 != p3, p1 != "1d", "2d" != p1, p1 != "2000-01-01"
(True, False, True, True)
>>> p1 >= p3, p1 >= "1d", "2d" >= p1
(False, True, True)
>>> p1 > p3, p1 > "1d", "2d" > p1
(False, False, True)
classmethod from_period(period: Period) TypePeriod[source]

Create a new Period object based on another Period object and return it.

>>> from hydpy import Period
>>> p1 = Period("1d")
>>> p2 = Period.from_period(p1)
>>> p2
Period("1d")
>>> p1 *= 2
>>> p1
Period("2d")
>>> p2
Period("1d")
classmethod from_timedelta(period: timedelta) TypePeriod[source]

Create a new Period object based on a timedelta object and return it.

timedelta objects defining days or seconds are allowed, but timedelta objects defining microseconds are not:

>>> from hydpy import Period
>>> from datetime import timedelta
>>> Period.from_timedelta(timedelta(1, 0))
Period("1d")
>>> Period.from_timedelta(timedelta(0, 1))
Period("1s")
>>> Period.from_timedelta(timedelta(0, 0, 1))
Traceback (most recent call last):
...
ValueError: For `Period` instances, microseconds must be zero.  However, for the given `timedelta` object it is `1` instead.
classmethod from_string(period: str) TypePeriod[source]

Create a new Period object based on a str object and return it.

The string must consist of a leading integer number followed by one of the lower chase characters s (seconds), m (minutes), h (hours), and d (days):

>>> from hydpy import Period
>>> Period.from_string("30s")
Period("30s")
>>> Period.from_string("5m")
Period("5m")
>>> Period.from_string("6h")
Period("6h")
>>> Period.from_string("1d")
Period("1d")

Ill-defined strings result in the following errors:

>>> Period.from_string("oned")
Traceback (most recent call last):
...
ValueError: All characters of the given period string, except the last one which represents the unit, need to define an integer number.  Instead, these characters are `one`.
>>> Period.from_string("1.5d")
Traceback (most recent call last):
...
ValueError: All characters of the given period string, except the last one which represents the unit, need to define an integer number.  Instead, these characters are `1.5`.
>>> Period.from_string("1D")
Traceback (most recent call last):
...
ValueError: The last character of the given period string needs to be either `d` (days), `h` (hours), `m` (minutes),  or `s` (seconds).  Instead, the last character is `D`.
classmethod from_seconds(seconds: int) TypePeriod[source]

Create a new Period object based on the given integer number of seconds and return it.

>>> from hydpy import Period
>>> Period.from_seconds(120)
Period("2m")
classmethod from_cfunits(units: Literal['days', 'd', 'hours', 'h', 'minutes', 'm', 'seconds', 's']) TypePeriod[source]

Create a Period object representing the time unit of the given units string agreeing with the NetCDF-CF conventions and return it.

We took the following example string from the Time Coordinate chapter of the NetCDF-CF conventions. Note that the character of the first entry (the actual time unit) is of relevance:

>>> from hydpy import Period
>>> Period.from_cfunits("seconds since 1992-10-8 15:15:42.5 -6:00")
Period("1s")
>>> Period.from_cfunits(" day since 1992-10-8 15:15:00")
Period("1d")
>>> Period.from_cfunits("m since 1992-10-8")
Period("1m")
timedelta

The handled timedelta object.

You are allowed to change and delete the handled timedelta object:

>>> from hydpy import Period
>>> period = Period("1d")
>>> period.timedelta.days
1
>>> del period.timedelta
>>> period.timedelta
Traceback (most recent call last):
...
AttributeError: The Period object does not handle a timedelta object at the moment.
>>> from datetime import timedelta
>>> period.timedelta = timedelta(1)
>>> hasattr(period, "timedelta")
True

Property timedelta supports the automatic conversion of given Period and str objects:

>>> period.timedelta = Period("2d")
>>> period.timedelta.days
2
>>> period.timedelta = "1h"
>>> period.timedelta.seconds
3600
>>> period.timedelta = 1
Traceback (most recent call last):
...
TypeError: The supplied argument must be either an instance of `Period´, `datetime.timedelta` or `str`.  The given argument's type is `int`.
property unit: str

The (most suitable) unit for the current period.

unit always returns the unit leading to the smallest integer value:

>>> from hydpy import Period
>>> period = Period("1d")
>>> period.unit
'd'
>>> period /= 2
>>> period.unit
'h'
>>> Period("120s").unit
'm'
>>> Period("90s").unit
's'
property seconds: float

Period length in seconds.

>>> from hydpy import Period
>>> Period("2d").seconds
172800.0
property minutes: float

Period length in minutes.

>>> from hydpy import Period
>>> Period("2d").minutes
2880.0
property hours: float

Period length in hours.

>>> from hydpy import Period
>>> Period("2d").hours
48.0
property days: float

Period length in days.

>>> from hydpy import Period
>>> Period("2d").days
2.0
check() None[source]

Raise a RuntimeError if the step size is undefined at the moment.

>>> from hydpy import Period
>>> Period("1d").check()
>>> Period().check()
Traceback (most recent call last):
...
RuntimeError: No step size defined at the moment.
class hydpy.core.timetools.Timegrid(firstdate: Date | datetime | str, lastdate: Date | datetime | str, stepsize: Period | timedelta | str)[source]

Bases: object

Defines an arbitrary number of equidistant dates via the first date, the last date, and the step size between subsequent dates.

In hydrological modelling, input (and output) data are usually only available with a certain resolution, which also determines the possible resolution of the actual simulation. Class Timegrid reflects this situation by representing equidistant dates.

To initialise a Timegrid, pass its first date, its last date and its stepsize as str objects, Date and Period objects, or datetime and timedelta objects (combinations are allowed):

>>> from hydpy import Date, Period, Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid
Timegrid("2000-01-01 00:00:00",
         "2001-01-01 00:00:00",
         "1d")
>>> timegrid == Timegrid(
...     Date("2000-01-01"), Date("2001-01-01"), Period("1d"))
True
>>> from datetime import datetime, timedelta
>>> timegrid == Timegrid(
...     datetime(2000, 1, 1), datetime(2001, 1, 1), timedelta(1))
True

Passing unsupported argument types results in errors like the following:

>>> Timegrid("2000-01-01", "2001-01-01", 1)
Traceback (most recent call last):
...
TypeError: While trying to prepare a Trimegrid object based on the arguments `2000-01-01`, `2001-01-01`, and `1`, the following error occurred: While trying to initialise a `Period` object based argument `1`, the following error occurred: The supplied argument must be either an instance of `Period`, `datetime.timedelta`, or `str`, but the given type is `int`.

You can query indices and the corresponding dates via indexing:

>>> timegrid[0]
Date("2000-01-01 00:00:00")
>>> timegrid[5]
Date("2000-01-06 00:00:00")
>>> timegrid[Date("2000-01-01")]
0
>>> timegrid["2000-01-06"]
5

Indexing beyond the ranges of the actual period is allowed:

>>> timegrid[-365]
Date("1999-01-01 00:00:00")
>>> timegrid["2002-01-01"]
731

However, dates that do not precisely match the defined grid result in the following error:

>>> timegrid["2001-01-01 12:00"]
Traceback (most recent call last):
...
ValueError: The given date `2001-01-01 12:00:00` is not properly alligned on the indexed timegrid `Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")`.

You can determine the length of and iterate over Timegrid objects:

>>> len(timegrid)
366
>>> for date in timegrid:
...     print(date)  
2000-01-01 00:00:00
2000-01-02 00:00:00
...
2000-12-30 00:00:00
2000-12-31 00:00:00

By default, iteration yields the left-side timestamps (the start points) of the respective intervals. Set the timestampleft option to False of you prefer the right-side timestamps:

>>> from hydpy import pub
>>> with pub.options.timestampleft(False):
...     for date in timegrid:
...         print(date)  
2000-01-02 00:00:00
2000-01-03 00:00:00
...
2000-12-31 00:00:00
2001-01-01 00:00:00

You can check Timegrid instances for equality:

>>> timegrid == Timegrid("2000-01-01", "2001-01-01", "1d")
True
>>> timegrid != Timegrid("2000-01-01", "2001-01-01", "1d")
False
>>> timegrid == Timegrid("2000-01-02", "2001-01-01", "1d")
False
>>> timegrid == Timegrid("2000-01-01", "2001-01-02", "1d")
False
>>> timegrid == Timegrid("2000-01-01", "2001-01-01", "2d")
False
>>> timegrid == 1
False

Also, you can check if a date or even the whole timegrid lies within a span defined by a Timegrid instance (note unaligned dates and time grids with different step sizes are considered unequal):

>>> Date("2000-01-01") in timegrid
True
>>> "2001-01-01" in timegrid
True
>>> "2000-07-01" in timegrid
True
>>> "1999-12-31" in timegrid
False
>>> "2001-01-02" in timegrid
False
>>> "2001-01-02 12:00" in timegrid
False
>>> timegrid in Timegrid("2000-01-01", "2001-01-01", "1d")
True
>>> timegrid in Timegrid("1999-01-01", "2002-01-01", "1d")
True
>>> timegrid in Timegrid("2000-01-02", "2001-01-01", "1d")
False
>>> timegrid in Timegrid("2000-01-01", "2000-12-31", "1d")
False
>>> timegrid in Timegrid("2000-01-01", "2001-01-01", "2d")
False

For convenience, you can temporarily modify the attributes of a Timegrid object by calling it after the with statement:

>>> with timegrid("1999-01-01", "2002-01-01", "1h"):
...     print(timegrid)
Timegrid("1999-01-01 00:00:00", "2002-01-01 00:00:00", "1h")
>>> print(timegrid)
Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")

You are free to select the attributes you want to change (see method modify() for further information) or to change specific attributes later within the with block:

>>> with timegrid(lastdate=None) as tg:
...     print(timegrid)
...     timegrid.firstdate = "1999-01-01"
...     tg.lastdate = "2002-01-01"
...     tg.stepsize = "1h"
...     print(timegrid)
Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")
Timegrid("1999-01-01 00:00:00", "2002-01-01 00:00:00", "1h")
>>> print(timegrid)
Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")
firstdate

The start date of the relevant period.

You can query and alter the value of property firstdate (call method verify() afterwards to make sure the Timegrid object did not become ill-defined):

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.firstdate
Date("2000-01-01 00:00:00")
>>> timegrid.firstdate += "1d"
>>> timegrid
Timegrid("2000-01-02 00:00:00",
         "2001-01-01 00:00:00",
         "1d")
lastdate

The end date of the relevant period.

You can query and alter the value of property lastdate (call method verify() afterwards to make sure the Timegrid object did not become ill-defined):

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.lastdate
Date("2001-01-01 00:00:00")
>>> timegrid.lastdate += "1d"
>>> timegrid
Timegrid("2000-01-01 00:00:00",
         "2001-01-02 00:00:00",
         "1d")
dates

Shortcut to get or set both property firstdate and property lastdate in one step.

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.dates
(Date("2000-01-01 00:00:00"), Date("2001-01-01 00:00:00"))
>>> timegrid.dates = "2002-01-01", "2003-01-01"
>>> timegrid.firstdate
Date("2002-01-01 00:00:00")
>>> timegrid.lastdate
Date("2003-01-01 00:00:00")
stepsize

The time series data and simulation step size.

You can query and alter the value of property stepsize (call method verify() afterwards to make sure the Timegrid object did not become ill-defined):

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.stepsize
Period("1d")
>>> timegrid.stepsize += "1d"
>>> timegrid
Timegrid("2000-01-01 00:00:00",
         "2001-01-01 00:00:00",
         "2d")
classmethod from_array(array: ndarray[Any, dtype[float64]]) TypeTimegrid[source]

Create a Timegrid instance based on information stored in the first 13 rows of a ndarray object and return it.

In HydPy, external time series files do define the time-related reference of their data on their own. For the numpy npy binary format, we achieve this by reserving the first six entries for the first date of the period, the next six entries for the last date of the period, and the last entry for the step size (in seconds):

>>> from numpy import array
>>> array_ = array([2000, 1, 1, 0, 0, 0,    # first date
...                 2000, 1, 1, 7, 0, 0,    # second date
...                 3600,                   # step size (in seconds)
...                 1, 2, 3, 4, 5, 6, 7])   # data

Use method from_array() to extract the time information:

>>> from hydpy import Timegrid
>>> timegrid = Timegrid.from_array(array_)
>>> timegrid
Timegrid("2000-01-01 00:00:00",
         "2000-01-01 07:00:00",
         "1h")

Too little information results in the following error message:

>>> Timegrid.from_array(array_[:12])
Traceback (most recent call last):
...
IndexError: To define a Timegrid instance via an array, 13 numbers are required, but the given array consist of 12 entries/rows only.

The inverse method to_array() creates a new numpy ndarray based on the current Timegrid object:

>>> from hydpy import round_
>>> round_(timegrid.to_array())
2000.0, 1.0, 1.0, 0.0, 0.0, 0.0, 2000.0, 1.0, 1.0, 7.0, 0.0, 0.0, 3600.0
to_array() ndarray[Any, dtype[float64]][source]

Return a 1-dimensional numpy ndarray storing the information of the actual Timegrid object.

See the documentation on method from_array() for more information.

classmethod from_timepoints(timepoints: Sequence[float], refdate: Date | datetime | str, unit: Literal['days', 'd', 'hours', 'h', 'minutes', 'm', 'seconds', 's'] = 'hours') TypeTimegrid[source]

Return a Timegrid object representing the given starting timepoints related to the given refdate.

At least two given time points must be increasing and equidistant. By default, from_timepoints() assumes them as hours elapsed since the given reference date:

>>> from hydpy import Timegrid
>>> Timegrid.from_timepoints([0.0, 6.0, 12.0, 18.0], "01.01.2000")
Timegrid("01.01.2000 00:00:00",
         "02.01.2000 00:00:00",
         "6h")
>>> Timegrid.from_timepoints([24.0, 30.0, 36.0, 42.0], "1999-12-31")
Timegrid("2000-01-01 00:00:00",
         "2000-01-02 00:00:00",
         "6h")

You can pass other time units (days or min) explicitly (only the first character counts):

>>> Timegrid.from_timepoints([0.0, 0.25, 0.5, 0.75], "01.01.2000", unit="d")
Timegrid("01.01.2000 00:00:00",
         "02.01.2000 00:00:00",
         "6h")
>>> Timegrid.from_timepoints([1.0, 1.25, 1.5, 1.75], "1999-12-31", unit="days")
Timegrid("2000-01-01 00:00:00",
         "2000-01-02 00:00:00",
         "6h")

When setting the timestampleft option to False, from_timepoints() assumes each time point to define the right side (the end) of a time interval. Repeating the above examples with this modification shifts the firstdate and the lastdate of the returned Timegrid objects to the left:

>>> from hydpy import pub
>>> with pub.options.timestampleft(False):
...     Timegrid.from_timepoints([0.0, 6.0, 12.0, 18.0], "01.01.2000")
Timegrid("31.12.1999 18:00:00",
         "01.01.2000 18:00:00",
         "6h")
>>> with pub.options.timestampleft(False):
...     Timegrid.from_timepoints([24.0, 30.0, 36.0, 42.0], "1999-12-31")
Timegrid("1999-12-31 18:00:00",
         "2000-01-01 18:00:00",
         "6h")
>>> with pub.options.timestampleft(False):
...     Timegrid.from_timepoints([0.0, 0.25, 0.5, 0.75], "01.01.2000", unit="d")
Timegrid("31.12.1999 18:00:00",
         "01.01.2000 18:00:00",
         "6h")
>>> with pub.options.timestampleft(False):
...     Timegrid.from_timepoints([1.0, 1.25, 1.5, 1.75], "1999-12-31", unit="d")
Timegrid("1999-12-31 18:00:00",
         "2000-01-01 18:00:00",
         "6h")
to_timepoints(unit: Literal['days', 'd', 'hours', 'h', 'minutes', 'm', 'seconds', 's'] = 'hours', offset: float | Period | timedelta | str = 0.0) ndarray[source]

Return a ndarray representing the starting time points of the Timegrid object.

By default, method to_timepoints() returns the time elapsed since the firstdate in hours:

>>> from hydpy import print_vector, Timegrid
>>> timegrid = Timegrid("2000-01-01", "2000-01-02", "6h")
>>> print_vector(timegrid.to_timepoints())
0.0, 6.0, 12.0, 18.0

You can define other time units (days or min) (only the first character counts):

>>> print_vector(timegrid.to_timepoints(unit="d"))
0.0, 0.25, 0.5, 0.75

Additionally, one can pass an offset that must be of type int or a valid Period initialisation argument:

>>> print_vector(timegrid.to_timepoints(offset=24))
24.0, 30.0, 36.0, 42.0
>>> print_vector(timegrid.to_timepoints(offset="1d"))
24.0, 30.0, 36.0, 42.0
>>> print_vector(timegrid.to_timepoints(unit="days", offset="1d"))
1.0, 1.25, 1.5, 1.75

When setting the timestampleft option to False, to_timepoints() assumes each time point to define the right side (the end) of a time interval. Repeating the above examples with this modification shifts the time points of the returned ndarray objects to the right:

>>> from hydpy import pub
>>> with pub.options.timestampleft(False):
...     print_vector(timegrid.to_timepoints())
6.0, 12.0, 18.0, 24.0
>>> with pub.options.timestampleft(False):
...     print_vector(timegrid.to_timepoints(unit="d"))
0.25, 0.5, 0.75, 1.0
>>> with pub.options.timestampleft(False):
...     print_vector(timegrid.to_timepoints(offset=24))
30.0, 36.0, 42.0, 48.0
>>> with pub.options.timestampleft(False):
...     print_vector(timegrid.to_timepoints(offset="1d"))
30.0, 36.0, 42.0, 48.0
>>> with pub.options.timestampleft(False):
...     print_vector(timegrid.to_timepoints(unit="days", offset="1d"))
1.25, 1.5, 1.75, 2.0
array2series(array: ndarray[Any, dtype[float64]]) ndarray[Any, dtype[float64]][source]

Prefix the information of the actual Timegrid object to the given array and return it.

The Timegrid information is available in the first thirteen values of the first axis of the returned series (see the documentation on the method from_array()).

To show how method array2series() works, we first apply it on a simple list containing numbers:

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-11-01 00:00", "2000-11-01 04:00", "1h")
>>> series = timegrid.array2series([1, 2, 3.5, "5.0"])

The first six entries contain the first date of the timegrid (year, month, day, hour, minute, second):

>>> from hydpy import round_
>>> round_(series[:6])
2000.0, 11.0, 1.0, 0.0, 0.0, 0.0

The six subsequent entries contain the last date:

>>> round_(series[6:12])
2000.0, 11.0, 1.0, 4.0, 0.0, 0.0

The thirteens value is the step size in seconds:

>>> round_(series[12])
3600.0

The last four values are the ones of the given vector:

>>> round_(series[-4:])
1.0, 2.0, 3.5, 5.0

The given array can have an arbitrary number of dimensions:

>>> import numpy
>>> array = numpy.eye(4)
>>> series = timegrid.array2series(array)

Now the timegrid information is stored in the first column:

>>> round_(series[:13, 0])
2000.0, 11.0, 1.0, 0.0, 0.0, 0.0, 2000.0, 11.0, 1.0, 4.0, 0.0, 0.0, 3600.0

All other columns of the first thirteen rows contain nan values:

>>> round_(series[12, :])
3600.0, nan, nan, nan

The original values are available in the last four rows:

>>> round_(series[13, :])
1.0, 0.0, 0.0, 0.0

Inappropriate array objects result in error messages like the following:

>>> timegrid.array2series([[1, 2], [3]])
Traceback (most recent call last):
...
ValueError: While trying to prefix timegrid information to the given array, the following error occurred: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.

The following error occurs when the given array does not fit the defined time grid:

>>> timegrid.array2series([[1, 2], [3, 4]])
Traceback (most recent call last):
...
ValueError: When converting an array to a sequence, the lengths of the timegrid and the given array must be equal, but the length of the timegrid object is `4` and the length of the array object is `2`.
verify() None[source]

Raise a ValueError if the dates or the step size of the Timegrid object are currently inconsistent.

Method verify() is called at the end of the initialisation of a new Timegrid object automatically:

>>> from hydpy import Timegrid
>>> Timegrid("2001-01-01", "2000-01-01", "1d")
Traceback (most recent call last):
...
ValueError: While trying to prepare a Trimegrid object based on the arguments `2001-01-01`, `2000-01-01`, and `1d`, the following error occurred: The temporal sequence of the first date (`2001-01-01 00:00:00`) and the last date (`2000-01-01 00:00:00`) is inconsistent.

However, the same does not hold when changing property firstdate, lastdate, or stepsize:

>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.stepsize = "4d"

When in doubt, call method verify() manually:

>>> timegrid.verify()
Traceback (most recent call last):
...
ValueError: The interval between the first date (`2000-01-01 00:00:00`) and the last date (`2001-01-01 00:00:00`) is `366d`, which is not an integral multiple of the step size `4d`.
modify(firstdate: Date | datetime | str | None = None, lastdate: Date | datetime | str | None = None, stepsize: Period | timedelta | str | None = None) None[source]

Modify one or more Timegrid attributes in one step.

If you want to change all attributes of an existing Timegrid object, it is often most convenient to do so in one step via method modify():

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("2000-01-01", "2001-01-01", "1d")
>>> timegrid.modify("1999-01-01", "2002-01-01", "1h")
>>> timegrid
Timegrid("1999-01-01 00:00:00",
         "2002-01-01 00:00:00",
         "1h")

Another benefit of method modify() is that all changes are optional. Ignore an argument or set it to None explicitly to leave the corresponding attribute unchanged:

>>> timegrid.modify(None, stepsize=None)
>>> timegrid
Timegrid("1999-01-01 00:00:00",
         "2002-01-01 00:00:00",
         "1h")
assignrepr(prefix: str, style: str | None = None, utcoffset: int | None = None) str[source]

Return a repr() string with a prefixed assignment.

>>> from hydpy import Timegrid
>>> timegrid = Timegrid("1996-11-01 00:00:00",
...                     "1997-11-01 00:00:00",
...                     "1d")
>>> print(timegrid.assignrepr(prefix="timegrid = "))
timegrid = Timegrid("1996-11-01 00:00:00",
                    "1997-11-01 00:00:00",
                    "1d")
>>> print(timegrid.assignrepr(
...     prefix="", style="iso1", utcoffset=120))
Timegrid("1996-11-01T01:00:00+02:00",
         "1997-11-01T01:00:00+02:00",
         "1d")
class hydpy.core.timetools.Timegrids(*args: None | Timegrid | Date | datetime | str | Period | timedelta, **kwargs: None | Timegrid | Date | datetime | str | Period | timedelta)[source]

Bases: object

Handles the “initialisation”, the “simulation”, and the “evaluation Timegrid object of a HydPy project.

The HydPy framework distinguishes three “time frames”, one associated with the initialisation period (init), one associated with the actual simulation period (sim), and one associated with the actual evaluation period (eval_). These time frames are represented by three different Timegrid objects, which are each handled by a single Timegrid object.

There is usually only one Timegrids object required within a HydPy project available as attribute timegrids of module pub. You have to create such an object at the beginning of your workflow.

In many cases, one either wants to perform simulations and evaluations covering the whole initialisation period or not perform any simulation or evaluation. In these situations, you can pass a single Timegrid instance to the constructor of class Timegrids:

>>> from hydpy import Timegrid, Timegrids
>>> timegrids = Timegrids(Timegrid("2000-01-01", "2001-01-01", "1d"))
>>> print(timegrids)
Timegrids("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")

An even shorter approach is to pass the arguments of the Timegrid constructor directly:

>>> timegrids == Timegrids("2000-01-01", "2001-01-01", "1d")
True

To define a simulation time grid different from the initialisation time grid, pass both as two individual Timegrid objects:

>>> timegrids = Timegrids(Timegrid("2000-01-01", "2001-01-01", "1d"),
...                       Timegrid("2000-01-01", "2000-07-01", "1d"))

The evaluation time grid then corresponds to the simulation time grid:

>>> timegrids
Timegrids(init=Timegrid("2000-01-01 00:00:00",
                        "2001-01-01 00:00:00",
                        "1d"),
          sim=Timegrid("2000-01-01 00:00:00",
                       "2000-07-01 00:00:00",
                       "1d"),
          eval_=Timegrid("2000-01-01 00:00:00",
                         "2000-07-01 00:00:00",
                         "1d"))

Of course, you can also define a separate evaluation period:

>>> timegrids = Timegrids(Timegrid("2000-01-01", "2001-01-01", "1d"),
...                       Timegrid("2000-01-01", "2000-07-01", "1d"),
...                       Timegrid("2000-06-01", "2000-07-01", "1d"))
>>> timegrids.init
Timegrid("2000-01-01 00:00:00",
         "2001-01-01 00:00:00",
         "1d")
>>> timegrids.sim
Timegrid("2000-01-01 00:00:00",
         "2000-07-01 00:00:00",
         "1d")
>>> timegrids.eval_
Timegrid("2000-06-01 00:00:00",
         "2000-07-01 00:00:00",
         "1d")

Class Timegrids supports keyword arguments:

>>> Timegrids(firstdate="2000-01-01 00:00:00",
...           lastdate="2001-01-01 00:00:00",
...           stepsize="1d")
Timegrids("2000-01-01 00:00:00",
          "2001-01-01 00:00:00",
          "1d")
>>> Timegrids("2000-01-01 00:00:00",
...           "2001-01-01 00:00:00",
...           stepsize="1d")
Timegrids("2000-01-01 00:00:00",
          "2001-01-01 00:00:00",
          "1d")
>>> Timegrids(init=Timegrid("2000-01-01 00:00:00",
...                         "2001-01-01 00:00:00",
...                         "1d"),
...           sim=Timegrid("2000-01-01 00:00:00",
...                        "2000-07-01 00:00:00",
...                        "1d"))
Timegrids(init=Timegrid("2000-01-01 00:00:00",
                        "2001-01-01 00:00:00",
                        "1d"),
          sim=Timegrid("2000-01-01 00:00:00",
                       "2000-07-01 00:00:00",
                       "1d"),
          eval_=Timegrid("2000-01-01 00:00:00",
                         "2000-07-01 00:00:00",
                         "1d"))
>>> Timegrids(init=Timegrid("2000-01-01 00:00:00",
...                         "2001-01-01 00:00:00",
...                         "1d"),
...           eval_=Timegrid("2000-06-01 00:00:00",
...                        "2000-07-01 00:00:00",
...                        "1d"))
Timegrids(init=Timegrid("2000-01-01 00:00:00",
                        "2001-01-01 00:00:00",
                        "1d"),
          sim=Timegrid("2000-01-01 00:00:00",
                       "2001-01-01 00:00:00",
                       "1d"),
          eval_=Timegrid("2000-06-01 00:00:00",
                         "2000-07-01 00:00:00",
                         "1d"))

Wrong arguments should result in understandable error messages:

>>> Timegrids(1, 2, 3, 4)
Traceback (most recent call last):
...
TypeError: While trying to define a new `Timegrids` object based on the arguments `1, 2, 3, and 4`, the following error occurred: Initialising `Timegrids` objects requires one, two, or three arguments but `4` are given.
>>> Timegrids("wrong")
Traceback (most recent call last):
...
ValueError: While trying to define a new `Timegrids` object based on the arguments `wrong`, the following error occurred: Initialising a `Timegrids` object either requires one, two, or three `Timegrid` objects or two dates objects (of type `Date`, `datetime`, or `str`) and one period object (of type `Period`, `timedelta`, or `str`), but objects of the types `str, None, and None` are given.
>>> Timegrids(wrong=Timegrid("2000-01-01", "2001-01-01", "1d"))
Traceback (most recent call last):
...
TypeError: While trying to define a new `Timegrids` object based on the arguments `Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")`, the following error occurred: Initialising class `Timegrids` does not support the following given keywords: `wrong`.
>>> Timegrids(
...     Timegrid("2000-01-01", "2001-01-01", "1d"),
...     init=Timegrid("2000-01-01", "2001-01-01", "1d"))
Traceback (most recent call last):
...
TypeError: While trying to define a new `Timegrids` object based on the arguments `Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d") and Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")`, the following error occurred: There is a conflict between the given positional and keyword arguments.

Two Timegrids objects are equal if all handled Timegrid objects are equal:

>>> timegrids == Timegrids(
...     Timegrid("2000-01-01", "2001-01-01", "1d"),
...     Timegrid("2000-01-01", "2000-07-01", "1d"),
...     Timegrid("2000-06-01", "2000-07-01", "1d"))
True
>>> timegrids == Timegrids(
...     Timegrid("1999-01-01", "2001-01-01", "1d"),
...     Timegrid("2000-01-01", "2000-07-01", "1d"),
...     Timegrid("2000-06-01", "2000-07-01", "1d"))
False
>>> timegrids == Timegrids(
...     Timegrid("2000-01-01", "2001-01-01", "1d"),
...     Timegrid("2000-01-01", "2001-01-01", "1d"),
...     Timegrid("2000-06-01", "2000-07-01", "1d"))
False
>>> timegrids == Timegrids(
...     Timegrid("2000-01-01", "2001-01-01", "1d"),
...     Timegrid("2000-01-01", "2000-07-01", "1d"),
...     Timegrid("2000-01-01", "2000-07-01", "1d"))
False
>>> timegrids == Date("2000-01-01")
False

Timegrids objects are iterable and yield their Timegrid objects in the order init sim eval_:

>>> for timegrid in timegrids:
...     print(timegrid)
Timegrid("2000-01-01 00:00:00", "2001-01-01 00:00:00", "1d")
Timegrid("2000-01-01 00:00:00", "2000-07-01 00:00:00", "1d")
Timegrid("2000-06-01 00:00:00", "2000-07-01 00:00:00", "1d")
init: Timegrid

Timegrid object covering the whole initialisation period.

sim: Timegrid

Timegrid object covering the actual simulation period only.

eval_: Timegrid

Timegrid object covering the actual evaluation period only.

stepsize

Stepsize of all handled Timegrid objects.

You can change the (the identical) stepsize of all handled Timegrid objects at once:

>>> from hydpy import Timegrids
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1d")
>>> timegrids.sim.lastdate = "2000-02-01"
>>> timegrids.eval_.lastdate = "2000-03-01"
>>> timegrids
Timegrids(init=Timegrid("2000-01-01 00:00:00",
                        "2001-01-01 00:00:00",
                        "1d"),
          sim=Timegrid("2000-01-01 00:00:00",
                       "2000-02-01 00:00:00",
                       "1d"),
          eval_=Timegrid("2000-01-01 00:00:00",
                         "2000-03-01 00:00:00",
                         "1d"))
>>> timegrids.stepsize
Period("1d")
>>> timegrids.stepsize = "1h"
>>> timegrids
Timegrids(init=Timegrid("2000-01-01 00:00:00",
                        "2001-01-01 00:00:00",
                        "1h"),
          sim=Timegrid("2000-01-01 00:00:00",
                       "2000-02-01 00:00:00",
                       "1h"),
          eval_=Timegrid("2000-01-01 00:00:00",
                         "2000-03-01 00:00:00",
                         "1h"))
verify() None[source]

Raise a ValueError if the different Timegrid objects are inconsistent.

Method verify() is called at the end of the initialisation of a new Timegrids object automatically:

>>> from hydpy import Timegrid, Timegrids
>>> Timegrids(init=Timegrid("2001-01-01", "2002-01-01", "1d"),
...           sim=Timegrid("2000-01-01", "2002-01-01", "1d"))
Traceback (most recent call last):
...
ValueError: While trying to define a new `Timegrids` object based on the arguments `Timegrid("2001-01-01 00:00:00", "2002-01-01 00:00:00", "1d") and Timegrid("2000-01-01 00:00:00", "2002-01-01 00:00:00", "1d")`, the following error occurred: The first date of the initialisation period (`2001-01-01 00:00:00`) must not be later than the first date of the simulation period (`2000-01-01 00:00:00`).

However, the same does not hold when one changes a single time grid later:

>>> timegrids = Timegrids(
...     init=Timegrid("2001-01-01", "2002-01-01", "1d"),
...     eval_=Timegrid("2001-01-01", "2002-01-01", "1d"))
>>> timegrids.eval_.lastdate = "2003-01-01"

When in doubt, call method verify() manually:

>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: The last date of the initialisation period (`2002-01-01 00:00:00`) must not be earlier than the last date of the evaluation period (`2003-01-01 00:00:00`).

Besides both tests explained by the above error messages, method verify() checks for an equal step size of all Timegrid objects and their proper alignment:

>>> timegrids.sim.lastdate = "2002-01-01"
>>> timegrids.sim.stepsize = "5d"
>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: The initialisation stepsize (`1d`) must be identical with the simulation stepsize (`5d`).
>>> timegrids.sim = Timegrid(
...     "2001-01-01 12:00", "2001-12-31 12:00", "1d")
>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: The simulation time grid `Timegrid("2001-01-01 12:00:00", "2001-12-31 12:00:00", "1d")` is not properly alligned on the initialisation time grid `Timegrid("2001-01-01 00:00:00", "2002-01-01 00:00:00", "1d")`.

Additionally, the method verify() calls the verification methods of all Timegrid objects:

>>> timegrids.sim.stepsize = "3d"
>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: While trying to verify the simulation time grid `Timegrid("2001-01-01 12:00:00", "2001-12-31 12:00:00", "3d")`, the following error occurred: The interval between the first date (`2001-01-01 12:00:00`) and the last date (`2001-12-31 12:00:00`) is `364d`, which is not an integral multiple of the step size `3d`.
>>> timegrids.sim = timegrids.init
>>> timegrids.eval_.stepsize = "3d"
>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: While trying to verify the evaluation time grid `Timegrid("2001-01-01 00:00:00", "2003-01-01 00:00:00", "3d")`, the following error occurred: The interval between the first date (`2001-01-01 00:00:00`) and the last date (`2003-01-01 00:00:00`) is `730d`, which is not an integral multiple of the step size `3d`.
>>> timegrids.init.stepsize = "3d"
>>> timegrids.verify()
Traceback (most recent call last):
...
ValueError: While trying to verify the initialisation time grid `Timegrid("2001-01-01 00:00:00", "2002-01-01 00:00:00", "3d")`, the following error occurred: The interval between the first date (`2001-01-01 00:00:00`) and the last date (`2002-01-01 00:00:00`) is `365d`, which is not an integral multiple of the step size `3d`.
property initindices: tuple[int, int]

A tuple containing the start and end index of the initialisation period.

>>> from hydpy import Timegrids
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1d")
>>> timegrids.initindices
(0, 366)
property simindices: tuple[int, int]

A tuple containing the start and end index of the simulation period regarding the initialisation period.

>>> from hydpy import Timegrids
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1d")
>>> timegrids.simindices
(0, 366)
>>> timegrids.sim.firstdate = "2000-01-11"
>>> timegrids.sim.lastdate = "2000-02-01"
>>> timegrids.simindices
(10, 31)
property evalindices: tuple[int, int]

A tuple containing the start and end index of the evaluation period regarding the initialisation period.

>>> from hydpy import Timegrids
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1d")
>>> timegrids.simindices
(0, 366)
>>> timegrids.eval_.firstdate = "2000-01-11"
>>> timegrids.eval_.lastdate = "2000-02-01"
>>> timegrids.evalindices
(10, 31)
qfactor(area: float) float[source]

Return the factor for converting mm/stepsize to m³/s for a reference area, given in km².

>>> from hydpy import Timegrids, round_
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1s")
>>> timegrids.qfactor(1.0)
1000.0
>>> timegrids.stepsize = "2d"
>>> round_(timegrids.qfactor(2.0))
0.011574
parfactor(stepsize: Period | timedelta | str) float[source]

Return the factor for adjusting time-dependent parameter values to the actual simulation step size (the given stepsize must be related to the original parameter values).

>>> from hydpy import Timegrids
>>> timegrids = Timegrids("2000-01-01", "2001-01-01", "1d")
>>> timegrids.parfactor("1d")
1.0
>>> timegrids.parfactor("1h")
24.0
assignrepr(prefix: str) str[source]

Return a repr() string with a prefixed assignment.

class hydpy.core.timetools.TOY(value: str | Date = '')[source]

Bases: object

Time of year handler.

TOY objects are used to define certain things that are true for a specific time point in each year. The smallest supported time unit is seconds.

For initialisation, one usually passes a string defining the month, the day, the hour, the minute and the second in the mentioned order and separated by single underscores:

>>> from hydpy.core.timetools import Date, TOY
>>> t = TOY("3_13_23_33_43")
>>> t.month
3
>>> t.day
13
>>> t.hour
23
>>> t.minute
33
>>> t.second
43

If a lower precision is required, one can shorten the string, which implicitly sets the omitted property to the lowest possible value:

>>> TOY("3_13_23_33")
TOY("3_13_23_33_0")

The most extreme example is to pass no string at all:

>>> TOY()
TOY("1_1_0_0_0")

One can prefix some information to the string, which is useful when the string is to be used as a valid variable name somewhere else:

>>> TOY("something_3_13_23_33_2")
TOY("3_13_23_33_2")

As one can see, we lose the prefixed information in the printed string representation. Instead, applying “str” returns a string with a standard prefix:

>>> str(TOY('something_3_13_23_33_2'))
'toy_3_13_23_33_2'

Alternatively, one can use a Date object as an initialisation argument, omitting the year:

>>> TOY(Date("2001.02.03 04:05:06"))
TOY("2_3_4_5_6")

Ill-defined constructor arguments result in error messages like the following:

>>> TOY("something")
Traceback (most recent call last):
...
ValueError: While trying to initialise a TOY object based on argument value `something` of type `str`, the following error occurred: When passing a prefixed string, you need to define at least the month.
>>> TOY("2_30_4_5_6")
Traceback (most recent call last):
...
ValueError: While trying to initialise a TOY object based on argument value `2_30_4_5_6` of type `str`, the following error occurred: While trying to retrieve the day, the following error occurred: The value of property `day` of the actual TOY (time of year) object must lie within the range `(1, 29)`, as the month has already been set to `2`, but the given value is `30`.

It is only allowed to modify the mentioned properties, not to define new ones:

>>> t.microsecond = 53
Traceback (most recent call last):
...
AttributeError: TOY (time of year) objects only allow to set the properties month, day, hour, minute, and second, but `microsecond` is given.

You can pass any objects that are convertible to integers:

>>> t.second = "53"
>>> t.second
53

Unconvertible objects cause the following error:

>>> t.second = "fiftythree"
Traceback (most recent call last):
...
ValueError: For TOY (time of year) objects, all properties must be of type `int`, but the value `fiftythree` of type `str` given for property `second` cannot be converted to `int`.

Additionally, given values are checked to lie within a suitable range:

>>> t.second = 60
Traceback (most recent call last):
...
ValueError: The value of property `second` of TOY (time of year) objects must lie within the range `(0, 59)`, but the given value is `60`.

Note that the allowed values for month and day depend on each other, which is why the order one defines them might be of importance. So, if January is predefined, one can set the day to the 31st:

>>> t.month = 1
>>> t.day = 31

Afterwards, one cannot directly change the month to April:

>>> t.month = 4
Traceback (most recent call last):
...
ValueError: The value of property `month` of the actual TOY (time of year) object must not be the given value `4`, as the day has already been set to `31`.

First, set day to a smaller value and change month afterwards:

>>> t.day = 30
>>> t.month = 4

It is possible to compare two TOY instances:

>>> t1, t2 = TOY("1"), TOY("2")
>>> t1 < t1, t1 < t2, t2 < t1
(False, True, False)
>>> t1 <= t1, t1 <= t2, t2 <= t1
(True, True, False)
>>> t1 == t1, t1 == t2, t1 == 1
(True, False, False)
>>> t1 != t1, t1 != t2, t1 != 1
(False, True, True)
>>> t1 >= t1, t1 >= t2, t2 >= t1
(True, False, True)
>>> t1 > t1, t1 > t2, t2 > t1
(False, False, True)

Subtracting two TOY objects gives their time difference in seconds:

>>> TOY("1_1_0_3_0") - TOY("1_1_0_1_30")
90

Subtraction never results in negative values due to assuming the left operand is the posterior (eventually within the subsequent year):

>>> TOY("1_1_0_1_30") - TOY("12_31_23_58_30")
180
month: int

The month of the current the actual time of the year.

day: int

The day of the current the actual time of the year.

hour: int

The hour of the current the actual time of the year.

minute: int

The minute of the current the actual time of the year.

second: int

The second of the current the actual time of the year.

property seconds_passed: int

The amount of time passed in seconds since the beginning of the year.

In the first example, the year is only one minute and thirty seconds old:

>>> from hydpy.core.timetools import TOY
>>> toy = TOY("1_1_0_1_30")
>>> toy.seconds_passed
90

Updating the TOY object triggers a recalculation of property seconds_passed:

>>> toy.day = 2
>>> toy.seconds_passed
86490

The second example shows the general inclusion of the 29th of February:

>>> TOY("3").seconds_passed
5184000
property seconds_left: int

The remaining amount of time part of the year (in seconds).

In the first example, only one minute and thirty seconds of the year remain:

>>> from hydpy.core.timetools import TOY
>>> toy = TOY("12_31_23_58_30")
>>> toy.seconds_left
90

Updating the TOY object triggers a recalculation of property seconds_passed:

>>> toy.day = 30
>>> toy.seconds_left
86490

The second example shows the general inclusion of the 29th of February:

>>> TOY("2").seconds_left
28944000
classmethod centred_timegrid() tuple[Timegrid, ndarray[Any, dtype[bool]]][source]

Return a Timegrid object defining the central time points of the year 2000 and a boolean array describing its intersection with the current initialisation period, not taking the year information into account.

The returned Timegrid object does not depend on the defined initialisation period:

>>> from hydpy.core.timetools import TOY
>>> from hydpy import pub
>>> pub.timegrids = "2001-10-01", "2010-10-01", "1d"
>>> TOY.centred_timegrid()[0]
Timegrid("2000-01-01 12:00:00",
         "2001-01-01 12:00:00",
         "1d")

The same holds for the shape of the returned boolean array:

>>> len(TOY.centred_timegrid()[1])
366

However, the single boolean values depend on whether the respective centred date lies at least one time within the initialisation period when ignoring the year information. In our example, all centred dates are “relevant” due to the long initialisation period of ten years:

>>> from hydpy import print_vector, round_
>>> round_(sum(TOY.centred_timegrid()[1]))
366

The boolean array contains only the value True for all initialisation periods covering at least a full year:

>>> pub.timegrids = "2000-02-01", "2001-02-01", "1d"
>>> round_(sum(TOY.centred_timegrid()[1]))
366
>>> pub.timegrids = "2001-10-01", "2002-10-01", "1d"
>>> round_(sum(TOY.centred_timegrid()[1]))
366

In all other cases, only the values related to the intersection are True:

>>> pub.timegrids = "2001-01-03", "2001-01-05", "1d"
>>> print_vector(TOY.centred_timegrid()[1][:5])
False, False, True, True, False
>>> pub.timegrids = "2001-12-30", "2002-01-04", "1d"
>>> print_vector(TOY.centred_timegrid()[1][:5])
True, True, True, False, False
>>> print_vector(TOY.centred_timegrid()[1][-5:])
False, False, False, True, True

It makes no difference whether initialisation periods not spanning an entire year contain the 29th of February:

>>> pub.timegrids = "2001-02-27", "2001-03-01", "1d"
>>> print_vector(TOY.centred_timegrid()[1][31+28-3-1:31+28+3-1])
False, False, True, True, True, False
>>> pub.timegrids = "2000-02-27", "2000-03-01", "1d"
>>> print_vector(TOY.centred_timegrid()[1][31+28-3-1:31+28+3-1])
False, False, True, True, True, False
hydpy.core.timetools.TOY0 = TOY("1_1_0_0_0")

The very first time of the year.