SimpleDateFormat Comparison Issue - java

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.

Related

Display relative date with custom format in Android

I want to translate a date to human readable format. I am using DateUtils. getRelativeDateTimeString, but this does not fit the criteria. The output I am getting looks like: 1 hour, 15 min. ago, etc.
I want to know if it is possible to change the format to:
3m instead of 3 min. ago,
1h instead of 1 hour. 15 min. ago etc.
using DateUtils or is there another way to do this?
To be more precise I am looking for an Android equivalent of this angular-filter where you can easily change the format of the relative date (for example: {{minutes}} minutes ago to {{minutes}}m.
To make myself clear, I am not searching for a way to format a date, but to translate a date to human readable format such as 'today', '1 hr', '38 min' (simillar to facebook's relative dates).
After some research, I found out some libraries like Time4A, Joda-Time, PrettyTime, Android-Ago.
However, I have decided not to use a library and override its text resources, but to create a method and store text in strings.xml for possible future localization.
private static final int SECOND_MILLIS = 1000;
private static final int MINUTE_MILLIS = 60 * SECOND_MILLIS;
private static final int HOUR_MILLIS = 60 * MINUTE_MILLIS;
private static final int DAY_MILLIS = 24 * HOUR_MILLIS;
private static final int WEEK_MILLIS = 7 * DAY_MILLIS;
public static String getTimeAgo(Date date, Context context) {
Date now = Calendar.getInstance().getTime();
final long diff = now.getTime() - date.getTime();
if (diff < SECOND_MILLIS) {
return context.getString(R.string.just_now);
} else if (diff < MINUTE_MILLIS) {
return diff / SECOND_MILLIS + context.getString(R.string.seconds_ago);
} else if (diff < 2 * MINUTE_MILLIS) {
return context.getString(R.string.a_minute_ago);
} else if (diff < 59 * MINUTE_MILLIS) {
return diff / MINUTE_MILLIS + context.getString(R.string.minutes_ago);
} else if (diff < 90 * MINUTE_MILLIS) {
return context.getString(R.string.an_hour_ago);
} else if (diff < 24 * HOUR_MILLIS) {
return diff / HOUR_MILLIS + context.getString(R.string.hours_ago);
} else if (diff < 48 * HOUR_MILLIS) {
return context.getString(R.string.yesterday);
} else if (diff < 6 * DAY_MILLIS) {
return diff / DAY_MILLIS + context.getString(R.string.days_ago);
} else if (diff < 11 * DAY_MILLIS) {
return context.getString(R.string.a_week_ago);
} else {
return diff / WEEK_MILLIS + context.getString(R.string.weeks_ago);
}
}
Use the built-in DateUtils utility library that was included in API level 3.
CharSequence getRelativeDateTimeString (Context c,
long time,
long minResolution,
long transitionResolution,
int flags) Return string describing the elapsed time since startTime formatted like "[relative time/date], [time]".
Example output strings for the US date format:
3 min. ago, 10:15 AM Yesterday, 12:20 PM Dec 12, 4:12 AM 11/14/2007,
8:20 AM
You should use SimpleDateFormat and specify your desired template. Which in your case would be something like this:
String template = "H'h', m'm'";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(template, Locale.getDefault());
String formatted = simpleDateFormat.format(new Date());
Why don't you parse the Date to the format you want?
With string.replace() you can do it in one line of code.
Edit 1: I usually use SimpleDateForma this way, hope it helps:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String currentDateandTime = sdf.format(new Date());
you will need this imports ( i think that Android automatically imports):
import java.text.SimpleDateFormat;
import java.util.Date;
Edit 2: In your example what you have to do is:
SimpleDateFormat sdf = new SimpleDateFormat("HH'h' mm'm'");
String currentDateandTime = sdf.format(new Date());
System.out.println("Today in dd-MM-yy:HH:mm:SS : " + date);

Java Subtract 2 duration in HH:MM:SS

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;
}

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

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