forked from tweitzel/beets-recordingdate
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
271 additions
and
269 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import datetime | ||
from dateutil import parser | ||
|
||
|
||
class DateWrapper(datetime.datetime): | ||
""" | ||
Wrapper class for datetime objects. | ||
Allows comparison between dates taking into | ||
account the month and day being optional. | ||
""" | ||
|
||
def __new__(cls, y: int = None, m: int = None, d: int = None, iso_string: str = None): | ||
""" | ||
Create a new datetime object using a convenience wrapper. | ||
Must specify at least one of either year or iso_string. | ||
:param y: The year, as an integer | ||
:param m: The month, as an integer (optional) | ||
:param d: The day, as an integer (optional) | ||
:param iso_string: A string representing the date in the format YYYYMMDD. Month and day are optional. | ||
""" | ||
if y is not None: | ||
year = min(max(y, datetime.MINYEAR), datetime.MAXYEAR) | ||
month = m if (m is not None and 0 < m <= 12) else 1 | ||
day = d if (d is not None and 0 < d <= 31) else 1 | ||
elif iso_string is not None: | ||
parsed = parser.isoparse(iso_string) | ||
return datetime.datetime.__new__(cls, parsed.year, parsed.month, parsed.day) | ||
else: | ||
raise TypeError("Must specify a value for year or a date string") | ||
|
||
return datetime.datetime.__new__(cls, year, month, day) | ||
|
||
@classmethod | ||
def today(cls): | ||
today = datetime.date.today() | ||
return DateWrapper(today.year, today.month, today.day) | ||
|
||
def __init__(self, y=None, m=None, d=None, iso_string=None): | ||
if y is not None: | ||
self.y = min(max(y, datetime.MINYEAR), datetime.MAXYEAR) | ||
self.m = m if (m is None or 0 < m <= 12) else 1 | ||
self.d = d if (d is None or 0 < d <= 31) else 1 | ||
elif iso_string is not None: | ||
# Remove any hyphen separators | ||
iso_string = iso_string.replace("-", "") | ||
length = len(iso_string) | ||
|
||
if length < 4: | ||
raise ValueError("Invalid value for year") | ||
|
||
self.y = int(iso_string[:4]) | ||
self.m = None | ||
self.d = None | ||
|
||
# Month and day are optional | ||
if length >= 6: | ||
self.m = int(iso_string[4:6]) | ||
if length >= 8: | ||
self.d = int(iso_string[6:8]) | ||
else: | ||
raise TypeError("Must specify a value for year or a date string") | ||
|
||
def __lt__(self, other): | ||
if self.y != other.y: | ||
return self.y < other.y | ||
elif self.m is None: | ||
return False | ||
else: | ||
if other.m is None: | ||
return True | ||
elif self.m == other.m: | ||
if self.d is None: | ||
return False | ||
else: | ||
if other.d is None: | ||
return True | ||
else: | ||
return self.d < other.d | ||
else: | ||
return self.m < other.m | ||
|
||
def __eq__(self, other): | ||
if self.y != other.y: | ||
return False | ||
elif self.m is not None and other.m is not None: | ||
if self.d is not None and other.d is not None: | ||
return self.d == other.d | ||
else: | ||
return self.m == other.m | ||
else: | ||
return self.m == other.m |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import datetime | ||
import unittest | ||
from beetsplug.date_wrapper import DateWrapper | ||
|
||
|
||
class DateWrapperTest(unittest.TestCase): | ||
def test_creating_date(self): | ||
result = DateWrapper(2022, 12, 10) | ||
self.assertEqual(2022, result.y) | ||
self.assertEqual(12, result.m) | ||
self.assertEqual(10, result.d) | ||
|
||
def test_invalid_date(self): | ||
result = DateWrapper(2022, 0, 10) | ||
self.assertEqual(2022, result.y) | ||
self.assertEqual(1, result.m) | ||
self.assertEqual(10, result.d) | ||
result = DateWrapper(2022, 10, 0) | ||
self.assertEqual(2022, result.y) | ||
self.assertEqual(10, result.m) | ||
self.assertEqual(1, result.d) | ||
|
||
# Force year to be within range 1 - 9999 | ||
def test_year_zero(self): | ||
result = DateWrapper(0, 12, 10) | ||
self.assertEqual(1, result.y) | ||
self.assertEqual(12, result.m) | ||
self.assertEqual(10, result.d) | ||
|
||
def test_year_10000(self): | ||
result = DateWrapper(10000, 12, 10) | ||
self.assertEqual(9999, result.y) | ||
self.assertEqual(12, result.m) | ||
self.assertEqual(10, result.d) | ||
|
||
def test_less_than_year(self): | ||
first_date = DateWrapper(2021, 12, 10) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertTrue(first_date < second_date) | ||
|
||
def test_less_than_month(self): | ||
first_date = DateWrapper(2022, 11, 10) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertTrue(first_date < second_date) | ||
|
||
def test_less_than_day(self): | ||
first_date = DateWrapper(2022, 12, 9) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertTrue(first_date < second_date) | ||
|
||
# If a value is None, that date should be bigger | ||
# This means when testing for oldest (smallest) the one with values gets picked | ||
def test_less_than_none_month(self): | ||
first_date = DateWrapper(2022, None, 9) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertFalse(first_date < second_date) | ||
|
||
def test_less_than_none_day(self): | ||
first_date = DateWrapper(2022, 12, None) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertFalse(first_date < second_date) | ||
|
||
def test_less_than_none_month_day(self): | ||
first_date = DateWrapper(2022, None, None) | ||
second_date = DateWrapper(2022, 1, 1) | ||
self.assertFalse(first_date < second_date) | ||
|
||
def test_less_than_none_month_backwards(self): | ||
first_date = DateWrapper(2022, 12, 9) | ||
second_date = DateWrapper(2022, None, 10) | ||
self.assertTrue(first_date < second_date) | ||
|
||
def test_less_than_none_day_backwards(self): | ||
first_date = DateWrapper(2022, 12, 10) | ||
second_date = DateWrapper(2022, 12, None) | ||
self.assertTrue(first_date < second_date) | ||
|
||
def test_less_than_none_month_day_backwards(self): | ||
first_date = DateWrapper(2022, 1, 1) | ||
second_date = DateWrapper(2022, None, None) | ||
self.assertTrue(first_date < second_date) | ||
|
||
def test_equal(self): | ||
first_date = DateWrapper(2022, 12, 10) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertEqual(first_date, first_date) | ||
self.assertEqual(first_date, second_date) | ||
|
||
def test_equal_none_month(self): | ||
first_date = DateWrapper(2022, None, 10) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertNotEqual(first_date, second_date) | ||
|
||
def test_equal_none_month_backwards(self): | ||
first_date = DateWrapper(2022, 12, 10) | ||
second_date = DateWrapper(2022, None, 10) | ||
self.assertNotEqual(first_date, second_date) | ||
|
||
def test_equal_none_months(self): | ||
first_date = DateWrapper(2022, None, 10) | ||
second_date = DateWrapper(2022, None, 10) | ||
self.assertTrue(first_date == second_date) | ||
|
||
def test_equal_none_day(self): | ||
first_date = DateWrapper(2022, 12, None) | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertNotEqual(first_date, second_date) | ||
|
||
def test_equal_none_day_backwards(self): | ||
first_date = DateWrapper(2022, 12, 10) | ||
second_date = DateWrapper(2022, 12, None) | ||
self.assertNotEqual(first_date, second_date) | ||
|
||
def test_equal_none_days(self): | ||
first_date = DateWrapper(2022, 12, None) | ||
second_date = DateWrapper(2022, 12, None) | ||
self.assertTrue(first_date == second_date) | ||
|
||
def test_isostring(self): | ||
first_date = DateWrapper(iso_string="2022-12-10") | ||
second_date = DateWrapper(2022, 12, 10) | ||
self.assertTrue(first_date == second_date) | ||
|
||
def test_isostring_year_month(self): | ||
first_date = DateWrapper(iso_string="2022-12") | ||
second_date = DateWrapper(2022, 12) | ||
self.assertTrue(first_date == second_date) | ||
|
||
def test_isostring_year(self): | ||
first_date = DateWrapper(iso_string="2022") | ||
second_date = DateWrapper(2022) | ||
self.assertTrue(first_date == second_date) | ||
|
||
def test_isostring_empty(self): | ||
with self.assertRaises(ValueError): | ||
DateWrapper(iso_string="") | ||
|
||
def test_no_year_no_isostring(self): | ||
with self.assertRaises(TypeError): | ||
DateWrapper() | ||
|
||
def test_today(self): | ||
first_date = DateWrapper.today() | ||
today = datetime.datetime.today() | ||
second_date = DateWrapper(today.year, today.month, today.day) | ||
|
||
self.assertEqual(first_date, second_date) |
Oops, something went wrong.