【问题标题】:Storing a Node.js Date in a Knex dateTime()/MySQL DATETIME field在 Knex dateTime()/MySQL DATETIME 字段中存储 Node.js 日期
【发布时间】:2017-06-14 10:27:49
【问题描述】:

我在查找 Node.js、knex 和 MySQL(通过 Bookshelf)通用的日期格式时遇到了一些麻烦。

我使用 Knex 架构构建器设置了一个表:

knex.schema.createTableIfNotExists("examples", function (table) {
    ...
    table.dateTime("some_datetime");
})

这会在 MySQL 中创建一个具有 DATETIME 类型的列。

我有一个 Bookshelf 模型来代表这个(这里省略了所有样板文件),我尝试使用内置的 Date.now() 作为值:

exampleModel.save({
    some_datetime: Date.now()
})

在 Knex 中打开调试后,我看到查询实际上是在尝试插入一个以毫秒为单位的纪元时间戳(为简洁起见,“...”是我的):

{ ...
  bindings: [ 1485644012453, ... ],
  sql: 'update `examples` set `some_datetime` = ? where `id` = ?' }

但这是不正确的,因为在这种情况下为MySQL expects you to use FROM_UNIXTIME,因此数据库中的结果日期当然是好的0000-00-00 00:00:00

我应该在这里做什么才能使这一切保持一致?

  • 在创建表时,我应该在架构生成器中使用其他类型吗?
  • 或者,除了 Date.now() 之外,我还应该使用其他方法来获取日期吗?
  • 或者,别的什么?

我在这里找不到共同点。我的直觉是在 Knex 中使用 dateTime,在 Node 中使用 Date.now(),在 MySQL 中使用 DATETIME,但这是不正确的。

要明确一点:这个问题不一定专注于哲学上的正确性——目前我实际上无法弄清楚如何在所有数据库中存储日期/时间>。我正在寻找一个工作组合,语义正确只是一个奖励。

【问题讨论】:

  • “在创建表时我应该在模式构建器中使用不同的类型吗?” - 您可以将列类型设置为(更改)@987654322 @。至于Date.now() 的东西;我不能说,因为我对X.js 方法一无所知。您正在使用的其他方法/脚本需要作为YYYY-MM-DD 00:00:00 传递给DATETIME
  • @Fred-ii- Hm,因此您建议完全放弃 Knex 和 MySQL 日期/时间类型,只使用内部存储为整数的纪元时间,并根据需要转换为其他格式,例如用户界面的东西?这似乎在哲学上是错误的,但它也是简单有效且合理的想法。我可能会这样做。谢谢。
  • 这取决于你。不同的存储方法(所有)各有利弊。就个人而言,我喜欢使用 MySQL 的内置日期(时间)函数。您最终必须使用 strtotime() 或 MySQL 的 DATE_FORMAT() 来稍后查询。

标签: javascript mysql node.js knex.js


【解决方案1】:

Javascript 函数Date.now() 以毫秒为单位返回纪元。 Mysql 驱动程序,knex 用于发送查询要求您将 ISO8061 字符串或 Date() 对象传递给 DATETIME 列。

日期对象被转换为 'YYYY-mm-dd HH:ii:ss' 字符串

https://github.com/mysqljs/mysql

所以不要使用Date.now(),而是使用new Date()new Date().toISOString()

编辑:

只是检查 mysql 是否真的接受 .toISOString() 输出,因为我在文档 https://dev.mysql.com/doc/refman/5.7/en/date-and-time-type-overview.html 中找不到关于它的提及

MariaDB [(none)]> select CAST('2017-01-30T16:49:19.278Z' AS DATETIME);
+----------------------------------------------+
| CAST('2017-01-30T16:49:19.278Z' AS DATETIME) |
+----------------------------------------------+
| 2017-01-30 16:49:19                          |
+----------------------------------------------+
1 row in set, 1 warning (0.00 sec)

看起来确实如此。耶!都很好。

EDIT2:实际上显然使用.toISOString() 在某些情况下会导致警告和错误,因此插入UTC DATETIME 列.toISOString().replace('Z','').replace('T', ' ') 应该这样做...或任何其他将UTC 日期时间对象转换为格式@987654337 的首选方法@。

此外,如果您使用的是 TIMESTAMP 列类型并且您想将 UTC 时间插入数据库,那么将数据库会话设置为 UTC 也很重要。否则,javascript 时间戳将被解释为本地数据库时间,并在存储之前将其转换为 UTC。

