sqlite:存储时间
环境:
- .net8
- Microsoft.Data.Sqlite 9.0.9
- sqlite 3.50.3
参考:
《官网: Datatypes In SQLite》
《官网: Date And Time Functions》
前提:sqlite中只有5中存储类型,没有单独的日期时间类型
sqlite中提供了三种存储日期时间的方法:
- 字符串表示法,ISO8601,如: “2025-10-14T15:58:02.123Z”
参考:《ISO8601》
- 整数表示法,现在距 1970-01-01 00:00:00 UTC 过去的秒数
- 浮点数表示法,
SQLite 的确 没有专门的日期/时间(Date/Time)存储类型,而是通过三种通用的数据格式来“表示”日期时间。这是 SQLite 设计上的一个核心特点。下面是详细中文说明:
一、SQLite 没有专门的日期类型
SQLite 内部只有 五种基本存储类型(storage class):
存储类型 | 示例类型 |
---|---|
NULL | 空值 |
INTEGER | 整数(1、2、4、8字节) |
REAL | 浮点数(8字节) |
TEXT | 文本字符串(UTF-8 / UTF-16) |
BLOB | 二进制数据 |
所以,即使你写:
CREATE TABLE logs (id INTEGER PRIMARY KEY,created_at DATETIME
);
SQLite 也不会真的创建一个“DATETIME 类型的列”,created_at 实际上仍然可以存储任何类型的值(文本、整数、浮点数都行)。
二、SQLite 支持的三种日期时间存储方式
SQLite 官方定义了三种方式来“保存”日期和时间:
格式 | 实际类型 | 示例 | 含义 |
---|---|---|---|
TEXT | ISO8601 | 字符串 | ‘2025-10-14 16:30:45’ |
REAL | 儒略日(Julian day number) | 2460625.1875 | 表示自公元前 4714 年 11 月 24 日格林尼治中午以来的天数 |
INTEGER | Unix 时间戳 | 1760415045 | 从 1970-01-01 00:00:00 UTC 起的秒数 |
三、sqlite存储示例
3.1 当我们选用 ISO-8601 存储时
获取当前时间:
SELECT date(), -- 获取UTC日期, 如: 2025-10-14date('now','localtime'), -- 获取本地日期, 如: 2025-10-14 time(),-- 获取UTC时间, 如: 08:36:01time('now','subsec'),-- 获取UTC时间带毫秒, 如: 08:36:01.743time('now','localtime'),-- 获取本地时间, 如: 16:36:01 time('now','localtime','subsec'), -- 获取本地时间带毫秒, 如: 16:36:01.743datetime(), -- 获取UTC日期时间, 如: 2025-10-14 08:36:01datetime('subsec'), -- 获取UTC日期时间带毫秒, 如: 2025-10-14 08:36:01.743datetime('now','localtime'), -- 获取本地日期时间, 如: 2025-10-14 16:36:01datetime('now','localtime','subsec'); -- 获取本地日期时间带毫秒, 如: 2025-10-14 16:36:01.743
3.1.1 没有时区信息吗?
⚠️ SQLite 不存储、不管理时区信息。
它所有的时间函数(datetime(‘now’)、strftime() 等)默认使用的是 UTC 时间,
除非你显式加上 ‘localtime’ 修饰符。
它只是把 ‘localtime’ 修饰符交给操作系统去换算。
3.1.2 如何进行时间比较?
当我们存储的是正常的日期时间字符串的时候,我们可以直接材质字符串的比较方式,因为它的比较结果和日期的比较结果时一样的。
所以,这到底比较的是日期还是字符串?
额,比较的还是字符串,但效果是一样的。
另外,注意:
- 必须使用完整的、零填充的格式,如 YYYY-MM-DD HH:MM:SS;
- 不要省略前导 0;
- 不要用其它分隔符。
重点!!!
存储的时候必须保证日期时间格式一致,比如都是:
yyyy-MM-dd HH:mm:ss.fff
如: “2025-01-02 12:01:02.123”yyyy-MM-ddTHH:mm:ss.fffzzz
如:“2025-01-02T12:01:02.123+08:00”
不能一会是 yyyy-MM-dd HH:mm:ss.fff
一会又是 yyyy-MM-ddTHH:mm:ss.fffzzz
,而且不能省略0(如:1月份写 “2025-1-1” 10月份写 “2025-10-10”)。
如:
CREATE TABLE logs (id INTEGER PRIMARY KEY,created_at TEXT
);
INSERT INTO logs(created_at) VALUES
('2025-10-14 10:00:00'),
('2025-10-14 16:00:00'),
('2025-10-13 09:00:00');SELECT * FROM logs
WHERE created_at > '2025-10-14 00:00:00'
ORDER BY created_at;
3.1.3 如何从字符串日期中分离出年月日时分秒呢?
SELECTCAST(strftime('%Y', '2025-10-14 16:45:12') as integer) AS year, -- 默认是字符串,可以选择转成数字,方便后续处理strftime('%m', '2025-10-14 16:45:12') AS month,strftime('%d', datetime('now','localtime')) AS day, -- 结合datetime获取当前的时间天strftime('%H', '2025-10-14 16:45:12') AS hour,strftime('%M', '2025-10-14 16:45:12') AS minute,strftime('%S', '2025-10-14 16:45:12') AS second;
3.1.4 如何进行时间的运算
可以直接在原时间上相加
-- 当前时间加 15 分钟, 如得到: 2025-10-14 17:16:40
SELECT datetime('now','localtime', '+15 minutes');-- 加 1天 1个小时(有没有注意到: 年份月份是错的, 它们是不可能为0的,但不影响运算。。。)
SELECT datetime('2025-10-14 00:00:00','+0000-00-01 01:00:00');
-- 加 1个小时
SELECT datetime('2025-10-14 00:00:00','+01:00:00');-- 多次运算, 得到: 2026-11-15 01:00:59
SELECT datetime('2025-10-14 00:00:00', '+1 days','-1 seconds','+1 months','+1 years','+1 hours','+1 minutes');
计算差值(毫秒):
-- 差值是: 1小时 3600s
select unixepoch('1971-01-01 01:00:00') - unixepoch('1971-01-01 00:00:00')
3.2 当我们选用 Unix timestamp 存储时
Unix 时间戳是从 1970年1月1日 00:00:00 UTC(称为 Unix 纪元)开始所经过的秒数,不包括闰秒。
所以,当我们决定使用它的时候就表示我们存储的秒数是先换算成UTC时间后再和 1970-01-01 00:00:00
相减得到的。
获取当前时间:
-- 假设当前系统时区是是北京时区, 时间为: '2025-10-14 17:40:41'
-- 那么查询得到: 1760434841, 2025-10-14 09:40:41, 2025-10-14 17:40:41
-- 即: unixepoch() 函数会自动获取当前本地时间并转成utc然后再计算毫秒数
select unixepoch(),datetime(unixepoch(),'unixepoch'),datetime(unixepoch(),'unixepoch','localtime');
存储、比较、运算就没必要一一说明了,都是整数,想怎么操作怎么操作呗。
需要注意的是,如何从 unixepoch 转成 ISO-8601?
select datetime(1760434841,'unixepoch','localtime')
3.3 当我们选用 Julian day number 存储时
这个我们应该不会用到吧,只粘贴一个 chatgpt 说明吧:
3.4 ISO-8601 和 Unix timestamp 之间的转换
将 IS0-8601转成 Unix timestamp
-- 假设系统是东八区即: 北京时间
-- 得到: 31536000=3600*24*365
select unixepoch('1971-01-01 08:00:00+08:00')
-- 也得到: 31536000 字符串不携带时区信息, 默认认为: UTC
select unixepoch('1971-01-01 00:00:00')
将 Unix timestamp 转成 IS0-8601
-- 1. 转换为 UTC 时区
-- 将 Unix 时间戳转换为 UTC 时区的 ISO-8601 格式
SELECT datetime(1704067200, 'unixepoch');
-- 输出: '2024-01-01 00:00:00'-- 使用自动识别格式
SELECT datetime(1704067200, 'auto');
-- 输出: '2024-01-01 00:00:00'-- 2. 添加时区信息
-- 转换为 UTC 时区并添加 'Z' 后缀
SELECT datetime(1704067200, 'unixepoch') || 'Z';
-- 输出: '2024-01-01 00:00:00Z'-- 或者使用替换方法
SELECT replace(datetime(1704067200, 'unixepoch'), ' ', 'T') || 'Z';
-- 输出: '2024-01-01T00:00:00Z'-- 3. 转换为特定时区
-- 转换为东八区 (北京时间)
SELECT datetime(1704067200, 'unixepoch', '+8 hours');
-- 输出: '2024-01-01 08:00:00'-- 添加时区偏移
SELECT datetime(1704067200, 'unixepoch', '+8 hours') || '+08:00';
-- 输出: '2024-01-01 08:00:00+08:00'-- 完整 ISO-8601 格式
SELECT replace(datetime(1704067200, 'unixepoch', '+8 hours'), ' ', 'T') || '+08:00';
-- 输出: '2024-01-01T08:00:00+08:00'
四、建议的存储
如果是从可读性考虑的话建议使用格式 "yyyy-MM-ddTHH:mm:ss.fffzzz"
(如:2025-10-14T20:57:01.123+08:00
),因为这是完整的 ISO-8601 格式,而且这也是json序列化默认的输出格式。
强烈建议带上时区。
如果是存储的整洁性考虑,使用长整型存储就ok。
五、c#中如何使用
映射起来也比较简单,就两种:
public class Model
{/// <summary>/// sqlite存储类型: text/// </summary>public DateTime? t_date { get; set; }/// <summary>/// sqlite存储类型: integer/// </summary>public long t_date2 { get; set; }
}