Java Subtract 2 duration in HH:MM:SS - java

I have 2 string in java (HH:MM:SS) please take note this is not time, but duration, i used end time - start time to get these values:
Case1:
duration1 = "12:04:45";
duration2 = "13:04:45";
Expected result: duration1 - duration 2 = "-1:00:00" (Note that there is negative)
Case2:
duration1 = "15:13:32";
duration2 = "12:04:45";
Expected result: duration1 - duration 2 = "3:08:47"
How can i do that? My attempt for the Case1 (codes modified from Java add dates of format dd:HH:mm:ss):
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String s1 = "12:04:45";
String s2 = "13:04:45";
Date d1 = format.parse(s1);
Date d2 = format.parse(s2);
int sec = d1.getSeconds() - d2.getSeconds();
int min = d1.getMinutes() - d2.getMinutes();
int hr = d1.getHours() - d2.getHours();
Time sum = new Time(hr, min, sec);
System.out.println(sum); // Output: 23:00:00 which is wrong

Using LocalTime or date calculations like some people suggest doesn't work if your period involved more than 24 hours since that doesn't fit in a day.
If you don't have Java 8, you can use JodaTime. I've just checked that this code also works with JodaTime 1.6.2, which is the last version that still works with JDK 1.4.2.
PeriodFormatter formatter = new PeriodFormatterBuilder()
.printZeroAlways().minimumPrintedDigits(2)
.appendHours().appendSuffix(":").appendMinutes().appendSuffix(":").appendSeconds()
.toFormatter();
Period period1 = formatter.parsePeriod("12:04:45");
Period period2 = formatter.parsePeriod("13:04:45");
Period difference1 = period1.minus(period2).normalizedStandard();
System.out.println(formatter.print(difference1));
Period period3 = formatter.parsePeriod("15:13:32");
Period period4 = formatter.parsePeriod("12:04:45");
Period difference2 = period3.minus(period4).normalizedStandard();
System.out.println(formatter.print(difference2));
Output:
-01:00:00
03:08:47
JodaTime:
Version 1.6.2 source: https://github.com/JodaOrg/joda-time/releases/tag/v1.6.2
Version 1.6.2 Maven Jar artifact: http://mvnrepository.com/artifact/joda-time/joda-time/1.6.2

With the Java time API you could use a Duration to calculate the duration and format it as you want:
String s1 = "12:04:45";
String s2 = "13:04:45";
LocalTime t1 = LocalTime.parse(s1);
LocalTime t2 = LocalTime.parse(s2);
Duration d = Duration.between(t2, t1);
System.out.println(d); //PT-1H
If you want to print it as -1:00:00 you will need to tweak the output format. It could look like this:
private static String toHHMMSS(Duration d) {
long hours = d.toHours();
int minutes = (int) (d.toMinutes() % 60);
int secs = (int) (d.getSeconds() % 60);
return hours + ":" + minutes + ":" + secs;
}

Use Calendar to calculate the time difference.
You can query each field and/or format as a time string as this example shows:
private Calendar getTimeDiffDate(Date d1, Date d2) {
Calendar c = Calendar.getInstance();
c.setTime(new Date(d1.getTime()-d2.getTime()));
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
// Format as a time string:
String formattedTime = formatter.format(c.getTime());
System.out.println("formattedTime: "+formattedTime);
// Query by each field:
System.out.println("Hours: "+c.get(Calendar.HOUR));
System.out.println("Minutes: "+c.get(Calendar.MINUTE));
System.out.println("Seconds: "+c.get(Calendar.SECOND));
return c;
}

Related

Android calculate days between two dates

