1

I am trying to determine if a timestamp is older than 30 seconds, but for some reason it is coming back as older than 30 seconds when it hasn't even been a couple seconds.

Example: https://ideone.com/KLIIBz

public static Boolean cooldown(int id) {

    Calendar now = Calendar.getInstance();
    now.add(Calendar.SECOND, -secondsAgo);
    long timeAgo = now.getTimeInMillis();
    if ( cooldown.containsKey(id) ) {

        System.out.println(cooldown.get(id) + " | " + timeAgo);

        // Stored timestamp always older than timeAgo
        if ( cooldown.get(id) < timeAgo ) {

            cooldown.remove(id);

        } else {

            // This code should be executed, as I am running the function one after another from same UUID not even a second or two apart.
            return false;

        }

    }

    now = Calendar.getInstance();
    cooldown.put(id, now.getTimeInMillis());

    return true;

}
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • 1
    Please post a [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). Your code is not that, e.g. what is `target`, and how do you call `cooldown()`? – Andreas Apr 03 '17 at 03:38
  • All that code is really redundant. Only issue is why the timestamp is coming back older than it is. – JordanSasquatchMan Apr 03 '17 at 03:47
  • 1
    If the rest of the code is very redundant, then creating a minimal example should be easy, right? – D M Apr 03 '17 at 04:11
  • The millis `1491192189486` from the first line of value `timeAgo` is `2017-04-02 21:03:09.486 PDT`, *same as* the log time of `21:03:09`, i.e. *not* offset by -30 seconds. Are you ***sure*** the value of `secondsAgo` is `30`, at the time the code is executed, and not still the initial value of `0`? Try changing declaration to `private static final int secondsAgo = 30;`. – Andreas Apr 03 '17 at 04:20
  • See updated online example. `secondsAgo` is established with the class. So yes I am sure it is 30. I'm not sure how to store a timestamp (whatever type) and check if it is 30 seconds old as this doesn't work. I made this from various examples here subtracting time to get a time go. – JordanSasquatchMan Apr 03 '17 at 04:34
  • Rather than `Calendar` you are probably better off using the date and time classes in Java 8 if you can: `Instant` and `Duration`; or just `System.currentTimeMillis()` as ΦXocę 웃 Пepeúpa ツ suggests in an answer. – Ole V.V. Apr 03 '17 at 05:29
  • I take it that `cooldown` is a map? What is the type of the value you get from there? I was thinking if it was `Integer`, this would explain why the timestamp appears to be very old. – Ole V.V. Apr 03 '17 at 05:30
  • 2
    Just looked at your code on Ideone in the link. In that code, no instance of your class `Ideone` is ever created. So `secondsAgo` is never set to 30. @Andreas is right in his suspicion. – Ole V.V. Apr 03 '17 at 05:36
  • 1
    When I asked if you're **sure** the value of `secondsAgo` is `30`, you said yes, without even checking. I asked, because that was the only cause I could see that would give the result displayed, so I was fairly certain it was `0`, and as it turns out, I was right. Next time, **verify**, e.g. using a **debugger**. If you don't know how to debug, now is a great time to learn. It is an essential skill for a programmer. See [What is a debugger and how can it help me diagnose problems?](http://stackoverflow.com/q/25385173/5221149) – Andreas Apr 03 '17 at 14:08

5 Answers5

8

tl;dr

Duration
.between( then , Instant.now() )
.toNanos()
>
Duration
.ofSeconds( 30 )
.toNanos()

Using java.time

You are using troublesome old date-time classes that are now legacy, supplanted by the java.time classes.

The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant start = Instant.now();
…
Instant stop = Instant.now();

Capture the span of time in between the start and stop as a Duration.

Duration duration = Duration.between( start , stop );

Represent your limit of 30 seconds.

Duration limit = Duration.ofSeconds( 30 );

Compare by calling the Comparable method of Duration::compareTo.

Boolean exceededLimit = ( duration.compareTo( limit ) > 0 );

By the way, having a collection and a method both named cooldown is not helpful.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 2
    Rather than `compareTo()` I think I’d consider just `duration.getSeconds() < secondsAgo` (where `secondsAgo` must be 30). Of course, declaring `limit` a `Duration` tells the reader that this is what it is, so there are pros and cons. And it’s a detail. In any case the question is a fine example of where the `java.time` classes are helpful. – Ole V.V. Apr 03 '17 at 05:44
  • 2
    @OleV.V. Certainly a good suggestion. The `compareTo` syntax is awkward and non-intuitive. Really though, the `Duration` should have comparison methods. Something like `isEqual`, `longerThan`, `shorterThan`. – Basil Bourque Apr 13 '17 at 23:14
4

Try this:

public class JavaApplication11 
{
    public static void main(String[] args)
    {
        // TODO code application logic here
        Calendar now = Calendar.getInstance();
        now.add(Calendar.SECOND, -30);//Set Calendar 30 seconds in the past.

        System.out.println(has30SecsPassed(now));
    }

    static boolean has30SecsPassed(Calendar calendar)
    {
        Long numberOfMilliSec = Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis();
        double numberOfSecondsPassed = numberOfMilliSec / 1000.0;
        System.out.println("seconds passed: " + numberOfSecondsPassed);
        return numberOfSecondsPassed >= 30;
    }

}
SedJ601
  • 12,173
  • 3
  • 41
  • 59
2

This:

Calendar now = Calendar.getInstance();
now.add(Calendar.SECOND, -secondsAgo);
long timeAgo = now.getTimeInMillis();

is too much overhead or just getting the actual time stamp in your app... you can just call

System.currentTimeMillis();

and verify this condition between cooldown.get(id) and timeAgo

so they meet cooldown.get(id) - timeAgo < 30*1000 or not...

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
2

To judge from the code on Ideone that you link to, the problem is that secondsAgo is 0, not 30. You set it to 30 in the constructor, but since you never create an object from the class, the constructor is never executed. secondsAgo is a static field, so you can access it without an object and without it having been initialized. You just get the initial value that all int fields have, namely 0.

The output I see in Ideone certainly confirms what I am saying. In all lines of the form 1491194338577 | 1491194338578 the numbers are either identical or just 1 millisecond apart. Even though you have subtracted secondsAgo seconds from the Calendar object that you get timeAgo from, timeAgo is never anywhere near 30 seconds earlier than the timestamp that you put into the map a millisecond ago. So no, you are not subtracting 30 seconds; you are subtracting 0 seconds.

That said I still agree with Basil Bourque’s answer: if there is any way you can use Java 8, you should throw the Calendar class away and use Instant and Duration. You get much clearer and more readable code. Put Instant objects in your map instead of Longs and it’s suddenly self-explanatory what they are.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
-3

currentTimeMillis returns: the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC

Can you explain why you use it.

Fady Saad
  • 1,169
  • 8
  • 13
  • I don't know. You tell me. Again. I am trying to store, and check a timestamp (whatever type of timestamp) to see if it is older than 30 seconds, or whatever I define, even minutes. Many examples on SO use System.currentTimeMillis() for timestamps. Please post an answer to the question rather than a question. This could be saved for a comment. – JordanSasquatchMan Apr 03 '17 at 03:50
  • 2
    Asking for clarification should be in a comment. It is certainly not an answer. Wait until you have the required 50 rep for leaving comments. Until then, delete this answer to clear the negative rep I just gave it. – Andreas Apr 03 '17 at 04:12