小巧。快速。可靠。
三选其二。
日期和时间函数

1. 概述

SQLite 支持以下七个标量日期和时间函数

  1. date(时间值, 修饰符, 修饰符, ...)
  2. time(时间值, 修饰符, 修饰符, ...)
  3. datetime(时间值, 修饰符, 修饰符, ...)
  4. julianday(时间值, 修饰符, 修饰符, ...)
  5. unixepoch(时间值, 修饰符, 修饰符, ...)
  6. strftime(格式, 时间值, 修饰符, 修饰符, ...)
  7. timediff(时间值, 时间值)

前六个日期和时间函数都接受一个可选的时间值作为参数,后面跟着零个或多个修饰符。strftime() 函数还将格式字符串作为其第一个参数。timediff() 函数正好接受两个参数,这两个参数都是时间值

SQLite 没有专用的日期/时间数据类型。相反,日期和时间值可以存储为以下任何一种

ISO-8601 一个 ISO 8601 日期/时间值的文本字符串。例如'2025-05-29 14:16:00'
儒略日数 自 -4713-11-24 12:00:00 以来的天数(包括小数天)。例如2460825.09444444
Unix 时间戳 自 1970-01-01 00:00:00 以来的秒数(包括小数秒)。例如1748528160

这三种格式统称为时间值。所有日期时间函数都接受时间值作为 ISO-8601 文本或儒略日数。还可以通过添加可选的修饰符参数'auto''unixepoch'来使其接受 Unix 时间戳。由于 timediff() 函数不接受修饰符,因此它只能使用 ISO-8601 和儒略日数时间值。

date() 函数以这种格式返回日期作为文本:YYYY-MM-DD。

time() 函数以格式化后的 HH:MM:SS 或 HH:MM:SS.SSS(如果使用了subsec 修饰符)的形式返回时间作为文本。

datetime() 函数以 YYYY-MM-DD HH:MM:SS 或 YYYY-MM-DD HH:MM:SS.SSS(如果使用了subsec 修饰符)的形式返回日期和时间。

julianday() 函数返回儒略日 - 自公元前 4714 年 11 月 24 日格林尼治中午以来的小数天数(推算格里高利历)。

unixepoch() 函数返回一个 Unix 时间戳 - 自 1970-01-01 00:00:00 UTC 以来经过的秒数。unixepoch() 函数通常返回整数秒数,但使用可选的subsec 修饰符时,它将返回一个浮点数,即小数秒数。

strftime() 函数根据作为第一个参数指定的格式字符串返回格式化的日期。格式字符串支持标准 C 库中的strftime() 函数中最常见的替换,以及两个新的替换,%f 和 %J。以下是截至 3.46.0 版 (2024-05-23) 的所有有效 strftime() 替换的完整列表。较早版本的 SQLite 可能不支持所有替换。如果看到未定义或不支持的替换,则结果为 NULL。

%d月份中的日期:01-31
%e月份中的日期,不带前导零:1-31
%f小数秒:SS.SSS
%FISO 8601 日期:YYYY-MM-DD
%G与 %V 对应的 ISO 8601 年份
%g与 %V 对应的两位数 ISO 8601 年份
%H小时:00-24
%I12 小时制的小时:01-12
%j一年中的日期:001-366
%J儒略日数(小数)
%k小时,不带前导零:0-24
%l%I,不带前导零:1-12
%m月份:01-12
%M分钟:00-59
%p根据小时显示“AM”或“PM”
%P根据小时显示“am”或“pm”
%RISO 8601 时间:HH:MM
%s自 1970-01-01 以来的秒数
%S秒:00-59
%TISO 8601 时间:HH:MM:SS
%U一年中的星期(00-53) - 第 1 周从第一个星期日开始
%u星期几 1-7,其中星期一为 1
%VISO 8601 一年中的星期
%w星期几 0-6,其中星期日为 0
%W一年中的星期(00-53) - 第 1 周从第一个星期一开始
%Y年份:0000-9999
%% %

其他日期和时间函数可以用 strftime() 表示

函数等效的 strftime()
date(...)strftime('%F', ...)
time(...)strftime('%T', ...)
datetime(...)strftime('%F %T', ...)
julianday(...) CAST(strftime('%J', ...) as REAL)
unixepoch(...) CAST(strftime('%s', ...) as INT)

