Skip to content

Commit

Permalink
Fix datetime conversion when tzinfo is used
Browse files Browse the repository at this point in the history
  • Loading branch information
jhonabreul committed Feb 28, 2024
1 parent 2df3c27 commit 0678780
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 5 deletions.
50 changes: 49 additions & 1 deletion src/embed_tests/TestConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,54 @@ public void ConvertDateTimeRoundTrip(DateTimeKind kind)
Assert.AreEqual(datetime, result);
}

[TestCase("", DateTimeKind.Unspecified)]
[TestCase("America/New_York", DateTimeKind.Unspecified)]
[TestCase("UTC", DateTimeKind.Utc)]
public void ConvertDateTimeWithTimeZonePythonToCSharp(string timeZone, DateTimeKind expectedDateTimeKind)
{
const int year = 2024;
const int month = 2;
const int day = 27;
const int hour = 12;
const int minute = 30;
const int second = 45;

using (Py.GIL())
{
dynamic module = PyModule.FromString("module", @$"
from clr import AddReference
AddReference(""Python.EmbeddingTest"")
AddReference(""System"")
from Python.EmbeddingTest import *
from datetime import datetime
from pytz import timezone
tzinfo = timezone('{timeZone}') if '{timeZone}' else None
def GetPyDateTime():
return datetime({year}, {month}, {day}, {hour}, {minute}, {second}, tzinfo=tzinfo) \
if tzinfo else \
datetime({year}, {month}, {day}, {hour}, {minute}, {second})
def GetNextDay(dateTime):
return TestConverter.GetNextDay(dateTime)
");

var pyDateTime = module.GetPyDateTime();
var dateTimeResult = default(object);

Assert.DoesNotThrow(() => Converter.ToManaged(pyDateTime, typeof(DateTime), out dateTimeResult, false));

var managedDateTime = (DateTime)dateTimeResult;

var expectedDateTime = new DateTime(year, month, day, hour, minute, second);
Assert.AreEqual(expectedDateTime, managedDateTime);
Assert.AreEqual(managedDateTime.Kind, expectedDateTimeKind);
}
}

[Test]
public void ConvertTimestampRoundTrip()
{
Expand Down Expand Up @@ -362,7 +410,7 @@ class PyGetListImpl(test.GetListImpl):
List<string> result = inst.GetList();
CollectionAssert.AreEqual(new[] { "testing" }, result);
}

[Test]
public void PrimitiveIntConversion()
{
Expand Down
11 changes: 7 additions & 4 deletions src/runtime/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ static Converter()
timeSpanCtor = Runtime.PyObject_GetAttrString(dateTimeMod.Borrow(), "timedelta").MoveToPyObject();
PythonException.ThrowIfIsNull(timeSpanCtor);


tzInfoCtor = new Lazy<PyObject>(() =>
{
var tzInfoMod = PyModule.FromString("custom_tzinfo", @"
Expand Down Expand Up @@ -1131,9 +1130,13 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
NewReference minutes = default;
if (!tzinfo.IsNone() && !tzinfo.IsNull())
{
hours = Runtime.PyObject_GetAttrString(tzinfo.Borrow(), hoursPtr);
minutes = Runtime.PyObject_GetAttrString(tzinfo.Borrow(), minutesPtr);
if (Runtime.PyLong_AsLong(hours.Borrow()) == 0 && Runtime.PyLong_AsLong(minutes.Borrow()) == 0)
var tznameMethod = Runtime.PyObject_GetAttrString(tzinfo.Borrow(), new StrPtr("tzname", Encoding.UTF8));
var args = Runtime.PyTuple_New(1);
Runtime.PyTuple_SetItem(args.Borrow(), 0, Runtime.None.Steal());
var tznameObj = Runtime.PyObject_CallObject(tznameMethod.Borrow(), args.Borrow());
var tzname = Runtime.GetManagedString(tznameObj.Borrow());

if (tzname.Contains("UTC", StringComparison.InvariantCultureIgnoreCase))
{
timeKind = DateTimeKind.Utc;
}
Expand Down

0 comments on commit 0678780

Please sign in to comment.