Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/test-results/2026-09-26-type-connection-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@

## 后续修复:GB18030 大字段边界

新增本机官方 ARM DM8 的 GB18030(`UNICODE=0`)隔离实例。修复前,包含中文和四字节 emoji 的 CLOB 跨越 16,000 字节写入边界时,文本读回虽完整,数据库的 `LENGTH(c)` 和 `DBMS_LOB.GETLENGTH(c)` 会少计 1 至 2 个字符。写入时现在让 GB18030 数据块结束在完整字符处。Python 3.10 在 UTF-8(`UNICODE=1`)和 GB18030 两套实例上的完整真实库回归各为 **166 passed、0 skipped**,另各有 2 个非真实库用例未选入。CI 增加 Python 3.9 至 3.13 × 两种编码的矩阵,并校验数据库实际 `UNICODE` 值;CI 结果待核实。
新增本机官方 ARM DM8 的 GB18030(`UNICODE=0`)隔离实例。修复前,包含中文和四字节 emoji 的 CLOB 跨越 16,000 字节写入边界时,文本读回虽完整,数据库的 `LENGTH(c)` 和 `DBMS_LOB.GETLENGTH(c)` 会少计 1 至 2 个字符。写入时现在让 GB18030 数据块结束在完整字符处。Python 3.10 在 UTF-8(`UNICODE=1`)和 GB18030 两套实例上的完整真实库回归各为 **166 passed、0 skipped**,另各有 2 个非真实库用例未选入。CI 增加 Python 3.9 至 3.13 × 两种编码的矩阵,并校验数据库实际 `UNICODE` 值;PR #17 的 10 个真实数据库任务和 5 个 ARM64 构建任务均已通过。

## 后续修复:本地时区时间戳绑定

`TIMESTAMP WITH LOCAL TIME ZONE` 列原先拒绝带时区的 Python `datetime`,返回 6015 日期时间格式错误。原因是 Python 默认格式把 `+05:30` 等偏移量紧贴时间,而该列要求偏移量前有空格。现在只在绑定 Python 带时区时间对象时规范化格式;正负偏移量与跨日输入均通过。Python 3.10 在 UTF-8 和 GB18030 官方 ARM DM8 实例上的完整真实库回归各为 **168 passed、0 skipped**,另各有 2 个非真实库用例未选入。
21 changes: 21 additions & 0 deletions src/native/vDateTime.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,9 +530,30 @@ TZVar_SetValue(

if (PyDateTime_Check(value) || PyTime_Check(value))
{
Py_ssize_t text_len;
text = PyObject_Str(value);
if (!text)
return -1;

/* DM8 local time zone columns require a space before +/-HH:MM. */
text_len = PyUnicode_GetLength(text);
if (text_len >= 6 &&
(PyUnicode_ReadChar(text, text_len - 6) == '+' ||
PyUnicode_ReadChar(text, text_len - 6) == '-') &&
PyUnicode_ReadChar(text, text_len - 3) == ':')
{
PyObject* prefix = PyUnicode_Substring(text, 0, text_len - 6);
PyObject* offset = PyUnicode_Substring(text, text_len - 6, text_len);
PyObject* spaced = NULL;
if (prefix && offset)
spaced = PyUnicode_FromFormat("%U %U", prefix, offset);
Py_XDECREF(prefix);
Py_XDECREF(offset);
Py_DECREF(text);
if (!spaced)
return -1;
text = spaced;
}
}

if (dmBuffer_FromObject(&buffer, text, var->environment->encoding) < 0)
Expand Down
29 changes: 29 additions & 0 deletions tests/integration/test_p1_type_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,35 @@ def test_timezone_time_roundtrip_preserves_instant(
cur.close()


@pytest.mark.parametrize(
"value",
[
dt.datetime(2024, 2, 29, 23, 59, 58, 123456, tzinfo=_TZ_PLUS_0530),
dt.datetime(2024, 1, 1, 0, 0, 1, 1, tzinfo=_TZ_MINUS_1259),
],
ids=["positive-offset", "negative-offset"],
)
def test_local_timezone_timestamp_accepts_aware_datetime(
conn, table_name_factory, drop_table, value
):
table = table_name_factory("DMPY_LOCAL_TZ")
offset = value.strftime("%z")
spaced_text = f"{value:%Y-%m-%d %H:%M:%S.%f} {offset[:3]}:{offset[3:]}"
cur = conn.cursor()
try:
cur.execute(f"CREATE TABLE {table} (id INT, v TIMESTAMP WITH LOCAL TIME ZONE)")
cur.execute(f"INSERT INTO {table} VALUES (?, ?)", (1, value))
cur.execute(f"INSERT INTO {table} VALUES (?, ?)", (2, spaced_text))
conn.commit()
cur.execute(f"SELECT id, v FROM {table} ORDER BY id")
rows = cur.fetchall()
assert rows[0][1] == rows[1][1]
finally:
drop_table(cur, table)
conn.commit()
cur.close()


@pytest.mark.parametrize(
"value",
[
Expand Down
Loading