Performing date and time calculations in Java - java

I'm trying to do simple date/time calculations and no matter what I do I'm getting confused with what appears to be time zones etc.
I need to add (and subtract)different date/times together so I figured the easiest way would be to convert them to seconds, perform the calculations and then format back to a string. But no matter what I do, I'm one hour out - which sounds like timezone/DST etc.
What's wrong with this:
long dateInMillis = 3600000L;
DateFormat formatter = new SimpleDateFormat("HH:mm");
Date dt = new Date();
dt.setTime(dateInMillis);
System.out.println( formatter.format(dt.getTime()));
The answer I get is 2:00. Even if I use:
long dateInMillis = 1;
I still get 1:00
Help please ;-)
TIA
Martin

I agree that using Joda is likely to make life easier. However what you are seeing is the effect of a TimeZone sensitive formatter.
You could try adding:
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

Save yourself a lot of headache, use an existing library for this - I like Joda Time myself.
For example the DateTime class supports various calculations (to add or subtract specific periods etc.) If you want an example, post your exact requirement, and I'll add it to the answer.

You should not try and format a long as a date. Just use:
long dateInMillis = 3600000L;
DateFormat formatter = new SimpleDateFormat("HH:mm");
Date dt = new Date();
dt.setTime(dateInMillis);
System.out.println( formatter.format(dt));
However, I would use a 3rd party lib for handling time-calculations like JodaTime as #Nim suggested, if your calculations are in anyway complicated.

I also suggest to use a framework as suggested in the comments.
However, Date is based on the milliseconds after Jan. 1st 1970. So 3600000L is just one hour from there, so 1:00 is correct.

According to people here, you could use Joda Time to resolve this calculations. If you want to stay with pure Java code, you can look for Calendar class to do your operations with dates. Here is a sample:
Calendar c = Calendar.getInstance();
DateFormat df = new SimpleDateFormat("HH:mm");
Date d = c.getTime();
System.out.println(df.format(d)); //actual hour:minute
c.add(Calendar.HOUR, -4);
d = c.getTime();
System.out.println(df.format(d)); //actual hour:minute minus 4 hours
Still, I won't rely too much in this kind of code. You'll have less problems using Joda Time.

When you're calling new Date(), current date in current timezone is created. When you're calling 'dt.setTime(dateInMillis);' you set offset in milliseconds from 1 January 1970 in GMT timezone. Let me explain, here in Msk, first call will give me Thu Mar 01 17:02:22 MSK 2012, the second call will give me 1 hour offset, thus in Msk it will be original offset (+3) plus 1, so it'll be Thu Jan 01 04:00:00 MSK 1970. Then when you're calling formatter.format(? extends Number) it calls new Date(((Number)obj).longValue()) inside, so it creates back you date which is Thu Jan 01 04:00:00 MSK 1970 and prints hours and minutes. So, you're right, it prints out time in your current timezone.

For posterity, here's example code using the Joda-Time 2.3 library as mentioned by the other answers and comments.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
long dateTimeInMillis = 3600000L; // 1,000 * 60 * 60 = 3600000L = 1 hour.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html (not quite up-to-date, read page for details)
DateTime dateTimeInUtc = new DateTime( dateTimeInMillis, DateTimeZone.UTC );
// Paris happens to be one hour ahead of UTC/GMT.
DateTime dateTimeInParis = dateTimeInUtc.toDateTime( DateTimeZone.forID( "Europe/Paris" ) );
Dump to console…
System.out.println( "dateTimeInUtc: " + dateTimeInUtc );
System.out.println( "dateTimeInParis: " + dateTimeInParis );
When run…
dateTimeInUtc: 1970-01-01T01:00:00.000Z
dateTimeInParis: 1970-01-01T02:00:00.000+01:00

Related

Java - Incorrect result when convert Millisecond to `HH:mm:ss:SSS` in Netbeans IDE

