undate


Nameundate JSON
Version 0.5.1 PyPI version JSON
download
home_pageNone
Summarylibrary for working with uncertain, fuzzy, or partially unknown dates and date intervals
upload_time2025-07-22 17:51:29
maintainerNone
docs_urlNone
authorRebecca Sutton Koeser, Cole Crawford, Julia Damerow, Robert Casties, Malte Vogl
requires_python>=3.10
licenseApache-2
keywords dates dating digital-humanities partially-known uncertain-dates uncertainty unknown
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # undate overview

![undate](_static/undate_logo.png)

**undate** is a python library for working with uncertain or partially known dates.

> [!WARNING]
> This is beta software and is not yet feature complete! Use with caution and give us feedback.
> Currently `undate` supports parsing and formatting dates in ISO8601, some
> portions of EDTF (Extended Date Time Format), and parsing and conversion for dates in Hebrew Anno Mundi and Islamic Hijri calendars.

_Undate was initially created as part of a [DH-Tech](https://dh-tech.github.io/) hackathon in November 2022._

---

[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.11068867.svg)](https://doi.org/10.5281/zenodo.11068867)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Documentation Status](https://readthedocs.org/projects/undate-python/badge/?version=latest)](https://undate-python.readthedocs.io/en/latest/?badge=latest)
[![unit tests](https://github.com/dh-tech/undate-python/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/dh-tech/undate-python/actions/workflows/unit_tests.yml)
[![codecov](https://codecov.io/gh/dh-tech/undate-python/branch/main/graph/badge.svg?token=GE7HZE8C9D)](https://codecov.io/gh/dh-tech/undate-python)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->

[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](CONTRIBUTORS.md)

<!-- ALL-CONTRIBUTORS-BADGE:END -->

Read [Contributors](CONTRIBUTORS.md) for detailed contribution information.

## Installation

_Recommended_: use pip to install the latest published version from PyPI:

```console
pip install undate
```

To install a development version or specific tag or branch, you can install from GitHub.
Use the `@name` notation to specify the branch or tag; e.g., to install development version:

```console
pip install git+https://github.com/dh-tech/undate-python@develop#egg=undate
```

## Example Usage

Often humanities and cultural data include imprecise or uncertain
temporal information. We want to store that information but also work
with it in a structured way, not just treat it as text for display.
Different projects may need to work with or convert between different
date formats or even different calendars.

An `undate.Undate` is analogous to python’s builtin `datetime.date`
object, but with support for varying degrees of precision and unknown
information. You can initialize an `Undate` with either strings or
numbers for whichever parts of the date are known or partially known.
An `Undate` can take an optional label.

```python
from undate import Undate

november7 = Undate(2000, 11, 7)
november = Undate(2000, 11)
year2k = Undate(2000)
november7_some_year = Undate(month=11, day=7)

partially_known_year = Undate("19XX")
partially_known_month = Undate(2022, "1X")

easter1916 = Undate(1916, 4, 23, label="Easter 1916")
```

You can convert an `Undate` to string using a date formatter (current default is ISO8601):

```python
>>> [str(d) for d in [november7, november, year2k, november7_some_year]]
['2000-11-07', '2000-11', '2000', '--11-07']
```

If enough information is known, an `Undate` object can report on its duration:

```python
>>> december = Undate(2000, 12)
>>> feb_leapyear = Undate(2024, 2)
>>> feb_regularyear = Undate(2023, 2)
>>> for d in [november7, november, december, year2k, november7_some_year, feb_regularyear, feb_leapyear]:
...    print(f"{d}  - duration in days: {d.duration().days}")
...
2000-11-07  - duration in days: 1
2000-11  - duration in days: 30
2000-12  - duration in days: 31
2000  - duration in days: 366
--11-07  - duration in days: 1
2023-02  - duration in days: 28
2024-02  - duration in days: 29
```

If enough of the date is known and the precision supports it, you can
check if one date falls within another date:

```python
>>> november7 = Undate(2000, 11, 7)
>>> november2000 = Undate(2000, 11)
>>> year2k = Undate(2000)
>>> ad100 = Undate(100)
>>> november7 in november
True
>>> november2000 in year2k
True
>>> november7 in year2k
True
>>> november2000 in ad100
False
>>> november7 in ad100
False
```

For dates that are imprecise or partially known, `undate` calculates
earliest and latest possible dates for comparison purposes so you can
sort dates and compare with equals, greater than, and less than. You
can also compare with python `datetime.date` objects.

```python
>>> november7_2020 = Undate(2020, 11, 7)
>>> november_2001 = Undate(2001, 11)
>>> year2k = Undate(2000)
>>> ad100 = Undate(100)
>>> sorted([november7_2020, november_2001, year2k, ad100])
[<Undate 0100>, <Undate 2000>, <Undate 2001-11>, <Undate 2020-11-07>]
>>> november7_2020 > november_2001
True
>>> year2k < ad100
False
>>> from datetime import date
>>> year2k > date(2001, 1, 1)
False
```

When dates cannot be compared due to ambiguity or precision, comparison
methods raise a `NotImplementedError`.

```python
>>> november_2020 = Undate(2020, 11)
>>> november7_2020 > november_2020
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/rkoeser/workarea/github/undate-python/src/undate/undate.py", line 262, in __gt__
    return not (self < other or self == other)
  File "/Users/rkoeser/workarea/github/undate-python/src/undate/undate.py", line 245, in __lt__
    raise NotImplementedError(
NotImplementedError: Can't compare when one date falls within the other
```

An `UndateInterval` is a date range between two `Undate` objects.
Intervals can be open-ended, allow for optional labels, and can
calculate duration if enough information is known. `UndateIntervals`
are inclusive (i.e., a closed interval), and include both the earliest
and latest date as part of the range.

```python
>>> from undate import UndateInterval
>>> UndateInterval(Undate(1900), Undate(2000))
<UndateInterval 1900/2000>
>>> UndateInterval(Undate(1801), Undate(1900), label="19th century")
>>> UndateInterval(Undate(1801), Undate(1900), label="19th century").duration().days
36524
<UndateInterval '19th century' (1801/1900)>
>>> UndateInterval(Undate(1901), Undate(2000), label="20th century")
<UndateInterval '20th century' (1901/2000)>
>>> UndateInterval(latest=Undate(2000))  # before 2000
<UndateInterval ../2000>
>>> UndateInterval(Undate(1900))  # after 1900
<UndateInterval 1900/>
>>> UndateInterval(Undate(1900), Undate(2000), label="19th century").duration().days
36890
>>> UndateInterval(Undate(2000, 1, 1), Undate(2000, 1,31)).duration().days
31
```

You can initialize `Undate` or `UndateInterval` objects by parsing a
date string with a specific converter, and you can also output an
`Undate` object in those formats. Currently available converters
are "ISO8601" and "EDTF" and supported calendars.

```python
>>> from undate import Undate
>>> Undate.parse("2002", "ISO8601")
<Undate 2002>
>>> Undate.parse("2002-05", "EDTF")
<Undate 2002-05>
>>> Undate.parse("--05-03", "ISO8601")
<Undate --05-03>
>>> Undate.parse("--05-03", "ISO8601").format("EDTF")
'XXXX-05-03'
>>> Undate.parse("1800/1900")
<UndateInterval 1800/1900>
```

### Calendars

All `Undate` objects are calendar aware, and date converters include
support for parsing and working with dates from other calendars. The
Gregorian calendar is used by default; currently `undate` supports the
Islamic Hijri calendar and the Hebrew Anno Mundi calendar based on
calendar conversion logic implemented in the
[convertdate](https://convertdate.readthedocs.io/en/latest/) package.

Dates are stored with the year, month, day and appropriate precision for
the original calendar; internally, earliest and latest dates are
calculated in Gregorian / Proleptic Gregorian calendar for standardized
comparison across dates from different calendars.

```python
>>> from undate import Undate
>>> tammuz4816 = Undate.parse("26 Tammuz 4816", "Hebrew")
>>> tammuz4816
<Undate '26 Tammuz 4816 Anno Mundi' 4816-04-26 (Hebrew)>
>>> rajab495 = Undate.parse("Rajab 495", "Islamic")
>>> rajab495
<Undate 'Rajab 495 Hijrī' 0495-07 (Islamic)>
>>> y2k = Undate.parse("2001", "EDTF")
>>> y2k
<Undate 2001 (Gregorian)>
>>> [str(d.earliest) for d in [rajab495, tammuz4816, y2k]]
['1102-04-28', '1056-07-17', '2001-01-01']
>>> [str(d.precision) for d in [rajab495, tammuz4816, y2k]]
['MONTH', 'DAY', 'YEAR']
>>> sorted([rajab495, tammuz4816, y2k])
[<Undate '26 Tammuz 4816 Anno Mundi' 4816-04-26 (Hebrew)>, <Undate 'Rajab 495 Hijrī' 0495-07 (Islamic)>, <Undate 2001 (Gregorian)>]
```

---

For more examples, refer to the code notebooks included in the[examples]
(https://github.com/dh-tech/undate-python/tree/main/examples/) in this
repository.

## Documentation

Project documentation is [available on ReadTheDocs](https://undate-python.readthedocs.io/en/latest/).

For instructions on setting up for local development, see [Developer Notes](DEVELOPER_NOTES.md).

## License

This software is licensed under the [Apache 2.0 License](LICENSE.md).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "undate",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "dates, dating, digital-humanities, partially-known, uncertain-dates, uncertainty, unknown",
    "author": "Rebecca Sutton Koeser, Cole Crawford, Julia Damerow, Robert Casties, Malte Vogl",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/13/41/49e370e5d1e4cfe36b6d0ceb66382cef71052bff5b567aa114abe3df51db/undate-0.5.1.tar.gz",
    "platform": null,
    "description": "# undate overview\n\n![undate](_static/undate_logo.png)\n\n**undate** is a python library for working with uncertain or partially known dates.\n\n> [!WARNING]\n> This is beta software and is not yet feature complete! Use with caution and give us feedback.\n> Currently `undate` supports parsing and formatting dates in ISO8601, some\n> portions of EDTF (Extended Date Time Format), and parsing and conversion for dates in Hebrew Anno Mundi and Islamic Hijri calendars.\n\n_Undate was initially created as part of a [DH-Tech](https://dh-tech.github.io/) hackathon in November 2022._\n\n---\n\n[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.11068867.svg)](https://doi.org/10.5281/zenodo.11068867)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Documentation Status](https://readthedocs.org/projects/undate-python/badge/?version=latest)](https://undate-python.readthedocs.io/en/latest/?badge=latest)\n[![unit tests](https://github.com/dh-tech/undate-python/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/dh-tech/undate-python/actions/workflows/unit_tests.yml)\n[![codecov](https://codecov.io/gh/dh-tech/undate-python/branch/main/graph/badge.svg?token=GE7HZE8C9D)](https://codecov.io/gh/dh-tech/undate-python)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\n<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->\n\n[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](CONTRIBUTORS.md)\n\n<!-- ALL-CONTRIBUTORS-BADGE:END -->\n\nRead [Contributors](CONTRIBUTORS.md) for detailed contribution information.\n\n## Installation\n\n_Recommended_: use pip to install the latest published version from PyPI:\n\n```console\npip install undate\n```\n\nTo install a development version or specific tag or branch, you can install from GitHub.\nUse the `@name` notation to specify the branch or tag; e.g., to install development version:\n\n```console\npip install git+https://github.com/dh-tech/undate-python@develop#egg=undate\n```\n\n## Example Usage\n\nOften humanities and cultural data include imprecise or uncertain\ntemporal information. We want to store that information but also work\nwith it in a structured way, not just treat it as text for display.\nDifferent projects may need to work with or convert between different\ndate formats or even different calendars.\n\nAn `undate.Undate` is analogous to python\u2019s builtin `datetime.date`\nobject, but with support for varying degrees of precision and unknown\ninformation. You can initialize an `Undate` with either strings or\nnumbers for whichever parts of the date are known or partially known.\nAn `Undate` can take an optional label.\n\n```python\nfrom undate import Undate\n\nnovember7 = Undate(2000, 11, 7)\nnovember = Undate(2000, 11)\nyear2k = Undate(2000)\nnovember7_some_year = Undate(month=11, day=7)\n\npartially_known_year = Undate(\"19XX\")\npartially_known_month = Undate(2022, \"1X\")\n\neaster1916 = Undate(1916, 4, 23, label=\"Easter 1916\")\n```\n\nYou can convert an `Undate` to string using a date formatter (current default is ISO8601):\n\n```python\n>>> [str(d) for d in [november7, november, year2k, november7_some_year]]\n['2000-11-07', '2000-11', '2000', '--11-07']\n```\n\nIf enough information is known, an `Undate` object can report on its duration:\n\n```python\n>>> december = Undate(2000, 12)\n>>> feb_leapyear = Undate(2024, 2)\n>>> feb_regularyear = Undate(2023, 2)\n>>> for d in [november7, november, december, year2k, november7_some_year, feb_regularyear, feb_leapyear]:\n...    print(f\"{d}  - duration in days: {d.duration().days}\")\n...\n2000-11-07  - duration in days: 1\n2000-11  - duration in days: 30\n2000-12  - duration in days: 31\n2000  - duration in days: 366\n--11-07  - duration in days: 1\n2023-02  - duration in days: 28\n2024-02  - duration in days: 29\n```\n\nIf enough of the date is known and the precision supports it, you can\ncheck if one date falls within another date:\n\n```python\n>>> november7 = Undate(2000, 11, 7)\n>>> november2000 = Undate(2000, 11)\n>>> year2k = Undate(2000)\n>>> ad100 = Undate(100)\n>>> november7 in november\nTrue\n>>> november2000 in year2k\nTrue\n>>> november7 in year2k\nTrue\n>>> november2000 in ad100\nFalse\n>>> november7 in ad100\nFalse\n```\n\nFor dates that are imprecise or partially known, `undate` calculates\nearliest and latest possible dates for comparison purposes so you can\nsort dates and compare with equals, greater than, and less than. You\ncan also compare with python `datetime.date` objects.\n\n```python\n>>> november7_2020 = Undate(2020, 11, 7)\n>>> november_2001 = Undate(2001, 11)\n>>> year2k = Undate(2000)\n>>> ad100 = Undate(100)\n>>> sorted([november7_2020, november_2001, year2k, ad100])\n[<Undate 0100>, <Undate 2000>, <Undate 2001-11>, <Undate 2020-11-07>]\n>>> november7_2020 > november_2001\nTrue\n>>> year2k < ad100\nFalse\n>>> from datetime import date\n>>> year2k > date(2001, 1, 1)\nFalse\n```\n\nWhen dates cannot be compared due to ambiguity or precision, comparison\nmethods raise a `NotImplementedError`.\n\n```python\n>>> november_2020 = Undate(2020, 11)\n>>> november7_2020 > november_2020\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\n  File \"/Users/rkoeser/workarea/github/undate-python/src/undate/undate.py\", line 262, in __gt__\n    return not (self < other or self == other)\n  File \"/Users/rkoeser/workarea/github/undate-python/src/undate/undate.py\", line 245, in __lt__\n    raise NotImplementedError(\nNotImplementedError: Can't compare when one date falls within the other\n```\n\nAn `UndateInterval` is a date range between two `Undate` objects.\nIntervals can be open-ended, allow for optional labels, and can\ncalculate duration if enough information is known. `UndateIntervals`\nare inclusive (i.e., a closed interval), and include both the earliest\nand latest date as part of the range.\n\n```python\n>>> from undate import UndateInterval\n>>> UndateInterval(Undate(1900), Undate(2000))\n<UndateInterval 1900/2000>\n>>> UndateInterval(Undate(1801), Undate(1900), label=\"19th century\")\n>>> UndateInterval(Undate(1801), Undate(1900), label=\"19th century\").duration().days\n36524\n<UndateInterval '19th century' (1801/1900)>\n>>> UndateInterval(Undate(1901), Undate(2000), label=\"20th century\")\n<UndateInterval '20th century' (1901/2000)>\n>>> UndateInterval(latest=Undate(2000))  # before 2000\n<UndateInterval ../2000>\n>>> UndateInterval(Undate(1900))  # after 1900\n<UndateInterval 1900/>\n>>> UndateInterval(Undate(1900), Undate(2000), label=\"19th century\").duration().days\n36890\n>>> UndateInterval(Undate(2000, 1, 1), Undate(2000, 1,31)).duration().days\n31\n```\n\nYou can initialize `Undate` or `UndateInterval` objects by parsing a\ndate string with a specific converter, and you can also output an\n`Undate` object in those formats. Currently available converters\nare \"ISO8601\" and \"EDTF\" and supported calendars.\n\n```python\n>>> from undate import Undate\n>>> Undate.parse(\"2002\", \"ISO8601\")\n<Undate 2002>\n>>> Undate.parse(\"2002-05\", \"EDTF\")\n<Undate 2002-05>\n>>> Undate.parse(\"--05-03\", \"ISO8601\")\n<Undate --05-03>\n>>> Undate.parse(\"--05-03\", \"ISO8601\").format(\"EDTF\")\n'XXXX-05-03'\n>>> Undate.parse(\"1800/1900\")\n<UndateInterval 1800/1900>\n```\n\n### Calendars\n\nAll `Undate` objects are calendar aware, and date converters include\nsupport for parsing and working with dates from other calendars. The\nGregorian calendar is used by default; currently `undate` supports the\nIslamic Hijri calendar and the Hebrew Anno Mundi calendar based on\ncalendar conversion logic implemented in the\n[convertdate](https://convertdate.readthedocs.io/en/latest/) package.\n\nDates are stored with the year, month, day and appropriate precision for\nthe original calendar; internally, earliest and latest dates are\ncalculated in Gregorian / Proleptic Gregorian calendar for standardized\ncomparison across dates from different calendars.\n\n```python\n>>> from undate import Undate\n>>> tammuz4816 = Undate.parse(\"26 Tammuz 4816\", \"Hebrew\")\n>>> tammuz4816\n<Undate '26 Tammuz 4816 Anno Mundi' 4816-04-26 (Hebrew)>\n>>> rajab495 = Undate.parse(\"Rajab 495\", \"Islamic\")\n>>> rajab495\n<Undate 'Rajab 495 Hijr\u012b' 0495-07 (Islamic)>\n>>> y2k = Undate.parse(\"2001\", \"EDTF\")\n>>> y2k\n<Undate 2001 (Gregorian)>\n>>> [str(d.earliest) for d in [rajab495, tammuz4816, y2k]]\n['1102-04-28', '1056-07-17', '2001-01-01']\n>>> [str(d.precision) for d in [rajab495, tammuz4816, y2k]]\n['MONTH', 'DAY', 'YEAR']\n>>> sorted([rajab495, tammuz4816, y2k])\n[<Undate '26 Tammuz 4816 Anno Mundi' 4816-04-26 (Hebrew)>, <Undate 'Rajab 495 Hijr\u012b' 0495-07 (Islamic)>, <Undate 2001 (Gregorian)>]\n```\n\n---\n\nFor more examples, refer to the code notebooks included in the[examples]\n(https://github.com/dh-tech/undate-python/tree/main/examples/) in this\nrepository.\n\n## Documentation\n\nProject documentation is [available on ReadTheDocs](https://undate-python.readthedocs.io/en/latest/).\n\nFor instructions on setting up for local development, see [Developer Notes](DEVELOPER_NOTES.md).\n\n## License\n\nThis software is licensed under the [Apache 2.0 License](LICENSE.md).\n",
    "bugtrack_url": null,
    "license": "Apache-2",
    "summary": "library for working with uncertain, fuzzy, or partially unknown dates and date intervals",
    "version": "0.5.1",
    "project_urls": {
        "Changelog": "https://github.com/dh-tech/undate-python/blob/main/CHANGELOG.md",
        "Documentation": "https://undate-python.readthedocs.io/en/latest/",
        "Homepage": "https://github.com/dh-tech/undate-python",
        "Issues": "https://github.com/dh-tech/undate-python/issues",
        "Repository": "https://github.com/dh-tech/undate-python.git"
    },
    "split_keywords": [
        "dates",
        " dating",
        " digital-humanities",
        " partially-known",
        " uncertain-dates",
        " uncertainty",
        " unknown"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d094e6c8c0c290815d73d11032f2e656f9978660e193bbc862f90f341d1cace2",
                "md5": "d2d5ddca4e615897bca852994f33ae27",
                "sha256": "3e946621cdf434697c2cec525328f4f3c2569b2b0b341f1bf1d65ca49e20d775"
            },
            "downloads": -1,
            "filename": "undate-0.5.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "d2d5ddca4e615897bca852994f33ae27",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 42399,
            "upload_time": "2025-07-22T17:51:27",
            "upload_time_iso_8601": "2025-07-22T17:51:27.732251Z",
            "url": "https://files.pythonhosted.org/packages/d0/94/e6c8c0c290815d73d11032f2e656f9978660e193bbc862f90f341d1cace2/undate-0.5.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "134149e370e5d1e4cfe36b6d0ceb66382cef71052bff5b567aa114abe3df51db",
                "md5": "cc8c6163bb9c818799de135e5a1d93b5",
                "sha256": "4e7ffea3077933472f8e2c3390643a62b8962fda7f574eecbd337a4438321b48"
            },
            "downloads": -1,
            "filename": "undate-0.5.1.tar.gz",
            "has_sig": false,
            "md5_digest": "cc8c6163bb9c818799de135e5a1d93b5",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 46502,
            "upload_time": "2025-07-22T17:51:29",
            "upload_time_iso_8601": "2025-07-22T17:51:29.072858Z",
            "url": "https://files.pythonhosted.org/packages/13/41/49e370e5d1e4cfe36b6d0ceb66382cef71052bff5b567aa114abe3df51db/undate-0.5.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-22 17:51:29",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dh-tech",
    "github_project": "undate-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "undate"
}
        
Elapsed time: 1.94799s