SQLite 对时间的支持
Posted liyuanbhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQLite 对时间的支持相关的知识,希望对你有一定的参考价值。
SQLite 对时间的支持
大多数的关系型数据库都会有时间或者日期类型。但是 SQLite 里面是没有的。虽然SQLite 没有专门的时间类型,但是它提供了一些时间和日期相关的转换函数。利用这些函数,我们也可以很方便的处理时间问题。
首先我们来说说 SQLite 为什么不直接提供时间类型。因为时间类型是个非常复杂的,要考虑的因素非常多。时间是个物理量,对于物理量来说就有分辨率和范围这两个概念。对于一个会议室预约管理系统来说,精确到分钟就够了,可是对于一个精密的物理实验,有可能我们要处理纳秒甚至皮秒量级的数据。范围跨度也是巨大的,我们可能只要记录几年之内的事件,但是对于天文学来说,可能观察到的是几亿年前发生的事件。我们很难用一个简单的数据结构兼顾如此大的时间跨度和如此小的时间分辨率。而且我们还没有考虑时区问题的,对于有些国家,还有夏令时问题。由于地球公转和自传的周期并不是严格的倍数关系,还要每隔几年来个闰秒。这些问题如果都要考虑进去,这个时间系统会非常的复杂,而且低效。所以SQlite 干脆就取消了时间类型,由用户自己决定用什么样的数据形式表示时间。
常用的时间表示方法
可以用来表示时间日期的方法有很多,各种方法有不同的范围和分辨率。下面介绍几种 SQLite 中常用的。
儒略日(Julian Day)
以下内容引自百度百科:
儒略日数(Julian Day Number,JDN)的计算是从格林威治标准时间的中午开始,包含一个整天的时间,起点的时间(0日)回溯至儒略历的公元前4713年1月1日中午12点(在格里历是公元前4714年11月24日),这个日期是三种多年周期的共同起点,且是历史上最接近现代的一个起点。例如,2000年1月1日的UTC 12:00是儒略日2,451,545。
儒略日期(Julian date,JD)是以格林威治标准时中午12:00的儒略日加上那一天的瞬时时间的分数。儒略日期是儒略日添加小数部分所表示的儒略日数。例如,2013年1月1日00:30:00(UT)是儒略日期2,456,293.520833。
通过上面的介绍可以知道,儒略日采用一个整数表示日期,小数部分表示当天的时间。因此我们可以用一个浮点数来存储时间。如果用 double 型(64-bit)来存储时间,时间分辨率优于1微秒。
如何在 SQLite 中使用Julian Day 呢。SQLite 给了几个函数,下面有几个例子:
SELECT julianday('now');
SELECT julianday('2013-01-01 00:30:00');
在我电脑上运行了一下,结果是:2459875.08420683 和 2456293.52083333。将这个结果作为一个浮点数存起来就行了。需要注意的是这里的时间都是 UTC 时间。
如果要取得当前时区的时间,要这样写:
SELECT julianday('2013-01-01 00:30:00', 'utc');
SELECT julianday('2013-01-01 00:30:00', 'localtime');
输出结果是:2456293.1875, 2456293.85416667
我们将这三个值比较一下:
2456293.52083333
2456293.1875
2456293.85416667
第一个值介于后两个中间,后两个分别与第一个结果的差是:0.33333334 也就是 1/3 天,我们知道中国在东 8 区。所以和 UTC 时间差 8 个小时,也是 1/3 天。东 8 区比 UTC 时间是早 8 个小时的,所以我们应该用 ‘utc’。这时把时间字符串当作时东 8 区时间,减 8 个小时转换为 UTC 时间后在计算成 Julian Day 时间。‘localtime’ 是把计算出的时间再加了 8 个小时。
所以,再强调一下,正确的写法是这样的;
SELECT julianday('2013-01-01 00:30:00', 'utc');
注意一定不要用 ‘localtime’。
上面的例子还可以用 strftime 函数来实现:
SELECT strftime('%J','2013-01-01 00:30:00');
SELECT strftime('%J','2013-01-01 00:30:00', 'utc');
下面是反向操作,将 Julian Day 转换回时间字符串:
SELECT datetime(2456293.52083333);
SELECT date(2456293.52083333);
SELECT time(2456293.52083333);
SELECT datetime(2456293.1875, 'localtime');
SELECT date(2456293.1875, 'localtime');
SELECT time(2456293.1875, 'localtime');
这里需要特别强调:
- 在时间字符串向浮点数转换时要用 ‘utc’,表示我的字符串时当前时区的,需要先转换为 UTC 时间。
- 浮点数向时间字符串转换时要用 ‘localtime’, 表示我们算出的时间字符串需要转换为当前时区的时间。
这两点对于下面的 Unix Timestamp 也适用。
Unix Timestamp
以下是百度百科的介绍:
Unix时间戳(Unix timestamp),或称Unix时间(Unix time)、POSIX时间(POSIX time),是一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。Unix时间戳不仅被使用在Unix系统、类Unix系统中,也在许多其他操作系统中被广泛采用。
Unix Timestamp 是以一个整数来存储时间的。时间分辨率是 1s。我们可以用 32 位的整数或者 64 位的整数来存储。Unix Timestamp 在 UXIX 系统中使用很方便,但是在 WINDOWS 下就不那么方便了。不过各种编程语言基本都提供了一些相应的转换函数来处理这种类型的时间。
下面的例子将时间字符串转换为 UNIX 时间戳:
SELECT strftime('%s','2004-08-19 18:51:06');
SELECT strftime('%s','2004-08-19 18:51:06', 'utc');
得到的结果是:1092941466 和 1092912666。 这两个时间的差是 28800。正好对应 8 个小时的秒数。
下面的例子将 UNIX 时间戳转换为时间字符串。
SELECT datetime(1092941466, 'unixepoch');
SELECT date(1092941466, 'unixepoch');
SELECT time(1092941466, 'unixepoch');
SELECT datetime(1092912666, 'unixepoch', 'localtime');
SELECT date(1092912666, 'unixepoch', 'localtime');
SELECT time(1092912666, 'unixepoch', 'localtime');
字符串
用一个字符串来存储时间也是一种非常常见的方式。通常我们采用 ISO 8601 规定的格式。日期采用 “YYYY-MM-DD” 格式,时间采用 “HH:MM:SS”。如果既要日期也要时间就写成 “YYYY-MM-DD HH:MM:SS”。 这个格式不是必须的,经常我们会看到有人把日期写为 “YYYY/MM/DD”。写成什么样其实都可以,只要用到的地方保持一致就好。
字符串方式的优点显而易见,容易阅读,方便调试,如果采用 ISO 8601 的格式,时间的比较就是标准的字符串比较。
字符串方式的缺点也很明显:
- 占用空间大,“YYYY-MM-DD HH:MM:SS” 要占用 20 个字节。
- 时间运算比较麻烦,通常需要转换为儒略日或者其他数值类型,计算完再转换回来。
- 要保证所有人输入时间时采用完全一致的格式。SQLite 以字符串格式存储日期时间时不会做任何校验。格式不对不会有任何的报错或者提示。因此应用程序需要自己保证格式正确。
SQLite 给出了一些函数,用来格式化时间字符串。下面给几个例子,首先是返回当前的时间和日期。
SELECT date('now');
SELECT time('now');
SELECT datetime('now');
这三个时间都是 UTC 时间。如果要显示当前时区的时间应该用:
SELECT date('now', 'localtime');
SELECT time('now', 'localtime');
SELECT datetime('now', 'localtime');
还可以是用 strftime 函数。下面是几个简单的例子:
SELECT strftime('%Y/%m/%d %H/%M/%f' , '2013-01-01 00:30:00');
SELECT strftime('%Y/%m/%d %H/%M/%f' , 2456293.52083333);
输出的结果是:2013/01/01 00/30/00.000。SQLite 默认这个时间都是 UTC 时间的。
如果要转换为当前时区时间,则需要写为:
SELECT strftime('%Y/%m/%d %H/%M/%f' , '2013-01-01 00:30:00', 'localtime');
SELECT strftime('%Y/%m/%d %H/%M/%f' , 2456293.52083333, 'localtime');
UNIX时间戳向时间字符串的转换也可以用 strftime。 下面是个例子:
SELECT strftime('%Y/%m/%d %H/%M/%f' , 1092941466, 'unixepoch', 'localtime');
基本上,这里讲的内容对于常规应该应该是够用了。如果需要更多的功能,还是需要去读 SQLite 的官方文档。
这里需要特别强调,SQLite 认为整数或者浮点数存储的时间都是 UTC 时间,字符串时间默认是 UTC 时间,如果字符串时间不是 UTC 时间,而是当前时区的时间,那么我们需要:
- 在时间字符串向数字(整数或者浮点数)转换时要用 ‘utc’,表示我的字符串时当前时区的,需要先转换为 UTC 时间。
- 数字(整数或者浮点数)向时间字符串转换时要用 ‘localtime’, 表示我们算出的时间字符串需要转换为当前时区的时间。
以上是关于SQLite 对时间的支持的主要内容,如果未能解决你的问题,请参考以下文章