{"id":24300097,"url":"https://github.com/sun-lab-nbb/ataraxis-time","last_synced_at":"2026-02-17T04:13:22.429Z","repository":{"id":244975094,"uuid":"815713480","full_name":"Sun-Lab-NBB/ataraxis-time","owner":"Sun-Lab-NBB","description":"Python library that provides a sub-microsecond-precise thread-safe timer and helper methods to work with date and time data.","archived":false,"fork":false,"pushed_at":"2024-11-25T00:33:49.000Z","size":245,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-12-25T02:33:55.201Z","etag":null,"topics":["ataraxis","blocking","date","delay","elapsed","non-blocking","sub-microsecond-precision","time","time-conversion","timer"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Sun-Lab-NBB.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-15T23:23:38.000Z","updated_at":"2024-11-25T00:20:24.000Z","dependencies_parsed_at":"2024-06-19T23:13:41.341Z","dependency_job_id":"2ff30f41-7928-45f3-acd6-2dae39273de9","html_url":"https://github.com/Sun-Lab-NBB/ataraxis-time","commit_stats":null,"previous_names":["sun-lab-nbb/ataraxistime","sun-lab-nbb/ataraxis-time"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-time","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-time/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-time/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Sun-Lab-NBB%2Fataraxis-time/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Sun-Lab-NBB","download_url":"https://codeload.github.com/Sun-Lab-NBB/ataraxis-time/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234285274,"owners_count":18808315,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ataraxis","blocking","date","delay","elapsed","non-blocking","sub-microsecond-precision","time","time-conversion","timer"],"created_at":"2025-01-16T22:38:44.668Z","updated_at":"2026-02-17T04:13:22.406Z","avatar_url":"https://github.com/Sun-Lab-NBB.png","language":"Python","readme":"# ataraxis-time\n\nProvides a high-precision thread-safe timer and helper methods to work with date and time data.\n\n![PyPI - Version](https://img.shields.io/pypi/v/ataraxis-time)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ataraxis-time)\n[![uv](https://tinyurl.com/uvbadge)](https://github.com/astral-sh/uv)\n[![Ruff](https://tinyurl.com/ruffbadge)](https://github.com/astral-sh/ruff)\n![type-checked: mypy](https://img.shields.io/badge/type--checked-mypy-blue?style=flat-square\u0026logo=python)\n![PyPI - License](https://img.shields.io/pypi/l/ataraxis-time)\n![PyPI - Status](https://img.shields.io/pypi/status/ataraxis-time)\n![PyPI - Wheel](https://img.shields.io/pypi/wheel/ataraxis-time)\n\n___\n\n## Detailed Description\n\nThis library uses the 'chrono' C++ library to access the fastest available system clock and use it to provide interval\ntiming, delay, timeout, and polling functionality via a Python binding API. While the performance of the timer heavily\ndepends on the particular system configuration and utilization, most modern CPUs should be capable of microsecond\nprecision using this timer. Due to using a C-extension to provide interval and delay timing functionality, the library\nis thread- and process-safe and releases the GIL when using the appropriate delay command configuration. Additionally,\nthe library offers a set of standalone helper functions for manipulating date and time data, including timestamp\ngeneration and parsing, time-unit conversion, rate-interval conversion, and timedelta interoperability.\n\n## Features\n\n- Supports Windows, Linux, and macOS.\n- Microsecond precision on modern CPUs (~ 3 GHz+) during delay and interval timing.\n- Releases GIL during (non-blocking) delay timing even when using microsecond and nanosecond precision.\n- Timeout guard class for activity-based and duration-based timeout tracking.\n- Lap recording, human-readable elapsed time formatting, and periodic polling via an infinite generator.\n- Frequency-to-interval and interval-to-frequency conversion helpers.\n- Timestamp generation, conversion, and parsing with configurable precision levels.\n- Interoperability with Python datetime.timedelta objects.\n- Apache 2.0 License.\n\n## Table of Contents\n\n- [Dependencies](#dependencies)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Precision Timer](#precision-timer)\n  - [Timeout](#timeout)\n  - [Date and Time Helper Functions](#date-and-time-helper-functions)\n- [API Documentation](#api-documentation)\n- [Developers](#developers)\n- [Versioning](#versioning)\n- [Authors](#authors)\n- [License](#license)\n- [Acknowledgments](#acknowledgments)\n\n___\n\n## Dependencies\n\nFor users, all library dependencies are installed automatically by all supported installation\nmethods. For developers, see the [Developers](#developers) section for information on installing\nadditional development dependencies.\n\n___\n\n## Installation\n\n### Source\n\n***Note,*** installation from source is ***highly discouraged*** for anyone who is not an active\nproject developer.\n\n1. Download this repository to the local machine using the preferred method, such as git-cloning.\n   Use one of the [stable releases](https://github.com/Sun-Lab-NBB/ataraxis-time/tags) that\n   include precompiled binary and source code distribution (sdist) wheels.\n2. If the downloaded distribution is stored as a compressed archive, unpack it using the\n   appropriate decompression tool.\n3. `cd` to the root directory of the prepared project distribution.\n4. Run `pip install .` to install the project and its dependencies.\n\n### pip\n\nUse the following command to install the library and all of its dependencies via\n[pip](https://pip.pypa.io/en/stable/): `pip install ataraxis-time`\n\n___\n\n## Usage\n\n### Precision Timer\n\nThe timer API is intentionally minimalistic to simplify class adoption and usage. It is heavily inspired by the\n[elapsedMillis](https://github.com/pfeerick/elapsedMillis/blob/master/elapsedMillis.h) library for\nTeensy and Arduino microcontrollers.\n\nAll timer class functionality is realized through a fast C-extension class wrapped into the PrecisionTimer class.\n\n#### Initialization and Configuration\n\nThe timer takes the 'precision' to use as the only initialization argument. All instances of the timer class are\nthread- and process-safe and do not interfere with each other.\n\n```python\nfrom ataraxis_time import PrecisionTimer, TimerPrecisions\n\n# Currently, the timer supports 4 precisions: 'ns' (nanoseconds), 'us' (microseconds), 'ms' (milliseconds), and\n# 's' (seconds). All precisions are defined in the TimerPrecisions enumeration.\ntimer = PrecisionTimer(TimerPrecisions.MICROSECOND)\nprint(f\"Precision: {timer.precision}\")\n\n# The precision can be adjusted after initialization if needed. While not recommended, it is possible to provide the\n# precision as a string instead of using the TimerPrecisions enumeration.\ntimer.set_precision('ms')  # Switches timer precision to milliseconds\nprint(f\"Precision: {timer.precision}\")\n```\n\n#### Interval Timing\n\nInterval timing functionality is realized through two methods: reset() and the elapsed property. This functionality is\nidentical to using perf_counter_ns() from the 'time' library. The main difference is that PrecisionTimer uses a\nslightly different interface (reset / elapsed) and automatically converts the output to the desired precision.\n\n```python\nfrom ataraxis_time import PrecisionTimer\nimport time as tm\n\ntimer = PrecisionTimer('us')\n\n# Interval timing example\ntimer.reset()  # Resets (re-bases) the timer\ntm.sleep(1)  # Simulates work (for 1 second)\nprint(f'Work time: {timer.elapsed} us')\n```\n\n#### Delay\n\nDelay timing functionality is the primary advantage of this library over the standard 'time' library. At the time of\nwriting, the 'time' library can provide nanosecond-precise delays via a 'busywait' perf_counter_ns() function that does\nnot release the GIL. Alternatively, it can release the GIL via the sleep() function, but it is only accurate\nup to millisecond precision. The PrecisionTimer class can delay for time-periods within microsecond precision, while\nreleasing or holding the GIL.\n\n```python\nimport threading\nimport time\nfrom ataraxis_time import PrecisionTimer\n\n# Instantiates a global counter for the background thread\ncounter = 0\nstop = False\n\n\ndef count_in_background():\n    \"\"\"Background thread that increments the global counter.\"\"\"\n    global counter\n    while not stop:\n        counter += 1\n\n\n# Setup\ntimer = PrecisionTimer('us')\n\n# Starts the background counter thread\nthread = threading.Thread(target=count_in_background, daemon=True)\nthread.start()\ntime.sleep(0.1)\n\n# GIL-releasing microsecond delay:\nprint(\"block=False (releases GIL):\")\ncounter = 0  # Resets the counter\ntimer.delay(100, block=False)  # 100us delay\nnon_blocking_count = counter\nprint(f\"counter = {counter}\")\n\n# Non-GIL-releasing microsecond delay:\nprint(\"block=True (holds GIL):\")\ncounter = 0  # Resets the counter\ntimer.delay(100, block=True)  # 100us delay\nblocking_count = counter\nprint(f\"counter = {counter}\")\n\n# Cleanup\nstop = True\n\n# With microsecond precisions, blocking runtime often results in the counter not being incremented at all.\nif blocking_count == 0:\n    blocking_count = 1\nprint(f\"Difference: block=False allows ~{non_blocking_count/blocking_count:.0f}x more counting!\")\nthread.join()\n```\n\n#### Lap Timing\n\nThe lap() method records the current elapsed time, appends it to an internal list, and resets the timer. All recorded\nlap times are accessible through the laps property.\n\n```python\nfrom ataraxis_time import PrecisionTimer\nimport time as tm\n\ntimer = PrecisionTimer('ms')\n\n# Records three laps\nfor i in range(3):\n    tm.sleep(0.1)  # Simulates work\n    duration = timer.lap()\n    print(f\"Lap {i + 1}: {duration} ms\")\n\n# Retrieves all recorded laps as a tuple\nprint(f\"All laps: {timer.laps}\")\n```\n\n#### Formatted Elapsed Time\n\nThe format_elapsed() method returns the current elapsed time as a human-readable string, automatically selecting the\nmost appropriate units.\n\n```python\nfrom ataraxis_time import PrecisionTimer\nimport time as tm\n\ntimer = PrecisionTimer('us')\ntm.sleep(2.5)  # Simulates work\nprint(f\"Elapsed: {timer.format_elapsed()}\")  # e.g. \"2 s 500.117 ms\"\nprint(f\"Detailed: {timer.format_elapsed(max_fields=3)}\")  # e.g. \"2 s 500 ms 117.0 us\"\n```\n\n#### Polling\n\nThe poll() method provides an infinite generator that yields an iteration count after each delay cycle. This is useful\nfor periodic task execution.\n\n```python\nfrom ataraxis_time import PrecisionTimer\n\ntimer = PrecisionTimer('ms')\n\n# Polls every 100 ms, runs 10 iterations\nfor count in timer.poll(100):\n    print(f\"Iteration {count}\")\n    if count \u003e= 10:\n        break\n```\n\n### Timeout\n\nThe Timeout class provides a timeout guard built on PrecisionTimer. It supports checking whether a specified duration\nhas elapsed and offers activity-based reset (kick) and full reset with optional duration changes.\n\n```python\nfrom ataraxis_time import Timeout\nimport time as tm\n\n# Creates a 500 ms timeout\ntimeout = Timeout(duration=500, precision='ms')\n\n# Checks timeout status\ntm.sleep(0.1)\nprint(f\"Expired: {timeout.expired}\")  # False\nprint(f\"Remaining: {timeout.remaining} ms\")\nprint(f\"Elapsed: {timeout.elapsed} ms\")\n\n# Resets the timeout timer without changing the duration (activity-based reset)\ntimeout.kick()\n\n# Resets the timeout with a new duration\ntimeout.reset(duration=1000)\n```\n\n### Date and Time Helper Functions\n\nThese are helper functions that are not directly part of the timer classes showcased above. Since these functions\nare not intended for realtime applications, they are implemented entirely in Python.\n\n#### Convert Time\n\nThis helper function performs time-conversions, rounding to 3 decimal places, and works with time-scales from\nnanoseconds to days.\n\n```python\nfrom ataraxis_time import convert_time, TimeUnits\n\n# The conversion works for Python and NumPy scalars. Use the TimeUnits enumeration to specify input and\n# output units. By default, the method returns the converted data as NumPy 64-bit floating scalars.\ninitial_time = 12\ntime_in_seconds = convert_time(time=initial_time, from_units=TimeUnits.DAY, to_units=TimeUnits.SECOND)\nprint(f\"12 days is {time_in_seconds} seconds.\")\n\n# It is possible to provide the units directly, instead of using the TimeUnits enumeration. Also,\n# it is possible to instruct the function to return Python floats.\ninitial_time = 5\ntime_in_minutes = convert_time(time=initial_time, from_units=\"s\", to_units=\"m\", as_float=True)\nprint(f\"5 seconds is {time_in_minutes} minutes.\")\n```\n\n#### Rate and Interval Conversion\n\nThe rate_to_interval() and interval_to_rate() functions convert between frequencies (Hz) and time intervals.\n\n```python\nfrom ataraxis_time import rate_to_interval, interval_to_rate, TimeUnits\n\n# Converts a 30 Hz frequency to a microsecond interval\ninterval_us = rate_to_interval(rate=30, to_units=TimeUnits.MICROSECOND)\nprint(f\"30 Hz = {interval_us} us interval\")\n\n# Converts a 1000 us interval back to Hz\nrate_hz = interval_to_rate(interval=1000, from_units=TimeUnits.MICROSECOND)\nprint(f\"1000 us = {rate_hz} Hz\")\n```\n\n#### Timedelta Interoperability\n\nThe to_timedelta() and from_timedelta() functions convert between numeric time values and Python datetime.timedelta\nobjects.\n\n```python\nfrom ataraxis_time import to_timedelta, from_timedelta, TimeUnits\n\n# Converts 500 milliseconds to a timedelta\ntd = to_timedelta(time=500, from_units=TimeUnits.MILLISECOND)\nprint(f\"500 ms as timedelta: {td}\")\n\n# Converts a timedelta back to microseconds\nus_value = from_timedelta(timedelta_value=td, to_units=TimeUnits.MICROSECOND)\nprint(f\"Timedelta as microseconds: {us_value}\")\n```\n\n#### Timestamps\n\nTimestamp methods generate and work with microsecond-precise UTC timestamps. The generated timestamp can be returned\nas and freely converted between three supported formats: string, bytes array, and an integer number of microseconds\nelapsed since the UTC epoch onset. The precision parameter controls how much detail is included in the output.\n\n```python\nfrom ataraxis_time import get_timestamp, convert_timestamp, TimestampFormats, TimestampPrecisions\n\n# Gets the current date and time as a timestamp. The timestamp is precise up to microseconds by default.\n# Use TimestampFormats to specify the desired format.\ndt = get_timestamp(time_separator='-', output_format=TimestampFormats.STRING)\nprint(f\"Current timestamp: {dt}.\")\n\n# Uses the precision parameter to control the detail level of the output.\ndt_day = get_timestamp(output_format=TimestampFormats.STRING, precision=TimestampPrecisions.DAY)\nprint(f\"Day-precision timestamp: {dt_day}.\")\n\n# The function also supports giving the timestamp as a serialized array of bytes. This is helpful when it is used as\n# part of a serialized communication protocol.\nbytes_dt = get_timestamp(output_format=TimestampFormats.BYTES)\nprint(f\"Byte-serialized current timestamp value: {bytes_dt}.\")\n\n# Use the convert_timestamp() function to convert the timestamp to a different format. It supports cross-converting\n# all timestamp formats stored in the TimestampFormats enumeration.\ninteger_dt = convert_timestamp(timestamp=bytes_dt, output_format=TimestampFormats.INTEGER)\nstring_dt = convert_timestamp(timestamp=integer_dt, output_format=TimestampFormats.STRING)\nprint(\n    f\"The timestamp can be read as a string: {string_dt}. It can also be read as the number of microseconds elapsed \"\n    f\"since UTC epoch onset: {integer_dt}.\"\n)\n```\n\n#### Parse Timestamp\n\nThe parse_timestamp() function parses arbitrary datetime strings using strptime-compatible format strings and returns\nthem as timestamps in any supported format.\n\n```python\nfrom ataraxis_time import parse_timestamp, TimestampFormats\n\n# Parses a datetime string into a microsecond integer timestamp\nus_timestamp = parse_timestamp(\n    date_string=\"2024-03-15 14:30:00\",\n    format_string=\"%Y-%m-%d %H:%M:%S\",\n    output_format=TimestampFormats.INTEGER,\n)\nprint(f\"Parsed timestamp: {us_timestamp}\")\n\n# Parses into a string timestamp with day precision\nday_timestamp = parse_timestamp(\n    date_string=\"March 15, 2024\",\n    format_string=\"%B %d, %Y\",\n    output_format=TimestampFormats.STRING,\n    precision=\"day\",\n)\nprint(f\"Day-precision parsed timestamp: {day_timestamp}\")\n```\n\n___\n\n## API Documentation\n\nSee the [API documentation](https://ataraxis-time-api-docs.netlify.app/) for the detailed\ndescription of the methods and classes exposed by components of this library. The documentation\nalso covers the C++ source code and the `axt-benchmark` CLI command.\n\n___\n\n## Developers\n\nThis section provides installation, dependency, and build-system instructions for the developers\nthat want to modify the source code of this library.\n\n### Installing the Project\n\n***Note,*** this installation method requires **mamba version 2.3.2 or above**. Currently, all\nSun lab automation pipelines require that mamba is installed through the\n[miniforge3](https://github.com/conda-forge/miniforge) installer.\n\n1. Download this repository to the local machine using the preferred method, such as git-cloning.\n2. If the downloaded distribution is stored as a compressed archive, unpack it using the\n   appropriate decompression tool.\n3. `cd` to the root directory of the prepared project distribution.\n4. Install the core Sun lab development dependencies into the ***base*** mamba environment via the\n   `mamba install tox uv tox-uv` command.\n5. Use the `tox -e create` command to create the project-specific development environment followed\n   by `tox -e install` command to install the project into that environment as a library.\n\n### Additional Dependencies\n\nIn addition to installing the project and all user dependencies, install the following\ndependencies:\n\n1. [Python](https://www.python.org/downloads/) distributions, one for each version supported by\n   the developed project. Currently, this library supports the three latest stable versions. It is\n   recommended to use a tool like [pyenv](https://github.com/pyenv/pyenv) to install and manage\n   the required versions.\n2. [Doxygen](https://www.doxygen.nl/manual/install.html), if you want to generate C++ code\n   documentation.\n3. An appropriate build tool or Docker, if you intend to build binary wheels via\n   [cibuildwheel](https://cibuildwheel.pypa.io/en/stable/). See the link for information on which\n   dependencies to install for each development platform.\n\n### Development Automation\n\nThis project uses `tox` for development automation. The following tox environments are available:\n\n| Environment          | Description                                                  |\n|----------------------|--------------------------------------------------------------|\n| `lint`               | Runs ruff formatting, ruff linting, and mypy type checking   |\n| `stubs`              | Generates py.typed marker and .pyi stub files                |\n| `{py312,...}-test`   | Runs the test suite via pytest for each supported Python     |\n| `coverage`           | Aggregates test coverage into an HTML report                 |\n| `docs`               | Builds the API documentation via Sphinx                      |\n| `build`              | Builds sdist and wheel distributions                         |\n| `upload`             | Uploads distributions to PyPI via twine                      |\n| `install`            | Builds and installs the project into its mamba environment   |\n| `uninstall`          | Uninstalls the project from its mamba environment            |\n| `create`             | Creates the project's mamba development environment          |\n| `remove`             | Removes the project's mamba development environment          |\n| `provision`          | Recreates the mamba environment from scratch                 |\n| `export`             | Exports the mamba environment as .yml and spec.txt files     |\n| `import`             | Creates or updates the mamba environment from a .yml file    |\n\nRun any environment using `tox -e ENVIRONMENT`. For example, `tox -e lint`.\n\n***Note,*** all pull requests for this project have to successfully complete the `tox` task before\nbeing merged. To expedite the task's runtime, use the `tox --parallel` command to run some tasks\nin parallel.\n\n### Automation Troubleshooting\n\nMany packages used in `tox` automation pipelines (uv, mypy, ruff) and `tox` itself may experience\nruntime failures. In most cases, this is related to their caching behavior. If an unintelligible\nerror is encountered with any of the automation components, deleting the corresponding cache\ndirectories (`.tox`, `.ruff_cache`, `.mypy_cache`, etc.) manually or via a CLI command typically\nresolves the issue.\n\n___\n\n## Versioning\n\nThis project uses [semantic versioning](https://semver.org/). See the\n[tags on this repository](https://github.com/Sun-Lab-NBB/ataraxis-time/tags) for the available\nproject releases.\n\n___\n\n## Authors\n\n- Ivan Kondratyev ([Inkaros](https://github.com/Inkaros))\n\n___\n\n## License\n\nThis project is licensed under the Apache 2.0 License: see the [LICENSE](LICENSE) file for\ndetails.\n\n___\n\n## Acknowledgments\n\n- All Sun lab [members](https://neuroai.github.io/sunlab/people) for providing the inspiration\n  and comments during the development of this library.\n- [elapsedMillis](https://github.com/pfeerick/elapsedMillis/blob/master/elapsedMillis.h) project\n  for providing the inspiration for the API and the functionality of the timer class.\n- [nanobind](https://github.com/wjakob/nanobind) project for providing a fast and convenient way\n  of binding C++ code to Python projects.\n- The creators of all other dependencies and projects listed in the\n  [pyproject.toml](pyproject.toml) file.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsun-lab-nbb%2Fataraxis-time","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsun-lab-nbb%2Fataraxis-time","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsun-lab-nbb%2Fataraxis-time/lists"}