在Java中获取HTTP格式的日期

56

我正在尝试在Java中获取一个日期字符串,其格式符合HTTP 1.1规定的格式。就我所知,该格式为:

Fri, 31 Dec 1999 23:59:59 GMT

其中时间始终为GMT。

从Date/Calendar/?中获取这个字符串的最简单方法是什么?


1
@BasilBourque 如果考虑月份的前9天,它就不符合要求。RFC 1123支持1-2位数字,但HTTP 1.1要求日期为两位数字。 - jontejj
1
@jontejj 感谢您的澄清。我删除了我的评论,因为它们没有帮助。 - Basil Bourque
8个回答

78

java.time

编辑:

DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH).withZone(ZoneId.of("GMT"))

这是使用纯 java.time 的方法。HTTP 1.1 不完全符合RFC 1123,因此使用 java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME 格式化器将无法处理小于10日的日期。(感谢@PavanKamar和@ankon指出这一点)

注意:为了向后兼容,还需要支持RFC 2616指定的另外两种格式。


2
很高兴在Java 8中看到这个,但是对我来说并不完全适用,我不得不使用ZonedDateTime.now()而不是Instant.now()。 - Jonas Geiregat
6
谢谢您的回答。我使用了ZonedDateTime.now(ZoneId.of("GMT"))来替换了这个例子。 - nikdeapen
5
此代码仅适用于10日及之后的日期。对于单个数字日期,RFC 5322要求日期为两位数,但此行代码生成单个数字日期字段。参考链接:http://tools.ietf.org/html/rfc5322#section-3.3 - Pavan Kumar
5
@PavanKumar: 很好的观点,但我认为参考资料是错误的 :)RFC 5322,第3.3节中说day=([FWS]1*2DIGIT FWS)/obs-day1*2DIGIT意味着“至少一个,最多两个数字”(https://tools.ietf.org/html/rfc5234#section-3.6,由RFC 5322引用)。然而,针对HTTP/1.1的目的:RFC 2616,第3.3节引用了RFC 1123的“固定长度”格式(使用相同的定义1*2DIGIT),将day定义为2DIGIT。这很混乱 :) - ankon
4
你可以把ZoneId.of("GMT")替换成预定义的常量ZoneOffset.UTC - Basil Bourque
显示剩余12条评论

52

如果有其他人试图在这里寻找答案(就像我一样),以下方法可以解决问题:

String getServerTime() {
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat dateFormat = new SimpleDateFormat(
        "EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
    dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
    return dateFormat.format(calendar.getTime());
}

为了将服务器设置为使用英语并在 GMT 时区中给出时间。


3
那段代码是人们讨厌Java的典型例子。我选择了@jontejj的答案。 - nikdeapen
2
需要注意的是,SimpleDateFormat 的使用是非线程安全的,如果 dateFormat 是共享的,则必须保证其非共享或正确同步。更好的选择是使用 Java 8 或 Joda Time 类。 - Konstantin Pelepelin
这是2011年的好答案。现在有点过时了... :-) 当然同意现在往下看答案列表。 - Ole V.V.
1
这个答案现在已经过时了,使用的是老旧的日期时间类,这些类已经被内置于Java 8及更高版本中的java.time类所取代。 - Basil Bourque

27
如果你正在使用Joda-Time(我强烈推荐任何在Java中处理日期和时间的操作都使用它),你可以这样做:
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

...

private static final DateTimeFormatter RFC1123_DATE_TIME_FORMATTER = 
    DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'")
    .withZoneUTC().withLocale(Locale.US);

...

RFC1123_DATE_TIME_FORMATTER.print(new DateTime())

1
这个很好用。但是为了确保英语环境,我必须添加:private static final DateTimeFormatter RFC1123_DATE_TIME_FORMATTER = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC().withLocale(Locale.US); - Joel Sjöstrand
3
Joda-Time 项目现在处于维护模式。团队建议迁移到内置于 Java 8 及更高版本中的 java.time 类。 - Basil Bourque
我正在寻找一种将字符串“UTC”打印到java.time.DateTimeFormatter的方法,发现您的答案很有用! - Alin Gabriel Arhip

17

两位数日期

一些应用程序要求日期格式包括两位数的日期,根据RFC7231规范。Java 8中的DateTimeFormatter.RFC_1123_DATE_TIME使用单个数字:

System.out.println(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));

