由于Java中的TimeZone问题而未选择数据

时间:2013-03-19 19:22:58

标签: java

我的代码中存在日期/时间计算的时区问题。 将需要你的建议。以下是情景。

我的应用程序必须从hbase db中选择数据。将根据hbase中的表中的日期搜索条件选择此数据。该表有一个日期时间列,用于过滤。

现在填写此表中数据的另一个应用程序是以GMT时区格式插入日期时间(该团队称之为)。该应用程序部署在EST时区的服务器上。我从用户那里得到一个输入日期(没有时间组件),我需要将其转换为GMT时区。但目前正在发生的事情是,当我进行转换时,一些数据没有在EST时区中被选中。相同的数据在我当地的时区IST中被选中。我已在代码中完成了对GMT的转换,该代码部署在我的本地和远程server.example中。

表格中存在数据的日期:2013-03-15 01:30:30

用户输入的数据:2013-03-15(无时间组件)。我特意添加了时间组件。

添加时间组件后的日期:开始日期-2013-03-15 00:00:00,结束日期 - 2013-03-15 23:59:59。

我在IST时区(UTC + 5:30)。所以转换为GMT的开始和结束日期都是

开始日期转换为GMT -2013-03-14 18:30:56。

结束日期转换为GMT - 2013-03-15 18:30:56。

现在,由于用户2013-03-15 01:30:30提供的日期属于上述范围,因此该表中的数据将被拉入此时区。

相同的代码部署在EST(UTC-5:00)的远程服务器中。

开始日期转换为GMT -2013-03-15 05:00:56。

结束日期转换为GMT - 2013-03-16 05:00:56。

现在由于用户2013-03-15 01:30:30提供的数据不属于此范围,因此数据不会被拉高。

我不确定如何处理这种情况,以便计算在各个时区之间保持一致。因为测试人员说数据应该跨时区保持一致。但正如您所看到的那样,GMT转换已完成,但日期因两个时区和数据的偏移量而关闭,因此数据不会被拉高。 如果你能让我知道如何处理这个问题,那将会很有帮助。

我没有在此处添加任何代码,因为它是在线发现的时区的标准代码,在大多数情况下也是如此。

2 个答案:

答案 0 :(得分:0)

这实际上取决于你在这里想要达到的目标。

如果真正希望将输入解释为本地时间,并转换为GMT来检查数据库,那么结果实际上是一致的,反过来考虑问题,哪些记录如果你把HBase的每个日期神奇地转换成当地时间,你会得到吗?

如果用户输入的日期实际上是TZ特定的,那么同一日期“文本”返回不同的结果是很自然的,因为两个用户实际上是指两个不同的时刻。

另一方面,如果您需要提供相同输入的用户获得完全相同的结果,那么您需要假设输入已经在某个一致的TZ中,例如,总是以UTC(或GMT in)你的情况)。

基本上你需要决定的是:

用户给出的输入2013-03-15是否特定于用户的TZ?或者在特定的TZ中,它总是在同一时刻吗? IST,EST,GMT,你决定。

如果您做出该决定,您的结果将保持一致。

答案 1 :(得分:0)

TL;博士

使用半开放查询逻辑:

SELECT * 
FROM tbl 
WHERE when >= ? AND when < ?   -- Half-Open logic, where beginning is inclusive while the ending is exclusive.
;

使用两个参数进行查询:

  • 一天的开始时刻
  • 以后开始的时刻

在现代Java中使用 java.time 类:

myPreparedStatement.setObject(  
    1 , 
    LocalDate.parse( "2013-03-15" )         // Represent entire day as a `LocalDate` object.
        .atStartOfDay(                      // Determine first moment of that date in a particular time zone, which may or may not be 00:00:00.
            ZoneId.of( "Asia/Kolkata" ) 
        )                                   // Returns a `ZonedDateTime` object. Your JDBC driver should be able to handle that smartly. If not, extract a `Instant` object in UTC by calling `ZonedDateTime::toInstant()`.
) ; 


myPreparedStatement.setObject( 
    2 , 
    LocalDate.parse( "2013-03-15" )        
        .plusDays( 1 )                     // Move to the following day.
        .atStartOfDay( 
            ZoneId.of( "Asia/Kolkata" ) 
        )
) ; 

检索后,调整到任何所需的时区。

myResultSet.getObject( … , Instant.class)  // Extract a `Instant` object from selected row.
    .atZone(                               // Apply a `ZoneId` to get a `ZonedDateTime` object.
        ZonedId.of( "America/Montreal" )
    )

服务器默认时区不相关

您服务器的当前默认时区应与您的编程无关。您应该通过各种日期时间方法的可选参数指定所需/预期的时区。

同样适用于Locale:明确指定而不是隐式依赖当前默认值。

在我看来,这些论点应该是必需的而不是可选的。似乎很多程序员都忽略了时区和语言环境的问题。

java.time

现代方法使用 java.time 时间类。

使用符合JDBC 4.2或更高版本的JDBC驱动程序,您可以直接与数据库交换java.time类型。

在UTC中,请使用Instant。对于没有时间且没有时区的日期,请使用LocalDate

LocalDate ld = LocalDate.parse( "2013-03-15" ) ;

要搜索在一整天范围内保留片刻的数据库记录,我们需要确定当天的第一个时刻和第二天的第一个时刻。然后我们进行半开放查询,其中开头是包含,而结尾是独占

不要假设这一天从00:00:00开始。夏令时(DST)等异常表示该日可能会在某个其他时间开始,例如01:00:00。我们需要一个时区。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;  // Determine when that particular day begins in that particular zone.

第二天。致电LocalDate::plusDays移至第二天。

ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDays( z ) ;

如果您希望看到UTC挂钟时间,请提取InstantInstant类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。

Instant start = zdtStart.toInstant();
Instant stop = zdtStop.toInstant() ;

您的SQL应该类似于:

String sql = "SELECT * FROM tbl WHERE when >= ? AND when < ? ;" ; // Half-open logic, where beginning is inclusive while the ending is exclusive.

传递占位符的值。

myPreparedStatement.setObject( 1 , start ) ; // You can exchange java.time object with JDBC 4.2 or later.
myPreparedStatement.setObject( 2 , stop ) ; 

并检索。

Instant instant = myResultSet.getObject( "when" , Instant.class ) ;

如果您想通过其他地区(时区)的人使用的挂钟时间镜头查看同一时刻,请应用ZoneId获取ZonedDateTime

ZonedDateTime zdt = instant.atZone( ZoneId.of( "America/New_York" ) ) ;

UTC

只是提示:通常最好以UTC格式思考,存储,交换和记录值。仅在业务逻辑或用户界面要求时调整到时区。

在工作编程或管理时,忘记自己的教区时区,而不是用UTC思考。在桌面上保留UTC时钟。将UTC视为 One True Time™,所有其他区域仅仅是变化。

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

如果JDBC driver符合JDBC 4.2或更高版本,您可以直接与数据库交换 java.time 对象。不需要字符串或java.sql。* classes。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore