Skip to content

Commit f7cc869

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

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

@@ -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

Comments
 (0)