I'm trying to convert 5007 to HH:mm:ss:SSS, I'm using Netbeans IDE v8.2:
Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date);
But this gives me an incorrect result:
01:00:05:007
^^-------------------------why does it print to me 01 in the hours?
But it should get:
00:00:05:007
^^-------------------------correct result
When I use ideone for example it gives me a correct result:
Any explication about this problem?
I testes with both versions: Java 7 and Java 8.
When you ask for the explanation of your problem I will be honest with you: You are misusing SimpleDateFormat for formatting a duration. It was never meant for this use and is not designed to give you the expected result if you try it anyway. The problem you got is not the only problem you will have. If your duration grows to more than 24 hours, the whole days will not be formatted, so your result will be wrong in a different way.
Apart from that, these days I recommend not using the SimpleDateFormat class at all. Its replacement is java.time.format.DateTimeFormatter, it came out in 2014 and comes with quite fewer negative surprises. It is not a good choice for formatting a duration either, though.
So what is a good choice? I will give you the Java 9 answer first because it is easy:
Duration dur = Duration.ofMillis(5007);
String hmss = String.format("%02d:%02d:%02d:%03d",
dur.toHours(),
dur.toMinutesPart(),
dur.toSecondsPart(),
dur.toMillisPart​());
The main advantage isn’t even the ease, it’s the clarity. For example, with this code no one needs to ask the question I asked in a comment, is it a point it time or a duration? The code very clearly states that it is a duration.
For Java 6, 7 and 8, you may still use the Duration class (in Java 6 and 7 through the ThreeTen Backport), but it doesn’t lend itself that easily to formatting, so I think I would resort to using the TimeUnit enum as in the question I had linked to (the link you asked me to remove again):
long millis = 5007;
long hours = TimeUnit.MILLISECONDS.toHours(millis);
millis -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
millis -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
millis -= TimeUnit.SECONDS.toMillis(seconds);
String hmss = String.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);
The result is the desired
00:00:05:007
To illustrate the point I was trying to make about what happens when the duration is more than 24 hours, let’s try to give it 100 000 000 milliseconds (there are 86 400 000 millis in a day):
27:46:40:000
Date date = new Date(5007);
creates a Date which is 5007 milliseconds after January 1, 1970, 00:00:00 GMT.
When you format it with SimpleDateFormat, it is converted to your machine's timezone (which I'm assuming is GMT+1).
When I run your code I get
02:00:05:007
And my timezone is GMT+2.
If you set the timezone of the DateFormat instance to GMT, you'll get the expected output:
Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
formatter.setTimeZone (TimeZone.getTimeZone ("GMT"));
String dateFormatted = formatter.format(date);
System.out.println (dateFormatted);
This prints
00:00:05:007
It is definitely a Locale issue. When you do not specify one it will use the (your system) default one. To override this;
try either adding this
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
or passing a Locale when constructing the SimpleDateFormat instance. For example:
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS", Locale.ENGLISH);
I assume Locale.ENGLISH has UTC timezone btw, but I hope you get the idea.

Converting a Joda DateTime to another timezone without changing the time

I have tried all the ways in all the other questions on SO, and I can't get it to work. It is making me want to kill myself.
I have a set of times which are something like "04:00 AM AEST", except the AEST is a glitch, they should be GMT. What I want to do is change them to "04:00 GMT", and then convert them up to the correct AEST times (which in this example would be "14:00 AEST"). I have tried everything, and nothing works. The closest was to manually make a new DateTime using each individual value from the original date, e.g.
DateTime dt = new DateTime(origdate.year, origdate.month, origdate.day, origdate.hour, origdate.minute, origdate.second, timezone.GMT)
But for some reason the results came out four and a half minutes over, which is weird because timezones differ on hours and half hours.
1st Method By following lines you will get GMT time in specified format :
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Date currentLocalTime = cal.getTime();
DateFormat date = new SimpleDateFormat("dd-MM-yyy HH:mm:ss z");
date.setTimeZone(TimeZone.getTimeZone("GMT"));
String gmtTime = date.format(currentLocalTime);
Hence, from GMT you can derive the time of any place.
2nd Method You can get system time of current place in milliseconds by:
Long current_time = System.currentTimeMillis() / 1000L;
Hope it helps.

SimpleDateFormat adding 7 hours?

