Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e6fb929
Merge branch 'release/0.4' into develop
rlskoeser Apr 11, 2025
8560961
Allow optional weekdays; add more month variants
rlskoeser Feb 6, 2025
4bb3170
Add more context to not implemented sort error
rlskoeser Feb 6, 2025
e3f35f9
Basic Seleucid calendar support, adapted from PGP
rlskoeser Feb 6, 2025
65b0667
Add methods to convert calendar after initialization
rlskoeser Feb 7, 2025
a56751a
Add weekday method to date object
rlskoeser Feb 7, 2025
d1fd878
Add WIP notebook experimenting with parsing PGP dates
rlskoeser Apr 11, 2025
f6f7db5
docs: update CONTRIBUTORS.md [skip ci]
allcontributors[bot] Apr 14, 2025
a4be322
docs: update .all-contributorsrc [skip ci]
allcontributors[bot] Apr 14, 2025
e664a81
Merge pull request #126 from dh-tech/all-contributors/add-taylor-arnold
rlskoeser May 8, 2025
0ea001e
Implement UnInt and UnDelta for uncertain date durations
rlskoeser Mar 6, 2025
4e5b243
Add a docstring describing UnInlt
rlskoeser May 9, 2025
20a9f8d
Fix comparison methods and add tests; incorporate @coderabbitai comments
rlskoeser May 9, 2025
43b37f2
Put variable first in tests to improve readability, per @coderabbitai
rlskoeser May 9, 2025
c8e790d
Update src/undate/date.py
rlskoeser May 22, 2025
6b18d71
Fix typo in weekday method docstring
rlskoeser May 22, 2025
0ad7d4f
Update comment for accuracy
rlskoeser May 22, 2025
75e641c
Add installation instructions and fix century dates
rlskoeser May 22, 2025
473da51
Increase version number for next release
rlskoeser May 22, 2025
47a6c57
Fix typo flagged by @coderabbitai; fix ordering and simplify imports
rlskoeser May 22, 2025
e7eb512
Fixed with ruff
rlskoeser May 22, 2025
f3598c8
Updated and revised PGP demo notebook
rlskoeser May 22, 2025
f434bc6
Clean up to address ruff check and re-run
rlskoeser May 22, 2025
57e3578
Add altair to notebook dependencies
rlskoeser May 22, 2025
44a3370
Update to newer codecov action and use upload token
rlskoeser May 22, 2025
90c95a1
Tweak pytest coverage command
rlskoeser May 22, 2025
2a9b2a2
Preliminary seleucid tests adapted from geniza codebase
rlskoeser May 22, 2025
4f25cc5
Add todo note
rlskoeser May 23, 2025
e01c065
Add a note that intervals are inclusive/closed; improve formatting
rlskoeser May 23, 2025
8b3030b
Revert switch to uv to see if it resolves codecov issue
rlskoeser May 23, 2025
2a7bcc1
Merge pull request #131 from dh-tech/feature/improve-readme
rlskoeser May 23, 2025
02da2b0
Fix nesting; add note about Date month = None
rlskoeser May 23, 2025
916ab7f
Configure codecov to check for 100% unit test coverage
rlskoeser May 23, 2025
312dbf3
Merge pull request #133 from dh-tech/feature/codecov-config
rlskoeser May 27, 2025
0b39f9f
Potential fix for code scanning alert no. 2: Workflow does not contai…
rlskoeser May 27, 2025
f615ea1
Potential fix for code scanning alert no. 1: Workflow does not contai…
rlskoeser May 27, 2025
8babfd4
Merge pull request #134 from dh-tech/alert-autofix-2
rlskoeser May 27, 2025
0044fc0
Add unit test for as_calendar method
rlskoeser Jun 25, 2025
7852106
Add test for weekday on non-day precision date
rlskoeser Jun 25, 2025
af35f81
Revise string formatting flagged by ruff check
rlskoeser Jun 25, 2025
b91fe3c
Merge pull request #125 from dh-tech/feature/pgp-dates
rlskoeser Jun 25, 2025
4a992a9
Merge pull request #129 from dh-tech/feature/uncertain-numbers
rlskoeser Jun 25, 2025
f065bf4
Make converter_cls check more explicit
rlskoeser Jun 25, 2025
5b5746d
Add duration logic for uncertain years
rlskoeser Jun 25, 2025
5bb57c0
Clean up flagged by @coderabbitai with additional refactoring
rlskoeser Jun 25, 2025
e6b96df
Refactor and test logic for possible years
rlskoeser Jun 25, 2025
e0bac94
Implement representative years filtering method for Gregorian calendar
rlskoeser Jun 25, 2025
43bc97b
Implement representative years for islamic calendar converter
rlskoeser Jun 25, 2025
b436f33
Implement representative years for hebrew calendar converter
rlskoeser Jun 25, 2025
c5c1cc8
Support duration for unknown hebrew & islamic years
rlskoeser Jun 25, 2025
5119ccd
Add unit tests for missing coverage
rlskoeser Jun 25, 2025
bda835c
Clean up print statements and other minor items flagged by @coderabbitai
rlskoeser Jun 25, 2025
7af0628
Additional testing
rlskoeser Jun 25, 2025
b5e9c7f
Add test for gregorian converter to version control
rlskoeser Jun 25, 2025
7170433
Fix typo in comment and add tests for Gregorian month max day
rlskoeser Jun 26, 2025
9efe7e3
Merge pull request #135 from dh-tech/bugfix/130-uncertain-years
rlskoeser Jun 26, 2025
20a66e9
Set version to 0.5 final and change status from alpha to beta
rlskoeser Jun 26, 2025
5859aad
Document changes included in v0.5
rlskoeser Jun 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@
"test",
"doc"
]
},
{
"login": "taylor-arnold",
"name": "Taylor Arnold",
"avatar_url": "https://avatars.githubusercontent.com/u/5752184?v=4",
"profile": "http://taylorarnold.org",
"contributions": [
"review",
"ideas"
]
}
],
"contributorsPerLine": 7,
Expand All @@ -77,5 +87,6 @@
"repoHost": "https://github.com",
"projectName": "undate-python",
"projectOwner": "dh-tech",
"badgeTemplate": "![All Contributors](https://img.shields.io/github/all-contributors/dh-tech/undate-python?color=ee8449&style=flat-square)"
"badgeTemplate": "![All Contributors](https://img.shields.io/github/all-contributors/dh-tech/undate-python?color=ee8449&style=flat-square)",
"commitType": "docs"
}
3 changes: 3 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ name: Check style + docs + types
on:
pull_request:

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
Expand Down
29 changes: 16 additions & 13 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: unit tests

