Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,13 @@ 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
```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>]
[undate.Undate(year=100, calendar="Gregorian"), undate.Undate(year=2000, calendar="Gregorian"), undate.Undate(year=2001, month=11, calendar="Gregorian"), undate.Undate(year=2020, month=11, day=7, calendar="Gregorian")]
>>> november7_2020 > november_2001
True
>>> year2k < ad100
Expand Down Expand Up @@ -161,17 +161,17 @@ and latest date as part of the range.
```python
>>> from undate import UndateInterval
>>> UndateInterval(Undate(1900), Undate(2000))
<UndateInterval 1900/2000>
undate.UndateInterval(earliest=undate.Undate(year=1900, calendar="Gregorian"), latest=undate.Undate(year=2000, calendar="Gregorian"))
>>> UndateInterval(Undate(1801), Undate(1900), label="19th century")
undate.UndateInterval(earliest=undate.Undate(year=1801, calendar="Gregorian"), latest=undate.Undate(year=1900, calendar="Gregorian"), 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)>
undate.UndateInterval(earliest=undate.Undate(year=1901, calendar="Gregorian"), latest=undate.Undate(year=2000, calendar="Gregorian"), label="20th century")
>>> UndateInterval(latest=Undate(2000)) # before 2000
<UndateInterval ../2000>
undate.UndateInterval(latest=undate.Undate(year=2000, calendar="Gregorian"))
>>> UndateInterval(Undate(1900)) # after 1900
<UndateInterval 1900/>
undate.UndateInterval(earliest=undate.Undate(year=1900, calendar="Gregorian"))
>>> UndateInterval(Undate(1900), Undate(2000), label="19th century").duration().days
36890
>>> UndateInterval(Undate(2000, 1, 1), Undate(2000, 1,31)).duration().days
Expand All @@ -186,15 +186,15 @@ are "ISO8601" and "EDTF" and supported calendars.
```python
>>> from undate import Undate
>>> Undate.parse("2002", "ISO8601")
<Undate 2002>
undate.Undate(year=2002, calendar="Gregorian")
>>> Undate.parse("2002-05", "EDTF")
<Undate 2002-05>
undate.Undate(year=2002, month=5, calendar="Gregorian")
>>> Undate.parse("--05-03", "ISO8601")
<Undate --05-03>
undate.Undate(month=5, day=3, calendar="Gregorian")
>>> Undate.parse("--05-03", "ISO8601").format("EDTF")
'XXXX-05-03'
>>> Undate.parse("1800/1900")
<UndateInterval 1800/1900>
>>> Undate.parse("1800/1900", format="EDTF")
undate.UndateInterval(earliest=undate.Undate(year=1800, calendar="Gregorian"), latest=undate.Undate(year=1900, calendar="Gregorian"))
```