I'm trying to do a simple subtraction of dates and getting odd results. For some reason when I format it with SimpleDateFormat, there are 7 extra hours difference.
package timedemo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Timedemo {
public static void main(String[] args) {
Date start = new Date(); // time right now
Date stop = new Date();
long startTime = start.getTime()-1000; // introduce a second of skew
long stopTime = stop.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
// First one shows up prior to 1970 epoch. Okay, except for 5 hour variance.
// Probably a timezone thing, as I'm in EST (-5).
System.out.println("New date is "+new Date(stopTime - startTime));
System.out.println("Raw Start is "+startTime); // fine
System.out.println("Raw Stop is "+stopTime); // fine
System.out.println("Raw Difference is "+(stopTime-startTime));
System.out.println("Formatted Start is "+sdf.format(startTime));
System.out.println("Formatted Stop is "+sdf.format(stopTime));
System.out.println("Formatted Difference is "+sdf.format(stopTime-startTime));
}
}
And the results are:
New date is Wed Dec 31 19:00:01 EST 1969
Raw Start is 1418397344360
Raw Stop is 1418397345360
Raw Difference is 1000
Formatted Start is 10:15:44
Formatted Stop is 10:15:45
Formatted Difference is 07:00:01
I had thought it was a timezone thing, but I'm in EST (-5), not MST (-7).
I would suspect Daylight Savings, but it's 7 hours, not 1.
12/24 hour difference? 12-7=5 which is my timezone offset... not sure what to make of it though.
Kind of out of ideas at this point.
Why the seven hour shift on the last line?
The "12-7 = 5" is definitely related to the problem... or more accurately, it's "12-5=7", i.e. 5 hours before midnight is 7pm. You'll see that if you format it as a full date/time:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Heck, you can see that in your first line: "Wed Dec 31 19:00:01 EST 1969" - that 19:00 is 7pm, which you're formatting using hh as 07.
Fundamentally, the problem is that you're trying to treat a difference in time as if it's a point in time. I would very strongly urge you not to do that. If you absolutely want to do that (and the difference will always be non-negative but less than 24 hours), then you should set the time zone on SimpleDateFormat to UTC, and use HH instead of hh. But it would be better to use Joda Time or java.time from Java 8 which can represent a Duration, i.e. the difference between two points in time. Date is simply not an appropriate data type for that.

How do I set a Calendar with TimeZone?