permissions:
contents: read
id-token: write

on:
push:
branches:
Expand Down Expand Up @@ -33,26 +37,25 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
cache: 'pip'
cache-dependency-path: '**/pyproject.toml'

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Install package with dev and test dependencies
run: uv sync --extra test
- name: Install package with dependencies
run: pip install -e ".[test]"

# for all versions but the one we use for code coverage, run normally
- name: Run unit tests normally
run: uv run pytest
- name: Run unit tests without code coverage
run: pytest
if: ${{ matrix.python != env.COV_PYTHON_VERSION }}

# run code coverage in one version only
- name: Run unit tests with code coverage reporting
run: uv run pytest --cov=undate
run: pytest --cov=.
if: ${{ matrix.python == env.COV_PYTHON_VERSION }}
- name: Upload test coverage to Codecov
uses: codecov/codecov-action@v3

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
if: ${{ matrix.python == env.COV_PYTHON_VERSION }}

19 changes: 17 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Change Log

## 0.5

- New `UnDelta` and `UnInt` classes for uncertain durations
- `Undate.duration` now returns either a `Timedelta` or an `UnDelta` if the duration is ambiguous
- New properties `possible_years` and `representative_years` on `Undate` class, used for calculating durations for uncertain years and months
- New `weekday` method on class `undate.date.Date`
- Calendar converter improvements:
- Calendar converter classes can optional provide minimum and maximum years for uncertain dates
- New calendar methods `days_in_year` and `representative_years`
- Hebrew date parser now allows for week days, along with additional month variants
- Preliminary Seleucide calendar converter class, based on Hebrew calendar with a year offset
- New method `as_calendar` on `Undate` class, to set calendar without doing any conversion
- Readme examples have been improved and extended
- New example notebook testing Hebrew, Islamic, and Seleucid date parsing and conversion with Princeton Geniza Project data
- bugfix: duration for uncertain years previously returned the duration from earliest to latest possible dates in range; now returns an `UnDelta` with the possible durations for the possible years in the given calendar

## 0.4

- Undate is now Calendar aware / Calendar explicit; default is Gregorian
Expand All @@ -20,7 +36,6 @@
- Reorganized examples folder to avoid unnecessary nesting
- ISMI data has been updated from older JSON data to examples in RDF (turtle)


## 0.3.1

Update readthedocs config for current installation
Expand Down Expand Up @@ -49,7 +64,7 @@ Update readthedocs config for current installation

### numpy impact

