2626
2727from contextlib import contextmanager
2828from datetime import (
29- timedelta ,
3029 date ,
31- time ,
3230 datetime ,
31+ time ,
32+ timedelta ,
33+ timezone ,
3334)
3435from decimal import (
3536 Decimal ,
@@ -827,7 +828,7 @@ def __getattr__(self, name):
827828 def today (cls , tz = None ):
828829 """Get the current date.
829830
830- :param tz: timezone or None to get a local :class:`.Date`.
831+ :param tz: timezone or None to get the local :class:`.Date`.
831832 :type tz: datetime.tzinfo or None
832833
833834 :rtype: Date
@@ -839,11 +840,11 @@ def today(cls, tz=None):
839840 if tz is None :
840841 return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
841842 else :
842- return tz . fromutc (
843- DateTime .from_clock_time (
844- Clock (). utc_time (), UnixEpoch
845- ). replace ( tzinfo = tz )
846- ). date ()
843+ return (
844+ DateTime .utc_now ()
845+ . replace ( tzinfo = timezone . utc ). astimezone ( tz )
846+ . date ( )
847+ )
847848
848849 @classmethod
849850 def utc_today (cls ):
@@ -868,14 +869,7 @@ def from_timestamp(cls, timestamp, tz=None):
868869 supported by the platform C localtime() function. It’s common for
869870 this to be restricted to years from 1970 through 2038.
870871 """
871- if tz is None :
872- return cls .from_clock_time (
873- ClockTime (timestamp ) + Clock ().local_offset (), UnixEpoch
874- )
875- else :
876- return tz .fromutc (
877- DateTime .utc_from_timestamp (timestamp ).replace (tzinfo = tz )
878- ).date ()
872+ return cls .from_native (datetime .fromtimestamp (timestamp , tz ))
879873
880874 @classmethod
881875 def utc_from_timestamp (cls , timestamp ):
@@ -1487,7 +1481,11 @@ def now(cls, tz=None):
14871481 if tz is None :
14881482 return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
14891483 else :
1490- return tz .fromutc (DateTime .from_clock_time (Clock ().utc_time (), UnixEpoch )).time ().replace (tzinfo = tz )
1484+ return (
1485+ DateTime .utc_now ()
1486+ .replace (tzinfo = timezone .utc ).astimezone (tz )
1487+ .timetz ()
1488+ )
14911489
14921490 @classmethod
14931491 def utc_now (cls ):
@@ -1894,7 +1892,12 @@ def replace(self, **kwargs):
18941892 def _utc_offset (self , dt = None ):
18951893 if self .tzinfo is None :
18961894 return None
1897- value = self .tzinfo .utcoffset (dt )
1895+ try :
1896+ value = self .tzinfo .utcoffset (dt )
1897+ except TypeError :
1898+ # For timezone implementations not compatible with the custom
1899+ # datetime implementations, we can't do better than this.
1900+ value = self .tzinfo .utcoffset (dt .to_native ())
18981901 if value is None :
18991902 return None
19001903 if isinstance (value , timedelta ):
@@ -1936,7 +1939,12 @@ def dst(self):
19361939 """
19371940 if self .tzinfo is None :
19381941 return None
1939- value = self .tzinfo .dst (self )
1942+ try :
1943+ value = self .tzinfo .dst (self )
1944+ except TypeError :
1945+ # For timezone implementations not compatible with the custom
1946+ # datetime implementations, we can't do better than this.
1947+ value = self .tzinfo .dst (self .to_native ())
19401948 if value is None :
19411949 return None
19421950 if isinstance (value , timedelta ):
@@ -1957,7 +1965,12 @@ def tzname(self):
19571965 """
19581966 if self .tzinfo is None :
19591967 return None
1960- return self .tzinfo .tzname (self )
1968+ try :
1969+ return self .tzinfo .tzname (self )
1970+ except TypeError :
1971+ # For timezone implementations not compatible with the custom
1972+ # datetime implementations, we can't do better than this.
1973+ return self .tzinfo .tzname (self .to_native ())
19611974
19621975 def to_clock_time (self ):
19631976 """Convert to :class:`.ClockTime`.
@@ -1986,8 +1999,8 @@ def iso_format(self):
19861999 :rtype: str
19872000 """
19882001 s = "%02d:%02d:%02d.%09d" % self .hour_minute_second_nanosecond
1989- if self .tzinfo is not None :
1990- offset = self . tzinfo . utcoffset ( self )
2002+ offset = self .utc_offset ()
2003+ if offset is not None :
19912004 s += "%+03d:%02d" % divmod (offset .total_seconds () // 60 , 60 )
19922005 return s
19932006
@@ -2100,9 +2113,24 @@ def now(cls, tz=None):
21002113 if tz is None :
21012114 return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
21022115 else :
2103- return tz .fromutc (cls .from_clock_time (
2104- Clock ().utc_time (), UnixEpoch
2105- ).replace (tzinfo = tz ))
2116+ try :
2117+ return tz .fromutc (cls .from_clock_time (
2118+ Clock ().utc_time (), UnixEpoch
2119+ ).replace (tzinfo = tz ))
2120+ except TypeError :
2121+ # For timezone implementations not compatible with the custom
2122+ # datetime implementations, we can't do better than this.
2123+ utc_now = cls .from_clock_time (
2124+ Clock ().utc_time (), UnixEpoch
2125+ )
2126+ utc_now_native = utc_now .to_native ()
2127+ now_native = tz .fromutc (utc_now_native )
2128+ now = cls .from_native (now_native )
2129+ return now .replace (
2130+ nanosecond = (now .nanosecond
2131+ + utc_now .nanosecond
2132+ - utc_now_native .microsecond * 1000 )
2133+ )
21062134
21072135 @classmethod
21082136 def utc_now (cls ):
@@ -2149,8 +2177,9 @@ def from_timestamp(cls, timestamp, tz=None):
21492177 ClockTime (timestamp ) + Clock ().local_offset (), UnixEpoch
21502178 )
21512179 else :
2152- return tz .fromutc (
2153- cls .utc_from_timestamp (timestamp ).replace (tzinfo = tz )
2180+ return (
2181+ cls .utc_from_timestamp (timestamp )
2182+ .replace (tzinfo = timezone .utc ).astimezone (tz )
21542183 )
21552184
21562185 @classmethod
@@ -2463,7 +2492,15 @@ def __add__(self, other):
24632492 time_ = Time .from_ticks_ns (round_half_to_even (
24642493 seconds * NANO_SECONDS + t .nanoseconds
24652494 ))
2466- return self .combine (date_ , time_ )
2495+ return self .combine (date_ , time_ ).replace (tzinfo = self .tzinfo )
2496+ if isinstance (other , Duration ):
2497+ t = (self .to_clock_time ()
2498+ + ClockTime (other .seconds , other .nanoseconds ))
2499+ days , seconds = symmetric_divmod (t .seconds , 86400 )
2500+ date_ = self .date () + Duration (months = other .months ,
2501+ days = days + other .days )
2502+ time_ = Time .from_ticks (seconds * NANO_SECONDS + t .nanoseconds )
2503+ return self .combine (date_ , time_ ).replace (tzinfo = self .tzinfo )
24672504 return NotImplemented
24682505
24692506 def __sub__ (self , other ):
@@ -2493,7 +2530,7 @@ def __sub__(self, other):
24932530 return timedelta (days = days , seconds = t .seconds ,
24942531 microseconds = (t .nanoseconds // 1000 ))
24952532 if isinstance (other , Duration ):
2496- return NotImplemented
2533+ return self . __add__ ( - other )
24972534 if isinstance (other , timedelta ):
24982535 return self .__add__ (- other )
24992536 return NotImplemented
@@ -2552,7 +2589,18 @@ def as_timezone(self, tz):
25522589 if self .tzinfo is None :
25532590 return self
25542591 utc = (self - self .utc_offset ()).replace (tzinfo = tz )
2555- return tz .fromutc (utc )
2592+ try :
2593+ return tz .fromutc (utc )
2594+ except TypeError :
2595+ # For timezone implementations not compatible with the custom
2596+ # datetime implementations, we can't do better than this.
2597+ native_utc = utc .to_native ()
2598+ native_res = tz .fromutc (native_utc )
2599+ res = self .from_native (native_res )
2600+ return res .replace (
2601+ nanosecond = (native_res .microsecond * 1000
2602+ + self .nanosecond % 1000 )
2603+ )
25562604
25572605 def utc_offset (self ):
25582606 """Get the date times utc offset.
@@ -2650,8 +2698,17 @@ def iso_format(self, sep="T"):
26502698
26512699 :rtype: str
26522700 """
2653- return "%s%s%s" % (self .date ().iso_format (), sep ,
2654- self .timetz ().iso_format ())
2701+ s = "%s%s%s" % (self .date ().iso_format (), sep ,
2702+ self .timetz ().iso_format ())
2703+ time_tz = self .timetz ()
2704+ offset = time_tz .utc_offset ()
2705+ if offset is not None :
2706+ # the time component will have taken care of formatting the offset
2707+ return s
2708+ offset = self .utc_offset ()
2709+ if offset is not None :
2710+ s += "%+03d:%02d" % divmod (offset .total_seconds () // 60 , 60 )
2711+ return s
26552712
26562713 def __repr__ (self ):
26572714 """"""
0 commit comments