@@ -30,6 +30,10 @@ private Converter()
3030 private static Type flagsType ;
3131 private static Type boolType ;
3232 private static Type typeType ;
33+ private static IntPtr decimalCtor ;
34+ private static IntPtr dateTimeCtor ;
35+ private static IntPtr timeSpanCtor ;
36+ private static IntPtr tzInfoCtor ;
3337
3438 static Converter ( )
3539 {
@@ -45,6 +49,37 @@ static Converter()
4549 flagsType = typeof ( FlagsAttribute ) ;
4650 boolType = typeof ( Boolean ) ;
4751 typeType = typeof ( Type ) ;
52+
53+ IntPtr decimalMod = Runtime . PyImport_ImportModule ( "decimal" ) ;
54+ if ( decimalMod == null ) throw new PythonException ( ) ;
55+
56+ IntPtr dateTimeMod = Runtime . PyImport_ImportModule ( "datetime" ) ;
57+ if ( dateTimeMod == null ) throw new PythonException ( ) ;
58+
59+ decimalCtor = Runtime . PyObject_GetAttrString ( decimalMod , "Decimal" ) ;
60+ if ( decimalCtor == null ) throw new PythonException ( ) ;
61+
62+ dateTimeCtor = Runtime . PyObject_GetAttrString ( dateTimeMod , "datetime" ) ;
63+ if ( dateTimeCtor == null ) throw new PythonException ( ) ;
64+
65+ timeSpanCtor = Runtime . PyObject_GetAttrString ( dateTimeMod , "timedelta" ) ;
66+ if ( timeSpanCtor == null ) throw new PythonException ( ) ;
67+
68+ IntPtr tzInfoMod = PythonEngine . ModuleFromString ( "custom_tzinfo" , @"
69+ from datetime import timedelta, tzinfo
70+ class GMT(tzinfo):
71+ def __init__(self, hours, minutes):
72+ self.hours = hours
73+ self.minutes = minutes
74+ def utcoffset(self, dt):
75+ return timedelta(hours=self.hours, minutes=self.minutes)
76+ def tzname(self, dt):
77+ return f'GMT {self.hours:00}:{self.minutes:00}'
78+ def dst (self, dt):
79+ return timedelta(0)" ) . Handle ;
80+
81+ tzInfoCtor = Runtime . PyObject_GetAttrString ( tzInfoMod , "GMT" ) ;
82+ if ( tzInfoCtor == null ) throw new PythonException ( ) ;
4883 }
4984
5085
@@ -71,6 +106,9 @@ internal static Type GetTypeByAlias(IntPtr op)
71106 if ( op == Runtime . PyBoolType )
72107 return boolType ;
73108
109+ if ( op == Runtime . PyDecimalType )
110+ return decimalType ;
111+
74112 return null ;
75113 }
76114
@@ -100,6 +138,9 @@ internal static IntPtr GetPythonTypeByAlias(Type op)
100138 if ( op == boolType )
101139 return Runtime . PyBoolType ;
102140
141+ if ( op == decimalType )
142+ return Runtime . PyDecimalType ;
143+
103144 return IntPtr . Zero ;
104145 }
105146
@@ -177,6 +218,14 @@ internal static IntPtr ToPython(object value, Type type)
177218 switch ( tc )
178219 {
179220 case TypeCode . Object :
221+ if ( value is TimeSpan )
222+ {
223+ var timespan = ( TimeSpan ) value ;
224+
225+ IntPtr timeSpanArgs = Runtime . PyTuple_New ( 1 ) ;
226+ Runtime . PyTuple_SetItem ( timeSpanArgs , 0 , Runtime . PyFloat_FromDouble ( timespan . TotalDays ) ) ;
227+ return Runtime . PyObject_CallObject ( timeSpanCtor , timeSpanArgs ) ;
228+ }
180229 return CLRObject . GetInstHandle ( value , type ) ;
181230
182231 case TypeCode . String :
@@ -229,6 +278,29 @@ internal static IntPtr ToPython(object value, Type type)
229278 case TypeCode . UInt64 :
230279 return Runtime . PyLong_FromUnsignedLongLong ( ( ulong ) value ) ;
231280
281+ case TypeCode . Decimal :
282+ string d2s = ( ( decimal ) value ) . ToString ( nfi ) ;
283+ IntPtr d2p = Runtime . PyString_FromString ( d2s ) ;
284+ IntPtr decimalArgs = Runtime . PyTuple_New ( 1 ) ;
285+ Runtime . PyTuple_SetItem ( decimalArgs , 0 , d2p ) ;
286+
287+ return Runtime . PyObject_CallObject ( decimalCtor , decimalArgs ) ;
288+
289+ case TypeCode . DateTime :
290+ var datetime = ( DateTime ) value ;
291+
292+ IntPtr dateTimeArgs = Runtime . PyTuple_New ( 8 ) ;
293+ Runtime . PyTuple_SetItem ( dateTimeArgs , 0 , Runtime . PyInt_FromInt32 ( datetime . Year ) ) ;
294+ Runtime . PyTuple_SetItem ( dateTimeArgs , 1 , Runtime . PyInt_FromInt32 ( datetime . Month ) ) ;
295+ Runtime . PyTuple_SetItem ( dateTimeArgs , 2 , Runtime . PyInt_FromInt32 ( datetime . Day ) ) ;
296+ Runtime . PyTuple_SetItem ( dateTimeArgs , 3 , Runtime . PyInt_FromInt32 ( datetime . Hour ) ) ;
297+ Runtime . PyTuple_SetItem ( dateTimeArgs , 4 , Runtime . PyInt_FromInt32 ( datetime . Minute ) ) ;
298+ Runtime . PyTuple_SetItem ( dateTimeArgs , 5 , Runtime . PyInt_FromInt32 ( datetime . Second ) ) ;
299+ Runtime . PyTuple_SetItem ( dateTimeArgs , 6 , Runtime . PyInt_FromInt32 ( datetime . Millisecond ) ) ;
300+ Runtime . PyTuple_SetItem ( dateTimeArgs , 7 , TzInfo ( datetime . Kind ) ) ;
301+
302+ return Runtime . PyObject_CallObject ( dateTimeCtor , dateTimeArgs ) ;
303+
232304 default :
233305 if ( value is IEnumerable )
234306 {
@@ -250,6 +322,16 @@ internal static IntPtr ToPython(object value, Type type)
250322 }
251323 }
252324
325+ private static IntPtr TzInfo ( DateTimeKind kind )
326+ {
327+ if ( kind == DateTimeKind . Unspecified ) return Runtime . PyNone ;
328+ var offset = kind == DateTimeKind . Local ? DateTimeOffset . Now . Offset : TimeSpan . Zero ;
329+ IntPtr tzInfoArgs = Runtime . PyTuple_New ( 2 ) ;
330+ Runtime . PyTuple_SetItem ( tzInfoArgs , 0 , Runtime . PyFloat_FromDouble ( offset . Hours ) ) ;
331+ Runtime . PyTuple_SetItem ( tzInfoArgs , 1 , Runtime . PyFloat_FromDouble ( offset . Minutes ) ) ;
332+ return Runtime . PyObject_CallObject ( tzInfoCtor , tzInfoArgs ) ;
333+ }
334+
253335
254336 /// <summary>
255337 /// In a few situations, we don't have any advisory type information
@@ -436,6 +518,26 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
436518 return false ;
437519 }
438520
521+ var underlyingType = Nullable . GetUnderlyingType ( obType ) ;
522+ if ( underlyingType != null )
523+ {
524+ return ToManagedValue ( value , underlyingType , out result , setError ) ;
525+ }
526+
527+ var opImplicit = obType . GetMethod ( "op_Implicit" , new [ ] { obType } ) ;
528+ if ( opImplicit != null )
529+ {
530+ if ( ToManagedValue ( value , opImplicit . ReturnType , out result , setError ) )
531+ {
532+ opImplicit = obType . GetMethod ( "op_Implicit" , new [ ] { result . GetType ( ) } ) ;
533+ if ( opImplicit != null )
534+ {
535+ result = opImplicit . Invoke ( null , new [ ] { result } ) ;
536+ }
537+ return opImplicit != null ;
538+ }
539+ }
540+
439541 return ToPrimitive ( value , obType , out result , setError ) ;
440542 }
441543
@@ -452,6 +554,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
452554
453555 switch ( tc )
454556 {
557+ case TypeCode . Object :
558+ if ( obType == typeof ( TimeSpan ) )
559+ {
560+ op = Runtime . PyObject_Str ( value ) ;
561+ TimeSpan ts ;
562+ var arr = Runtime . GetManagedString ( op ) . Split ( ',' ) ;
563+ string sts = arr . Length == 1 ? arr [ 0 ] : arr [ 1 ] ;
564+ if ( ! TimeSpan . TryParse ( sts , out ts ) )
565+ {
566+ goto type_error ;
567+ }
568+ Runtime . XDecref ( op ) ;
569+
570+ int days = 0 ;
571+ if ( arr . Length > 1 )
572+ {
573+ if ( ! int . TryParse ( arr [ 0 ] . Split ( ' ' ) [ 0 ] . Trim ( ) , out days ) )
574+ {
575+ goto type_error ;
576+ }
577+ }
578+ result = ts . Add ( TimeSpan . FromDays ( days ) ) ;
579+ return true ;
580+ }
581+ break ;
582+
455583 case TypeCode . String :
456584 string st = Runtime . GetManagedString ( value ) ;
457585 if ( st == null )
@@ -801,6 +929,30 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
801929 Runtime . XDecref ( op ) ;
802930 result = d ;
803931 return true ;
932+
933+ case TypeCode . Decimal :
934+ op = Runtime . PyObject_Str ( value ) ;
935+ decimal m ;
936+ string sm = Runtime . GetManagedString ( op ) ;
937+ if ( ! Decimal . TryParse ( sm , NumberStyles . Number , nfi , out m ) )
938+ {
939+ goto type_error ;
940+ }
941+ Runtime . XDecref ( op ) ;
942+ result = m ;
943+ return true ;
944+
945+ case TypeCode . DateTime :
946+ op = Runtime . PyObject_Str ( value ) ;
947+ DateTime dt ;
948+ string sdt = Runtime . GetManagedString ( op ) ;
949+ if ( ! DateTime . TryParse ( sdt , out dt ) )
950+ {
951+ goto type_error ;
952+ }
953+ Runtime . XDecref ( op ) ;
954+ result = sdt . EndsWith ( "+00:00" ) ? dt . ToUniversalTime ( ) : dt ;
955+ return true ;
804956 }
805957
806958
@@ -845,7 +997,7 @@ private static bool ToArray(IntPtr value, Type obType, out object result, bool s
845997 int size = Runtime . PySequence_Size ( value ) ;
846998 result = null ;
847999
848- if ( size < 0 )
1000+ if ( size < 0 || elementType . IsGenericType )
8491001 {
8501002 if ( setError )
8511003 {
0 commit comments