I am trying to parse various slightly different date strings for an Android application.
Two examples are:
Thu, 20 Aug 2020 13:30:16 +0000
Wed, 19 Aug 2020 15:28:47 GMT
Here is how I tried parsing these Strings:
Date pubDate = org.apache.commons.lang3.time.DateUtils.parseDate(dateString,
"EEE, dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss zzz");
As far as I can tell this should work, but I am getting java.text.ParseException: Unable to parse the date: Wed, 19 Aug 2020 15:28:47 GMT exceptions for both those examples. What am I doing wrong here?
Your format is built into java.time
Use DateTimeFormatter.RFC_1123_DATE_TIME; it works for both of your strings. It’s really the same format, only with the offset from UTC (or GMT) denoted differently. DateTimeFormatter is part of java.time, the modern Java date and time API. I recommend that you prefer java.time over Date and DateUtils, also because the Date class is poorly designed and long outdated.
String s = "Thu, 20 Aug 2020 13:30:16 +0000";
OffsetDateTime dt = OffsetDateTime.parse(s, DateTimeFormatter.RFC_1123_DATE_TIME);
System.out.println(dt);
Output is:
2020-08-20T13:30:16Z
Let’s try with your other string example:
String s = "Wed, 19 Aug 2020 15:28:47 GMT";
2020-08-19T15:28:47Z
Question: Doesn’t java.time require Android API level 26?
java.time works nicely on both older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On older Android either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Your time zones are in 2 different formats:
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
The first one looks like it follows the format:
EEE, dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss Z
While the second is of the format:
EEE, dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss z
I do not see any problem with your formats.
Demo:
import java.text.ParseException;
import java.util.Date;
public class Main {
public static void main(String[] args) throws ParseException {
String[] dateStrings = { "Wed, 19 Aug 2020 15:28:47 GMT", "Thu, 20 Aug 2020 13:30:16 +0000" };
for (String dateString : dateStrings) {
Date pubDate = org.apache.commons.lang3.time.DateUtils.parseDate(dateString, "EEE, dd MMM yyyy HH:mm:ss Z",
"EEE, dd MMM yyyy HH:mm:ss zzz");
System.out.println(pubDate);
}
}
}
Output:
Wed Aug 19 16:28:47 BST 2020
Thu Aug 20 14:30:16 BST 2020
However, I strongly suggest you stop using the outdated and error-prone java.util date-time API and SimpleDateFormat. Switch to the modern java.time date-time API and the corresponding formatting API (java.time.format). Learn more about the modern date-time API from Trail: Date Time.
Using modern date-time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Test strings
String[] dateStrings = { "Wed, 19 Aug 2020 15:28:47 GMT", "Thu, 20 Aug 2020 13:30:16 +0000" };
// Define formatter
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("[EEE, dd MMM yyyy HH:mm:ss Z][EEE, dd MMM yyyy HH:mm:ss zzz]", Locale.ENGLISH);
for (String dateString : dateStrings) {
ZonedDateTime zdt = ZonedDateTime.parse(dateString, formatter);
System.out.println(zdt);
}
}
}
Output:
2020-08-19T15:28:47Z[GMT]
2020-08-20T13:30:16Z
If your android version is not compliant with Java-8, you can backport using ThreeTen-BackportCheck. Check How to use ThreeTenABP in Android Project
Related
I have strings like Thu Feb 04 21:25:12 CET 2021 and Sat Oct 03 11:57:00 CEST 2020
and I need to convert it to ISO_Instant time but my code doesn't work.
I hope someone can help me.
My Code:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz uuuu");
Instant ist = OffsetDateTime.parse("Thu Feb 04 19:03:27 CET 2021", dtf).toInstant();
Your date-time string has timezone Id instead of timezone offset and therefore you should parse it into ZonedDateTime. Also, Never use SimpleDateFormat or DateTimeFormatter without a Locale.
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz uuuu", Locale.ENGLISH);
Instant instant = ZonedDateTime.parse("Thu Feb 04 19:03:27 CET 2021", dtf).toInstant();
System.out.println(instant);
instant = ZonedDateTime.parse("Sat Oct 03 11:57:00 CEST 2020", dtf).toInstant();
System.out.println(instant);
}
}
Output:
2021-02-04T18:03:27Z
2020-10-03T09:57:00Z
Learn more about java.time, the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I am having this string "Mon Nov 11 10:36:53 GMT+02:00 2019". What is the pattern when using SimpleDateFormat();? Is there some way I can test or generate it without multiple try and error?
java.time and ThreeTenABP
What is the pattern when using SimpleDateFormat();?
My suggestion is that you don’t use SimpleDateFormat. That class is notoriously troublesome and long outdated. On Android — and on your API level too — you can use java.time, the modern Java date and time API.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
String stringWereHaving = "Mon Nov 11 10:36:53 GMT+02:00 2019";
ZonedDateTime dateTime = ZonedDateTime.parse(stringWereHaving, formatter);
System.out.println(dateTime);
Output is:
2019-11-11T10:36:53+02:00[Etc/GMT-2]
The only confusing thing here is that the sign used in the time zone name Etc/GMT-2 has been intentionally reversed compared to normal usage.
Only if you need an old-fashioned Date object for a legacy API not yet upgraded to java.time, convert like this:
Instant i = dateTime.toInstant();
Date oldfashionedDate = DateTimeUtils.toDate(i);
System.out.println(oldfashionedDate);
Mon Nov 11 09:36:53 CET 2019
Output comes from my computer in Europe/Copenhagen time zone, so the hour of day is adjusted by 1 hour compared to your input at offset `02:00. We have got the same point in time as in the string.
Is there some way I can test or generate it without multiple try and
error?
There’s always Java SimpleDateFormat Online Tester. I give you the link at the bottom. I don’t know of a similar service for DateTimeFormatter. Many of the patterns are the same, also the one I am using above, so it’s probably worthwhile trying.
Question: Doesn’t java.time require Android API level 26?
java.time works nicely on both older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Java SimpleDateFormat Online Tester
Try using
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class Test{
public static void main(String[] args) {
//Try This
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd hh:mm:ss 'GMT' Z yyyy");
System.out.println(simpleDateFormat.format(new Date()));
}
}
What do you mean " Is there some way I can test or generate it without multiple try and error?"
BTW, I'm using DateFormat dateFormat = new SimpleDateFormat(<put your desired pattern here via STRING>);
and get the current date:
Date date = new Date();
String result = dateFormat.format(date); // formatting the date and passing it on string.
sample code:
DateFormat dateFormat = new SimpleDateFormat("MMM. dd, yyyy EEE HH:mm:ss");
Date date = new Date();
result = dateFormat.format(date);
Output : Nov. 13, 2019 Wed 17:02:00
Question
Why is my Android app unable to parse String str1= "Tue Jun 20 15:56:29 CEST 2017"?
I found some similar questions, but none of them helped me.
Sidenotes
In my project I have some Java applications which are running on a computer and some Android applications. They are able to communicate to each other.
In the messages are timestamps. However my Java applications are sending timestamps in a format like String str1= "Tue Jun 20 15:56:29 CEST 2017" and my Android apps like String str2 = "Tue Jun 20 13:40:37 GMT+02:00 2017". To save the message including the time I have to parse the incoming time to a date.
My Android-App
In my Android app I can't parse the String str1= "Tue Jun 20 15:56:29 CEST 2017" correctly:
java.text.ParseException: Unparseable date: "Tue Jun 20 15:56:29 CEST 2017"
String str2 = "Tue Jun 20 13:40:37 GMT+02:00 2017"is working fine.
Code:
String str1 = "Tue Jun 20 14:53:08 CEST 2017";
SimpleDateFormat formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str1);
} catch (ParseException e) {
e.printStackTrace();
}
// test
String str2 = "Tue Jun 20 13:40:37 GMT+02:00 2017";
formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str2);
} catch (ParseException e) {
e.printStackTrace();
}
My Java-App
However, my Java application can parse both strings correctly.
Code:
String str = "Tue Jun 20 14:53:08 CEST 2017";
SimpleDateFormat formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
// test
str = "Tue Jun 20 13:40:37 GMT+02:00 2017";
formatter = new SimpleDateFormat("EE MMM dd HH:mm:ss zzzz yyyy", Locale.US);
try {
Date date = formatter.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
ThreeTen Solution
String to LocalDateTime
To convert my incoming string I'm using the following code:
String time = "Mon Jun 26 15:42:51 GMT 2017";
DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
LocalDateTime timestamp = LocalDateTime.parse(time, gmtDateTimeFormatter);
LocalDateTime to String
To convert my LocalDateTime to a string I used this:
LocalDateTime timestamp = LocalDateTime.now();
DateTimeFormatter gmtDateTimeFormatter = DateTimeFormatter.ofPattern("EE MMM dd HH:mm:ss 'GMT' yyyy", Locale.ENGLISH));
String time = gmtDateTimeFormatter.format(timestamp);
Maybe there's a difference on how Android handles the zzzz pattern (probably Java's implementation handles it better than Android, so it "guesses" the correct timezone in a way that Android doesn't). I don't know.
Anyway, may I suggest you to avoid using those old classes? These old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
To parse both formats, you can use a DateTimeFormatter with optional sections. That's because CEST is a timezone short name and GMT+02:00 is an UTC offset, so if you want to parse both with the same formatter, you'll need to use one optional section for each format.
Another detail is that short names like CET or CEST are ambiguous and not standard. The new API uses IANA timezones names (always in the format Continent/City, like America/Sao_Paulo or Europe/Berlin).
So, you need to choose one timezone that suits your needs. In the example below, I've just picked a timezone that's in CEST (Europe/Berlin), but you can change it according to what you need - you can get a list of all names using ZoneId.getAvailableZoneIds().
As the new API doesn't resolve CEST (because of its ambiguity), I need to create a set with the prefered timezone in order to correctly parse the input:
// when parsing, if finds ambiguous CET or CEST, it uses Berlin as prefered timezone
Set<ZoneId> set = new HashSet<>();
set.add(ZoneId.of("Europe/Berlin"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// your pattern (weekday, month, day, hour/minute/second)
.appendPattern("EE MMM dd HH:mm:ss ")
// optional timezone short name (like "CST" or "CEST")
.optionalStart().appendZoneText(TextStyle.SHORT, set).optionalEnd()
// optional GMT offset (like "GMT+02:00")
.optionalStart().appendPattern("OOOO").optionalEnd()
// year
.appendPattern(" yyyy")
// create formatter (using English locale to make sure it parses weekday and month names correctly)
.toFormatter(Locale.US);
To parse Tue Jun 20 14:53:08 CEST 2017, just use the formatter:
ZonedDateTime z1 = ZonedDateTime.parse("Tue Jun 20 14:53:08 CEST 2017", fmt);
System.out.println(z1);
The output is:
2017-06-20T14:53:08+02:00[Europe/Berlin]
Note that CEST was mapped to Europe/Berlin, according to the set we created.
To parse Tue Jun 20 13:40:37 GMT+02:00 2017, we can use the same formatter. But GMT+02:00 can be in a lot of different regions, so the API can't map it to a single timezone. To convert it to the correct timezone, I need to use withZoneSameInstant() method:
// parse with UTC offset
ZonedDateTime z2 = ZonedDateTime.parse("Tue Jun 20 13:40:37 GMT+02:00 2017", fmt)
// convert to Berlin timezone
.withZoneSameInstant(ZoneId.of("Europe/Berlin"));
System.out.println(z2);
The output is:
2017-06-20T13:40:37+02:00[Europe/Berlin]
PS: the first case (z1) works in Java 8, but in ThreeTen Backport it's not setting the timezone to Berlin. To fix it, just call .withZoneSameInstant(ZoneId.of("Europe/Berlin")) as we did with z2.
If you still need to use java.util.Date, you can convert from and to the new API.
In java.time, new methods were added to Date class:
// convert ZonedDateTime to Date
Date date = Date.from(z1.toInstant());
// convert back to ZonedDateTime (using Berlin timezone)
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Europe/Berlin"));
In ThreeTen backport (and Android), you can use the org.threeten.bp.DateTimeUtils class:
// convert ZonedDateTime to Date
Date date = DateTimeUtils.toDate(z1.toInstant());
// convert back to ZonedDateTime (using Berlin timezone)
ZonedDateTime z = DateTimeUtils.toInstant(date).atZone(ZoneId.of("Europe/Berlin"));
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Do not use a fixed text for the timezone:
Do not use a fixed text (e.g. 'GMT') for the timezone as you have done because that approach may fail for other locales.
Solution using java.time, the modern Date-Time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(parse("Tue Jun 20 14:53:08 CEST 2017"));
System.out.println(parse("Tue Jun 20 13:40:37 GMT+02:00 2017"));
}
static ZonedDateTime parse(String strDateTime) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM d H:m:s z u", Locale.ENGLISH);
return ZonedDateTime.parse(strDateTime, dtf);
}
}
Output:
2017-06-20T14:53:08+02:00[Europe/Paris]
2017-06-20T13:40:37+02:00[GMT+02:00]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I am trying to make the SimpleDateFormat class to do what I need without success.
Here is the date format I need:
Fri 29 May 2015 10:22:30 GMT-0400 (Eastern Daylight Time)
Here is the closest format definition I was able to come with:
SimpleDateFormat("EEE dd MM yyyy HH:mm:ss Z (zzzz)", Locale.ENGLISH)
This will output:
Fri 29 May 2015 10:22:30 -0400 (Eastern Daylight Time)
Is there a formatting that can do what I need or do I have no other choice than manipulating the String to insert the missing GMT tag?
You can add the literal GMT surrounded by quotes:
new SimpleDateFormat("EEE dd MM yyyy HH:mm:ss 'GMT'Z (zzzz)", Locale.ENGLISH);
Try using EEE dd MM yyyy HH:mm:ss zZ (zzzz)
Working example
java.time
I recommend you use the modern date-time API*.
Using the available symbols, the closest match can be obtained with the pattern, EEE dd MMM uuuu HH:mm:ss OOOO (zzzz). However, it will display a : as the separator in the timezone offset. If you want the exact format, you can use the pattern, EEE dd MMM uuuu HH:mm:ss 'GMT'XX (zzzz) in which GMT has been used as a String literal.
Demo:
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(LocalDate.of(2015, Month.MAY, 29), LocalTime.of(10, 22, 30),
ZoneId.of("America/New_York"));
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("EEE dd MMM uuuu HH:mm:ss OOOO (zzzz)", Locale.ENGLISH);
String formatted1 = dtf1.format(zdt);
System.out.println(formatted1);
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("EEE dd MMM uuuu HH:mm:ss 'GMT'XX (zzzz)", Locale.ENGLISH);
String formatted2 = dtf2.format(zdt);
System.out.println(formatted2);
}
}
Output:
Fri 29 May 2015 10:22:30 GMT-04:00 (Eastern Daylight Time)
Fri 29 May 2015 10:22:30 GMT-0400 (Eastern Daylight Time)
Learn more about the the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
am getting date as string
String dateStr = Mon Mar 31 2014 00:00:00 GMT+0530 (India Standard Time)
but am getting unparsable date exception when am tring to parse using SimpleDateFormat
java.util.Date date = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss Z", Locale.ENGLISH).parse(dateStr);
please help me to solve this
The "GMT" part is confusing things - the Z format specifier expects just "0800" or similar.
You can change your format to:
"EEE MMM dd yyyy HH:mm:ss 'GMT'Z"
and that will work. (It's ignoring the time zone name at the end of the string, of course.)
This'll work:
java.util.Date date = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'Z", Locale.ENGLISH).parse(dateStr);
java.time
The legacy date-time API (java.util date-time types and their formatting API, SimpleDateFormat) are outdated and error-prone. It is recommended to stop using them completely and switch to java.time, the modern date-time API*.
Demo using modern date-time API:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.util.Locale;
public class Main {
public static void main(String args[]) {
String dateStr = "Mon Mar 31 2014 00:00:00 GMT+0530 (India Standard Time)";
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("EEE MMM d u H:m:s")
.appendLiteral(' ')
.appendZoneId()
.appendPattern("X")
.appendLiteral(" (")
.appendZoneText(TextStyle.FULL)
.appendLiteral(')')
.toFormatter(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(dateStr, dtf);
System.out.println(zdt);
}
}
Output:
2014-03-31T00:00+05:30[Asia/Kolkata]
For any reason, if you need an object of java.util.Date from this object of ZonedDateTime, you can so as follows:
Date date = Date.from(zdt.toInstant());
Learn more about the the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.