date()、time() 和 datetime() 函数都返回文本,因此它们的 strftime() 等效项是精确的。但是,julianday() 和 unixepoch() 函数返回数值。它们的 strftime() 等效项返回一个字符串,该字符串是相应数字的文本表示形式。

提供除 strftime() 之外的函数的主要原因是为了方便和提高效率。julianday() 和 unixepoch() 函数分别返回实数和整数值,并且不会产生使用 '%J' 或 '%s' 格式说明符与 strftime() 函数一起使用所导致的格式转换成本或不精确性。

timediff(A,B) 函数返回一个字符串,该字符串描述了必须添加到 B 中才能达到时间 A 的时间量。timediff() 结果的格式旨在易于人类阅读。格式为

(+|-)YYYY-MM-DD HH:MM:SS.SSS

此时间差字符串也是其他日期/时间函数允许的修饰符。对于时间值 A 和 B,以下不变式成立

datetime(A) = datetime(B, timediff(A,B))

月份和年份的长度各不相同。二月比三月短。闰年比平年长。timediff() 的输出考虑了所有这些因素。timediff() 函数旨在提供时间跨度的用户友好描述。如果您想知道两个日期 A 和 B 之间的天数或秒数,则始终可以执行以下操作之一

SELECT julianday(B) - julianday(A);
SELECT unixepoch(B) - unixepoch(A);

即使对于跨越不同天数的值 A 和 B,timediff(A,B) 也可能会返回相同的结果 - 取决于开始日期。例如,以下两个 timediff() 调用都返回相同的结果(“-0000-01-00 00:00:00.000”),即使第一个时间跨度为 28 天,第二个时间跨度为 31 天

SELECT timediff('2023-02-15','2023-03-15');
SELECT timediff('2023-03-15','2023-04-15');

总结:如果您想要用户友好的时间跨度,请使用 timediff()。如果您想要精确的时间差(以天或秒为单位),请使用两个 julianday() 或 unixepoch() 调用的差值。

2. 时间值

时间值可以采用以下所示的任何格式。该值通常是字符串,但在格式 12 的情况下,它可以是整数或浮点数。

  1. YYYY-MM-DD
  2. YYYY-MM-DD HH:MM
  3. YYYY-MM-DD HH:MM:SS
  4. YYYY-MM-DD HH:MM:SS.SSS
  5. YYYY-MM-DDTHH:MM
  6. YYYY-MM-DDTHH:MM:SS
  7. YYYY-MM-DDTHH:MM:SS.SSS
  8. HH:MM
  9. HH:MM:SS
  10. HH:MM:SS.SSS
  11. now
  12. DDDDDDDDDD

在格式 5 到 7 中,“T”是一个分隔日期和时间的文字字符,这是ISO-8601 所要求的。格式 8 到 10 仅指定时间,假定日期为 2000-01-01。格式 11,字符串“now”,将转换为从正在使用的sqlite3_vfs对象的 xCurrentTime 方法获得的当前日期和时间。日期和时间函数的“now”参数在同一sqlite3_step()调用中的多次调用中始终返回完全相同的值。协调世界时 (UTC) 用于此。格式 12 是儒略日数,表示为整数或浮点数。如果格式 12 后面紧跟着'auto''unixepoch'修饰符,则它也可能被解释为 Unix 时间戳。

格式 2 到 10 后面可以可选地跟随着“[+-]HH:MM”或仅“Z”形式的时区指示符。日期和时间函数在内部使用 UTC 或“Zulu”时间,因此“Z”后缀是无操作的。任何非零的“HH:MM”后缀都会从指示的日期和时间中减去,以计算 Zulu 时间。例如,以下所有时间值都等效

2013-10-07 08:23:19.120
2013-10-07T08:23:19.120Z
2013-10-07 04:23:19.120-04:00
2456572.84952685

在格式 4、7 和 10 中,小数秒值 SS.SSS 后面可以有一个或多个数字。示例中显示了正好三个数字,因为只有前三个数字对结果有意义,但输入字符串可以有少于或多于三个数字,日期/时间函数仍然可以正常工作。类似地,格式 12 显示了 10 位有效数字,但日期/时间函数实际上可以接受任意多或少的数字,只要足以表示儒略日数即可。

在除 timediff() 之外的所有函数中,都可以省略时间值(以及所有修饰符),在这种情况下,将假定时间值为“now”。

3. 修饰符

