Pergunta

I'm working on some code that takes care of timezone differences (in this case, converting a date from UTC to EST/EDT) and I noticed that I'm getting the incorrect DATE field from the Calendar variable.

Here are the log excerpts from the timezone change:

UTC time: Fri Nov 15 00:28:44 EST 2013
America/New_York time: Thu Nov 14 19:28:44 EST 2013

The code is correctly updating the time to the 14th, based on what shows up with Calendar.getTime(), but when I call Calendar.get(Calendar.DATE), I'm still getting 15 as the date.

Why are the fields not updating when the time itself is?


Code excerpt:

Calendar cal = Calendar.getInstance();
cal.setTime(new Date());

TimeZone fromTz = TimeZone.getTimeZone("UTC");
TimeZone toTz = TimeZone.getTimeZone("America/New_York");

jlog.info(fromTz.getID() + " time: " + cal.getTime().toString());

cal.setTimeZone(fromTz);
// Finds the difference between the two timezones based on their offset from UTC (in the case of the system timezone changing in the future).
long currentTime = System.currentTimeMillis();
int timeDifference = toTz.getOffset(currentTime) - fromTz.getOffset(currentTime);

cal.add(Calendar.MILLISECOND, timeDifference);

if (dst) { // Logic for determining whether or not the extra hour for DST is needed.
  cal.add(Calendar.MILLISECOND, fromTz.getDSTSavings());
}

jlog.info(toTz.getID() + " time: " + cal.getTime().toString());

jlog.info(cal.getTime().toString() + " - " + cal.get(Calendar.DATE));

Output for the above code:

UTC time: Fri Nov 15 00:28:44 EST 2013
America/New_York time: Thu Nov 14 19:28:44 EST 2013
Thu Nov 14 19:28:44 EST 2013 - 15

NOTE: Please don't suggest that I use Joda-time. I'm just looking for an answer to this question as is.

If it's not solvable because Calendar/Date are terrible packages, that's fine - I'll just parse the date myself to get the correct values. Thanks in advance.

Foi útil?

Solução

You haven't shown us enough code to know what's going on, but your output is very easy to explain: you're calling Date.toString() which always formats the instant in time using the system local time zone. The time zone in the Calendar object is irrelevant.

So it looks like your Calendar may be in UTC for example, in which case the day is 15.

Note that the first line of your log is clearly nonsense:

UTC time: Fri Nov 15 00:28:44 EST 2013

It's either UTC or it's EST. It can't be both.

It's not clear where your data comes from or exactly how you're dealing with it, but if you're manually adjusting the date/time by adding or subtracting parts, you're almost certainly doing it wrong. It's very important to understand that a java.util.Date doesn't have any notion of a time zone as part of its data - it's just an instant in time.

To format a Date in a particular time zone, you'd usually use a SimpleDateFormat and specify the time zone on that. For example:

DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("The time in New York is " + format.format(new Date()));

EDIT: Now that we've seen your code, you should definitely not do things that way. You're adjusting the point in time, which isn't what you want - you only want to change your view on the same point in time, which can be achieved using Calendar.setTimeZone to change what's returned by Calendar.get, and using DateFormat.setTimeZone to change which time zone is used for parsing/formatting text.

Outras dicas

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*.

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

import java.time.LocalDate;
import java.time.LocalTime;
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 zdtUtc = ZonedDateTime.of(LocalDate.of(2013, 11, 15), LocalTime.of(0, 28, 44),
                ZoneId.of("Etc/UTC"));

        ZonedDateTime zdtNewYork = zdtUtc.withZoneSameInstant(ZoneId.of("America/New_York"));

        System.out.println(zdtNewYork);

        // Custom format
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z uuuu", Locale.ENGLISH);
        System.out.println(zdtNewYork.format(dtf));
    }
}

Output:

2013-11-14T19:28:44-05:00[America/New_York]
Thu Nov 14 19:28:44 EST 2013

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.

If I understand your question, you could do it with something like this -

Calendar c = Calendar.getInstance();
DateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy",
        Locale.ENGLISH);
formatter.setTimeZone(TimeZone.getTimeZone("UTC")); // <-- Set the timezone on the
                                                    // DateFormat
/* Your input date/time */
c.setTime(new GregorianCalendar(2013, 10, 15, 0, 28, 44).getTime());

System.out.println(formatter.format(c.getTime()));
c.setTimeZone(TimeZone.getTimeZone("EST"));
System.out.println(c.getTime());

The output of this code is

Fri Nov 15 05:28:44 UTC 2013
Fri Nov 15 00:28:44 EST 2013
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top