Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pendulum/datetime.py: 34%
494 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1# -*- coding: utf-8 -*-
2from __future__ import absolute_import
3from __future__ import division
5import calendar
6import datetime
8from typing import Optional
9from typing import TypeVar
10from typing import Union
12import pendulum
14from .constants import ATOM
15from .constants import COOKIE
16from .constants import MINUTES_PER_HOUR
17from .constants import MONTHS_PER_YEAR
18from .constants import RFC822
19from .constants import RFC850
20from .constants import RFC1036
21from .constants import RFC1123
22from .constants import RFC2822
23from .constants import RSS
24from .constants import SATURDAY
25from .constants import SECONDS_PER_DAY
26from .constants import SECONDS_PER_MINUTE
27from .constants import SUNDAY
28from .constants import W3C
29from .constants import YEARS_PER_CENTURY
30from .constants import YEARS_PER_DECADE
31from .date import Date
32from .exceptions import PendulumException
33from .helpers import add_duration
34from .helpers import timestamp
35from .period import Period
36from .time import Time
37from .tz import UTC
38from .tz.timezone import Timezone
39from .utils._compat import _HAS_FOLD
42_D = TypeVar("_D", bound="DateTime")
45class DateTime(datetime.datetime, Date):
47 EPOCH = None # type: DateTime
49 # Formats
51 _FORMATS = {
52 "atom": ATOM,
53 "cookie": COOKIE,
54 "iso8601": lambda dt: dt.isoformat(),
55 "rfc822": RFC822,
56 "rfc850": RFC850,
57 "rfc1036": RFC1036,
58 "rfc1123": RFC1123,
59 "rfc2822": RFC2822,
60 "rfc3339": lambda dt: dt.isoformat(),
61 "rss": RSS,
62 "w3c": W3C,
63 }
65 _EPOCH = datetime.datetime(1970, 1, 1, tzinfo=UTC)
67 _MODIFIERS_VALID_UNITS = [
68 "second",
69 "minute",
70 "hour",
71 "day",
72 "week",
73 "month",
74 "year",
75 "decade",
76 "century",
77 ]
79 if not _HAS_FOLD:
81 def __new__(
82 cls,
83 year,
84 month,
85 day,
86 hour=0,
87 minute=0,
88 second=0,
89 microsecond=0,
90 tzinfo=None,
91 fold=0,
92 ):
93 self = datetime.datetime.__new__(
94 cls, year, month, day, hour, minute, second, microsecond, tzinfo=tzinfo
95 )
97 self._fold = fold
99 return self
101 @classmethod
102 def now(cls, tz=None): # type: (Optional[Union[str, Timezone]]) -> DateTime
103 """
104 Get a DateTime instance for the current date and time.
105 """
106 return pendulum.now(tz)
108 @classmethod
109 def utcnow(cls): # type: () -> DateTime
110 """
111 Get a DateTime instance for the current date and time in UTC.
112 """
113 return pendulum.now(UTC)
115 @classmethod
116 def today(cls): # type: () -> DateTime
117 return pendulum.now()
119 @classmethod
120 def strptime(cls, time, fmt): # type: (str, str) -> DateTime
121 return pendulum.instance(datetime.datetime.strptime(time, fmt))
123 # Getters/Setters
125 def set(
126 self,
127 year=None,
128 month=None,
129 day=None,
130 hour=None,
131 minute=None,
132 second=None,
133 microsecond=None,
134 tz=None,
135 ):
136 if year is None:
137 year = self.year
138 if month is None:
139 month = self.month
140 if day is None:
141 day = self.day
142 if hour is None:
143 hour = self.hour
144 if minute is None:
145 minute = self.minute
146 if second is None:
147 second = self.second
148 if microsecond is None:
149 microsecond = self.microsecond
150 if tz is None:
151 tz = self.tz
153 return pendulum.datetime(
154 year, month, day, hour, minute, second, microsecond, tz=tz
155 )
157 if not _HAS_FOLD:
159 @property
160 def fold(self):
161 return self._fold
163 def timestamp(self):
164 if self.tzinfo is None:
165 s = timestamp(self)
167 return s + self.microsecond / 1e6
168 else:
169 kwargs = {"tzinfo": self.tzinfo}
171 if _HAS_FOLD:
172 kwargs["fold"] = self.fold
174 dt = datetime.datetime(
175 self.year,
176 self.month,
177 self.day,
178 self.hour,
179 self.minute,
180 self.second,
181 self.microsecond,
182 **kwargs
183 )
184 return (dt - self._EPOCH).total_seconds()
186 @property
187 def float_timestamp(self):
188 return self.timestamp()
190 @property
191 def int_timestamp(self):
192 # Workaround needed to avoid inaccuracy
193 # for far into the future datetimes
194 kwargs = {"tzinfo": self.tzinfo}
196 if _HAS_FOLD:
197 kwargs["fold"] = self.fold
199 dt = datetime.datetime(
200 self.year,
201 self.month,
202 self.day,
203 self.hour,
204 self.minute,
205 self.second,
206 self.microsecond,
207 **kwargs
208 )
210 delta = dt - self._EPOCH
212 return delta.days * SECONDS_PER_DAY + delta.seconds
214 @property
215 def offset(self):
216 return self.get_offset()
218 @property
219 def offset_hours(self):
220 return self.get_offset() / SECONDS_PER_MINUTE / MINUTES_PER_HOUR
222 @property
223 def timezone(self): # type: () -> Optional[Timezone]
224 if not isinstance(self.tzinfo, Timezone):
225 return
227 return self.tzinfo
229 @property
230 def tz(self): # type: () -> Optional[Timezone]
231 return self.timezone
233 @property
234 def timezone_name(self): # type: () -> Optional[str]
235 tz = self.timezone
237 if tz is None:
238 return None
240 return tz.name
242 @property
243 def age(self):
244 return self.date().diff(self.now(self.tz).date(), abs=False).in_years()
246 def is_local(self):
247 return self.offset == self.in_timezone(pendulum.local_timezone()).offset
249 def is_utc(self):
250 return self.offset == UTC.offset
252 def is_dst(self):
253 return self.dst() != datetime.timedelta()
255 def get_offset(self):
256 return int(self.utcoffset().total_seconds())
258 def date(self):
259 return Date(self.year, self.month, self.day)
261 def time(self):
262 return Time(self.hour, self.minute, self.second, self.microsecond)
264 def naive(self): # type: (_D) -> _D
265 """
266 Return the DateTime without timezone information.
267 """
268 return self.__class__(
269 self.year,
270 self.month,
271 self.day,
272 self.hour,
273 self.minute,
274 self.second,
275 self.microsecond,
276 )
278 def on(self, year, month, day):
279 """
280 Returns a new instance with the current date set to a different date.
282 :param year: The year
283 :type year: int
285 :param month: The month
286 :type month: int
288 :param day: The day
289 :type day: int
291 :rtype: DateTime
292 """
293 return self.set(year=int(year), month=int(month), day=int(day))
295 def at(self, hour, minute=0, second=0, microsecond=0):
296 """
297 Returns a new instance with the current time to a different time.
299 :param hour: The hour
300 :type hour: int
302 :param minute: The minute
303 :type minute: int
305 :param second: The second
306 :type second: int
308 :param microsecond: The microsecond
309 :type microsecond: int
311 :rtype: DateTime
312 """
313 return self.set(
314 hour=hour, minute=minute, second=second, microsecond=microsecond
315 )
317 def in_timezone(self, tz): # type: (Union[str, Timezone]) -> DateTime
318 """
319 Set the instance's timezone from a string or object.
320 """
321 tz = pendulum._safe_timezone(tz)
323 return tz.convert(self, dst_rule=pendulum.POST_TRANSITION)
325 def in_tz(self, tz): # type: (Union[str, Timezone]) -> DateTime
326 """
327 Set the instance's timezone from a string or object.
328 """
329 return self.in_timezone(tz)
331 # STRING FORMATTING
333 def to_time_string(self):
334 """
335 Format the instance as time.
337 :rtype: str
338 """
339 return self.format("HH:mm:ss")
341 def to_datetime_string(self):
342 """
343 Format the instance as date and time.
345 :rtype: str
346 """
347 return self.format("YYYY-MM-DD HH:mm:ss")
349 def to_day_datetime_string(self):
350 """
351 Format the instance as day, date and time (in english).
353 :rtype: str
354 """
355 return self.format("ddd, MMM D, YYYY h:mm A", locale="en")
357 def to_atom_string(self):
358 """
359 Format the instance as ATOM.
361 :rtype: str
362 """
363 return self._to_string("atom")
365 def to_cookie_string(self):
366 """
367 Format the instance as COOKIE.
369 :rtype: str
370 """
371 return self._to_string("cookie", locale="en")
373 def to_iso8601_string(self):
374 """
375 Format the instance as ISO 8601.
377 :rtype: str
378 """
379 string = self._to_string("iso8601")
381 if self.tz and self.tz.name == "UTC":
382 string = string.replace("+00:00", "Z")
384 return string
386 def to_rfc822_string(self):
387 """
388 Format the instance as RFC 822.
390 :rtype: str
391 """
392 return self._to_string("rfc822")
394 def to_rfc850_string(self):
395 """
396 Format the instance as RFC 850.
398 :rtype: str
399 """
400 return self._to_string("rfc850")
402 def to_rfc1036_string(self):
403 """
404 Format the instance as RFC 1036.
406 :rtype: str
407 """
408 return self._to_string("rfc1036")
410 def to_rfc1123_string(self):
411 """
412 Format the instance as RFC 1123.
414 :rtype: str
415 """
416 return self._to_string("rfc1123")
418 def to_rfc2822_string(self):
419 """
420 Format the instance as RFC 2822.
422 :rtype: str
423 """
424 return self._to_string("rfc2822")
426 def to_rfc3339_string(self):
427 """
428 Format the instance as RFC 3339.
430 :rtype: str
431 """
432 return self._to_string("rfc3339")
434 def to_rss_string(self):
435 """
436 Format the instance as RSS.
438 :rtype: str
439 """
440 return self._to_string("rss")
442 def to_w3c_string(self):
443 """
444 Format the instance as W3C.
446 :rtype: str
447 """
448 return self._to_string("w3c")
450 def _to_string(self, fmt, locale=None):
451 """
452 Format the instance to a common string format.
454 :param fmt: The name of the string format
455 :type fmt: string
457 :param locale: The locale to use
458 :type locale: str or None
460 :rtype: str
461 """
462 if fmt not in self._FORMATS:
463 raise ValueError("Format [{}] is not supported".format(fmt))
465 fmt = self._FORMATS[fmt]
466 if callable(fmt):
467 return fmt(self)
469 return self.format(fmt, locale=locale)
471 def __str__(self):
472 return self.isoformat("T")
474 def __repr__(self):
475 us = ""
476 if self.microsecond:
477 us = ", {}".format(self.microsecond)
479 repr_ = "{klass}(" "{year}, {month}, {day}, " "{hour}, {minute}, {second}{us}"
481 if self.tzinfo is not None:
482 repr_ += ", tzinfo={tzinfo}"
484 repr_ += ")"
486 return repr_.format(
487 klass=self.__class__.__name__,
488 year=self.year,
489 month=self.month,
490 day=self.day,
491 hour=self.hour,
492 minute=self.minute,
493 second=self.second,
494 us=us,
495 tzinfo=self.tzinfo,
496 )
498 # Comparisons
499 def closest(self, dt1, dt2, *dts):
500 """
501 Get the farthest date from the instance.
503 :type dt1: datetime.datetime
504 :type dt2: datetime.datetime
505 :type dts: list[datetime.datetime,]
507 :rtype: DateTime
508 """
509 dt1 = pendulum.instance(dt1)
510 dt2 = pendulum.instance(dt2)
511 dts = [dt1, dt2] + [pendulum.instance(x) for x in dts]
512 dts = [(abs(self - dt), dt) for dt in dts]
514 return min(dts)[1]
516 def farthest(self, dt1, dt2, *dts):
517 """
518 Get the farthest date from the instance.
520 :type dt1: datetime.datetime
521 :type dt2: datetime.datetime
522 :type dts: list[datetime.datetime,]
524 :rtype: DateTime
525 """
526 dt1 = pendulum.instance(dt1)
527 dt2 = pendulum.instance(dt2)
529 dts = [dt1, dt2] + [pendulum.instance(x) for x in dts]
530 dts = [(abs(self - dt), dt) for dt in dts]
532 return max(dts)[1]
534 def is_future(self):
535 """
536 Determines if the instance is in the future, ie. greater than now.
538 :rtype: bool
539 """
540 return self > self.now(self.timezone)
542 def is_past(self):
543 """
544 Determines if the instance is in the past, ie. less than now.
546 :rtype: bool
547 """
548 return self < self.now(self.timezone)
550 def is_long_year(self):
551 """
552 Determines if the instance is a long year
554 See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_
556 :rtype: bool
557 """
558 return (
559 pendulum.datetime(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1]
560 == 53
561 )
563 def is_same_day(self, dt):
564 """
565 Checks if the passed in date is the same day
566 as the instance current day.
568 :type dt: DateTime or datetime or str or int
570 :rtype: bool
571 """
572 dt = pendulum.instance(dt)
574 return self.to_date_string() == dt.to_date_string()
576 def is_anniversary(self, dt=None):
577 """
578 Check if its the anniversary.
579 Compares the date/month values of the two dates.
581 :rtype: bool
582 """
583 if dt is None:
584 dt = self.now(self.tz)
586 instance = pendulum.instance(dt)
588 return (self.month, self.day) == (instance.month, instance.day)
590 # the additional method for checking if today is the anniversary day
591 # the alias is provided to start using a new name and keep the backward compatibility
592 # the old name can be completely replaced with the new in one of the future versions
593 is_birthday = is_anniversary
595 # ADDITIONS AND SUBSTRACTIONS
597 def add(
598 self,
599 years=0,
600 months=0,
601 weeks=0,
602 days=0,
603 hours=0,
604 minutes=0,
605 seconds=0,
606 microseconds=0,
607 ): # type: (_D, int, int, int, int, int, int, int, int) -> _D
608 """
609 Add a duration to the instance.
611 If we're adding units of variable length (i.e., years, months),
612 move forward from curren time,
613 otherwise move forward from utc, for accuracy
614 when moving across DST boundaries.
615 """
616 units_of_variable_length = any([years, months, weeks, days])
618 current_dt = datetime.datetime(
619 self.year,
620 self.month,
621 self.day,
622 self.hour,
623 self.minute,
624 self.second,
625 self.microsecond,
626 )
627 if not units_of_variable_length:
628 offset = self.utcoffset()
629 if offset:
630 current_dt = current_dt - offset
632 dt = add_duration(
633 current_dt,
634 years=years,
635 months=months,
636 weeks=weeks,
637 days=days,
638 hours=hours,
639 minutes=minutes,
640 seconds=seconds,
641 microseconds=microseconds,
642 )
644 if units_of_variable_length or self.tzinfo is None:
645 return pendulum.datetime(
646 dt.year,
647 dt.month,
648 dt.day,
649 dt.hour,
650 dt.minute,
651 dt.second,
652 dt.microsecond,
653 tz=self.tz,
654 )
656 dt = self.__class__(
657 dt.year,
658 dt.month,
659 dt.day,
660 dt.hour,
661 dt.minute,
662 dt.second,
663 dt.microsecond,
664 tzinfo=UTC,
665 )
667 dt = self.tz.convert(dt)
669 return self.__class__(
670 dt.year,
671 dt.month,
672 dt.day,
673 dt.hour,
674 dt.minute,
675 dt.second,
676 dt.microsecond,
677 tzinfo=self.tz,
678 fold=dt.fold,
679 )
681 def subtract(
682 self,
683 years=0,
684 months=0,
685 weeks=0,
686 days=0,
687 hours=0,
688 minutes=0,
689 seconds=0,
690 microseconds=0,
691 ):
692 """
693 Remove duration from the instance.
695 :param years: The number of years
696 :type years: int
698 :param months: The number of months
699 :type months: int
701 :param weeks: The number of weeks
702 :type weeks: int
704 :param days: The number of days
705 :type days: int
707 :param hours: The number of hours
708 :type hours: int
710 :param minutes: The number of minutes
711 :type minutes: int
713 :param seconds: The number of seconds
714 :type seconds: int
716 :param microseconds: The number of microseconds
717 :type microseconds: int
719 :rtype: DateTime
720 """
721 return self.add(
722 years=-years,
723 months=-months,
724 weeks=-weeks,
725 days=-days,
726 hours=-hours,
727 minutes=-minutes,
728 seconds=-seconds,
729 microseconds=-microseconds,
730 )
732 # Adding a final underscore to the method name
733 # to avoid errors for PyPy which already defines
734 # a _add_timedelta method
735 def _add_timedelta_(self, delta):
736 """
737 Add timedelta duration to the instance.
739 :param delta: The timedelta instance
740 :type delta: pendulum.Duration or datetime.timedelta
742 :rtype: DateTime
743 """
744 if isinstance(delta, pendulum.Period):
745 return self.add(
746 years=delta.years,
747 months=delta.months,
748 weeks=delta.weeks,
749 days=delta.remaining_days,
750 hours=delta.hours,
751 minutes=delta.minutes,
752 seconds=delta.remaining_seconds,
753 microseconds=delta.microseconds,
754 )
755 elif isinstance(delta, pendulum.Duration):
756 return self.add(
757 years=delta.years, months=delta.months, seconds=delta._total
758 )
760 return self.add(seconds=delta.total_seconds())
762 def _subtract_timedelta(self, delta):
763 """
764 Remove timedelta duration from the instance.
766 :param delta: The timedelta instance
767 :type delta: pendulum.Duration or datetime.timedelta
769 :rtype: DateTime
770 """
771 if isinstance(delta, pendulum.Duration):
772 return self.subtract(
773 years=delta.years, months=delta.months, seconds=delta._total
774 )
776 return self.subtract(seconds=delta.total_seconds())
778 # DIFFERENCES
780 def diff(self, dt=None, abs=True):
781 """
782 Returns the difference between two DateTime objects represented as a Duration.
784 :type dt: DateTime or None
786 :param abs: Whether to return an absolute interval or not
787 :type abs: bool
789 :rtype: Period
790 """
791 if dt is None:
792 dt = self.now(self.tz)
794 return Period(self, dt, absolute=abs)
796 def diff_for_humans(
797 self,
798 other=None, # type: Optional[DateTime]
799 absolute=False, # type: bool
800 locale=None, # type: Optional[str]
801 ): # type: (...) -> str
802 """
803 Get the difference in a human readable format in the current locale.
805 When comparing a value in the past to default now:
806 1 day ago
807 5 months ago
809 When comparing a value in the future to default now:
810 1 day from now
811 5 months from now
813 When comparing a value in the past to another value:
814 1 day before
815 5 months before
817 When comparing a value in the future to another value:
818 1 day after
819 5 months after
820 """
821 is_now = other is None
823 if is_now:
824 other = self.now()
826 diff = self.diff(other)
828 return pendulum.format_diff(diff, is_now, absolute, locale)
830 # Modifiers
831 def start_of(self, unit):
832 """
833 Returns a copy of the instance with the time reset
834 with the following rules:
836 * second: microsecond set to 0
837 * minute: second and microsecond set to 0
838 * hour: minute, second and microsecond set to 0
839 * day: time to 00:00:00
840 * week: date to first day of the week and time to 00:00:00
841 * month: date to first day of the month and time to 00:00:00
842 * year: date to first day of the year and time to 00:00:00
843 * decade: date to first day of the decade and time to 00:00:00
844 * century: date to first day of century and time to 00:00:00
846 :param unit: The unit to reset to
847 :type unit: str
849 :rtype: DateTime
850 """
851 if unit not in self._MODIFIERS_VALID_UNITS:
852 raise ValueError('Invalid unit "{}" for start_of()'.format(unit))
854 return getattr(self, "_start_of_{}".format(unit))()
856 def end_of(self, unit):
857 """
858 Returns a copy of the instance with the time reset
859 with the following rules:
861 * second: microsecond set to 999999
862 * minute: second set to 59 and microsecond set to 999999
863 * hour: minute and second set to 59 and microsecond set to 999999
864 * day: time to 23:59:59.999999
865 * week: date to last day of the week and time to 23:59:59.999999
866 * month: date to last day of the month and time to 23:59:59.999999
867 * year: date to last day of the year and time to 23:59:59.999999
868 * decade: date to last day of the decade and time to 23:59:59.999999
869 * century: date to last day of century and time to 23:59:59.999999
871 :param unit: The unit to reset to
872 :type unit: str
874 :rtype: DateTime
875 """
876 if unit not in self._MODIFIERS_VALID_UNITS:
877 raise ValueError('Invalid unit "%s" for end_of()' % unit)
879 return getattr(self, "_end_of_%s" % unit)()
881 def _start_of_second(self):
882 """
883 Reset microseconds to 0.
885 :rtype: DateTime
886 """
887 return self.set(microsecond=0)
889 def _end_of_second(self):
890 """
891 Set microseconds to 999999.
893 :rtype: DateTime
894 """
895 return self.set(microsecond=999999)
897 def _start_of_minute(self):
898 """
899 Reset seconds and microseconds to 0.
901 :rtype: DateTime
902 """
903 return self.set(second=0, microsecond=0)
905 def _end_of_minute(self):
906 """
907 Set seconds to 59 and microseconds to 999999.
909 :rtype: DateTime
910 """
911 return self.set(second=59, microsecond=999999)
913 def _start_of_hour(self):
914 """
915 Reset minutes, seconds and microseconds to 0.
917 :rtype: DateTime
918 """
919 return self.set(minute=0, second=0, microsecond=0)
921 def _end_of_hour(self):
922 """
923 Set minutes and seconds to 59 and microseconds to 999999.
925 :rtype: DateTime
926 """
927 return self.set(minute=59, second=59, microsecond=999999)
929 def _start_of_day(self):
930 """
931 Reset the time to 00:00:00
933 :rtype: DateTime
934 """
935 return self.at(0, 0, 0, 0)
937 def _end_of_day(self):
938 """
939 Reset the time to 23:59:59.999999
941 :rtype: DateTime
942 """
943 return self.at(23, 59, 59, 999999)
945 def _start_of_month(self):
946 """
947 Reset the date to the first day of the month and the time to 00:00:00.
949 :rtype: DateTime
950 """
951 return self.set(self.year, self.month, 1, 0, 0, 0, 0)
953 def _end_of_month(self):
954 """
955 Reset the date to the last day of the month
956 and the time to 23:59:59.999999.
958 :rtype: DateTime
959 """
960 return self.set(self.year, self.month, self.days_in_month, 23, 59, 59, 999999)
962 def _start_of_year(self):
963 """
964 Reset the date to the first day of the year and the time to 00:00:00.
966 :rtype: DateTime
967 """
968 return self.set(self.year, 1, 1, 0, 0, 0, 0)
970 def _end_of_year(self):
971 """
972 Reset the date to the last day of the year
973 and the time to 23:59:59.999999
975 :rtype: DateTime
976 """
977 return self.set(self.year, 12, 31, 23, 59, 59, 999999)
979 def _start_of_decade(self):
980 """
981 Reset the date to the first day of the decade
982 and the time to 00:00:00.
984 :rtype: DateTime
985 """
986 year = self.year - self.year % YEARS_PER_DECADE
987 return self.set(year, 1, 1, 0, 0, 0, 0)
989 def _end_of_decade(self):
990 """
991 Reset the date to the last day of the decade
992 and the time to 23:59:59.999999.
994 :rtype: DateTime
995 """
996 year = self.year - self.year % YEARS_PER_DECADE + YEARS_PER_DECADE - 1
998 return self.set(year, 12, 31, 23, 59, 59, 999999)
1000 def _start_of_century(self):
1001 """
1002 Reset the date to the first day of the century
1003 and the time to 00:00:00.
1005 :rtype: DateTime
1006 """
1007 year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + 1
1009 return self.set(year, 1, 1, 0, 0, 0, 0)
1011 def _end_of_century(self):
1012 """
1013 Reset the date to the last day of the century
1014 and the time to 23:59:59.999999.
1016 :rtype: DateTime
1017 """
1018 year = self.year - 1 - (self.year - 1) % YEARS_PER_CENTURY + YEARS_PER_CENTURY
1020 return self.set(year, 12, 31, 23, 59, 59, 999999)
1022 def _start_of_week(self):
1023 """
1024 Reset the date to the first day of the week
1025 and the time to 00:00:00.
1027 :rtype: DateTime
1028 """
1029 dt = self
1031 if self.day_of_week != pendulum._WEEK_STARTS_AT:
1032 dt = self.previous(pendulum._WEEK_STARTS_AT)
1034 return dt.start_of("day")
1036 def _end_of_week(self):
1037 """
1038 Reset the date to the last day of the week
1039 and the time to 23:59:59.
1041 :rtype: DateTime
1042 """
1043 dt = self
1045 if self.day_of_week != pendulum._WEEK_ENDS_AT:
1046 dt = self.next(pendulum._WEEK_ENDS_AT)
1048 return dt.end_of("day")
1050 def next(self, day_of_week=None, keep_time=False):
1051 """
1052 Modify to the next occurrence of a given day of the week.
1053 If no day_of_week is provided, modify to the next occurrence
1054 of the current day of the week. Use the supplied consts
1055 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1057 :param day_of_week: The next day of week to reset to.
1058 :type day_of_week: int or None
1060 :param keep_time: Whether to keep the time information or not.
1061 :type keep_time: bool
1063 :rtype: DateTime
1064 """
1065 if day_of_week is None:
1066 day_of_week = self.day_of_week
1068 if day_of_week < SUNDAY or day_of_week > SATURDAY:
1069 raise ValueError("Invalid day of week")
1071 if keep_time:
1072 dt = self
1073 else:
1074 dt = self.start_of("day")
1076 dt = dt.add(days=1)
1077 while dt.day_of_week != day_of_week:
1078 dt = dt.add(days=1)
1080 return dt
1082 def previous(self, day_of_week=None, keep_time=False):
1083 """
1084 Modify to the previous occurrence of a given day of the week.
1085 If no day_of_week is provided, modify to the previous occurrence
1086 of the current day of the week. Use the supplied consts
1087 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1089 :param day_of_week: The previous day of week to reset to.
1090 :type day_of_week: int or None
1092 :param keep_time: Whether to keep the time information or not.
1093 :type keep_time: bool
1095 :rtype: DateTime
1096 """
1097 if day_of_week is None:
1098 day_of_week = self.day_of_week
1100 if day_of_week < SUNDAY or day_of_week > SATURDAY:
1101 raise ValueError("Invalid day of week")
1103 if keep_time:
1104 dt = self
1105 else:
1106 dt = self.start_of("day")
1108 dt = dt.subtract(days=1)
1109 while dt.day_of_week != day_of_week:
1110 dt = dt.subtract(days=1)
1112 return dt
1114 def first_of(self, unit, day_of_week=None):
1115 """
1116 Returns an instance set to the first occurrence
1117 of a given day of the week in the current unit.
1118 If no day_of_week is provided, modify to the first day of the unit.
1119 Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY.
1121 Supported units are month, quarter and year.
1123 :param unit: The unit to use
1124 :type unit: str
1126 :type day_of_week: int or None
1128 :rtype: DateTime
1129 """
1130 if unit not in ["month", "quarter", "year"]:
1131 raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
1133 return getattr(self, "_first_of_{}".format(unit))(day_of_week)
1135 def last_of(self, unit, day_of_week=None):
1136 """
1137 Returns an instance set to the last occurrence
1138 of a given day of the week in the current unit.
1139 If no day_of_week is provided, modify to the last day of the unit.
1140 Use the supplied consts to indicate the desired day_of_week, ex. DateTime.MONDAY.
1142 Supported units are month, quarter and year.
1144 :param unit: The unit to use
1145 :type unit: str
1147 :type day_of_week: int or None
1149 :rtype: DateTime
1150 """
1151 if unit not in ["month", "quarter", "year"]:
1152 raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
1154 return getattr(self, "_last_of_{}".format(unit))(day_of_week)
1156 def nth_of(self, unit, nth, day_of_week):
1157 """
1158 Returns a new instance set to the given occurrence
1159 of a given day of the week in the current unit.
1160 If the calculated occurrence is outside the scope of the current unit,
1161 then raise an error. Use the supplied consts
1162 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1164 Supported units are month, quarter and year.
1166 :param unit: The unit to use
1167 :type unit: str
1169 :type nth: int
1171 :type day_of_week: int or None
1173 :rtype: DateTime
1174 """
1175 if unit not in ["month", "quarter", "year"]:
1176 raise ValueError('Invalid unit "{}" for first_of()'.format(unit))
1178 dt = getattr(self, "_nth_of_{}".format(unit))(nth, day_of_week)
1179 if dt is False:
1180 raise PendulumException(
1181 "Unable to find occurence {} of {} in {}".format(
1182 nth, self._days[day_of_week], unit
1183 )
1184 )
1186 return dt
1188 def _first_of_month(self, day_of_week):
1189 """
1190 Modify to the first occurrence of a given day of the week
1191 in the current month. If no day_of_week is provided,
1192 modify to the first day of the month. Use the supplied consts
1193 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1195 :type day_of_week: int
1197 :rtype: DateTime
1198 """
1199 dt = self.start_of("day")
1201 if day_of_week is None:
1202 return dt.set(day=1)
1204 month = calendar.monthcalendar(dt.year, dt.month)
1206 calendar_day = (day_of_week - 1) % 7
1208 if month[0][calendar_day] > 0:
1209 day_of_month = month[0][calendar_day]
1210 else:
1211 day_of_month = month[1][calendar_day]
1213 return dt.set(day=day_of_month)
1215 def _last_of_month(self, day_of_week=None):
1216 """
1217 Modify to the last occurrence of a given day of the week
1218 in the current month. If no day_of_week is provided,
1219 modify to the last day of the month. Use the supplied consts
1220 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1222 :type day_of_week: int or None
1224 :rtype: DateTime
1225 """
1226 dt = self.start_of("day")
1228 if day_of_week is None:
1229 return dt.set(day=self.days_in_month)
1231 month = calendar.monthcalendar(dt.year, dt.month)
1233 calendar_day = (day_of_week - 1) % 7
1235 if month[-1][calendar_day] > 0:
1236 day_of_month = month[-1][calendar_day]
1237 else:
1238 day_of_month = month[-2][calendar_day]
1240 return dt.set(day=day_of_month)
1242 def _nth_of_month(self, nth, day_of_week):
1243 """
1244 Modify to the given occurrence of a given day of the week
1245 in the current month. If the calculated occurrence is outside,
1246 the scope of the current month, then return False and no
1247 modifications are made. Use the supplied consts
1248 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1250 :type nth: int
1252 :type day_of_week: int or None
1254 :rtype: DateTime
1255 """
1256 if nth == 1:
1257 return self.first_of("month", day_of_week)
1259 dt = self.first_of("month")
1260 check = dt.format("%Y-%M")
1261 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
1262 dt = dt.next(day_of_week)
1264 if dt.format("%Y-%M") == check:
1265 return self.set(day=dt.day).start_of("day")
1267 return False
1269 def _first_of_quarter(self, day_of_week=None):
1270 """
1271 Modify to the first occurrence of a given day of the week
1272 in the current quarter. If no day_of_week is provided,
1273 modify to the first day of the quarter. Use the supplied consts
1274 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1276 :type day_of_week: int or None
1278 :rtype: DateTime
1279 """
1280 return self.on(self.year, self.quarter * 3 - 2, 1).first_of(
1281 "month", day_of_week
1282 )
1284 def _last_of_quarter(self, day_of_week=None):
1285 """
1286 Modify to the last occurrence of a given day of the week
1287 in the current quarter. If no day_of_week is provided,
1288 modify to the last day of the quarter. Use the supplied consts
1289 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1291 :type day_of_week: int or None
1293 :rtype: DateTime
1294 """
1295 return self.on(self.year, self.quarter * 3, 1).last_of("month", day_of_week)
1297 def _nth_of_quarter(self, nth, day_of_week):
1298 """
1299 Modify to the given occurrence of a given day of the week
1300 in the current quarter. If the calculated occurrence is outside,
1301 the scope of the current quarter, then return False and no
1302 modifications are made. Use the supplied consts
1303 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1305 :type nth: int
1307 :type day_of_week: int or None
1309 :rtype: DateTime
1310 """
1311 if nth == 1:
1312 return self.first_of("quarter", day_of_week)
1314 dt = self.set(day=1, month=self.quarter * 3)
1315 last_month = dt.month
1316 year = dt.year
1317 dt = dt.first_of("quarter")
1318 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
1319 dt = dt.next(day_of_week)
1321 if last_month < dt.month or year != dt.year:
1322 return False
1324 return self.on(self.year, dt.month, dt.day).start_of("day")
1326 def _first_of_year(self, day_of_week=None):
1327 """
1328 Modify to the first occurrence of a given day of the week
1329 in the current year. If no day_of_week is provided,
1330 modify to the first day of the year. Use the supplied consts
1331 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1333 :type day_of_week: int or None
1335 :rtype: DateTime
1336 """
1337 return self.set(month=1).first_of("month", day_of_week)
1339 def _last_of_year(self, day_of_week=None):
1340 """
1341 Modify to the last occurrence of a given day of the week
1342 in the current year. If no day_of_week is provided,
1343 modify to the last day of the year. Use the supplied consts
1344 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1346 :type day_of_week: int or None
1348 :rtype: DateTime
1349 """
1350 return self.set(month=MONTHS_PER_YEAR).last_of("month", day_of_week)
1352 def _nth_of_year(self, nth, day_of_week):
1353 """
1354 Modify to the given occurrence of a given day of the week
1355 in the current year. If the calculated occurrence is outside,
1356 the scope of the current year, then return False and no
1357 modifications are made. Use the supplied consts
1358 to indicate the desired day_of_week, ex. DateTime.MONDAY.
1360 :type nth: int
1362 :type day_of_week: int or None
1364 :rtype: DateTime
1365 """
1366 if nth == 1:
1367 return self.first_of("year", day_of_week)
1369 dt = self.first_of("year")
1370 year = dt.year
1371 for i in range(nth - (1 if dt.day_of_week == day_of_week else 0)):
1372 dt = dt.next(day_of_week)
1374 if year != dt.year:
1375 return False
1377 return self.on(self.year, dt.month, dt.day).start_of("day")
1379 def average(self, dt=None):
1380 """
1381 Modify the current instance to the average
1382 of a given instance (default now) and the current instance.
1384 :type dt: DateTime or datetime
1386 :rtype: DateTime
1387 """
1388 if dt is None:
1389 dt = self.now(self.tz)
1391 diff = self.diff(dt, False)
1392 return self.add(
1393 microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2
1394 )
1396 def __sub__(self, other):
1397 if isinstance(other, datetime.timedelta):
1398 return self._subtract_timedelta(other)
1400 if not isinstance(other, datetime.datetime):
1401 return NotImplemented
1403 if not isinstance(other, self.__class__):
1404 if other.tzinfo is None:
1405 other = pendulum.naive(
1406 other.year,
1407 other.month,
1408 other.day,
1409 other.hour,
1410 other.minute,
1411 other.second,
1412 other.microsecond,
1413 )
1414 else:
1415 other = pendulum.instance(other)
1417 return other.diff(self, False)
1419 def __rsub__(self, other):
1420 if not isinstance(other, datetime.datetime):
1421 return NotImplemented
1423 if not isinstance(other, self.__class__):
1424 if other.tzinfo is None:
1425 other = pendulum.naive(
1426 other.year,
1427 other.month,
1428 other.day,
1429 other.hour,
1430 other.minute,
1431 other.second,
1432 other.microsecond,
1433 )
1434 else:
1435 other = pendulum.instance(other)
1437 return self.diff(other, False)
1439 def __add__(self, other):
1440 if not isinstance(other, datetime.timedelta):
1441 return NotImplemented
1443 return self._add_timedelta_(other)
1445 def __radd__(self, other):
1446 return self.__add__(other)
1448 # Native methods override
1450 @classmethod
1451 def fromtimestamp(cls, t, tz=None):
1452 return pendulum.instance(datetime.datetime.fromtimestamp(t, tz=tz), tz=tz)
1454 @classmethod
1455 def utcfromtimestamp(cls, t):
1456 return pendulum.instance(datetime.datetime.utcfromtimestamp(t), tz=None)
1458 @classmethod
1459 def fromordinal(cls, n):
1460 return pendulum.instance(datetime.datetime.fromordinal(n), tz=None)
1462 @classmethod
1463 def combine(cls, date, time):
1464 return pendulum.instance(datetime.datetime.combine(date, time), tz=None)
1466 def astimezone(self, tz=None):
1467 return pendulum.instance(super(DateTime, self).astimezone(tz))
1469 def replace(
1470 self,
1471 year=None,
1472 month=None,
1473 day=None,
1474 hour=None,
1475 minute=None,
1476 second=None,
1477 microsecond=None,
1478 tzinfo=True,
1479 fold=None,
1480 ):
1481 if year is None:
1482 year = self.year
1483 if month is None:
1484 month = self.month
1485 if day is None:
1486 day = self.day
1487 if hour is None:
1488 hour = self.hour
1489 if minute is None:
1490 minute = self.minute
1491 if second is None:
1492 second = self.second
1493 if microsecond is None:
1494 microsecond = self.microsecond
1495 if tzinfo is True:
1496 tzinfo = self.tzinfo
1497 if fold is None:
1498 fold = self.fold
1500 transition_rule = pendulum.POST_TRANSITION
1501 if fold is not None:
1502 transition_rule = pendulum.PRE_TRANSITION
1503 if fold:
1504 transition_rule = pendulum.POST_TRANSITION
1506 return pendulum.datetime(
1507 year,
1508 month,
1509 day,
1510 hour,
1511 minute,
1512 second,
1513 microsecond,
1514 tz=tzinfo,
1515 dst_rule=transition_rule,
1516 )
1518 def __getnewargs__(self):
1519 return (self,)
1521 def _getstate(self, protocol=3):
1522 return (
1523 self.year,
1524 self.month,
1525 self.day,
1526 self.hour,
1527 self.minute,
1528 self.second,
1529 self.microsecond,
1530 self.tzinfo,
1531 )
1533 def __reduce__(self):
1534 return self.__reduce_ex__(2)
1536 def __reduce_ex__(self, protocol):
1537 return self.__class__, self._getstate(protocol)
1539 def _cmp(self, other, **kwargs):
1540 # Fix for pypy which compares using this method
1541 # which would lead to infinite recursion if we didn't override
1542 kwargs = {"tzinfo": self.tz}
1544 if _HAS_FOLD:
1545 kwargs["fold"] = self.fold
1547 dt = datetime.datetime(
1548 self.year,
1549 self.month,
1550 self.day,
1551 self.hour,
1552 self.minute,
1553 self.second,
1554 self.microsecond,
1555 **kwargs
1556 )
1558 return 0 if dt == other else 1 if dt > other else -1
1561DateTime.min = DateTime(1, 1, 1, 0, 0, tzinfo=UTC)
1562DateTime.max = DateTime(9999, 12, 31, 23, 59, 59, 999999, tzinfo=UTC)
1563DateTime.EPOCH = DateTime(1970, 1, 1)