diff --git a/README.md b/README.md index dff6a50..3a091bf 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,135 @@ It was initially created as part of a [DH-Tech](https://dh-tech.github.io/) hack Read [Contributors](CONTRIBUTORS.md) for detailed contribution information. +## 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.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]) +[, , , ] +>>> 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 "", line 1, in + 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 +```python +>>> from undate.undate import UndateInterval +>>> UndateInterval(Undate(1900), Undate(2000)) + +>>> UndateInterval(Undate(1900), Undate(2000), label="19th century") +>>> UndateInterval(Undate(1900), Undate(2000), label="19th century").duration().days +36890 + +>>> UndateInterval(Undate(1900), Undate(2000), label="20th century") + +>>> UndateInterval(latest=Undate(2000)) # before 2000 + +>>> UndateInterval(Undate(1900)) # after 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 formatter. +```python +>>> from undate.dateformat.iso8601 import ISO8601DateFormat +>>> isoformatter = ISO8601DateFormat() +>>> isoformatter.parse("2002") + +>>> isoformatter.parse("2002-05") + +>>> isoformatter.parse("--05-03") + +>>> isoformatter.parse("--05-03") + +>>> isoformatter.parse("1800/1900") + +``` + ## Documentation Project documentation is available on ReadTheDocs https://undate-python.readthedocs.io/en/latest/ diff --git a/src/undate/undate.py b/src/undate/undate.py index ee94bb6..971ded7 100644 --- a/src/undate/undate.py +++ b/src/undate/undate.py @@ -20,7 +20,7 @@ class DatePrecision(IntEnum): of the date is known.""" # numbers should be set to allow logical greater than / less than - # comparison, e.g. year precision > month + # comparison, e.g. year precision < month #: day DAY = 1