1

I'm always getting the parse exception even if the format to check and the string value are same. Here is the code:

String format = "EEE MMM dd HH:mm:ss z yyyy";
String value = "Mon Sep 18 10:30:06 MST 2017";
public static boolean isValidFormat(String format, String value) {
    Date date = null;
    try {
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        date = sdf.parse(value); // here it breaks
        if (!value.equals(sdf.format(date))) {
            date = null;
        }
    } catch (ParseException ex) {
        ex.printStackTrace(); //java.text.ParseException: Unparseable date: 
                                "Mon Sep 18 10:30:06 MST 2017" (at offset 0)
    }
    return date != null;
}
pratz9999
  • 539
  • 4
  • 20
sai
  • 97
  • 1
  • 10
  • What you wanna do? Tell me then I'll se how to give the solution of an answer – Alok Sep 18 '17 at 17:50
  • actually the above date "value" I'm fetching from database and I need to convert the above date value to different format. So first Im changing the above string value to date and then convert the obtained date to my required format using another sdf – sai Sep 18 '17 at 17:54
  • Do want to get the code of dateFormatter()? – Alok Sep 18 '17 at 17:57
  • I think the problem is with the time zone, when I run your code without the z and MST it returns true, maybe MST it's not the way SimpleDateFormatter it's expecting it. – Chris Gomez Sep 18 '17 at 17:57
  • i'm giving out the code of date formatter, just check it whether it works for you or not. If it works then don't forget to mark it as correct answer. – Alok Sep 18 '17 at 17:59
  • It is used to work all the time from past 2 months. Suddenly from today Im getting this exception. – sai Sep 18 '17 at 18:01
  • Did you change your java version? – Chris Gomez Sep 18 '17 at 18:03
  • no. I did not change any of the code/version – sai Sep 18 '17 at 18:04

4 Answers4

2

It says that your date-time string is unparseable at index 0. Index 0 is where it says Mon, so the three letter time zone abbreviation is not the first suspect. The locale is. “Mon” works as abbreviation for Monday in English, but not in very many other languages. So if your device has a non-English language setting — maybe it has even been changed recently — this will fully explain your observation.

The shortsighted solution is

        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.ROOT);

I use Locale.ROOT to mean that no language specific processing should be done. If your string is in English because English is generally the language used in computing around the globe, I would consider this choice appropriate. If on the other hand it is in English because it comes from an English speaking locale, that locale will be the right one to use.

With this change, on my computer your code formats your date into Mon Sep 18 11:30:06 MDT 2017, which, as you can see is not the same as the value we started out from, so your method returns false. My JVM understood MST as Mountain Standard Time, and then assumed summer time (DST) in September and formatted the string accordingly.

ThreeTenABP

That said, Date and SimpleDateFormat are long outdated classes. You should give it a thought to get rid of them and use the modern Java date and time API instead. On Android you get it in the ThreeTenABP, see this question: How to use ThreeTenABP in Android Project. Now you may do:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format, Locale.ROOT);
    try {
        return ZonedDateTime.parse(value, dtf).format(dtf).equals(value); 
    } catch (DateTimeParseException dtpe) {
        dtpe.printStackTrace();
        return false;
    }

This behaves the same as above.

Three letter time zone abbreviations

You should avoid the three and four letter time zone abbreviations where you can. They are not standardized and generally ambiguous. MST, for example, may mean Malaysia Standard Time or Mountain Standard Time. The latter isn’t even a full time zone, since MDT is used for the greater part of the year, which caused the trouble I observed as I said above.

Instead, see if you can get a string in ISO 8601 format, like 2017-09-18T10:30:06+08:00. Second best, just get something unambiguous. One way is to include an offset from UTC rather than a time zone ID (or both).

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    Regarding MST vs MDT, I've made some tests and when parsing, MST defaults to America/Denver (which has DST) - you can check by calling `sdf.getTimeZone().getID()` after parsing (yes, the parsed timezone is set in the formatter, why?). But maybe the OP's default timezone is one that doesn't have DST (such as America/Phoenix), and that would explain why the input has MST in September. –  Sep 18 '17 at 20:01
  • 1
    @Ole V.V Yes, customer reported that he changed device language to spanish, it caused the error. when he again changed the device to english it worked. – sai Sep 18 '17 at 20:35
1

Never use SimpleDateFormat or DateTimeFormatter without a Locale

Since the given date-time is in English, you should use Locale.ENGLISH with your date-time parser; otherwise the parsing will fail in a system (computer, phone etc.) which is using a non-English type of locale.

Also, note that the date-time API of java.util 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.

Demo:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
        System.out.println(zdt);
    }
}

Output:

2017-09-18T10:30:06-06:00[America/Denver]

An important note about timezone before we proceed further:

Avoid specifying a timezone with the 3-letter abbreviation. A timezone should be specified with a name in the format, Region/City e.g. ZoneId.of("Europe/London"). With this convention, the ZoneId for UTC can be specified with ZoneId.of("Etc/UTC"). A timezone specified in terms of UTC[+/-]Offset can be specified as Etc/GMT[+/-]Offset e.g. ZoneId.of("Etc/GMT+1"), ZoneId.of("Etc/GMT+1") etc.

There are some exceptional cases as well e.g. to specify the timezone of Turkey, we use

ZoneId.of("Turkey")

The following code will give you all the available ZoneIds:

// Get the set of all time zone IDs.
Set<String> allZones = ZoneId.getAvailableZoneIds();

You should ask your server application to provide you with the date-time using this convention e.g.

Mon Sep 18 10:30:06 America/Denver 2017

The above code, without any change, will work for this date-time string.

Coming back to the original topic:

By default, DateTimeFormatter#ofPattern uses the default FORMAT locale which the JVM sets during startup based on the host environment. Same is the case with SimpleDateFormat. I have tried to illustrate the problem through the following demo:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        final String strDateTime = "Mon Sep 18 10:30:06 America/Denver 2017";
        DateTimeFormatter dtfWithDefaultLocale = null;

        System.out.println("JVM's Locale: " + Locale.getDefault());
        // Using DateTimeFormatter with the default Locale
        dtfWithDefaultLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out
                .println("Parsed with JVM's default locale: " + ZonedDateTime.parse(strDateTime, dtfWithDefaultLocale));

        // Setting the JVM's default locale to Locale.FRANCE
        Locale.setDefault(Locale.FRANCE);

        // Using DateTimeFormatter with Locale.ENGLISH explicitly (recommended)
        DateTimeFormatter dtfWithEnglishLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu", Locale.ENGLISH);
        System.out.println("JVM's Locale: " + Locale.getDefault());
        System.out.println("DateTimeFormatter's Locale: " + dtfWithEnglishLocale.getLocale());
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtfWithEnglishLocale);
        System.out.println("Parsed with Locale.ENGLISH: " + zdt);

        System.out.println("JVM's Locale: " + Locale.getDefault());
        // Using DateTimeFormatter with the default Locale
        dtfWithDefaultLocale = DateTimeFormatter.ofPattern("EEE MMM d H:m:s z uuuu");
        System.out.println("DateTimeFormatter's Locale: " + dtfWithDefaultLocale.getLocale());
        System.out
                .println("Parsed with JVM's default locale: " + ZonedDateTime.parse(strDateTime, dtfWithDefaultLocale));
    }
}

Output:

JVM's Locale: en_GB
DateTimeFormatter's Locale: en_GB
Parsed with JVM's default locale: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: en
Parsed with Locale.ENGLISH: 2017-09-18T10:30:06-06:00[America/Denver]
JVM's Locale: fr_FR
DateTimeFormatter's Locale: fr_FR
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Mon Sep 18 10:30:06 America/Denver 2017' could not be parsed at index 0
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:598)
    at Main.main(Main.java:32)

The following demo, using SimpleDateFormat, is just for the sake of completeness:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) throws ParseException {
        final String strDateTime = "Mon Sep 18 10:30:06 MST 2017";
        SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM d H:m:s z yyyy", Locale.ENGLISH);
        Date date = sdf.parse(strDateTime);
        System.out.println(date);
    }
}

Output:

Mon Sep 18 18:30:06 BST 2017

Note: The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the milliseconds from the Epoch of January 1, 1970. When you print an object of java.util.Date, its toString method returns the date-time calculated from this milliseconds value. Since java.util.Date does not have timezone information, it applies the timezone of your JVM and displays the same. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFomrat and obtain the formatted string from it.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
0

Here is the code of dateformatter which will hep you to convert your date into any time format.

public void setDate(String date) {
            dateInput = (TextView) itemView.findViewById(R.id.dateText);
            DateFormat inputFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
            try {
                dateData = inputFormat.parse(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            DateFormat outputFormat = new SimpleDateFormat("pur your desirable format");
            String outputString = outputFormat.format(dateData);
            dateInput.setText(outputString);
        }
Alok
  • 8,452
  • 13
  • 55
  • 93
  • Thanks for the quick response. But I see my code is same from yours. Do you think there is any problem with my code? – sai Sep 18 '17 at 18:06
  • try not to put the DateFormat inside your try and catch block and please the above code because there is a difference in it. You just see by yourslef I've used this `DateFormat inputFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");` – Alok Sep 18 '17 at 18:09
  • The only change is you changed SDF to DateFormat. I tried using the same code still it throws error – sai Sep 18 '17 at 18:25
0

I use the almost use the same code as you do with only slight difference in SimpleDateFormat instantiation.

public static final String DATE_FORMAT = "EEE MMM d yyyy z HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.ROOT);
simpleDateFormat.format(date);

It returns Mon Sep 18 2017 GMT+03:00 23:04:10.

Thracian
  • 43,021
  • 16
  • 133
  • 222