代码之家  ›  专栏  ›  技术社区  ›  PacificNW_Lover

如果事件超过特定时区,如何捕获基于UTC的事件?

  •  0
  • PacificNW_Lover  · 技术社区  · 7 年前

    有一个MySQL数据库,其默认时区是Linux上的系统时区,即UTC。

    CREATE TABLE `event_schedule` (
       `id` int(11) NOT NULL AUTO_INCREMENT,
       `description` varchar(30) DEFAULT NULL,
       `event_id` varchar(30) NOT NULL DEFAULT,
       `event_type_id` varchar(30) NOT NULL DEFAULT,
       `event_date` datetime DEFAULT NULL,
       `event_date_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
    )
    

    | 1 | karoake | aab3223  | 2  |  2017-08-15 00:00:00 | 2017-08-15 16:00:00 |
    | 2 | comedy  | cce8465  | 3  |  2017-08-25 00:00:00 | 2017-08-25 19:00:00 | 
    

    在Spring JDBC查询中,我得到如下当前事件:

    private String getEventIdFromEventDay(String eventTypeId) {
        String eventId = "";
        SqlRowSet resultSet = null;
    
        try {
            resultSet = jdbcTemplate.queryForRowSet(
                    "select * from event_schedule where event_date >= CURDATE() and event_type_id=? order by event_date limit 1;",
                    new Object[] { eventTypeId });
            if (resultSet != null && resultSet.next()) {
                    eventId = resultSet.getString("event_id");
                }
            }
        } 
        catch (Exception e) {
            logger.error("Exception om getEventIdFromEventDay: " + e);
        }
        return eventId;
    }
    

    所以,情况是这样的:

    • 例如,如果某个事件发生在特定的一天,如果喜剧事件发生在2017-08-24,从我的数据库中的太平洋标准时间下午7:00开始,则该事件发生在UTC时间:

      | 2017-08-25 00:00:00 | 2017-08-25 19:00:00 |

      因此,对于太平洋标准时间下午5:00或之后开始的每个事件,此查询都有效。

    • 然而,假设一个事件开始于2017-08-15太平洋标准时间下午4:00(这意味着是UTC的11:00),这个查询只工作到太平洋标准时间下午4:59,因为太平洋标准时间下午5:00,MySQL中的系统时钟变为午夜(导致第二天)。解决此问题的唯一方法是手动将该日期更新到第二天2017-08-16,以使此查询生效。

    我想知道是否有更好的查询我正在尝试做什么。。。

    尝试了以下操作:

    select * from event_schedule where CONVERT_TZ(event_date,'+00:00','-07:00') >= CONVERT_TZ(CURDATE(),'+00:00','-07:00') and CONVERT_TZ(TIMESTAMP(event_date, '23:59:59'),'+00:00','-07:00') <= CONVERT_TZ(TIMESTAMP(CURDATE(), '23:59:59'),'+00:00','-07:00') and event_type_id = "2";
    

    收益率:

    Empty set (0.00 sec)
    

    有人知道我怎么解决这个问题吗?


    阅读巴兹尔的建议后编辑

    // Get current time in UTC
    Instant now = Instant.now();
    
    // Get time zone in PST
    ZoneId pstTimeZone = ZoneId.of("America/Los_Angeles");
    
    // Adjust into that time zone from UTC, producing a ZonedDateTime object.
    ZonedDateTime adjustedZDTimeZoneFromUtc =now.atZone(pstTimeZone);
    
    // Extract Date Only Value
    LocalDate localDate = adjustedZDTimeZoneFromUtc.toLocalDate();
    
    // Determine the first moment of the following day for that timezone
    ZonedDateTime zdt = localDate.plusDays(1).atStartOfDay(pstTimeZone);
    
    // Adjust back to UTC from that Time Zone
    Instant firstMomentOfTomorrow = zdt.toInstant();
    System.out.println("\n\n\t\tFirst moment of tomorrow: " + firstMomentOfTomorrow + "\n\n");
    

    First moment of tomorrow: 2017-10-25T07:00:00Z
    

    因此,当尝试在原始SQL调用中使用明天日期的第一个时刻时(为了在将其放回Java代码中之前对其进行测试):

    SELECT * FROM event_schedule WHERE event_date >= now() AND event_date <= "2017-10-25T07:00:00Z";
    

    它产生:

    Empty set, 1 warning (0.00 sec)
    

    也尝试了巴兹尔的提问:

    SELECT * FROM event_schedule WHERE when >= now() AND when < "2017-10-25T07:00:00Z";
    

    ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'when >= now() AND when < "2017-10-25T07:00:00Z"' at line 1
    

    问题:

    1. 我应该创建一个名为 first_moment_of_tomorrow

    select的问题 now() vs。 curdate() 现在是用实际秒数返回实际日期,而event_date的格式如下:

    2017-08-15 00:00:00 
    

    因此,在尝试curdate时:

    select curdate();
    

    结果如下:

    +------------+
    | curdate()  |
    +------------+
    | 2017-10-24 |
    +------------+
    1 row in set (0.01 sec)
    

    现在尝试()时:

    select now();
    

    结果如下:

    +---------------------+
    | now()               |
    +---------------------+
    | 2017-10-24 18:27:37 |
    +---------------------+
    1 row in set (0.00 sec)
    

    有人知道问题是什么吗?我需要对 event_schedule.event_date :(

    2 回复  |  直到 7 年前
        1
  •  1
  •   Basil Bourque    7 年前

    只需存储一个日期时间

    试图改变你的两个日期时间值会让你发疯。

    学会在UTC中思考和工作。通常,您的所有数据存储和数据交换都应该在UTC中,您的大部分业务逻辑也应该在UTC中。仅在需要时调整到时区,例如通过使用用户的时区来确定“同一天”的含义。

    重要提示:如果您将活动安排得足够远,以至于可能与新宣布的政府改变时区的授权相冲突,那么您还有其他问题。一般来说,这一时间范围是几个月或几个月,但在一些地方,如土耳其,最近可能只有几周。在这样的未来计划中,您将存储日期和时间 没有

    查询前确定时间点

    使用Java确定时间点。不要出于您的目的调用SQL中的当前时间函数,因为这会有所不同 在期间 正如你所看到的那样。

    仅使用java。时间类,避免了麻烦的遗留类( Date , Calendar 等)。

    TIMESTAMP TIMESTAMP WITH TIME ZONE 其中传入值调整为UTC,然后存储。 因此,按UTC查询。

    获取UTC中的当前时刻。

    Instant now = Instant.now();
    

    确定你的目标范围。显然,你想要一天剩下的时间,由用户首选/预期时区定义一天。

    指定 proper time zone name 格式为 continent/region America/Montreal , Africa/Casablanca Pacific/Auckland . 切勿使用3-4个字母的缩写,例如 PST EST IST 因为他们是

    ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
    

    ZonedDateTime 对象

    ZonedDateTime nowZdt = now.atZone( z ) ;
    

    从那以后 分区DateTime ,提取仅日期值,a LocalDate

    LocalDate ld = nowZdt.toLocalDate() ;
    

    确定该时区第二天的第一个时刻。

    ZonedDateTime zdt = ld.plusDays( 1 ).atStartOfDay( z ) ;
    

    从该时区调整回UTC。

    Instant firstMomentOfTomorrow = zdt.toInstant() ;
    

    沿着以下几行进行查询:

    String sql = "SELECT * FROM tbl WHERE when >= ? AND when < ? ; " ; // Do not use `BETWEEN`.
    …
    myPreparedStatement.setObject( 1 , now ) ;
    myPreparedStatement.setObject( 2 , firstMomentOfTomorrow ) ;
    

    使用符合JDBC 4.2或更高版本的JDBC驱动程序进行检索。

    Instant instant = myResultSet.getObject( … , Instant.class ) ;
    

    所有这些问题在堆栈溢出上已经讨论过多次。搜索更多信息、讨论和示例代码。


    关于java。时间

    这个 java.time 框架内置于Java 8及更高版本中。这些课程取代了麻烦的老人 legacy 日期时间类,例如 java.util.Date , Calendar ,& SimpleDateFormat .

    这个 Joda-Time maintenance mode ,建议迁移到 课程。

    Oracle Tutorial JSR 310 .

    在哪里可以获得java。时间课程?

    这个 ThreeTen-Extra project扩展了java。额外上课的时间。该项目是未来可能添加到java的一个试验场。时间您可以在这里找到一些有用的类,例如 Interval , YearWeek , YearQuarter more .

        2
  •  0
  •   PacificNW_Lover    7 年前

    select * from event_schedule where event_id=? and bye_week=0 and date(convert_tz(event_date_time,'UTC','US/Pacific'))=date(convert_tz(now(), @@system_time_zone, 'US/Pacific'))