如何更改 Java 中 String.format() 中的 %tZ 使用的时区?

How do I change the timezone used by %tZ in String.format() in Java?

在下面的代码片段中,我想更改 String.format()

使用的时区
long mls = System.currentTimeMillis();
String time = String.format("%tT %tZ", mls, mls);
System.out.println("TIME: " + time + " \n");

如果你看一下 javadoc, you'll see that the Z pattern uses your JVM's default timezone:

'Z'    A string representing the abbreviation for the time zone. This value will be adjusted as necessary for Daylight Saving Time. For long, Long, and Date the time zone used is the default time zone for this instance of the Java virtual machine. The Formatter's locale will supersede the locale of the argument (if any).

因此,最简单的方法是使用 java.util.TimeZone class 更改默认时区。一些随机时区的例子:

TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
long mls = System.currentTimeMillis();
String time = String.format("%tT %tZ", mls, mls);
System.out.println("TIME: " + time + " \n");

输出:

TIME: 14:03:54 BST


新建JavaDate/TimeAPI

我认为这不是格式化日期的最佳方式,特别是因为它取决于系统的默认时区(可以在运行时更改并破坏您的代码),并且它使用旧的 API (TimeZone class).

现在 Java 有一个新的 API 让事情变得更容易。

如果您正在使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.

如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).

下面的代码适用于两者。 唯一的区别是包名称(在 Java 8 中是 java.time,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但是 classes和方法names是一样的。

如你所愿(hour/minute/second),你可以使用LocalTimeclass和DateTimeFormatter自定义输出格式。我还使用了 ZoneId class 来指定时区:

// get current time in London
LocalTime now = LocalTime.now(ZoneId.of("Europe/London"));
// formatter for HH:mm:ss output
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println(fmt.format(now)); // 14:09:22

输出为:

14:09:22

请注意,我使用 Europe/London 作为时区。更喜欢使用这些长名称,因为短名称(如 CSTIST)是 ambiguous and not standard.

您可以使用 ZoneId.getAvailableZoneIds() 获得所有可用时区的列表。


如果你想输出时区短名称(就像模式 %tZ 那样),你必须使用 ZonedDateTime class,因为 LocalTime 上没有时区信息:

// get current date/time in London
ZonedDateTime z = ZonedDateTime.now(ZoneId.of("Europe/London"));
// formatter for "HH:mm:ss zone" output
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss z");
System.out.println(fmt.format(z)); // 14:09:22 BST

在这种情况下,输出将是:

14:09:22 BST

您可以查看 DateTimeFormatter javadoc 以查看所有可能的格式。

在 Java 中有很多方法可以用时区格式化时间,String.format() 是其中最不灵活的。

下面的所有 6 条建议都假定您有一个 long 毫秒值 (mls),并且需要从中格式化一个时间。所有 6 个建议的输出都是相同的。

如果您坚持使用String.format(),您可以通过多种方式控制时区:

  • 由于 longLongjava.util.Date 参数将使用 JVM 的默认时区,您可以使用 TimeZone.setDefault(TimeZone zone),但这通常不是一个好主意,所以我 强烈反对 你不要这样做。

  • 因为 Calendar 参数有一个时区,你可以改变你的 long 毫秒参数来使用:

    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Pacific/Honolulu"));
    cal.setTimeInMillis(mls);
    String time = String.format("%tT %tZ", cal, cal);
    
  • 在Java8中,还可以使用新的java.time对象:

    ZonedDateTime zdt = Instant.ofEpochMilli(mls).atZone(ZoneId.of("Pacific/Honolulu"));
    String time = String.format("%tT %tZ", zdt, zdt);
    

如果你不必使用 String.format(),你应该使用新的 java.time objects in Java 8. If on Java 6 or 7, you can add the ThreeTen Backport jar:

  • 使用ZonedDateTime指定时区:

    ZonedDateTime zdt = Instant.ofEpochMilli(mls).atZone(ZoneId.of("Pacific/Honolulu"));
    String time = zdt.format(DateTimeFormatter.ofPattern("HH:mm:ss z"));
    
  • DateTimeFormatter中指定时区:

    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss z")
                                             .withZone(ZoneId.of("Pacific/Honolulu"));
    String time = fmt.format(Instant.ofEpochMilli(mls));
    

如果您使用的是 Java 8 之前的版本,并且不想为 ThreeTen Backport 添加额外的 jar,您可以这样做:

  • 使用SimpleDateFormat:

    SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss z");
    fmt.setTimeZone(TimeZone.getTimeZone("Pacific/Honolulu"));
    String time = fmt.format(mls);
    

注意:以上所有代码都使用火奴鲁鲁时间,因为那是我想要所在的时区。

Formatter、class、String.format() 和其他人使用的格式字符串接受大量的日期和时间类型。例如:

    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Lisbon"));
    String time = String.format("%tT %<tZ", zdt);
    System.out.println("TIME: " + time + " \n");

输出:

TIME: 18:33:05 WEST 

我认为西欧夏令时是 WEST。

并不是说您真的想要输出四个字母的时区缩写,现在我们开始吧。这些缩写往往含糊不清并且没有标准化。对于时区的大写 Z 我更喜欢小写 z 作为与 UTC 的偏移量。他们从不模棱两可。不幸的是,像 Europe/Lisbon 这样的长时区名称似乎不是这里的一个选项。为此,您可以使用 DateTimeFormatter.

来自文档:“Date/Time - 可应用于能够对日期或时间进行编码的 Java 类型:longLongCalendarDateTemporalAccessor”。最后一个是大多数现代日期和时间 classes 实现的接口,包括 DayOfWeekHijrahDateHijrahEraInstantIsoEra , JapaneseDate, JapaneseEra, LocalDate, LocalDateTime, LocalTime, MinguoDate, MinguoEra, Month, MonthDayOffsetDateTimeOffsetTimeThaiBuddhistDateThaiBuddhistEraYearYearMonthZonedDateTimeZoneOffset.

我在格式字符串中使用 < 以再次使用相同的参数,因此我不必将 zdt 两次传递给 format()