I have written the following code to find days between two dates
startDateValue = new Date(startDate);
endDateValue = new Date(endDate);
long diff = endDateValue.getTime() - startDateValue.getTime();
long seconds = diff / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = (hours / 24) + 1;
Log.d("days", "" + days);
When start and end date are 2/3/2017 and 3/3/2017 respectively the number of days showing is 29.Though when they are of the same day it is showing 1.(The number of days one takes a leave.So if one takes a single day leave,he has to select same start and end date.So in this case he has taken two days leave).
What am I doing wrong?
Thank you for your time.
Note: Please don't use the date constructor. Check the accepted answer below. Use simpledateformat or Joda time. Date constructor is deprecated.
Your code for generating date object:
Date date = new Date("2/3/2017"); //deprecated
You are getting 28 days as answer because according to Date(String) constructor it is thinking day = 3,month = 2 and year = 2017
You can convert String to Date as follows:
String dateStr = "2/3/2017";
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date date = sdf.parse(dateStr);
Use above template to make your Date object. Then use below code for calculating days in between two dates. Hope this clear the thing.
It can de done as follows:
long diff = endDateValue.getTime() - startDateValue.getTime();
System.out.println ("Days: " + TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS));
Please check link
If you use Joda Time it is much more simple:
int days = Days.daysBetween(date1, date2).getDays();
Please check JodaTime
How to use JodaTime in Java Project
Kotlin
Here is the example to calculate days from today to some date:
val millionSeconds = yourDate.time - Calendar.getInstance().timeInMillis
leftDays.text = TimeUnit.MILLISECONDS.toDays(millionSeconds).toString() + "days"
If you want to calculate two days, then change:
val millionSeconds = yourDate1.time - yourDate2.time
should work.
public static int getDaysDifference(Date fromDate,Date toDate)
{
if(fromDate==null||toDate==null)
return 0;
return (int)( (toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24));
}
Does Android fully support java-8? If yes you can simple use ChronoUnit class
LocalDate start = LocalDate.of(2017,2,3);
LocalDate end = LocalDate.of(2017,3,3);
System.out.println(ChronoUnit.DAYS.between(start, end)); // 28
or same thing using formatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/yyyy");
LocalDate start = LocalDate.parse("2/3/2017",formatter);
LocalDate end = LocalDate.parse("3/3/2017",formatter);
System.out.println(ChronoUnit.DAYS.between(start, end)); // 28
java.time and ThreeTenABP
If I understand correctly, you want the number of days from start day through end date inclusive.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d/M/u");
String startDate = "2/3/2017";
String endDate = "3/3/2017";
LocalDate startDateValue = LocalDate.parse(startDate, dateFormatter);
LocalDate endDateValue = LocalDate.parse(endDate, dateFormatter);
long days = ChronoUnit.DAYS.between(startDateValue, endDateValue) + 1;
System.out.println("Days: " + days);
Output:
Days: 2
ChronoUnit.DAYS.between() gives us a count of days from start date inclusive to end date exclusive. So to include the end date too we needed to add 1 day just as you did in the question.
What went wrong in your code?
You are using the Date(String) constructor. This constructor has been deprecated since 1997 because it works unreliably across time zones, so don’t use it. Also it’s kind of magical: at least I never really know what I get. Apparently it takes 2/3/2017 to mean February 3, 2017, where you intended 2 March 2017. From February 3 to March 3 inclusive is 29 days (since 2017 wasn’t a leap year). This explains why you got 29. (If necessary, we could spell our way through the documentation and find out why 2/3/2017 is interpreted the way it is, only I’d find that a pointless waste of time to do.)
You can’t convert from milliseconds. Please also note that not only the question but also the very many answers that convert from milliseconds to days are incorrect. Such a conversion assumes that a day is always 24 hours. Because of summer time (DST) and other time anomalies a day is not always 24 hours. All of those answers will count a day too few for example if the leave crosses the spring gap or spring forward when summer time begins.
Question: Doesn’t java.time require Android API level 26?
java.time works nicely on both older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
What date format do you use? Is it d/M/yyyy or M/d/yyyy?
d = day, M = month, yyyy = year
(see: https://developer.android.com/reference/java/text/SimpleDateFormat.html)
Then the codes:
public static final String DATE_FORMAT = "d/M/yyyy"; //or use "M/d/yyyy"
public static long getDaysBetweenDates(String start, String end) {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH);
Date startDate, endDate;
long numberOfDays = 0;
try {
startDate = dateFormat.parse(start);
endDate = dateFormat.parse(end);
numberOfDays = getUnitBetweenDates(startDate, endDate, TimeUnit.DAYS);
} catch (ParseException e) {
e.printStackTrace();
}
return numberOfDays;
}
And for getUnitBetweenDates method:
private static long getUnitBetweenDates(Date startDate, Date endDate, TimeUnit unit) {
long timeDiff = endDate.getTime() - startDate.getTime();
return unit.convert(timeDiff, TimeUnit.MILLISECONDS);
}
Very simple, just use Calendar, create two instances for the two dates, convert to milliseconds, subtract and convert to days (rounded up)... like this, basically:
Calendar startDate = Calendar.getInstance();
startDate.set(mStartYear, mStartMonth, mStartDay);
long startDateMillis = startDate.getTimeInMillis();
Calendar endDate = Calendar.getInstance();
endDate.set(mEndYear, mEndMonth, mEndDay);
long endDateMillis = endDate.getTimeInMillis();
long differenceMillis = endDateMillis - startDateMillis;
int daysDifference = (int) (differenceMillis / (1000 * 60 * 60 * 24));
Have look at this code , this is helpful for me ,hope it will help you.
public String get_count_of_days(String Created_date_String, String Expire_date_String) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
Date Created_convertedDate = null, Expire_CovertedDate = null, todayWithZeroTime = null;
try {
Created_convertedDate = dateFormat.parse(Created_date_String);
Expire_CovertedDate = dateFormat.parse(Expire_date_String);
Date today = new Date();
todayWithZeroTime = dateFormat.parse(dateFormat.format(today));
} catch (ParseException e) {
e.printStackTrace();
}
int c_year = 0, c_month = 0, c_day = 0;
if (Created_convertedDate.after(todayWithZeroTime)) {
Calendar c_cal = Calendar.getInstance();
c_cal.setTime(Created_convertedDate);
c_year = c_cal.get(Calendar.YEAR);
c_month = c_cal.get(Calendar.MONTH);
c_day = c_cal.get(Calendar.DAY_OF_MONTH);
} else {
Calendar c_cal = Calendar.getInstance();
c_cal.setTime(todayWithZeroTime);
c_year = c_cal.get(Calendar.YEAR);
c_month = c_cal.get(Calendar.MONTH);
c_day = c_cal.get(Calendar.DAY_OF_MONTH);
}
/*Calendar today_cal = Calendar.getInstance();
int today_year = today_cal.get(Calendar.YEAR);
int today = today_cal.get(Calendar.MONTH);
int today_day = today_cal.get(Calendar.DAY_OF_MONTH);
*/
Calendar e_cal = Calendar.getInstance();
e_cal.setTime(Expire_CovertedDate);
int e_year = e_cal.get(Calendar.YEAR);
int e_month = e_cal.get(Calendar.MONTH);
int e_day = e_cal.get(Calendar.DAY_OF_MONTH);
Calendar date1 = Calendar.getInstance();
Calendar date2 = Calendar.getInstance();
date1.clear();
date1.set(c_year, c_month, c_day);
date2.clear();
date2.set(e_year, e_month, e_day);
long diff = date2.getTimeInMillis() - date1.getTimeInMillis();
float dayCount = (float) diff / (24 * 60 * 60 * 1000);
return ("" + (int) dayCount + " Days");
}
Be carefur if you'd like to use received integer e.g. to indicate specific day in custom calendar implementation. For example, I tried to go in m app from monthly calendar view to daily view and show daily content, by calculating dates from 1970-01-01 to selected one, and each 25-31th day of month shows me as one day earlier, because datesDifferenceInMillis / (24 * 60 * 60 * 1000); may return something like 17645,95833333333, and casting this to int you'll get value lower by 1. In this case correctly number of days you'll get by rounding received float by using NumberFormat class. Here's my code:
NumberFormat numberFormat = NumberFormat.getInstance(Locale.getDefault());
numberFormat.setRoundingMode(RoundingMode.HALF_UP);
numberFormat.setMaximumFractionDigits(0);
numberFormat.setMinimumFractionDigits(0);
int days = numberFormat.parse(numberFormat.format(value)).intValue();
I hope it will be helpful.
SUPER SIMPLE
use LocalDate() include implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1' to use in android
Example
IN KOTLIN
val daysDifferene = LocalDate.of(2017,3,3).toEpochDay() - LocalDate.of(2017,3,2)
even better
create extension function to LocalDate class
private operator fun LocalDate.minus(other: LocalDate) = toEpochDay() - other.toEpochDay()
now just say
val daysDifference = localDate1 - localDate2 // you get number of days in Long type
IN JAVA
long daysDifference = LocalDate.of(2017,3,3).toEpochDay() - LocalDate.of(2107,3,2)
I modified Jitendra's answer in Kotlin:
fun getDaysBetweenDates(firstDateValue: String, secondDateValue: String, format: String): String
{
val sdf = SimpleDateFormat(format, Locale.getDefault())
val firstDate = sdf.parse(firstDateValue)
val secondDate = sdf.parse(secondDateValue)
if (firstDate == null || secondDate == null)
return 0.toString()
return (((secondDate.time - firstDate.time) / (1000 * 60 * 60 * 24)) + 1).toString()
}
and call it like
val days = getDaysBetweenDates("31-03-2020", "24-04-2020","dd-MM-yyyy")
fun countDaysBetweenTwoCalendar(calendarStart: Calendar, calendarEnd: Calendar) : Int{
val millionSeconds = calendarEnd.timeInMillis - calendarStart.timeInMillis
val days = TimeUnit.MILLISECONDS.toDays(millionSeconds) //this way not round number
val daysRounded = (millionSeconds / (1000.0 * 60 * 60 * 24)).roundToInt()
return daysRounded
}
while none of these worked for me, here is an easy way to implement your code with a very simple fonction :
private long getDaysDifference(Date fromDate,Date toDate) {
if(fromDate == null || toDate == null)
return 0;
int a = Integer.parseInt(DateFormat.format("dd", fromDate)+"");
int b = Integer.parseInt(DateFormat.format("dd", toDate)+"");
if ( b <= a){
return Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH) + b - a;
}
return b - a;
}
EnJoY
You can use joda time, it so simple
fun getBetweenDates(startDate: Long, endDate: Long): String {
val period = Period(startDate, endDate, PeriodType.yearMonthDayTime())
val formatter = PeriodFormatterBuilder()
.appendYears().appendSuffix(" year ")
.appendMonths().appendSuffix(" month ")
.appendDays().appendSuffix(" days ")
.appendHours().appendSuffix(" hours ")
.appendMinutes().appendSuffix(" minutes ")
.appendSeconds().appendSuffix(" seconds ")
.appendMillis().appendSuffix(" millis ")
.toFormatter()
return formatter.print(period)
}
start and end dates millisecond, and result example: "2 year 1 month ..."
use this way :
fun stringDateToCalendar(dateString: String?, formatString: String): Calendar? {
if (dateString == null || dateString.isEmpty() || formatString.isBlank())
return null
val inputDateFormat = SimpleDateFormat(formatString, Locale.ENGLISH)
return try {
inputDateFormat.parse(dateString)?.let {
val cal = Calendar.getInstance()
cal.time = it
cal
}
} catch (e: ParseException) {
null
}
}
val calendarFrom = stringDateToCalendar(
"2021-12-12",
"yyyy-MM-dd"
)
val calendarTo = CalendarUtils.stringDateToCalendar(
"2022-03-20",
"yyyy-MM-dd"
)
val msDiff = calendarTo?.timeInMillis?.minus(calendarFrom?.timeInMillis ?: 0) ?: 0
val daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff)
Here the simply common function Kotlin code
Here comparing these formatted dates "2022-11-04", "2022-11-20"
Output will be 16 Days
open fun dateSubstraction(date1: String, date2: String): String {
val dateFormatter = SimpleDateFormat("yyyy-MM-dd") //Define input date format here
val formatedDate1= dateFormat.parse(date1) //formated date1
val formatedDate2= dateFormat.parse(date2) //formated date2
val millionSeconds = formatedDate2.time - formatedDate1.time
return TimeUnit.MILLISECONDS.toDays(millionSeconds).toString()+"Days"
}
I have done this work in past. This was very simple
val currentCalendar = Calendar.getInstance()
val targetCalendar = Calendar.getInstance().apply {
set(2023, Calendar.APRIL, 9, 23, 59, 59)
}//your input: date, month, and time
val difference = targetCalendar.timeInMillis - currentCalendar.timeInMillis
val differenceInDays = TimeUnit.MILLISECONDS.toDays(difference)
val remainingMonths = differenceInDays / 30//remaining months
val remainingDays = differenceInDays % 30//remaining days
val differenceInSeconds = TimeUnit.MILLISECONDS.toSeconds(difference)
val differenceInMinutes = TimeUnit.MILLISECONDS.toMinutes(difference)
val differenceInHours = TimeUnit.MILLISECONDS.toHours(difference)
val remainingSeconds = differenceInSeconds % 60//remaining seconds
val remainingMinutes = differenceInMinutes % 60//remaining minutes
val remainingHours = differenceInHours % 24// remaining hours
Thats It We have done it

