{"id":16489586,"url":"https://github.com/alvinwan/timefhuman","last_synced_at":"2025-05-16T17:06:44.380Z","repository":{"id":32817041,"uuid":"143509052","full_name":"alvinwan/timefhuman","owner":"alvinwan","description":"Extract datetimes and durations from natural language text as Python objects. Supports ranges, lists, and more.","archived":false,"fork":false,"pushed_at":"2025-02-24T02:39:32.000Z","size":143,"stargazers_count":106,"open_issues_count":4,"forks_count":17,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T15:58:42.704Z","etag":null,"topics":["date-parser","datetime","datetime-inputs","nlp","python3"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/timefhuman/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alvinwan.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,"zenodo":null}},"created_at":"2018-08-04T07:49:10.000Z","updated_at":"2025-04-09T08:51:45.000Z","dependencies_parsed_at":"2025-01-21T22:00:29.759Z","dependency_job_id":"995b98a7-aaf0-4f91-80c9-d22bc57fd355","html_url":"https://github.com/alvinwan/timefhuman","commit_stats":{"total_commits":37,"total_committers":6,"mean_commits":6.166666666666667,"dds":0.3513513513513513,"last_synced_commit":"ae31f38fca070b241ae9849d871a8ba5ea4a7d1e"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinwan%2Ftimefhuman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinwan%2Ftimefhuman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinwan%2Ftimefhuman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinwan%2Ftimefhuman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alvinwan","download_url":"https://codeload.github.com/alvinwan/timefhuman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254573588,"owners_count":22093731,"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":["date-parser","datetime","datetime-inputs","nlp","python3"],"created_at":"2024-10-11T13:44:38.585Z","updated_at":"2025-05-16T17:06:44.360Z","avatar_url":"https://github.com/alvinwan.png","language":"Python","readme":"# timefhuman\n\n[![PyPi Downloads per Month](https://img.shields.io/pypi/dm/timefhuman.svg)](https://pypi.python.org/pypi/timefhuman/)\n[![Coverage Status](https://coveralls.io/repos/github/alvinwan/timefhuman/badge.svg?branch=master)](https://coveralls.io/github/alvinwan/timefhuman?branch=master)\n\nExtract datetimes, datetime ranges, and datetime lists from natural language text. Supports Python3+[^1].\n\n✨ Try the [demo](https://pytwiddle.com/app/?id=example%3Adatetime.py) or [code](https://pytwiddle.com/?id=example%3Adatetime.py) online. No installation needed. ✨\n\n[^1]: https://github.com/alvinwan/timefhuman/issues/3\n\n----\n\n## Getting Started\n\nInstall with pip using\n\n```shell\n$ pip install timefhuman\n```\n\nThen, find natural language dates and times in any text.\n\n```python\n# All these tests assume the current date is August 4, 2018 at 2 PM\n\u003e\u003e\u003e from timefhuman import timefhuman\n\n\u003e\u003e\u003e timefhuman(\"How does 5p mon sound? Or maybe 4p tu?\")\n[datetime.datetime(2018, 8, 6, 17, 0), datetime.datetime(2018, 8, 7, 16, 0)]\n```\n\nThe text can contain not only datetimes but also ranges of datetimes or lists of datetimes.\n\n```python\n\u003e\u003e\u003e timefhuman('3p-4p')  # time range\n[(datetime.datetime(2018, 8, 4, 15, 0), datetime.datetime(2018, 8, 4, 16, 0))]\n\n\u003e\u003e\u003e timefhuman('7/17 4PM to 7/17 5PM')  # range of datetimes\n[(datetime.datetime(2018, 7, 17, 16, 0), datetime.datetime(2018, 7, 17, 17, 0))]\n\n\u003e\u003e\u003e timefhuman('Monday 3 pm or Tu noon')  # list of datetimes\n[[datetime.datetime(2018, 8, 6, 15, 0), datetime.datetime(2018, 8, 7, 12, 0)]]\n\n\u003e\u003e\u003e timefhuman('7/17 4-5 or 5-6 PM')  # list of ranges of datetimes!\n[[(datetime.datetime(2018, 7, 17, 16, 0), datetime.datetime(2018, 7, 17, 17, 0)),\n  (datetime.datetime(2018, 7, 17, 17, 0), datetime.datetime(2018, 7, 17, 18, 0))]]\n```\n\nDurations are also supported.\n\n```python\n\u003e\u003e\u003e timefhuman('30 minutes')  # duration\n[datetime.datetime(2018, 8, 4, 14, 30)]\n\n\u003e\u003e\u003e timefhuman('30-40 mins')  # range of durations\n[(datetime.datetime(2018, 8, 4, 14, 30), datetime.datetime(2018, 8, 4, 14, 40))]\n\n\u003e\u003e\u003e timefhuman('30 or 40m')  # list of durations\n[[datetime.datetime(2018, 8, 4, 14, 30), datetime.datetime(2018, 8, 4, 14, 40)]]\n```\n\nWhen possible, timefhuman will infer any missing information, using context from other datetimes.\n\n```python\n\u003e\u003e\u003e timefhuman('3-4p')  # infer \"PM\" for \"3\"\n[(datetime.datetime(2018, 8, 4, 15, 0), datetime.datetime(2018, 8, 4, 16, 0))]\n\n\u003e\u003e\u003e timefhuman('7/17 4 or 5 PM')  # infer \"PM\" for \"4\" and infer \"7/17\" for \"5 PM\"\n[[datetime.datetime(2018, 7, 17, 16, 0), datetime.datetime(2018, 7, 17, 17, 0)]]\n\n\u003e\u003e\u003e timefhuman('7/17, 7/18, 7/19 at 9')  # infer \"9a\" for \"7/17\", \"7/18\"\n[[datetime.datetime(2018, 7, 17, 9, 0), datetime.datetime(2018, 7, 18, 9, 0),\n  datetime.datetime(2018, 7, 19, 9, 0)]]\n\n\u003e\u003e\u003e timefhuman('3p -4p PDT')  # infer timezone \"PDT\" for \"3p\"\n[(datetime.datetime(2018, 8, 4, 15, 0, tzinfo=\u003cDstTzInfo 'US/Pacific' ...\u003e),\n  datetime.datetime(2018, 8, 4, 16, 0, tzinfo=\u003cDstTzInfo 'US/Pacific' ...\u003e))]\n```\n\nYou can also use natural language descriptions of dates and times.\n\n```python\n\u003e\u003e\u003e timefhuman('next Monday')\n[datetime.datetime(2018, 8, 6, 0, 0)]\n\n\u003e\u003e\u003e timefhuman('next next Monday')\n[datetime.datetime(2018, 8, 13, 0, 0)]\n\n\u003e\u003e\u003e timefhuman('last Wednesday of December')\n[datetime.datetime(2018, 12, 26, 0, 0)]\n\n\u003e\u003e\u003e timefhuman('afternoon')\n[datetime.datetime(2018, 8, 4, 15, 0)]\n```\n\nSee more examples in [`tests/test_e2e.py`](tests/test_e2e.py).\n\n## Advanced Usage\n\nFor more configuration options, simply create a `tfhConfig` object.\n\n```python\nfrom timefhuman import tfhConfig\nconfig = tfhConfig()\n```\n\n**Return matched text**: You can additionally grab the matched text from the input string, as well as the string indices of the matched substring. This is useful for modifying the input string, for example. \n\n```python\n\u003e\u003e\u003e config = tfhConfig(return_matched_text=True, now=datetime.datetime(2025, 2, 23))\n\n\u003e\u003e\u003e timefhuman('We could maybe do 3 PM, if you still have time', config=config)\n[('3 PM', (18, 22), datetime.datetime(2025, 2, 23, 15, 0))]\n```\n\n**Change 'Now'**: You can configure the default date that timefhuman uses to fill in missing information. This would be useful if you're extracting dates from an email sent a year ago.\n\n```python\n\u003e\u003e\u003e config = tfhConfig(now=datetime.datetime(2018, 8, 4, 0, 0))\n\n\u003e\u003e\u003e timefhuman('upcoming Monday noon', config=config)\n[datetime.datetime(2018, 8, 6, 12, 0)]\n```\n\nYou can also set a default timezone, by again using the config's `now`.\n\n```python\n\u003e\u003e\u003e config = tfhConfig(\n...     now=datetime.datetime(2018, 8, 4, tzinfo=pytz.timezone('US/Pacific'))\n... )\n\n\u003e\u003e\u003e timefhuman('Wed', config=config)\n[datetime.datetime(2018, 8, 8, 0, 0, tzinfo=\u003cDstTzInfo 'US/Pacific' ...\u003e)]\n\n\u003e\u003e\u003e timefhuman('Wed EST', config=config)  # EST timezone in the input takes precedence\n[datetime.datetime(2018, 8, 8, 0, 0, tzinfo=\u003cDstTzInfo 'US/Michigan' ...\u003e)]\n```\n\n**Use explicit information only**: Say you only want to extract *dates* OR *times* OR *timedeltas*. You don't want the library to infer information. You can disable most inference by setting `infer_datetimes=False`. Instead of always returning a datetime, timefhuman will be able to return date, time, or timedelta objects depending on what's provided.\n\n```python\n\u003e\u003e\u003e config = tfhConfig(infer_datetimes=False)\n\n\u003e\u003e\u003e timefhuman('3 PM', config=config)  # time\n[datetime.time(15, 0)]\n\n\u003e\u003e\u003e timefhuman('12/18/18', config=config)  # date\n[datetime.date(2018, 12, 18)]\n\n\u003e\u003e\u003e timefhuman('30 minutes', config=config)  # duration\n[datetime.timedelta(seconds=1800)]\n```\n\n**Past datetimes**: By default, datetimes are assumed to occur in the future, so if \"3pm\" today has already passed, the returned datetime will be for *tomorrow*. However, if datetimes are assumed to have occurred in the past (e.g., from an old letter, talking about past events), you can configure the direction.\n\n```python\n\u003e\u003e\u003e from timefhuman import Direction\n\u003e\u003e\u003e config = tfhConfig(direction=Direction.previous, now=datetime.datetime(2018, 8, 4, 14))\n\n\u003e\u003e\u003e timefhuman('3PM')  # the default\n[datetime.datetime(2018, 8, 4, 15, 0)]\n\n\u003e\u003e\u003e timefhuman('3PM', config=config)  # changing direction\n[datetime.datetime(2018, 8, 3, 15, 0)]\n```\n\n**Change global defaults**: You can also modify the default configuration used by timefhuman, if you don't want to manually pass in configs everywhere.\n\n```python\n\u003e\u003e\u003e from timefhuman import DEFAULT_CONFIG\n\u003e\u003e\u003e DEFAULT_CONFIG.now = datetime.datetime(2025, 2, 23, 12, 0, 0)\n\n\u003e\u003e\u003e timefhuman('3PM')\n[datetime.datetime(2025, 2, 23, 15, 0)]\n```\n\nHere is the full set of supported configuration options:\n\n```python\n@dataclass\nclass tfhConfig:\n    # Default to the next valid datetime or the previous one\n    direction: Direction = Direction.next\n    \n    # Always produce datetime objects. If no date, use the current date. If no time,\n    # use midnight. If timedelta, add it to the current datetime. Still allows ranges\n    # (tuples) of datetimes and lists of datetimes.\n    infer_datetimes: bool = True\n    \n    # The 'current' datetime, used if infer_datetimes is True\n    now: datetime | None = None\n    \n    # Return the matched text from the input string\n    return_matched_text: bool = False\n```\n\n## Development\n\nInstall the development version.\n\n```shell\n$ pip install -e .\\[test\\]  # for zsh\n```\n\nTo run tests and simultaneously generate a coverage report, use the following commands:\n\n```shell\n$ py.test --cov --cov-report=html\n$ open htmlcov/index.html\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvinwan%2Ftimefhuman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falvinwan%2Ftimefhuman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvinwan%2Ftimefhuman/lists"}