56

I'm trying to get a String of a date in Java in the format specified in HTTP 1.1. Which, as far as I can tell, is:

Fri, 31 Dec 1999 23:59:59 GMT

With the time always being GMT.

What would be the easiest way to get this from Date/Calendar/?

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Fergusmac
  • 3,679
  • 4
  • 20
  • 24
  • 1
    @BasilBourque it does not comply if you consider the first 9 days of the month. Rfc 1123 supports 1-2 digits, but http 1.1 requires it to be two digits for the day-of-month. – jontejj Aug 05 '19 at 11:19
  • 1
    @jontejj Thanks For the clarification. I deleted my comments as they were not helpful. – Basil Bourque Aug 05 '19 at 14:19

8 Answers8

78

java.time

EDIT:

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

is the way to do it with pure java.time. HTTP 1.1 is to not a 100% match with RFC 1123, so using the java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME formatter will fail for day-of-month less than 10. (thanks to @PavanKamar and @ankon for pointing that out)

Note: to be backwards compliant, you would need to also support the other two formats specified by RFC 2616

Community
  • 1
  • 1
jontejj
  • 2,800
  • 1
  • 25
  • 27
  • 2
    Lovely to see this in Java 8 but, this didn't quite work for me I had to use ZonedDateTime.now() instead of Instant.now() – Jonas Geiregat Dec 19 '14 at 21:16
  • 6
    I used this except with ZonedDateTime.now(ZoneId.of("GMT")). Thanks for the answer. – nikdeapen Jan 11 '15 at 02:26
  • 5
    This works only for dates on or after 10th. For single digit dates, RFC 5322 expects the date to be of two digits, but this line of code generates a single digit date field. http://tools.ietf.org/html/rfc5322#section-3.3 – Pavan Kumar Apr 21 '15 at 12:56
  • 5
    @PavanKumar: Good point, but I think the reference is wrong :) RFC 5322, section 3.3 says `day = ([FWS] 1*2DIGIT FWS) / obs-day`. `1*2DIGIT` means "at least one, at most two of DIGIT" (https://tools.ietf.org/html/rfc5234#section-3.6 as referenced by RFC 5322). For the purpose of HTTP/1.1 though: RFC 2616, section 3.3., references a "fixed-length" format of RFC 1123 (which uses the same definition `1*2DIGIT`), defining day as `2DIGIT`. It's a mess :) – ankon Jul 06 '15 at 18:33
  • 1
    Java 8 Time should be thread safe, unless otherwise specified. – jontejj Jan 13 '16 at 08:35
  • Yes, these older date-time specs are a mess. Modern protocols adopt the sensible [ISO 8601](https://en.m.wikipedia.org/wiki/ISO_8601) standard. Both the java.time and Joda-Time frameworks use ISO 8601 formats by default when parsing/generating textual representations of date-time values. – Basil Bourque Mar 15 '16 at 16:12
  • 4
    @jontejj You could replace that `ZoneId.of("GMT")` with the predefined constant `ZoneOffset.UTC`. – Basil Bourque Sep 01 '16 at 22:25
  • 1
    Can also be used in Java 6 & 7 and Android. Much of the java.time functionality is back-ported to Java 6 & 7 in [ThreeTen-Backport](http://www.threeten.org/threetenbp/) and further adapted to [Android](https://en.wikipedia.org/wiki/Android_(operating_system)) in [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) (see [*How to use…*](http://stackoverflow.com/q/38922754/642706)). – Basil Bourque Sep 01 '16 at 22:26
  • Please don't vote this up, as @PavanKumar pointed out, it only works for dates on of after the 10th. It took me a day to figure out why my HTTP request fails with a date header generated with this line of code. – apcuk Feb 08 '19 at 15:52
  • @apcuk date formats referenced from older http rfcs should still be supported/accepted. So I would assume that the parser you have doesn't? "HTTP/1.1 clients and servers that parse the date value MUST accept all three formats (for compatibility with HTTP/1.0), though they MUST only generate the RFC 1123 format for representing HTTP-date values in header fields." - from the http rfc. – jontejj Feb 09 '19 at 20:41
  • @jontejj I was trying to connect to Azure, and it said that it uses RFC1123 as date format. I was happy to find it as a built-in pattern in Java 8. But it only accepted my header with a fixed-length day-of-month, and it was extremely hard to figure out what was wrong... It's not completely clear to me, but it seems that the HTTP standard is a "subset" of RFC1123, so I think your answer is fine if you need RFC1123, but not for all HTTP requests. – apcuk Feb 11 '19 at 15:15
  • Not according to the quote I found? I think that either java 8 or azure has a bug? I need to dig deeper into the format to be sure, will get back to you on that. – jontejj Feb 13 '19 at 06:05
  • Gili thanks for reminding me :) @apcuk it seems like you are correct... [https://stackoverflow.com/a/51636514/541755](@malt's answer) is the better answer. HTTP 1/1 indeed uses a modified rfc1123 format. So the sad news is that there is no built in formatter in java 8 for parsing/formatting http dates... – jontejj Aug 05 '19 at 11:15
  • 1
    *Beware of locale* This format, while correct, is locale-dependent. You must use an English locale for generating compliant HTTP dates. – Étienne Miret Mar 23 '20 at 21:27
  • @EtienneMiret I edited the answer to account for that. – jontejj Mar 24 '20 at 22:17
  • @BasilBourque if you use ZoneOffset.UTC it prints Z instead of GMT – jontejj Mar 24 '20 at 22:18
  • 1
    @jontejj Yes, indeed, you are right about that `Z`appearing instead `GMT`. Surprised me. See [demo in IdeOne.com](https://ideone.com/B9MZEM). FYI, semantically, they mean the same, `Z` & `GMT` being an offset of zero hours-minutes-seconds. – Basil Bourque Mar 24 '20 at 23:51
52

In case someone else will try to find the answer here (like I did) here's what will do the trick:

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

in order to set the server to speak English and give time in GMT timezone.

Hannes R.
  • 1,379
  • 16
  • 23
  • 3
    That code is a prime example of why people hate Java. I went with the answer by @jontejj. – nikdeapen Jan 11 '15 at 02:23
  • 2
    It should be noted that use of `SimpleDateFormat` is not thread-safe and `dateFormat` must be non-shared or properly synchronized if shared. Better go with Java 8 or Joda Time classes. – Konstantin Pelepelin Mar 31 '17 at 12:46
  • This was a good answer in 2011. It’s just wearing… :-) Certainly agree to look further down the answer list now. – Ole V.V. Apr 07 '17 at 10:41
  • 1
    This Answer is now outdated, using troublesome old date-time classes that were supplanted by the *java.time* classes built into Java 8 and later. – Basil Bourque Dec 28 '17 at 09:09
27

If you're using Joda-Time (which I would highly recommend for any handling of dates and times in Java), you can do:

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())
Mark Slater
  • 831
  • 7
  • 18
  • 1
    This worked great. However, to ensure an English locale I had to add: `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 Oct 02 '14 at 13:10
  • 3
    The Joda-Time project is now in maintenance mode. The team advises migration to the *java.time* classes built into Java 8 and later. – Basil Bourque Dec 28 '17 at 09:13
  • I was looking for a way to print the `String` "UTC" to a `java.time.DateTimeFormatter` and found your answer useful! – Alin Gabriel Arhip Sep 08 '22 at 07:29
17

Two-digit day-of-month

Some applications require the format to include a two digit day-of-month as per RFC7231. The Java 8 DateTimeFormatter.RFC_1123_DATE_TIME uses a single digit:

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

Output: Wed, 1 Aug 2018 14:56:46 GMT

Some applications don't like that. Before you use the old answers that use Joda-time or a pre-java8 SimpleDateFormat, here's a working Java-8 DateTimeFormatter:

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

Now, when you do this:

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

You get Wed, 01 Aug 2018 14:56:46 GMT - note the leading zero in the day-of-month field.

Community
  • 1
  • 1
Malt
  • 28,965
  • 9
  • 65
  • 105
  • 2
    You need to remember also to set the English locale in the formatter – Jacek Obarymski Mar 01 '19 at 05:26
  • @JacekObarymski How would I do that? `withLocale(Locale.US)` gives the same result. – pjanssen Apr 02 '19 at 12:14
  • @pjanssen DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O", Locale.ENGLISH). If your default locale is English then you might not see the difference but it's still a good practice IMHO. – Jacek Obarymski Apr 03 '19 at 13:37
  • @JacekObarymski Ah, I thought you meant setting the locale on `RFC_1123_DATE_TIME` would add the leading zero. – pjanssen Apr 03 '19 at 14:52
  • 2
    No, what I mean is that if you use that pattern "EEE, dd MMM yyyy HH:mm:ss O" and don't have English locale set then the abbreviated month name 'MMM' will be in you locale's language which obviously won't be RFC_1123 compliant anymore – Jacek Obarymski Apr 03 '19 at 15:18
11

If you, like me, are trying to format a Java 8 java.time.Instant you need to explicitly add the time zone to the formatter. Like this:

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

Which prints:

Tue, 15 Mar 2016 14:45:34 GMT

K Erlandsson
  • 13,408
  • 6
  • 51
  • 67
  • Good answer. Alternative syntax: `OffsetDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.RFC_1123_DATE_TIME )` – Basil Bourque Mar 18 '17 at 01:44
  • When GMT offset is a requirement for the formatting, I certainly like the idea of making the offset an attribute of the formatter. Immediately upvoted. – Ole V.V. Apr 07 '17 at 05:56
8

If you are not afraid of additional dependencies, you can use apache DateUtils:

import org.apache.http.impl.cookie.DateUtils;
DateUtils.formatDate(new Date(System.currentTimeMillis()));
// Tue, 17 Apr 2012 18:59:02 GMT

This will format your date with respect to RFC 822 RFC1123.

Community
  • 1
  • 1
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • 1
    RFC 822 actually states a 2 digit year, however, [RFC 1123](http://tools.ietf.org/html/rfc1123) which supersedes RFC 822 changes this to a 4 digit year. – MrWhite Jan 02 '13 at 15:16
  • Looks like `org.apache.http.impl.cookie.DateUtils` is deprecated. The `formatDate` method now lives in `org.apache.http.client.utils.DateUtils`. – rcgeorge23 Oct 25 '17 at 14:56
3

I know this is a late response but just wanted to add it for completeness.

You did not mention what you need the string for. But if you need it for an HTTP response header, you can use HttpServletResponse.setDateHeader(). It does all the formatting for you.

Henry Wilson
  • 141
  • 1
  • 3
  • 11
  • [Link to doc: `HttpServletResponse.setDateHeader()`](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletResponse.html#setDateHeader-java.lang.String-long-). It accpets millis since the epoch. You can for instance get those from `Instant.toEpochMilli()` or (old-fashioned) `Date.getTime()`. – Ole V.V. Apr 07 '17 at 05:43
2
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
System.out.println("Date: " + dateFormat.format(calendar.getTime()));

You can play with it. The documentation is here: http://download.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

cos
  • 219
  • 1
  • 8
  • 1
    What about the requirement that it be in GMT? – Fergusmac Oct 10 '11 at 00:55
  • I also think the above example returns the date in system timezone, not GMT (of course it works if you set system timezone to GMT in the Locale, but not everybody are willing to do that) so - anyone - what would be the complete answer? – Hannes R. Dec 23 '11 at 14:32