在正确的时间检索日期,与DST无关 语言:Java 数据库:mySQL

在正确的时间检索日期,与DST无关 语言:Java 数据库:mySQL,java,mysql,datetime,timezone,Java,Mysql,Datetime,Timezone,我必须为用户建立一个警报系统,在特定时间发送通知。例如,用户可以指定时间为上午7点,日期为2020年5月1日,这意味着必须在2020年5月1日上午7点发送警报。用户可以在世界的任何地方,因此我们必须考虑不同的时区。我最初认为我们必须以UTC存储时间。例如,如果我是EST时区的用户,db将存储2020年5月1日的日期和12点的时间(我计划将日期和时间存储在单独的列中)。但是,现在我很困惑,因为如果我上周存储了日期,即夏令时之前,存储的UTC时间应该是2020年5月1日上午11点。因此,当DST发生

我必须为用户建立一个警报系统,在特定时间发送通知。例如,用户可以指定时间为上午7点,日期为2020年5月1日,这意味着必须在2020年5月1日上午7点发送警报。用户可以在世界的任何地方,因此我们必须考虑不同的时区。我最初认为我们必须以UTC存储时间。例如,如果我是EST时区的用户,db将存储2020年5月1日的日期和12点的时间(我计划将日期和时间存储在单独的列中)。但是,现在我很困惑,因为如果我上周存储了日期,即夏令时之前,存储的UTC时间应该是2020年5月1日上午11点。因此,当DST发生时,用户不会在正确的时间收到警报。我如何存储时间,以便用户总是在早上7点收到警报?我必须将日期存储在用户本地时区吗?顺便提一下,我有一个存储用户时区信息的表。

MySQL
TIMESTAMP
数据类型非常适合这个问题。它们总是存储在
UTC
(只要服务器的操作系统没有设置奇怪的时区)。只要操作系统的时钟是UTC,您就会处于良好状态

然后,当您给出如下MySQL命令时:

   SET time_zone = 'America/New_York';
   INSERT INTO notification (deliver_timestamp, '2020-11-20 11:30:00')
在存储
时间戳的过程中,MySQL将其从当前时间转换为UTC。在出去的时候,它会把它转换回当地时间。例如,如果你这样做

   SET time_zone = 'America/Los_Angeles';
   SELECT timestamp FROM notifications;
您将得到
1010-11-20 08:30:00
,这是同一时间,但晚了三个小时。像
美国/纽约
亚洲/加尔各答
这样的字符串是时区的名称。MySQL和其他使用这种类型的软件知道所有关于不同地方何时从白天切换到常规的电子政治内容。更不用说这些规则的历史变化可以追溯到半个多世纪。这是一个复杂的系统

为了让它发挥作用 要使其在全球范围内运行,您必须为每个使用您的系统的人设置时区用户首选项设置。您必须装配某种下拉菜单,显示来自的。MySQL的
SET time\u zone='zoneinfo'
命令需要该zoneinfo名称。您可以在类中使用这些相同的文本字符串

然后,每次代表用户访问数据库时,都要在执行其他操作之前设置时区。在典型的web应用程序中,每个用户的时区偏好都是其用户配置文件的一部分

注意:只有
TIMESTAMP
列是这样工作的<代码>日期时间
列存储和检索您提供给它们的任何时间数据,而不通知时区

而且,如果Java代码需要UTC时间,请执行此操作

   SET time_zone = 'UTC';
   SELECT timestamp FROM notifications;
这可能会让你有点难以理解。MySQL使用zoneinfo规则进行这些转换。Zoneinfo知道何时是DST,何时不是。

使用
LocalTime
检查以下各项:

此类用于表示基于人的时间,例如 电影时间,或当地图书馆的开放和关闭时间

对于这个用例,带有时区或区域偏移的日期时间是错误的选择。我假设您的数据库中有用户的时区,并且您已经安排了一些玉米作业来发送通知。当作业运行以发送通知时,只需检查哪些用户的本地时间为上午7:00(根据您的要求和玉米作业的频率加/减几分钟,例如5分钟),例如

import java.time.LocalTime;
import java.time.ZoneId;

public class Main {
    public static void main(String[] args) {
        LocalTime time = LocalTime.now(ZoneId.of("Europe/London"));// Your server's timezone
        System.out.println(isLocalTimeSameAsNotificationTime(ZoneId.of("Asia/Calcutta"), time)); // User's timezone
    }

    static boolean isLocalTimeSameAsNotificationTime(ZoneId zone, LocalTime notificationTime) {
        LocalTime time = LocalTime.now(zone);// Local time at the param zone

        // True if +/- 5 minutes notificationTime
        return !(time.isBefore(notificationTime.minusMinutes(5)) || time.isAfter(notificationTime.plusMinutes(5)));
    }
}
corn作业中的查询应该类似于
SELECT userId,user FROM tableName WHERE notificationTime=now加/减5分钟
,现在您必须循环这个结果集。对于resultset中的每条记录,您需要调用函数,
IsLocalTimesMeasNotificationTime
将时区和服务器的本地时间作为参数传递,如果返回
true
,则触发通知


注意:我在这里提到的SQL查询是一个如何编写查询的示例;语法不正确。

您在数据库中以UTC存储值,但您使用带有用户时区的
ZoneDateTime
计算值。@Khazana对于
ZoneDateTime
,没有“DST之前”和“DST之后”,因为
ZoneDateTime
知道任何给定的日期/时间是白天还是标准时间,例如,它知道2020年5月1日上午7点是夏令时。这就是为什么您应该使用
ZonedDateTime
“将本地时间转换为UTC是一种有损操作”——如果您从本地日期/时间开始,转换为UTC,然后使用相同的时区规范转换回UTC,则这是不正确的。让我们调用本地日期/时间
t
。在这两种情况下使用的DST偏移量将是在时间
t
有效的,即使您在不同的时间使用不同的当前DST偏移量执行转换。假设您的事件发生在2020年10月31日当地时间07:00。您可以应用用户的本地时区将其转换为即时。假设现在是美国太平洋时间。此时,PDT适用,因此偏移量为-0700,UTC为
2020-10-31 14:00Z
。无论何时进行转换,这一点都不会改变。现在您要计算一周后的时间。获取该日期/时间,转换为
LocalDateTime
,添加周,然后转换回UTC。添加周后,新的本地值为
2020-11-06 07:00
,但由于DST无效,偏移量为-0800。。。。。。UTC是
2020-11-06 15:00Z
。再说一次,这永远不会改变。谢谢!但是如果你插入
设置时区='美国/纽约';在通知中插入(交付时间戳,'2020-11-20 11:30:00')
(考虑dat