I have a GUI that plots time-series graphs. The user enters the dates they want to plot from and to by entering values in text boxes. For example, if they enter 25/07/13 22:00 and 26/07/13 00:00 the graph should plot data from 22:00:00 on the 25th through to 00:00:59 the following morning. The times the user enters are parsed into a Calendar object.
My problem comes with DST.
The user doesn't care about DST, so all they want to see is a graph between those two times. However, the Calendar objects do care about DST and so my "to" date is currently not 00:00, but 01:00. (I am in London and we are currently GMT + 1 hour for DST).
I want to effectively ignore DST and act as though everything is GMT + 0 when I want to plot graphs.
How can I do this?
Thanks for the answers guys, they helped me get my head around the problem. It sort of comes down to the fact that I use both the Calendar object, for presentation and storage of data, and the epoch for temporal calculations.
It turns out that the Calendar set() methods will take into account DST as a matter of course. So when I parse the time values in the text boxes that the user enters, and use set() for each individual Calendar field, the Calendar object will know, based-off historical data, whether the date you've just set will have DST applied. Because of this, it understands that you meant, for example, GMT+1, even if you didn't realise it (because, frankly, who does?!).
However, when you do getTimeInMillis(), the epoch returned has no concept of time zones or DST, so to match with the current time zone you have to apply DST manually to the returned epoch, if it applies. Conversely, when you use setTimeInMillis() on a Calendar object, it is assumed that the time you entered is GMT+0, but if the epoch is a date that currently has DST applied, the Calendar object will add it on for you, meaning you're +1 hour from where you thought you were. To solve this problem, you need to subtract DST, again if necessary, from the epoch before setting it in the calendar.
All of this confusion is particularly important on day boundaries, especially if you're using day resolution for anything, like me.
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.DST_OFFSET, 0);
If I understand you correctly you need to parse 25/07/13 22:00 as GMT date/time:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = sdf.parse("25/07/13 22:00");
and make a Calendar based on this date
Calendar c= Calendar.getInstance();
c.setTime(date);
TimeZone tz = TimeZone.getTimeZone("Etc/GMT0");
DateFormat df = DateFormat.getDateTimeInstance();
df.setTimeZone(tz);
System.out.println(df.format(new Date()));
Related
I'm using Java 6. Our servers are on Eastern Standard Time (GMT-5), and I'm trying to store a value that should be interpreted as such but I'm confused about how to convert it. I have
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("GMT-5"));
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
System.out.println(dateObj.toString());
but right now this prints out "Sat May 18 19:00:00 CDT 2013" (since my local machine is on CDT) but I want the result to be "Sat May 18 24:00:00 CDT 2013". How can i interpret the date string "1368921600000" as an EST date? Thanks, - Dave
1368921600000 is an instant in time, the same instant, everywhere in the world. To convert this to a date and time, you have to specify where in the world you want to know the date/time at that instant. It just so happens that the timestamp is relative to UTC, and is Sun, 19 May 2013 00:00:00 GMT.
If you want the time of this instant (the same instant) somewhere else in the world, you can use the Calendar as you did and extract the individual field values (e.g. HOUR_OF_DAY). If all you care about is getting a text string, you use a DateFormat instance such as SimpleDateFormat:
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String timeOnUSEastCoast = df.format(new Date(1368921600000L));
// will be GMT-5 or GMT-4 depending on DST
In this example, the output would be GMT-4: Sat, 18 May 2013 20:00:00 EDT (not only has the hour changed, but it's still the previous day on the US east coast).
If you want to output the UTC time but just want to pretend it's EST then it's just easier to tell the DateFormat to leave off the time zone field (remove the "z") in the text output and call it whatever you want, but understand that the timestamp value is always UTC.
It is often not necessary to use the Joda Time library to get historically accurate time-zone- and daylight-savings-aware local time mappings, although this is the common go to response for many.
If you have a database of timestamps that require local time conversion, then here are some principles that have served me well:
Store date/times (Instants, in the parlance of Joda; Dates, in the parlance of the Java Calendar API) in UTC time. UTC does not care about DST. It does not care about time zones. UTC simply represents a universally representable moment in time. This step alone can save a lot of date/time headaches.
Database records should contain TimeZone and/or Locale fields so that mapping from UTC can be performed. Think about your data. Every timestamp in your database does not need time zone information associated with it. Instead, associate the time zone data with a part of your data model that provides appropriate granularity. If your app will only ever be localized in one time zone, then you needn't store this infor at all. In my recent project, I created a Locale table that contains the TZ ID for timestamps in my Encounters table. All other timestamps are subordinate to these records, so it made sense to associate it there.
Use the Java API GregorianCalendar to map UTC Dates to local time. And that's all I ever use it for. I almost never use GregorianCalendars to do date arithmetic or other date operations. Here is the paradigm that I've been working with:
public static void main(String[] args) {
m_GregorianCalendar = new GregorianCalendar(TimeZone.getTimeZone(
"America/Chicago"));
Date d = new Date();
String fmt = "dd-MMM-yyyy # HH:mm";
:
:
String myDate = mapToLocalTime(d, fmt, gc);
:
:
}
public String mapToLocalTime(Date utc, String format, GregorianCalendar gc) {
gc.setTime(utc); // this calendar is already timezone aware (constructed
// with time zone id (DST too))
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setCalendar(gc); // formatter uses conventions of calendar
sdf.applyPattern(fmt); // pattern for formatter
return sdf.format(utc);
}
Consider representing timestamps internally in a numeric format (longs, doubles). This greatly simplifies date comparisons and date arithmetic. The only downside is that conversions must be done to format the data into a human recognizable form, but if you use functions for these conversions it need not be a big deal at all.
Use either a new java.util.SimpleDateFormat(format) or java.util.DateFormat.getDateTimeInstance(int,int), then use #setTimeZone(timezone).
When you're printing Date.toString(), the Calendar's time zone is not taken into consideration at all. After you do a cal.getTime(), whatever was set in Calendar is no longer relevant.
What is is TimeZone's default time zone.
So right before printing the date, set the default time zone to the one you want to print in, e.g.:
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
TimeZone gmtZero = TimeZone.getTimeZone("GMT");
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
TimeZone.setDefault(gmtZero);
System.out.println(dateObj.toString());
This will print the date in GMT regardless of your system time zone.
Just remember to bring back the original default TimeZone afterwards!
Is there any way to update only a Date's time path?
I tried Date.setTime() but it replaces the date path too. I there any java method or the only way is to set hour, minute, second and milisecond?
Thank you
A Java Date is just a wrapper around a long that counts time from the epoch (January 1, 1970). Much more flexible is Calendar. You can create a Calendar from a Date:
Date date = . . .;
Calendar cal = new GregorianCalendar();
cal.setTime(date);
Then you can set various fields of the Calendar:
cal.set(Calendar.HOUR_OF_DAY, 8);
// etc.
I would start by moving away from java.util.Date entirely. Ideally, use Joda Time as it's a far more capable date/time library.
Otherwise, you should use java.util.Calendar. A java.util.Date doesn't have a particular date/time until you decide what time zone you're interested in - it just represents an instant in time, which different people around the world will consider to be a different date and time of day.
You'll want to take a look at java.util.Calender.
It will allow you to change the individual parts of the date/time.
Calendar cal = Calender.getInstance();
cal.setTime(date);
cal.set(Calender.HOUR, hour);
Alternatively, as has already being suggested, I'd take a look at Joda Time
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Timezone conversion
I have a date in UTC, how to convert it to other timezone?
java.util.Date
Despite what the output of Date.toString() suggests, Date instances are not timezone aware. They simply represent a point in time, irrespective to the timezone. So if you have a Date instance, there is nothing more you need to do. And what if you have time in one time zone and you want to know what is the time in other time zone? You need
java.util.Calendar
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Asia/Tokyo"))
cal.set(Calendar.HOUR_OF_DAY, 15) //15:00 in Tokyo
cal.set(Calendar.MONTH, Calendar.NOVEMBER)
cal.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"))
cal.get(Calendar.HOUR_OF_DAY) //17:00 in Melbourne
Note that after changing the time zone the date (point in time) didn't changed. Only the representation (current hour in this particular time zone). Also note that November is important there. If we change the month to July suddenly the hour in Melbourne changes to 16:00. That's because Tokyo does not observe DST, while Melbourne does.
java.text.DateFormat
There is another catch in Java with time zones. When you are trying to format a date you need to specify time zone explicitly:
DateFormat format = DateFormat.getTimeInstance
format.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"))
Otherwise DateFormat always uses current computer's time zone which is often inappropriate:
format.format(cal.getTime())
Since format() method does not allow Calendar instances (even though it accepts Object as a parameter - sic!) you have to call Calendar.getTime() - which returns Date. And as being said previously - Date instances are not aware of time zones, hence the Tokyo and Melbourne settings are lost.
You can try Joda-Time library. They have 2 functions called withZone() and withZoneRetainFields() to perform timezone calculations.
I have a mobile application where I capture a date/time and send it as miliseconds to a servlet. -For example I capture 12:55 AM in my cellphone and send it to my server-
So, I construct the date from the sent miliseconds and print it with SimpleDateFormat but the time is diferent, it prints 8:55 AM.
I know that the problem is because I use 2 diferent timezones, my question is:
how can I show that time without apply any timezone to show the same time?
Thanks in advance
You need to use Calendar to change the TimeZone but there is no API for that.
// Change a date in another timezone
public static Date changeTimeZone(Date date, TimeZone zone) {
Calendar first = Calendar.getInstance(zone);
first.setTimeInMillis(date.getTime());
Calendar output = Calendar.getInstance();
output.set(Calendar.YEAR, first.get(Calendar.YEAR));
output.set(Calendar.MONTH, first.get(Calendar.MONTH));
output.set(Calendar.DAY_OF_MONTH, first.get(Calendar.DAY_OF_MONTH));
output.set(Calendar.HOUR_OF_DAY, first.get(Calendar.HOUR_OF_DAY));
output.set(Calendar.MINUTE, first.get(Calendar.MINUTE));
output.set(Calendar.SECOND, first.get(Calendar.SECOND));
output.set(Calendar.MILLISECOND, first.get(Calendar.MILLISECOND));
return output.getTime();
}
Link: http://blog.vinodsingh.com/2009/03/date-and-timezone-in-java.html
I think this should work. I haven't extensively tested it.
Calendar and Date objects store their date information in milliseconds in relation to UTC.
(the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC)
This means that Calendar/Date Objects do not store different values for different Time Zones. They always use the same value internally... the format is what normally changes.
Maybe you can use a SimpleDateFormat in your local/default timezone:
SimpleDateFormat sdf = new SimpleDateFormat("S")
Date d = sdf.parse(milliseconds);
You can also try to change the DateFormat's timezone until it matches your expected output.
sdf.setTimeZone(TimeZone.getTimeZone("GMT-8"));
System.out.println(sdf.toString());
sdf.setTimeZone(TimeZone.getTimeZone("GMT-4"));
System.out.println(sdf.toString());
I have a db, that stores dates in OleDateTime format, in GMT timezone. I've implemented a class, extending Date in java to represent that in classic date format. But my class is locale-dependent (I'm in GMT+2). Therefore, it converts the date in the db as date - 2 hours. How do I make it convert the date correctly? I want my class to be locale-independent, always using GMT timezone. Actually, the question is:
class MyOleDateTime extends Date {
static {
Locale.setDefault(WhatGoesHere?)
}
// ... some constructors
// ... some methods
}
Well, it's better to use the Calendar object like suggested in other answers. However, if you really want to set global timezone, you can use TimeZone.setDefault(TimeZone.getTimeZone("UTC")); early in your application code. There is also user.timezone Java system property.
Also (just fun to know), it appears that the only country actually living by GMT/UTC time (without daylight saving changes) is Liberia.
In fact, Date objects per se are always locale- and timezone-independent. Its getTime() method will always return the number of milliseconds passed since January 1, 1970 00:00:00 (not counting leap seconds) in UTC. But if you want to get something else than milliseconds, you have to use Calendar, which is timezone-dependent. But it is the right way to go. You don't use that deprecated methods in Date class, do you?
As Michael Borgwardt has already said, the Java Date object does not know anything about timezones. It's just a wrapper for a number of milliseconds since 01-01-1970 00:00:00 UTC.
You start dealing with timezones only when you for example convert the Date object to a String using a DateFormat. You set the timezone on the DateFormat to specify in which timezone you want to see the Date.
DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss Z");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String text = df.format(date); // text will contain date represented in UTC
A Date is locale-independent, always using GMT timezone. It's just a wrapper around a millisecond timestamp in GMT (more correctly: UTC).
The only things in Date that are timezone dependant are the deprecated methods like getDay() - that's why they're deprecated. Those use the default time zone. The correct thing to do is to avoid using those deprecated methods - not to set the default timezone to UTC! That could cause problems elsewhere, and you can't prevent other parts of the code from setting the default timezone to something else.
Use a Calendar object:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"),
locale);
Here's a snippet I used to calculate the GMT offset from the Calendar instance and format it. I appreciate all the help I've gotten from this site, its nice to contribute. I hope this helps someone somewhere. Enjoy.
Calendar calInst = Calendar.getInstance();
//calculate the offset to keep calendar instance GMT
int gmtOffsetMilli = calInst.get(Calendar.ZONE_OFFSET);
long gmtOffsetHr = TimeUnit.HOURS.convert(gmtOffsetMilli, TimeUnit.MILLISECONDS);
calInst = Calendar.getInstance(TimeZone.getTimeZone("GMT " + gmtOffsetHr));