【问题标题】:Java: SimpleDateFormat not formatting in desired TimeZoneJava:SimpleDateFormat 未格式化为所需的时区
【发布时间】:2016-05-20 04:16:16
【问题描述】:

我想为特定时区 GMT 格式化日期,并且无论应用程序在哪个时区运行,我都希望格式化的结果始终相同。

例如,在 GMT 时区创建 Calendar 实例并填充其字段:

TimeZone gmtTimeZone = TimeZone.getTimeZone( "GMT" );

Calendar calendar = Calendar.getInstance();
calendar.setTimeZone( gmtTimeZone );
calendar.set( Calendar.YEAR, 1982 );
calendar.set( Calendar.MONTH, Calendar.JANUARY );
calendar.set( Calendar.DAY_OF_MONTH, 23 );
calendar.set( Calendar.HOUR, 1 );
calendar.set( Calendar.MINUTE, 2 );
calendar.set( Calendar.SECOND, 3 );
calendar.set( Calendar.MILLISECOND, 4 );

从日历中检索 UTC 时间戳:

Date utcDate = calendar.getTime();

据我了解,utcDate 现在是January 1, 1970, 00:00:00.000 GMTJanuary 23, 1982, 01:02:03.004 GMT 之间的毫秒数。 见DateJavadocs:

    /**
     * Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
     * represented by this <tt>Date</tt> object.
     *
     * @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
     *          represented by this date.
     */

创建日期格式化程序并将其时区也设置为 GMT:

SimpleDateFormat dateTimeFormat = new SimpleDateFormat( "yyyy-MM-dd' 'HH:mm:ss.SSSZ" );
dateTimeFormat.setTimeZone( gmtTimeZone );

将日期对象格式化为字符串:

String stringDate = dateTimeFormat.format( utcDate );

现在,当我这样做时:

System.out.println( utcDate.getTime() );
System.out.println( stringDate );

我明白了:

> 380638923004
> 1982-01-23 13:02:03.004+0000

但是,我的预期是(注意 13 hours vs 01 hours):

> 1982-01-23 01:02:03.004+0000

也就是说,因为我使用calendar.set( Calendar.HOUR, 1 ); 将时间设置为 1(凌晨 1 点),所以我预计时间是 1(凌晨 1 点)而不是 13(下午 1 点)。

我哪里错了?

【问题讨论】:

  • 所以你想要下午 1 点而不是 1300 小时?
  • 因此,根据JavaDocs“H - 一天中的小时 (0-23)”。也许您应该检查文档以找到更符合您要求的说明符
  • 不,我预计凌晨 1 点,因为calendar.set( Calendar.HOUR, 1 );
  • 现在我明白你的意思了,谢谢@MadProgrammer

标签: java date datetime formatting timezone


【解决方案1】:
SimpleDateFormat dateTimeFormat = new SimpleDateFormat( "yyyy-MM-dd' 'hh:mm:ss.SSSZ" );
dateTimeFormat.setTimeZone( gmtTimeZone );

这将为您提供预期的输出。

根据 SimpleDateFormat 的 JavaDoc,日期格式字符串中的“h”将显示为上午/下午 (1-12) 中的小时。

编辑: Calendar.HOUR_OF_DAY 应该用于设置 24 小时制的小时。

【讨论】:

  • Spencer,你介意修改你的答案,去掉关于h vs H 和 AM vs PM 的点吗?我想要一个 24 小时制的时钟,从未另行指定。正如您在第一次编辑中指出的那样,实际问题是 Calendar.HOURCalendar.HOUR_OF_DAY。谢谢
【解决方案2】:

正如Spencer Brettcorrect Answer 所说,如果您的意思是12-hour clock 而不是24 小时,您应该使用小写的hh

java.time

在使用 Java 8 及更高版本时,您应该避免使用这些众所周知的麻烦的旧日期时间类(java.util.Date/.Calendar 等)。它们已被java.time 框架取代。见Tutorial

Instant

Instant 是 UTC 时间线上的一个时刻。

Instant instant = Instant.now();

OffsetDateTime

但是Instant 的格式有限,实例化的方式也有限。因此,让我们看一下OffsetDateTime,您可以将其视为Instantoffset-from-UTCZoneOffset)的组合。要指定我们的偏移量,我们可以使用常量java.time.ZoneOffset.UTC。要实例化一个OffsetDateTime,我们首先构建一个date-only 和一个time-only,然后结合一个偏移量。

纳秒级分辨率

java.time 类具有nanosecond 分辨率。您在问题的示例中打算使用毫秒。所以我们将使用TimeUnit 进行转换。你可以自己做乘法,但这很容易出错。而TimeUnit 是自我记录的。

LocalDate localDate = LocalDate.of( 1982 , 1 , 23 );
long nanoseconds = TimeUnit.NANOSECONDS.convert( 4 , TimeUnit.MILLISECONDS );
LocalTime localTime = LocalTime.of( 1 , 2 , 3 , nanoseconds );
OffsetDateTime odt = OffsetDateTime.of( localDate , localTime , ZoneOffset.UTC );

ISO 8601

java.time 类在其toString 方法中默认使用ISO 8601 标准格式。因此,您无需指定格式,因为它接近您所需的输出。与标准格式不太一样,在中间包含 T 而不是 SPACE。

String output = odt.toString();

1982-01-23T01:02:03.004Z

要么将T 替换为空格,要么在堆栈溢出中搜索许多在java.time 中使用DateTimeFormatter 类的示例。

String output = odt.toString().replace( "T" , " " );

【讨论】:

  • 谢谢罗勒。仅供参考,我的错误不是hhHH——HH 是 24 小时制,这就是我想要的。我的错误是使用Calendar.HOUR 而不是Calendar.HOUR_OF_DAY。感谢您提供有关 Java 8 的提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多