如何使用 MariaDB 返回本地时间 SQL 结果

Posted

技术标签:

【中文标题】如何使用 MariaDB 返回本地时间 SQL 结果【英文标题】:How to return local time SQL results using MariaDB 【发布时间】:2018-08-04 14:17:53 【问题描述】:

这个问题已经被多次询问(并回答)了 mysql,我确信这些答案也适用于 MariaDB,但要么他们不这样做,要么更有可能我做错了什么。运行这些脚本几分钟后,date 返回Sat Feb 24 18:20:38 UTC 2018。我已经得出结论,在大多数情况下 MySQL/MariaDB 应该保持为 UTC 配置,我不会做不同的事情。我确实让事情按预期工作,并在下面发布了结果。

<?php
//php.ini has set date.timezone =America/Los_Angeles

function displayTime($desc,$db) 
    echo("<h5>$desc</h5>");
    $stmt=$db->query('SELECT @@global.time_zone');
    echo 'MariaDb global.time_zone: '.$stmt->fetchColumn()."<br>";

    $stmt=$db->query('SELECT @@session.time_zone');
    echo 'MariaDb session.time_zone: '.$stmt->fetchColumn()."<br>";

    $stmt=$db->query('SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);');
    echo 'MariaDb offset: '.$stmt->fetchColumn()."<br>";

    $stmt=$db->query('SELECT tsValueUpdated FROM points WHERE id=6');
    echo 'Adjusted time: '.$stmt->fetchColumn()."<br>";


function getTimezoneFromDb() 
    $tzs = DateTimeZone::listIdentifiers();
    return $tzs[rand(0, count($tzs)-1)];


function getOffset() 
    $os=(new DateTime())->getOffset();
    if($os>(13*60*60)) $os=-24*60*60; //MySQL/MariaDB bug for Pacific/Kiritimati, Pacific/Chatham, and Pacific/Apia
    return $os >= 0?'+'.gmdate("G:i", $os):'-'.gmdate("G:i", -$os);


function test($sql, $value, $db) 
    $desc="Test for $sql using $value";
    $stmt=$db->prepare($sql);
    try
        $stmt->execute([$value]);
        displayTime($desc,$db);
    
    catch(PDOException $e) 
        echo("<h5>$desc</h5>".$e->getMessage().'<br>');
    


//tsValueUpdated is type datetime and data was inserted using NOW()
$db=parse_ini_file(__DIR__.'/../config.ini',true)['mysql'];
$db=new PDO("mysql:host=$db['host'];dbname=$db['dbname'];charset=$db['charset']",$db['username'],$db['password'],array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_OBJ));

displayTime('Before changing timezone',$db);

$tz=getTimezoneFromDb();
echo "Timezone: $tz<br>";
date_default_timezone_set($tz);

displayTime('After changing PHP timezone',$db);

$os=getOffset();
echo "Offset: $os<br>";

//Reference https://***.com/a/19069310/1032531
test('SET GLOBAL time_zone = ?', $os, $db);
test('SET GLOBAL time_zone = ?', $tz, $db);
test('SET @@global.time_zone = ?', $os, $db);
test('SET time_zone = ?', $os, $db);
test('SET time_zone = ?', $tz, $db);
test('SET @@session.time_zone = ?', $os, $db);

输出