对于除 timediff() 之外的所有日期/时间函数,时间值参数后面可以跟随着零个或多个修改日期和/或时间的修饰符。每个修饰符都是应用于其左侧时间值的转换。修饰符从左到右应用;顺序很重要。可用的修饰符如下。

  1. NNN 天
  2. NNN 小时
  3. NNN 分钟
  4. NNN 秒
  5. NNN 个月
  6. NNN 年
  7. ±HH:MM
  8. ±HH:MM:SS
  9. ±HH:MM:SS.SSS
  10. ±YYYY-MM-DD
  11. ±YYYY-MM-DD HH:MM
  12. ±YYYY-MM-DD HH:MM:SS
  13. ±YYYY-MM-DD HH:MM:SS.SSS
  14. 向上取整
  15. 向下取整
  16. 月初
  17. 年初
  18. 当天开始
  19. 星期 N
  20. unixepoch
  21. julianday
  22. auto
  23. 本地时间
  24. UTC
  25. subsec
  26. subsecond

前十三种修饰符(1 到 13)将其左侧参数指定的时间值加上指定的时间量。修饰符名称中的“s”字符(1 到 6)是可选的。NNN 值可以是任何浮点数,可以带有可选的“+”或“-”前缀。

时间偏移修饰符(7 到 13)根据指定的年、月、日、时、分和/或秒数移动时间值。格式 10 到 13 必须以“+”或“-”开头,而格式 7、8 和 9 则可选。更改从左到右应用。首先将年份偏移 YYYY,然后将月份偏移 MM,然后将日期偏移 DD,依此类推。timediff(A,B) 函数返回格式 13 的时间偏移,该偏移将时间值 B 偏移到 A。

由于月份或年份的长度在不同月份或年份之间会发生变化,因此在按月和/或年偏移日期时可能会出现歧义。例如,2024-02-29 一年后的日期是什么?是 2025-02-28 还是 2025-03-01?或者 2023-12-31 两个月后的日期是什么?是 2024-02-29 还是 2024-03-02?对于如何解决这种歧义,没有达成共识,因此提供了“上限”和“下限”修饰符(14 和 15)供程序员决定。如果时间偏移后的下一个修饰符是“上限”,则通过选择较晚的日期来解决日期中的任何歧义。“下限”修饰符通过解析到前一个月的最后一天来解决歧义。默认行为是“上限”。

开始于”修饰符(16 到 18)将日期向后偏移到主题月、年或日的开始。

星期几”修饰符根据需要将日期向前推进到星期几编号为 N 的下一个日期。星期日为 0,星期一为 1,依此类推。如果日期已在所需的星期几,则“星期几”修饰符不会更改日期。

unixepoch”修饰符(20)仅在它紧跟 DDDDDDDDDD 格式的时间值之后时才有效。此修饰符导致将 DDDDDDDDDD 解释为Unix 时间(自 1970 年以来的秒数),而不是通常的儒略日编号。如果“unixepoch”修饰符没有紧跟表示自 1970 年以来秒数的 DDDDDDDDDD 格式的时间值,或者其他修饰符将“unixepoch”修饰符与之前的 DDDDDDDDDD 分隔开,则行为未定义。

julianday”修饰符必须紧跟初始时间值,该时间值必须为 DDDDDDDDD 格式。任何其他使用 'julianday' 修饰符都是错误的,并导致函数返回 NULL。'julianday' 修饰符强制将时间值解释为儒略日编号。由于这是默认行为,因此 'julianday' 修饰符几乎只是一个空操作。唯一的区别是添加 'julianday' 会强制使用 DDDDDDDDD 时间值格式,如果使用任何其他时间值格式,则会导致返回 NULL。

auto”修饰符必须紧跟初始时间值。如果时间值是数字(DDDDDDDDDD 格式),则 'auto' 修饰符会根据其大小将时间值解释为儒略日编号或 Unix 时间戳。如果值介于 0.0 和 5373484.499999 之间,则将其解释为儒略日编号(对应于 -4713-11-24 12:00:00 到 9999-12-31 23:59:59,含)。对于超出有效儒略日编号范围但位于 -210866760000 到 253402300799 范围内的数字值,'auto' 修饰符会导致将该值解释为 Unix 时间戳。其他数字值超出范围,并导致返回 NULL。对于 ISO 8601 文本时间值,'auto' 修饰符是空操作。“auto”修饰符旨在与时间值一起使用,即使在不知道数据库文件中存储了哪种时间值格式的情况下,或在同一列在不同行上以不同格式存储时间值的情况下。“auto”修饰符将自动选择合适的格式。但是,存在一些歧义。1970 年前 63 天的 Unix 时间戳将被解释为儒略日编号。“auto”修饰符在数据集保证不包含该范围内的任何日期时非常有用,但应避免在可能使用 1970 年初几个月的日期的应用程序中使用。

