2

I ran into an issue while parsing the following date.

08 März 2015 02:15:20

Code

SimpleDateFormat fmt = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
fmt.setLenient(false);
try {
    System.out.println(fmt.parse("08 März 2015 02:15:20"));
} catch (ParseException e) {
    e.printStackTrace();
}

After some investigation, I found out that due to how DST works, 2 AM in my timezone does not exist. Once the clock strikes 1:59 AM, the next time it updates will be 3 AM. I have checked that this is the case with the following dates:

"08 März 2015 01:59:59"
"08 März 2015 03:00:00"

Both of which can be parsed correctly.

What I want is a date object that correctly interprets the input date exactly as I see it:

Mar 8, 2015 at 2:15:20 AM

How can I accomplish this?

MxLDevs
  • 19,048
  • 36
  • 123
  • 194

2 Answers2

2

Ideally, you'd use a parser that allowed you to parse date/time values without trying to apply a time zone at all - both java.time.* and Joda Time allow for this, IIRC, and both are much cleaner than java.util.*.

However, if you have to use Date, and you don't know the origin time zone, the safest approach is to use TimeZone:

SimpleDateFormat parser = new SimpleDateFormat(
   "dd MMM yyyy HH:mm:ss",
   Locale.GERMAN);
parser.setLenient(false);
parser.setTimeZone(TimeZone.getTimeZone("UTC"));

So, that will parse it as if it were originally in UTC - so when you want to format it back to text, you need to do the same thing:

SimpleDateFormat formatter = new SimpleDateFormat(
   "EEE d, yyyy at h:mm:ss tt",
   Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
String text = formatter.format(date);
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks! Wish our system was initially designed to account for timezones before accepting international dates. This will be somewhat tricky... – MxLDevs Mar 19 '15 at 18:50
0

TL;DR

Set TimeZone.getTimeZone("Europe/Berlin") as the timezone to fmt.

Demo:

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

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat fmt = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
        fmt.setLenient(false);
        fmt.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        try {
            Date date = fmt.parse("08 März 2015 02:15:20");
            System.out.println(fmt.format(date));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Output:

08 März 2015 02:15:20

ONLINE DEMO

In case you want the output with AM/PM marker, the technique will stay the same with an additional step to create another SimpleDateFormat instance for output (because the format of the output will be different from the input).

Demo:

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

public class Main {
    public static void main(String[] args) {
        SimpleDateFormat parser = new SimpleDateFormat("dd MMM yyyy HH:mm:ss", Locale.GERMAN);
        parser.setLenient(false);
        parser.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy hh:mm:ss a", Locale.GERMAN);
        formatter.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        try {
            Date date = parser.parse("08 März 2015 02:15:20");
            System.out.println(formatter.format(date));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Output:

08 März 2015 02:15:20 AM

ONLINE DEMO

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.

Solution using java.time, the modern Date-Time API:

Unlike java.util.Date which is simply a wrapper around the number of milliseconds since the UNIX epoch (January 1, 1970, 00:00:00 GMT), java.time types truly represent a date, a time, a date-time, with and without timezone information.

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

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter parser = DateTimeFormatter.ofPattern("dd MMM uuuu HH:mm:ss", Locale.GERMAN);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMM uuuu hh:mm:ss a", Locale.GERMAN);
        LocalDateTime ldt = LocalDateTime.parse("08 März 2015 02:15:20", parser);
        System.out.println(parser.format(ldt));
        System.out.println(formatter.format(ldt));
    }
}

Output:

08 März 2015 02:15:20
08 März 2015 02:15:20 AM

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.

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