I have simple question, I have the following function and there is argument on it that called cacheTime, How can I set it to 4 hours, should I set it to 4 * 3600000?
public static File getCache(String name, Context c, int cacheTime)
{
if (cacheTime <= 0)
return null;
File cache = new File(c.getCacheDir(), name);
long now = System.currentTimeMillis();
if (cache.exists() && (now - cache.lastModified() < cacheTime))
return cache;
return null;
}
miliseconds are 1/1000 of a second. So 4 hours would be 4 * 60 * 60 * 1000 = 14,400,000
For cache invalidation this is probably fine. That said, date math is often dangerous. When dealing with larger units of time than milliseconds one can easily get tripped up during daylight savings transitions, leap seconds and all the other stuff that Calendar is meant to take care of. In some cases that rare imprecision is acceptable, and in others it's not. Be careful when doing date math.
For determining human consumable times in larger units of time such as +1 days, use Calendar.roll().
Learn to use the handy TimeUnit enum so you can do things like so:
TimeUnit.Hours.toMillis(4)
And not rely on napkin math and magic numbers all over your code.
// 4 hours * 60 (min/hour) * 60 (sec/min) * 1000 (msec/sec)
getCache(name, c, 4 * 3600 * 1000);
4 * 1000 * 3600
There are 1000 milliseconds in a second and 3600 seconds in an hour.
Related
I'm trying to calculate how many milliseconds between two times (for example between 13:00 to 13:01 there are 60000 milliseconds).
The times are represented by 2 integers (hour, minute).
I wrote this function:
public static long millisBetweenTimes(int h1, int m1, int h2, int m2) { //hour1, minute1, hour2, minute2
long millis;
millis = (h2 - h1) * (60 * 60000);
if (m < tm)
millis += (m2 - m1) * 60000;
else
millis -= (m1 - m2) * 60000;
return millis;
}
However, this won't work when the second time is the day after (e.g. how many milliseconds between 14:00 Sunday to 13:00 Monday?)
As Robby already said in the comments, you should use classes from the java.time package. With the classes LocalTime and Duration, you could get the milliseconds between two points in time.
LocalTime t0 = LocalTime.of(14, 0);
LocalTime t1 = LocalTime.of(13, 0);
Duration d = Duration.between(t0, t1);
if (d.isNegative()) {
d = d.plusDays(1);
}
System.out.println(d.toMillis());
A Duration is a, well, duration: the amount of time between two points in time. If the second time lies before the first, then the duration is negative. In such case, we need to add 1 day to the duration.
Now we have an amount of time represented by the Duration class. This class contains many methods to convert it to a certain time unit. In our case, toMillis() is exactly what we need.
Online demo
Instead of d.isNegative(), you can also use t1.isBefore(t0), if you think it's more expressive.
Note: I think this is not as half as clumsy as doing the math yourself.
Your approach would only work in a single 24 hour cycle as you are passing in two hour integers and subtracting them. So if you are calculating the amount of milliseconds from 13:00 to 14:00 tomorrow the second time input needs to be 25:00 as 24 hours have passed. Another way you can approach this is by using java dates and taking out the hour from the day you want to start and finish.
I am storing two DateTimes (Joda) in an object and then I get a Period from the object by new Period(dateTime1, dateTime2).
I then want to add all the periods from different objects together.
I am both adding all the periods together in a variable and summing up some periods in smaller periods stored in a HashMap<long, Period>.
The result and issue is this.
The first period gets "2 hours and 30 minutes" with a PeriodFormat.getDefault().print(p) (the values are the same if i concatenate getHours and getMinutes).
The second value "5 hours and 52 minutes". So far so good.
But when I do it with the 3rd and 4th, the minutes stop converting to hours.
"5 hours and 103 minutes"
"8 hours and 132 minutes"
It should be 10h and 12m, but as you can see. That's not what I am getting. What is the issue? How can Period just forget to do the conversion? I don't have any problems with the selected sums, yet.
code: (with variable names changed)
mainSum= new Period();
tasksSum= new HashMap<Long, Period>();
for(Entry entry: entries){
long main_id= entry.getMain_id();
long task_id = entry.getTask_id();
Period entryPeriod = entry.getPeriod();
if(main_id == mainStuff.getId()){
mainSum = entryPeriod.plus(mainSum);
Timber.d("mainSum: " + PeriodFormat.getDefault().print(mainSum));
Timber.d("sum of workplace: " + mainSum.getHours() + " : " + mainSum.getMinutes());
Period taskPeriod = tasksPeriodSums.remove(task_id);
if(taskPeriod == null){
tasksPeriodSums.put(task_id, entryPeriod);
} else {
tasksPeriodSums.put(task_id, taskPeriod.plus(entryPeriod));
}
}
}
Please help, thank you :)
This is documented behaviour, check out the Javadoc for the plus(Period) function:
/**
* Returns a new period with the specified period added.
* <p>
* Each field of the period is added separately. Thus a period of
* 2 hours 30 minutes plus 3 hours 40 minutes will produce a result
* of 5 hours 70 minutes - see {#link #normalizedStandard()}.
* <p>
...
Drilling down into the Javadoc of the normalizedStandard(..) function itself, we see what's the tradeoff:
/**
* Normalizes this period using standard rules, assuming a 12 month year,
* 7 day week, 24 hour day, 60 minute hour and 60 second minute,
*
...
* However to achieve this it makes the assumption that all years are
* 12 months, all weeks are 7 days, all days are 24 hours,
* all hours are 60 minutes and all minutes are 60 seconds. This is not
* true when daylight savings time is considered, and may also not be true
* for some chronologies.
...
I'm wanting to have my JLabel display values in the format of HH:mm:ss without making use of any external libraries. (the label will update every second)
So for example, the following input in seconds and the desired output are below:
Seconds: Output:
--------------------------------------------------
long seconds = 0 00:00:00
long seconds = 5 00:00:05
long seconds = 500 00:08:20
long seconds = 5000 01:23:20
Note: the seconds value is of type long
I'm aware that typically one would just do the following conversions to get the desired numbers:
long s = 5000; //total seconds
long hrs = (s / 3600) //hours
long mins = ((s%3600)/60) //minutes
long secs = (s%60) //seconds
However, this leaves decimals on the values. Perhaps there is some sort of formatting that will allow me to toss the un-needed decimals.
Options I have come across were String.format(), SimpleDateFormat(), or concatenating a string myself.
The thing is, I will be updating this JLabel every second and sometimes it can count to the equivalent of 5-6 days if not longer.
So I'm looking for someone who has more experience in the area than I, and knows the most efficient way to tackle this issue.
I would use SimpleDateFormat if I were you.
If SDF is too slow for you, profile all your options and pick the fastest one, then refactor the rest of your code until it's fast enough.
Remember that premature optimization is the root of all evil, and that you should only really do any optimizing after you've profiled your code and missed your target execution time.
SimpleDateFormat() is really quite appropriate for your needs.
Use the TimeUnit class, as shown here in combination with the javax.swing.Timer class set to execute at 1 second intervals.
If you don't mind values wrapping then use SimpleDateFormat as follows. Remember x1000 to convert to milliseconds and to manually override the timezone.
long value = 5 * 24 * 3600 + 5000;
// wrapping solution
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
// ensure no daylight saving +1 hour
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(sdf.format(value * 1000));
Output
01:23:20
If you want the hours to go past 23.59.59 then this is the simplest I could come up with. I used DecimalFormat to force at least 2 digits for the hours.
long value = 5 * 24 * 3600 + 5000;
long hours = value / 3600; // whole hours
long mins = value / 60 - hours * 60;
long secs = value % 60;
System.out.println(String.format("%s:%2d:%2d",
new DecimalFormat("00").format(hours), mins, secs));
Output
121:23:20
I've found this to be extremely fast. Try it out. Seconds go from 0 - 59, minutes go from 0 - 59, hours go from 0 - 2,562,047,788,015. Afterwards the hours become negative and begin going towards that maximum.
performing the "+" operator on Strings is very slow. A StringBuilder performs grouping strings together the fastest from what I've seen. You should also be using "chars" not "String/Byte" Bytes are very slow as well. I'd prefer doing only multiplication however dividing by 36 and 6 give decimals that are to large for holding.
StringBuilder sb = new StringBuilder(8);
long hours = time / 3600000;
long minutes = (time - hours * 3600000) / 60000;
long seconds = (time - hours * 3600000 - minutes * 60000) / 1000;
if (hours < 10)
sb.append('0');
sb.append(hours);
sb.append(':');
if (minutes < 10)
sb.append('0');
sb.append(minutes);
sb.append(':');
if (seconds < 10)
sb.append('0');
sb.append(seconds);
String formattedTime = sb.toString();
.....
If you don't want to use a formatter class, you can get your work done by using basic operations like conversion among wrapper classes and String operations. Take a look at this code:
long h, m, s; // Initialize them after calculation.
String h1, m1, s1;
h1 = Long.toString( h );
m1 = Long.toString( m );
s1 = Long.toString( s );
if ( s1.length() < 2 )
s1 = "0" + s1;
if ( m1.length() < 2 )
m1 = "0" + m1;
if ( h1.length() < 2 )
h1 = "0" + h1;
String output = h1+":"+m1+":"+s1;
Supposing you have correctly calculated values of seconds, minutes and hours, you can gather String versions of these variables, then format them with a simple length check and finally concatenate these time unit parts.
i think you want to do the math you indicated, but take the floor of each value. then concatenate..
public class Test{
public static void main(String args[]){
double d = -100.675;
float f = -90;
System.out.println(Math.floor(d));
System.out.println(Math.floor(f));
System.out.println(Math.ceil(d));
System.out.println(Math.ceil(f));
}
}
I want to create a function that will convert the days into milliseconds. The days format is stored as 0.2444, so how to convert this to milliseonds?
The best practice for this, in my opinion is:
TimeUnit.DAYS.toMillis(1); // 1 day to milliseconds.
TimeUnit.MINUTES.toMillis(23); // 23 minutes to milliseconds.
TimeUnit.HOURS.toMillis(4); // 4 hours to milliseconds.
TimeUnit.SECONDS.toMillis(96); // 96 seconds to milliseconds.
In addition to the other answers, there is also the TimeUnit class which allows you to convert one time duration to another. For example, to find out how many milliseconds make up one day:
TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS); //gives 86400000
Note that this method takes a long, so if you have a fraction of a day, you will have to multiply it by the number of milliseconds in one day.
Won't days * 24 * 60 * 60 * 1000 suffice?
24 hours = 86400 seconds = 86400000 milliseconds. Just multiply your number with 86400000.
Its important to mention that once in 4-5 years this method might give a 1 second error, becase of a leap-second (http://www.nist.gov/pml/div688/leapseconds.cfm), and the correct formula for that day would be
(24*60*60 + 1) * 1000
There is a question Are leap seconds catered for by Calendar? and the answer is no.
So, if You're designing super time-dependant software, be careful about this formula.
public static double toMilliSeconds(double day)
{
return day * 24 * 60 * 60 * 1000;
}
or as long:
public static long toMilliSeconds(double day)
{
return (long) (day * 24 * 60 * 60 * 1000);
}
You can use this utility class -
public class DateUtils
{
public static final long SECOND_IN_MILLIS = 1000;
public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
}
If you are working on Android framework then just import it (also named DateUtils) under package android.text.format
int day = 5;
long dayInMilliseconds = day * org.apache.commons.lang.time.DateUtils.MILLIS_PER_DAY
Date nowdate = new Date();
long nowms = nowdate.getTime();
long differencems = numdaysback * 24 * 60 * 60 * 1000;
long thenms = nowms - differencems;
Date thendate = new Date(thenms);
If numdaysback is 365, then I would suppose that thendate would be one year ago. but it's not... it's about three weeks ago?!?
NUMDAYSBACK: 365
NOWDATE: Wed Jun 22 20:31:58 SGT 2011
NOWMS: 1308745918625
DIFFERENCEMS: 1471228928
THENMS: 1307274689697
THENDATE: Sun Jun 05 19:51:29 SGT 2011
How about:
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -1);
Date thendate = cal.getTime();
Returns the same time of day regardless of DST or leap years, is shorter and clearer...
Generally Calendar is the way to go in such cases (unless you use a 3rd party library like Joda Time). You can use it for all kinds of calculations: add N days/hours/months/seconds, truncate time to a whole hour etc. - stuff that would be too much pain with long only.
Regarding your original question, it seems to be a victim of integer overflow. It works if the multiplication explicitly uses long:
long differencems = 365 * 24 * 60 * 60 * 1000L;
If you are using Java 8 and up, you can use the newer java.time library to do this a bit more cleanly.
Date xDaysAgo = Date.from( Instant.now().minus( Duration.ofDays( x ) ) );
Just try this:
long differencems = numdaysback * 24L * 60 * 60 * 1000;
With the new code you will not loose the digits due to integer multiplication.
Since we have marked the literal 24 as long, the multiplication will be done by auto converting the first operand numdaysback into long. The rest of the multiplication will also be done on long operands.
This line:
long differencems = numdaysback * 24 * 60 * 60 * 1000;
the RHS should be: 31536000000. You have something much less, the reason being the RHS is being evaluated as an int (as all the quantities are ints), and you are exceeding MAX_INT. To correct to this:
long differencems = numdaysback * 24 * 60 * 60 * 1000l;
Note the "l" which makes 1000 be a long - now the RHS will be evaluated as a long.
The Date class is (informally) deprecated. The API has so many faults, that it is really difficult to get Dates/Times right with it. The easiest example is something like your code for differencems. It fails, if the time inbetween contains a daylight savings switch (if you don't use UT) and will always fail to take care of leap seconds.
If your application depends on correct dates, you might want to use Joda Time.