localtime”修饰符假设其左侧的时间值以协调世界时 (UTC) 表示,并调整该时间值使其成为本地时间。如果“localtime”跟随不是 UTC 的时间,则行为未定义。“utc”修饰符与“localtime”相反。“utc”假设其左侧的时间值位于本地时区,并将该时间值调整为 UTC。如果左侧的时间不在本地时间,则“utc”的结果未定义。

subsecond”修饰符(可以缩写为“subsec”)提高了 datetime()time()unixepoch() 以及 strftime() 中的“%s”格式字符串的输出分辨率。“subsecond”修饰符对其他日期/时间函数没有影响。当前的实现将分辨率从秒提高到毫秒,但在 SQLite 的未来版本中可能会提高到更高的分辨率。当“subsec”与 datetime()time() 一起使用时,末尾的秒字段后跟一个小数点和一个或多个数字以显示小数秒。当“subsec”与 unixepoch() 一起使用时,结果是一个浮点值,表示自 1970-01-01 以来的秒数和小数秒。“subsecond”和“subsec”修饰符具有一个特殊属性,即它们可以作为日期/时间函数的第一个参数出现(或作为 strftime() 的格式字符串后的第一个参数)。当发生这种情况时,通常位于第一个参数中的时间值被理解为“现在”。例如,获取自 1970 年以来当前时间的毫秒精度秒数的快捷方式是:

SELECT unixepoch('subsec');

4. 示例

计算当前日期。

SELECT date();

计算当前月的最后一天。

SELECT date('now','start of month','+1 month','-1 day');

给定 Unix 时间戳 1092941466,计算日期和时间。

SELECT datetime(1092941466, 'unixepoch');
SELECT datetime(1092941466, 'auto'); -- 对 1970 年初不起作用!

给定 Unix 时间戳 1092941466,计算日期和时间,并补偿您的本地时区。

SELECT datetime(1092941466, 'unixepoch', 'localtime');

计算当前 Unix 时间戳。

SELECT unixepoch();
SELECT strftime('%s');

计算自美国独立宣言签署以来的天数。

SELECT julianday('now') - julianday('1776-07-04');

计算自 2004 年某个特定时刻以来的秒数

SELECT unixepoch() - unixepoch('2004-01-01 02:34:56');

计算当年 10 月份第一个星期二的日期。

SELECT date('now','start of year','+9 months','weekday 2');

计算自 Unix 纪元以来的毫秒精度秒数

SELECT (julianday('now') - 2440587.5)*86400.0;
SELECT unixepoch('now','subsec');

计算如果亚伯拉罕·林肯今天还活着,他会多大年龄

SELECT timediff('now','1809-02-12');

5. 注意事项和错误

本地时间的计算在很大程度上取决于政治家的想法,因此难以对所有地区都计算正确。在此实现中,使用标准 C 库函数 localtime_r() 来辅助计算本地时间。localtime_r() C 函数通常仅适用于 1970 年到 2037 年之间的年份。对于此范围之外的日期,SQLite 尝试将年份映射到此范围内的等效年份,执行计算,然后将年份映射回。

这些函数仅适用于 0000-01-01 00:00:00 到 9999-12-31 23:59:59 之间的日期(儒略日编号 1721059.5 到 5373484.5)。对于此范围之外的日期,这些函数的结果未定义。

非 Vista Windows 平台仅支持一组 DST 规则。Vista 仅支持两组。因此,在这些平台上,历史 DST 计算将不正确。例如,在美国,2007 年 DST 规则发生了变化。非 Vista Windows 平台也将 2007 年的新 DST 规则应用于所有先前的年份。Vista 在获得 1986 年(规则也发生变化)以来的正确结果方面做得稍微好一些。

所有内部计算都假设使用公历系统。它们还假设每一天的持续时间正好为 86400 秒;没有包含闰秒。

此页面上次修改于 2024-08-14 17:04:32 UTC