Skip to content

Commit d177a45

Browse files
committed
Fix datetime %Z parsing
1 parent 2d1881b commit d177a45

2 files changed

Lines changed: 38 additions & 14 deletions

File tree

graalpython/com.oracle.graal.python.test/src/tests/test_datetime.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
# SOFTWARE.
3939

4040
import datetime
41+
import os
42+
import subprocess
43+
import sys
44+
import textwrap
45+
import time
4146
import unittest
4247

4348
class DateTest(unittest.TestCase):
@@ -542,12 +547,29 @@ def test_strptime(self):
542547
actual = datetime.datetime.strptime("+00:00 GMT", "%z %Z")
543548
self.assertEqual(actual.tzinfo.tzname(None), "GMT")
544549

545-
import time
546550
timezone_name = time.localtime().tm_zone
547551
self.assertIsNotNone(timezone_name)
548552
actual = datetime.datetime.strptime(f"+00:00 {timezone_name}", "%z %Z")
549553
self.assertEqual(actual.tzinfo.tzname(None), timezone_name)
550554

555+
if hasattr(time, "tzset") and sys.executable:
556+
proc = subprocess.run(
557+
[sys.executable, "-c", textwrap.dedent("""\
558+
import datetime
559+
import time
560+
561+
time.tzset()
562+
timezone_name = time.localtime().tm_zone
563+
actual = datetime.datetime.strptime(f"+00:00 {timezone_name}", "%z %Z")
564+
assert actual.tzinfo.tzname(None) == timezone_name
565+
""")],
566+
env={**os.environ, "TZ": "Etc/GMT-1"},
567+
stdout=subprocess.PIPE,
568+
stderr=subprocess.PIPE,
569+
text=True,
570+
)
571+
self.assertEqual(proc.returncode, 0, proc.stderr)
572+
551573
# time zone name without utc offset is ignored
552574
actual = datetime.datetime.strptime("UTC", "%Z")
553575
self.assertIsNone(actual.tzinfo)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/datetime/DateTimeBuiltins.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,22 +2310,14 @@ private static Object parse(String string, String format, PythonContext context,
23102310
TimeZone timeZone = TimeModuleBuiltins.getGlobalTimeZone(context);
23112311
String zoneName = timeZone.getDisplayName(false, TimeZone.SHORT);
23122312
String zoneNameDaylightSaving = timeZone.getDisplayName(true, TimeZone.SHORT);
2313+
String matchedZoneName = matchTimeZoneName(string, i, zoneName, zoneNameDaylightSaving, "UTC", "GMT");
23132314

2314-
if (string.startsWith("UTC", i)) {
2315-
builder.setTimeZoneName("UTC");
2316-
i += 3;
2317-
} else if (string.startsWith("GMT", i)) {
2318-
builder.setTimeZoneName("GMT");
2319-
i += 3;
2320-
} else if (string.startsWith(zoneName, i)) {
2321-
builder.setTimeZoneName(zoneName);
2322-
i += zoneName.length();
2323-
} else if (string.startsWith(zoneNameDaylightSaving, i)) {
2324-
builder.setTimeZoneName(zoneNameDaylightSaving);
2325-
i += zoneNameDaylightSaving.length();
2326-
} else {
2315+
if (matchedZoneName == null) {
23272316
throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.TIME_DATA_S_DOES_NOT_MATCH_FORMAT_S, string, format);
23282317
}
2318+
2319+
builder.setTimeZoneName(matchedZoneName);
2320+
i += matchedZoneName.length();
23292321
}
23302322
case 'j' -> {
23312323
var pos = new ParsePosition(i);
@@ -2487,6 +2479,16 @@ private static Integer parseDigits(String source, int from, int digitsCount) {
24872479
return result;
24882480
}
24892481

2482+
private static String matchTimeZoneName(String string, int from, String... candidates) {
2483+
String matched = null;
2484+
for (String candidate : candidates) {
2485+
if (candidate != null && string.startsWith(candidate, from) && (matched == null || candidate.length() > matched.length())) {
2486+
matched = candidate;
2487+
}
2488+
}
2489+
return matched;
2490+
}
2491+
24902492
@TruffleBoundary
24912493
private static Integer parseDigitsUpTo(String source, ParsePosition from, int maxDigitsCount) {
24922494
int result = 0;

0 commit comments

Comments
 (0)