I've been trying to make a cooldown system in java given three variables,
the cooldown time, for example 100 seconds,
the click timestamp(when it was last clicked)
the current timestamp.
I made it work before but it looks very complicated, I was wondering if there is a better way to do it.
My code to check if the click is on cooldown or not:
private boolean onCooldown(String playerUUID, int npcId, int cooldown) {
boolean toReturn = false;
try {
String timeStamp = new SimpleDateFormat("yyyy/MM/dd_HH:mm:ss").format(Calendar.getInstance().getTime());
DateFormat df = new SimpleDateFormat("yyyy/MM/dd_HH:mm:ss");
Date savedTime;
Date nowTime;
String savedString = database.getTime(playerUUID, npcId);
nowTime = df.parse(timeStamp);
savedTime = df.parse(savedString);
long diff = nowTime.getTime() - savedTime.getTime();
long savedSeconds = diff / 1000 % 60;
long savedMinutes = diff / (60 * 1000) % 60;
long nowSeconds = cooldown % 60;
long nowMinutes = cooldown / 60 % 60;
if (!((savedMinutes >= nowMinutes) && (savedSeconds >= nowSeconds))) {
toReturn = true;
}
} catch (Exception e) {
toReturn = false;
}
return toReturn;
}
Is there a better or easier way than this?
Cheers.
I'd recommend using a HashMap that stores the player's UUID and the current timestamp. Within the method you can then check if the map contains the player's key and if so check if the value stored and your desired cooldown is less/greater than the current time.
As Craigr8806 stated in the comments, I'd recommend using System.nanoTime() for the timestamps.
Related
I want to calculate the total running time of my program from start to end and refresh running time in JFrame, but when I run my program I get excess 70 years, 1 day and 2 hours. Why ? What wrong ?
private void setMachineTime(){
Timer timer = new Timer();
long startTime = new Date().getTime();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
long endTime = new Date().getTime();
long diffTime = endTime - startTime ;
String time = new SimpleDateFormat("yy:mm:dd:HH:mm:ss").format(diffTime);
System.out.println(time);
}
}, 0, 1000);
}
actual result
UPD:
I rewrote code with my own format time method. Now I got what I want. Thanks to all of you.
private void setMachineTime(){
Timer timer = new Timer();
long startTime = new Date().getTime();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
long endTime = new Date().getTime();
long diffTime = endTime - startTime;
String diffSeconds = formatTime(diffTime / 1000 % 60);
String diffMinutes = formatTime(diffTime / (60 * 1000) % 60);
String diffHours = formatTime(diffTime / (60 * 60 * 1000) % 24);
System.out.println(diffHours + ":" + diffMinutes + ":" + diffSeconds);
}
}, 0, 1000);
}
private String formatTime(long diff){
long t;
t = diff;
if(t < 10){
return String.valueOf("0"+t);
} else {
return String.valueOf(t);
}
}
You are formatting the time difference as yy:mm:dd:HH:mm:ss. Just printing out diffTime would give you the milliseconds, divide by 1000 if you need seconds.
EDIT: I think i see what you are trying to do, but you are dealing with a time interval, which cannot be formatted as a date. You'll need to roll your own formatting for displaying the time as seconds, minutes, hours etc. or use an external library.
getTime return number of milliseconds from 1.1.1970...and same is for SimpleDateFormat converting number to date (and then formating it). It means when your diffTime = 0, SimpleDateFormat will try to format Date 1.1.1970 0:00:00 and with your formating string it will be 70:01:01:00:00:00. Try to use http://joda-time.sourceforge.net/api-release/org/joda/time/Interval.html instead.
And by the way, your formating string is wrong anyway...you use mm where I supouse you wanted month...but mm are minutes.
I want to add string time with format HH:mm:ss and special hour field. Example :
"20:15:30" (string) add "13:50:35" (string) -> result i want : "34:06:05" (string).
I have search similar code :
String time1="20:15:30";
String time2="13:50:35";
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
timeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = timeFormat.parse(time1);
Date date2 = timeFormat.parse(time2);
long sum = date1.getTime() + date2.getTime();
String date3 = timeFormat.format(new Date(sum));
System.out.println("The sum is "+ date3);
And result of above code : The sum is 10:06:05 not i want. How is easy way to do this ?
You could simply take advantage of either Java 8's or Joda Time's duration capabilities.
For example, this simply creates a duration which is the sum of the number of seconds of the two times
LocalTime lt1 = LocalTime.parse("20:15:30", DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime lt2 = LocalTime.parse("13:50:35", DateTimeFormatter.ofPattern("HH:mm:ss"));
//long t = lt1.toSecondOfDay() + lt2.toSecondOfDay();
//Duration duration = Duration.ofSeconds(t);
Duration duration = Duration.between(lt2, lt1);
System.out.println(formatDuration(duration));
Which prints out 34:06:05
formatDuration method
public static String formatDuration(Duration duration) {
long hours = duration.toHours();
duration = duration.minusHours(hours);
long minutes = duration.toMinutes();
duration = duration.minusMinutes(minutes);
long seconds = duration.getSeconds();
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
SimpleDateFormat can't do that, but you can do it yourself, by parsing the input with a regular expression, and formatting the output with the format method.
private static String addTime(String ... times) {
if (times.length < 2)
throw new IllegalArgumentException("At least 2 times are required");
Pattern timePattern = Pattern.compile("([0-9]+):([0-5][0-9]):([0-5][0-9])");
// Parse times and sum hours, minutes, and seconds
int hour = 0, minute = 0, second = 0;
for (String time : times) {
Matcher m = timePattern.matcher(time);
if (! m.matches())
throw new IllegalArgumentException("Invalid time: " + time);
hour += Integer.parseInt(m.group(1));
minute += Integer.parseInt(m.group(2));
second += Integer.parseInt(m.group(3));
}
// Handle overflow
minute += second / 60; second %= 60;
hour += minute / 60; minute %= 60;
// Format and return result
return String.format("%02d:%02d:%02d", hour, minute, second);
}
Test
System.out.println(addTime("20:15:30", "13:50:35"));
System.out.println(addTime("20:15:30", "13:50:35", "20:15:30", "13:50:35"));
System.out.println(addTime("98765:43:21", "12:34:56"));
Output
34:06:05
68:12:10
98778:18:17
So i'm trying to get a random time between two set times. But the resulting date is not what I'm expecting.
I'm expecting a result that is within the two dates I give as the earliest and the latest, but I get a date thats on the next day, and it appears as though if I take the time i'm supposed to get and subtract 12 I get this answer.
This is the log get: http://prntscr.com/6205yh
private long nextLong(Random rng, long n) {
long bits, val;
do
{
bits = (rng.nextLong() << 1) >>> 1;
val = bits % n;
} while (bits - val + (n - 1) < 0L);
return val;
}
#SuppressWarnings("deprecation")
public Calendar getNextDate() {
try
{
Calendar now = Calendar.getInstance(Locale.getDefault());
String earliest = getConfig().getString("Date.Spawn Earliest");
String latest = getConfig().getString("Date.Spawn Latest");
// Format the hours and minutes into dates
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
Date earliestDate = format.parse(earliest);
Date latestDate = format.parse(latest);
// Figure out the random time between the two
long e = earliestDate.getTime();
long l = latestDate.getTime();
long d = nextLong(new Random(), l - e) + e;
Date date = new Date(d);
// Update the hours and minutes into a new Calander with todays day,month and year.
Calendar then = Calendar.getInstance(Locale.getDefault());
System.out.println(date.getHours()+":"+date.getMinutes());
then.set(Calendar.HOUR, date.getHours());
then.set(Calendar.MINUTE, date.getMinutes());
// If it is later then the random time and nows hours are still higher then the latest time; add 7 days to get next week
if (now.after(then) && now.getTime().getHours() > latestDate.getHours())
then.add(Calendar.DAY_OF_MONTH, 7);
System.out.println("At the moment it is: " + now.getTime().toString());
System.out.println("Dragon will spawn at: " + then.getTime().toString());
return then;
}
catch (ParseException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}`
If someone could explain to me whats going on I would be very grateful.
This could be better :)
Date dateStart;
Date dateEnd;
int diff = (int) (dateEnd.getTime() - dateStart.getTime());
Date randomDate = new Date(dateStart.getTime()
+ new Random(System.currentTimeMillis()).nextInt(diff));
Calendar then = Calendar.getInstance();
then.setTime(randomDate);
I have a program that collects information from the database.
in my previous question i asked about an execption and one of the reponses made me rethink the idea of my loop Link to the old question: ConcurrentModificationExecption
So here is the background i collest alot of information from the database one of the information is the name of the call type that is stored in my Database for example: Mail, Telefon ect. would be different type of contact in formation (which we call, CallQueues)
Since the information that i am extracting is over several days many of the call types will have dublicates. The following is an example of how a row in the database looks like:
ID Name date NoC NoAC
1 Mail 2012-11-27 3 3
Where NoC = number of calls
and NoAC = number of answered calls.
Now to my question.
My original idea was to loop through the list of queues and see if the name reapeared however this would not work as i could not change the list while looping through it. So here is my new idea starting from the while loop, what i want to know is: Is this the best way to avoid dublicates in this situation and if not could you please explain to me how i should do it?
** CODE **
ArrayList<CallQueue> queues = new ArrayList<>();
while (query.next()) {
boolean isNew = true;
if (!queues.isEmpty()) {
for (CallQueue callQueue : queues) {
if (callQueue.getType().equals(query.getString("NAME"))) {
double decimalTime = query.getDouble("DATE");
int hourOfDay = (int)Math.round(24 * decimalTime);
int callAmount = query.getInteger("NoC");
if (hourOfDay > 19) {
hourOfDay = 19;
}
callQueue.addCallsByTime(hourOfDay, callAmount);
isNew = false;
}else {
isNew = true;
}
}
/* Out side the foreach loop, checks if the boolean isNew is true if it is create a new object and insert into the list*/
if (isNew) {
String queueName = query.getString("NAME");
if (!queueName.equalsIgnoreCase("PrivatOverflow")) {
CallQueue cq = new CallQueue(query.getString("NAME"));
double decimalTime = query.getDouble("DATE");
int hourOfDay = (int)Math.round(24 * decimalTime);
int callAmount = query.getInteger("NoC");
if (hourOfDay > 19) {
hourOfDay = 19;
}
cq.addCallsByTime(hourOfDay, callAmount);
queues.add(cq);
}
}
/* if queues is empty which it will be the first time*/
}else {
String queueName = query.getString("NAME");
if (!queueName.equalsIgnoreCase("PrivatOverflow")) {
CallQueue cq = new CallQueue(query.getString("NAME"));
double decimalTime = query.getDouble("DATE");
int hourOfDay = (int)Math.round(24 * decimalTime);
int callAmount = query.getInteger("NoC");
if (hourOfDay > 19) {
hourOfDay = 19;
}
cq.addCallsByTime(hourOfDay, callAmount);
queues.add(cq);
}
}
}
Explanation
Don't make queues an ArrayList, make it a HashSet or some other sort of Set. This will catch duplicates for you in O(1) time instead of O(n). Once you're done loading the query, you can always take the data out of the HashSet and put it into an ArrayList for future purposes (which has the added advantage of you will know exactly how long to make the ArrayList. It would be an O(n) copy, but you'd only have to do it once instead of every time like the other way.
To do this correctly, you will probably have to override CallQueue.hashCode() and .equals(), but that's easy enough, just return the .hashCode() and .equals() methods of the String name field.
Bug??
By the way, I assumed that query is a java.sql.ResultSet, but ResultSet doesn't have getInteger, it has getInt. Does your code compile?
The Code
Here's the code, to see what I mean:
HashMap<String, CallQueue> queues = new HashMap<String, CallQueue>();
while (query.next()) {
if (!queues.isEmpty()) {
if (queues.containsKey(query.getString("NAME"))) {
CallQueue oldQueue = queues.get(query.getString("NAME"));
double decimalTime = query.getDouble("DATE");
int hourOfDay = (int)Math.round(24 * decimalTime);
int callAmount = query.getInt("NoC");
if (hourOfDay > 19) {
hourOfDay = 19;
}
oldQueue.addCallsByTime(hourOfDay, callAmount);
} else {
String queueName = query.getString("NAME");
if (!queueName.equalsIgnoreCase("PrivatOverflow")) {
CallQueue cq = new CallQueue(query.getString("NAME"));
double decimalTime = query.getDouble("DATE");
int hourOfDay = (int)Math.round(24 * decimalTime);
int callAmount = query.getInt("NoC");
if (hourOfDay > 19) {
hourOfDay = 19;
}
cq.addCallsByTime(hourOfDay, callAmount);
queues.put(query.getString("NAME"), cq);
}
}
}
}
// you could return this if you just want a collection...
Collection<CallQueue> values = queues.values();
// Or this if you MUST have an ArrayList...
return new ArrayList(values);
You can also do this in sql using group by name or select distinct.
Please include the nanos, otherwise it would be trivial:
long diff = Math.abs(t1.getTime () - t2.getTime ());
[EDIT] I want the most precise result, so no doubles; only integer/long arithmetic. Also, the result must be positive. Pseudo code:
Timestamp result = abs (t1 - t2);
Examples:
t1 = (time=1001, nanos=1000000), t2 = (time=999, nanos=999000000)
-> diff = (time=2, nanos=2000000)
Yes, milliseconds in java.sql.Timestamp are duplicated in the time and the nanos par, so 1001 milliseconds means 1 second (1000) and 1 milli which is in the time part and the nanos part because 1 millisecond = 1000000 nanoseconds). This is much more devious than it looks.
I suggest not to post an answer without actually testing the code or having a working code sample ready :)
After one hour and various unit tests, I came up with this solution:
public static Timestamp diff (java.util.Date t1, java.util.Date t2)
{
// Make sure the result is always > 0
if (t1.compareTo (t2) < 0)
{
java.util.Date tmp = t1;
t1 = t2;
t2 = tmp;
}
// Timestamps mix milli and nanoseconds in the API, so we have to separate the two
long diffSeconds = (t1.getTime () / 1000) - (t2.getTime () / 1000);
// For normals dates, we have millisecond precision
int nano1 = ((int) t1.getTime () % 1000) * 1000000;
// If the parameter is a Timestamp, we have additional precision in nanoseconds
if (t1 instanceof Timestamp)
nano1 = ((Timestamp)t1).getNanos ();
int nano2 = ((int) t2.getTime () % 1000) * 1000000;
if (t2 instanceof Timestamp)
nano2 = ((Timestamp)t2).getNanos ();
int diffNanos = nano1 - nano2;
if (diffNanos < 0)
{
// Borrow one second
diffSeconds --;
diffNanos += 1000000000;
}
// mix nanos and millis again
Timestamp result = new Timestamp ((diffSeconds * 1000) + (diffNanos / 1000000));
// setNanos() with a value of in the millisecond range doesn't affect the value of the time field
// while milliseconds in the time field will modify nanos! Damn, this API is a *mess*
result.setNanos (diffNanos);
return result;
}
Unit tests:
Timestamp t1 = new Timestamp (0);
Timestamp t3 = new Timestamp (999);
Timestamp t4 = new Timestamp (5001);
// Careful here; internally, Java has set nanos already!
t4.setNanos (t4.getNanos () + 1);
// Show what a mess this API is...
// Yes, the milliseconds show up in *both* fields! Isn't that fun?
assertEquals (999, t3.getTime ());
assertEquals (999000000, t3.getNanos ());
// This looks weird but t4 contains 5 seconds, 1 milli, 1 nano.
// The lone milli is in both results ...
assertEquals (5001, t4.getTime ());
assertEquals (1000001, t4.getNanos ());
diff = DBUtil.diff (t1, t4);
assertEquals (5001, diff.getTime ());
assertEquals (1000001, diff.getNanos ());
diff = DBUtil.diff (t4, t3);
assertEquals (4002, diff.getTime ());
assertEquals (2000001, diff.getNanos ());
I use this method to get difference between 2 java.sql.Timestmap
/**
* Get a diff between two timestamps.
*
* #param oldTs The older timestamp
* #param newTs The newer timestamp
* #param timeUnit The unit in which you want the diff
* #return The diff value, in the provided time unit.
*/
public static long getDateDiff(Timestamp oldTs, Timestamp newTs, TimeUnit timeUnit) {
long diffInMS = newTs.getTime() - oldTs.getTime();
return timeUnit.convert(diffInMS, TimeUnit.MILLISECONDS);
}
// Examples:
// long diffMinutes = getDateDiff(oldTs, newTs, TimeUnit.MINUTES);
// long diffHours = getDateDiff(oldTs, newTs, TimeUnit.HOURS);
In what units? your diff above will give milliseconds, Timestamp.nanos() returns an int, which would be in (millionths?) of a millisecond.So do you mean e.g.
(t1.getTime () + (.000001*t1.getNanos()) - (t2.getTime () + (.000001*t2.getNanos())
or am I missing something? Another question is do you need this level of precision? AFAIK the JVM isn't guaranteed to be precise at this level, I don't think it'd matter unless you're sure your datasource is that precise.
Building on mmyers code...
import java.math.BigInteger;
import java.sql.Timestamp;
public class Main
{
// 1s == 1000ms == 1,000,000us == 1,000,000,000ns (1 billion ns)
public final static BigInteger ONE_BILLION = new BigInteger ("1000000000");
public static void main(String[] args) throws InterruptedException
{
final Timestamp t1;
final Timestamp t2;
final BigInteger firstTime;
final BigInteger secondTime;
final BigInteger diffTime;
t1 = new Timestamp(System.currentTimeMillis());
Thread.sleep(20);
t2 = new Timestamp(System.currentTimeMillis());
System.out.println(t1);
System.out.println(t2);
firstTime = BigInteger.valueOf(t1.getTime() / 1000 * 1000).multiply(ONE_BILLION ).add(BigInteger.valueOf(t1.getNanos()));
secondTime = BigInteger.valueOf(t2.getTime() / 1000 * 1000).multiply(ONE_BILLION ).add(BigInteger.valueOf(t2.getNanos()));
diffTime = firstTime.subtract(secondTime);
System.out.println(firstTime);
System.out.println(secondTime);
System.out.println(diffTime);
}
}
(old code removed to shorten answer)
EDIT 2: New code:
public class ArraySizeTest {
public static void main(String[] args) throws InterruptedException {
Timestamp t1 = new Timestamp(System.currentTimeMillis());
t1.setNanos(t1.getNanos() + 60);
Thread.sleep(20);
Timestamp t2 = new Timestamp(System.currentTimeMillis());
t2.setNanos(t2.getNanos() + 30);
System.out.println(t1);
System.out.println(t2);
// The actual diff...
long firstTime = (getTimeNoMillis(t1) * 1000000) + t1.getNanos();
long secondTime = (getTimeNoMillis(t2) * 1000000) + t2.getNanos();
long diff = Math.abs(firstTime - secondTime); // diff is in nanos
System.out.println(diff);
System.out.println(Math.abs(t1.getTime() - t2.getTime()));
}
private static long getTimeNoMillis(Timestamp t) {
return t.getTime() - (t.getNanos()/1000000);
}
}
Output:
2009-02-24 10:35:15.56500006
2009-02-24 10:35:15.59600003
30999970
31
Edit 3: If you'd prefer something that returns a Timestamp, use this:
public static Timestamp diff(Timestamp t1, Timestamp t2) {
long firstTime = (getTimeNoMillis(t1) * 1000000) + t1.getNanos();
long secondTime = (getTimeNoMillis(t2) * 1000000) + t2.getNanos();
long diff = Math.abs(firstTime - secondTime); // diff is in nanoseconds
Timestamp ret = new Timestamp(diff / 1000000);
ret.setNanos((int) (diff % 1000000000));
return ret;
}
private static long getTimeNoMillis(Timestamp t) {
return t.getTime() - (t.getNanos()/1000000);
}
This code passes your unit tests.