即使提供了浏览器时区,Java将字符串转换为日期也会减少一天

时间:2018-04-05 18:41:07

标签: java timezone date-parsing java-date

我将Angular应用程序中的日期作为String发送到服务器并转换为java DatePicker对象以存储在数据库中。

同时从UI发送Date以在转换时使用客户端的时区。 (谷歌搜索后,我发现这种方法可以根据用户位置获得正确的结果)

编写以下代码进行转换:

timeZoneOffset

预期输出

  

默认时区:America / New_York
      抵消时区:GMT + 05:30
      转换日期:星期四4月5日00:00:00 IST 2018年

服务器中的实际结果

  

默认时区:America / New_York
      抵消时区:GMT + 05:30
      转换日期:4月4日星期三14:30:00 EDT 2018

为什么即使设置用户时区,日期也会减少到一天?我是日期和时间相关概念的新手,我用谷歌搜索了几次没有找到答案,有人可以帮忙解决这个问题。

提前致谢

2 个答案:

答案 0 :(得分:1)

它没有减少一天,它减少了11.5小时。这恰好是GMT + 05:30和“America / New_York”之间的时差,即GMT-04:30或GMT-05:30(取决于一年中的时间)。

我认为格林尼治标准时间+05:30在印度的某个地方,因为那是使用30分钟偏移而不是整整一小时的唯一地方。当它在4月5日在印度时,它仍然是4月4日在纽约。

问题可能是你没有从客户那里得到时间,所以它将假设午夜。如果您正在进行时区转换,最好包括实际时间。

答案 1 :(得分:1)

Answer by Godfrey是正确的。

TL;博士

LocalDate.parse( 
    "04/05/2018" ,
    DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) 
)
.atStartOfDay(
    ZoneId.of( "Asia/Kolkata" ) 
)
.toString()
  

2018-04-05T00:00 + 05:30 [亚/加尔各答]

要在数据库中存储,请使用UTC。

当新的一天在印度开始时,UTC的日期仍然是“昨天”,所以4月4日而不是4月5日。同一时刻,时间轴上的同一点,不同的挂钟时间。

LocalDate.parse( 
    "04/05/2018" ,
    DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) 
)
.atStartOfDay(
    ZoneId.of( "Asia/Kolkata" ) 
)
.toInstant()
  

2018-04-04T18:30:00Z

java.time

你正在使用可怕的旧日期时间类,这些类已被证明设计糟糕,令人困惑且麻烦。它们现在被 java.time 类取代。

完全避免遗留日期时间类

ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(-timeZoneOffset * 60);
…
TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);

您正在将现代类(ZoneOffset)与麻烦的遗留类(TimeZone)混合在一起。 不要将现代类与遗留类混合。忘记所有旧类,包括DateCalendarSimpleDateFormat java.time 类旨在完全取代遗留类。

而不是TimeZone,请使用ZoneId(和ZoneOffset)。

LocalDate

将输入字符串解析为LocalDateLocalDate类表示没有时间且没有时区的仅限日期的值。

String input = "04/05/2018" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;

偏移与时区

  

int timeZoneOffset = -330;

offset-from-UTC 不是时区。偏移量只是从UTC移位的小时数,分钟数和秒数。您选择的变量名称表明可能存在混淆。

ZoneOffset offset = ZoneOffset.of( -3 , 30 ) ;  

time zone是特定地区人民使用的偏移的过去,现在和将来变化的历史记录。因此,时区始终优于偏移。

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

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;  // India time zone. Currently uses offset of +05:30 (five and a half hours ahead of UTC). 

一天的第一时刻

你好像是在那个区域的那个日期的第一个时刻。让java.time确定当天的第一时刻。不要以为时间是00:00:00。在某些日期的某些区域,日期可能会在另一个时间开始,例如01:00:00。

ZonedDateTime zdt = ld.atStartOfDay( z ) ;  // Determine the first moment of the day on this date in this zone. Not always 00:00:00.

作为为什么你应该使用时区而不仅仅是从UTC偏移的一个例子,看看-330的示例数据,我可能很容易误解为比UTC晚三个半小时。此偏移目前仅在区域America/St_Johns中使用,并且仅在一年中的部分时间使用。因此,如果您将-03:30的偏移量应用于该年度错误部分的日期,则您的结果将无效但未被检测到。

使用偏移(不推荐)

但是你的例子没有时区,所以让我们选择偏离UTC而不是区域。

使用int整数来表示与UTC的偏移量是类型的不良选择。首先,它含糊不清。可能会将-330解释为在-03:30偏离计划后三个半小时的笨拙尝试。其次,它使解析比需要更棘手。第三,作为几分钟,它忽略了秒数偏移的可能性。第四,尽管常见的用法和标准用法相反,你使用负数<%>提前提前的UTC(显然)。最后,它忽略了ISO 8601设置的明确标准,用于将偏移量表示为文本:±HH:MM:SS(和变体)。顺便说一句,填充零在标准中是可选的,但我建议总是包括因为各种库和协议所期望的。

您的意图似乎是整数所需的分钟数。

long seconds =( TimeUnit.MINUTES.toSeconds( - 330 ) * -1 );  // Multiply by negative one to flip the sign to standard ISO 8601 usage, where `+` means “ahead* of UTC and `-` means *behind* UTC.
  

秒:19800

ZoneOffset offset = ZoneOffset.ofTotalSeconds( ( int ) seconds );
  

offset.toString():+ 05:30

最后一步:在此偏移量中获取当天的第一个时刻。警告:我们不确定这个偏移在这个日期是否有效,因为我们没有时区。

从返回的ZonedDateTime转换为OffsetDateTime。如上所述,确定第一时刻应始终使用时区来完成,从而获得ZonedDateTime。我们违反了使用偏移量的合理做法,但使用返回的ZonedDateTime对象会产生误导,因为我们缺少真正的时区,并且只有一个偏移量。因此OffsetDateTime类使我们的意图清晰,我们的代码更加自我记录。

    OffsetDateTime odt = ld.atStartOfDay( offset ).toOffsetDateTime();

同样,这种使用偏移的方法是而不是推荐,因为您应该从用户那里收集时区 name 作为输入而不是偏移量。

UTC

通常最好以UTC格式存储时刻。

InstantOffsetDateTime中提取ZonedDateTime以获得与UTC相同的时刻。

Instant instant = zdt.toInstant() ;
  

2018-04-04T18:30:00Z

关于 java.time

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

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

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

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

从哪里获取java.time类?

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