Skip to content

Commit 4c4058e

Browse files
author
Rodrigo Roldán
committed
perf: minor changes on some modules
1 parent 1325dad commit 4c4058e

5 files changed

Lines changed: 197 additions & 3 deletions

File tree

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Explicitly re-ignore some folders
2+
.*
3+
**/__pycache__

src/eones/__init__.py

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,157 @@
77
reason about time — not just measure it.
88
"""
99

10+
from datetime import datetime, timedelta
11+
from typing import Any, List, Union
12+
13+
from eones.core.date import Date
14+
from eones.core.parser import Parser
15+
from eones.errors import InvalidFormatError, InvalidTimezoneError
1016
from eones.interface import Eones
1117

18+
19+
# Utility functions for backward compatibility and convenience
20+
def parse_date(
21+
value: Union[str, dict, datetime], tz: str = "UTC", formats: List[str] = None
22+
) -> Date:
23+
"""Parse a date from various input formats.
24+
25+
Args:
26+
value: Date input (string, dict, or datetime)
27+
tz: Timezone string
28+
formats: List of format strings for parsing
29+
30+
Returns:
31+
Date: Parsed date object
32+
"""
33+
# Include common datetime formats including ISO with timezone if none provided
34+
if formats is None:
35+
formats = [
36+
"%Y-%m-%d",
37+
"%d/%m/%Y",
38+
"%Y-%m-%d %H:%M:%S",
39+
"%Y-%m-%d %H:%M",
40+
"%d/%m/%Y %H:%M:%S",
41+
"%d/%m/%Y %H:%M",
42+
"%Y-%m-%dT%H:%M:%S%z",
43+
"%Y-%m-%dT%H:%M:%S",
44+
]
45+
parser = Parser(tz=tz, formats=formats)
46+
return parser.parse(value)
47+
48+
49+
def format_date(date: Union[Date, datetime], fmt: str) -> str:
50+
"""Format a date using the given format string.
51+
52+
Args:
53+
date: Date object or datetime to format
54+
fmt: Format string
55+
56+
Returns:
57+
str: Formatted date string
58+
"""
59+
if isinstance(date, Date):
60+
return date.format(fmt)
61+
elif isinstance(date, datetime):
62+
return date.strftime(fmt)
63+
else:
64+
raise TypeError(f"Expected Date or datetime object, got {type(date).__name__}")
65+
66+
67+
def add_days(date: Date, days: int) -> Date:
68+
"""Add days to a date.
69+
70+
Args:
71+
date: Date object
72+
days: Number of days to add (can be negative)
73+
74+
Returns:
75+
Date: New date with days added
76+
"""
77+
if not isinstance(date, Date):
78+
raise ValueError("Expected Date object")
79+
return date.shift(timedelta(days=days))
80+
81+
82+
def date_diff_days(date1: Date, date2: Date) -> int:
83+
"""Calculate the difference in days between two dates.
84+
85+
Args:
86+
date1: First date
87+
date2: Second date
88+
89+
Returns:
90+
int: Difference in days (date2 - date1)
91+
"""
92+
if not isinstance(date1, Date) or not isinstance(date2, Date):
93+
raise ValueError("Expected Date objects")
94+
return (date2.to_datetime() - date1.to_datetime()).days
95+
96+
97+
def date_range(start: Date, end: Date, step_days: int = 1) -> List[Date]:
98+
"""Generate a range of dates from start to end.
99+
100+
Args:
101+
start: Start date
102+
end: End date (inclusive)
103+
step_days: Step size in days
104+
105+
Returns:
106+
List[Date]: List of dates in the range
107+
"""
108+
if not isinstance(start, Date) or not isinstance(end, Date):
109+
raise ValueError("Expected Date objects")
110+
111+
dates = []
112+
current = start
113+
114+
while current.to_datetime().date() <= end.to_datetime().date():
115+
dates.append(current)
116+
current = current.shift(timedelta(days=step_days))
117+
118+
return dates
119+
120+
121+
def to_timestamp(date: Union[Date, datetime]) -> int:
122+
"""Convert a date to Unix timestamp.
123+
124+
Args:
125+
date: Date object or datetime
126+
127+
Returns:
128+
int: Unix timestamp
129+
"""
130+
if isinstance(date, Date):
131+
return int(date.to_unix())
132+
elif isinstance(date, datetime):
133+
return int(date.timestamp())
134+
else:
135+
raise ValueError("Expected Date or datetime object")
136+
137+
138+
def from_timestamp(timestamp: Union[int, float], tz: str = "UTC") -> Date:
139+
"""Create a Date from Unix timestamp.
140+
141+
Args:
142+
timestamp: Unix timestamp (int or float)
143+
tz: Timezone (default: "UTC")
144+
145+
Returns:
146+
Date: Date object
147+
"""
148+
return Date.from_unix(float(timestamp), tz=tz)
149+
150+
12151
__version__ = "1.2.0"
13-
__all__ = ["Eones"]
152+
__all__ = [
153+
"Eones",
154+
"InvalidFormatError",
155+
"InvalidTimezoneError",
156+
"parse_date",
157+
"format_date",
158+
"add_days",
159+
"date_diff_days",
160+
"date_range",
161+
"to_timestamp",
162+
"from_timestamp",
163+
]

src/eones/core/date.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def from_iso(cls, iso_str: str, tz: Optional[str] = "UTC") -> Date:
308308
try:
309309
dt = datetime.fromisoformat(iso_str)
310310

311-
except ZoneInfoNotFoundError as exc:
311+
except ValueError as exc:
312312
raise InvalidTimezoneError(tz) from exc
313313

314314
if dt.tzinfo is None:

src/eones/humanize.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def diff_for_humans(
2626
if other is None:
2727
from eones.core.date import Date # pylint: disable=import-outside-toplevel
2828

29-
other = Date.now(tz=date.timezone)
29+
other = Date.now(tz=date.timezone, naive="utc")
3030

3131
diff_seconds = (date.to_datetime() - other.to_datetime()).total_seconds()
3232
future = diff_seconds > 0

src/eones/interface.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,3 +298,44 @@ def _coerce_to_date(self, other) -> "Date":
298298
return other
299299

300300
return self._parser.parse(other)
301+
302+
@staticmethod
303+
def is_valid_format(date_str: str, formats: List[str]) -> bool:
304+
"""Check if a date string matches any of the provided formats.
305+
306+
Args:
307+
date_str: Date string to validate
308+
formats: List of format strings to try
309+
310+
Returns:
311+
bool: True if the string matches any format
312+
"""
313+
from datetime import datetime
314+
315+
for fmt in formats:
316+
try:
317+
datetime.strptime(date_str, fmt)
318+
return True
319+
except ValueError:
320+
continue
321+
return False
322+
323+
@staticmethod
324+
def sanitize_formats(formats: List[Any]) -> List[str]:
325+
"""Remove duplicates and invalid formats from a format list.
326+
327+
Args:
328+
formats: List of format strings (may contain duplicates or invalid types)
329+
330+
Returns:
331+
List[str]: Clean list of unique format strings
332+
"""
333+
seen = set()
334+
result = []
335+
336+
for fmt in formats:
337+
if isinstance(fmt, str) and fmt not in seen:
338+
seen.add(fmt)
339+
result.append(fmt)
340+
341+
return result

0 commit comments

Comments
 (0)