难倒各路大模型的两道简单 SQLite 问题

问题描述以及示例 prompt 如下


你是 SQLite 专家, 请完成下面两个问题.

  1. 用 SQLite 写一个 query, 根据 “now” 获得当地时间今日零点的 unix 时间戳. 注: “当地” 指执行 SQL 的机器的系统时区, “今日” 指当地日期的今日.

例: 若 now 为 ‘2025-05-05 04:00:00+08:00’, 则返回 ‘2025-05-05 00:00:00+08:00’. (假设当地时区为 UTC+8)

  1. 用 SQLite 写一个 query, 根据 “now” 获得上周的周一的日期. 假设周一为一周的开始, 全程只在 UTC 时间考虑问题 (不用考虑时区).

例: 若 now 为 ‘2025-05-05’ 周一, 则返回 ‘2025-04-28’. 若 now 为 ‘2025-05-04’ 周日, 则返回 ‘2025-04-21’.

参考代码如下, 请填充对应的 SQL.

import sqlite3
import time


def f(sql):
    conn = sqlite3.connect(':memory:')
    cursor = conn.cursor()
    cursor.execute(sql)
    result = cursor.fetchone()[0]
    if isinstance(result, int):
        print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(result)))
    elif isinstance(result, str):
        print(result)
    conn.close()


# now 替换成了具体时间戳
sqls = [
    # 当地今日零点
    "SELECT unixepoch('2025-05-05 04:00:00+08:00', ...)",
    
    # 上周周一
    "SELECT DATE('2025-05-05', ...)",
    "SELECT DATE('2025-05-04', ...)",
]
for sql in sqls:
    f(sql)

在看答案之前可以自己尝试做一做.

问了一圈 LLM, 4o 无联网非推理模式能完全做对 (一定概率做对, 推理反而做不对). DeepSeek-R1 能做对 (一定概率). 其他大多数 LLM 包括 Gemini 2.5 Pro 也没全对.

参考答案

这里 的 4o 回答.

以下是完整的 SQL 填空答案,分别对应两个问题。

1. 当地今日零点的 unix 时间戳

我们要将 '2025-05-05 04:00:00+08:00' 转换为当地时间的今日零点,再转为 unix 时间戳。

SQLite 的 unixepoch() 函数会返回 UTC 的时间戳,所以我们需要先将时间转换为本地的今日零点,然后取 unixepoch()

SELECT unixepoch('2025-05-05 04:00:00+08:00', 'localtime', 'start of day', 'utc')

解释:

  • 'localtime':将输入时间转为本地时间。
  • 'start of day':获取该地时间的当天零点。
  • 'utc':再转为 UTC 时间,以便 unixepoch() 得到正确的 unix 时间戳。

2. 上周周一的日期(仅考虑 UTC)

SQLite 的 strftime('%w', date) 得到一周中的数字(0=周日,1=周一,…,6=周六)。我们可以用它来计算相对于当前日期的偏移。

公式:当前日期 - (当前是周几 + 6) % 7 + 7

所以最终表达式是:

SELECT DATE('2025-05-05', '-' || ((strftime('%w', '2025-05-05') + 6) % 7 + 7) || ' days')

对于 '2025-05-05'(周一,%w=1):

  • (1 + 6) % 7 + 7 = 0 + 7 = 7 → 返回上周一:2025-04-28

对于 '2025-05-04'(周日,%w=0):

  • (0 + 6) % 7 + 7 = 6 + 7 = 13 → 返回上周一:2025-04-21

✅ 填充完整代码如下:

sqls = [
    # 当地今日零点
    "SELECT unixepoch('2025-05-05 04:00:00+08:00', 'localtime', 'start of day', 'utc')",
    
    # 上周周一
    "SELECT DATE('2025-05-05', '-' || ((strftime('%w', '2025-05-05') + 6) % 7 + 7) || ' days')",
    "SELECT DATE('2025-05-04', '-' || ((strftime('%w', '2025-05-04') + 6) % 7 + 7) || ' days')",
]

是否需要我帮你扩展为一个支持动态 now 参数的通用函数?

答案说明

第一题

今日零点我的写法和 4o 相同. 这里反直觉的地方是 SQLite 的 ‘localtime’ 与 ‘utc’ 两个修饰符的命名. 典型的错误是最后没有加 utc 修饰符.

官方文档 这样写道

The “localtime” modifier assumes the time-value to its left is in Universal Coordinated Time (UTC) and adjusts that time value so that it is in localtime.

The “utc” modifier is the opposite of “localtime”. “utc” assumes that the time-value to its left is in the local timezone and adjusts that time-value to be in UTC.

在 prompt 中提供这一段后, 就有相当一部分 LLM 能做对了.

SQLite 时间函数的修饰符从左往右依次叠加. 其中 localtime 实际上就是单纯 +8 小时, 而 utc 则是 -8 小时.

这些修饰符可以重复叠加, 比如

SELECT unixepoch('2025-05-05 04:00:00+08:00', 'localtime', 'localtime')

会得到 ‘2025-05-05 20:00:00+08:00’.

第二题

我用了和 4o 不同的实现

SELECT MAX(
    DATE('2025-05-05', 'weekday 0', '+1 day', '-14 days'), 
    DATE('2025-05-05', 'weekday 1', '-14 days')
)

这里的坑在于如果左边的日期就是 weekday N, 则这个修饰符不做事情. 用 weekday 修饰符的 LLM 都栽了, 而且给答错的 LLM 提供下面的信息后依然会答错.

The “weekday” modifier advances the date forward, if necessary, to the next date where the weekday number is N. Sunday is 0, Monday is 1, and so forth. If the date is already on the desired weekday, the “weekday” modifier leaves the date unchanged.