Before changing timezone
MariaDb global.time_zone: +06:00
MariaDb session.time_zone: +06:00
MariaDb offset: 06:00:00
Adjusted time: 2018-02-24 18:15:46
Timezone: Africa/Tripoli
After changing PHP timezone
MariaDb global.time_zone: +06:00
MariaDb session.time_zone: +06:00
MariaDb offset: 06:00:00
Adjusted time: 2018-02-24 18:15:46
Offset: +2:00
Test for SET GLOBAL time_zone = ? using +2:00
MariaDb global.time_zone: +02:00
MariaDb session.time_zone: +06:00
MariaDb offset: 06:00:00
Adjusted time: 2018-02-24 18:15:46
Test for SET GLOBAL time_zone = ? using Africa/Tripoli
SQLSTATE[HY000]: General error: 1298 Unknown or incorrect time zone: 'Africa/Tripoli'
Test for SET @@global.time_zone = ? using +2:00
MariaDb global.time_zone: +02:00
MariaDb session.time_zone: +06:00
MariaDb offset: 06:00:00
Adjusted time: 2018-02-24 18:15:46
Test for SET time_zone = ? using +2:00
MariaDb global.time_zone: +02:00
MariaDb session.time_zone: +02:00
MariaDb offset: 02:00:00
Adjusted time: 2018-02-24 18:15:46
Test for SET time_zone = ? using Africa/Tripoli
SQLSTATE[HY000]: General error: 1298 Unknown or incorrect time zone: 'Africa/Tripoli'
Test for SET @@session.time_zone = ? using +2:00
MariaDb global.time_zone: +02:00
MariaDb session.time_zone: +02:00
MariaDb offset: 02:00:00
Adjusted time: 2018-02-24 18:15:46

命令行测试

MariaDB [datalogger]> explain points;
+----------------+-------------+------+-----+---------+----------------+
| Field          | Type        | Null | Key | Default | Extra          |
+----------------+-------------+------+-----+---------+----------------+
| id             | int(11)     | NO   | PRI | NULL    | auto_increment |
| idPublic       | int(11)     | NO   | MUL | 0       |                |
| accountsId     | int(11)     | NO   | MUL | NULL    |                |
| name           | varchar(45) | NO   | MUL | NULL    |                |
| value          | float       | YES  |     | NULL    |                |
| valueOld       | float       | YES  |     | NULL    |                |
| units          | varchar(45) | YES  |     | NULL    |                |
| type           | char(8)     | NO   | MUL | NULL    |                |
| slope          | float       | NO   |     | 1       |                |
| intercept      | float       | NO   |     | 0       |                |
| tsValueUpdated | datetime    | YES  |     | NULL    |                |
+----------------+-------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)

MariaDB [datalogger]> SELECT tsValueUpdated FROM points WHERE id=6;
+---------------------+
| tsValueUpdated      |
+---------------------+
| 2018-02-24 18:09:46 |
+---------------------+
1 row in set (0.00 sec)

MariaDB [datalogger]> SET time_zone ='+12:00';
Query OK, 0 rows affected (0.00 sec)

MariaDB [datalogger]> SELECT tsValueUpdated FROM points WHERE id=6;
+---------------------+
| tsValueUpdated      |
+---------------------+
| 2018-02-24 18:09:46 |
+---------------------+
1 row in set (0.00 sec)

MariaDB [datalogger]> SET GLOBAL time_zone ='+12:00';
Query OK, 0 rows affected (0.00 sec)

MariaDB [datalogger]> SELECT tsValueUpdated FROM points WHERE id=6;
+---------------------+
| tsValueUpdated      |
+---------------------+
| 2018-02-24 18:09:46 |
+---------------------+
1 row in set (0.00 sec)

MariaDB [datalogger]>

mysqld --help --verbose | grep 时区

2018-02-24 18:07:19 140183024801920 [Warning] Changed limits: max_open_files: 1024  max_connections: 151  table_cache: 431
2018-02-24 18:07:19 140183024801920 [Note] Plugin 'FEEDBACK' is disabled.
2018-02-24 18:07:19 140183024801920 [Warning] Could not open mysql.plugin table. Some options may be missing from the help text
  --default-time-zone=name
default-time-zone                                          (No default value)
system-time-zone 

编辑。新研究

<?php
date_default_timezone_set('America/Los_Angeles');

$config=parse_ini_file(__DIR__.'/../config.ini',true);
$db = $config['mysql'];
$db=new \PDO("mysql:host=$db['host'];dbname=$db['dbname'];charset=$db['charset']",$db['username'],$db['password'],array(\PDO::ATTR_EMULATE_PREPARES=>false,\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_DEFAULT_FETCH_MODE=>\PDO::FETCH_OBJ));

