@@ -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
@@ -187,6 +218,14 @@ internal static IntPtr ToPython(object value, Type type)
187218 switch ( tc )
188219 {
189220 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+ }
190229 return CLRObject . GetInstHandle ( value , type ) ;
191230
192231 case TypeCode . String :
@@ -247,6 +286,21 @@ internal static IntPtr ToPython(object value, Type type)
247286
248287 return Runtime . PyObject_CallObject ( decimalCtor , decimalArgs ) ;
249288
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+
250304 default :
251305 if ( value is IEnumerable )
252306 {
@@ -268,6 +322,16 @@ internal static IntPtr ToPython(object value, Type type)
268322 }
269323 }
270324
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+
271335
272336 /// <summary>
273337 /// In a few situations, we don't have any advisory type information
@@ -490,6 +554,32 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
490554
491555 switch ( tc )
492556 {
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+
493583 case TypeCode . String :
494584 string st = Runtime . GetManagedString ( value ) ;
495585 if ( st == null )
@@ -851,6 +941,18 @@ private static bool ToPrimitive(IntPtr value, Type obType, out object result, bo
851941 Runtime . XDecref ( op ) ;
852942 result = m ;
853943 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 = dt ;
955+ return true ;
854956 }
855957
856958
0 commit comments