ubelt.util_time module

This is util_time, it contains functions for handling time related code.

The timestamp() function returns an iso8601 timestamp without much fuss.

The timeparse() is the inverse of timestamp, and makes use of dateutil if it is available.

The Timer class is a context manager that times a block of indented code. It includes tic and toc methods a more matlab like feel.

Timerit is gone! Use the standalone and separate module timerit.

See also

tempora - https://github.com/jaraco/tempora - time related utility functions from Jaraco

pendulum - https://github.com/sdispater/pendulum - drop in replacement for datetime

arrow - https://github.com/arrow-py/arrow

kwutil.util_time - https://kwutil.readthedocs.io/en/latest/auto/kwutil.util_time.html

ubelt.util_time.timestamp(datetime=None, precision=0, default_timezone='local', allow_dateutil=True)[source]

Make a concise iso8601 timestamp suitable for use in filenames.

Parameters:
  • datetime (datetime.datetime | datetime.date | None) – A datetime to format into a timestamp. If unspecified, the current local time is used. If given as a date, the time 00:00 is used.

  • precision (int) – if non-zero, adds up to 6 digits of sub-second precision.

  • default_timezone (str | datetime.timezone) – if the input does not specify a timezone, assume this one. Can be “local” or “utc”, or a standardized code if dateutil is installed.

  • allow_dateutil (bool) – if True, will use dateutil to lookup the default timezone if needed

Returns:

The timestamp, which will always contain a date, time, and timezone.

Return type:

str

Note

For more info see [WikiISO8601], [PyStrptime], [PyTime].

References

Example

>>> import ubelt as ub
>>> stamp = ub.timestamp()
>>> print('stamp = {!r}'.format(stamp))
stamp = ...-...-...T...

Example

>>> import ubelt as ub
>>> import datetime as datetime_mod
>>> from datetime import datetime as datetime_cls
>>> # Create a datetime object with timezone information
>>> ast_tzinfo = datetime_mod.timezone(datetime_mod.timedelta(hours=-4), 'AST')
>>> datetime = datetime_cls.utcfromtimestamp(123456789.123456789).replace(tzinfo=ast_tzinfo)
>>> stamp = ub.timestamp(datetime, precision=2)
>>> print('stamp = {!r}'.format(stamp))
stamp = '1973-11-29T213309.12-4'
>>> # Demo with a fractional hour timezone
>>> act_tzinfo = datetime_mod.timezone(datetime_mod.timedelta(hours=+9.5), 'ACT')
>>> datetime = datetime_cls.utcfromtimestamp(123456789.123456789).replace(tzinfo=act_tzinfo)
>>> stamp = ub.timestamp(datetime, precision=2)
>>> print('stamp = {!r}'.format(stamp))
stamp = '1973-11-29T213309.12+0930'
>>> # Can accept datetime or date objects with local, utc, or custom default timezones
>>> act_tzinfo = datetime_mod.timezone(datetime_mod.timedelta(hours=+9.5), 'ACT')
>>> datetime_utc = ub.timeparse('2020-03-05T112233', default_timezone='utc')
>>> datetime_act = ub.timeparse('2020-03-05T112233', default_timezone=act_tzinfo)
>>> datetime_notz = datetime_utc.replace(tzinfo=None)
>>> date = datetime_utc.date()
>>> stamp_utc = ub.timestamp(datetime_utc)
>>> stamp_act = ub.timestamp(datetime_act)
>>> stamp_date_utc = ub.timestamp(date, default_timezone='utc')
>>> print(f'stamp_utc      = {stamp_utc}')
>>> print(f'stamp_act      = {stamp_act}')
>>> print(f'stamp_date_utc = {stamp_date_utc}')
stamp_utc      = 2020-03-05T112233+0
stamp_act      = 2020-03-05T112233+0930
stamp_date_utc = 2020-03-05T000000+0

Example

>>> # xdoctest: +REQUIRES(module:dateutil)
>>> # Make sure we are compatible with dateutil
>>> import ubelt as ub
>>> from dateutil.tz import tzlocal
>>> import datetime as datetime_mod
>>> from datetime import datetime as datetime_cls
>>> tz_act = datetime_mod.timezone(datetime_mod.timedelta(hours=+9.5), 'ACT')
>>> tzinfo_list = [
>>>     tz_act,
>>>     datetime_mod.timezone(datetime_mod.timedelta(hours=-4), 'AST'),
>>>     datetime_mod.timezone(datetime_mod.timedelta(hours=0), 'UTC'),
>>>     datetime_mod.timezone.utc,
>>>     None,
>>>     tzlocal()
>>> ]
>>> # Note: there is a win32 bug here
>>> # https://bugs.python.org/issue37 that means we cant use
>>> # dates close to the epoch
>>> datetime_list = [
>>>     datetime_cls.utcfromtimestamp(123456789.123456789 + 315360000),
>>>     datetime_cls.utcfromtimestamp(0 + 315360000),
>>> ]
>>> basis = {
>>>     'precision': [0, 3, 9],
>>>     'tzinfo': tzinfo_list,
>>>     'datetime': datetime_list,
>>>     'default_timezone': ['local', 'utc', tz_act],
>>> }
>>> for params in ub.named_product(basis):
>>>     dtime = params['datetime'].replace(tzinfo=params['tzinfo'])
>>>     precision = params.get('precision', 0)
>>>     stamp = ub.timestamp(datetime=dtime, precision=precision)
>>>     recon = ub.timeparse(stamp)
>>>     alt = recon.strftime('%Y-%m-%dT%H%M%S.%f%z')
>>>     print('---')
>>>     print('params = {}'.format(ub.repr2(params, nl=1)))
>>>     print(f'dtime={dtime}')
>>>     print(f'stamp={stamp}')
>>>     print(f'recon={recon}')
>>>     print(f'alt  ={alt}')
>>>     shift = 10 ** precision
>>>     a = int(dtime.timestamp() * shift)
>>>     b = int(recon.timestamp() * shift)
>>>     assert a == b, f'{a} != {b}'
ubelt.util_time.timeparse(stamp, default_timezone='local', allow_dateutil=True)[source]