I have a timestamp that I am trying to put into a Date object, however when I use Calendar, I am running into a strange problem. I seem to be able to unable to create a Date object with the values I want:
public static void main(String args[]){
Date today = new Date();
int hour = 4, min=0, sec=0, ms=64;
Calendar cal = GregorianCalendar.getInstance();
cal.clear();
cal.setTimeZone(TimeZone.getTimeZone("EDT"));
cal.setTime(today);
cal.set(Calendar.HOUR_OF_DAY,hour);
cal.set(Calendar.MINUTE,min);
cal.set(Calendar.SECOND,sec);
cal.set(Calendar.MILLISECOND,ms);
System.out.println("Time is: "+cal.getTime());
}
This produces:
Time is: Mon Jan 13 23:00:00 EST 2014
which is not the result I am looking for.
However, if I comment out the 'setTimeZone' method call, I get the following result:
Time is: Tue Jan 14 04:00:00 EST 2014
This is the result that I am looking for but I am concerned that if I am running on a machine that is not running in the same time zone, I will not get consistent behavior.
This is the result that I am looking for but I am concerned that if I am running on a machine that is not running in the same time zone
it is the problem. The internal representation should be ok, but it prints on local timezone: representation differs from real content.
use SimpleDateFormat http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html and set TimeZone to see the date on the Zone desired.
The problem here is that Java does not know of the timezone "EDT" (Eastern Daylight Time). As a result, Calendar seems to be setting the timezone to GMT.
The timezone needed here is "America/New_York" or "EST5EDT". When either of these values are used, the correct result is produced.
The list of valid Time Zones can be obtained by calling TimeZone.getAvailableIDs()
It is unfortunate that no warnings are produced when the requested Time Zone is not found.
If you can do away with java.util.Date, you can use joda time API to conveniently set these values as desired:
For your query, you can set your already created Calendar instance as a constructor parameter to DateTime.
DateTime dt = new DateTime(cal);
System.out.println(dt.toDateTimeISO());
Output:
2014-01-14T04:00:00.064-05:00
Calendar.getTime() returns a java.util.Date object. Date objects do not know anything about timezones. The Date object that Calendar.getTime() returns does not know to what timezone the Calendar that it came from is set.
When you print a Date object (for example, by implicitly calling toString() object, as you are doing) it is formatted in the default time zone of the machine you are running it on.
If you want to print a Date in a specific timezone, use a SimpleDateFormat, and set the desired timezone on the SimpleDateFormat object. For example:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
df.setTimeZone(TimeZone.getTimeZone("EDT"));
// Will display the date that the calendar is set to in the EDT timezone
System.out.println(df.format(cal.getTime()));
Java Date objects represent the number of milliseconds seconds since January 1, 1970, 00:00:00 GMT due to the fact that the other methods are deprecated. The two ways to "view" a Date object directly are "getTime()" and "toString()" (using "dow mon dd hh:mm:ss zzz yyyy"). Therefore, you are formatting the GMT value to your local timezone.
When working with dates, it is best to think of them as GMT values, and then as a "formatting" exercise when viewing the date.
For comparison, here is that same kind of code but using Joda-Time 2.3.
Avoid the java.util.Date & .Calendar classes.
Never use three-letter codes for time zones. They are neither standardized nor unique. Instead use proper time zone names. In this case, use "America/New_York" or "America/Montreal".
// Use time zone names, such as from this slightly outdated list: http://joda-time.sourceforge.net/timezones.html
DateTimeZone timeZone = DateTimeZone.forID( "America/New_York" );
// Input.
int hour = 4, min = 0, sec = 0, ms = 64;
// Start with now, then adjust the time of day.
DateTime now = new DateTime( timeZone );
DateTime dateTime = now.withHourOfDay( hour ).withMinuteOfHour( min ).withSecondOfMinute( sec ).withMillisOfSecond( ms );
// If needed, translate to a java.util.Date for use with other classes.
java.util.Date date = dateTime.toDate();
Dump to console…
System.out.println( "now: " + now );
System.out.println( "dateTime: " + dateTime );
System.out.println( "date: " + date );
When run…
now: 2014-01-20T21:04:51.237-05:00
dateTime: 2014-01-20T04:00:00.064-05:00
date: Mon Jan 20 01:00:00 PST 2014

Get Time in London

How can I get the current local wall clock time (in number of millis since 1 Jan 1970) in London? Since my application can run on a server in any location, I think I need to use a TimeZone of "Europe/London". I also need to take Daylight Savings into account i.e. the application should add an hour during the "summer".
I would prefer to use the standard java.util libraries.
Is this correct?
TimeZone tz = TimeZone.getTimeZone("Europe/London") ;
Calendar cal = Calendar.getInstance(tz);
return cal.getTime().getTime() + tz.getDSTSavings();
Thanks
I'm not sure what this quantity represents, since the "number of millis since 1 Jan 1970" doesn't vary based on location or daylight saving. But, perhaps this calculation is useful to you:
TimeZone london = TimeZone.getTimeZone("Europe/London");
long now = System.currentTimeMillis();
return now + london.getOffset(now);
Most applications are better served using either UTC time or local time; this is really neither. You can get the UTC time and time in a particular zone like this:
Instant now = Instant.now(); /* UTC time */
ZonedDateTime local = now.atZone(ZoneId.of("Europe/London"));
Others have said that it may well not be a good idea to do this - I believe it depends on your situation, but using UTC is certainly something to consider.
However, I think you've missed something here: the number of seconds which have occurred since January 1st 1970 UTC (which is how the Unix epoch is always defined - and is actually the same as in London, as the offset on that date was 0) is obtainable with any of these expressions:
System.currentTimeMillis()
new Date().getTime()
Calendar.getInstance().getTime().getTime()
If you think about it, the number of milliseconds since that particular instant doesn't change depending on which time zone you're in.
Oh, and the normal suggestion - for a much better date and time API, see Joda Time.
To get the current time in London:
SimpleDateFormat f = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
f.setTimeZone(TimeZone.getTimeZone("Europe/London"));
System.out.println(f.format(GregorianCalendar.getInstance().getTime()));

Categories

Resources