26
26
27
27
from contextlib import contextmanager
28
28
from datetime import (
29
- timedelta ,
30
29
date ,
31
- time ,
32
30
datetime ,
31
+ time ,
32
+ timedelta ,
33
+ timezone ,
33
34
)
34
35
from decimal import (
35
36
Decimal ,
@@ -827,7 +828,7 @@ def __getattr__(self, name):
827
828
def today (cls , tz = None ):
828
829
"""Get the current date.
829
830
830
- :param tz: timezone or None to get a local :class:`.Date`.
831
+ :param tz: timezone or None to get the local :class:`.Date`.
831
832
:type tz: datetime.tzinfo or None
832
833
833
834
:rtype: Date
@@ -839,11 +840,11 @@ def today(cls, tz=None):
839
840
if tz is None :
840
841
return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
841
842
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
+ )
847
848
848
849
@classmethod
849
850
def utc_today (cls ):
@@ -868,14 +869,7 @@ def from_timestamp(cls, timestamp, tz=None):
868
869
supported by the platform C localtime() function. It’s common for
869
870
this to be restricted to years from 1970 through 2038.
870
871
"""
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 ))
879
873
880
874
@classmethod
881
875
def utc_from_timestamp (cls , timestamp ):
@@ -1487,7 +1481,11 @@ def now(cls, tz=None):
1487
1481
if tz is None :
1488
1482
return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
1489
1483
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
+ )
1491
1489
1492
1490
@classmethod
1493
1491
def utc_now (cls ):
@@ -1894,7 +1892,12 @@ def replace(self, **kwargs):
1894
1892
def _utc_offset (self , dt = None ):
1895
1893
if self .tzinfo is None :
1896
1894
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 ())
1898
1901
if value is None :
1899
1902
return None
1900
1903
if isinstance (value , timedelta ):
@@ -1936,7 +1939,12 @@ def dst(self):
1936
1939
"""
1937
1940
if self .tzinfo is None :
1938
1941
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 ())
1940
1948
if value is None :
1941
1949
return None
1942
1950
if isinstance (value , timedelta ):
@@ -1957,7 +1965,12 @@ def tzname(self):
1957
1965
"""
1958
1966
if self .tzinfo is None :
1959
1967
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 ())
1961
1974
1962
1975
def to_clock_time (self ):
1963
1976
"""Convert to :class:`.ClockTime`.
@@ -1986,8 +1999,8 @@ def iso_format(self):
1986
1999
:rtype: str
1987
2000
"""
1988
2001
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 :
1991
2004
s += "%+03d:%02d" % divmod (offset .total_seconds () // 60 , 60 )
1992
2005
return s
1993
2006
@@ -2100,9 +2113,24 @@ def now(cls, tz=None):
2100
2113
if tz is None :
2101
2114
return cls .from_clock_time (Clock ().local_time (), UnixEpoch )
2102
2115
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
+ )
2106
2134
2107
2135
@classmethod
2108
2136
def utc_now (cls ):
@@ -2149,8 +2177,9 @@ def from_timestamp(cls, timestamp, tz=None):
2149
2177
ClockTime (timestamp ) + Clock ().local_offset (), UnixEpoch
2150
2178
)
2151
2179
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 )
2154
2183
)
2155
2184
2156
2185
@classmethod
@@ -2463,7 +2492,15 @@ def __add__(self, other):
2463
2492
time_ = Time .from_ticks_ns (round_half_to_even (
2464
2493
seconds * NANO_SECONDS + t .nanoseconds
2465
2494
))
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 )
2467
2504
return NotImplemented
2468
2505
2469
2506
def __sub__ (self , other ):
@@ -2493,7 +2530,7 @@ def __sub__(self, other):
2493
2530
return timedelta (days = days , seconds = t .seconds ,
2494
2531
microseconds = (t .nanoseconds // 1000 ))
2495
2532
if isinstance (other , Duration ):
2496
- return NotImplemented
2533
+ return self . __add__ ( - other )
2497
2534
if isinstance (other , timedelta ):
2498
2535
return self .__add__ (- other )
2499
2536
return NotImplemented
@@ -2552,7 +2589,18 @@ def as_timezone(self, tz):
2552
2589
if self .tzinfo is None :
2553
2590
return self
2554
2591
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
+ )
2556
2604
2557
2605
def utc_offset (self ):
2558
2606
"""Get the date times utc offset.
@@ -2650,8 +2698,17 @@ def iso_format(self, sep="T"):
2650
2698
2651
2699
:rtype: str
2652
2700
"""
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
2655
2712
2656
2713
def __repr__ (self ):
2657
2714
""""""
0 commit comments