opendate 0.1.12__tar.gz → 0.1.13__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.
- {opendate-0.1.12 → opendate-0.1.13}/PKG-INFO +3 -3
- {opendate-0.1.12 → opendate-0.1.13}/pyproject.toml +1 -1
- {opendate-0.1.12 → opendate-0.1.13}/src/date/__init__.py +1 -1
- {opendate-0.1.12 → opendate-0.1.13}/src/date/date.py +360 -101
- {opendate-0.1.12 → opendate-0.1.13}/LICENSE +0 -0
- {opendate-0.1.12 → opendate-0.1.13}/README.md +0 -0
- {opendate-0.1.12 → opendate-0.1.13}/src/date/extras.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: opendate
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: Python business datetimes
|
|
5
|
-
Home-page: https://github.com/bissli/opendate
|
|
6
5
|
License: MIT
|
|
7
6
|
Author: bissli
|
|
8
7
|
Author-email: bissli.xyz@protonmail.com
|
|
@@ -13,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Provides-Extra: test
|
|
17
17
|
Requires-Dist: asserts ; extra == "test"
|
|
18
18
|
Requires-Dist: bump2version ; extra == "test"
|
|
@@ -46,28 +46,30 @@ __all__ = [
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def Timezone(name:str = 'US/Eastern') -> _zoneinfo.ZoneInfo:
|
|
49
|
-
"""
|
|
49
|
+
"""Create a timezone object with the specified name.
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Simple wrapper around Pendulum's Timezone function that ensures
|
|
52
|
+
consistent timezone handling across the library.
|
|
52
53
|
|
|
54
|
+
Parameters
|
|
55
|
+
name: Timezone name (e.g., 'US/Eastern', 'UTC')
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
A timezone object for the specified timezone
|
|
59
|
+
|
|
60
|
+
Examples
|
|
61
|
+
|
|
62
|
+
US/Eastern is equivalent to America/New_York:
|
|
53
63
|
>>> winter1 = DateTime(2000, 1, 1, 12, tzinfo=Timezone('US/Eastern'))
|
|
54
64
|
>>> winter2 = DateTime(2000, 1, 1, 12, tzinfo=Timezone('America/New_York'))
|
|
65
|
+
>>> winter1 == winter2
|
|
66
|
+
True
|
|
55
67
|
|
|
68
|
+
This works in both summer and winter:
|
|
56
69
|
>>> summer1 = DateTime(2000, 7, 1, 12, tzinfo=Timezone('US/Eastern'))
|
|
57
70
|
>>> summer2 = DateTime(2000, 7, 1, 12, tzinfo=Timezone('America/New_York'))
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
... winter1.astimezone(Timezone('America/New_York')),
|
|
61
|
-
... winter2.astimezone(Timezone('US/Eastern')),
|
|
62
|
-
... ]
|
|
63
|
-
>>> assert all(x==winter[0] for x in winter)
|
|
64
|
-
|
|
65
|
-
>>> summer = [summer1, summer2,
|
|
66
|
-
... summer1.astimezone(Timezone('America/New_York')),
|
|
67
|
-
... summer2.astimezone(Timezone('US/Eastern')),
|
|
68
|
-
... ]
|
|
69
|
-
>>> assert all(x==summer[0] for x in summer)
|
|
70
|
-
|
|
71
|
+
>>> summer1 == summer2
|
|
72
|
+
True
|
|
71
73
|
"""
|
|
72
74
|
return _pendulum.tz.Timezone(name)
|
|
73
75
|
|
|
@@ -172,8 +174,17 @@ expect_time = partial(expect, typ=_datetime.time)
|
|
|
172
174
|
|
|
173
175
|
|
|
174
176
|
def type_class(typ, obj):
|
|
177
|
+
if isinstance(typ, str):
|
|
178
|
+
if typ == 'Date':
|
|
179
|
+
return Date
|
|
180
|
+
if typ == 'DateTime':
|
|
181
|
+
return DateTime
|
|
182
|
+
if typ == 'Interval':
|
|
183
|
+
return Interval
|
|
175
184
|
if typ:
|
|
176
185
|
return typ
|
|
186
|
+
if obj.__class__ in {_pendulum.Interval, Interval}:
|
|
187
|
+
return Interval
|
|
177
188
|
if obj.__class__ in {_datetime.datetime, _pendulum.DateTime, DateTime}:
|
|
178
189
|
return DateTime
|
|
179
190
|
if obj.__class__ in {_datetime.date, _pendulum.Date, Date}:
|
|
@@ -240,7 +251,15 @@ expect_utc_timezone = partial(prefer_utc_timezone, force=True)
|
|
|
240
251
|
|
|
241
252
|
|
|
242
253
|
class Entity(ABC):
|
|
243
|
-
"""
|
|
254
|
+
"""Abstract base class for calendar entities with business day definitions.
|
|
255
|
+
|
|
256
|
+
This class defines the interface for calendar entities that provide
|
|
257
|
+
business day information, such as market open/close times and holidays.
|
|
258
|
+
Not available in pendulum.
|
|
259
|
+
|
|
260
|
+
Concrete implementations (like NYSE) provide specific calendar rules
|
|
261
|
+
for different business contexts.
|
|
262
|
+
"""
|
|
244
263
|
|
|
245
264
|
tz = UTC
|
|
246
265
|
|
|
@@ -261,7 +280,15 @@ class Entity(ABC):
|
|
|
261
280
|
|
|
262
281
|
|
|
263
282
|
class NYSE(Entity):
|
|
264
|
-
"""New York Stock Exchange
|
|
283
|
+
"""New York Stock Exchange calendar entity.
|
|
284
|
+
|
|
285
|
+
Provides business day definitions, market hours, and holidays
|
|
286
|
+
according to the NYSE trading calendar. Uses pandas_market_calendars
|
|
287
|
+
for the underlying implementation.
|
|
288
|
+
|
|
289
|
+
This entity is used as the default for business day calculations
|
|
290
|
+
throughout the library.
|
|
291
|
+
"""
|
|
265
292
|
|
|
266
293
|
BEGDATE = _datetime.date(1900, 1, 1)
|
|
267
294
|
ENDDATE = _datetime.date(2200, 1, 1)
|
|
@@ -293,27 +320,70 @@ class NYSE(Entity):
|
|
|
293
320
|
|
|
294
321
|
|
|
295
322
|
class DateBusinessMixin:
|
|
323
|
+
"""Mixin class providing business day functionality.
|
|
324
|
+
|
|
325
|
+
This mixin adds business day awareness to Date and DateTime classes,
|
|
326
|
+
allowing date operations to account for weekends and holidays according
|
|
327
|
+
to a specified calendar entity.
|
|
328
|
+
|
|
329
|
+
Features not available in pendulum:
|
|
330
|
+
- Business day mode toggle
|
|
331
|
+
- Entity-specific calendar rules
|
|
332
|
+
- Business-aware date arithmetic
|
|
333
|
+
"""
|
|
296
334
|
|
|
297
335
|
_entity: type[NYSE] = NYSE
|
|
298
336
|
_business: bool = False
|
|
299
337
|
|
|
300
338
|
def business(self) -> Self:
|
|
339
|
+
"""Switch to business day mode for date calculations.
|
|
340
|
+
|
|
341
|
+
In business day mode, date arithmetic only counts business days
|
|
342
|
+
as defined by the associated entity (default NYSE).
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
Self instance for method chaining
|
|
346
|
+
"""
|
|
301
347
|
self._business = True
|
|
302
348
|
return self
|
|
303
349
|
|
|
304
350
|
@property
|
|
305
351
|
def b(self) -> Self:
|
|
352
|
+
"""Shorthand property for business() method.
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
Self instance for method chaining
|
|
356
|
+
"""
|
|
306
357
|
return self.business()
|
|
307
358
|
|
|
308
359
|
def entity(self, entity: type[NYSE] = NYSE) -> Self:
|
|
360
|
+
"""Set the calendar entity for business day calculations.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
entity: Calendar entity class (defaults to NYSE)
|
|
364
|
+
|
|
365
|
+
Returns
|
|
366
|
+
Self instance for method chaining
|
|
367
|
+
"""
|
|
309
368
|
self._entity = entity
|
|
310
369
|
return self
|
|
311
370
|
|
|
312
371
|
@store_entity
|
|
313
372
|
def add(self, years: int = 0, months: int = 0, weeks: int = 0, days: int = 0, **kwargs) -> Self:
|
|
314
|
-
"""Add
|
|
315
|
-
|
|
316
|
-
|
|
373
|
+
"""Add time periods to the current date or datetime.
|
|
374
|
+
|
|
375
|
+
Extends pendulum's add method with business day awareness. When in business mode,
|
|
376
|
+
only counts business days for the 'days' parameter.
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
years: Number of years to add
|
|
380
|
+
months: Number of months to add
|
|
381
|
+
weeks: Number of weeks to add
|
|
382
|
+
days: Number of days to add (business days if in business mode)
|
|
383
|
+
**kwargs: Additional time units to add
|
|
384
|
+
|
|
385
|
+
Returns
|
|
386
|
+
New instance with added time
|
|
317
387
|
"""
|
|
318
388
|
_business = self._business
|
|
319
389
|
self._business = False
|
|
@@ -521,14 +591,15 @@ class DateBusinessMixin:
|
|
|
521
591
|
|
|
522
592
|
|
|
523
593
|
class DateExtrasMixin:
|
|
524
|
-
"""
|
|
525
|
-
scope of Pendulum. Ideally these should be removed.
|
|
526
|
-
|
|
527
|
-
See how pendulum does end_of and next_ with getattr
|
|
594
|
+
"""Extended date functionality not provided by Pendulum.
|
|
528
595
|
|
|
529
|
-
|
|
596
|
+
This mixin provides additional date utilities primarily focused on:
|
|
597
|
+
- Financial date calculations (nearest month start/end)
|
|
598
|
+
- Weekday-oriented date navigation
|
|
599
|
+
- Relative date lookups
|
|
530
600
|
|
|
531
|
-
|
|
601
|
+
These methods extend Pendulum's functionality with features commonly
|
|
602
|
+
needed in financial applications and reporting scenarios.
|
|
532
603
|
"""
|
|
533
604
|
|
|
534
605
|
def nearest_start_of_month(self):
|
|
@@ -635,17 +706,14 @@ class DateExtrasMixin:
|
|
|
635
706
|
|
|
636
707
|
@classmethod
|
|
637
708
|
def third_wednesday(cls, year, month):
|
|
638
|
-
"""
|
|
709
|
+
"""Calculate the date of the third Wednesday in a given month/year.
|
|
639
710
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
Date(2022, 12, 21)
|
|
647
|
-
>>> Date.third_wednesday(2023, 6)
|
|
648
|
-
Date(2023, 6, 21)
|
|
711
|
+
Parameters
|
|
712
|
+
year: The year to use
|
|
713
|
+
month: The month to use (1-12)
|
|
714
|
+
|
|
715
|
+
Returns
|
|
716
|
+
A Date object representing the third Wednesday of the specified month
|
|
649
717
|
"""
|
|
650
718
|
third = cls(year, month, 15) # lowest 3rd day
|
|
651
719
|
w = third.weekday()
|
|
@@ -655,7 +723,16 @@ class DateExtrasMixin:
|
|
|
655
723
|
|
|
656
724
|
|
|
657
725
|
class Date(DateExtrasMixin, DateBusinessMixin, _pendulum.Date):
|
|
658
|
-
"""
|
|
726
|
+
"""Date class extending pendulum.Date with business day and additional functionality.
|
|
727
|
+
|
|
728
|
+
This class inherits all pendulum.Date functionality while adding:
|
|
729
|
+
- Business day calculations with NYSE calendar integration
|
|
730
|
+
- Additional date navigation methods
|
|
731
|
+
- Enhanced parsing capabilities
|
|
732
|
+
- Custom financial date utilities
|
|
733
|
+
|
|
734
|
+
Unlike pendulum.Date, methods that create new instances return Date objects
|
|
735
|
+
that preserve business status and entity association when chained.
|
|
659
736
|
"""
|
|
660
737
|
|
|
661
738
|
def to_string(self, fmt: str) -> str:
|
|
@@ -666,6 +743,84 @@ class Date(DateExtrasMixin, DateBusinessMixin, _pendulum.Date):
|
|
|
666
743
|
"""
|
|
667
744
|
return self.strftime(fmt.replace('%-', '%#') if os.name == 'nt' else fmt)
|
|
668
745
|
|
|
746
|
+
@store_entity(typ='Date')
|
|
747
|
+
def replace(self, *args, **kwargs):
|
|
748
|
+
"""Replace method that preserves entity and business status.
|
|
749
|
+
"""
|
|
750
|
+
return _pendulum.Date.replace(self, *args, **kwargs)
|
|
751
|
+
|
|
752
|
+
@store_entity(typ='Date')
|
|
753
|
+
def closest(self, *args, **kwargs):
|
|
754
|
+
"""Closest method that preserves entity and business status.
|
|
755
|
+
"""
|
|
756
|
+
return _pendulum.Date.closest(self, *args, **kwargs)
|
|
757
|
+
|
|
758
|
+
@store_entity(typ='Date')
|
|
759
|
+
def farthest(self, *args, **kwargs):
|
|
760
|
+
"""Farthest method that preserves entity and business status.
|
|
761
|
+
"""
|
|
762
|
+
return _pendulum.Date.farthest(self, *args, **kwargs)
|
|
763
|
+
|
|
764
|
+
@store_entity(typ='Date')
|
|
765
|
+
def average(self, dt=None):
|
|
766
|
+
"""Modify the current instance to the average
|
|
767
|
+
of a given instance (default now) and the current instance.
|
|
768
|
+
|
|
769
|
+
Parameters
|
|
770
|
+
dt: The date to average with (defaults to today)
|
|
771
|
+
|
|
772
|
+
Returns
|
|
773
|
+
A new Date object representing the average date
|
|
774
|
+
"""
|
|
775
|
+
return _pendulum.Date.average(self)
|
|
776
|
+
|
|
777
|
+
@classmethod
|
|
778
|
+
def fromordinal(cls, *args, **kwargs):
|
|
779
|
+
"""Create a Date from an ordinal.
|
|
780
|
+
|
|
781
|
+
Parameters
|
|
782
|
+
n: The ordinal value
|
|
783
|
+
|
|
784
|
+
Returns
|
|
785
|
+
Date instance
|
|
786
|
+
"""
|
|
787
|
+
result = _pendulum.Date.fromordinal(*args, **kwargs)
|
|
788
|
+
return cls.instance(result)
|
|
789
|
+
|
|
790
|
+
@classmethod
|
|
791
|
+
def fromtimestamp(cls, timestamp, tz=None):
|
|
792
|
+
"""Create a Date from a timestamp.
|
|
793
|
+
|
|
794
|
+
Parameters
|
|
795
|
+
timestamp: Unix timestamp
|
|
796
|
+
tz: Optional timezone (defaults to UTC)
|
|
797
|
+
|
|
798
|
+
Returns
|
|
799
|
+
Date instance
|
|
800
|
+
"""
|
|
801
|
+
# Ensure timezone is always applied to get consistent results
|
|
802
|
+
tz = tz or UTC
|
|
803
|
+
dt = _datetime.datetime.fromtimestamp(timestamp, tz=tz)
|
|
804
|
+
return cls(dt.year, dt.month, dt.day)
|
|
805
|
+
|
|
806
|
+
@store_entity(typ='Date')
|
|
807
|
+
def nth_of(self, unit: str, nth: int, day_of_week: WeekDay) -> Self:
|
|
808
|
+
"""Returns a new instance set to the given occurrence
|
|
809
|
+
of a given day of the week in the current unit.
|
|
810
|
+
|
|
811
|
+
Parameters
|
|
812
|
+
unit: The unit to use ("month", "quarter", or "year")
|
|
813
|
+
nth: The position of the day in the unit (1 to 5)
|
|
814
|
+
day_of_week: The day of the week (pendulum.MONDAY to pendulum.SUNDAY)
|
|
815
|
+
|
|
816
|
+
Returns
|
|
817
|
+
A new Date object for the nth occurrence
|
|
818
|
+
|
|
819
|
+
Raises
|
|
820
|
+
ValueError: If the occurrence can't be found
|
|
821
|
+
"""
|
|
822
|
+
return _pendulum.Date.nth_of(self, unit, nth, day_of_week)
|
|
823
|
+
|
|
669
824
|
@classmethod
|
|
670
825
|
def parse(
|
|
671
826
|
cls,
|
|
@@ -795,7 +950,7 @@ class Date(DateExtrasMixin, DateBusinessMixin, _pendulum.Date):
|
|
|
795
950
|
return
|
|
796
951
|
|
|
797
952
|
with contextlib.suppress(ValueError):
|
|
798
|
-
if float(s) and
|
|
953
|
+
if float(s) and len(s) != 8: # 20000101
|
|
799
954
|
if raise_err:
|
|
800
955
|
raise ValueError('Invalid date: %s', s)
|
|
801
956
|
return
|
|
@@ -955,6 +1110,16 @@ class Date(DateExtrasMixin, DateBusinessMixin, _pendulum.Date):
|
|
|
955
1110
|
|
|
956
1111
|
|
|
957
1112
|
class Time(_pendulum.Time):
|
|
1113
|
+
"""Time class extending pendulum.Time with additional functionality.
|
|
1114
|
+
|
|
1115
|
+
This class inherits all pendulum.Time functionality while adding:
|
|
1116
|
+
- Enhanced parsing for various time formats
|
|
1117
|
+
- Default UTC timezone when created
|
|
1118
|
+
- Simple timezone conversion utilities
|
|
1119
|
+
|
|
1120
|
+
Unlike pendulum.Time, this class has more lenient parsing capabilities
|
|
1121
|
+
and different timezone defaults.
|
|
1122
|
+
"""
|
|
958
1123
|
|
|
959
1124
|
@classmethod
|
|
960
1125
|
@prefer_utc_timezone
|
|
@@ -1102,7 +1267,18 @@ class Time(_pendulum.Time):
|
|
|
1102
1267
|
|
|
1103
1268
|
|
|
1104
1269
|
class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
1105
|
-
"""
|
|
1270
|
+
"""DateTime class extending pendulum.DateTime with business day and additional functionality.
|
|
1271
|
+
|
|
1272
|
+
This class inherits all pendulum.DateTime functionality while adding:
|
|
1273
|
+
- Business day calculations with NYSE calendar integration
|
|
1274
|
+
- Enhanced timezone handling
|
|
1275
|
+
- Extended parsing capabilities
|
|
1276
|
+
- Custom utility methods for financial applications
|
|
1277
|
+
|
|
1278
|
+
Unlike pendulum.DateTime:
|
|
1279
|
+
- today() returns start of day rather than current time
|
|
1280
|
+
- Methods preserve business status and entity when chaining
|
|
1281
|
+
- Has timezone handling helpers not present in pendulum
|
|
1106
1282
|
"""
|
|
1107
1283
|
|
|
1108
1284
|
def epoch(self):
|
|
@@ -1110,6 +1286,95 @@ class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
|
1110
1286
|
"""
|
|
1111
1287
|
return self.timestamp()
|
|
1112
1288
|
|
|
1289
|
+
@store_entity(typ='DateTime')
|
|
1290
|
+
def astimezone(self, *args, **kwargs):
|
|
1291
|
+
"""Convert to a timezone-aware datetime in a different timezone.
|
|
1292
|
+
"""
|
|
1293
|
+
return _pendulum.DateTime.astimezone(self, *args, **kwargs)
|
|
1294
|
+
|
|
1295
|
+
@store_entity(typ='DateTime')
|
|
1296
|
+
def in_timezone(self, *args, **kwargs):
|
|
1297
|
+
"""Convert to a timezone-aware datetime in a different timezone.
|
|
1298
|
+
"""
|
|
1299
|
+
return _pendulum.DateTime.in_timezone(self, *args, **kwargs)
|
|
1300
|
+
|
|
1301
|
+
@store_entity(typ='DateTime')
|
|
1302
|
+
def in_tz(self, *args, **kwargs):
|
|
1303
|
+
"""Convert to a timezone-aware datetime in a different timezone.
|
|
1304
|
+
"""
|
|
1305
|
+
return _pendulum.DateTime.in_tz(self, *args, **kwargs)
|
|
1306
|
+
|
|
1307
|
+
@store_entity(typ='DateTime')
|
|
1308
|
+
def replace(self, *args, **kwargs):
|
|
1309
|
+
"""Replace method that preserves entity and business status.
|
|
1310
|
+
"""
|
|
1311
|
+
return _pendulum.DateTime.replace(self, *args, **kwargs)
|
|
1312
|
+
|
|
1313
|
+
@classmethod
|
|
1314
|
+
def fromordinal(cls, *args, **kwargs):
|
|
1315
|
+
"""Create a DateTime from an ordinal.
|
|
1316
|
+
|
|
1317
|
+
Parameters
|
|
1318
|
+
n: The ordinal value
|
|
1319
|
+
|
|
1320
|
+
Returns
|
|
1321
|
+
DateTime instance
|
|
1322
|
+
"""
|
|
1323
|
+
result = _pendulum.DateTime.fromordinal(*args, **kwargs)
|
|
1324
|
+
return cls.instance(result)
|
|
1325
|
+
|
|
1326
|
+
@classmethod
|
|
1327
|
+
def fromtimestamp(cls, timestamp, tz=None):
|
|
1328
|
+
"""Create a DateTime from a timestamp.
|
|
1329
|
+
|
|
1330
|
+
Parameters
|
|
1331
|
+
timestamp: Unix timestamp
|
|
1332
|
+
tz: Optional timezone
|
|
1333
|
+
|
|
1334
|
+
Returns
|
|
1335
|
+
DateTime instance
|
|
1336
|
+
"""
|
|
1337
|
+
tz = tz or UTC
|
|
1338
|
+
result = _pendulum.DateTime.fromtimestamp(timestamp, tz)
|
|
1339
|
+
return cls.instance(result)
|
|
1340
|
+
|
|
1341
|
+
@classmethod
|
|
1342
|
+
def strptime(cls, time_str, fmt):
|
|
1343
|
+
"""Parse a string into a DateTime according to a format.
|
|
1344
|
+
|
|
1345
|
+
Parameters
|
|
1346
|
+
time_str: String to parse
|
|
1347
|
+
fmt: Format string
|
|
1348
|
+
|
|
1349
|
+
Returns
|
|
1350
|
+
DateTime instance
|
|
1351
|
+
"""
|
|
1352
|
+
result = _pendulum.DateTime.strptime(time_str, fmt)
|
|
1353
|
+
return cls.instance(result)
|
|
1354
|
+
|
|
1355
|
+
@classmethod
|
|
1356
|
+
def utcfromtimestamp(cls, timestamp):
|
|
1357
|
+
"""Create a UTC DateTime from a timestamp.
|
|
1358
|
+
|
|
1359
|
+
Parameters
|
|
1360
|
+
timestamp: Unix timestamp
|
|
1361
|
+
|
|
1362
|
+
Returns
|
|
1363
|
+
DateTime instance
|
|
1364
|
+
"""
|
|
1365
|
+
result = _pendulum.DateTime.utcfromtimestamp(timestamp)
|
|
1366
|
+
return cls.instance(result)
|
|
1367
|
+
|
|
1368
|
+
@classmethod
|
|
1369
|
+
def utcnow(cls):
|
|
1370
|
+
"""Create a DateTime representing current UTC time.
|
|
1371
|
+
|
|
1372
|
+
Returns
|
|
1373
|
+
DateTime instance
|
|
1374
|
+
"""
|
|
1375
|
+
result = _pendulum.DateTime.utcnow()
|
|
1376
|
+
return cls.instance(result)
|
|
1377
|
+
|
|
1113
1378
|
@classmethod
|
|
1114
1379
|
def now(cls, tz: str | _zoneinfo.ZoneInfo | _datetime.tzinfo | None = None) -> Self:
|
|
1115
1380
|
"""Get a DateTime instance for the current date and time.
|
|
@@ -1127,7 +1392,16 @@ class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
|
1127
1392
|
|
|
1128
1393
|
@classmethod
|
|
1129
1394
|
def today(cls, tz: str | _zoneinfo.ZoneInfo | None = None):
|
|
1130
|
-
"""
|
|
1395
|
+
"""Create a DateTime object representing today at the start of day.
|
|
1396
|
+
|
|
1397
|
+
Unlike pendulum.today() which returns current time, this method
|
|
1398
|
+
returns a DateTime object at 00:00:00 of the current day.
|
|
1399
|
+
|
|
1400
|
+
Parameters
|
|
1401
|
+
tz: Optional timezone (defaults to local timezone)
|
|
1402
|
+
|
|
1403
|
+
Returns
|
|
1404
|
+
DateTime instance representing start of current day
|
|
1131
1405
|
"""
|
|
1132
1406
|
return DateTime.now(tz).start_of('day')
|
|
1133
1407
|
|
|
@@ -1174,38 +1448,44 @@ class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
|
1174
1448
|
entity: Entity = NYSE,
|
|
1175
1449
|
raise_err: bool = False
|
|
1176
1450
|
) -> Self | None:
|
|
1177
|
-
"""
|
|
1451
|
+
"""Convert a string or timestamp to a DateTime with extended format support.
|
|
1452
|
+
|
|
1453
|
+
Unlike pendulum's parse, this method supports:
|
|
1454
|
+
- Unix timestamps (int/float)
|
|
1455
|
+
- Special codes (T=today, Y=yesterday, P=previous business day)
|
|
1456
|
+
- Business day offsets (e.g., 'T-3b' for 3 business days before today)
|
|
1457
|
+
- Multiple date-time formats beyond ISO 8601
|
|
1458
|
+
|
|
1459
|
+
Parameters
|
|
1460
|
+
s: String or timestamp to parse
|
|
1461
|
+
entity: Calendar entity for business day calculations
|
|
1462
|
+
raise_err: Whether to raise error on parse failure
|
|
1178
1463
|
|
|
1464
|
+
Returns
|
|
1465
|
+
DateTime instance or None if parsing fails and raise_err is False
|
|
1466
|
+
|
|
1467
|
+
Examples
|
|
1468
|
+
|
|
1469
|
+
Basic formats:
|
|
1179
1470
|
>>> DateTime.parse('2022/1/1')
|
|
1180
1471
|
DateTime(2022, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1181
1472
|
|
|
1182
|
-
|
|
1473
|
+
Timezone handling:
|
|
1183
1474
|
>>> this_est1 = DateTime.parse('Fri, 31 Oct 2014 18:55:00').in_timezone(EST)
|
|
1184
1475
|
>>> this_est1
|
|
1185
1476
|
DateTime(2014, 10, 31, 14, 55, 0, tzinfo=Timezone('US/Eastern'))
|
|
1186
1477
|
|
|
1187
|
-
This is actually 18:55 UTC with -4 hours applied = EST
|
|
1188
1478
|
>>> this_est2 = DateTime.parse('Fri, 31 Oct 2014 14:55:00 -0400')
|
|
1189
1479
|
>>> this_est2
|
|
1190
1480
|
DateTime(2014, 10, 31, 14, 55, 0, tzinfo=...)
|
|
1191
1481
|
|
|
1192
|
-
UTC time technically equals GMT
|
|
1193
1482
|
>>> this_utc = DateTime.parse('Fri, 31 Oct 2014 18:55:00 GMT')
|
|
1194
1483
|
>>> this_utc
|
|
1195
1484
|
DateTime(2014, 10, 31, 18, 55, 0, tzinfo=tzutc())
|
|
1196
1485
|
|
|
1197
|
-
|
|
1198
|
-
>>> this_est1==this_est2==this_utc
|
|
1199
|
-
True
|
|
1200
|
-
|
|
1201
|
-
Format tests
|
|
1486
|
+
Timestamp parsing:
|
|
1202
1487
|
>>> DateTime.parse(1707856982).replace(tzinfo=UTC).epoch()
|
|
1203
1488
|
1707856982.0
|
|
1204
|
-
>>> DateTime.parse('Jan 29 2010')
|
|
1205
|
-
DateTime(2010, 1, 29, 0, 0, 0, tzinfo=Timezone('UTC'))
|
|
1206
|
-
>>> _ = DateTime.parse('Sep 27 17:11')
|
|
1207
|
-
>>> _.month, _.day, _.hour, _.minute
|
|
1208
|
-
(9, 27, 17, 11)
|
|
1209
1489
|
"""
|
|
1210
1490
|
if not s:
|
|
1211
1491
|
if raise_err:
|
|
@@ -1216,6 +1496,8 @@ class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
|
1216
1496
|
raise TypeError(f'Invalid type for datetime parse: {s.__class__}')
|
|
1217
1497
|
|
|
1218
1498
|
if isinstance(s, int | float):
|
|
1499
|
+
if len(str(int(s))) == 13:
|
|
1500
|
+
s /= 1000 # Convert from milliseconds to seconds
|
|
1219
1501
|
iso = _datetime.datetime.fromtimestamp(s).isoformat()
|
|
1220
1502
|
return cls.parse(iso).replace(tzinfo=LCL)
|
|
1221
1503
|
|
|
@@ -1254,41 +1536,46 @@ class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
|
1254
1536
|
tz: str | _zoneinfo.ZoneInfo | _datetime.tzinfo | None = None,
|
|
1255
1537
|
raise_err: bool = False,
|
|
1256
1538
|
) -> Self | None:
|
|
1257
|
-
"""
|
|
1539
|
+
"""Create a DateTime instance from various datetime-like objects.
|
|
1540
|
+
|
|
1541
|
+
This method provides a unified interface for converting different
|
|
1542
|
+
date/time types including pandas and numpy datetime objects into
|
|
1543
|
+
DateTime instances.
|
|
1544
|
+
|
|
1545
|
+
Unlike pendulum, this method:
|
|
1546
|
+
- Handles pandas Timestamp and numpy datetime64 objects
|
|
1547
|
+
- Adds timezone (UTC by default) when none is specified
|
|
1548
|
+
- Has special handling for time objects
|
|
1549
|
+
|
|
1550
|
+
Parameters
|
|
1551
|
+
obj: Date, datetime, time, or compatible object to convert
|
|
1552
|
+
tz: Optional timezone to apply (if None, uses obj's timezone or UTC)
|
|
1553
|
+
raise_err: Whether to raise error if obj is None/NA
|
|
1554
|
+
|
|
1555
|
+
Returns
|
|
1556
|
+
DateTime instance or None if obj is None/NA and raise_err is False
|
|
1557
|
+
|
|
1558
|
+
Examples
|
|
1258
1559
|
|
|
1560
|
+
From Python datetime types:
|
|
1259
1561
|
>>> DateTime.instance(_datetime.date(2022, 1, 1))
|
|
1260
1562
|
DateTime(2022, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1261
|
-
>>> DateTime.instance(Date(2022, 1, 1))
|
|
1262
|
-
DateTime(2022, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1263
1563
|
>>> DateTime.instance(_datetime.datetime(2022, 1, 1, 0, 0, 0))
|
|
1264
1564
|
DateTime(2022, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1265
|
-
>>> DateTime.instance(_pendulum.DateTime(2022, 1, 1, 0, 0, 0))
|
|
1266
|
-
DateTime(2022, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1267
|
-
>>> DateTime.instance(None)
|
|
1268
1565
|
|
|
1269
|
-
|
|
1566
|
+
Preserves timezone behavior:
|
|
1270
1567
|
>>> DateTime.instance(DateTime(2022, 1, 1, 0, 0, 0))
|
|
1271
1568
|
DateTime(2022, 1, 1, 0, 0, 0)
|
|
1272
|
-
>>> DateTime.instance(DateTime(2000, 1, 1))
|
|
1273
|
-
DateTime(2000, 1, 1, 0, 0, 0)
|
|
1274
1569
|
|
|
1275
|
-
|
|
1570
|
+
From Time objects:
|
|
1276
1571
|
>>> DateTime.instance(Time(4, 4, 21))
|
|
1277
1572
|
DateTime(..., 4, 4, 21, tzinfo=Timezone('UTC'))
|
|
1278
|
-
|
|
1279
|
-
tzinfo on time -> time tzinfo (precedence)
|
|
1280
1573
|
>>> DateTime.instance(Time(4, 4, 21, tzinfo=UTC))
|
|
1281
1574
|
DateTime(..., 4, 4, 21, tzinfo=Timezone('UTC'))
|
|
1282
|
-
>>> DateTime.instance(Time(4, 4, 21, tzinfo=LCL))
|
|
1283
|
-
DateTime(..., 4, 4, 21, tzinfo=Timezone('...'))
|
|
1284
1575
|
|
|
1576
|
+
From numpy/pandas datetime:
|
|
1285
1577
|
>>> DateTime.instance(np.datetime64('2000-01', 'D'))
|
|
1286
1578
|
DateTime(2000, 1, 1, 0, 0, 0, tzinfo=Timezone('UTC'))
|
|
1287
|
-
|
|
1288
|
-
Convert date to datetime (will use native time zone)
|
|
1289
|
-
>>> DateTime.instance(_datetime.date(2000, 1, 1))
|
|
1290
|
-
DateTime(2000, 1, 1, 0, 0, 0, tzinfo=Timezone('...'))
|
|
1291
|
-
|
|
1292
1579
|
"""
|
|
1293
1580
|
if pd.isna(obj):
|
|
1294
1581
|
if raise_err:
|
|
@@ -1716,33 +2003,5 @@ END:VCALENDAR
|
|
|
1716
2003
|
"""
|
|
1717
2004
|
|
|
1718
2005
|
|
|
1719
|
-
# apply any missing Date functions
|
|
1720
|
-
for func in (
|
|
1721
|
-
'average',
|
|
1722
|
-
'closest',
|
|
1723
|
-
'farthest',
|
|
1724
|
-
'fromordinal',
|
|
1725
|
-
'fromtimestamp',
|
|
1726
|
-
'nth_of',
|
|
1727
|
-
'replace',
|
|
1728
|
-
):
|
|
1729
|
-
setattr(Date, func, store_entity(getattr(_pendulum.Date, func), typ=Date))
|
|
1730
|
-
|
|
1731
|
-
# apply any missing DateTime functions
|
|
1732
|
-
for func in (
|
|
1733
|
-
'astimezone',
|
|
1734
|
-
'date',
|
|
1735
|
-
'fromordinal',
|
|
1736
|
-
'fromtimestamp',
|
|
1737
|
-
'in_timezone',
|
|
1738
|
-
'in_tz',
|
|
1739
|
-
'replace',
|
|
1740
|
-
'strptime',
|
|
1741
|
-
'utcfromtimestamp',
|
|
1742
|
-
'utcnow',
|
|
1743
|
-
):
|
|
1744
|
-
setattr(DateTime, func, store_entity(getattr(_pendulum.DateTime, func), typ=DateTime))
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
2006
|
if __name__ == '__main__':
|
|
1748
2007
|
__import__('doctest').testmod(optionflags=4 | 8 | 32)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|