0

I try to retrieve mails from a Gmail account using tPOP and then save somewhere the date of the last mail retrieved (so the next time I run the job I won't be retrieving the same mails twice).

The only date that I could retrieve from a gmail mail got this format :

Fri, 13 Apr 2018 02:26:19 -0700 (PDT)

How can I convert this into a Java.util.Date format?

Mit94
  • 718
  • 8
  • 28
  • 1
    Possible duplicate of [Java string to date conversion](https://stackoverflow.com/questions/4216745/java-string-to-date-conversion). And of many more questions, please search. – Ole V.V. Apr 13 '18 at 16:57
  • 1
    Any particular reason why you are asking for `java.util.Date`? That class is considered long outmoded. Instead I recommend you go for [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). It is so much nicer to work with. – Ole V.V. Apr 13 '18 at 17:00
  • FYI, as Ole V.V. commented, the troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Apr 13 '18 at 21:19

4 Answers4

4

You can use SimpleDateFormat for this task:

String dt = "Fri, 13 Apr 2018 02:26:19 -0700 (PDT)";
Date date = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss", Locale.ENGLISH).parse(dt);

Read more:

Aniket Sahrawat
  • 12,410
  • 3
  • 41
  • 67
  • Thanks for the answer but It gives me this error : `java.text.ParseException: Unparseable date: "Fri, 13 Apr 2018 02:26:19 -0700 (PDT)"` – Mit94 Apr 13 '18 at 10:24
  • 2
    @AniketSahrawat : simple date formats are locale sensitive. "Fri" has a meaning in english, not in french or russian or whatever. Therefore, a "french" SimpleDateFormat will fail to parse it. You should probably set the SimpleDateFormat's locale before parsing . Anyway, this is not a complete answer (although it should work most of the time). See : https://stackoverflow.com/a/17404726/2131074 : day of week is optional and timezone specification are not taken into account in this sample, both of which may lead to a parsing failure. – GPI Apr 13 '18 at 10:36
  • @AniketSahrawat I still have the same issue after adding the Locale.ENGLISH. – Mit94 Apr 13 '18 at 14:34
  • Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. – Ole V.V. Apr 13 '18 at 16:57
  • java.time is not available to Android API <26 Sucks, but this answer is still pretty valuable – NewEndian Aug 14 '19 at 17:43
2

tl;dr

String input = "Fri, 13 Apr 2018 02:26:19 -0700 (PDT)" ;
…
java.util.Date.from(                                     // Convert from modern classes `OffsetDateTime` & `Instant` to troublesome legacy class `Date`. Do so only if absolutely necessary.
    OffsetDateTime.parse(
        input.substring( 0 , input.indexOf( " (" ) ) ,   // Drop the ambiguity-prone pseudo-zone ` (PDT)` from the end.
        DateTimeFormatter.RFC_1123_DATE_TIME             // Parse using a built-in formatter defined for RFC 1123 strings.
    )
    .toInstant()                                         // Extract UTC value (`Instant`) from `OffsetDateTime`.
)                                                        // Returns a legacy `java.util.Date` object.

DateTimeFormatter.RFC_1123_DATE_TIME

As discussed in the Answer by GPI, Java provides a formatter for this format defined in RFC 1123 (see also RFC 822).

Except that pseudo-time-zone codes such as PDT cannot be parsed unambiguously as they are not standardized and are not unique. So strip that off.

String original = "Fri, 13 Apr 2018 02:26:19 -0700 (PDT)";
// Delete non-standard pseudo-zone at end.
String input = original;
if ( input.endsWith( ")" ) ) {
    int index = input.indexOf( " (" );
    input = input.substring( 0 , index );
}
DateTimeFormatter f = DateTimeFormatter.RFC_1123_DATE_TIME;
OffsetDateTime odt = OffsetDateTime.parse( input , f );

Dump to console.

System.out.println( original );
System.out.println( input );
System.out.println( odt );

Fri, 13 Apr 2018 02:26:19 -0700 (PDT)

Fri, 13 Apr 2018 02:26:19 -0700

2018-04-13T02:26:19-07:00

Convert

How can I convert this into a Java.util.Date format?

Don’t. The java.util.Date is a poorly-designed, confusing, and troublesome. Avoid it.

But if you must interoperate with old code not yet updated to java.time classes, you can convert back and forth. Call new methods added to the old classes.

Extract a Instant from OffsetDateTime, as that class replaces java.util.Date to represent a moment in UTC.

Instant instant = odt.toInstant() ;
java.util.Date d = java.util.Date.from( instant ) ;

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • While your hack works, two erroraneous statements should be corrected: a) The input string is not defined in RFC 1123 but RFC 5322 which allows comments (the trailing "(PDT)" is indeed such a comment). b) In RFC 1123, the zones PDT etc. are NOT ambigous but well defined and exclusively related to North America, see the original paper in [RFC 822](https://www.ietf.org/rfc/rfc822.txt) - page 25). – Meno Hochschild Apr 16 '18 at 12:31
1

Other answers to date, although they will probably work most of the time, do not take into account the subtelties of the RFC1123 format (see section 5.2.14, that replaces the original RFC-822 definition).

By this normative definition, the day of week field is optional (SimpleDateForamt can not deal with optional fields), and the timezones may be provided both as +/- offsets (capital Z in SimpleDateFormat), or as names (lowercase z in SimpleDateFormat).

You could create a combination of SimpleDateFormats, and see which match, but that is slow. You could use JodaTime and build the optional parts because it has an API for that, but that takes time too.

Or if you have Java8+, you have DateTimeFormatter.RFC_1123_DATE_TIME for free. Which is not a complete implementation of the standard, but probably as close as you will ever get if you code it yourself. And probably safer too.

See also : Getting Date in HTTP format in Java

Community
  • 1
  • 1
GPI
  • 9,088
  • 2
  • 31
  • 38
  • 1
    `RFC_1123_DATE_TIME` won't take into account the time-zone name, I don't think. – achAmháin Apr 13 '18 at 12:20
  • 1
    It says it does. Javadoc : `The format consists of: [...] The {@link ZoneOffset#getId() offset ID} without colons or seconds. An offset of zero uses "GMT. `, although as a matter of text recognition of timezones, only GMT is recognized, not the normative possibilities of various american timezones. In the question's case, the GMT based numerical offset is used, not such a text timezone. – GPI Apr 13 '18 at 13:47
1

Use Message-ID: header instead

the next time I run the job I won't be retrieving the same mails twice

To avoid downloading the same message, use the Message-ID: header rather than the Date:. That is its purpose, to identify each message uniquely. No need to parse anything.

See fields described here.

Example:

Message-ID: <f6a363400703050910y7d591d42raf015fcef16f95ea@mail.gmail.com>
Date: Mon, 5 Mar 2007 09:10:41 -0800
From: UserName <address@gmail.com>
To: OtherUserName <address@system.com>
Subject: Subject Line
MIME-Version: 1.0

Use this string as your unique identifier:

f6a363400703050910y7d591d42raf015fcef16f95ea@mail.gmail.com

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154