$os=(new \DateTime())->getOffset();
if($os>(13*60*60)) $os=-24*60*60; //MySQL/MariaDB bug for Pacific/Kiritimati, Pacific/Chatham, and Pacific/Apia
$os = $os >= 0?'+'.gmdate("G:i", $os):'-'.gmdate("G:i", -$os);

$stmtSelect = $db->prepare("SELECT id, NOW() now, mydatetime FROM test WHERE id = ?");
$stmtInsert = $db->prepare("INSERT INTO test(id, mydatetime) VALUES(?,NOW())");
$stmtSelectConvert1 = $db->prepare("SELECT id, NOW() now, CONVERT_TZ(mydatetime, '+00:00', '$os') mydatetime FROM test WHERE id = ?");

$stmtInsert->execute([1]);

$stmtSelect->execute([1]);
echo("\n\nINSERTED BEFORE SETTING SQL TIMEZONE.  NO CONVERT\n");
print_r($stmtSelect->fetch());

$stmtSelectConvert1->execute([1]);
echo("\n\nINSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT1\n");
print_r($stmtSelectConvert1->fetch());

echo("\n\nSET SQL TIMEZONE\n");
$db->exec("SET time_zone='$os';");

$stmtInsert->execute([2]);

$stmtSelectConvert2 = $db->prepare("SELECT id, NOW() now, CONVERT_TZ(mydatetime, '+00:00', '$os') mydatetime FROM test WHERE id = ?");

$stmtSelect->execute([1]);
echo("\n\nINSERTED BEFORE SETTING SQL TIMEZONE.  NO CONVERT\n");
print_r($stmtSelect->fetch());

$stmtSelectConvert1->execute([1]);
echo("\n\nINSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT1\n");
print_r($stmtSelectConvert1->fetch());

$stmtSelectConvert2->execute([1]);
echo("\n\nINSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT2\n");
print_r($stmtSelectConvert2->fetch());

$stmtSelect->execute([2]);
echo("\n\nINSERTED AFTER SETTING SQL TIMEZONE.  NO CONVERT\n");
print_r($stmtSelect->fetch());

$stmtSelectConvert1->execute([2]);
echo("\n\nINSERTED AFTER SETTING SQL TIMEZONE.  YES CONVERT1\n");
print_r($stmtSelectConvert1->fetch());

$stmtSelectConvert2->execute([2]);
echo("\n\nINSERTED AFTER SETTING SQL TIMEZONE.  YES CONVERT2\n");
print_r($stmtSelectConvert2->fetch());

输出

INSERTED BEFORE SETTING SQL TIMEZONE.  NO CONVERT
stdClass Object
(
    [id] => 1
    [now] => 2018-02-27 20:16:22
    [mydatetime] => 2018-02-27 20:16:22
)




INSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT1
stdClass Object
(
    [id] => 1
    [now] => 2018-02-27 20:16:22
    [mydatetime] => 2018-02-27 12:16:22
)




SET SQL TIMEZONE




INSERTED BEFORE SETTING SQL TIMEZONE.  NO CONVERT
stdClass Object
(
    [id] => 1
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 20:16:22
)




INSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT1
stdClass Object
(
    [id] => 1
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 12:16:22
)




INSERTED BEFORE SETTING SQL TIMEZONE.  YES CONVERT2
stdClass Object
(
    [id] => 1
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 12:16:22
)




INSERTED AFTER SETTING SQL TIMEZONE.  NO CONVERT
stdClass Object
(
    [id] => 2
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 12:16:22
)




INSERTED AFTER SETTING SQL TIMEZONE.  YES CONVERT1
stdClass Object
(
    [id] => 2
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 04:16:22
)




