I have two calendar dates where i am getting the difference between in days, hours, and minutes.
This works perfectly if the end date is greater than the start date.
What doesnt work is if the start date is the same day of week as the end date, but an earlier time than the end date.
For example: end date 2:20 pm Saturday, and start date is 7:20 pm on saturday.
It calculates it at like 0days, and 5 hours. But, it should be more like 7 days.
Here is the code
long t1 = curCal.getTimeInMillis();
long t2 = setCal.getTimeInMillis();
if(t2 < t1){
days = t1-t2;
}else{
days = t2-t1;
}
long toDays = TimeUnit.MILLISECONDS.toDays(days);
long toHours = TimeUnit.MILLISECONDS.toHours(days) % 24;
long toMinutes = TimeUnit.MILLISECONDS.toMinutes(days) % 60;
String toastMessage = String.format(" %d Days %d Hours %d Minutes", toDays, toHours, toMinutes);
Toast.makeText(context, "ALARM in" + " " + toastMessage , Toast.LENGTH_LONG).show();
How can i handle the case where the end date is the same day as the start date, but the end date is a time before the start date?
Thanks
EDIT
I think i solved my problem. I am adding it for anyone else having the same issue. if end date = startdate(same day) add 7 to the calendar object for enddate. psuedocode
if (enddate == startdate)) {
enddate.add(Calendar.DAY_OF_YEAR, 7);
}
ZoneId zone = ZoneId.of("Europe/Busingen");
DayOfWeek alarmDay = DayOfWeek.SUNDAY;
LocalTime alarmTime = LocalTime.of(14, 20);
ZonedDateTime now = ZonedDateTime.now(zone);
ZonedDateTime alarmDateTime = now.with(alarmDay).with(alarmTime);
if (alarmDateTime.isBefore(now)) {
alarmDateTime = alarmDateTime.plusWeeks(1);
}
Duration difference = Duration.between(now, alarmDateTime);
String toastMessage = String.format(" %d Days %d Hours %d Minutes",
difference.toDaysPart(), difference.toHoursPart(), difference.toMinutesPart());
System.out.println(toastMessage);
Running just now (Sunday 22:03:17 in Büsingen) I got:
6 Days 16 Hours 16 Minutes
I believe that I am contributing the answer that is not only the modern one but also the more robust one.
Modern: The Calendar class is long outdated and by today’s standards poorly designed. Instead I use and recommend java.time, the modern Java date and time API.
Robust: As far as I can tell your code doesn’t only have an issue when today and alarm date are the same day of week, but also if the alarm falls on an earlier day of week. I take that into account.
Furthermore accurate: In cases where you cross transitions to and from summer time (DST), you may get the wrong number of hours when you use the millisecond values in your calculation. Using two ZonedDateTime objects minimizes surprises here. It does require you to fill in your desired time zone where I put Europe/Busingen since summer time transitions are time zone specific.
Furthermore more precisely modelled: Using a Calendar, a date and time, for a weekly recurring alarm seems a bit funny. What you need is a day-of-week and a time of day, so I use that. java.time offers the classes needed, the DayOfWeek enum and the LocalTime class.
I am in fact so modern that I am using the toXxxPart methods of the Duration class that were introduced in Java 9. For formatting the Duration if you are not yet using Java 9 you will need to subtract first the days from the duration to get the hours: use the minusDays method. Then do similarly with minusHours to get the minutes.
long toDays = difference.toDays();
difference = difference.minusDays(toDays);
long toHours = difference.toHours();
difference = difference.minusHours(toHours);
long toMinutes = difference.toMinutes();
Question: Can I use java.time on Android?
Yes, java.time works nicely on 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, I’m told) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new 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.timeto 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.
Reading your question another way, if t1 is the start date and t2 is the end date, your logic does not include the case where t1 < t2 and t2 - t1 < 1. In this case, you need to add 7 to the number of days. Something like:
long t1 = curCal.getTimeInMillis();
long t2 = setCal.getTimeInMillis();
if(t2 < t1){
days = t1-t2;
}else{
days = t2-t1;
if (days < 1) {
days += 7;
}
}
All of this can be simplified to
days = Math.abs(t1 - t2);
if (days < 1 && t1 < t2) {
days += 7;
}
Related
I'm unsure whether this can be made more efficient or not, but I need to get the number of days that have passed since a unix/epoch timestamp, where the time itself is not a factor, only the date compared to now.
Example 1:
Timestamp is : 3rd September 14:35
Compared to now which is: 4th September 00:35
Days difference = 1
Example 2:
Timestamp is: 3rd September 23:55
Compared to now which is: 4th September 00:35
Days difference = 1
Example 3:
Timestamp is: 2nd September 02:23
Compared to now which is: 4th September 00:35
Days difference = 2
To get this, I have the following code:
String epoch = "1599134401" // the unix/epoch timestamp in seconds
Long epochMillis = Long.valueOf(epoch) * 1000;
Date epochDateObj = new Date(epochMillis);
Calendar tsCal = Calendar.getInstance();
tsCal.setTime(epochDateObj);
tsCal.set(Calendar.HOUR_OF_DAY, 0);
tsCal.set(Calendar.MINUTE, 0);
tsCal.set(Calendar.SECOND, 0);
tsCal.set(Calendar.MILLISECOND, 0);
Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);
long diffInMillies = Math.abs(today.getTime().getTime() - tsCal.getTime().getTime());
long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
if(diff > 1) {
return diff + " days";
} else {
return diff + " day";
}
The above code works, but to me, it seems quite elaborate for such a rather small thing as this.
Any suggestions for optimizing it? Maybe there's some functionality I don't know about. Its an Android app which is using a rather old SDK (back to Android 4.1).
Days are quite fundamentally a human concept. They involve politics, opinion, confusion, timezones, eras, epochs, and other very hairy concepts. java.util.Date has no snowball's chance in hades to do it right. Nor does calendar.
Your only hope is a proper API, such as java.time.
Furthermore you need to clean up your question. What you're asking is impossible; you're comparing guns to grandmas. epoch-time is fundamentally a 'computer' concept - it refers solely to moments in time, it has no idea when, where, who, which political party, etc you are asking. Which is a problem, because without any of that information it is NOT possible to know what day it is. Seconds are more or less universal, but days are not. A day can be 23 hours or 25 hours, or 23 hours, 59 minutes and 59 seconds, or 24 hours and 1 second - sometimes whole days get skipped, etcetera. 'how long is a day' is not answerable without knowing who you ask and what timezone (and sometimes, political entity!) is used as context.
So, let's say you're asking someone in arizona. The answer will then depend rather a lot on where in arizona you ask and who you ask: You would need to (potentially) know whether the person you so happen to ask applies daylight savings time or not back in 1970 as well as in the 'target' time. This depends on whether you're asking when you're on an native american reservation within arizona or not, and/or if the person you're asking is sticking to NAR zones or not. Hence why I mentioned the politics thing, and why what you want is completely impossible.
java.time to the rescue which can actually represent the crazy mess!
Instant represents a moment in time. It's internally stored as epoch-millis and cannot tell you the day, month, year, era, hour, etc of that moment in time by itself. That's because.. well, that's because that's how reality works. If I snap my fingers right now, and I ask someone 'what time is it', it depends on where I am and where the person I'm asking is and what political parties they ascribe to, so it's not possible. But, you combine a Zone and an Instant and now we're getting somewhere.
LocalDateTime represents a time as a human would say it: A year/month/day + hour/minute/second. It is not possible to turn this into epochmillis for the same reason in reverse. And for the same reason, if you combine this with a Zone doors start opening.
ZonedDateTime tries to bridge the gap: It represents a time as a human would say it, but we code in the location (and political affiliations) of the human who said it. You can store this either as a LocalDateTime + TimeZone, or as an Instant+TimeZone (you don't need to know how it is implemented, of course). You can move from a ZDT to either Instant or LocalDateTime, of course, and this one can answer many questions.
Let's try to solve your problem:
String epoch = "1599134401"; // the unix/epoch timestamp in seconds
String where = "Europe/Berlin"; // what you want is impossible without this!!
Instant instant = Instant.ofEpochSecond(Long.valueOf(epoch));
ZonedDateTime target = instant.atZone(ZoneId.of(where));
ZonedDateTime today = ZonedDateTime.now(where);
long days = ChronoUnit.DAYS.between(target, today);
System.out.println(days);
As a general rule, if you start doing serious math on dates you're messing up and it won't work. Not that your tests will ever catch it of course; it'll go ape when the clocks go back or forward or some political party decides 5 days before it happens to end daylight savings time, or the client is in one place and your server is in another, etc - all stuff that tests rarely catch.
Proper use of java.time should usually mean you aren't doing much calculation, and so it is here, fortunately.
There’s already a very great and insightful answer by rzwitserloot, I highly recommend it. Just as a minor supplement I wanted to give you my go at the code. Still using java.time, the modern Java date and time API, of course.
ZoneId zone = ZoneId.of("Europe/Tirane");
DateTimeFormatter epochSecondFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS)
.toFormatter();
String epoch = "1599134401"; // the unix/epoch timestamp in seconds
Instant then = epochSecondFormatter.parse(epoch, Instant::from);
LocalDate thatDay = then.atZone(zone).toLocalDate();
LocalDate today = LocalDate.now(zone);
long diff = ChronoUnit.DAYS.between(thatDay, today);
diff = Math.abs(diff);
if (diff == 1) {
System.out.println("" + diff + " day");
} else {
System.out.println("" + diff + " days");
}
When I ran the code just now, the output was:
1 day
Since you want to ignore the time of day, LocalDate is the correct class to use for the dates. A LocalDate is a date with time of day and without time zone.
In English (not being a native speaker, though) I prefer saying “0 days”, not “0 day”. So I have changed your condition for choosing between singular and plural.
Did your code work?
Your code gives inaccurate results in corner cases. TimeUnit is generally a fine enum for time unit conversions, but it assumes that a day is always 24 hours, which is not always the case, as rzwitserloot explained. The java.time code of that answer and of this one correctly takes transitions to and from summer time (DST) and other time anomalies into account.
Question: Does java.time work on Android 4.1?
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 either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case 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).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
If we don't want to add the ThreeTenABP library to our project, we need to normalize to a date-without-time in UTC, in order to prevent things like Daylight Savings Time to skew the results.
For that, a helper method is appropriate:
static long toDateUtcMillis(Date time) {
// Get year/month/day according to default time zone
Calendar cal = Calendar.getInstance();
cal.setTime(time);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int day = cal.get(Calendar.DAY_OF_MONTH);
// Set year/month/day in UTC
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.clear();
cal.set(year, month, day);
return cal.getTimeInMillis();
}
We can now easily calculate the number of days. In the following we return negative value if the dates are reverse. Add call to Math.abs() if that's not desired.
static int daysBetween(Date date1, Date date2) {
long dateMillis1 = toDateUtcMillis(date1);
long dateMillis2 = toDateUtcMillis(date2);
return (int) TimeUnit.MILLISECONDS.toDays(dateMillis2 - dateMillis1);
}
Test
public static void main(String[] args) throws Exception {
test("3 September 2020 14:35", "4 September 2020 00:35");
test("3 September 2020 23:55", "4 September 2020 00:35");
test("2 September 2020 02:23", "4 September 2020 00:35");
}
static void test(String date1, String date2) throws ParseException {
// Parse the date strings in default time zone
SimpleDateFormat format = new SimpleDateFormat("d MMMM yyyy HH:mm", Locale.US);
int days = daysBetween(format.parse(date1), format.parse(date2));
System.out.println("Timestamp is: " + date1);
System.out.println("Compared to: " + date2);
System.out.println("Days difference = " + days);
System.out.println();
}
Output
Timestamp is: 3 September 2020 14:35
Compared to: 4 September 2020 00:35
Days difference = 1
Timestamp is: 3 September 2020 23:55
Compared to: 4 September 2020 00:35
Days difference = 1
Timestamp is: 2 September 2020 02:23
Compared to: 4 September 2020 00:35
Days difference = 2
Below is giving me 279 days in local but in different server it is giving me 278 why this happening ?
String sDate = "10-11-2017";
String eDate = "16-08-2018";
Date startDate = new SimpleDateFormat(MsmConstants.DATE_FORMAT).parse(sDate);
Date endDate = new SimpleDateFormat(MsmConstants.DATE_FORMAT).parse(eDate);
long difference = startDate.getTime() - endDate.getTime();
return Math.abs(difference / (1000 * 60 * 60 * 24));
This is probably coming from some kind of rounding combined with a difference in timezones.
Since both operands on the left and right of your / operator are integral types, you are actually losing precision with that operator.
If you change your expression to: (double) difference / (1000 * 60 * 60 * 24)
you will notice that the result is actually: -278.9583333333333
The reason it is not exact days is because of daylight savings and similar date/time adjustments.
If on your PC you have different regional settings from the server, which do not have the same daylight savings, then you might have got a different number, which exceeded 279.
As a sidenote, the Java 7 and earlier date/time API was very buggy. SimpleDateFormat wasn't even thread safe, and there were lots of issues with daylight savings and similar situations. You should really move to Java 8. What you are doing would simply become: Duration.between(startDate, endDate) and the calculation would be done correctly for you.
String sDate = "10-11-2017";
String eDate = "16-08-2018";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDateTime startDate = LocalDate.parse(sDate, formatter).atTime(0, 0);
LocalDateTime endDate = LocalDate.parse(eDate, formatter).atTime(0, 0);
return Duration.between(startDate, endDate).toDays();
You will see that this actually gives you 279.
jbx has already in another answer explained nicely what went wrong. 10 November is in winter on the Northern hemisphere, and 16 August is in summer. Between those two dates summer time (DST) begins, which causes one day to be just 23 hours, so the difference you calculate is 1 hour short of being 279 days at you had expected and had observed locally.
java.time
Date and time math is too complicated and error-prone to do yourself the way you tried. You should always leave it to a well-proven library. Here’s the correct and modern solution.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d-MM-uuuu");
String sDate = "10-11-2017";
String eDate = "16-08-2018";
LocalDate startDate = LocalDate.parse(sDate, dateFormatter);
LocalDate endDate = LocalDate.parse(eDate, dateFormatter);
long difference = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println(Math.abs(difference));
Output is the expected:
279
Since your date strings haven’t got time of day, there is no reason to use LocalDateTime. LocalDate is the correct class to use. It also makes sure that no number truncation can happen since there is always a whole number of days between two dates.
Question: Will that work on Java 7?
Yes, java.time 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 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.
SimpleDataFormat uses the JVM's default timezone to create Date objects. In case of daylight saving time, this might result in a +/- 1 day difference between two dates.
You have a couple of options:
Explicitly set the timezone of your SimpleDateFormat objects to GMT/UTC.
Explicitly set the timezone of your JVM to GMT/UTC.
In Java 8, use the new java.time API; in earlier versions of Java use an alternative like Joda Time.
I want to calculate the sleeping time of user, he should enter when he slept and when he woke up and I should calculate how many hours he slept
For example if the user enter
His sleeping time : 11:00 pm
His waking up time : 7:30 am
The number of sleeping hours should be : 8:30 hrs
Is there any way to make this calculation easily
This is the code
cal.set(Calendar.HOUR_OF_DAY, 11);
cal.set(Calendar.MINUTE, 0);
sleepTimeOnOffDays = cal.getTime();
private long subtractTwoDates(Date date1, Date date2) {
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
calendar1.setTime(date1);
calendar2.setTime(date2);
long milsecs1 = calendar1.getTimeInMillis();
long milsecs2 = calendar2.getTimeInMillis();
long diff = milsecs2 - milsecs1;
long dhours = diff / (60 * 60 * 1000);
return Math.abs(dhours);
}
From Java SE 8 onwards i would like to suggest using the java.time API. For Android users, java.time is added in API 26+.
Java 8 introduced new APIs for Date and Time to address the shortcomings of the older java.util.Date and java.util.Calendar.
Using the java.time API calculating the duration between 2 date times is very easy.
LocalDateTime localDateTime1 = LocalDateTime.now();
localDateTime1 = localDateTime1.withHour(23).withMinute(0).withSecond(0).withNano(0);
LocalDateTime localDateTime2 = LocalDateTime.now();
localDateTime2 = localDateTime2.plusDays(1).withHour(7).withMinute(30).withSecond(0).withNano(0);
Duration diff = Duration.between(localDateTime1, localDateTime2);
String hms = String.format("%d:%02d:%02d",
diff.toHoursPart(),
diff.toMinutesPart(),
diff.toSecondsPart());
System.out.println(hms);
-----------------------
Console output:
8:30:00
If the java.time API is not available for your project, because for example your project is not yet on API level 26, then i would recommend using the ThreeTen Android Backport API as suggested by #Ole V.V in the comments below.
The Syntax is pretty much the same as the java.time API. The code snippet above works for the ThreeTen Android Backport API as well (like #Basil Bourque already mentioned) with one exception. The Duration class of the ThreeTen Android Backport API unfortunately does not provide helper methods for extracting the hours part, minutes part etc. from the duration object. Instead when you use the backport you need to subtract first the hours, then the minutes from the duration so only the seconds are left:
long hours = diff.toHours();
diff = diff.minusHours(hours);
long minutes = diff.toMinutes();
diff = diff.minusMinutes(minutes);
long seconds = diff.getSeconds();
String hms = String.format("%d:%02d:%02d",
hours,
minutes,
seconds);
Another difference is you need to import the java.time classes from the org.threeten.bp package (not the java.time package).
If you want to use the ThreeTen Backport API, just include it's dependency to your project.
tl;dr
11:00 PM to 7:30 AM = 7.5 or 8.0 or 8.5 or 9.5 or some other number of hours depending on the particular dates and time zone.
Duration.between(
ZonedDateTime.of( 2019 , 1 , 23 , 23 , 0 , 0 , 0 , ZoneId.of( "Africa/Cairo" ) ) ,
ZonedDateTime.of( 2019 , 1 , 24 , 7 , 30 , 0 , 0 , ZoneId.of( "Africa/Cairo" ) )
)
.toString()
See this code run live at IdeOne.com.
PT8H30M
Calculating elapsed time requires date, time-of-day, and time zone
The Answer by Markus Hänsel is headed the right way, but fails to account for anomalies such as Daylight Saving Time (DST).
Your Question is not quite clear. Do you mean to track the general idea of time-of-day using generic 24-hour days? Or do you mean to track actual moments, such as the time slept two days ago, the time slept yesterday, and so on?
If the latter, then you cannot use LocalDateDate as that class cannot, by definition, track moments. The LocalDateTime class lacks any concept of time zone or offset-from-UTC. As such, a LocalDateTime represents potential moments along a range of about 26-27 hours (the range of time zones around the globe).
Given your example of 11:00 pm to 7:30 am, that would mean 8.5 hours in a certain time zone with no anomalies on that date. But on a day of DST cut-over that might mean 7.5 hours (“Spring ahead”), or 9.5 hours (“Fall back”). Or maybe 8.0 hours on day such as last year when North Korea adjusted their clock by a half-hour. Or when in 2007 Venezuela turned back time a half-hour… and then a decade later flipped back again. These changes happen more often than you may realize. Politicians around the globe have shown a penchant for redefining the offset of their time zone(s).
➥ The upshot is that you cannot reliably calculate elapsed time with only the time-of-day. You must use a date and a time zone.
ZoneId
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime
To track moments, use the ZonedDateTime class. This class combines a date, a time-of-day, and a time zone.
LocalDate ldStart = LocalDate.of( 2019 , 1 , 23 ) ;
LocalTime ldStart = LocalTime.of( 23 , 0 ) ;
ZonedDateTime zdtStart = ZonedDateTime.of( ldStart , ltStart , z ) ;
And the stop time.
LocalDate ldStop = LocalDate.of( 2019 , 1 , 24 ) ; // Next day.
LocalTime ldStop = LocalTime.of( 7 , 30 ) ;
ZonedDateTime zdtStop = ZonedDateTime.of( ldStop , ltStop , z ) ;
Calculate elapsed time using Duration class.
Duration d = Duration.between( zdtStart , zdtStop ) ;
ISO 8601
I recommend you not report elapsed time using time-of-day format, HH:MM:SS. Instead use the standard format defined for this purpose, in the ISO 8601 standard.
So, 8.5 hours would be PT8H30M.
The java.time classes use ISO 8601 formats by default when parsing/generating strings.
String output = d.toString() ; // Generate `PT8H30M`.
And parsing.
Duration d = Duration.parse( "PT8H30M" ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You can use the SimpleDateFormat for getting the input. I'm using the predefined input, since you wanted the difference calculation between two given times. Here is the code fo you. Please note I have given out the answer for both 24 or 12 hours format.
Getting the value of sleep time and waking up time
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm");
Date sleepingTime = simpleDateFormat.parse("22:00");
Date wakeUptime = simpleDateFormat.parse("07:00");
For difference is less than zero, this happens in 12 hours format
long difference = sleepingTime.getTime() - wakeUpTime.getTime();
if(difference<0) {
Date dateMax = simpleDateFormat.parse("24:00");
Date dateMin = simpleDateFormat.parse("00:00");
difference=(dateMax.getTime() -sleepingTime.getTime() )+(wakeUpTime.getTime()-
dateMin.getTime());
}
Calculating the difference
//days and minutes are optional
int days = (int) (difference / (1000*60*60*24));
int hours = (int) ((difference - (1000*60*60*24*days)) / (1000*60*60));
int min = (int) (difference - (1000*60*60*24*days) - (1000*60*60*hours)) /
(1000*60);
Log.e("log_tag",hours+" hours");
I hope it will answer your query. Thanks :)
Just Try following line of code to get difference in minutes
import java.util.concurrent.TimeUnit; // predefined method from java 6.
private long subtractTwoDates(Date date1, Date date2) {
Long diffInMinutes=null,diff=null; // just declaration of variables
diff = date1.getTime() - date2.getTime();
diffInMinutes= TimeUnit.MILLISECONDS.toMinutes(diff);
log.info("diff min : "+difInMin);
return diffInMinutes; // returns long value in minutes.
}
String time1 = br.readLine();
DateFormat sdf = new SimpleDateFormat("hh:mm:ss",Locale.ENGLISH);
Date d1 = sdf.parse(time1);
int n = Integer.parseInt(br.readLine());
for(int i=0;i<n;i++) {
String time2 = br.readLine();
Date d2 = sdf.parse(time2);
long diffMs = d1.getTime() - d2.getTime();
System.out.println(diffMs/(1000*60*60));
Input:
time1 = 23:05:38
time2 = 12:36:07
Output should be 10 but it’s giving 22.
First, I am assuming that the calculation takes place in a time zone without summer time (DST) and other anomalies, or at least on dates where there is no transition.
I left out the reading from the BufferedReader since you already seem to be handling this fine.
String time1 = "23:05:38";
LocalTime t1 = LocalTime.parse(time1);
String time2 = "12:36:07";
LocalTime t2 = LocalTime.parse(time2);
Duration diff = Duration.between(t2, t1);
System.out.println(diff.toHours());
This prints the expected
10
As you can see, I am following Deb’s suggestion in the comment: using the modern Java date and timeAPI known as JSR-310 or java.time. This automatically solves your problem since LocalTime parses your times on the 24 hour clock without any explicit formatter and therefore without the opportunity to use the wrong case in a format pattern string.
That’s right, I agree with d.j.brown that the lowercase hh in your pattern is the culprit. Your times were interpreted as 23:05:38 AM, which is nonsense, but the outdated SimpleDateFormat just takes that to mean 11:05:38 PM, that is 23:05:38 on a 24 hour clock, that is what you had expected. IMHO it’s quite nasty to let you get away with such a bug without telling you. You were fortunate to have a time that started with “12”, for 12:36:07 AM means 0:36:07 on a 24 hour clock, so this time you got a wrong result and were made aware something was wrong. Otherwise your bug would have gone unnoticed, maybe for a long time. This long story to carry a morale: stay far away from SimpleDateFormat.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (that’s ThreeTen for JSR-310, where the modern API was first defined).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.
Hi I have something weird happening. I am simply taking a calendar object, converting it to its individual parts, and putting it back into a calendar (with or without any changes. In this case I make no changes). I mean, this should be cut and paste. I've also tried to create a calendar with calendar = Calendar.getInstance() and set everything manually. calendar.set(Calendar.YEAR, mStartYear); so on. Still gives wrong Calendar objects. I've tried also setting Milliseconds, always seem to have some garbage milliseconds.. But the time one way or another just is completely off. Maybe someone sees the stupid oversight, but I'm stumped.
Also this is an android application, but shouldn't matter for a basic Java library object.
Note Weekview is a datawrapper for one of the libraries I'm using. It has a start and an end calendar.
Here is what the debugger lists as in memory..
mEndDay = 19
mEndHour = 9
mEndMinute = 30
mEndMonth = 8
mEndYear = 2015
mSeekAmount = 0
mStartDay = 18
mStartHour = 23
mStartMinute = 0
mStartMonth = 8
mStartYear = 2015
Calendar calendarStart = Calendar.getInstance();
calendarStart.set(mStartYear,mStartMonth,mStartDay,mStartHour,mStartMinute);
Calendar calendarEnd = Calendar.getInstance();
calendarEnd.set(mEndYear,mEndMonth,mEndDay,mEndHour,mEndMinute);
I end up with
Start 1442363359161
End 1442363359161
calendarStart = {GregorianCalendar#20968} "java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=258,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=6,HOUR_OF_DAY=23,MINUTE=0,SECOND=19,MILLISECOND=161,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
calendarEnd = {GregorianCalendar#20969} "java.util.GregorianCalendar[time=?,areFieldsSet=false,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=19,DAY_OF_YEAR=258,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=6,HOUR_OF_DAY=9,MINUTE=30,SECOND=19,MILLISECOND=161,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
EXPECT
Start 1442638800000
End 1442676600000
mEndTime = {GregorianCalendar#20990} "java.util.GregorianCalendar[time=1442676600000,areFieldsSet=true,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=19,DAY_OF_YEAR=262,DAY_OF_WEEK=7,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=9,HOUR_OF_DAY=9,MINUTE=30,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
mName = {String#20991} "sleep"
mStartTime = {GregorianCalendar#20992} "java.util.GregorianCalendar[time=1442638800000,areFieldsSet=true,lenient=true,zone=America/Denver,firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2015,MONTH=8,WEEK_OF_YEAR=38,WEEK_OF_MONTH=3,DAY_OF_MONTH=18,DAY_OF_YEAR=261,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=-25200000,DST_OFFSET=3600000]"
Here is where I set it up initially..
WeekViewEvent weekViewEvent = dateWrapperParam.getWeekViewEvent();
Calendar endCalendar = weekViewEvent.getEndTime();
Calendar startCalendar = weekViewEvent.getStartTime();
Date endDate = endCalendar.getTime();
Date startDate = startCalendar.getTime();
mStartHour = startCalendar.get(Calendar.HOUR_OF_DAY);
mStartMinute = startCalendar.get(Calendar.MINUTE);
mStartDay = startCalendar.get(Calendar.DAY_OF_MONTH);
mStartMonth = startCalendar.get(Calendar.MONTH);
mStartYear = startCalendar.get(Calendar.YEAR);
mEndHour = endCalendar.get(Calendar.HOUR_OF_DAY);
mEndMinute = endCalendar.get(Calendar.MINUTE);
mEndDay = endCalendar.get(Calendar.DAY_OF_MONTH);
mEndMonth = endCalendar.get(Calendar.MONTH);
mEndYear = endCalendar.get(Calendar.YEAR);
In the documentation of Calendar.set, it is said :
Sets the values for the fields YEAR, MONTH, DAY_OF_MONTH, HOUR, MINUTE, and SECOND. Previous values of other fields are retained. If this is not desired, call clear() first.
The reason is that not all fields are set with this method, in you case, you don't have MILLISECOND set. So it keep the value when the instance was created.
The call of Calendar.clear will
Sets all the calendar field values and the time value (millisecond offset from the Epoch) of this Calendar undefined.
A quick example :
Calendar c = GregorianCalendar.getInstance();
c.clear();
c.set(2019, Calendar.NOVEMBER, 03, 16, 15, 03);
System.out.println(c.getTime());
System.out.println(c.getTimeInMillis());
Sun Nov 03 16:15:03 CET 2019
1572794103000
Milliseconds being undefined will give 0
As per my comments under your question , there is only difference in seconds and milliseconds between startCalendar and calendarStart time, because that values were not reset.
See DEMO
java.time and ThreeTenABP
I suggest that you use java.time, the modern Java date and time API, for your date and time work. For example:
ZonedDateTime start = ZonedDateTime.now(ZoneId.systemDefault());
ZonedDateTime end = start;
System.out.println("Start: " + start);
System.out.println("End: " + end);
Output when I ran the code in my time zone just now:
Start: 2020-06-24T19:24:04.811+02:00[Europe/Copenhagen]
End: 2020-06-24T19:24:04.811+02:00[Europe/Copenhagen]
A ZonedDateTime is a date and time of day in some time zone. It’s the closest we come to a modern equivalent of GregorianCalendar (the subclass of Calendar that your code gave you). Which modern class to use varies with more precise requirements, so sometimes you will prefer to use for example LocalDate, OffsetDateTime or even LocalTime.
To truncate the values to whole minutes (setting seconds and fraction of second to 0):
ZonedDateTime start = ZonedDateTime.now(ZoneId.systemDefault())
.truncatedTo(ChronoUnit.MINUTES);
Start: 2020-06-24T19:24+02:00[Europe/Copenhagen]
ZonedDateTime and the other classes of java.time offer plenty of ways to modify the values obtained. For example:
ZonedDateTime end = start.plusDays(2).withHour(13);
End: 2020-06-26T13:24+02:00[Europe/Copenhagen]
If you want to create the end time manually using only selected fields from the start time:
ZonedDateTime end = ZonedDateTime.of(
2021, start.getMonthValue(), start.getDayOfMonth(),
start.getHour(), 30, 0, 0, start.getZone());
End: 2021-06-24T19:30+02:00[Europe/Copenhagen]
What went wrong in your code?
Part of the answer is already in the other answers: The set methods of Calendar set only the fields they promise to set and leave other fields unchanged where possible. While this is probably expected from the set(int field, int value) method, it often surprises with the set(int year, int month, int date) method and even more with set(int year, int month, int date, int hourOfDay, int minute) and set(int year, int month, int date, int hourOfDay, int minute, int second). In general while well intended the Calendar class and its subclasses are poorly and confusingly designed and cumbersome to work with. This is the main reason why I recommend java.time above.
The other part of the answer is that Calendar calculates its fields leniently. So when you look at the Calendar object in your debugger after calling set, it will contain a lot of garbage values. Calling getTime() forces the Calendar to compute its fields, so after that call the way it looks in the debugger should make more sense. Again it’s confusing behaviour, and it can also sometimes be observed without using the debugger.
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.
When you initialize the Calendar object, it is getting the current time including the current second and millisecond. The code provided sets the hour and minute, but it does not set the second and millisecond, leaving it as it was when the Calendar object was initialized.
In order to set the second and nanosecond to zero, use:
StartCalendar.set(Year, Month, DayofMonth, Hour, Minute, 0,0);