Related
This question already has answers here:
How to format a duration in java? (e.g format H:MM:SS)
(22 answers)
Comparing time is incorrect when picking 12:00
(3 answers)
Closed 4 years ago.
I am trying to calculate the difference between two hours. Time format must be hh:mm:ss! I implement this code:
public static String timeDifference(long timeDifference1) {
long timeDifference = timeDifference1 / 1000;
int h = (int) (timeDifference / (3600));
int m = (int) ((timeDifference - (h * 3600)) / 60);
int s = (int) (timeDifference - (h * 3600) - m * 60);
return String.format("%02d:%02d:%02d", h, m, s);
}
public static void main(String[] args) throws ParseException {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String timeStart = sc.next();
String timeStop = sc.next();
char lol[]=timeStop.toCharArray();
if(lol[0]=='0' && lol[1]=='0'){
lol[0]='2';
lol[1]='4';
}
String tetx=String.valueOf(lol);
timeStop=tetx;
char kk[]=timeStart.toCharArray();
if(kk[0]=='0' && kk[1]=='0'){
kk[0]='2';
kk[1]='4';
}
String hhh=String.valueOf(kk);
timeStart=hhh;
SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
Date d1 = null;
Date d2 = null;
d1 = format.parse(timeStart);
d2 = format.parse(timeStop);
long diff;
if (d1.getTime() > d2.getTime()) {
diff = (int) (d1.getTime() - d2.getTime());
} else
diff = (int) (d2.getTime() - d1.getTime());
System.out.println(timeDifference(diff));
}
}
Input must be:
10:03:43 15:00:58
13:00:00 14:00:00
00:00:00 12:05:00
12:05:00 00:00:00
And output:
04:57:15
01:00:00
12:05:00
11:55:00
But i get
04:57:15
01:00:00
00:05:00
00:05:00
How can i fix this?
Don't reinvent the wheel. You can do all of this with the java.time package.
The logic becomes slightly less elegant once we have the requirement that a second value of 00:00:00 represents tomorrow. We need to use LocalDateTimes and potentially add a day:
private static String getDifference(final String first, final String second)
{
final LocalTime firstTime = LocalTime.parse(first, DateTimeFormatter.ISO_LOCAL_TIME);
final LocalTime secondTime = LocalTime.parse(second, DateTimeFormatter.ISO_LOCAL_TIME);
final LocalDateTime firstDateTime = LocalDateTime.of(LocalDate.now(), firstTime);
final LocalDateTime secondDateTime = LocalDateTime.of(
LocalDate.now().plusDays(second.equals("00:00:00") ? 1 : 0),
secondTime
);
final Duration diff = Duration.between(firstDateTime, secondDateTime).abs();
return String.format(
"%02d:%02d:%02d",
diff.toDaysPart() < 1 ? diff.toHoursPart() : 24,
diff.toMinutesPart(),
diff.toSecondsPart()
);
}
Call like so:
System.out.println(getDifference("12:05:00", "00:00:00"));
Sample output:
11:55:00
Please note that toMinutesPart and its sibling methods were added in JDK 9. The logic is fairly similar in JDK 8 but more verbose.
The answer by Michael is good (+1). Allow me to add that you don’t need to mention any formatter (though I also see the advantage of being explicit about the format) and you don’t need to invent an artificial and probably incorrect date to deal with the 24:00 issue.
LocalTime start = LocalTime.parse(timeStart);
LocalTime stop = LocalTime.parse(timeStop);
if (stop.isAfter(start)) { // the normal situation
System.out.println(formatDuration(Duration.between(start, stop)));
} else if (stop.equals(LocalTime.MIDNIGHT)) {
System.out.println(
formatDuration(Duration.between(start, stop).plusDays(1)));
} else {
System.out.println("End time " + timeStop + " was before start time " + timeStart);
}
I am assuming that the times are on the same date except that an end time of 00:00:00 would mean midnight at the end of the day (sometimes called 24.00 where I come from). If you need to calculate, say from 13:00 one day to 13:00 to the next day as 24 hours, just delete the second if condition and the last else block.
Feeding your example input gives the output you asked for:
04:57:15
01:00:00
12:05:00
11:55:00
As Michael mentions, the toMinutesPart and toSecondsPart methods were introduced in Java 9. For how to format the duration in earlier Java versions see my answer here.
What went wrong in your code?
To parse times on a 24 hour clock correctly (12:05:00, 13:00:00, 14:00:00, 15:00:58) you need to use uppercase HH for hour of day. Lowercase hh is for hour within AM or PM from 01 to 12 inclusive. When you don’t specify AM or PM, AM is used as default. So 10:03:43 is parsed as you expected. Funnily 15:00:58 is too even though there is no 15:00:58 AM. SimpleDateFormat just extrapolates. The trouble comes with 12:05:00 since 12:05:00 AM means 00:05:00. On my computer I got 23:55:00 (not 00:05:00, as you said you got). This is because you had first altered the start time into 24:00:00 and next calculated the time from 00:05:00 to 24:00:00, which is 23:55:00. Since you know which time is the start time and which is the end time, you probably shouldn’t swap them in the case where they seem to be in the wrong order. In your last example I got 23:55:00 too. What happens is the same except the times aren’t swapped since 00:05:00 is already before 24:00:00.
Please change hh to HH in SimpleDateFormat. This will give you result in 24 hour format. https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
You can add date to fix it:
import java.time.LocalTime;
import java.time.LocalDate;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class MyClass {
public static void main(String args[]) {
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss");
LocalTime timeStart = LocalTime.parse("00:00:00");
LocalTime timeStop = LocalTime.parse("12:05:00");
LocalDateTime dateTime1 = LocalDateTime.of(LocalDate.now(), timeStart);
LocalDateTime dateTime2 = LocalDateTime.of(LocalDate.now(), timeStop);
executeDifference(dateTime1, dateTime2);
LocalTime timeStart2 = LocalTime.parse("12:05:00");
LocalTime timeStop2 = LocalTime.parse("00:00:00");
LocalDateTime dateTime3 = LocalDateTime.of(LocalDate.now(), timeStart2);
LocalDateTime dateTime4 = LocalDateTime.of(LocalDate.now().plusDays(1), timeStop2);
executeDifference(dateTime3, dateTime4);
}
private static void executeDifference(LocalDateTime timeStart, LocalDateTime timeStop) {
Duration duration = Duration.between(timeStart, timeStop);
durationOutput(duration);
}
private static void durationOutput(Duration duration) {
long hours = duration.toHours();
long minutes = duration.toMinutes() - (hours * 60);
long seconds = duration.getSeconds() - (hours * 3600) - (minutes * 60);
System.out.println(timeUnitsOutput(hours) + ":" + timeUnitsOutput(minutes) + ":" + timeUnitsOutput(seconds));
}
private static String timeUnitsOutput(long units) {
return (units < 10) ? ("0" + units) : String.valueOf(units);
}
}
The output will be:
12:05:00
11:55:00
After subtracting time 1:10 and 2:15 using java I am getting the ouput as long which is in milliseconds -3900000. Then I convert this millisecond into time format, then the output produced is
Output : -01:-05:00.
The output I am expecting is like -01:05:00.
There should be only one negative sign in the output
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
public class TimeAddition {
public static void main(String args[]) throws ParseException {
String b = "1:10";
String a = "2:15";
// converting String time into dateTime
DateFormat sdf = new SimpleDateFormat("hh:mm");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date bTime = sdf.parse(b);
Date aTime = sdf.parse(a);
long totalTime = bTime.getTime() - aTime.getTime();
System.out.println("Total time in millisecond = " + totalTime);
long millis = totalTime;
String hms = String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis)
- TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis)
- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
System.out.println("Time format " + hms);
}
}
Output produced :
Total time in millisecond = -3900000
Time format -1:-5:00
Expected Time format = -01:05:00
You should do something like that:
you compute if you should add a sign (-) or not.
if the amount of time is negative, you get its absolute value (you can also use Maths.abs).
you format it with the sign before.
Which gives:
String sign = totalTime < 0 ? "-":"";
long millis = totalTime < 0 ? -totalTime:totalTime;
String hms = String.format("%s%02d:%02d:%02d", sign, TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis)
- TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis)
- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
Just Change this code :
TimeUnit.MILLISECONDS.toMinutes(millis)
- TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis))
to this code:
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)) - TimeUnit.MILLISECONDS.toMinutes(millis)
Date and SimpleDateFormat are both oldfashioned and unsuited for the purpose. They were intended for a point in time and its representation in a certain timezone, never for the length of a time interval. I would not be comfortable using them for the latter, or I would at the very least test thoroughly on computers running different time zones.
My suggestion is to use the new java.time package. Despite the fact discussed in the comments to this answer that it does not include a duration formatter.
String b = "1:10";
String a = "2:15";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("H:mm", Locale.ROOT);
LocalTime end = LocalTime.parse(b, dtf);
LocalTime start = LocalTime.parse(a, dtf);
Duration totalTime = Duration.between(start, end);
String sign;
if (totalTime.isNegative()) {
totalTime = totalTime.negated();
sign = "-";
} else {
sign = "";
}
long hours = totalTime.toHours();
totalTime = totalTime.minusHours(hours);
long minutes = totalTime.toMinutes();
totalTime = totalTime.minusMinutes(minutes);
long seconds = totalTime.getSeconds();
System.out.format("%s%d:%02d:%02d%n", sign, hours, minutes, seconds);
This prints:
-1:05:00
The price is we need to handle a negative interval explicitly. The good news is the library classes do all calculations for us: calculating the length of time and conversion to hours, minutes and seconds.
You can use this approach as well to get time difference easily.
public static void main(String[] args)
{
String time1 = "01:10:00";
String time2 = "02:15:00";
String finalDiff;
try{
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
Date date1 = format.parse(time1);
Date date2 = format.parse(time2);
long difference = date1.getTime() - date2.getTime();
Date d;
if(difference<0) {
d = new Date(-1*difference);
finalDiff = "-"+ format.format(d);
}
else{
d = new Date(difference);
finalDiff = format.format(d);
}
System.out.println(finalDiff);// gives the difference as required by you.
}catch(Exception e){}
}
I'm making a stop watch where I'm using Java's SimpleDateFormat to convert the number of milliseconds into a nice "hh:mm:ss:SSS" format. The problem is the hours field always has some random number in it. Here's the code I'm using:
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.SSS");
String strDate = sdf.format(millis);
return strDate;
}
If I take off the hh part then it works fine. Otherwise in the hh part it'll display something random like "07" even if the argument passed in (number of milliseconds) is zero.
I don't know much about the SimpleDateFormat class though. Thanks for any help.
Support for what you want to do is built in to the latest JDKs with a little known class called TimeUnit.
What you want to use is java.util.concurrent.TimeUnit to work with intervals.
SimpleDateFormat does just what it sounds like it does, it formats instances of java.util.Date, or in your case it converts the long value into the context of a java.util.Date and it doesn't know what to do with intervals which is what you apparently are working with.
You can easily do this without having to resort to external libraries like JodaTime.
import java.util.concurrent.TimeUnit;
public class Main
{
private static String formatInterval(final long l)
{
final long hr = TimeUnit.MILLISECONDS.toHours(l);
final long min = TimeUnit.MILLISECONDS.toMinutes(l - TimeUnit.HOURS.toMillis(hr));
final long sec = TimeUnit.MILLISECONDS.toSeconds(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min));
final long ms = TimeUnit.MILLISECONDS.toMillis(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min) - TimeUnit.SECONDS.toMillis(sec));
return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
}
public static void main(final String[] args)
{
System.out.println(formatInterval(Long.parseLong(args[0])));
}
}
The output will be formatted something like this
13:00:00.000
A shorter way to do this is to use the DurationFormatUtils class in Apache Commons Lang:
public static String formatTime(long millis) {
return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
Why not this ?
public static String GetFormattedInterval(final long ms) {
long millis = ms % 1000;
long x = ms / 1000;
long seconds = x % 60;
x /= 60;
long minutes = x % 60;
x /= 60;
long hours = x % 24;
return String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis);
}
Here's how I've done it, using only the standard JDK (this will work as far back as Java 1.1 by changing StringBuilder back to StringBuffer):
static public String formatMillis(long val) {
StringBuilder buf=new StringBuilder(20);
String sgn="";
if(val<0) { sgn="-"; val=Math.abs(val); }
append(buf,sgn,0,(val/3600000)); val%=3600000;
append(buf,":",2,(val/ 60000)); val%= 60000;
append(buf,":",2,(val/ 1000)); val%= 1000;
append(buf,".",3,(val ));
return buf.toString();
}
/** Append a right-aligned and zero-padded numeric value to a `StringBuilder`. */
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
tgt.append(pfx);
if(dgt>1) {
int pad=(dgt-1);
for(long xa=val; xa>9 && pad>0; xa/=10) { pad--; }
for(int xa=0; xa<pad; xa++ ) { tgt.append('0'); }
}
tgt.append(val);
}
This is the first bit of Joda work I've done where it seemed more tedious than the JDK support. A Joda implementation for the requested format (making a few assumptions about zero fields) is:
public void printDuration(long milliSecs)
{
PeriodFormatter formatter = new PeriodFormatterBuilder()
.printZeroIfSupported()
.appendHours()
.appendSeparator(":")
.minimumPrintedDigits(2)
.appendMinutes()
.appendSeparator(":")
.appendSecondsWithMillis()
.toFormatter();
System.out.println(formatter.print(new Period(milliSecs)));
}
Reviewing the other answers, I came up with this function...
public static String formatInterval(final long interval, boolean millisecs )
{
final long hr = TimeUnit.MILLISECONDS.toHours(interval);
final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
if( millisecs ) {
return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
} else {
return String.format("%02d:%02d:%02d", hr, min, sec );
}
}
Here is what's going on. When you pass milliseconds, that number is relative to Jan 1st, 1970. When you pass 0, it takes that date and converts it to your local time zone. If you are in Central time then that happens to be 7PM. If you run this then it all makes sense.
new SimpleDateFormat().format(0) => 12/31/69 7:00 PM
Edit, I think what you want to do is get elapsed time. For this I recommend using JodaTime which already does this pretty well. You do something like
PeriodFormatter formatter = new PeriodFormatterBuilder()
.appendHours()
.appendSuffix(" hour", " hours")
.appendSeparator(" and ")
.appendMinutes()
.appendSuffix(" minute", " minutes")
.appendSeparator(" and ")
.appendSeconds()
.appendSuffix(" second", " seconds")
.toFormatter();
String formattedText = formatter.print(new Period(elapsedMilliSeconds));
Here's another way to do it. Fully self-contained and fully backwards-compatible. Unlimited number of days.
private static String slf(double n) {
return String.valueOf(Double.valueOf(Math.floor(n)).longValue());
}
public static String timeSpan(long timeInMs) {
double t = Double.valueOf(timeInMs);
if(t < 1000d)
return slf(t) + "ms";
if(t < 60000d)
return slf(t / 1000d) + "s " +
slf(t % 1000d) + "ms";
if(t < 3600000d)
return slf(t / 60000d) + "m " +
slf((t % 60000d) / 1000d) + "s " +
slf(t % 1000d) + "ms";
if(t < 86400000d)
return slf(t / 3600000d) + "h " +
slf((t % 3600000d) / 60000d) + "m " +
slf((t % 60000d) / 1000d) + "s " +
slf(t % 1000d) + "ms";
return slf(t / 86400000d) + "d " +
slf((t % 86400000d) / 3600000d) + "h " +
slf((t % 3600000d) / 60000d) + "m " +
slf((t % 60000d) / 1000d) + "s " +
slf(t % 1000d) + "ms";
}
The format happens according to your local timezone, so if you pass 0, it assumes 0 GMT and then converts it in your local timezone.
tl;dr
LocalTime // Represents a time-of-day, without date and without time zone.
.ofNanoOfDay( // Convert a count of nanoseconds into a time-of-day in a generic 24-hour day, ignoring real-world anomalies such as Daylight Saving Time (DST).
Duration // Class that represents a span-of-time not attached to the timeline.
.of( milliseconds ) // Parse your count of milliseconds as a span-of-time.
.toNanos() // Extract total number of nanoseconds in this span-of-time.
) // Returns a `LocalDate` object.
.toString() // Generate text in standard ISO 8601 format for a time-of-day (*not* recommended by me).
java.time.Duration
The proper class to represent a span-of-time unattached to the timeline with a scale of hours-minutes-seconds is Duration. For a scale of years-months-days, use Period.
Duration d = Duration.of( milliseconds ) ;
ISO 8601 format
I suggest you avoid reporting a span-of-time using the time-of-day format of HH:MM:SS. I have seen the inherent ambiguity lead to misinterpretation and confusion in real-world business apps.
There is a standard for reporting such values, defined in ISO 8601: PnYnMnDTnHnMnS. The P marks the beginning while the T separates any year-month-day portion from any hour-minute-second portion.
The java.time classes use the ISO 8601 standard formats by default when parsing/generating strings. This includes the Duration class.
Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toString() ;
See this code run live at IdeOne.com.
PT1H23M45.678S
These ISO 8601 strings can be parsed as well as generated.
Duration d = Duration.parse( "PT1H23M45.678S" ) ;
Time-of-day format
But if you insist on use time-of-day format for your duration, you can put together such a sting by calling the to…Part methods on Duration object.
Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toHoursPart() + ":" + d.toMinutesPart() + ":" + d.toSecondsPart() + "." + TimeUnit.NANOSECONDS.toMillis( d.toNanosPart() ) ;
1:23:45.678
Or we could abuse the LocalTime class to create your string.
Duration d = Duration.ofMillis( 5_025_678L ) ;
long nanoOfDay = d.toNanos() ;
LocalTime localTimeBogus = LocalTime.ofNanoOfDay( nanoOfDay ) ;
String output = localTimeBogus.toString() ;
Again, you can see this code run live at IdeOne.com.
01:23:45.678
org.apache.commons.lang.time.DurationFormatUtils.formatDuration(stopWatch.getTime(), "HH:mm:ss")
What you are trying to format is a duration, not a Date-Time
You have a duration (no. of milliseconds elapsed) which is different from a Date-Time i.e. an instant on timeline. You use SimpleDateFormat to format a java.util.Date which represents an instant on the timeline. The difference can be better understood by the following examples:
This machine has been running since 2021-09-30'T'10:20:30.
This machine has been running for 3 days 4 hours 5 minutes.
The former represents an instant on timeline while the later represents a duration.
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*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API: I recommend you use java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.
Demo:
import java.time.Duration;
public class Main {
public static void main(String[] args) {
// A sample input
long millis = 123456789L;
Duration duration = Duration.ofMillis(millis);
// Default format
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format("%2d:%02d:%02d.%d", duration.toHours() % 24,
duration.toMinutes() % 60, duration.toSeconds() % 60, duration.toMillis() % 1000);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%2d:%02d:%02d.%d", duration.toHoursPart(), duration.toMinutesPart(),
duration.toSecondsPart(), duration.toMillisPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT34H17M36.789S
10:17:36.789
10:17:36.789
ONLINE DEMO
Learn more about 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. Android 8.0 Oreo already provides support for java.time.
Using a plain Java Calendar for intervals up to one day (24 hours) see my answer to the question: How to format time intervals in Java?
Variant: Up to 24 hours
Simple formatting for elapsed time less than 24h. Over 24h the code will only display the hours within the next day and won't add the elapsed day to the hours.
public static String formatElapsedTime(long milliseconds) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(milliseconds);
}
Missing features in sample code:
Eliminate the timezone with "UTC"
Use the 24h format "HH"
Variant: Over 24 hours
public static String formatElapsedTimeOver24h(long milliseconds) {
// Compiler will take care of constant arithmetics
if (24 * 60 * 60 * 1000 > milliseconds) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(milliseconds);
} else {
SimpleDateFormat sdf = new SimpleDateFormat(":mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// Keep long data type
// Compiler will take care of constant arithmetics
long hours = milliseconds / (60L * 60L * 1000L);
return hours + sdf.format(milliseconds);
}
}
This one actually works, but it seems like I'm tweaking the intent of the method :-).
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String strDate = sdf.format(millis - 3600000);
return strDate;
}
For those of you who really knows how this works you'll probably find some caveats.
You can use formatElapsedTime or formatSameDayTime methods in DateUtils class.
This method must work in all not too old versions of Java )
public static String formatNanoSeconds(long duration) {
StringBuilder sb = new StringBuilder(20);
long quotient = duration / 1_000_000;
long remainder = quotient % 1_000;
sb.insert(0, String.format(".%03d", remainder));
quotient = quotient / 1_000;
remainder = quotient % 60;
sb.insert(0, String.format(":%02d", remainder));
quotient = quotient / 60;
remainder = quotient % 60;
sb.insert(0, String.format(":%02d", remainder));
quotient = quotient / 60;
remainder = quotient % 24;
sb.insert(0, String.format(" %02d", remainder));
quotient = quotient / 24;
sb.insert(0, String.format("%d", quotient));
return sb.toString();
}
StringBuilder.insert(), though fulfills array copy, must still work better than string concatenation.
And here's the test:
#Test
void formatNanoSeconds() {
long m = 1_000_000;
assertEquals("0 00:00:00.000", formatNanoSeconds(0));
assertEquals("0 00:00:00.000", formatNanoSeconds(1));
assertEquals("0 00:00:00.000", formatNanoSeconds(999_999));
assertEquals("0 00:00:00.001", formatNanoSeconds(1_000_000));
assertEquals("0 00:00:00.999", formatNanoSeconds(999 * m));
assertEquals("0 00:00:01.000", formatNanoSeconds(1000 * m));
assertEquals("0 00:00:59.000", formatNanoSeconds(59_000 * m));
assertEquals("0 00:01:00.000", formatNanoSeconds(60_000 * m));
assertEquals("0 00:01:01.000", formatNanoSeconds(61_000 * m));
assertEquals("0 00:59:00.000", formatNanoSeconds(59 * 60_000 * m));
assertEquals("0 01:00:00.000", formatNanoSeconds(60 * 60_000 * m));
assertEquals("0 01:01:00.000", formatNanoSeconds(61 * 60_000 * m));
assertEquals("0 23:00:00.000", formatNanoSeconds(23 * 60 * 60_000 * m));
assertEquals("1 00:00:00.000", formatNanoSeconds(24 * 60 * 60_000 * m));
assertEquals("1 01:00:00.000", formatNanoSeconds(25 * 60 * 60_000 * m));
assertEquals("125 17:28:58.819", formatNanoSeconds(
((((125L * 24 * 3600) + (17 * 3600) + (28 * 60) + 58) * 1000) + 819) * m + 1));
assertEquals("90 08:05:04.009", formatNanoSeconds(
((((90L * 24 * 3600) + (8 * 3600) + (5 * 60) + 4) * 1000) + 9) * m + 358));
}
I am not able to get the correct Time duration.So can anyone please help me in finding the solution
//code
public static String getDateDifference(java.util.Date start, java.util.Date end) {
logger.info("Enter getDateDifference ");
Calendar startCal = Calendar.getInstance();
startCal.setTime(start);
Calendar endCal = Calendar.getInstance();
endCal.setTime(end);
int hourDiff = Math.abs(endCal.get(Calendar.HOUR_OF_DAY) - startCal.get(Calendar.HOUR_OF_DAY));
int minDiff = Math.abs(endCal.get(Calendar.MINUTE) - startCal.get(Calendar.MINUTE));
String diff = Integer.toString(hourDiff) + ":" + Integer.toString(minDiff);
logger.info("Date Difference : " + diff);
logger.info("Exit getDateDifference ");
return diff;
}
Won't this fail if the start is 23:59 and the end 00:01?
Instead just get the milliseconds from the two dates, subtract and then convert to hours and minutes.
long millis = end.getTime() - start.getTime();
long seconds = millis/1000L;
long hours = seconds/3600L;
long mins = (seconds % 3600L) / 60L;
If you can use JodaTime this becomes fairly trivial. Like so:
Period period = new Period( new DateTime( start ), new DateTime( end ), PeriodType.time() );
return period.getHours() + ":" + period.getMinutes();
why don't you do the following
1) convert your 2 Dates to a common unit (here hours)
2) calculate the difference
3) transform the result to a format you want (Date, string ...)
I don't have the code handy but it should be fairly straight forwards.
your method is open to small errors like forgetting to increment/decrement a day when you go above 24h or under 0h.
also it will be diffcult to maintain if you want to suddenly add minutes and seconds ...
hope this helps
Jason
I need to find the number of days between two dates: one is from a report and one is the current date. My snippet:
int age=calculateDifference(agingDate, today);
Here calculateDifference is a private method, agingDate and today are Date objects, just for your clarification. I've followed two articles from a Java forum, Thread 1 / Thread 2.
It works fine in a standalone program although when I include this into my logic to read from the report I get an unusual difference in values.
Why is it happening and how can I fix it?
EDIT :
I'm getting a greater number of days compared to the actual amount of Days.
public static int calculateDifference(Date a, Date b)
{
int tempDifference = 0;
int difference = 0;
Calendar earlier = Calendar.getInstance();
Calendar later = Calendar.getInstance();
if (a.compareTo(b) < 0)
{
earlier.setTime(a);
later.setTime(b);
}
else
{
earlier.setTime(b);
later.setTime(a);
}
while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
{
tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
difference += tempDifference;
earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
}
if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
{
tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
difference += tempDifference;
earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
}
return difference;
}
Note :
Unfortunately, none of the answers helped me solve the problem. I've accomplished this problem with the help of Joda-time library.
I would suggest you use the excellent Joda Time library instead of the flawed java.util.Date and friends. You could simply write
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;
Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34
I might be too late to join the game but what the heck huh? :)
Do you think this is a threading issue? How are you using the output of this method for example? OR
Can we change your code to do something as simple as:
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar1.set(<your earlier date>);
calendar2.set(<your current date>);
long milliseconds1 = calendar1.getTimeInMillis();
long milliseconds2 = calendar2.getTimeInMillis();
long diff = milliseconds2 - milliseconds1;
long diffSeconds = diff / 1000;
long diffMinutes = diff / (60 * 1000);
long diffHours = diff / (60 * 60 * 1000);
long diffDays = diff / (24 * 60 * 60 * 1000);
System.out.println("\nThe Date Different Example");
System.out.println("Time in milliseconds: " + diff
+ " milliseconds.");
System.out.println("Time in seconds: " + diffSeconds
+ " seconds.");
System.out.println("Time in minutes: " + diffMinutes
+ " minutes.");
System.out.println("Time in hours: " + diffHours
+ " hours.");
System.out.println("Time in days: " + diffDays
+ " days.");
}
The diff / (24 * etc) does not take Timezone into account, so if your default timezone has a DST in it, it can throw the calculation off.
This link has a nice little implementation.
Here is the source of the above link in case the link goes down:
/** Using Calendar - THE CORRECT WAY**/
public static long daysBetween(Calendar startDate, Calendar endDate) {
//assert: startDate must be before endDate
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
and
/** Using Calendar - THE CORRECT (& Faster) WAY**/
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
//assert: startDate must be before endDate
int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
long endInstant = endDate.getTimeInMillis();
int presumedDays =
(int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);
Calendar cursor = (Calendar) startDate.clone();
cursor.add(Calendar.DAY_OF_YEAR, presumedDays);
long instant = cursor.getTimeInMillis();
if (instant == endInstant)
return presumedDays;
final int step = instant < endInstant ? 1 : -1;
do {
cursor.add(Calendar.DAY_OF_MONTH, step);
presumedDays += step;
} while (cursor.getTimeInMillis() != endInstant);
return presumedDays;
}
java.time
In Java 8 and later, use the java.time framework (Tutorial).
Duration
The Duration class represents a span of time as a number of seconds plus a fractional second. It can count days, hours, minutes, and seconds.
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());
ChronoUnit
If all you need is the number of days, alternatively you can use the ChronoUnit enum. Notice the calculation methods return a long rather than int.
long days = ChronoUnit.DAYS.between( then, now );
import java.util.Calendar;
import java.util.Date;
public class Main {
public static long calculateDays(String startDate, String endDate)
{
Date sDate = new Date(startDate);
Date eDate = new Date(endDate);
Calendar cal3 = Calendar.getInstance();
cal3.setTime(sDate);
Calendar cal4 = Calendar.getInstance();
cal4.setTime(eDate);
return daysBetween(cal3, cal4);
}
public static void main(String[] args) {
System.out.println(calculateDays("2012/03/31", "2012/06/17"));
}
/** Using Calendar - THE CORRECT WAY**/
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
}
It depends on what you define as the difference. To compare two dates at midnight you can do.
long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;
Solution using difference between milliseconds time, with correct rounding for DST dates:
public static long daysDiff(Date from, Date to) {
return daysDiff(from.getTime(), to.getTime());
}
public static long daysDiff(long from, long to) {
return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}
One note: Of course, dates must be in some timezone.
The important code:
Math.round( (to - from) / 86400000D )
If you don't want round, you can use UTC dates,
Illustration of the problem: (My code is computing delta in weeks, but same issue applies with delta in days)
Here is a very reasonable-looking implementation:
public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;
static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
return deltaInWeeks;
}
But this test will fail:
public void testGetDeltaInWeeks() {
delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
assertEquals("weeks between Feb23 and Mar09", 2, delta);
}
The reason is:
Mon Mar 09 00:00:00 EDT 2009 = 1,236,571,200,000 Mon Feb 23
00:00:00 EST 2009 = 1,235,365,200,000 MillisPerWeek =
604,800,000 Thus, (Mar09 - Feb23) / MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994...
but anyone looking at a calendar would agree that the answer is 2.
I use this funcion:
DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days
my function:
import java.util.Date;
public long DATEDIFF(String date1, String date2) {
long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
long days = 0l;
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");
Date dateIni = null;
Date dateFin = null;
try {
dateIni = (Date) format.parse(date1);
dateFin = (Date) format.parse(date2);
days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;
} catch (Exception e) { e.printStackTrace(); }
return days;
}
Look at the getFragmentInDays methods in this apache commons-lang class DateUtils.
Based on #Mad_Troll's answer, I developed this method.
I've run about 30 test cases against it, is the only method that handles sub day time fragments correctly.
Example: If you pass now & now + 1 millisecond that is still the same day.
Doing 1-1-13 23:59:59.098 to 1-1-13 23:59:59.099 returns 0 days, correctly; allot of the other methods posted here will not do this correctly.
Worth noting it does not care about which way you put them in, If your end date is before your start date it will count backwards.
/**
* This is not quick but if only doing a few days backwards/forwards then it is very accurate.
*
* #param startDate from
* #param endDate to
* #return day count between the two dates, this can be negative if startDate is after endDate
*/
public static long daysBetween(#NotNull final Calendar startDate, #NotNull final Calendar endDate) {
//Forwards or backwards?
final boolean forward = startDate.before(endDate);
// Which direction are we going
final int multiplier = forward ? 1 : -1;
// The date we are going to move.
final Calendar date = (Calendar) startDate.clone();
// Result
long daysBetween = 0;
// Start at millis (then bump up until we go back a day)
int fieldAccuracy = 4;
int field;
int dayBefore, dayAfter;
while (forward && date.before(endDate) || !forward && endDate.before(date)) {
// We start moving slowly if no change then we decrease accuracy.
switch (fieldAccuracy) {
case 4:
field = Calendar.MILLISECOND;
break;
case 3:
field = Calendar.SECOND;
break;
case 2:
field = Calendar.MINUTE;
break;
case 1:
field = Calendar.HOUR_OF_DAY;
break;
default:
case 0:
field = Calendar.DAY_OF_MONTH;
break;
}
// Get the day before we move the time, Change, then get the day after.
dayBefore = date.get(Calendar.DAY_OF_MONTH);
date.add(field, multiplier);
dayAfter = date.get(Calendar.DAY_OF_MONTH);
// This shifts lining up the dates, one field at a time.
if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
fieldAccuracy--;
// If day has changed after moving at any accuracy level we bump the day counter.
if (dayBefore != dayAfter) {
daysBetween += multiplier;
}
}
return daysBetween;
}
You can remove the #NotNull annotations, these are used by Intellij to do code analysis on the fly
You say it "works fine in a standalone program," but that you get "unusual difference values" when you "include this into my logic to read from report". That suggests that your report has some values for which it doesn't work correctly, and your standalone program doesn't have those values. Instead of a standalone program, I suggest a test case. Write a test case much as you would a standalone program, subclassing from JUnit's TestCase class. Now you can run a very specific example, knowing what value you expect (and don't give it today for the test value, because today changes over time). If you put in the values you used in the standalone program, your tests will probably pass. That's great - you want those cases to keep working. Now, add a value from your report, one that doesn't work right. Your new test will probably fail. Figure out why it's failing, fix it, and get to green (all tests passing). Run your report. See what's still broken; write a test; make it pass. Pretty soon you'll find your report is working.
Hundred lines of code for this basic function???
Just a simple method:
protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24);
// MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
final int SECONDS = 60;
final int MINUTES = 60;
final int HOURS = 24;
final int MILLIES = 1000;
long temp;
if (timestamp1 < timestamp2) {
temp = timestamp1;
timestamp1 = timestamp2;
timestamp2 = temp;
}
Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
endDate.setTimeInMillis(timestamp1);
startDate.setTimeInMillis(timestamp2);
if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
int day1 = endDate.get(Calendar.DAY_OF_MONTH);
int day2 = startDate.get(Calendar.DAY_OF_MONTH);
if (day1 == day2) {
return 0;
} else {
return 1;
}
}
int diffDays = 0;
startDate.add(Calendar.DAY_OF_MONTH, diffDays);
while (startDate.before(endDate)) {
startDate.add(Calendar.DAY_OF_MONTH, 1);
diffDays++;
}
return diffDays;
}
ThreeTen-Extra
The Answer by Vitalii Fedorenko is correct, describing how to perform this calculation in a modern way with java.time classes (Duration & ChronoUnit) built into Java 8 and later (and back-ported to Java 6 & 7 and to Android).
Days
If you are using a number of days routinely in your code, you can replace mere integers with use of a class. The Days class can be found in the ThreeTen-Extra project, an extension of java.time and proving ground for possible future additions to java.time. The Days class provides a type-safe way of representing a number of days in your application. The class includes convenient constants for ZERO and ONE.
Given the old outmoded java.util.Date objects in the Question, first convert them to modern java.time.Instant objects. The old date-time classes have newly added methods to facilitate conversion to java.time, such a java.util.Date::toInstant.
Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant(); // Exclusive.
Pass both Instant objects to factory method for org.threeten.extra.Days.
In the current implementation (2016-06) this is a wrapper calling java.time.temporal.ChronoUnit.DAYS.between, read the ChronoUnit class doc for details. To be clear: all uppercase DAYS is in the enum ChronoUnit while initial-cap Days is a class from ThreeTen-Extra.
Days days = Days.between( start , stop );
You can pass these Days objects around your own code. You can serialize to a String in the standard ISO 8601 format by calling toString. This format of PnD uses a P to mark the beginning and D means “days”, with a number of days in between. Both java.time classes and ThreeTen-Extra use these standard formats by default when generating and parsing Strings representing date-time values.
String output = days.toString();
P3D
Days days = Days.parse( "P3D" );
This code calculates days between 2 date Strings:
static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
static final String DATE_FORMAT = "dd-MM-yyyy";
public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
Date fromDate;
Date toDate;
fromDate = format.parse(fromDateStr);
toDate = format.parse(toDateStr);
return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}
If you're looking for a solution that returns proper number or days between e.g. 11/30/2014 23:59 and 12/01/2014 00:01 here's solution using Joda Time.
private int getDayDifference(long past, long current) {
DateTime currentDate = new DateTime(current);
DateTime pastDate = new DateTime(past);
return currentDate.getDayOfYear() - pastDate.getDayOfYear();
}
This implementation will return 1 as a difference in days. Most of the solutions posted here calculate difference in milliseconds between two dates. It means that 0 would be returned because there's only 2 minutes difference between these two dates.
You should use Joda Time library because Java Util Date returns wrong values sometimes.
Joda vs Java Util Date
For example days between yesterday (dd-mm-yyyy, 12-07-2016) and first day of year in 1957 (dd-mm-yyyy, 01-01-1957):
public class Main {
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date date = null;
try {
date = format.parse("12-07-2016");
} catch (ParseException e) {
e.printStackTrace();
}
//Try with Joda - prints 21742
System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
//Try with Java util - prints 21741
System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));
}
private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
DateTime jodaDateTime = new DateTime(date);
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
DateTime y1957 = formatter.parseDateTime("01-01-1957");
return Days.daysBetween(y1957 , jodaDateTime).getDays();
}
private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date y1957 = null;
try {
y1957 = format.parse("01-01-1957");
} catch (ParseException e) {
e.printStackTrace();
}
return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}
So I really advice you to use Joda Time library.
I did it this way. it's easy :)
Date d1 = jDateChooserFrom.getDate();
Date d2 = jDateChooserTo.getDate();
Calendar day1 = Calendar.getInstance();
day1.setTime(d1);
Calendar day2 = Calendar.getInstance();
day2.setTime(d2);
int from = day1.get(Calendar.DAY_OF_YEAR);
int to = day2.get(Calendar.DAY_OF_YEAR);
int difference = to-from;