opendate 0.1.19__tar.gz → 0.1.20__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opendate might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opendate
3
- Version: 0.1.19
3
+ Version: 0.1.20
4
4
  Summary: Python business datetimes
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "opendate"
3
- version = "0.1.19"
3
+ version = "0.1.20"
4
4
  description = "Python business datetimes"
5
5
  authors = ["bissli <bissli.xyz@protonmail.com>"]
6
6
  readme = "README.md"
@@ -1,4 +1,4 @@
1
- __version__ = '0.1.19'
1
+ __version__ = '0.1.20'
2
2
 
3
3
  import datetime as _datetime
4
4
 
@@ -26,6 +26,7 @@ from date.date import WEEKDAY_SHORTNAME
26
26
  from date.date import expect_date
27
27
  from date.date import expect_datetime
28
28
  from date.date import expect_time
29
+ from date.date import expect_date_or_datetime
29
30
  from date.date import expect_native_timezone
30
31
  from date.date import expect_utc_timezone
31
32
  from date.date import prefer_native_timezone
@@ -128,6 +129,7 @@ __all__ = [
128
129
  'expect_date',
129
130
  'expect_datetime',
130
131
  'expect_time',
132
+ 'expect_date_or_datetime',
131
133
  'expect_native_timezone',
132
134
  'expect_utc_timezone',
133
135
  'instance',
@@ -40,6 +40,7 @@ __all__ = [
40
40
  'expect_date',
41
41
  'expect_datetime',
42
42
  'expect_time',
43
+ 'expect_date_or_datetime',
43
44
  'Entity',
44
45
  'NYSE',
45
46
  'WEEKDAY_SHORTNAME',
@@ -113,17 +114,34 @@ def isdateish(x) -> bool:
113
114
 
114
115
 
115
116
  def parse_arg(typ, arg):
116
- if isdateish(arg):
117
- if typ == _datetime.datetime:
117
+ """Parse argument to specified type or 'smart' to preserve Date/DateTime.
118
+ """
119
+ if not isdateish(arg):
120
+ return arg
121
+
122
+ if typ == 'smart':
123
+ if isinstance(arg, Date | DateTime):
124
+ return arg
125
+ if isinstance(arg, _datetime.datetime | pd.Timestamp | np.datetime64):
118
126
  return DateTime.instance(arg)
119
- if typ == _datetime.date:
127
+ if isinstance(arg, _datetime.date):
120
128
  return Date.instance(arg)
121
- if typ == _datetime.time:
129
+ if isinstance(arg, _datetime.time):
122
130
  return Time.instance(arg)
131
+ return arg
132
+
133
+ if typ == _datetime.datetime:
134
+ return DateTime.instance(arg)
135
+ if typ == _datetime.date:
136
+ return Date.instance(arg)
137
+ if typ == _datetime.time:
138
+ return Time.instance(arg)
123
139
  return arg
124
140
 
125
141
 
126
142
  def parse_args(typ, *args):
143
+ """Parse args to specified type or 'smart' mode.
144
+ """
127
145
  this = []
128
146
  for a in args:
129
147
  if isinstance(a, Sequence) and not isinstance(a, str):
@@ -133,30 +151,31 @@ def parse_args(typ, *args):
133
151
  return this
134
152
 
135
153
 
136
- def expect(func, typ: type[_datetime.date], exclkw: bool = False) -> Callable:
137
- """Decorator to force input type of date/datetime inputs
154
+ def expect(func=None, *, typ: type[_datetime.date] | str = None, exclkw: bool = False) -> Callable:
155
+ """Decorator to force input type of date/datetime inputs.
156
+
157
+ typ can be _datetime.date, _datetime.datetime, _datetime.time, or 'smart'
138
158
  """
139
- @wraps(func)
140
- def wrapper(*args, **kwargs):
141
- args = parse_args(typ, *args)
142
- if not exclkw:
143
- for k, v in kwargs.items():
144
- if isdateish(v):
145
- if typ == _datetime.datetime:
146
- kwargs[k] = DateTime.instance(v)
147
- continue
148
- if typ == _datetime.date:
149
- kwargs[k] = Date.instance(v)
150
- continue
151
- if typ == _datetime.time:
152
- kwargs[k] = Time.instance(v)
153
- return func(*args, **kwargs)
154
- return wrapper
159
+ def decorator(func):
160
+ @wraps(func)
161
+ def wrapper(*args, **kwargs):
162
+ args = parse_args(typ, *args)
163
+ if not exclkw:
164
+ for k, v in kwargs.items():
165
+ if isdateish(v):
166
+ kwargs[k] = parse_arg(typ, v)
167
+ return func(*args, **kwargs)
168
+ return wrapper
169
+
170
+ if func is None:
171
+ return decorator
172
+ return decorator(func)
155
173
 
156
174
 
157
175
  expect_date = partial(expect, typ=_datetime.date)
158
176
  expect_datetime = partial(expect, typ=_datetime.datetime)
159
177
  expect_time = partial(expect, typ=_datetime.time)
178
+ expect_date_or_datetime = partial(expect, typ='smart')
160
179
 
161
180
 
162
181
  def type_class(typ, obj):
@@ -218,6 +237,34 @@ def reset_business(func):
218
237
  return wrapper
219
238
 
220
239
 
240
+ def normalize_date_datetime_pairs(func):
241
+ """Decorator to normalize mixed Date/DateTime pairs to DateTime.
242
+ """
243
+ @wraps(func)
244
+ def wrapper(*args, **kwargs):
245
+ if len(args) >= 3:
246
+ cls_or_self, begdate, enddate = args[0], args[1], args[2]
247
+ rest_args = args[3:]
248
+
249
+ tz = UTC
250
+ if isinstance(begdate, DateTime) and begdate.tzinfo:
251
+ tz = begdate.tzinfo
252
+ elif isinstance(enddate, DateTime) and enddate.tzinfo:
253
+ tz = enddate.tzinfo
254
+
255
+ if isinstance(begdate, Date) and not isinstance(begdate, DateTime):
256
+ if isinstance(enddate, DateTime):
257
+ begdate = DateTime(begdate.year, begdate.month, begdate.day, tzinfo=tz)
258
+ elif isinstance(enddate, Date) and not isinstance(enddate, DateTime):
259
+ if isinstance(begdate, DateTime):
260
+ enddate = DateTime(enddate.year, enddate.month, enddate.day, tzinfo=tz)
261
+
262
+ args = (cls_or_self, begdate, enddate) + rest_args
263
+
264
+ return func(*args, **kwargs)
265
+ return wrapper
266
+
267
+
221
268
  def prefer_utc_timezone(func, force:bool = False) -> Callable:
222
269
  """Return datetime as UTC.
223
270
  """
@@ -1436,11 +1483,15 @@ class Interval(_pendulum.Interval):
1436
1483
  _business: bool = False
1437
1484
  _entity: type[NYSE] = NYSE
1438
1485
 
1486
+ @expect_date_or_datetime
1487
+ @normalize_date_datetime_pairs
1439
1488
  def __new__(cls, begdate: Date | DateTime, enddate: Date | DateTime) -> Self:
1440
1489
  assert begdate and enddate, 'Interval dates cannot be None'
1441
1490
  instance = super().__new__(cls, begdate, enddate, False)
1442
1491
  return instance
1443
1492
 
1493
+ @expect_date_or_datetime
1494
+ @normalize_date_datetime_pairs
1444
1495
  def __init__(self, begdate: Date | DateTime, enddate: Date | DateTime) -> None:
1445
1496
  super().__init__(begdate, enddate, False)
1446
1497
  self._sign = 1 if begdate <= enddate else -1
File without changes
File without changes
File without changes