opendate 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl
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.
- date/__init__.py +9 -6
- date/date.py +139 -183
- date/extras.py +90 -0
- {opendate-0.1.2.dist-info → opendate-0.1.3.dist-info}/METADATA +2 -2
- opendate-0.1.3.dist-info/RECORD +7 -0
- date/business.py +0 -42
- opendate-0.1.2.dist-info/RECORD +0 -7
- {opendate-0.1.2.dist-info → opendate-0.1.3.dist-info}/LICENSE +0 -0
- {opendate-0.1.2.dist-info → opendate-0.1.3.dist-info}/WHEEL +0 -0
date/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
__version__ = '0.1.
|
|
1
|
+
__version__ = '0.1.3'
|
|
2
2
|
|
|
3
3
|
import datetime as _datetime
|
|
4
4
|
|
|
@@ -31,7 +31,9 @@ from date.date import expect_utc_timezone
|
|
|
31
31
|
from date.date import prefer_native_timezone
|
|
32
32
|
from date.date import prefer_utc_timezone
|
|
33
33
|
from date.date import Timezone
|
|
34
|
-
from date.
|
|
34
|
+
from date.extras import overlap_days
|
|
35
|
+
from date.extras import is_business_day
|
|
36
|
+
from date.extras import is_within_business_hours
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
timezone = Timezone
|
|
@@ -57,8 +59,7 @@ def parse():
|
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
def instance(obj: _datetime.date | _datetime.datetime | _datetime.time) -> DateTime | Date | Time:
|
|
60
|
-
"""
|
|
61
|
-
Create a DateTime/Date/Time instance from a datetime/date/time native one.
|
|
62
|
+
"""Create a DateTime/Date/Time instance from a datetime/date/time native one.
|
|
62
63
|
"""
|
|
63
64
|
if isinstance(obj, DateTime | Date | Time):
|
|
64
65
|
return obj
|
|
@@ -104,6 +105,8 @@ __all__ = [
|
|
|
104
105
|
'NYSE',
|
|
105
106
|
'date',
|
|
106
107
|
'datetime',
|
|
107
|
-
'time'
|
|
108
|
-
'
|
|
108
|
+
'time',
|
|
109
|
+
'overlap_days',
|
|
110
|
+
'is_within_business_hours',
|
|
111
|
+
'is_business_day',
|
|
109
112
|
]
|
date/date.py
CHANGED
|
@@ -8,9 +8,7 @@ import time
|
|
|
8
8
|
import warnings
|
|
9
9
|
import zoneinfo as _zoneinfo
|
|
10
10
|
from abc import ABC, abstractmethod
|
|
11
|
-
from collections import namedtuple
|
|
12
11
|
from collections.abc import Callable, Sequence
|
|
13
|
-
from enum import IntEnum
|
|
14
12
|
from functools import lru_cache, partial, wraps
|
|
15
13
|
from typing import Self
|
|
16
14
|
|
|
@@ -31,8 +29,6 @@ __all__ = [
|
|
|
31
29
|
'IntervalError',
|
|
32
30
|
'Time',
|
|
33
31
|
'Timezone',
|
|
34
|
-
'WeekDay',
|
|
35
|
-
'WEEKDAY_SHORTNAME',
|
|
36
32
|
'EST',
|
|
37
33
|
'UTC',
|
|
38
34
|
'GMT',
|
|
@@ -45,6 +41,7 @@ __all__ = [
|
|
|
45
41
|
'expect_datetime',
|
|
46
42
|
'Entity',
|
|
47
43
|
'NYSE'
|
|
44
|
+
'WEEKDAY_SHORTNAME',
|
|
48
45
|
]
|
|
49
46
|
|
|
50
47
|
|
|
@@ -59,16 +56,7 @@ GMT = Timezone('GMT')
|
|
|
59
56
|
EST = Timezone('US/Eastern')
|
|
60
57
|
LCL = _pendulum.tz.Timezone(_pendulum.tz.get_local_timezone().name)
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
class WeekDay(IntEnum):
|
|
64
|
-
MONDAY = 0
|
|
65
|
-
TUESDAY = 1
|
|
66
|
-
WEDNESDAY = 2
|
|
67
|
-
THURSDAY = 3
|
|
68
|
-
FRIDAY = 4
|
|
69
|
-
SATURDAY = 5
|
|
70
|
-
SUNDAY = 6
|
|
71
|
-
|
|
59
|
+
WeekDay = _pendulum.day.WeekDay
|
|
72
60
|
|
|
73
61
|
WEEKDAY_SHORTNAME = {
|
|
74
62
|
'MO': WeekDay.MONDAY,
|
|
@@ -281,7 +269,7 @@ class NYSE(Entity):
|
|
|
281
269
|
if begdate <= d <= enddate}
|
|
282
270
|
|
|
283
271
|
|
|
284
|
-
class
|
|
272
|
+
class DateBusinessMixin:
|
|
285
273
|
|
|
286
274
|
_entity: type[NYSE] = NYSE
|
|
287
275
|
_business: bool = False
|
|
@@ -509,7 +497,141 @@ class PendulumBusinessDateMixin:
|
|
|
509
497
|
return self
|
|
510
498
|
|
|
511
499
|
|
|
512
|
-
class
|
|
500
|
+
class DateExtrasMixin:
|
|
501
|
+
"""Legacy support functionality well outside the
|
|
502
|
+
scope of Pendulum. Ideally these should be removed.
|
|
503
|
+
|
|
504
|
+
See how pendulum does end_of and next_ with getattr
|
|
505
|
+
|
|
506
|
+
Create a nearest [start_of, end_of] [week, day, month, quarter, year]
|
|
507
|
+
|
|
508
|
+
combo that accounts for whatever prefix and unit is passed in
|
|
509
|
+
"""
|
|
510
|
+
|
|
511
|
+
def nearest_start_of_month(self):
|
|
512
|
+
"""Get `nearest` start of month
|
|
513
|
+
|
|
514
|
+
1/1/2015 -> Thursday (New Year's Day)
|
|
515
|
+
2/1/2015 -> Sunday
|
|
516
|
+
|
|
517
|
+
>>> from date import Date
|
|
518
|
+
>>> Date(2015, 1, 1).nearest_start_of_month()
|
|
519
|
+
Date(2015, 1, 1)
|
|
520
|
+
>>> Date(2015, 1, 15).nearest_start_of_month()
|
|
521
|
+
Date(2015, 1, 1)
|
|
522
|
+
>>> Date(2015, 1, 15).b.nearest_start_of_month()
|
|
523
|
+
Date(2015, 1, 2)
|
|
524
|
+
>>> Date(2015, 1, 16).nearest_start_of_month()
|
|
525
|
+
Date(2015, 2, 1)
|
|
526
|
+
>>> Date(2015, 1, 31).nearest_start_of_month()
|
|
527
|
+
Date(2015, 2, 1)
|
|
528
|
+
>>> Date(2015, 1, 31).b.nearest_start_of_month()
|
|
529
|
+
Date(2015, 2, 2)
|
|
530
|
+
"""
|
|
531
|
+
_business = self._business
|
|
532
|
+
self._business = False
|
|
533
|
+
if self.day > 15:
|
|
534
|
+
d = self.end_of('month')
|
|
535
|
+
if _business:
|
|
536
|
+
return d.business().add(days=1)
|
|
537
|
+
return d.add(days=1)
|
|
538
|
+
d = self.start_of('month')
|
|
539
|
+
if _business:
|
|
540
|
+
return d.business().add(days=1)
|
|
541
|
+
return d
|
|
542
|
+
|
|
543
|
+
def nearest_end_of_month(self):
|
|
544
|
+
"""Get `nearest` end of month
|
|
545
|
+
|
|
546
|
+
12/31/2014 -> Wednesday
|
|
547
|
+
1/31/2015 -> Saturday
|
|
548
|
+
|
|
549
|
+
>>> from date import Date
|
|
550
|
+
>>> Date(2015, 1, 1).nearest_end_of_month()
|
|
551
|
+
Date(2014, 12, 31)
|
|
552
|
+
>>> Date(2015, 1, 15).nearest_end_of_month()
|
|
553
|
+
Date(2014, 12, 31)
|
|
554
|
+
>>> Date(2015, 1, 15).b.nearest_end_of_month()
|
|
555
|
+
Date(2014, 12, 31)
|
|
556
|
+
>>> Date(2015, 1, 16).nearest_end_of_month()
|
|
557
|
+
Date(2015, 1, 31)
|
|
558
|
+
>>> Date(2015, 1, 31).nearest_end_of_month()
|
|
559
|
+
Date(2015, 1, 31)
|
|
560
|
+
>>> Date(2015, 1, 31).b.nearest_end_of_month()
|
|
561
|
+
Date(2015, 1, 30)
|
|
562
|
+
"""
|
|
563
|
+
_business = self._business
|
|
564
|
+
self._business = False
|
|
565
|
+
if self.day <= 15:
|
|
566
|
+
d = self.start_of('month')
|
|
567
|
+
if _business:
|
|
568
|
+
return d.business().subtract(days=1)
|
|
569
|
+
return d.subtract(days=1)
|
|
570
|
+
d = self.end_of('month')
|
|
571
|
+
if _business:
|
|
572
|
+
return d.business().subtract(days=1)
|
|
573
|
+
return d
|
|
574
|
+
|
|
575
|
+
def next_relative_date_of_week_by_day(self, day='MO'):
|
|
576
|
+
"""Get next relative day of week by relativedelta code
|
|
577
|
+
|
|
578
|
+
>>> from date import Date
|
|
579
|
+
>>> Date(2020, 5, 18).next_relative_date_of_week_by_day('SU')
|
|
580
|
+
Date(2020, 5, 24)
|
|
581
|
+
>>> Date(2020, 5, 24).next_relative_date_of_week_by_day('SU')
|
|
582
|
+
Date(2020, 5, 24)
|
|
583
|
+
"""
|
|
584
|
+
if self.weekday() == WEEKDAY_SHORTNAME.get(day):
|
|
585
|
+
return self
|
|
586
|
+
return self.next(WEEKDAY_SHORTNAME.get(day))
|
|
587
|
+
|
|
588
|
+
def weekday_or_previous_friday(self):
|
|
589
|
+
"""Return the date if it is a weekday, else previous Friday
|
|
590
|
+
|
|
591
|
+
>>> from date import Date
|
|
592
|
+
>>> Date(2019, 10, 6).weekday_or_previous_friday() # Sunday
|
|
593
|
+
Date(2019, 10, 4)
|
|
594
|
+
>>> Date(2019, 10, 5).weekday_or_previous_friday() # Saturday
|
|
595
|
+
Date(2019, 10, 4)
|
|
596
|
+
>>> Date(2019, 10, 4).weekday_or_previous_friday() # Friday
|
|
597
|
+
Date(2019, 10, 4)
|
|
598
|
+
>>> Date(2019, 10, 3).weekday_or_previous_friday() # Thursday
|
|
599
|
+
Date(2019, 10, 3)
|
|
600
|
+
"""
|
|
601
|
+
dnum = self.weekday()
|
|
602
|
+
if dnum in {WeekDay.SATURDAY, WeekDay.SUNDAY}:
|
|
603
|
+
return self.subtract(days=dnum - 4)
|
|
604
|
+
return self
|
|
605
|
+
|
|
606
|
+
"""
|
|
607
|
+
create a simple nth weekday function that accounts for
|
|
608
|
+
[1,2,3,4] and weekday as options
|
|
609
|
+
or weekday, [1,2,3,4]
|
|
610
|
+
|
|
611
|
+
"""
|
|
612
|
+
|
|
613
|
+
@classmethod
|
|
614
|
+
def third_wednesday(cls, year, month):
|
|
615
|
+
"""Third Wednesday date of a given month/year
|
|
616
|
+
|
|
617
|
+
>>> from date import Date
|
|
618
|
+
>>> Date.third_wednesday(2022, 6)
|
|
619
|
+
Date(2022, 6, 15)
|
|
620
|
+
>>> Date.third_wednesday(2023, 3)
|
|
621
|
+
Date(2023, 3, 15)
|
|
622
|
+
>>> Date.third_wednesday(2022, 12)
|
|
623
|
+
Date(2022, 12, 21)
|
|
624
|
+
>>> Date.third_wednesday(2023, 6)
|
|
625
|
+
Date(2023, 6, 21)
|
|
626
|
+
"""
|
|
627
|
+
third = cls(year, month, 15) # lowest 3rd day
|
|
628
|
+
w = third.weekday()
|
|
629
|
+
if w != WeekDay.WEDNESDAY:
|
|
630
|
+
third = third.replace(day=(15 + (WeekDay.WEDNESDAY - w) % 7))
|
|
631
|
+
return third
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class Date(DateExtrasMixin, DateBusinessMixin, _pendulum.Date):
|
|
513
635
|
"""Inherits and wraps pendulum.Date
|
|
514
636
|
"""
|
|
515
637
|
|
|
@@ -768,105 +890,6 @@ class Date(PendulumBusinessDateMixin, _pendulum.Date):
|
|
|
768
890
|
with contextlib.suppress(Exception):
|
|
769
891
|
return self.isocalendar()[1]
|
|
770
892
|
|
|
771
|
-
"""
|
|
772
|
-
See how pendulum does end_of and next_ with getattr
|
|
773
|
-
|
|
774
|
-
Create a nearest [start_of, end_of] [week, day, month, quarter, year]
|
|
775
|
-
|
|
776
|
-
combo that accounts for whatever prefix and unit is passed in
|
|
777
|
-
"""
|
|
778
|
-
|
|
779
|
-
def nearest_start_of_month(self):
|
|
780
|
-
"""Get `nearest` start of month
|
|
781
|
-
|
|
782
|
-
1/1/2015 -> Thursday (New Year's Day)
|
|
783
|
-
2/1/2015 -> Sunday
|
|
784
|
-
|
|
785
|
-
>>> Date(2015, 1, 1).nearest_start_of_month()
|
|
786
|
-
Date(2015, 1, 1)
|
|
787
|
-
>>> Date(2015, 1, 15).nearest_start_of_month()
|
|
788
|
-
Date(2015, 1, 1)
|
|
789
|
-
>>> Date(2015, 1, 15).b.nearest_start_of_month()
|
|
790
|
-
Date(2015, 1, 2)
|
|
791
|
-
>>> Date(2015, 1, 16).nearest_start_of_month()
|
|
792
|
-
Date(2015, 2, 1)
|
|
793
|
-
>>> Date(2015, 1, 31).nearest_start_of_month()
|
|
794
|
-
Date(2015, 2, 1)
|
|
795
|
-
>>> Date(2015, 1, 31).b.nearest_start_of_month()
|
|
796
|
-
Date(2015, 2, 2)
|
|
797
|
-
"""
|
|
798
|
-
_business = self._business
|
|
799
|
-
self._business = False
|
|
800
|
-
if self.day > 15:
|
|
801
|
-
d = self.end_of('month')
|
|
802
|
-
if _business:
|
|
803
|
-
return d.business().add(days=1)
|
|
804
|
-
return d.add(days=1)
|
|
805
|
-
d = self.start_of('month')
|
|
806
|
-
if _business:
|
|
807
|
-
return d.business().add(days=1)
|
|
808
|
-
return d
|
|
809
|
-
|
|
810
|
-
def nearest_end_of_month(self):
|
|
811
|
-
"""Get `nearest` end of month
|
|
812
|
-
|
|
813
|
-
12/31/2014 -> Wednesday
|
|
814
|
-
1/31/2015 -> Saturday
|
|
815
|
-
|
|
816
|
-
>>> Date(2015, 1, 1).nearest_end_of_month()
|
|
817
|
-
Date(2014, 12, 31)
|
|
818
|
-
>>> Date(2015, 1, 15).nearest_end_of_month()
|
|
819
|
-
Date(2014, 12, 31)
|
|
820
|
-
>>> Date(2015, 1, 15).b.nearest_end_of_month()
|
|
821
|
-
Date(2014, 12, 31)
|
|
822
|
-
>>> Date(2015, 1, 16).nearest_end_of_month()
|
|
823
|
-
Date(2015, 1, 31)
|
|
824
|
-
>>> Date(2015, 1, 31).nearest_end_of_month()
|
|
825
|
-
Date(2015, 1, 31)
|
|
826
|
-
>>> Date(2015, 1, 31).b.nearest_end_of_month()
|
|
827
|
-
Date(2015, 1, 30)
|
|
828
|
-
"""
|
|
829
|
-
_business = self._business
|
|
830
|
-
self._business = False
|
|
831
|
-
if self.day <= 15:
|
|
832
|
-
d = self.start_of('month')
|
|
833
|
-
if _business:
|
|
834
|
-
return d.business().subtract(days=1)
|
|
835
|
-
return d.subtract(days=1)
|
|
836
|
-
d = self.end_of('month')
|
|
837
|
-
if _business:
|
|
838
|
-
return d.business().subtract(days=1)
|
|
839
|
-
return d
|
|
840
|
-
|
|
841
|
-
def next_relative_date_of_week_by_day(self, day='MO'):
|
|
842
|
-
"""Get next relative day of week by relativedelta code
|
|
843
|
-
|
|
844
|
-
>>> Date(2020, 5, 18).next_relative_date_of_week_by_day('SU')
|
|
845
|
-
Date(2020, 5, 24)
|
|
846
|
-
>>> Date(2020, 5, 24).next_relative_date_of_week_by_day('SU')
|
|
847
|
-
Date(2020, 5, 24)
|
|
848
|
-
"""
|
|
849
|
-
if self.weekday() == WEEKDAY_SHORTNAME.get(day):
|
|
850
|
-
return self
|
|
851
|
-
return self.next(WEEKDAY_SHORTNAME.get(day))
|
|
852
|
-
|
|
853
|
-
def weekday_or_previous_friday(self):
|
|
854
|
-
"""Return the date if it is a weekday, else previous Friday
|
|
855
|
-
|
|
856
|
-
>>> Date(2019, 10, 6).weekday_or_previous_friday() # Sunday
|
|
857
|
-
Date(2019, 10, 4)
|
|
858
|
-
>>> Date(2019, 10, 5).weekday_or_previous_friday() # Saturday
|
|
859
|
-
Date(2019, 10, 4)
|
|
860
|
-
>>> Date(2019, 10, 4).weekday_or_previous_friday() # Friday
|
|
861
|
-
Date(2019, 10, 4)
|
|
862
|
-
>>> Date(2019, 10, 3).weekday_or_previous_friday() # Thursday
|
|
863
|
-
Date(2019, 10, 3)
|
|
864
|
-
"""
|
|
865
|
-
dnum = self.weekday()
|
|
866
|
-
if dnum in {WeekDay.SATURDAY, WeekDay.SUNDAY}:
|
|
867
|
-
return self.subtract(days=dnum - 4)
|
|
868
|
-
return self
|
|
869
|
-
|
|
870
893
|
def lookback(self, unit='last') -> Self:
|
|
871
894
|
"""Date back based on lookback string, ie last, week, month.
|
|
872
895
|
|
|
@@ -897,32 +920,6 @@ class Date(PendulumBusinessDateMixin, _pendulum.Date):
|
|
|
897
920
|
'year': _lookback(years=1),
|
|
898
921
|
}.get(unit)
|
|
899
922
|
|
|
900
|
-
"""
|
|
901
|
-
create a simple nth weekday function that accounts for
|
|
902
|
-
[1,2,3,4] and weekday as options
|
|
903
|
-
or weekday, [1,2,3,4]
|
|
904
|
-
|
|
905
|
-
"""
|
|
906
|
-
|
|
907
|
-
@staticmethod
|
|
908
|
-
def third_wednesday(year, month):
|
|
909
|
-
"""Third Wednesday date of a given month/year
|
|
910
|
-
|
|
911
|
-
>>> Date.third_wednesday(2022, 6)
|
|
912
|
-
Date(2022, 6, 15)
|
|
913
|
-
>>> Date.third_wednesday(2023, 3)
|
|
914
|
-
Date(2023, 3, 15)
|
|
915
|
-
>>> Date.third_wednesday(2022, 12)
|
|
916
|
-
Date(2022, 12, 21)
|
|
917
|
-
>>> Date.third_wednesday(2023, 6)
|
|
918
|
-
Date(2023, 6, 21)
|
|
919
|
-
"""
|
|
920
|
-
third = Date(year, month, 15) # lowest 3rd day
|
|
921
|
-
w = third.weekday()
|
|
922
|
-
if w != WeekDay.WEDNESDAY:
|
|
923
|
-
third = third.replace(day=(15 + (WeekDay.WEDNESDAY - w) % 7))
|
|
924
|
-
return third
|
|
925
|
-
|
|
926
923
|
|
|
927
924
|
class Time(_pendulum.Time):
|
|
928
925
|
|
|
@@ -1050,7 +1047,7 @@ class Time(_pendulum.Time):
|
|
|
1050
1047
|
tzinfo=obj.tzinfo or tz)
|
|
1051
1048
|
|
|
1052
1049
|
|
|
1053
|
-
class DateTime(
|
|
1050
|
+
class DateTime(DateBusinessMixin, _pendulum.DateTime):
|
|
1054
1051
|
"""Inherits and wraps pendulum.DateTime
|
|
1055
1052
|
"""
|
|
1056
1053
|
|
|
@@ -1613,47 +1610,6 @@ class Interval:
|
|
|
1613
1610
|
raise ValueError('Basis range [0, 4]. Unknown basis {basis}.')
|
|
1614
1611
|
|
|
1615
1612
|
|
|
1616
|
-
Range = namedtuple('Range', ['start', 'end'])
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
def overlap_days(range_one, range_two, days=False):
|
|
1620
|
-
"""Test by how much two date ranges overlap
|
|
1621
|
-
if `days=True`, we return an actual day count,
|
|
1622
|
-
otherwise we just return if it overlaps True/False
|
|
1623
|
-
poached from Raymond Hettinger http://stackoverflow.com/a/9044111
|
|
1624
|
-
|
|
1625
|
-
>>> date1 = Date(2016, 3, 1)
|
|
1626
|
-
>>> date2 = Date(2016, 3, 2)
|
|
1627
|
-
>>> date3 = Date(2016, 3, 29)
|
|
1628
|
-
>>> date4 = Date(2016, 3, 30)
|
|
1629
|
-
|
|
1630
|
-
>>> assert overlap_days((date1, date3), (date2, date4))
|
|
1631
|
-
>>> assert overlap_days((date2, date4), (date1, date3))
|
|
1632
|
-
>>> assert not overlap_days((date1, date2), (date3, date4))
|
|
1633
|
-
|
|
1634
|
-
>>> assert overlap_days((date1, date4), (date1, date4))
|
|
1635
|
-
>>> assert overlap_days((date1, date4), (date2, date3))
|
|
1636
|
-
>>> overlap_days((date1, date4), (date1, date4), True)
|
|
1637
|
-
30
|
|
1638
|
-
|
|
1639
|
-
>>> assert overlap_days((date2, date3), (date1, date4))
|
|
1640
|
-
>>> overlap_days((date2, date3), (date1, date4), True)
|
|
1641
|
-
28
|
|
1642
|
-
|
|
1643
|
-
>>> assert not overlap_days((date3, date4), (date1, date2))
|
|
1644
|
-
>>> overlap_days((date3, date4), (date1, date2), True)
|
|
1645
|
-
-26
|
|
1646
|
-
"""
|
|
1647
|
-
r1 = Range(*range_one)
|
|
1648
|
-
r2 = Range(*range_two)
|
|
1649
|
-
latest_start = max(r1.start, r2.start)
|
|
1650
|
-
earliest_end = min(r1.end, r2.end)
|
|
1651
|
-
overlap = (earliest_end - latest_start).days + 1
|
|
1652
|
-
if days:
|
|
1653
|
-
return overlap
|
|
1654
|
-
return overlap >= 0
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
1613
|
def create_ics(begdate, enddate, summary, location):
|
|
1658
1614
|
"""Create a simple .ics file per RFC 5545 guidelines."""
|
|
1659
1615
|
|
date/extras.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from collections import namedtuple
|
|
2
|
+
|
|
3
|
+
from date import NYSE, DateTime, Entity
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
'is_within_business_hours',
|
|
7
|
+
'is_business_day',
|
|
8
|
+
'overlap_days',
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_within_business_hours(entity: Entity = NYSE) -> bool:
|
|
13
|
+
"""Return whether the current native datetime is between
|
|
14
|
+
open and close of business hours.
|
|
15
|
+
|
|
16
|
+
>>> from unittest.mock import patch
|
|
17
|
+
>>> tz = NYSE.tz
|
|
18
|
+
|
|
19
|
+
>>> with patch('date.DateTime.now') as mock:
|
|
20
|
+
... mock.return_value = DateTime(2000, 5, 1, 12, 30, 0, 0, tzinfo=tz)
|
|
21
|
+
... is_within_business_hours()
|
|
22
|
+
True
|
|
23
|
+
|
|
24
|
+
>>> with patch('date.DateTime.now') as mock:
|
|
25
|
+
... mock.return_value = DateTime(2000, 7, 2, 12, 15, 0, 0, tzinfo=tz) # Sunday
|
|
26
|
+
... is_within_business_hours()
|
|
27
|
+
False
|
|
28
|
+
|
|
29
|
+
>>> with patch('date.DateTime.now') as mock:
|
|
30
|
+
... mock.return_value = DateTime(2000, 11, 1, 1, 15, 0, 0, tzinfo=tz)
|
|
31
|
+
... is_within_business_hours()
|
|
32
|
+
False
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
this = DateTime.now()
|
|
36
|
+
this_entity = DateTime.now(tz=entity.tz).entity(entity)
|
|
37
|
+
bounds = this_entity.business_hours()
|
|
38
|
+
return this_entity.business_open() and (bounds[0] <= this.astimezone(entity.tz) <= bounds[1])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_business_day(entity: Entity = NYSE) -> bool:
|
|
42
|
+
"""Return whether the current native datetime is a business day.
|
|
43
|
+
"""
|
|
44
|
+
return DateTime.now(tz=entity.tz).entity(entity).is_business_day()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Range = namedtuple('Range', ['start', 'end'])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def overlap_days(range_one, range_two, days=False):
|
|
51
|
+
"""Test by how much two date ranges overlap
|
|
52
|
+
if `days=True`, we return an actual day count,
|
|
53
|
+
otherwise we just return if it overlaps True/False
|
|
54
|
+
poached from Raymond Hettinger http://stackoverflow.com/a/9044111
|
|
55
|
+
|
|
56
|
+
>>> from date import Date
|
|
57
|
+
>>> date1 = Date(2016, 3, 1)
|
|
58
|
+
>>> date2 = Date(2016, 3, 2)
|
|
59
|
+
>>> date3 = Date(2016, 3, 29)
|
|
60
|
+
>>> date4 = Date(2016, 3, 30)
|
|
61
|
+
|
|
62
|
+
>>> assert overlap_days((date1, date3), (date2, date4))
|
|
63
|
+
>>> assert overlap_days((date2, date4), (date1, date3))
|
|
64
|
+
>>> assert not overlap_days((date1, date2), (date3, date4))
|
|
65
|
+
|
|
66
|
+
>>> assert overlap_days((date1, date4), (date1, date4))
|
|
67
|
+
>>> assert overlap_days((date1, date4), (date2, date3))
|
|
68
|
+
>>> overlap_days((date1, date4), (date1, date4), True)
|
|
69
|
+
30
|
|
70
|
+
|
|
71
|
+
>>> assert overlap_days((date2, date3), (date1, date4))
|
|
72
|
+
>>> overlap_days((date2, date3), (date1, date4), True)
|
|
73
|
+
28
|
|
74
|
+
|
|
75
|
+
>>> assert not overlap_days((date3, date4), (date1, date2))
|
|
76
|
+
>>> overlap_days((date3, date4), (date1, date2), True)
|
|
77
|
+
-26
|
|
78
|
+
"""
|
|
79
|
+
r1 = Range(*range_one)
|
|
80
|
+
r2 = Range(*range_two)
|
|
81
|
+
latest_start = max(r1.start, r2.start)
|
|
82
|
+
earliest_end = min(r1.end, r2.end)
|
|
83
|
+
overlap = (earliest_end - latest_start).days + 1
|
|
84
|
+
if days:
|
|
85
|
+
return overlap
|
|
86
|
+
return overlap >= 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == '__main__':
|
|
90
|
+
__import__('doctest').testmod(optionflags=4 | 8 | 32)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: opendate
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary:
|
|
5
5
|
Home-page: https://github.com/bissli/opendate
|
|
6
6
|
License: MIT
|
|
@@ -49,7 +49,7 @@ thedate.business().add(days=5) # add 5 business day
|
|
|
49
49
|
|
|
50
50
|
# subtract days
|
|
51
51
|
thedate.subtract(days=5)
|
|
52
|
-
thedate.business().
|
|
52
|
+
thedate.business().subtract(days=5) # subtract 5 business day
|
|
53
53
|
|
|
54
54
|
# start of month
|
|
55
55
|
thedate.start_of('month')
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
date/__init__.py,sha256=OUG51eAQp1unyaQT2pxSXrsU_VUorfofQ6xcGLmJ9OM,2693
|
|
2
|
+
date/date.py,sha256=W6TcGhkRfaDNtY6gWeLfJy-pi80M10CTqWTpQKXhDEU,53971
|
|
3
|
+
date/extras.py,sha256=7xsOsdhKrmGoyLl5W4Xhg9TfuytaaIH7uKWW9PvR5sE,2832
|
|
4
|
+
opendate-0.1.3.dist-info/LICENSE,sha256=V4Rx8WWy7v8Fim6PHcEBszpZkDLbCHeorz1e_gr0Cbk,1111
|
|
5
|
+
opendate-0.1.3.dist-info/METADATA,sha256=cySMy5_i5RWt0XTzOO3UUmz6qSrBONhSiEtES2HXwMI,1816
|
|
6
|
+
opendate-0.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
7
|
+
opendate-0.1.3.dist-info/RECORD,,
|
date/business.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
from date import NYSE, DateTime, Entity
|
|
2
|
-
|
|
3
|
-
__all__ = ['is_within_business_hours', 'is_business_day']
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def is_within_business_hours(entity: Entity = NYSE) -> bool:
|
|
7
|
-
"""Return whether the current native datetime is between
|
|
8
|
-
open and close of business hours.
|
|
9
|
-
|
|
10
|
-
>>> from unittest.mock import patch
|
|
11
|
-
>>> tz = NYSE.tz
|
|
12
|
-
|
|
13
|
-
>>> with patch('date.DateTime.now') as mock:
|
|
14
|
-
... mock.return_value = DateTime(2000, 5, 1, 12, 30, 0, 0, tzinfo=tz)
|
|
15
|
-
... is_within_business_hours()
|
|
16
|
-
True
|
|
17
|
-
|
|
18
|
-
>>> with patch('date.DateTime.now') as mock:
|
|
19
|
-
... mock.return_value = DateTime(2000, 7, 2, 12, 15, 0, 0, tzinfo=tz) # Sunday
|
|
20
|
-
... is_within_business_hours()
|
|
21
|
-
False
|
|
22
|
-
|
|
23
|
-
>>> with patch('date.DateTime.now') as mock:
|
|
24
|
-
... mock.return_value = DateTime(2000, 11, 1, 1, 15, 0, 0, tzinfo=tz)
|
|
25
|
-
... is_within_business_hours()
|
|
26
|
-
False
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
this = DateTime.now()
|
|
30
|
-
this_entity = DateTime.now(tz=entity.tz).entity(entity)
|
|
31
|
-
bounds = this_entity.business_hours()
|
|
32
|
-
return this_entity.business_open() and (bounds[0] <= this.astimezone(entity.tz) <= bounds[1])
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def is_business_day(entity: Entity = NYSE) -> bool:
|
|
36
|
-
"""Return whether the current native datetime is a business day.
|
|
37
|
-
"""
|
|
38
|
-
return DateTime.now(tz=entity.tz).entity(entity).is_business_day()
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if __name__ == '__main__':
|
|
42
|
-
__import__('doctest').testmod(optionflags=4 | 8 | 32)
|
opendate-0.1.2.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
date/__init__.py,sha256=KRdrThrkKiQrf8b2VqAHx78hPCT1p0Tn0251k9ERFiE,2552
|
|
2
|
-
date/business.py,sha256=g4dHpmEaUuADgMxIcTG96AEviaRo9NvGDJ8IPKXD7xQ,1386
|
|
3
|
-
date/date.py,sha256=DrjvL_c3hYSHXaVVkccOpEyr7nDZvc02XUfSP8n_tNI,55210
|
|
4
|
-
opendate-0.1.2.dist-info/LICENSE,sha256=V4Rx8WWy7v8Fim6PHcEBszpZkDLbCHeorz1e_gr0Cbk,1111
|
|
5
|
-
opendate-0.1.2.dist-info/METADATA,sha256=qFurRfzxKJl1bx36as2o8KNXGufuQPYIhobsQOyFaDA,1815
|
|
6
|
-
opendate-0.1.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
7
|
-
opendate-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|