Skip to content

Commit 8bf39fc

Browse files
authored
Merge pull request #81 from jhonabreul/bug-7760-datetime-with-tzinfo-conversion
Fix for datetime tzinfo conversion
2 parents c36b843 + 3d655f2 commit 8bf39fc

File tree

5 files changed

+71
-7
lines changed

5 files changed

+71
-7
lines changed

src/embed_tests/TestConverter.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,36 @@ def GetNextDay(dateTime):
231231

232232
var expectedDateTime = new DateTime(year, month, day, hour, minute, second);
233233
Assert.AreEqual(expectedDateTime, managedDateTime);
234+
235+
Assert.AreEqual(DateTimeKind.Unspecified, managedDateTime.Kind);
236+
}
237+
}
238+
239+
[Test]
240+
public void ConvertDateTimeWithExplicitUTCTimeZonePythonToCSharp()
241+
{
242+
const int year = 2024;
243+
const int month = 2;
244+
const int day = 27;
245+
const int hour = 12;
246+
const int minute = 30;
247+
const int second = 45;
248+
249+
using (Py.GIL())
250+
{
251+
var csDateTime = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc);
252+
// Converter.ToPython will set the datetime tzinfo to UTC using a custom tzinfo class
253+
using var pyDateTime = Converter.ToPython(csDateTime).MoveToPyObject();
254+
var dateTimeResult = default(object);
255+
256+
Assert.DoesNotThrow(() => Converter.ToManaged(pyDateTime, typeof(DateTime), out dateTimeResult, false));
257+
258+
var managedDateTime = (DateTime)dateTimeResult;
259+
260+
var expectedDateTime = new DateTime(year, month, day, hour, minute, second);
261+
Assert.AreEqual(expectedDateTime, managedDateTime);
262+
263+
Assert.AreEqual(DateTimeKind.Utc, managedDateTime.Kind);
234264
}
235265
}
236266

src/perf_tests/Python.PerformanceTests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
16-
<PackageReference Include="quantconnect.pythonnet" Version="2.0.27" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.28" GeneratePathProperty="true">
1717
<IncludeAssets>compile</IncludeAssets>
1818
</PackageReference>
1919
</ItemGroup>
@@ -25,7 +25,7 @@
2525
</Target>
2626

2727
<Target Name="CopyBaseline" AfterTargets="Build">
28-
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.27\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.28\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
2929
</Target>
3030

3131
<Target Name="CopyNewBuild" AfterTargets="Build">

src/runtime/Converter.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,13 +1123,31 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
11231123
var minute = Runtime.PyObject_GetAttrString(value, minutePtr);
11241124
var second = Runtime.PyObject_GetAttrString(value, secondPtr);
11251125
var microsecond = Runtime.PyObject_GetAttrString(value, microsecondPtr);
1126+
var timeKind = DateTimeKind.Unspecified;
1127+
var tzinfo = Runtime.PyObject_GetAttrString(value, tzinfoPtr);
1128+
1129+
NewReference hours = default;
1130+
NewReference minutes = default;
1131+
if (!ReferenceNullOrNone(tzinfo))
1132+
{
1133+
// We set the datetime kind to UTC if the tzinfo was set to UTC by the ToPthon method
1134+
// using it's custom GMT Python tzinfo class
1135+
hours = Runtime.PyObject_GetAttrString(tzinfo.Borrow(), hoursPtr);
1136+
minutes = Runtime.PyObject_GetAttrString(tzinfo.Borrow(), minutesPtr);
1137+
if (!ReferenceNullOrNone(hours) &&
1138+
!ReferenceNullOrNone(minutes) &&
1139+
Runtime.PyLong_AsLong(hours.Borrow()) == 0 && Runtime.PyLong_AsLong(minutes.Borrow()) == 0)
1140+
{
1141+
timeKind = DateTimeKind.Utc;
1142+
}
1143+
}
11261144

11271145
var convertedHour = 0L;
11281146
var convertedMinute = 0L;
11291147
var convertedSecond = 0L;
11301148
var milliseconds = 0L;
11311149
// could be python date type
1132-
if (!hour.IsNull() && !hour.IsNone())
1150+
if (!ReferenceNullOrNone(hour))
11331151
{
11341152
convertedHour = Runtime.PyLong_AsLong(hour.Borrow());
11351153
convertedMinute = Runtime.PyLong_AsLong(minute.Borrow());
@@ -1143,7 +1161,8 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
11431161
(int)convertedHour,
11441162
(int)convertedMinute,
11451163
(int)convertedSecond,
1146-
(int)milliseconds);
1164+
(int)milliseconds,
1165+
timeKind);
11471166

11481167
year.Dispose();
11491168
month.Dispose();
@@ -1153,6 +1172,16 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
11531172
second.Dispose();
11541173
microsecond.Dispose();
11551174

1175+
if (!tzinfo.IsNull())
1176+
{
1177+
tzinfo.Dispose();
1178+
if (!tzinfo.IsNone())
1179+
{
1180+
hours.Dispose();
1181+
minutes.Dispose();
1182+
}
1183+
}
1184+
11561185
Exceptions.Clear();
11571186
return true;
11581187
default:
@@ -1183,6 +1212,11 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
11831212
return false;
11841213
}
11851214

1215+
private static bool ReferenceNullOrNone(NewReference reference)
1216+
{
1217+
return reference.IsNull() || reference.IsNone();
1218+
}
1219+
11861220

11871221
private static void SetConversionError(BorrowedReference value, Type target)
11881222
{

src/runtime/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
55
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
66

7-
[assembly: AssemblyVersion("2.0.27")]
8-
[assembly: AssemblyFileVersion("2.0.27")]
7+
[assembly: AssemblyVersion("2.0.28")]
8+
[assembly: AssemblyFileVersion("2.0.28")]

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<RootNamespace>Python.Runtime</RootNamespace>
66
<AssemblyName>Python.Runtime</AssemblyName>
77
<PackageId>QuantConnect.pythonnet</PackageId>
8-
<Version>2.0.27</Version>
8+
<Version>2.0.28</Version>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>

0 commit comments

Comments
 (0)