27

I am looking for the best way to add milliseconds to a Java Date when milliseconds is stored as a 'long'. Java calendar has an add function, but it only takes an 'int' as the amount.

This is one solution I am proposing...

Calendar now = Calendar.getInstance();
Calendar timeout = Calendar.getInstance();

timeout.setTime(token.getCreatedOn());
timeout.setTimeInMillis(timeout.getTimeInMillis() + token.getExpiresIn());

Any other suggestions?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
cweston
  • 11,297
  • 19
  • 82
  • 107

4 Answers4

26

Your solution looks nearly fine to me, actually. I originally posted an answer going via Date, when I hadn't taken getTimeInMillis and setTimeInMillis into account properly.

However, you're calling setTime and then setTimeInMillis which seems somewhat redundant to me. Your code looks equivalent to this:

Calendar timeout = Calendar.getInstance();
timeout.setTimeInMillis(token.getCreatedOn().getTime() + token.getExpiresIn());

A generally nicer alternative would be to use Joda Time though :) It's generally a much nicer date/time API.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I was using calendar.getInstance() for some implementations but had to give up on it because if the Date comes with TimeZones the calendar will break it. – will824 Dec 21 '13 at 21:53
  • @will824: I don't understand your comment at all - `Date` *never* has a time zone associated with it. It's *always* just a number of milliseconds since the Unix epoch. – Jon Skeet Dec 22 '13 at 08:45
  • @Jon Actually it should be as you say, but if you check the Date object, it always carries some kind of timezone along with it. Seems Java has had lots of issues with Date manipulation (thats why Joda was born) and this is one of them. That is exactly the reason why I could not use calendars to fix my issue but instead had to resolve to Joda millis manipulation. Of course my issue was very specific and was related to the total seconds that passed from 1/1/1970 that should have been 0 but I was receiving a negative number because of timezone. Using calendars would diminish 1 hour in my case :( – will824 Jan 02 '14 at 23:09
  • @will824: No, java.util.Date doesn't have a time zone in it. It really doesn't - what makes you think it does? – Jon Skeet Jan 02 '14 at 23:12
  • @JonSkeet I know theoretically it does not, but if you debug a Date, it will have 2 properties, one of which will have a value related to timezone. Try to ask a date created in a GMT based server with 1/1/1970 00:00:00 date how many millis it has and surprisingly it will return a negative number. Perhaps is not the Date object itself but the instantiation of it. – will824 Jan 03 '14 at 14:51
  • @will824: It's not a matter of "theoretically" - a `Date` object *only* represents millis since the Unix epoch. The implementation has an extra field to cache some values for efficiency, but that doesn't cause any visible behavioural change. Your point about 1970-01-01 is probably due to the time zone *actually* being Europe/London, which was on British Standard Time (UTC+1) at the Unix epoch. Either that, or you've run into http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4832236. Either way, that doesn't support your claim that `Date` has a time zone internally. – Jon Skeet Jan 03 '14 at 17:55
  • @will842: Note that there's a *static* field which is a `Calendar` which has an associated time zone, but that's basically for conversions - it doesn't affect the data in any particular instance of `Date`. – Jon Skeet Jan 03 '14 at 17:56
  • @JonSkeet You are right but still in practice the behavior product of this bug prevented me to use the suggested solution, therefore I had to use Joda, which provided the milliseconds correctly. – will824 Jan 03 '14 at 18:52
26

You can also create a date with the current local date plus the number of miliseconds you need to add as Expire time

import java.util.Date;

long expiremilis = 60000l; //  1 minute
// Expires in one minute from now
Date expireDate = new Date(System.currentTimeMillis() + expiremilis);

Or the same with a calendar

long expiremilis = 60000l; // 1 minute
Calendar expireDate= Calendar.getInstance();
// Expires on one minute from now
expireDate.setTimeInMillis(System.currentTimeMillis() + expiremilis);

If you use an existing Date object you can do:

import java.util.Date;

long expiremilis = 60000l; // 1 minute
// Expires on one minute from the date object date
Date expireDate = new Date(myDate.getTime() + expiremilis);

And with an existing Calendar Object

long expiremilis = 60000l; // 1 minute
Calendar expireDate= Calendar.getInstance();
// Expires on one minute from the calendar date
expireDate.setTimeInMillis(myCalendar.getTimeInMillis() + expiremilis);
Dubas
  • 2,855
  • 1
  • 25
  • 37
5

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

Demo using java.time, the modern API:

import java.time.Instant;

public class Main {
    public static void main(String[] args) {
        Instant createdOn = Instant.ofEpochMilli(1_622_800_000_000L);
        System.out.println(createdOn);

        Instant timeout = createdOn.plusMillis(50_000L);
        System.out.println(timeout);
    }
}

See this code run live at IdeOne.com.

Output:

2021-06-04T09:46:40Z
2021-06-04T09:47:30Z

An Instant represents an instantaneous point on the timeline in UTC. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

So, the solution to your problem would be:

Instant createdOn = token.getCreatedOn().toInstant();
Instant timeout = createdOn.plusMillis(token.getExpiresIn());

Check documentation of Date#toInstant to learn more about it.

Learn more about java.time, 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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • 2
    This is the answer everyone should want. You may want to reinforce the point that a `long` value can be added by demonstrating that `50_000L` and even `5_000_000_000L` can be added. – Ole V.V. Jun 04 '21 at 20:21
2

Calendar is a fairly expensive Date object, and it functionality is not the best. If you want an all purpose Date object I suggest looking at JODA Time and it has a function which does what you want.

However, a simpler Java Date object is the Date class as @Dubas indicates.

Even simpler for the type of operation you are performing is to use a long. This is also much faster.

long timeoutMS = token.getCreatedOn() + token.getExpiresIn(); 

I use long (in GMT) for all my dates and only use Date for the presentation layer. ie. when you want to convert the date to Text.

reevesy
  • 3,452
  • 1
  • 26
  • 23
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130