Calculating the time difference between two times in Java - java

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.

Related

time convert issue while converting adding one day extra

I am trying to convert time zone, but it's adding one day extra from java function.
"" deActivationDate=2021-06-25T23:59:59.000+0000"";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
try {
Date date =formatter.parse(deActivationDate);
deActivationDate=formatter.format(date);
LOGGER.info("time format printing 1" +deActivationDate);//2021-06-26T04:29:59.000+0430
deActivationDate = deActivationDate.substring(0, deActivationDate.length()-2)+":30";
LOGGER.info("time format printing 2" +deActivationDate);//2021-06-26T04:29:59.000+04:30""
In above deactivation date is 25 when I am giving input but after formater parase method its converting as 26 why one day os getting add how to avoid it.
java.time through ThreeTen Backport
You should seriously consider using java.time, the modern Java date and time API, for your non-trivial date and time work.
It’s not very clear from your question, but I think that you want to convert the date and time string to the same date and wall-clock time in your own time zone, in this case, Asia/Tehran time zone. So a different point in time: near the end of the day in Iran rather than near the end of the day in UTC. And with a colon in the UTC offset.
I am declaring two formatters, one for parsing without colon and one for formatting back with colon:
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("xx")
.toFormatter();
private static final DateTimeFormatter PRINTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("xxx")
.toFormatter();
Now your conversion goes like this:
String deActivationDate = "2021-06-25T23:59:59.000+0000";
OffsetDateTime dateTime = OffsetDateTime.parse(deActivationDate, PARSER);
deActivationDate = dateTime.atZoneSimilarLocal(ZoneId.systemDefault())
.format(PRINTER);
System.out.println("time format printing: " +deActivationDate);
Output is — tested on Java 1.7.0_67 with ThreeTen Backport version 1.3.6:
time format printing: 2021-06-25T23:59:59+04:30
Java knows that Asia/Tehran time zone uses summer time (DST) on June 25, so converts to and prints your desired offset of +04:30. Had the date been in the standard time part of the year, +03:30 would have been printed instead.
The 0 milliseconds are not printed, which for most purposes is an advantage. The format is ISO 8601, and according to the ISO 8601 standard the fraction of second is optional when it is 0. If you require the millis to be there, use this simpler formatter instead:
private static final DateTimeFormatter PRINTER
= DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxxx");
time format printing: 2021-06-25T23:59:59.000+04:30
Half-open: You should not represent the end of the day by 1 second before the start of the new day. First, it’s wrong: the day does not end a second before it ends. Second, it may give rise to errors because of times that fall within that last second and therefore in your program will neither belong to one day or the other. Even if this does not happen in practice, you will have programmers wasting their time wondering whether it may happen. Instead represent the end of the day as the first moment of the following day exclusive (typically 00:00). When testing, require a time to be strictly before the end of the day to belong to the day. This approach is standard for all kinds of intervals and certainly for time intervals. They are then known as half-open intervals.
Question: Doesn’t java.time require Java 8?
java.time works nicely on Java 7. 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.
Wikipedia article: ISO 8601
Here's the fix for your code. Though we recommend not to do it via substring method.
String deActivationDate="2021-06-25T23:59:59.000+0000";
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date =formatter.parse(deActivationDate);
deActivationDate=formatter.format(date);
System.out.println("time format printing 1: " +deActivationDate);
//2021-06-25T23:59:59.000+0000
deActivationDate = deActivationDate.substring(0,
deActivationDate.length()-4)+"0430";
System.out.println("time format printing 2: " +deActivationDate);
//2021-06-25T23:59:59.000+0430
} catch (Exception e) {
System.err.println(e.getMessage());
}
Thanks to all for your suggestion #beshambher-chaukhwan m i have achieved changes with below code
String deActivationDate="2021-06-25T23:59:59.000+0000";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
try {
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date =formatter.parse(deActivationDate);
deActivationDate=formatter.format(date);
if(TimeZone.getDefault().useDaylightTime()) {
deActivationDate = deActivationDate.substring(0, deActivationDate.length()-4)+"04:30";
}else {
deActivationDate = deActivationDate.substring(0, deActivationDate.length()-4)+"03:30";
}

Get number of days since date object, disregard time

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

Date difference in days is different in different location environment

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.

calendar start date before end date on same day difference

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

Java Calendar adds a random number of milliseconds?

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

Categories

Resources