# 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)

  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

  [WikiISO8601]

  https://en.wikipedia.org/wiki/ISO_8601

  [PyStrptime]

  https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior

  [PyTime]

  https://docs.python.org/3/library/time.html

  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)

  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)

  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()

    starts the timer

    Returns:

      self

    Return type:

      Timer

  toc()

    stops the timer

    Returns:

      number of second or nanoseconds

    Return type:

      float | int