输出:Wed, 1 Aug 2018 14:56:46 GMT

有些应用程序不喜欢这样。在使用 Joda-time 或 pre-java8 的 SimpleDateFormat 的旧答案之前,请使用下面的可用的 Java-8 DateTimeFormatter

DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O")

现在,当您执行此操作时:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O");
System.out.println(formatter.format(ZonedDateTime.now(ZoneOffset.UTC)));

您将获得 Wed, 01 Aug 2018 14:56:46 GMT - 请注意日期中的日字段前导零。


2
你还需要记得在格式化程序中设置英语区域设置。 - Jacek Obarymski
@JacekObarymski 我该怎么做?withLocale(Locale.US)给出了相同的结果。 - pjanssen
@pjanssen DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O", Locale.ENGLISH). 如果您的默认语言环境是英语,那么您可能看不出区别,但在我看来,这仍然是一个很好的实践。 - Jacek Obarymski
@JacekObarymski 啊,我以为你是指在“RFC_1123_DATE_TIME”上设置区域设置会添加前导零。 - pjanssen
2
不,我的意思是,如果您使用模式“EEE,dd MMM yyyy HH:mm:ss O”,并且没有设置英语区域设置,则缩写月份名称“MMM”将以您的区域设置语言显示,这显然不再符合RFC_1123标准。 - Jacek Obarymski

11

如果你和我一样正在尝试格式化Java 8的java.time.Instant,则需要在格式化程序中显式添加时区。像这样:

如果您和我一样正在尝试格式化Java 8的java.time.Instant,则需要在格式化程序中显式添加时区。就像这样:

Instant instant = Instant.now();
String formatted = DateTimeFormatter.RFC_1123_DATE_TIME
        .withZone(ZoneOffset.UTC)
        .format(instant);
System.out.println(formatted);

输出结果为:

Tue, 15 Mar 2016 14:45:34 GMT


好的答案。替代语法:OffsetDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.RFC_1123_DATE_TIME ) - Basil Bourque
当需要GMT偏移量进行格式化时,我肯定喜欢将偏移量作为格式化程序的属性。立即点赞。 - Ole V.V.

8
如果您不介意增加依赖项,您可以使用 Apache DateUtils:
import org.apache.http.impl.cookie.DateUtils;
DateUtils.formatDate(new Date(System.currentTimeMillis()));
// Tue, 17 Apr 2012 18:59:02 GMT

这将根据RFC 822 RFC1123格式化您的日期。

1
RFC 822 实际上指定了一个二位数字的年份,然而,RFC 1123 取代了 RFC 822,并将其更改为四位数字的年份。 - MrWhite
看起来 org.apache.http.impl.cookie.DateUtils 已经被弃用了。现在 formatDate 方法已经移到了 org.apache.http.client.utils.DateUtils 中。 - rcgeorge23

3

我知道这是一个晚回复,但是为了完整性,我想补充一下。

您没有提到需要字符串的用途。但如果您需要将其用于HTTP响应头,则可以使用HttpServletResponse.setDateHeader()。它会自动进行格式化。


HttpServletResponse.setDateHeader()链接。它接受自纪元以来的毫秒数。例如,您可以从Instant.toEpochMilli()或(老式)Date.getTime()中获取这些数字。 - Ole V.V.

2

1
那么它必须是GMT时间的要求呢? - Fergusmac
我认为上面的例子返回的日期是系统时区,而不是GMT时间(当然,如果您将系统时区设置为GMT,则它可以工作,但并非所有人都愿意这样做),那么有人知道完整的答案吗? - Hannes R.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接