1. mongo
mongo ISODate是一种存储时间的object,它支持到毫秒精度,如果要存储到纳秒精度,则最好用long存储时间戳的方式。
golang time.Time是带有时区的时间,在存到Mongo时驱动会转化为ISODate类型,ISODate类型在数据库存储为UTC时间,ISODate.toLocaleString()可以显示当前带有当前时区的字符串。
go mongo驱动读取时没有转化为当前时区,还是UTC时区存到time.Time,即time.Time.loc=nil //nil即UTC。
注:
如果time.Time作为map的一个元素,存入mongo时,则time.Time类型转化会有问题,见:https://stackoverflow.com/questions/49657422/using-time-time-in-mongodb-record
golang time.Time的时间精度是到纳秒[0,999999999],所以如果是取的time.Now()存入mongo后再取出,与之前的变量比较即使时区一样两者也是不相等的。因为精度有损失。
2.MySQL
mysql表达时间的有: DATE, TIME, DATETIME, TIMESTAMP,和YEAR.他们对应的零值分别为:’0000-00-00’,’00:00:00’,’0000-00-00 00:00:00’,’0000-00-00 00:00:00’,0000.当存入值无效或者超出对应类型范围时,就会被设置成零值。
DATE,DATETIME,TIMESTAMP 最大精度为微秒(6位),使用时在字段后面标注6。如果省略标注,默认是0。如果表中定义的小数位小于实际值,则实际值在存储时会进行截断,并进行四舍五入处理。从MySQL 5.6.4开始, 存储在DATETIME或TIMESTAMP列中的值中的任何小数部分 都会存储而不是丢弃。
DATE:
范围:’1000-01-01’~’9999-12-31’,mysql以’YYYY-MM-DD’格式显示,允许使用字符串或者数字格式分配给列存储
DATETIME:
范围:’1000-01-01 00:00:00.000000’到 ‘9999-12-31 23:59:59.999999’,mysql以’YYYY-MM-DD hh:mm:ss[.fraction]’格式显示,允许使用字符串或者数字格式分配给列存储。
TIMESTAMP:
范围:’1970-01-01 00:00:01.000000’UTC到’2038-01-19 03:14:07.999999’UTC,mysql以’YYYY-MM-DD hh:mm:ss[.fraction]’格式显示。
TIME:
范围:’-838:59:59.000000′ 至’838:59:59.000000’,以’hh:mm:ss[.fraction]’格式显示,允许使用字符串或者数字格式分配给列存储。
TIME类型不仅可以用来表示一天中的某个时间(必须少于24小时),而且还可以用来表示经过的时间或两个事件之间的时间间隔(可能远大于24小时,甚至是负数的)。
YEAR:
2位或4位格式的年份。默认为4位数字格式。YEAR(2)或 YEAR(4)显示格式不同,但取值范围相同。在4位格式,值显示为1901到 2155或0000。在2位格式,值显示为70至 69,表示年从1970年到2069的MySQL显示YEAR在值 YYYY或 YY格式
有关时区:
MySQL将TIMESTAMP值从当前时区转换为UTC以进行存储,然后从UTC转换为当前时区以进行检索。(对于其他类型,例如DATETIME。不会发生这种情况。)默认情况下,每个连接的当前时区是服务器的时间。可以在每个连接的基础上设置时区。只要时区设置保持不变,您将获得与存储相同的值。如果您存储一个TIMESTAMP值,然后更改时区并检索该值,则检索到的值与您存储的值不同。发生这种情况是因为未在两个方向上使用相同的时区进行转换。当前时区可作为time_zone系统变量。
官网https://dev.mysql.com/doc/refman/5.6/en/time-zone-support.html又说明了:
会话时区设置会影响对时区敏感的时间值的显示和存储。这包括由诸如NOW()或 等功能显示的值 CURTIME(),以及存储在TIMESTAMP 列中并从列中检索的值。TIMESTAMP 列的值从会话时区转换为UTC以进行存储,并从UTC转换为会话时区以进行检索。
会话时区设置不会影响功能显示的 UTC_TIMESTAMP()值或者 TIME,DATE,或 DATETIME列中的值。这些数据类型中的值也不会存储在UTC中;时区仅在从TIMESTAMP值转换时适用 。如果要针对DATE, TIME或 DATETIME值进行语言环境特定的算术 ,请将其转换为UTC,执行该算术,然后再转换回去。
测试验证会话时区:
表(_id, uid_pk, regist_time, time)// regist_time:DATETIME;time:TIMESTAMP
//查看全局时区和会话时区
SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;
@@GLOBAL.time_zone @@SESSION.time_zone
+08:00 +08:00
insert into user(_id, uid_pk, regist_time, time) VALUES
("3","3","2021-03-01 15:31:45", "2021-03-01 15:31:45");
//两者在表中存储时间都为"2021-03-01 15:31:45"
//设置会话时区
set time_zone = '+0:00';
flush privileges;
//再次查询时区
SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;
@@GLOBAL.time_zone @@SESSION.time_zone
+08:00 +00:00
//再次插入
insert into user(_id, uid_pk, regist_time, time) VALUES
("4","4","2021-03-01 15:31:45", "2021-03-01 15:31:45");
//这时regist_time还是"2021-03-01 15:31:45",但是time已经变成了"2021-03-01 23:31:45"加了8小时
注:
go-sql-driver 的连接参数:
loc是设置用户服务器时区,并且要与parseTime=true一起使用,即把time.Time的时区改变为指定时区
time_zone才是设置mysql服务器会话时区,比如设为url.QueryEscape(“+8:00”),详情见:https://github.com/go-sql-driver/mysql#system-variables
3.pg
pg表达时间的有:timestamp without time zone, timestamp with time zone, date, time without time zone, time with time zone, interval.
除了date以外的都可以设置最多6位精度。
4.sqlite3
sqlite3虽然声明可以有datetime/timestamp/date,但是都会存储为UTC时间且包含日期和时间。