当前位置:首页 > 文章列表 > 数据库 > MySQL > 关于“时间”的一次探索

关于“时间”的一次探索

来源:SegmentFault 2023-01-14 15:22:17 0浏览 收藏

怎么入门数据库编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《关于“时间”的一次探索》,涉及到MySQL、javascript、sequelize,有需要的可以收藏一下

原文对 ISO 8601 时间格式中 T 和 Z 的表述有一些错误,我已经对原文进行了一些修订,抱歉给大家造成误解。

最近使用

new Date();           // 当前时间
new Date(value);      // 自 1970-01-01 00:00:00 UTC 经过的毫秒数
new Date(dateString); // 时间字符串
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);

需要注意的是:构造出的日期用来显示时,会被转换为本地时间(调用

> new Date()
Mon Jan 11 2016 20:15:18 GMT+0800 (CST)

打印出我写这篇文章时的本地时间。后面的

> typeof Date()
'string'
> typeof new Date()
'object'

时间字符串

我们先说最复杂的时间字符串形式。它实际上支持两种格式:一种是 RFC-2822 的标准;另一种是 ISO 8601 的标准。我们主要介绍后一种。

ISO 8601

ISO 8601的标准格式是:

> new Date('1970-01-01 00:00:00')
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

> new Date('1970-01-01T00:00:00')
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

这里补充一点需要注意的,时间字符串这种形式有一个特殊的逻辑:如果你不提供“时间”(也就是

> new Date('1970-01-01')
Thu Jan 01 1970 08:00:00 GMT+0800 (CST)

> new Date('1970-01-01T00:00')
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)
Z

> new Date('1970-01-01T00:00:00')
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

> new Date('1970-01-01T00:00:00Z')
Thu Jan 01 1970 08:00:00 GMT+0800 (CST)

示例 1 是东八区时间,显示的时间和传入的时间一致(因为我本地时区是东八区)。

示例 2 指定了 Z(也就是 UTC 零时区),显示的时间会加上本地时区的偏移(8 小时)。

RFC-2822

RFC-2822 的标准格式大概是这样:

> new Date('Thu Jan 01 1970 00:00:00 GMT+0800 (CST)')
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

除了能表示基本信息,还可以表示星期,但是一点也不容易读,不建议使用。完整的规范可以在这里查看:http://tools.ietf.org/html/rf...

时间戳

> new Date(1000 * 1)
Thu Jan 01 1970 08:00:01 GMT+0800 (CST)

传人 1 秒,等价于:

> new Date(1970, 0, 1, 0, 0, 0)
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

显示时间和传入时间一致,均是本地时间。注意:月份是从 0 开始的。

Date.parse

> Date.parse('1970-01-01 00:00:00')
-28800000

> new Date(Date.parse('1970-01-01 00:00:00'))
Thu Jan 01 1970 00:00:00 GMT+0800 (CST)

> Date.parse('1970-01-01T00:00:00')
0

> new Date(Date.parse('1970-01-01T00:00:00'))
Thu Jan 01 1970 08:00:00 GMT+0800 (CST)

示例 1,-28800000 换算后刚好是 8 小时表示的毫秒数,

> Date.UTC(1970,0,1,0,0,0)
0

> Date.parse('1970-01-01T00:00:00')
0

> Date.parse('1970-01-01 00:00:00Z')
0

可以看出,

> Date.now()
1452520484343

> new Date(Date.now())
Mon Jan 11 2016 21:54:55 GMT+0800 (CST)

> new Date()
Mon Jan 11 2016 21:55:00 GMT+0800 (CST)

MySQL 中的“时间”

MySQL 中和时间相关的数据类型主要包括:

CREATE TABLE `tests` (
    `id` INTEGER NOT NULL auto_increment , 
    `datetime` DATETIME, 
    `timestamp` TIMESTAMP, 
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

连接到数据库服务器后,可以执行

INSERT INTO `tests` (`id`, `datetime`, `timestamp`) VALUES (DEFAULT, '1970-01-01 00:00:00', '1970-01-01 00:00:00');

会发现有一个报错:

INSERT INTO `tests` (`id`, `datetime`, `timestamp`) VALUES (DEFAULT, '2000-01-01 00:00:00', '2000-01-01 00:00:00');

这次就成功插入了。

再次检索时结果也是正确的(数据库服务器将值从

SELECT * FROM sample.tests;

返回:

id datetime timestamp
1 2000-01-01 00:00:00 2000-01-01 00:00:00

如果我们先将

SET time_zone = '+00:00';
SELECT * FROM sample.tests;

返回:

id datetime timestamp
1 2000-01-01 00:00:00 1999-12-31 16:00:00

可以看到 datetime 列值没有受

connection.query("SET time_zone = '" + self.sequelize.options.timezone + "'"); /* jshint ignore: line */

第二个用途主要体现在两个地方:1)在 JavaScript 中调用 ORM 方法进行插入、更新时,需要将

SqlString.dateToString = function(date, timeZone, dialect) {
  if (moment.tz.zone(timeZone)) {
    date = moment(date).tz(timeZone);
  } else {
    date = moment(date).utcOffset(timeZone);
  }

  if (dialect === 'mysql' || dialect === 'mariadb') {
    return date.format('YYYY-MM-DD HH:mm:ss');
  } else {
    // ZZ here means current timezone, _not_ UTC
    return date.format('YYYY-MM-DD HH:mm:ss.SSS Z');
  }
};

代码逻辑如下:

  1. 检查
      switch (field.type) {
        case Types.TIMESTAMP:
        case Types.DATE:
        case Types.DATETIME:
        case Types.NEWDATE:
          var dateString = parser.parseLengthCodedString();
          if (dateStrings) {
            return dateString;
          }
          var dt;
    
          if (dateString === null) {
            return null;
          }
    
          var originalString = dateString;
          if (field.type === Types.DATE) {
            dateString += ' 00:00:00';
          }
    
          if (timeZone !== 'local') {
            dateString += ' ' + timeZone;
          }
    
          dt = new Date(dateString);
          if (isNaN(dt.getTime())) {
            return originalString;
          }
    
          return dt;
       // 更多代码...
    }

    处理过程大概是这样:

    1. Test.create({
          'datetime': new Date('2016-01-10 20:07:00'),
          'timestamp': new Date('2016-01-10 20:07:00')
        });

      会进行上面提到的 JavaScript 时间到 MySQL 时间字符串的转换,生成的 SQL 其实是(时间被转换为了

      INSERT INTO `tests` (`id`,`datetime`,`timestamp`) VALUES (DEFAULT,'2016-01-10 12:07:00','2016-01-10 12:07:00');

      当我们执行

      > new Date('2016-01-10 12:07:00+00:00')
      Sun Jan 10 2016 20:07:00 GMT+0800 (CST)

      和我们插入时的时间是一致的。

      如果我们通过 MySQL 命令行来查询数据时,发现其实是这样的结果:

      id datetime timestamp
      1 2016-01-10 12:07:00 2016-01-10 20:07:00

      这很好理解,因为我们数据库服务器的

      time_zone
      默认是东八区,
      TIMESTAMP
      是受时区影响的,查询时被数据库服务器从
      UTC
      时间转换回了
      time_zone
      时区时间;
      DATETIME
      不受影响,还是
      UTC
      时间。

      如果我们先执行

      SET time_zone = '+00:00'
      ,再进行查询,那结果就都会是
      UTC
      时间了。所以,不要以为数据出错了哦。

      总结下就是,sequelize 会将本地时间转换为

      UTC
      时间后入库,查询时再将
      UTC
      时间转换为本地时间。这能达到最好的兼容性,存储总是使用
      UTC
      时间,展示时应用端自己转换为本地时区时间后显示。当然这个的前提是数据类型选用
      DATETIME

      兼容老数据

      这里要说的最后一个问题是基于旧表定义 sequelize 模型,并且表中时间值插入时没有转换为

      UTC
      时间(全部是东八区时间),而且
      DATETIME
      TIMESTAMP
      混用,该怎么办?

      在默认配置下,情况如下:

      查询

      DATETIME
      类型数据时,时间总是会晚 8 小时。比如,数据库中某条老数据的时间是
      2012-01-01 01:00:00
      (已经是本地时间了,因为没转换),查询时被 sequelize 转换为
      new Date('2012-01-01 01:00:00+00:00')
      ,显示时转换为本地时间
      2012-01-01 09:00:00
      ,结果显然不对。

      查询

      TIMESTAMP
      类型数据时,时间是正确的。这是因为
      TIMESTAMP
      time_zone
      影响,sequelize 默认将其设置为
      +00:00
      ,查询时数据库服务器先将时间转换到
      time_zone
      设置的时区时间,由于没有时区偏移,刚好查出来的就是数据库中的值。比如:
      2012-01-01 00:00:00
      (注意这个值是 UTC 时间),sequelize 将其转换为
      new Date('2012-01-01 00:00:00+00:00')
      ,显示时转换为本地时间
      2012-01-01 08:00:00
      ,刚好“侥幸”正确。

      新插入的数据 sequelize 会进行上一部分说的双向转换来保证结果的正确。

      维持默认配置显然导致查询

      DATETIME
      不准确,解决方法就是将 sequelize 的
      timezone
      配置为
      +08:00
      。这样一来,情况变成下面这样:

      查询

      DATETIME
      类型数据时,时间
      2012-01-01 01:00:00
      被转换为
      new Date('2012-01-01 01:00:00+08:00')
      ,显示时转换为本地时间
      2012-01-01 01:00:00
      ,结果正确。

      查询

      TIMESTAMP
      类型数据时,由于
      time_zone
      被设置为了
      +08:00
      ,数据库服务器先将库中
      UTC
      时间
      2011-01-01 00:00:00
      转换到
      time_zone
      时区时间(加上 8 小时偏移)为
      2011-01-01 08:00:00
      ,sequelize 将其转换为
      new Date('2011-01-01 08:00:00+08:00')
      ,显示时转换为本地时间
      2011-01-01 08:00:00
      ,结果正确。

      插入、更新数据时,所有 JavaScript 时间会转换为东八区时间入库。

      这样带来的问题是,所有入库时间都是东八区时间,如果有其他应用的时区不是东八区,那就需要自己基于东八区时间计算偏移并转换时间后显示了。

      参考资料

      一不小心写的有点长了,下面列出参考资料供大家进一步学习:

      以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。

版本声明
本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
Mysql行连接,列连接Mysql行连接,列连接
上一篇
Mysql行连接,列连接
【整理分享】Mysql中的一些常用命令
下一篇
【整理分享】Mysql中的一些常用命令
评论列表
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    16次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    25次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    30次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    42次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    35次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码