【问题标题】:Java SimpleDateFormat interprets 'z' differently on different OSJava SimpleDateFormat 在不同的操作系统上以不同的方式解释“z”
【发布时间】:2018-09-24 22:24:42
【问题描述】:

我遵循了代码(简化为专注于问题)。使用SimpleDateFormat 模式打印时区信息。

你知道为什么 z 在不同机器上的处理方式不同吗?如果有办法告诉 Java 在所有机器上统一处理它?

该类在JavaMail 中使用,这导致我们的电子邮件标题包含不符合RFC 2822 的时间。

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class DateFormatTest {
    String PATTERN = "z";
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.PATTERN);

    public static void main(final String[] args) {
        new DateFormatTest().printTimezone();
    }

    public void printTimezone() {
        System.out.println(this.simpleDateFormat.format(Calendar.getInstance().getTime()));
    }

}

输出:Windows / Mac

PDT

输出:Linux(CentOS Linux 版本 7.5.1804(核心))/Ubuntu 14 / 18

GMT-07:00

【问题讨论】:

  • 您能指定违反了哪个 RFC(和部分)吗?
  • RFC2822 并且有问题的格式字符串是 EEE, d MMM yyyy HH:mm:ss Z (z) java 邮件使用的。在 Linux 上,此格式字符串生成 Sat, 22 Sep 2018 22:20:17 -0700 (GMT-07:00),但在 mac 上为 Sat, 22 Sep 2018 22:17:44 -0700 (PDT)(缺少时区偏移)
  • @Gopinath RFC 2822 - Internet Message Format - IETF Datatracker ???可能是 RFC 822,它是大写的 Z,而不是小写的 z
  • 它的 RFC2822 记录在 Java Mail API javaee.github.io/javaee-spec/javadocs/javax/mail/internet/… 上。无论如何,令人费解的问题是什么会影响格式字符串的小写z 的输出? z 之外的某些环境具有带偏移量的时区,而其他环境仅具有时区。
  • 仅供参考,非常麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 中内置的 java.time 类所取代,之后。见Tutorial by Oracle

标签: java date datetime jakarta-mail simpledateformat


【解决方案1】:

tl;博士

永远不要使用Calendar。请改用 java.time 类。

RFC 1123/RFC 822 格式的字符串:

OffsetDateTime
.now( ZoneOffset.UTC )
.format( DateTimeFormatter.RFC_1123_DATE_TIME )

格林威治标准时间 2018 年 9 月 24 日星期一 23:45:21

获取特定时区的当前偏移量:

ZoneId
.systemDefault()
.getRules()
.getOffset(
    Instant.now() 
)
.toString()

-07:00

避免Calendar

您正在使用多年前被 java.time 取代的糟糕的旧日期时间类。永远不要使用那些遗留类;他们真是一团糟。

您关于 Calendar 行为的特殊问题没有实际意义,因为没有必要再次使用该类。即使与尚未更新到 java.time 的旧代码进行互操作,您也可以通过添加到旧类的新方法轻松地在旧类和现代类之间转换。

ZonedDateTime zdt = myGregorianCalendar.toZonedDateTime() ;

……和……

GregorianCalendar gc = GregorianCalendar.from( zdt ) ; 

java.time

显然您想要当前默认时区的当前偏移量。

获取当前默认时区,ZoneId

ZoneId z = ZoneId.systemDefault() ;  // Or specify ZoneId.of( "Pacific/Auckland" ) or so on.

询问该时区的rules

ZoneRules rules = z.getRules() ;

获取特定时刻在该区域中有效的 UTC 偏移量。我们将使用当前时刻,Instant

Instant now = Instant.now() ;
ZoneOffset offset = rules.getOffset( now ) ;

生成一个表示该偏移量的文本。

String output = "At " + now + " in zone " + z + " the offset is " + offset;

在美国/洛杉矶地区的 2018-09-24T23:38:44.192642Z,偏移量为 -07:00

RFC 1123 / RFC 822

您提到了一个 RFC,但没有具体说明。也许是 RFC 1123 / 822?

formatter for that 内置在 java.time 中。

OffsetDateTime nowInUtc = OffsetDateTime.now( ZoneOffset.UTC ) ;
String output = nowInUtc.format( DateTimeFormatter.RFC_1123_DATE_TIME ) ;

格林威治标准时间 2018 年 9 月 24 日星期一 23:45:21

ISO 8601

仅供参考,RFC 1123 / RFC 822 格式是一种可怕格式。它假定英语。机器难以解析,人类难以阅读。但我知道您可能需要它来处理过时的旧协议。

只知道现代协议使用ISO 8601 标准格式。方便的是,这些格式在解析/生成字符串时默认在 java.time 类中使用。


关于java.time

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

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

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

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

从哪里获取 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 对完整性表示敬意。
  • FWIW,OffsetDateTime.now(ZoneId.of("America/Los_Angeles")).format(DateTimeFormatter.RFC_1123_DATE_TIME) 产生 Mon, 24 Sep 2018 22:16:36 -0700。如果提问者愿意,RFC 不应该也可以吗?
  • @OleV.V.当然,这也有效。 (a) 问题并不清楚实际目标是哪个区域或偏移量。 (b) 我倾向于使用 UTC,除非有明确的理由不这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-16
  • 2014-02-06
  • 2018-10-16
  • 2015-09-14
  • 2021-06-03
  • 1970-01-01
  • 2016-01-16
相关资源
最近更新 更多