Skip to content

Commit c355ab4

Browse files
committed
Implements System.DateTime support
Converts System.DateTime and System.TimeSpan to datetime.datetime and datetime.timedelta and vice-versa
1 parent 570e0f8 commit c355ab4

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

src/runtime/converter.cs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)