### Calendars
Expand All @@ -215,26 +215,26 @@ comparison across dates from different calendars.
>>> from undate import Undate
>>> tammuz4816 = Undate.parse("26 Tammuz 4816", "Hebrew")
>>> tammuz4816
<Undate '26 Tammuz 4816 Anno Mundi' 4816-04-26 (Hebrew)>
undate.Undate(year=4816, month=4, day=26, label="26 Tammuz 4816 Anno Mundi", calendar="Hebrew")
>>> rajab495 = Undate.parse("Rajab 495", "Islamic")
>>> rajab495
<Undate 'Rajab 495 Hijrī' 0495-07 (Islamic)>
undate.Undate(year=495, month=7, label="Rajab 495 Islamic", calendar="Islamic")
>>> y2k = Undate.parse("2001", "EDTF")
>>> y2k
<Undate 2001 (Gregorian)>
undate.Undate(year=2001, calendar="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)>]
[undate.Undate(year=4816, month=4, day=26, label="26 Tammuz 4816 Anno Mundi", calendar="Hebrew"), undate.Undate(year=495, month=7, label="Rajab 495 Islamic", calendar="Islamic"), undate.Undate(year=2001, calendar="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/)
directory in this repository.

## Documentation

Expand Down
11 changes: 9 additions & 2 deletions src/undate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
__version__ = "0.6.0.dev0"

from undate.date import DatePrecision
from undate.date import DatePrecision, UnDelta
from undate.undate import Undate, Calendar
from undate.interval import UndateInterval

__all__ = ["Undate", "UndateInterval", "Calendar", "DatePrecision", "__version__"]
__all__ = [
"Undate",
"UndateInterval",
"Calendar",
"DatePrecision",
"UnDelta",
"__version__",
]
2 changes: 1 addition & 1 deletion src/undate/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def __init__(self, *days: int):
def __repr__(self):
# customize string representation for simpler notation; default
# specifies full UnInt initialization with upper and lower keywords
return f"{self.__class__.__name__}(days=[{self.days.lower},{self.days.upper}])"
return f"undate.{self.__class__.__name__}({self.days.lower},{self.days.upper})"

def __eq__(self, other: object) -> bool:
# is an uncertain duration ever *equal* another, even if the values are the same?
Expand Down
12 changes: 9 additions & 3 deletions src/undate/interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,15 @@ def format(self, format) -> str:
raise ValueError(f"Unsupported format '{format}'")

def __repr__(self) -> str:
if self.label:
return "<UndateInterval '%s' (%s)>" % (self.label, self)
return "<UndateInterval %s>" % self
init_opts = {
"earliest": repr(self.earliest) if self.earliest else None,
"latest": repr(self.latest) if self.latest else None,
"label": f"{self.label!r}" if self.label else None,
}
init_str = ", ".join(
[f"{key}={val}" for key, val in init_opts.items() if val is not None]
)
return f"undate.UndateInterval({init_str})"

def __eq__(self, other) -> bool:
# currently doesn't support comparison with any other types
Expand Down
11 changes: 9 additions & 2 deletions src/undate/undate.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,15 @@ def __str__(self) -> str:
return self.converter.to_string(self)

def __repr__(self) -> str:
label_str = f" '{self.label}'" if self.label else ""
return f"<Undate{label_str} {self} ({self.calendar.name.title()})>"
init_opts = {k: v for k, v in self.initial_values.items() if v is not None}
# include label if set
if self.label:
init_opts["label"] = self.label
# always include calendar
init_opts["calendar"] = self.calendar.value.title()
# combine parameters; use !r to quote strings
init_str = ", ".join([f"{key}={val!r}" for key, val in init_opts.items()])
return f"undate.Undate({init_str})"

@classmethod
def parse(cls, date_string, format) -> Union["Undate", UndateInterval]:
Expand Down
16 changes: 14 additions & 2 deletions tests/test_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,20 @@ def test_init_validation(self):
UnDelta(10)

def test_repr(self):
# customized string representation
assert repr(UnDelta(28, 29)) == "UnDelta(days=[28,29])"
# test customized string representation

# import undate to test eval of fully-qualified repr string
import undate # noqa: F401

feb_undelt = UnDelta(28, 29)
assert repr(feb_undelt) == "undate.UnDelta(28,29)"
# can't compare directly because uncertain deltas aren't equal,
# but compare values
assert eval(repr(feb_undelt.days.lower)) == feb_undelt.days.lower
assert eval(repr(feb_undelt.days.upper)) == feb_undelt.days.upper

larger_undelt = UnDelta(10, 12, 14, 16)
assert repr(larger_undelt) == "undate.UnDelta(10,16)"

def test_eq(self):
# uncertain deltas are not equivalent
Expand Down
27 changes: 23 additions & 4 deletions tests/test_interval.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,33 @@ def test_format(self):
assert open_end.format("ISO8601") == "2000/"

def test_repr(self):
# import undate to test eval of fully-qualified repr string
import undate # noqa: F401

# interval with start and end
closed_interval = UndateInterval(Undate(2022), Undate(2023))
assert (
repr(closed_interval)
== f"undate.UndateInterval(earliest={repr(closed_interval.earliest)}, latest={repr(closed_interval.latest)})"
)
# should be able to evaluate repr string to get an equivalent object
assert eval(repr(closed_interval)) == closed_interval
# interval with a label
fancy_epoch = UndateInterval(Undate(2022), Undate(2023), label="Fancy Epoch")
assert (
repr(UndateInterval(Undate(2022), Undate(2023)))
== "<UndateInterval 2022/2023>"
repr(fancy_epoch)
== f"undate.UndateInterval(earliest={repr(fancy_epoch.earliest)}, latest={repr(fancy_epoch.latest)}, label='Fancy Epoch')"
)
assert eval(repr(fancy_epoch)) == fancy_epoch

open_interval = UndateInterval(
Undate(33),
)
assert (
repr(UndateInterval(Undate(2022), Undate(2023), label="Fancy Epoch"))
== "<UndateInterval 'Fancy Epoch' (2022/2023)>"
repr(open_interval)
== f"undate.UndateInterval(earliest={repr(open_interval.earliest)})"
)
assert eval(repr(open_interval)) == open_interval

def test_str_open_range(self):
# 900 -
Expand Down
32 changes: 28 additions & 4 deletions tests/test_undate.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,36 @@ def test_partially_known_str(self):
# assert str(Undate(2022, day=7)) == "2022-XX-07" @ currently returns 2022-07

def test_repr(self):
assert repr(Undate(2022, 11, 7)) == "<Undate 2022-11-07 (Gregorian)>"
# import undate to test eval of fully-qualified undate repr string
import undate # noqa: F401

nov2022 = Undate(2022, 11, 7)
# repr string should provide sufficient details to initialize
assert (
repr(nov2022)
== "undate.Undate(year=2022, month=11, day=7, calendar='Gregorian')"
)
# eval on repr string should be equivalent to the object
assert eval(repr(nov2022)) == nov2022
nov2022_labeled = Undate(2022, 11, 7, label="A Special Day")
assert (
repr(nov2022_labeled)
== "undate.Undate(year=2022, month=11, day=7, label='A Special Day', calendar='Gregorian')"
)
assert eval(repr(nov2022_labeled)) == nov2022_labeled
# different calendar, missing fields
islamic_date = Undate(484, calendar=Calendar.ISLAMIC)
assert repr(islamic_date) == "undate.Undate(year=484, calendar='Islamic')"
assert eval(repr(islamic_date)) == islamic_date

# test string values for month/day
unknown_year = Undate(month="1X", day="3X")
assert (
repr(Undate(2022, 11, 7, label="A Special Day"))
== "<Undate 'A Special Day' 2022-11-07 (Gregorian)>"
repr(unknown_year)
== "undate.Undate(month='1X', day='3X', calendar='Gregorian')"
)
assert repr(Undate(484, calendar=Calendar.ISLAMIC)) == "<Undate 0484 (Islamic)>"
# unknown dates aren't equal, but string representation should match
assert str(eval(repr(unknown_year))) == str(unknown_year)

def test_init_str(self):
assert Undate("2000").earliest.year == 2000
Expand Down