Performance differences seem to be negligible, but it does increase payload size. The virtualenv for installing version 0.2 was 14MB; when installing the newer version with numpy, the virtualenv is 46MB (the numpy folder in site packages is 31MB on its own).
Performance differences seem to be negligible, but it does increase payload size. The virtualenv for installing version 0.2 was 14MB; when installing the newer version with numpy, the virtualenv is 46MB (the numpy folder in site packages is 31MB on its own).

## 0.2

Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ We use [All Contributors](https://allcontributors.org/) because we recognize tha
<td align="center" valign="top" width="14.28%"><a href="https://github.com/robcast"><img src="https://avatars.githubusercontent.com/u/1488847?v=4?s=100" width="100px;" alt="Robert Casties"/><br /><sub><b>Robert Casties</b></sub></a><br /><a href="#data-robcast" title="Data">🔣</a> <a href="#ideas-robcast" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/dh-tech/undate-python/pulls?q=is%3Apr+reviewed-by%3Arobcast" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jdamerow"><img src="https://avatars.githubusercontent.com/u/8881141?v=4?s=100" width="100px;" alt="Julia Damerow"/><br /><sub><b>Julia Damerow</b></sub></a><br /><a href="https://github.com/dh-tech/undate-python/commits?author=jdamerow" title="Code">💻</a> <a href="https://github.com/dh-tech/undate-python/pulls?q=is%3Apr+reviewed-by%3Ajdamerow" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/dh-tech/undate-python/commits?author=jdamerow" title="Tests">⚠️</a> <a href="#eventOrganizing-jdamerow" title="Event Organizing">📋</a> <a href="#ideas-jdamerow" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/maltevogl"><img src="https://avatars.githubusercontent.com/u/20907912?v=4?s=100" width="100px;" alt="Malte Vogl"/><br /><sub><b>Malte Vogl</b></sub></a><br /><a href="https://github.com/dh-tech/undate-python/commits?author=maltevogl" title="Code">💻</a> <a href="https://github.com/dh-tech/undate-python/pulls?q=is%3Apr+reviewed-by%3Amaltevogl" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/dh-tech/undate-python/commits?author=maltevogl" title="Tests">⚠️</a> <a href="https://github.com/dh-tech/undate-python/commits?author=maltevogl" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://taylorarnold.org"><img src="https://avatars.githubusercontent.com/u/5752184?v=4?s=100" width="100px;" alt="Taylor Arnold"/><br /><sub><b>Taylor Arnold</b></sub></a><br /><a href="https://github.com/dh-tech/undate-python/pulls?q=is%3Apr+reviewed-by%3Ataylor-arnold" title="Reviewed Pull Requests">👀</a> <a href="#ideas-taylor-arnold" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
</tbody>
<tfoot>
Expand Down
101 changes: 76 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,60 @@
**undate** is a python library for working with uncertain or partially known dates.

> [!WARNING]
> This is alpha 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
> 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.*
_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.
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.
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.undate import Undate
from undate import Undate

november7 = Undate(2000, 11, 7)
november = Undate(2000, 11)
Expand All @@ -46,12 +72,14 @@ 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)
Expand All @@ -68,7 +96,9 @@ If enough information is known, an `Undate` object can report on its duration:
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:
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)
Expand All @@ -86,7 +116,10 @@ False
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.
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)
Expand All @@ -104,7 +137,8 @@ False
False
```

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

```python
>>> november_2020 = Undate(2020, 11)
Expand All @@ -118,17 +152,22 @@ Traceback (most recent call last):
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
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.undate import UndateInterval
>>> from undate import UndateInterval
>>> UndateInterval(Undate(1900), Undate(2000))
<UndateInterval 1900/2000>
>>> UndateInterval(Undate(1900), Undate(2000), label="19th century")
>>> UndateInterval(Undate(1900), Undate(2000), label="19th century").duration().days
36890
<UndateInterval '19th century' (1900/2000)>
>>> UndateInterval(Undate(1900), Undate(2000), label="20th century")
<UndateInterval '20th century' (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
Expand All @@ -139,8 +178,10 @@ An `UndateInterval` is a date range between two `Undate` objects. Intervals can
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.
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
Expand All @@ -158,9 +199,17 @@ Currently available converters are "ISO8601" and "EDTF" and supported calendars.

### 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.
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.
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
Expand All @@ -181,9 +230,11 @@ Dates are stored with the year, month, day and appropriate precision for the ori
[<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.
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

Expand Down
14 changes: 14 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
coverage:
status:
project:
default: false # disable default status check
app: # custom target for app code
target: auto # default target (95%)
paths:
- "src/" # limit to files within src code directory
tests: # declare a new status context for "tests"
target: 100% # we always want 100% coverage here
paths:
- "tests/" # only include coverage in "tests/" folder


Loading