21

I need to parse an RFC 2822 string representation of a date in Java. An example string is here:

Sat, 13 Mar 2010 11:29:05 -0800

It looks pretty nasty so I wanted to make sure I was doing everything right and would run into weird problems later with the date being interpreted wrong either through AM-PM/Military time problems, UTC time problems, problems I don't anticipate, etc...

Thanks!

skaffman
  • 398,947
  • 96
  • 818
  • 769
Chris Dutrow
  • 48,402
  • 65
  • 188
  • 258
  • 3
    Have you tried using `java.text.SimpleDateFormat`? – skaffman Mar 16 '10 at 22:10
  • That looks like the right one to use, I was worried about what the correct format string was as well, it looked like something that would be easy to mess up. This appears to be the right string for RFC 2822 though: "EEE, d MMM yyyy HH:mm:ss Z" (as per the answer below) – Chris Dutrow Mar 16 '10 at 22:38
  • Thats wild, I took that off the official SimpleDateFormat page. Its technically correct for their example, but still wrong: http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html Thanks for giving me a heads up, that is what I had in my code. – Chris Dutrow Mar 16 '10 at 23:02
  • Source of the standard: [Internet Message Format](https://www.ietf.org/rfc/rfc2822.txt), scroll down to section *3.3. Date and Time Specification*. – Ole V.V. Dec 13 '18 at 12:57

7 Answers7

27

This is quick code that does what you ask (using SimpleDateFormat)

String rfcDate = "Sat, 13 Mar 2010 11:29:05 -0800";
String pattern = "EEE, dd MMM yyyy HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern);
Date javaDate = format.parse(rfcDate);

//Done.

PS. I've not dealt with exceptions and concurrency here (as SimpleDateFormat is not synchronized when parsing date).

Mike Grace
  • 16,636
  • 8
  • 59
  • 79
Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • use this link http://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html for latest acceptable values – rajadilipkolli Sep 06 '16 at 09:29
  • 1
    FYI, this Answer is now outdated. The `SimpleDateFormat` and `Date` classes were supplanted years ago by the modern *java.time* classes defined in JSR 310. See [the Answer by pathfinder78](https://stackoverflow.com/a/53758647/642706). – Basil Bourque Jul 31 '20 at 19:35
20

If your application is using another language than English, you may want to force the locale for the date parsing/formatting by using an alternate SimpleDateFormat constructor:

String pattern = "EEE, dd MMM yyyy HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.ENGLISH);
Laurent VB
  • 1,187
  • 9
  • 18
  • 4
    Setting the Locale is definitely required. The android javadoc for Locale suggests using Locale.US for computer-to-computer communication. [link](https://developer.android.com/reference/java/util/Locale.html) – jason gilbert Sep 18 '12 at 18:02
  • +1 Locale.ROOT would also be a good choice, but isn't available on all systems. – Jules Mar 05 '13 at 08:09
  • @jasongilbert Can you explain why you say that the Locale "is definitely required"? According to the docs, it's not: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html -- but maybe you meant "required if..." and it's the continuation of the "if" that I'm interested in... – Adam Tuttle Sep 12 '13 at 15:02
  • 2
    @AdamTuttle It's required if you want your dates to parse properly when the default locale is not US English. For example, if a user is in Germany the dates would not parse properly with their default Locale. – jason gilbert Oct 19 '13 at 17:10
6

Please keep in mind that the [day-of-week ","] is optional in RFC-2822, hence the suggested examples are not covering all RFC-2822 date formats. Additional, the RFC-822 date type allowed many different time zone notations(obs-zone), which are not covered by the "Z" format specifier.

I guess there is no easy way out, other than looking for "," and "-|+" to determine which pattern to use.

Casper
  • 61
  • 1
  • 2
  • 2
    +1 for pointing out that the day of week is optional. I'm setting up a waterfall of try/catches with various patterns to just keep trying variations until it finds one that works. – Adam Tuttle Sep 12 '13 at 15:05
  • 1
    not only the `day-of-week` is optional, also the seconds in time-of-day are declared as optional: `time-of-day = hour ":" minute [ ":" second ]` – Roman Vottner Aug 20 '15 at 15:37
4

DateTimeFormatter.RFC_1123_DATE_TIME

Since Java 8 new datetime classes were implemented: java.time.ZonedDateTime and java.time.LocalDateTime. ZonedDateTime supports the parsing of RFC strings nearly out of the box:

String rfcDate = "Tue, 4 Dec 2018 17:37:31 +0100 (CET)";  
if (rfcDate.matches(".*[ ]\\(\\w\\w\\w\\)$")) {
    //Brackets with time zone are added sometimes, for example by JavaMail
    //This must be removed before parsing
    //from: "Tue, 4 Dec 2018 17:37:31 +0100 (CET)"
    //  to: "Tue, 4 Dec 2018 17:37:31 +0100"
    rfcDate = rfcDate.substring(0, rfcDate.length() - 6);
}

//and now parsing... 
DateTimeFormatter dateFormat = DateTimeFormatter.RFC_1123_DATE_TIME;
try {
    ZonedDateTime zoned = ZonedDateTime.parse(rfcDate, dateFormat);
    LocalDateTime local = zoned.toLocalDateTime();        
} catch (DateTimeParseException e) { ... }
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    Good answer for the parsing. But I no not recommending ever converting a `ZonedDateTime` to `LocalDateTime`. In doing so you are discarding valuable information, the time zone, with *nothing gained* in return. Like an amount of money without the context of a particular currency, a `LocalDateTime` is ambiguous, and therefore cannot represent a moment. – Basil Bourque Jul 31 '20 at 19:36
3

There is a javax.mail class that perform the parsing of RFC-2822 dates :

javax.mail.internet.MailDateFormat

including the optional and obsolete formats.

Just do :

new javax.mail.internet.MailDateFormat().parse("Sat, 13 Mar 2010 11:29:00 -0800")
new javax.mail.internet.MailDateFormat().parse("13 Mar 2010 11:29:00 -0800")
new javax.mail.internet.MailDateFormat().parse("13 Mar 2010 11:29 -0800")

It will correctly parse these valid RFC-2822 dates

As for other old DateFormatters, the MailDateFormat class is not thread safe.

Thierry
  • 5,270
  • 33
  • 39
1

Try this:

String dateTime = OffsetDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME); 
//RFC_1123 == RFC_2822
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
Serhii D
  • 56
  • 3
  • 1
    While correct this is answering the opposite question. This question was about parsing from a string to an `OffsetDateTme` (or other date-time object). Thanks for your contribution. – Ole V.V. Dec 19 '22 at 18:43
  • Happy to help. This is the only correct option in my case that the email verification system accepted. I tried other options. I settled on this one. – Serhii D Dec 20 '22 at 19:24
1

RFC 2822 date-time string contains timezone offset e.g. the given string Sat, 13 Mar 2010 11:29:05 -0800 has a timezone offset of -08:00 hours from UTC i.e. the equivalent date-time at UTC can be obtained by adding 8 hours to Sat, 13 Mar 2010 11:29:05.

java.time

The modern date-time API API has OffsetDateTime to represent a date-time with timezone offset.

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        String strRFC2822DateTimeStr = "Sat, 13 Mar 2010 11:29:05 -0800";

        OffsetDateTime odt = OffsetDateTime.parse(strRFC2822DateTimeStr, DateTimeFormatter.RFC_1123_DATE_TIME);
        System.out.println(odt);

        // Alternatively: using a custom DateTimeFormatter
        DateTimeFormatter parser = DateTimeFormatter.ofPattern("EEE, dd MMM uuuu HH:mm:ss XX", Locale.ENGLISH);
        System.out.println(OffsetDateTime.parse(strRFC2822DateTimeStr, parser));

        // In case you need the equivalent date-time at UTC
        OffsetDateTime odtUtc = odt.withOffsetSameInstant(ZoneOffset.UTC);
        System.out.println(odtUtc);
    }
}

Output:

2010-03-13T11:29:05-08:00
2010-03-13T11:29:05-08:00
2010-03-13T19:29:05Z

Learn more about the modern Date-Time API from Trail: Date Time.

Some useful links:

  1. Never use SimpleDateFormat or DateTimeFormatter without a Locale.
  2. You can use y instead of u but I prefer u to y.
  3. How to use OffsetDateTime with JDBC.
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110