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

为世界各地的所有商店创建一个上午9点开始的促销活动

  •  1
  • ktm5124  · 技术社区  · 6 年前

    例如,让我们假设,全球所有商店的促销活动从上午9点开始。这意味着它开始于上午9点CST在芝加哥的商店,上午9点PST在西雅图的商店,上午9点GMT在英国的商店。

    在我们 promotions 在Postgres上,我们会将这次促销的开始时间设置为09:00:00。

    每个商店都有一台带有Web浏览器的计算机,用于查找可用的促销活动。它需要将本地时间传递给服务器,以便服务器可以返回该本地时间的所有提升。因此,我们需要找到一种方法来捕获JavaScript中的本地时间,对其进行编码,将其发送到Java后端,重构它,然后将其与起始时间进行比较。 促销 表。

    当然,当地时间取决于时区。如果在芝加哥是上午9点,那么芝加哥的一家商店应该告诉服务器是上午9点。没有时区指示就发送UTC时间是徒劳的。

    问题: 什么是捕获JavaScript本地时间(基于时区)的好方法,将其编码,发送到Java后端,将其重构为Java。 Date ,然后比较Java 日期 在Postgres数据库中使用上午9点的促销开始时间?

    我的(不满意的)方法: 我能想到的最好方法是使用javascript的 Date.getTime 方法,以及时区偏移量,可以使用javascript以分钟计算 Date.getTimezoneOffset 方法并转换为毫秒。在毫秒内从UTC时间减去时区偏移量,然后我们可以创建一个Java 日期 来自结果差异的对象。如果是芝加哥的上午9点,那么,希望Java 日期 将于上午9点储存。然而,这种方法有点奇怪,就是Java。 日期 实际上将存储9am UTC,即使它代表9am CST。这只是我不满意这种方法的原因之一。你能想出更好的办法吗?

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

    后格雷斯

    在Postgres中,当您的意思是在某个日期的任何地方上午9点或任何地方上午9点时,请使用列数据类型 TIMESTAMP WITHOUT TIME ZONE . 输入中包含的任何时区或与UTC的偏移量都将被忽略,日期和时间将按原样(无调整)并存储。此数据类型故意缺少 time zone offset-from-UTC .

    对于一天中没有日期的时间,请使用 TIME WITHOUT TIME ZONE 数据类型。Postgres还提供 TIME WITH TIME ZONE 只是因为它是SQL规范所要求的;这个 WITH 类型是无意义的,不应使用。

    Postgres是此类项目的最佳选择,因为它在这两个方面都提供了极好的日期时间支持。 its data types 而在 its functions . 数据库的日期-时间特性差异很大。

    爪哇

    在Java后端上,只使用现代 Java.时间 类。这些年前取代了 可怕的 与最早的Java版本捆绑在一起的旧日期时间类。

    如果还没有使用Java 8或更高版本,则在Java 6和7中的后端找到几乎相同的功能。 ThreeTen-Backport 项目。很值得把这个库添加到您的项目中。从那些给你带来 Java.时间 课程和乔达时间计划,都由同一个人斯蒂芬·科尔伯恩领导。

    LocalDateTime

    Java.时间 ,使用 本地日期时间 当你的意思是在某个特定日期的任何地方/任何地方早上9点的时候。喜欢 不带时区的时间戳 在Postgres中,这个类故意缺少任何分区或偏移的概念。

    LocalDateTime ldt = LocalDateTime.of( 2018 , 1 , 23 , 15 , 0 , 0 , 0 ) ;  // 3 PM on 23rd of January this year.
    

    LocalTime

    如果只指一天中没有日期的时间,请使用该类 当地时间 .

    LocalTime lt = LocalTime.of( 15 , 0 ) ;  // 3 PM.
    

    JDBC

    从JDBC 4.2及更高版本开始,您可以交换 Java.时间 数据库中的对象 getObject setObject 方法。

    LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
    

    如果您的JDBC驱动程序还没有更新到4.2,那么返回到可怕的旧遗留类,但立即转换为 Java.时间 类。

    考虑到遗留类缺少一个日期加上一天中没有时区的时间的类,我们必须伪造它。使用 java.sql.Timestamp 它表示在UTC中分辨率为纳秒的时刻,而忽略它是在UTC中的事实。

    java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
    

    对于Java 8和以后,使用新方法添加到旧类中进行转换。先转换为 java.time.Instant ,它还表示UTC中分辨率为纳秒的时刻。然后转换为 本地日期时间 有效地消除了UTC的概念。

    Instant instant = ts.toInstant() ;  // Convert from legacy class to modern one.
    LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ;  // Remove the concept of UTC (or any other offset or zone) from our data.
    

    对于Java 6和7使用 螺纹后端口 库,在其实用程序中使用转换方法 DateTimeUtils 班级。

    org.threeten.bp.Instant instant = org.threeten.bp.DateTimeUtils.toInstant( ts ) ;  // Convert from legacy class to modern.
    org.threeten.bp.LocalDateTime ldt = LocalDateTime.ofInstant( instant , ZoneOffset.UTC ) ;  // Remove the concept of UTC (or any other offset or zone) from our data.
    

    ZonedDateTime

    这个 Local… 类的定义是没有真正意义的,除非将它们放在时区的上下文中。一 本地日期时间 等一下,是吗 表示时间线上的一个点。

    指定一个 proper time zone name 格式为 continent/region ,如 America/Montreal , Africa/Casablanca Pacific/Auckland . 切勿使用2-4字母缩写,如 PST BST EST IST 因为它们是 真正的时区,不标准,甚至不独特!.

    LocalDateTime ldt = 
        LocalDateTime.of(
            LocalDate.of( 2018 , Month.January , 23 ) ,
            LocalTime.of( 9 , 0 ) 
        )
    ;
    
    ZoneId zLosAngeles = ZoneId.of( "America/Los_Angeles" ) ;  // Seattle time zone.
    ZonedDateTime zdtSeattle = ldt.atZone( zLosAngeles ) ;
    
    ZoneId zChicago = ZoneId.of( "America/Chicago" ) ;  
    ZonedDateTime zdtChicago = ldt.atZone( zChicago ) ;
    
    ZoneId zLondon = ZoneId.of( "Europe/London" ) ;  
    ZonedDateTime zdtLondon = ldt.atZone( zLondon ) ;
    

    我们有三个 ZoDeDeDeTimeTimes 物体: zdtSeattle , zdtChicago zdtLondon .这些都是今年1月23日上午9点。要知道,这是三个非常不同的时刻,每一个时刻都比你往东走早几个小时。它们都有相同的挂钟时间(23日上午9点),但时间线上有三个不同的点。

    javascript

    虽然我对javascript的了解还不足以肯定,但我怀疑您是否有足够丰富的库来处理日期时间。这个 Java.时间 框架是行业领先的。

    对于Web客户端用户界面开发,我使用 Vaadin 因此,这是一个非问题:纯Java在后端自动生成Web浏览器所需的HTML/CSS/DOM/JavaScript。

    找到一种用JavaScript捕获本地时间的方法

    至于在客户机中检测当前默认时区,我不是专家,但我记得浏览器不会返回指定时区,只返回与UTC的偏移量。见 Answer by Matt Johnson 为了一个可能的解决方案。在任何应用程序(桌面或网络)中,如果正确的时区至关重要,那么您必须询问或 与用户确认所需/预期时区 . 在你的用户界面上的某个地方指出你的应用程序使用的时区也许是明智的。

    如果需要在前端的Java后端和JavaScript代码之间交换日期时间值,则主要有两种选择:

    • 国际标准化组织8601
    • 从epoch开始计数

    国际标准化组织8601

    这个 ISO 8601 标准定义了用于交换日期时间值的各种文本格式。这些都是为了避免歧义而设计的。它们很容易被机器解析,也很容易被跨文化的人类阅读。

    这个 Java.时间 类在生成/分析字符串时默认使用这些格式。

    从epoch开始计数

    我不建议使用这种方法,因为这种方法容易混淆和出错,在发送或接收的人和库之间存在歧义和错误假设。

    epoch reference date 是用作基线的时间点。然后向前或向后计数是由一些粒度组成的。

    一个大问题是 dozens of epoch references 用于各种系统。这个 java.time时间 类默认使用 Unix time 1970年第一个时刻的纪元,1970-01-01T00:00Z。

    另一个问题是存在许多粒度,如整秒、毫秒、微秒和纳秒。程序员必须清楚地记录/交流起作用的粒度。

    如果您要将三家商店的三个开放时间作为epoch的计数发送到javascript,那么您将发送三个不同的数字。

    long millisecondsSeattle = zdtSeattle.toInstant().toEpochMilli() ;
    long millisecondsChicago = zdtChicago.toInstant().toEpochMilli() ;
    long millisecondsLondon = zdtLondon.toInstant().toEpochMilli() ;
    

    在三个不同的时刻产生三个不同的数字。


    关于 java.time时间

    这个 java.time 框架是在Java 8和之后构建的。这些阶级取代了麻烦的旧阶级。 legacy 日期时间类,如 java.util.Date ,请 Calendar ,& SimpleDateFormat .

    这个 Joda-Time 项目,现在在 maintenance mode ,建议迁移到 java.time 类。

    要了解更多信息,请参阅 Oracle Tutorial .以及搜索堆栈溢出以获得许多示例和解释。规格为 JSR 310 .

    你可以换 Java.时间 直接使用数据库的对象。使用A JDBC driver 顺从 JDBC 4.2 或稍后。不需要字符串,不需要 java.sql.* 类。

    在哪里获取java.time类?

    这个 ThreeTen-Extra Project使用其他类扩展java.time。这个项目是将来可能添加到java.time的一个试验场。您可以在这里找到一些有用的类,例如 Interval , YearWeek , YearQuarter ,和 more .

        2
  •  1
  •   Matt Johnson-Pint    6 年前

    您不需要捕获用户的本地时间,只需要捕获他们的IANA时区标识符,例如 "America/Los_Angeles" . 然后,可以在接受时间区的API中使用Java后端代码。

    在大多数现代浏览器中,您可以这样捕获时区ID:

    Intl.DateTimeFormat().resolvedOptions().timeZone
    

    如果您需要支持较旧的浏览器,有几个库将在可用时使用此intl API,但在不可用时将返回到有根据的猜测。 More on this here .