mean time of two string time in java

I have two times variable as string
want to find the mean time. please help me
inTime = shift.getInTime()+":00";
outTime = shift.getOutTime()+":00";
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
Date d1=df.parse(inTime);
Date d2 = df.parse(outTime);
long date1InMilSec=d1.getTime();
long date2InMilSec=d2.getTime();
long half =date1InMilSec + ((date2InMilSec - date1InMilSec) / 2);
long minute = (half / (1000 * 60)) % 60;
long hour = (half / (1000 * 60 * 60)) % 24;
String time = String.format("%02d:%02d", hour, minute);
First of all, if I use your code and set fixed time values, then I get 11:30:00 instead of your 07:00:00, so there is maybe something else wrong with inTime and outTime.
Since I get 11:30:00 there is maybe something wrong with your calculation of hour and minute, but I won't bother that. Let's use a new Date instance to do the conversion:
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
Date d1 = df.parse("09:30:00");
Date d2 = df.parse("15:30:00");
long date1InMilSec = d1.getTime();
long date2InMilSec = d2.getTime();
long half = date1InMilSec + ((date2InMilSec - date1InMilSec) / 2);
Date meanTime = new Date(half); // new Date instance, instead of own calculation
String time = df.format(meanTime);
System.out.println(time);
This code prints:
12:30:00
String inTime = "09:30:00";
String outTime = "15:30:00";
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");
Date dateIn = df.parse(inTime);
Date dateOut = df.parse(outTime);
long dateInMill = dateIn.getTime();
long dateOutMill = dateOut.getTime();
long dateMiddleMill = dateInMill + ((dateOutMill - dateInMill) / 2);
Date dateMiddle = new Date(dateMiddleMill);
System.out.println(df.format(dateMiddle));