INSERTED AFTER SETTING SQL TIMEZONE.  YES CONVERT2
stdClass Object
(
    [id] => 2
    [now] => 2018-02-27 12:16:22
    [mydatetime] => 2018-02-27 04:16:22
)
NotionCommotion
Quote
MultiQuote
Edit

【问题讨论】:

您尝试直接在 Maria 的 my.cnf 文件中更改 time_zone 吗? default_time_zone=Europe/Parishttps://***.com/questions/26349714/mariadb-set-timezone-in-config 也许我看不到你的问题。 MariaDB 使用系统时区。您可以通过转到 /etc/my.cnf.d/mariadb-server.cnf 来更改它,在 [server] 中为 UTC 或您需要的添加“default-time-zone=+00:00”。 (1) 问题是什么? (2) 输出的哪一部分不是您所期望的? (3)“运行这些脚本几分钟后,日期返回......”是什么意思? @MatheusOliveira 另一个问题,但我似乎没有。请参阅dba.stackexchange.com/questions/198737/…。 【参考方案1】:

...或者更可能是我做错了什么。

宾果! :-)

看这里...(强调):

MariaDB [datalogger]> 解释要点;
+----------------+-------------+------+-----+----- ----+----------------+
|领域 |类型 |空 |钥匙 |默认 |额外 |
+----------------+-------------+------+-----+----- ----+----------------+
|编号 |整数(11) |否 |优先级 |空 |自动增量 |
| idPublic |整数(11) |否 |穆尔 | 0 | |
|帐户ID |整数(11) |否 |穆尔 |空 | |
|姓名 | varchar(45) |否 |穆尔 |空 | |
|价值 |浮动 |是 | |空 | |
|值旧 |浮动 |是 | |空 | |
|单位 | varchar(45) |是 | |空 | |
|类型 |字符(8) |否 |穆尔 |空 | |
|坡度 |浮动 |否 | | 1 | |
|拦截 |浮动 |否 | | 0 | |
| tsValue 更新 | 日期时间 |是 | |空 | |
+----------------+-------------+------+-----+----- ----+----------------+
一组 11 行(0.00 秒)

MariaDB 有两种不同的数据类型能够存储日期 + 时间:DATETIME(您正在使用)和 TIMESTAMP(您没有使用)。

主要区别在于 TIMESTAMP 从会话 time_zone 转换为会话并以 UTC 格式存储,而 DATETIME 只是按提供的方式存储和检索(不考虑时区 )。

这记录在Time zone effects:

某些功能会受到时区设置的影响。其中包括

NOW() CURTIME() UNIX_TIMESTAMP()

以及从TIMESTAMP 列中存储和检索的值。后者在存储时转换为 UTC (Coordinated Universal Time),并在检索时转换回来。

部分功能不受影响。其中包括:

UTC_TIMESTAMP()

还有DATETIMEDATETIME 列。

因此,无论您如何调整时区设置,如果您的值存储在 DATETIME 列中,那么它们将始终按照输入的内容进行检索。如果您希望 MariaDB 在插入/检索时处理会话时区之间的转换,则必须改用 TIMESTAMP 列。

【讨论】:

感谢eggyal,我发现我可以在将NOW() 插入记录之前设置时区,该值将是本地时间,但是,这从来都不是一件好事。相反,我一直使用 UTC 时间保存 DATETIME,如果需要本地时间,则使用 CONVERT_TZ() 检索。 @user1032531:我建议使用TIMESTAMP 并为自己省去麻烦(另外还可以省去以后想记住它是如何工作的任何人),但这取决于你。

以上是关于如何使用 MariaDB 返回本地时间 SQL 结果的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL:, 如何将变量传递给 SELECT 语句并返回所有结果行

选择 Mariadb 中除顶行以外的所有行

MariaDB:如何使用STR_TO_DATE考虑本地日期格式?

两个日期之间每天未结订单的 SQL 计数

SQL Concat int 到 mariadb 中的字符串

如何将 SQL 查询结果行作为具有不同标题名称的列? [关闭]