CREATE TABLE test (descr TEXT, dt DATETIME, ts TIMESTAMP);

SET time_zone = '+8:00';
INSERT INTO test (descr, dt, ts) VALUES 
  ('session tz +08:00 insert local times', '2020-01-01T00:00:00', '2020-01-01T00:00:00');

SET time_zone = '+0:00';
INSERT INTO test (descr, dt, ts) VALUES 
  ('session tz +00:00 insert local times', '2020-01-01 00:00:00', '2020-01-01 00:00:00');


SET time_zone = '+02:00';
select 'server local time:', now();
select * from test;

SET time_zone = '+08:00';
select 'server local time:', now();
select * from test;
---

**Query #1**

    SET time_zone = '+02:00';

There are no results to be displayed.

---
**Query #2**

    select 'server local time:', now();

| server local time: | now()               |
| ------------------ | ------------------- |
| server local time: | 2020-05-10 16:38:26 |

---
**Query #3**

    select * from test;

| descr                                | dt                  | ts                  |
| ------------------------------------ | ------------------- | ------------------- |
| session tz +08:00 insert local times | 2020-01-01 00:00:00 | 2019-12-31 18:00:00 |
| session tz +00:00 insert local times | 2020-01-01 00:00:00 | 2020-01-01 02:00:00 |

---
**Query #4**

    SET time_zone = '+08:00';

There are no results to be displayed.

---
**Query #5**

    select 'server local time:', now();

| server local time: | now()               |
| ------------------ | ------------------- |
| server local time: | 2020-05-10 22:38:26 |

---
**Query #6**

    select * from test;

| descr                                | dt                  | ts                  |
| ------------------------------------ | ------------------- | ------------------- |
| session tz +08:00 insert local times | 2020-01-01 00:00:00 | 2020-01-01 00:00:00 |
| session tz +00:00 insert local times | 2020-01-01 00:00:00 | 2020-01-01 08:00:00 |

---

View on DB Fiddle

显示存储和读取 TIMESTAMP 的实际方式始终作为本地时间处理,而 DATATIME 独立于 DB 会话的 time_zone 设置。

通过快速测试,即使在 mysql 8 上,我也无法使 yyyy-MM-dd HH:mm:ss+zz:zz 类型的时间戳/日期时间工作。

【讨论】:

  • 那个“1 个警告”实际上是个问题,用 sql_mode STRICT_TRANS_TABLES 或 STRICT_ALL_TABLES 写入 ISO 日期。 MySQL 不支持 ISO 日期;它似乎仅在您的示例中有效,因为明确允许 T 作为分隔符而不是空格。时区部分(此处为“Z”)不兼容。见:dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html
  • 感谢您的信息!因此,要将正确的时间存储到数据库中,是否应该首先将 Date() 对象解释为数据库时区的本地时间,然后才将其存储为没有时区的时间戳?或者有没有办法告诉那个时间戳字符串中的时区偏移量?
  • 据我所知,MySQL 不支持在任何日期列中存储时区。它转换为 UTC 进行存储,然后在检索时转换回本地时间。
  • 真正的时区信息不会存储到数据库中,但问题是mysql在将时间戳提供给数据库时如何解释没有时区的时间戳......我的意思是2017-01-30T16:49:19.278Z很清楚,并说时间以 UTC 时区给出。即使数据库时区已设置为其他地方,时间戳也是绝对的,并且不依赖于数据库的本地时区。但是,当2017-01-30T16:49:19.278 被赋予 myslq 时,时间戳被解释为数据库的本地时区或 UTC(也许我更容易尝试一下)。
  • 好的,看起来像 mysql 8.0 +00:00 等。支持绝对时间类型,而不必插入从本地时间转换的本地时间 -> UTC for TIMESTAMP 并按原样存储对于 DATETIME 列类型dev.mysql.com/doc/refman/8.0/en/datetime.html,我将很快用更好的示例更新答案。
【解决方案2】:

您可以使用Knex.js 的内置now() 函数来代替Date.now()

const knexfile = require('../../knexfile');
const knex = require('knex')(knexfile.development);
const date = knex.fn.now();

替代方案:

const knexfile = require('../../knexfile');
const knex = require('knex')(knexfile.development);
knex.raw('CURRENT_TIMESTAMP');

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-23
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 2012-02-20
    • 2010-11-11
    • 2017-05-18
    相关资源
    最近更新 更多