Java add dates of format dd:HH:mm:ss

I have three dates as String objects in the format: dd:HH:mm:ss
00:1:9:14
00:3:10:4
00:3:39:49
How do I add these dates in Java to get the sum (00:7:59:07)?
Sample code:
SimpleDateFormat sdf = new SimpleDateFormat("dd:HH:mm:ss");
Date d1 = sdf.parse("00:1:9:14");
Date d2 = sdf.parse("00:3:10:4");
Date d3 = sdf.parse("00:3:39:49");
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
Date d = new Date(d1.getTime() + d2.getTime() + d3.getTime());
System.out.println(d);
Output(wrong):
Wed Dec 31 01:09:14 IST 1969
Wed Dec 31 03:10:04 IST 1969
Wed Dec 31 03:39:49 IST 1969
Sun Dec 28 20:59:07 IST 1969
The dd format includes the day of the month. So your value of 00 will underflow if you use SimpleDateFormat (or Java Date because it also includes a day of the month). Instead, parse your time parts and do the math yourself.
For example, you could create a class TimePart with days, hours, minutes and seconds like
static class TimePart {
int days = 0;
int hours = 0;
int minutes = 0;
int seconds = 0;
static TimePart parse(String in) {
if (in != null) {
String[] arr = in.split(":");
TimePart tp = new TimePart();
tp.days = ((arr.length >= 1) ? Integer.parseInt(arr[0]) : 0);
tp.hours = ((arr.length >= 2) ? Integer.parseInt(arr[1]) : 0);
tp.minutes = ((arr.length >= 3) ? Integer.parseInt(arr[2]) : 0);
tp.seconds = ((arr.length >= 4) ? Integer.parseInt(arr[3]) : 0);
return tp;
}
return null;
}
public TimePart add(TimePart a) {
this.seconds += a.seconds;
int of = 0;
while (this.seconds >= 60) {
of++;
this.seconds -= 60;
}
this.minutes += a.minutes + of;
of = 0;
while (this.minutes >= 60) {
of++;
this.minutes -= 60;
}
this.hours += a.hours + of;
of = 0;
while (this.hours >= 24) {
of++;
this.hours -= 24;
}
this.days += a.days + of;
return this;
}
#Override
public String toString() {
return String.format("%02d:%02d:%02d:%02d", days, hours, minutes,
seconds);
}
}
Then your test-cases like
public static void main(String[] args) {
try {
TimePart d1 = TimePart.parse("00:1:9:14");
TimePart d2 = TimePart.parse("00:3:10:4");
TimePart d3 = TimePart.parse("00:3:39:49");
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
TimePart d4 = d1.add(d2).add(d3);
System.out.println(d4);
} catch (Exception e) {
e.printStackTrace();
}
}
And it seems to perform the addition correctly like
00:01:09:14
00:03:10:04
00:03:39:49
00:07:59:07
The above sum is arithmetic addition so you need a ref --here d0 (default epoch). Date class has a lot of problems beware...
SimpleDateFormat sdf = new SimpleDateFormat("dd:HH:mm:ss");
Date d0 = sdf.parse("00:00:00:00"); // ref
Date d1 = sdf.parse("00:01:09:14");
Date d2 = sdf.parse("00:03:10:04");
Date d3 = sdf.parse("00:03:39:49");
System.out.println(d0);
System.out.println(d1);
System.out.println(d2);
System.out.println(d3);
Date d = new Date(d1.getTime() + d2.getTime() + d3.getTime() - 2 * d0.getTime()); // impt
System.out.println(d);
Note:- Date addition is not an easy task, think twice.
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String s1 = "01:02:03";
String s2 = "10:12:13";
Date d1 = format.parse(s1);
Date d2 = format.parse(s2);
int sec = d1.getSeconds() + d2.getSeconds();
int min = d1.getMinutes() + d2.getMinutes();
int hr = d1.getHours() + d2.getHours();
Time sum = new Time(hr, min, sec);
System.out.println(sum); // Output: 11:14:16
Do your strings represent/denote amounts of time? So use the Duration class. Let’s first write an auxiliary method that parses a string into a Duration:
private static Duration parseDuration(String timeString) {
// First convert the string to ISO 8601 through a regex
String isoTimeString = timeString.replaceFirst("^(\\d+):(\\d+):(\\d+):(\\d+)$", "P$1DT$2H$3M$4S");
// Then parse into Duration
return Duration.parse(isoTimeString);
}
Duration.parse() requires ISO 8601 format, it goes like PT1H9M14S for a period of time of 1 hour 9 minutes 14 seconds. Or optionally P0DT1H9M14S. The 0D for 0 days goes before the T. So I use a regular expression (AKA a regex) to modify your string format into ISO 8601 before parsing it. The $1, $2, etc., in the replacement string refer to what was matched inside the round brackets, the so-called groups in the regular expression.
Now we can add the times up:
String[] timeStrings = { "00:1:9:14", "00:3:10:4", "00:3:39:49" };
Duration totalTime = Duration.ZERO;
for (String timeString : timeStrings) {
Duration dur = parseDuration(timeString);
totalTime = totalTime.plus(dur);
}
System.out.println(totalTime);
Output:
PT7H59M7S
7 hours 59 minutes 7 seconds. If you want, you may format it back into your format of 00:7:59:07. Search for how.
What went wrong in your code?
Your first mistake seems to have been before writing the code: thinking of the times as dates. They are not, and it would not make any sense to add dates. What is the sum of April 7 and December 25?
Mislead by this thinking, you tried to parse into Date objects. A Date is a point in time, not an amount of time, so this is wrong. Other than that the Date class is poorly designed, and the SimpleDateFormat class that you also tried to use is notoriously troublesome. Fortunately we’ve got no use for them here, and also for dates and times they are long outdated, superseded by java.time, the modern Java date and time API, of which Duration is but one of many classes.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Documentation of…
String.replaceFirst() on the use of a regular expression
Duration
Question: How to format a duration in java? (e.g format H:MM:SS); some of the answers are using the Duration class.
private static String addTimes(String time1, String time2) throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(dateFormat.parse(time1));
c2.setTime(dateFormat.parse(time2));
c1.add(Calendar.HOUR, c2.get(Calendar.HOUR));
c1.add(Calendar.MINUTE, c2.get(Calendar.MINUTE));
c1.add(Calendar.SECOND, c2.get(Calendar.SECOND));
return dateFormat.format(c1.getTime());
}
addTimes("1:9:14", "3:10:4");
Output: 04:19:18

