当前位置: 首页 > 知识库问答 >
问题:

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

冯胤
2023-03-14

我从Angular应用程序将日期作为字符串发送到服务器,并转换为javadate对象存储在数据库中。

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

编写了以下代码进行转换:

public static void main(String args[]) throws ParseException {
    String inputDate = "04/05/2018"; // This date coming from UI
    int timeZoneOffset = -330; // This offset coming from UI 
                               // (new Date().getTimeZoneOffset())
    getDate(inputDate, timeZoneOffset);
}

public static Date getDate(String inputDate, int timeZoneOffset)
        throws ParseException {
    SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
    ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(-timeZoneOffset * 60);
    System.out.println("Default time zone: " + TimeZone.getDefault().getID());
    TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);
    System.out.println("Time zone from offset: " + timeZone.getID());
    dateFormat.setTimeZone(timeZone);
    Date date = dateFormat.parse(inputDate);
    System.out.println("Converted date: " + date);
    return date;
}

默认时区:America/New_York
从偏移量开始的时区:GMT+05:30
转换日期:2018年4月5日Thu 00:00:00

服务器中的实际结果:

默认时区:America/New_York
从偏移量开始的时区:GMT+05:30
转换日期:美国东部时间2018年4月4日Wed 14:30:00

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

提前致谢

共有1个答案

干宏邈
2023-03-14

戈弗雷的答案是正确的。

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

2018-04-05T00:00+05:30“亚洲/加尔各答”

对于数据库中的存储,请使用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类所取代。

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

您将现代类(ZoneOffset)与麻烦的遗留类(TimeZone)混合在一起。不要将现代类与遗留类混合使用。忘记所有旧类,包括datecalendarsimpledateformat。java.time类被设计为完全替代遗留类。

使用zoneid(和zoneoffset)代替timezone

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

偏离UTC不是时区。偏移量只是从UTC位移的小时数、分钟数和秒数。您对变量名的选择表明在这一点上可能存在混淆。

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

时区是一个特定地区的人们使用的过去、现在和将来偏移变化的历史。所以时区总是比偏移量更好。

大陆/地区格式指定适当的时区名称,例如美国/蒙特利尔非洲/卡萨布兰卡太平洋/奥克兰。切勿使用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的偏移量应用于一年中错误的某一天,那么您的结果将是无效的,但不会被检测到。

但是您的示例缺少时区,所以让我们使用offset-from-UTC而不是zone。

使用int整数来表示offset-from-UTC是一种错误的类型选择。首先是模棱两可。这个-330可能会被解释为在-03:30偏移量落后计划三个半小时的情况下进行的一次笨拙的尝试。其次,它使解析变得比需要的更棘手。第三,作为一个分钟数,它忽略了与秒相抵消的可能性。第四,对于UTC前面的偏移量使用负数(显然),尽管常见用法和标准用法是相反的。最后,它忽略了ISO 8601设定的将偏移表示为文本的明确标准:±hh:mm:ss(和变体)。顺便说一下,填充零在标准中是可选的,但我建议总是包含,因为各种库和html" target="_blank">协议都期望它。

您的意图似乎是整数预期的分钟数。

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 );

最后一步:在这个偏移量中得到一天的第一时刻。注意:由于缺少时区,我们不确定此偏移在此日期是否有效。

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

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

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

通常最好以UTC存储时刻。

offsetdatetimezoneddatetime中提取instant以获得与UTC相同的时刻。

Instant instant = zdt.toInstant() ;

2018-04-04T18:30:00Z

Java.time框架内置在Java8和更高版本中。这些类取代了麻烦的旧的遗留日期-时间类,如java.util.dateCalendar、&SimpleDateFormat

要了解更多信息,请参阅Oracle教程。并搜索堆栈溢出以获得许多示例和解释。规范是JSR310。

您可以直接与数据库交换java.time对象。使用与JDBC4.2或更高版本兼容的JDBC驱动程序。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

  • Java SE 8、Java SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9添加了一些小功能和修补程序。
    • 大部分Java.time功能在threeten-backport中后端移植到Java6&7。
    • java.time类的Android捆绑实现的更高版本。
    • 对于早期的Android(<26),ThreeTenABP项目适用于ThreeTen-Backport(上面提到)。查看如何使用ThreeTenabp…。

    ThreeTen-Extra项目使用额外的类扩展了java.time。这个项目是将来可能添加java.time的一个试验场。您可以在这里找到一些有用的类,如intervalyearweekyearquard等等。

 类似资料:
  • 问题内容: 我的时区有日期,我想将其转换为另一个时区,例如我的日期为“ 2013年3月15日下午3:01:53 PM”,位于时区“ GMT-06:00”。我想在“ GMT-05:00”时区进行转换。我的搜索量很多,对日期的实际工作方式感到困惑。如何将时区应用于日期。我尝试使用SimpleDateFormat,日历和偏移量。 它返回输出 。我想应该是 ,因为时间是在’GMT-06:00’时区,所以在

  • 问题内容: 该java.util.Date 方法显示当地时区的日期。 在几种常见的情况下,我们希望以UTC格式打印数据,包括日志,数据导出以及与外部程序的通信。 在UTC中创建String表示形式的最佳方法是什么? 如何用更好的格式替换无法排序的juDate 格式(感谢@JonSkeet!)? 附录 我认为以自定义格式和时区打印日期的标准方法非常繁琐: 我一直在寻找像这样的单线: 问题答案: 遵循

  • 问题内容: 我需要有关此java方法的一些建议。此方法的目的是采用表示日期的字符串-该字符串是从EST时区中的日期创建的-并将其转换为UTC时区中的java Date对象。 我看到的问题是dt的值似乎已关闭。例如,如果dateString为‘10 / 16/2012 12:06 PM’-我期望dt(以UTC为单位)的值类似于“ 2012年10月16日,星期二,下午4:06”。取而代之的是,dt的值

  • 我正在与它的时区约会,我想把它转换成另一个时区,例如。我的日期是“3/15/2013 3:01:53 PM”,它位于时区“GMT-06:00”。我想在'GMT-05:00'时区转换这个。我有很多搜索,我不知道实际约会是如何运作。如何将时区应用到日期。我尝试过SimpleDateFormat、Calender和Offset。 它返回输出。我猜应该是,因为时间在'GMT-06:00'时区,所以应该是H

  • 我有一些日期格式的字符串,例如 我使用SimpleDateForate并尝试将String类型转换为Date类型。它将String类型转换为Date类型,但是Java无法识别时区“MYT”,而是将其设置为计算机的默认时区,即“CST”。我需要在Date对象中记录时区。我该怎么做?

  • 问题内容: 我在mysql中使用NOW()获取当前日期和时间。我想将日期值转换为varchar并将其与另一个字符串连接。我该怎么做? 问题答案: 使用DATE_FORMAT()