Create a datetime.datetime object from a string timestamp.

Without any extra dependencies this will parse the output of ubelt.util_time.timestamp() into a datetime object. In the case where the format differs, dateutil.parser.parse() will be used if the python-dateutil package is installed.

Parameters:
  • stamp (str) – a string encoded timestamp

  • default_timezone (str) – if the input does not specify a timezone, assume this one. Can be “local” or “utc”.

  • allow_dateutil (bool) – if False we only use the minimal parsing and do not allow a fallback to dateutil.

Returns:

the parsed datetime

Return type:

datetime.datetime

Raises:

ValueError – if if parsing fails.

Todo

  • [ ] Allow defaulting to local or utm timezone (currently default is local)

Example

>>> import ubelt as ub
>>> # Demonstrate a round trip of timestamp and timeparse
>>> stamp = ub.timestamp()
>>> datetime = ub.timeparse(stamp)
>>> assert ub.timestamp(datetime) == stamp
>>> # Round trip with precision
>>> stamp = ub.timestamp(precision=4)
>>> datetime = ub.timeparse(stamp)
>>> assert ub.timestamp(datetime, precision=4) == stamp

Example

>>> import ubelt as ub
>>> # We should always be able to parse these
>>> good_stamps = [
>>>     '2000-11-22',
>>>     '2000-11-22T111111.44444Z',
>>>     '2000-11-22T111111.44444+5',
>>>     '2000-11-22T111111.44444-05',
>>>     '2000-11-22T111111.44444-0500',
>>>     '2000-11-22T111111.44444+0530',
>>>     '2000-11-22T111111Z',
>>>     '2000-11-22T111111+5',
>>>     '2000-11-22T111111+0530',
>>> ]
>>> for stamp in good_stamps:
>>>     print(f'----')
>>>     print(f'stamp={stamp}')
>>>     result = ub.timeparse(stamp, allow_dateutil=0)
>>>     print(f'result={result!r}')
>>>     recon = ub.timestamp(result)
>>>     print(f'recon={recon}')

Example

>>> import ubelt as ub
>>> # We require dateutil to handle these types of stamps
>>> import pytest
>>> conditional_stamps = [
>>>         '2000-01-02T11:23:58.12345+5:30',
>>>         '09/25/2003',
>>>         'Thu Sep 25 10:36:28 2003',
>>> ]
>>> for stamp in conditional_stamps:
>>>     with pytest.raises(ValueError):
>>>         result = ub.timeparse(stamp, allow_dateutil=False)
>>> have_dateutil = bool(ub.modname_to_modpath('dateutil'))
>>> if have_dateutil:
>>>     for stamp in conditional_stamps:
>>>         result = ub.timeparse(stamp)
class ubelt.util_time.Timer(label='', verbose=None, newline=True, ns=False)[source]

Bases: object

Measures time elapsed between a start and end point. Can be used as a with-statement context manager, or using the tic/toc api.

Variables:
  • elapsed (float) – number of seconds measured by the context manager

  • tstart (float) – time of last tic reported by self._time()

  • write (Callable) – function used to write

  • flush (Callable) – function used to flush

Example

>>> # Create and start the timer using the context manager
>>> import math
>>> import ubelt as ub
>>> timer = ub.Timer('Timer test!', verbose=1)
>>> with timer:
>>>     math.factorial(10)
>>> assert timer.elapsed > 0
tic('Timer test!')
...toc('Timer test!')=...

Example

>>> # Create and start the timer using the tic/toc interface
>>> import ubelt as ub
>>> timer = ub.Timer().tic()
>>> elapsed1 = timer.toc()
>>> elapsed2 = timer.toc()
>>> elapsed3 = timer.toc()
>>> assert elapsed1 <= elapsed2
>>> assert elapsed2 <= elapsed3

Example

>>> # In Python 3.7+ nanosecond resolution can be enabled
>>> import ubelt as ub
>>> import sys
>>> if sys.version_info[0:2] <= (3, 6):
>>>     import pytest
>>>     pytest.skip()
>>> # xdoctest +REQUIRES(Python>=3.7)  # fixme directive doesnt exist yet
>>> timer = ub.Timer(label='perf_counter_ns', ns=True).tic()
>>> elapsed1 = timer.toc()
>>> elapsed2 = timer.toc()
>>> assert elapsed1 <= elapsed2
>>> assert isinstance(elapsed1, int)
Parameters:
  • label (str) – identifier for printing. Default to ‘’.

  • verbose (int | None) – verbosity flag, defaults to True if label is given, otherwise 0.

  • newline (bool) – if False and verbose, print tic and toc on the same line. Defaults to True.

  • ns (bool) – if True, a nano-second resolution timer to avoid precision loss caused by the float type. Defaults to False.

_default_time()

perf_counter() -> float

Performance counter for benchmarking.

tic()[source]

starts the timer

Returns:

self

Return type:

Timer

toc()[source]

stops the timer

Returns:

number of second or nanoseconds

Return type:

float | int