SimpleDateFormat Comparison Issue

I'd like to compare two dates in a string format and return the result in HH:MM:SS:SSS as a String.
When i try running the following with a startDate of 15 Jul 2013 17:08:34.903 and endDate of
15 Jul 2013 17:08:51.247 I'd expect to see a result of 00:00:16.344. Instead i'm getting a diff of 01:00:16.344. Any ideas why this is happening??
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("d MMM yyyy HH:mm:ss.SSS");
private static final SimpleDateFormat DATE_FORMAT_HH_MM_SS = new SimpleDateFormat("HH:mm:ss.SSS");
public String calculateDuration(String startDateStr, String endDateStr){
String methodName = "calculateDuration";
try {
Date startDate = DATE_FORMAT.parse(startDateStr);
Date endDate = DATE_FORMAT.parse(endDateStr);
long diff = endDate.getTime() - startDate.getTime();
return DATE_FORMAT_HH_MM_SS.format(diff);
The JDK Date API is horrible. I recommend using Joda Time library but if you must use JDK Date API try this code:
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
"d MMM yyyy HH:mm:ss.SSS");
public String calculateDuration(String startDateStr, String endDateStr)
throws ParseException {
String methodName = "calculateDuration";
Date startDate = DATE_FORMAT.parse(startDateStr);
Date endDate = DATE_FORMAT.parse(endDateStr);
// in milliseconds
long diff = endDate.getTime() - startDate.getTime();
long diffMiliseconds = diff % 1000;
long diffSeconds = diff / 1000 % 60;
long diffMinutes = diff / (60 * 1000) % 60;
long diffHours = diff / (60 * 60 * 1000) % 24;
return String.format("%02d:%02d:%02d.%02d", diffHours, diffMinutes,
diffSeconds, diffMiliseconds);
}
Since in the comments you say that you're simply measuring running time for an application, don't involve Date, SimpleDateFormat, or Calendar at all.
long tic = System.nanoTime();
...is all you need. Don't use a cannon to kill a mosquito when you have a fly swatter on hand.
You've not initialized your SimpleDateFormat formatter with a Calendar object, so it is using a default one to do the mapping. I am guessing that this default Calendar is recognizing DST. This is not manifest when you .parse() the datetimes, because the DST adjustment cancels out in the subtraction.
When you convert the result to HMS format, the formatter again applies the DST adjustment. Since there is no subtraction in this step, it appears in your result.
It does seem to me that whenever anybody has a date arithmetic question, the rubber-stamp, knee-jerk reflex response is "use JodaTime." We all love JodaTime; it is a well-crafted API. But using it is still an external dependency and unless you -need- it you should not use it. In my opinion, for this situation you do not need JodaTime at all ... you only need the Java Calendar API to parse your datetimes into Dates. And that's all you need it for.
Once you have the difference in milliseconds, you really should avoid using a formatter to convert the result back. Using simple arithmetic will be far more performant:
private static final SimpleDateFormat DATE_FORMAT =
new SimpleDateFormat("d MMM yyyy HH:mm:ss.SSS");
private static final int DAYS = 0;
private static final int HOURS = 1;
private static final int MINUTES = 2;
private static final int SECONDS = 3;
private static final int MILLIS = 4;
private final int[] result = new int[MILLIS];
public String calculateDuration(String startDateStr, String endDateStr){
try {
Date startDate = DATE_FORMAT.parse(startDateStr);
Date endDate = DATE_FORMAT.parse(endDateStr);
} catch (Exception e) { /* exception processing */ }
long diff = endDate.getTime() - startDate.getTime();
double d;
for (int i = DAYS; i <= MILLIS; i++) {
switch(i) {
case DAYS:
d = ((double) diff) / (86400.0 * 1000.0);
break;
case HOURS:
d = d * 24.0; break;
case MINUTES: case SECONDS:
d = d * 60.0; break;
case MILLIS:
d = d * 1000.0; break;
}
result[i] = (int) Math.floor(d);
d = d - (double) result[i];
}
At the conclusion of this last for loop, the array result contains integers that correspond to the number of DAYS, HOURS, MINUTES, SECONDS, and MILLIS derived from your date subtraction. No objects need to be created, allocated, or invoked in order to get this result. You can now easily use the String.format() function to put these integers into the string format of your choosing.

how to parse this date

I cant quite figure out what the format should be to parse this date. Its a millisecond value followed by timezone. thx.
// so far tried: "S Z"
// "SSSSSSSSSSS-ZZZZ",
// "SSSSSSSSSSS-Z",
// etc.
Format formatter = new SimpleDateFormat("SSSSSSSSSSSS Z", Locale.CANADA);
// source string looks like this /Date(928164000000-0400)/
String temp = jsonUserObj.getString("DateOfBirth").substring(6, 6+17);
System.err.println("got date="+temp);
Date date = (Date) formatter.parseObject(temp);
You can do it without parser.
String[] parts = new String[]{temp.substring(0, temp.indexOf('-')), temp.substring(temp.indexOf('-') + 1)};
long millis = Long.parseLong(parts[0]);
parts[1] = parts[1].replaceAll("^0*(\\-?[0-9]*)$", "$1");
int timeZone = Integer.parseInt(parts[1]);
int rawOffset = (timeZone / 100) * 3600000 + (timeZone % 100);
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis(millis);
cal.setTimeZone(new SimpleTimeZone(rawOffset, "GMT"));
SimpleDateFormat expects a milliseconds value < 1000, as it expects you would increment seconds, then minutes, etc, for larger values.
You'll need to convert the value first; this post might help: Unix epoch time to Java Date object

Categories