@@ -35,6 +35,7 @@ public class timedelta : ICodeFormattable {
3535 private bool _fWithDaysAndSeconds = false ; // whether _tsWithDaysAndSeconds initialized
3636 private bool _fWithSeconds = false ;
3737
38+ internal static readonly timedelta Zero = new timedelta ( 0 , 0 , 0 ) ;
3839 internal static readonly timedelta _DayResolution = new timedelta ( 1 , 0 , 0 ) ;
3940 // class attributes:
4041 [ System . Diagnostics . CodeAnalysis . SuppressMessage ( "Microsoft.Security" , "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes" ) ]
@@ -211,12 +212,11 @@ public static object __getnewargs__(int days, int seconds, int microseconds) {
211212 return PythonTuple . MakeTuple ( new timedelta ( days , seconds , microseconds , 0 , 0 , 0 , 0 ) ) ;
212213 }
213214
214- public override bool Equals ( object obj ) {
215- timedelta delta = obj as timedelta ;
216- if ( delta == null ) return false ;
215+ internal bool Equals ( timedelta delta )
216+ => _days == delta . _days && _seconds == delta . _seconds && _microseconds == delta . _microseconds ;
217217
218- return this . _days == delta . _days && this . _seconds == delta . _seconds && this . _microseconds == delta . _microseconds ;
219- }
218+ public override bool Equals ( object obj )
219+ => obj is timedelta delta && Equals ( delta ) ;
220220
221221 public override int GetHashCode ( ) {
222222 return this . _days ^ this . _seconds ^ this . _microseconds ;
@@ -242,11 +242,7 @@ public override string ToString() {
242242
243243 #region Rich Comparison Members
244244
245- private int CompareTo ( object other ) {
246- timedelta delta = other as timedelta ;
247- if ( delta == null )
248- throw PythonOps . TypeError ( "can't compare datetime.timedelta to {0}" , PythonTypeOps . GetName ( other ) ) ;
249-
245+ private int CompareTo ( timedelta delta ) {
250246 int res = this . _days - delta . _days ;
251247 if ( res != 0 ) return res ;
252248
@@ -256,21 +252,13 @@ private int CompareTo(object other) {
256252 return this . _microseconds - delta . _microseconds ;
257253 }
258254
259- public static bool operator > ( timedelta self , object other ) {
260- return self . CompareTo ( other ) > 0 ;
261- }
255+ public static bool operator > ( [ NotNull ] timedelta self , [ NotNull ] timedelta other ) => self . CompareTo ( other ) > 0 ;
262256
263- public static bool operator < ( timedelta self , object other ) {
264- return self . CompareTo ( other ) < 0 ;
265- }
257+ public static bool operator < ( [ NotNull ] timedelta self , [ NotNull ] timedelta other ) => self . CompareTo ( other ) < 0 ;
266258
267- public static bool operator >= ( timedelta self , object other ) {
268- return self . CompareTo ( other ) >= 0 ;
269- }
259+ public static bool operator >= ( [ NotNull ] timedelta self , [ NotNull ] timedelta other ) => self . CompareTo ( other ) >= 0 ;
270260
271- public static bool operator <= ( timedelta self , object other ) {
272- return self . CompareTo ( other ) <= 0 ;
273- }
261+ public static bool operator <= ( [ NotNull ] timedelta self , [ NotNull ] timedelta other ) => self . CompareTo ( other ) <= 0 ;
274262
275263 #endregion
276264
@@ -987,7 +975,8 @@ public override date replace(CodeContext/*!*/ context, [ParamDictionary]IDiction
987975 return new datetime ( lyear , lmonth , lday , lhour , lminute , lsecond , lmicrosecond , tz ) ;
988976 }
989977
990- public object astimezone ( tzinfo tz ) {
978+ public object astimezone ( tzinfo tz = null ) {
979+ // TODO: https://github.com/IronLanguages/ironpython3/issues/1136
991980 if ( tz == null )
992981 throw PythonOps . TypeError ( "astimezone() argument 1 must be datetime.tzinfo, not None" ) ;
993982
@@ -1553,14 +1542,87 @@ public virtual timedelta utcoffset(object dt) {
15531542 }
15541543
15551544 public PythonTuple __reduce__ ( CodeContext /*!*/ context ) {
1545+ object args = PythonTuple . EMPTY ;
1546+ if ( PythonOps . TryGetBoundAttr ( context , this , "__getinitargs__" , out var getinitargs ) ) {
1547+ args = PythonOps . CallWithContext ( context , getinitargs ) ;
1548+ }
1549+
15561550 object dict ;
15571551 if ( GetType ( ) == typeof ( tzinfo ) ||
15581552 ! PythonOps . TryGetBoundAttr ( context , this , "__dict__" , out dict ) ) {
1559- return PythonTuple . MakeTuple ( DynamicHelpers . GetPythonType ( this ) , PythonTuple . EMPTY ) ;
1553+ return PythonTuple . MakeTuple ( DynamicHelpers . GetPythonType ( this ) , args ) ;
15601554 }
15611555
1562- return PythonTuple . MakeTuple ( DynamicHelpers . GetPythonType ( this ) , PythonTuple . EMPTY , dict ) ;
1556+ return PythonTuple . MakeTuple ( DynamicHelpers . GetPythonType ( this ) , args , dict ) ;
1557+ }
1558+ }
1559+
1560+ #nullable enable
1561+
1562+ [ PythonType ]
1563+ public sealed class timezone : tzinfo , ICodeFormattable {
1564+ private readonly timedelta _offset ;
1565+ private readonly string ? _name ;
1566+
1567+ private timezone ( timedelta offset , string ? name = null ) {
1568+ if ( offset <= - timedelta . _DayResolution || offset >= timedelta . _DayResolution )
1569+ throw PythonOps . ValueError ( $ "offset must be a timedelta strictly between -timedelta(hours=24) and timedelta(hours=24), not { PythonOps . Repr ( DefaultContext . Default , offset ) } .") ;
1570+ _offset = offset ;
1571+ _name = name ;
1572+ }
1573+
1574+ public static timezone __new__ ( CodeContext context , [ NotNull ] PythonType cls , [ NotNull ] timedelta offset )
1575+ => __new__ ( context , cls , offset , null ! ) ;
1576+
1577+ public static timezone __new__ ( CodeContext context , [ NotNull ] PythonType cls , [ NotNull ] timedelta offset , [ NotNull ] string name ) {
1578+ if ( name is null && offset . Equals ( timedelta . Zero ) )
1579+ return utc ;
1580+ return new timezone ( offset , name ) ;
1581+ }
1582+
1583+ public static timezone utc { get ; } = new timezone ( timedelta . Zero ) ;
1584+
1585+ public override timedelta utcoffset ( object ? dt ) => _offset ;
1586+
1587+ public override timedelta ? dst ( object ? dt ) => null ;
1588+
1589+ public override object fromutc ( [ NotNull ] datetime dt ) {
1590+ if ( ! ReferenceEquals ( this , dt . tzinfo ) ) throw PythonOps . ValueError ( "fromutc: dt.tzinfo is not self" ) ;
1591+ return dt + _offset ;
1592+ }
1593+
1594+ private bool IsUtc => ReferenceEquals ( this , utc ) ;
1595+
1596+ public override string tzname ( object ? dt ) {
1597+ if ( _name is not null ) return _name ;
1598+
1599+ if ( IsUtc ) return "UTC" ;
1600+
1601+ var totalSeconds = _offset . total_seconds ( ) ;
1602+ var time = TimeSpan . FromSeconds ( totalSeconds ) . ToString ( "c" ) ;
1603+ if ( totalSeconds >= 0 ) time = "+" + time ; // prefix with 0
1604+ if ( time . EndsWith ( ":00" , StringComparison . OrdinalIgnoreCase ) ) time = time . Substring ( 0 , time . Length - 3 ) ; // remove trailing seconds
1605+ return $ "UTC" + time ;
1606+ }
1607+
1608+ #region ICodeFormattable Members
1609+
1610+ public string __repr__ ( CodeContext context ) {
1611+ if ( IsUtc )
1612+ return "datetime.timezone.utc" ;
1613+ if ( _name is null )
1614+ return $ "datetime.timezone({ PythonOps . Repr ( context , _offset ) } )";
1615+ return $ "datetime.timezone({ PythonOps . Repr ( context , _offset ) } , { PythonOps . Repr ( context , _name ) } )";
1616+ }
1617+
1618+ #endregion
1619+
1620+ public PythonTuple __getinitargs__ ( CodeContext context ) {
1621+ if ( _name is null ) return PythonTuple . MakeTuple ( _offset ) ;
1622+ return PythonTuple . MakeTuple ( _offset , _name ) ;
15631623 }
15641624 }
1625+
1626+ #nullable restore
15651627 }
15661628}
0 commit comments