@@ -31,6 +31,9 @@ private Converter()
3131 private static Type boolType ;
3232 private static Type typeType ;
3333 private static IntPtr decimalCtor ;
34+ private static IntPtr dateTimeCtor ;
35+ private static IntPtr timeSpanCtor ;
36+ private static IntPtr tzInfoCtor ;
3437
3538 static Converter ( )
3639 {
@@ -49,6 +52,34 @@ static Converter()
4952
5053 IntPtr decimalMod = Runtime . PyImport_ImportModule ( "decimal" ) ;
5154 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\n " +
70+ "class GMT(tzinfo):\n " +
71+ " def __init__(self, hours, minutes):\n " +
72+ " self.hours = hours\n " +
73+ " self.minutes = minutes\n " +
74+ " def utcoffset(self, dt):\n " +
75+ " return timedelta(hours=self.hours, minutes=self.minutes)\n " +
76+ " def tzname(self, dt):\n " +
77+ " return \" GMT {0:00}:{1:00}\" .format(self.hours, self.minutes)\n " +
78+ " def dst (self, dt):\n " +
79+ " return timedelta(0)\n " ) . Handle ;
80+
81+ tzInfoCtor = Runtime . PyObject_GetAttrString ( tzInfoMod , "GMT" ) ;
82+ if ( tzInfoCtor == null ) throw new PythonException ( ) ;
5283 }
5384
5485
@@ -176,6 +207,14 @@ internal static IntPtr ToPython(object value, Type type)
176207 switch ( tc )
177208 {
178209 case TypeCode . Object :
210+ if ( value is TimeSpan )
211+ {
212+ var timespan = ( TimeSpan ) value ;
213+
214+ IntPtr timeSpanArgs = Runtime . PyTuple_New ( 1 ) ;
215+ Runtime . PyTuple_SetItem ( timeSpanArgs , 0 , Runtime . PyFloat_FromDouble ( timespan . TotalDays ) ) ;
216+ return Runtime . PyObject_CallObject ( timeSpanCtor , timeSpanArgs ) ;
217+ }
179218 return CLRObject . GetInstHandle ( value , type ) ;
180219
181220 case TypeCode . String :
@@ -236,6 +275,21 @@ internal static IntPtr ToPython(object value, Type type)
236275
237276 return Runtime . PyObject_CallObject ( decimalCtor , decimalArgs ) ;
238277
278+ case TypeCode . DateTime :
279+ var datetime = ( DateTime ) value ;
280+
281+ IntPtr dateTimeArgs = Runtime . PyTuple_New ( 8 ) ;
282+ Runtime . PyTuple_SetItem ( dateTimeArgs , 0 , Runtime . PyInt_FromInt32 ( datetime . Year ) ) ;
283+ Runtime . PyTuple_SetItem ( dateTimeArgs , 1 , Runtime . PyInt_FromInt32 ( datetime . Month ) ) ;
284+ Runtime . PyTuple_SetItem ( dateTimeArgs , 2 , Runtime . PyInt_FromInt32 ( datetime . Day ) ) ;
285+ Runtime . PyTuple_SetItem ( dateTimeArgs , 3 , Runtime . PyInt_FromInt32 ( datetime . Hour ) ) ;
286+ Runtime . PyTuple_SetItem ( dateTimeArgs , 4 , Runtime . PyInt_FromInt32 ( datetime . Minute ) ) ;
287+ Runtime . PyTuple_SetItem ( dateTimeArgs , 5 , Runtime . PyInt_FromInt32 ( datetime . Second ) ) ;
288+ Runtime . PyTuple_SetItem ( dateTimeArgs , 6 , Runtime . PyInt_FromInt32 ( datetime . Millisecond ) ) ;
289+ Runtime . PyTuple_SetItem ( dateTimeArgs , 7 , TzInfo ( datetime . Kind ) ) ;
290+
291+ return Runtime . PyObject_CallObject ( dateTimeCtor , dateTimeArgs ) ;
292+
239293 default :
240294 if ( value is IEnumerable )
241295 {
@@ -257,6 +311,16 @@ internal static IntPtr ToPython(object value, Type type)
257311 }
258312 }
259313
314+ private static IntPtr TzInfo ( DateTimeKind kind )
315+ {
316+ if ( kind == DateTimeKind . Unspecified ) return Runtime . PyNone ;
317+ var offset = kind == DateTimeKind . Local ? DateTimeOffset . Now . Offset : TimeSpan . Zero ;
318+ IntPtr tzInfoArgs = Runtime . PyTuple_New ( 2 ) ;
319+ Runtime . PyTuple_SetItem ( tzInfoArgs , 0 , Runtime . PyFloat_FromDouble ( offset . Hours ) ) ;
320+ Runtime . PyTuple_SetItem ( tzInfoArgs , 1 , Runtime . PyFloat_FromDouble ( offset . Minutes ) ) ;
321+ return Runtime . PyObject_CallObject ( tzInfoCtor , tzInfoArgs ) ;
322+ }
323+
260324
261325 /// <summary>
262326 /// In a few situations, we don't have any advisory type information
@@ -459,6 +523,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
459523
460524 switch ( tc )
461525 {
526+ case TypeCode . Object :
527+ if ( obType == typeof ( TimeSpan ) )
528+ {
529+ op = Runtime . PyObject_Str ( value ) ;
530+ TimeSpan ts ;
531+ var arr = Runtime . GetManagedString ( op ) . Split ( ',' ) ;
532+ string sts = arr . Length == 1 ? arr [ 0 ] : arr [ 1 ] ;
533+ if ( ! TimeSpan . TryParse ( sts , out ts ) )
534+ {
535+ goto type_error ;
536+ }
537+ Runtime . XDecref ( op ) ;
538+
539+ int days = 0 ;
540+ if ( arr . Length > 1 )
541+ {
542+ if ( ! int . TryParse ( arr [ 0 ] . Split ( ' ' ) [ 0 ] . Trim ( ) , out days ) )
543+ {
544+ goto type_error ;
545+ }
546+ }
547+ result = ts . Add ( TimeSpan . FromDays ( days ) ) ;
548+ return true ;
549+ }
550+ break ;
551+
462552 case TypeCode . String :
463553 string st = Runtime . GetManagedString ( value ) ;
464554 if ( st == null )
@@ -820,6 +910,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
820910 Runtime . XDecref ( op ) ;
821911 result = m ;
822912 return true ;
913+
914+ case TypeCode . DateTime :
915+ op = Runtime . PyObject_Str ( value ) ;
916+ DateTime dt ;
917+ string sdt = Runtime . GetManagedString ( op ) ;
918+ if ( ! DateTime . TryParse ( sdt , out dt ) )
919+ {
920+ goto type_error ;
921+ }
922+ Runtime . XDecref ( op ) ;
923+ result = dt ;
924+ return true ;
823925 }
824926
825927
0 commit comments