I am using two instances of Calendar, and trying to get the time between each instance (I am calling the instances depart and arrive).
Here is a code snippet that I am focusing on:
public static void main(String[] args)
{
Calendar departDST = Calendar.getInstance();
departDST.set(Calendar.YEAR, 2015);
departDST.set(Calendar.MONTH, 2);
departDST.set(Calendar.DAY_OF_MONTH, 7);
departDST.set(Calendar.HOUR_OF_DAY, 17);
departDST.set(Calendar.MINUTE, 35);
Calendar arriveDST = Calendar.getInstance();
arriveDST.set(Calendar.YEAR, 2015);
arriveDST.set(Calendar.MONTH, 2);
arriveDST.set(Calendar.DAY_OF_MONTH, 8);
arriveDST.set(Calendar.HOUR_OF_DAY, 9);
arriveDST.set(Calendar.MINUTE, 50);
departDST.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
arriveDST.setTimeZone(TimeZone.getTimeZone("UTC"));
printFlightDuration(departDST, arriveDST);
}
public static void printFlightDuration(Calendar depart, Calendar arrive)
{
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm z", Locale.US);
formatter.setTimeZone(depart.getTimeZone());
System.out.println("Depart: " + formatter.format(depart.getTime()));
formatter.setTimeZone(arrive.getTimeZone());
System.out.println("Arrive: " + formatter.format(arrive.getTime()));
long hours = (arrive.getTimeInMillis() - depart.getTimeInMillis()) / (1000*60*60);
long minutes = (arrive.getTimeInMillis() - depart.getTimeInMillis()) / (1000*60) - (hours*60);
System.out.println("Flight duration: " + hours + " hours" + " " + minutes + " minutes");
}
Now, if I set the date of depart to be 7/15/2015 at 5:35 pm and arrive to be 7/16/2015 at 9:50 am, the application reports the correct time of 9 hours and 15 minutes.
If I set the date of depart to be 3/7/2015 at 5:35 pm and arrive to be 3/8/2015 at 9:50 am, the application reports in at 10 hours and 15 minutes [because of daylights savings time occurring at 2:00 am on 3/8/2015].
How should I change my application so that it reports the correct duration of 9 hours 15 minutes, regardless of which date I set depart and arrive to?
One idea I had was to convert to depart and arrive to UTC, and then calculate the difference between them but either I implemented the solution incorrectly or the solution altogether is an incorrect answer to the problem
Related
I am creating a Workday calendar program that calculates a 'start' and 'end' date
the results should output:
"Starting date: 24-05-2004 07:03 with addition of 8.276628 working days is end date: 04-06-2004 10:12"
or
24-05-2004 18:03 with the addition of -6.7470217 working days is 13-05-2004 10:02
The mathematical solution is to multiply hours pr day with incrementInWokringdays
like 8.0f hours a day * 2.5f days = 18.4f hours and then the result of this should be added to the Date calendar this way
-date.add( Calendar.Hours_of_Day, 18.0f ) //but from float converted to integers
-date.add( Calendar.Minutes_of_Day, 0.4f )//
how do i split the value 18.4f hours in to
'int hours = 18;'
and
'int minutes = 40;'
????
public Date getWorkdayIncrement(Calendar date, float incrementInWorkdays) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm");
System.out.println("start day: " + f.format(date.getTime()));
// so if chosen incrementDays is 2.3 days and workinghours pr day is 7.5h you multiply them together to get total working hours
float totalHoursWorkedPrDay = getWorkdayStartAndStop() * incrementInWorkdays;
// needed to convert hours and minutes to integer values in order to increment calendar
int hoursToIncrement = (int) totalHoursWorkedPrDay; //gets only hours
// get the last to decimals 0.25 representing the minutes which means 25 percent of 60min
float lastTwoDecimalsOfTotalWorkingHours = ((totalHoursWorkedPrDay - (float) hoursToIncrement) * 100);
//calculate percent of minutes and convert to integer (25 / 100 * 60) = 15min
int minutesToIncrement = (int) ((lastTwoDecimalsOfTotalWorkingHours / 100) *60);
System.out.println("Hours to increment: " + hoursToIncrement);
System.out.println("Minutes to increment: " + minutesToIncrement);
//increment calendar
date.add(Calendar.HOUR_OF_DAY, hoursToIncrement);
date.add(Calendar.MINUTE, minutesToIncrement);
Date endDate = date.getTime();
System.out.println("End date excluding holidays: " + f.format(endDate));
}
I didn’t follow everything you were doing in the question. So for now I focus on calculating the working hours of a working day given start and end time. I am assuming that you get start and end as GregorianCalendar objects from some legacy API that you cannot afford to upgrade to java.time just now.
I recommend that you use java.time, the modern Java date and time API, for your date and time work. Even if you are getting your data as objects of the old-fashioned date-time types. java.time is so much nicer to work with.
static float getHoursBetween(Calendar start, Calendar end) {
ZonedDateTime startZdt = ((GregorianCalendar) start).toZonedDateTime();
ZonedDateTime endZdt = ((GregorianCalendar) end).toZonedDateTime();
long wholeDays = ChronoUnit.DAYS.between(startZdt, endZdt);
startZdt = startZdt.plusDays(wholeDays);
Duration workDay = Duration.between(startZdt, endZdt);
return (float) workDay.toMinutes() / (float) Duration.ofHours(1).toMinutes();
}
The method ignores the date difference between start and end by adding the number of full days to the start time. This means that the result we get is based only on the hours and minutes. To try the method out, let’s construct a couple of GregorianCalendar objects (of course I am using java.time for that too):
ZoneId zone = ZoneId.of("America/Araguaina");
ZonedDateTime workDayStart = ZonedDateTime.of(2020, 6, 14, 7, 30, 0, 0, zone);
Calendar workDayStartCal = GregorianCalendar.from(workDayStart);
ZonedDateTime workDayEnd = ZonedDateTime.of(2020, 6, 15, 15, 15, 0, 0, zone);
Calendar workDayEndCal = GregorianCalendar.from(workDayEnd);
float workingDay = getHoursBetween(workDayStartCal, workDayEndCal);
System.out.println(workingDay);
Output from the example is:
7.75
Edit: for the opposite conversion this works since Java 9:
float hours = 7.75f;
Duration dur = Duration.ofMinutes(Math.round(hours * Duration.ofHours(1).toMinutes()));
System.out.println("Hours: " + dur.toHours());
System.out.println("Minutes: " + dur.toMinutesPart());
Hours: 7
Minutes: 45
Link
Oracle tutorial: Date Time explaining how to use java.time.
I am creating a Calendar instance, and setting the date to July 1, 1997 like so:
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(1997, 6, 1);
What I want to do, is that without using an external library, get the following output from that date (proper calculating of leap years / seconds would be good, but not required) prior to current date (e.g. November 1, 2015 02:45:30):
18 years, 4 months, 0 days, 2 hours, 45 minutes, 30 seconds
I am not quite sure if this is possible at all. I've tried some weird, not very logical calculations, which needed lots of improvements, but couldn't make it work:
int years = currentYear - calendar.get(Calendar.YEAR);
int months = calendar.get(Calendar.MONTH);
if(currentMonth > months) {
years -= 1;
}
UPDATE - Code until now:
Calendar currentDate = Calendar.getInstance();
currentDate.clear();
Calendar birthDate = Calendar.getInstance();
birthDate.clear();
birthDate.set(this.birthYear, this.birthMonth - 1, this.birthDay);
Calendar date = Calendar.getInstance();
date.clear();
date.setTimeInMillis(birthDate.getTimeInMillis() - currentDate.getTimeInMillis());
System.out.println(Integer.toString(date.get(Calendar.YEAR)));
if you are using java 8 then you have LocalDateTime and PlainTimeStamp classes to use
here you find some answers Java 8: Calculate difference between two LocalDateTime
This might help
Calendar startCalendar = Calendar.getInstance();
startCalendar.clear();
startCalendar.set(1997, 6, 1);
Date start = startCalendar.getTime();
Calendar endCalendar = Calendar.getInstance();
// endCalendar.clear();
// endCalendar.set(2015, 10, 1);
Date end = endCalendar.getTime();
long diff = end.getTime() - start.getTime();
long days = TimeUnit.MILLISECONDS.toDays(diff);
long hours = TimeUnit.MILLISECONDS.toHours(diff) % TimeUnit.DAYS.toHours(1);
long minutes = TimeUnit.MILLISECONDS.toMinutes(diff) % TimeUnit.HOURS.toMinutes(1);
long seconds = TimeUnit.MILLISECONDS.toSeconds(diff) % TimeUnit.MINUTES.toSeconds(1);
System.out.println(days + " " + hours + " " + minutes + " " + seconds);
from the days we can write the logic to find the number of leap years, months using modulo division
Java 8 has a new Date API you can try that too since you're using Java 8
This question already has answers here:
Calculating the difference between two Java date instances
(45 answers)
Closed 7 years ago.
Im trying to calculate the time difference between 2 Timestamps, this is the code:
Calendar calendar = Calendar.getInstance();
java.util.Date now = calendar.getTime();
Timestamp currentTimestamp = new Timestamp(now.getTime());
System.out.println("Current\n"+currentTimestamp);
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date date = dateFormat.parse("28/02/2015");
Timestamp timestampBefore = new Timestamp(date.getTime());
System.out.println("Before\n"+timestampBefore);
Timestamp calculated = new Timestamp(currentTimestamp.getTime() - timestampBefore.getTime());
System.out.println("Calculated\n"+calculated);
Output:
Current
2015-02-28 12:12:40.975
Before
2015-02-28 00:00:00.0
Calculated
1970-01-01 13:12:40.975
I can understand why it returns 1970-01-01 but why does it return 13:12:40.975 ,1 hour more?
How to calculate the difference between 2 dates so the output is like this (based on this example):
Years:0, Months:0, Days:0, Hours:12, Minutes:12, Seconds:40 ?
Update: for java below 1.8 check out http://www.joda.org/joda-time/index.html
and for java 1.8 see answer.
Similar solution here: Java 8: Calculate difference between two LocalDateTime
(1) A timestamp is a point in time. If you calculate the difference between two timestamps, the result is not a timestamp (point in time), but a duration. So it is nonsense to convert the difference to a timestamp, hence it is useless to discuss the reason why the result is strange.
(2) You should probably use the new Java 8 time API (if you are able to use Java 8):
LocalTime now = LocalTime.now();
LocalTime previous = LocalTime.of(0, 0, 0, 0);
Duration duration = Duration.between(previous, now);
System.out.println(now);
System.out.println(previous);
System.out.println(duration);
Note that this just calculates the duration between two times of a day (hour-minute-second). If your want to include date information, use LocalDateTime instead:
LocalDateTime nextFirework = LocalDate.now()
.with(TemporalAdjusters.firstDayOfNextYear())
.atTime(LocalTime.MIDNIGHT);
LocalDateTime now = LocalDateTime.now();
// duration (in seconds and nanos)
Duration duration = Duration.between(now, nextFirework);
// duration in total hours
long hours = now.until(nextFirework, ChronoUnit.HOURS);
// equals to: duration.toHours();
If you want to have 'normalized' duration in years/months/days/hours/seconds, there is suprisingly no direct support. You could convert the duration to days, hours, minutes and seconds by yourself:
long d = duration.toDays();
long h = duration.toHours() - 24 * d;
long m = duration.toMinutes() - 60 * duration.toHours();
long s = duration.getSeconds() - 60 * duration.toMinutes();
System.out.println(d + "d " + h + "h " + m + "m " + s + "s ");
But note that you will have difficulties converting the days into months and years, as there is no unique number of days per month and a year can be a leap year with 366 days. For that, you can use Period, as in opposite to Duration, this class is associated with a timeline. Unfortunately, Period does only support dates, but no times:
// period in years/months/days (ignoring time information)
Period p = Period.between(now.toLocalDate(), nextFirework.toLocalDate());
System.out.println(p); // or use p.getYears(), p.getMonths(), p.getDays()
So probably you could combine both approaches - first, compute the Period from the dates and then the Duration using the times. Note that the duration can be negative, so you'll have to take care of that in case of:
Duration dur = Duration.between(start.toLocalTime(), end.toLocalTime());
LocalDate e = end.toLocalDate();
if (dur.isNegative()) {
dur = dur.plusDays(1);
e = e.minusDays(1);
}
Period per = Period.between(start.toLocalDate(), e);
System.out.println(per.toString() + ", " + dur.toString());
Here is the code, for your viewing pleasure:
public static void main(String[] args) throws Exception {
Calendar cal = Calendar.getInstance();
cal.set(2010, Calendar.JULY, 10, 1, 0, 20);
Date d1 = cal.getTime();
Date d2 = new Date();
int seconds = 22;
d2.setTime(d1.getTime() - seconds*1000);
SimpleDateFormat iso_format = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss");
System.out.println(iso_format.format(d1) + " - " + seconds + "s = " + iso_format.format(d2));
}
Output: 2010-07-10 01:00:20 - 22s = 2010-07-10 24:59:58
Shouldn't the answer be 2010-07-09 24:59:58? Why does it loop back to the same day? Is there a way to fix it?
the hour 24 (since you used 'kk' to format) is considered the next day, ie the 10th. it is equivalent to midnight. I use 'HH' to format hours, which would display the 24th hour as '00'. this makes more sense to me and i believe is more standards compliant.
the date changes over at the 00/24th hour. if you were to subtract another hour from the resulting date, the date would become the 9th as expected.
also, if you want true ISO time format 'HH' is better than 'kk'.
http://en.wikipedia.org/wiki/ISO_8601#Times
Program followed by output. Someone please explain to me why 10,000,000 milliseconds from Jan 1, 1970 is November 31, 1969. Well, someone please explain what's wrong with my assumption that the first test should produce a time 10,000,000 milliseconds from Jan 1, 1970. Numbers smaller than 10,000,000 produce the same result.
public static void main(String[] args) {
String x = "10000000";
long l = new Long(x).longValue();
System.out.println("Long value: " + l);
Calendar c = new GregorianCalendar();
c.setTimeInMillis(l);
System.out.println("Calendar time in Millis: " + c.getTimeInMillis());
String origDate = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DAY_OF_MONTH);
System.out.println("Date in YYYY-MM-DD format: " + origDate);
x = "1000000000000";
l = new Long(x).longValue();
System.out.println("\nLong value: " + l);
c.setTimeInMillis(l);
System.out.println("Calendar time in Millis: " + c.getTimeInMillis());
origDate = c.get(Calendar.YEAR) + "-" + c.get(Calendar.MONTH) + "-" + c.get(Calendar.DAY_OF_MONTH);
System.out.println("Date in YYYY-MM-DD format: " + origDate);
}
Long value: 10000000
Calendar time in Millis: 10000000
Date in YYYY-MM-DD format: 1969-11-31
Long value: 1000000000000
Calendar time in Millis: 1000000000000
Date in YYYY-MM-DD format: 2001-8-8
The dates you print from Calendar are local to your timezone, whereas the epoch is defined to be midnight of 1970-01-01 in UTC. So if you live in a timezone west of UTC, then your date will show up as 1969-12-31, even though (in UTC) it's still 1970-01-01.
First, c.get(Calendar.MONTH) returns 0 for Jan, 1 for Feb, etc.
Second, use DateFormat to output dates.
Third, your problems are a great example of how awkward Java's Date API is. Use Joda Time API if you can. It will make your life somewhat easier.
Here's a better example of your code, which indicates the timezone:
public static void main(String[] args) {
final DateFormat dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
long l = 10000000L;
System.out.println("Long value: " + l);
Calendar c = new GregorianCalendar();
c.setTimeInMillis(l);
System.out.println("Date: " + dateFormat.format(c.getTime()));
l = 1000000000000L;
System.out.println("\nLong value: " + l);
c.setTimeInMillis(l);
System.out.println("Date: " + dateFormat.format(c.getTime()));
}
Calendar#setTimeInMillis() sets the calendar's time to the number of milliseconds after Jan 1, 1970 GMT.
Calendar#get() returns the requested field adjusted for the calendar's timezone which, by default, is your machine's local timezone.
This should work as you expect if you specify "GMT" timezone when you construct the calendar:
Calendar c = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
Sadly, java.util.Date and java.util.Calendar were poorly designed leading to this sort of confusion.
Your timezone is most likely lagging behind GMT (e.g., GMT-5), therefore 10,000,000ms from epoch is December 31 1969 in your timezone, but since months are zero-based in java.util.Calendar your Calendar-to-text conversion is flawed and you get 1969-11-31 instead of the expected 1969-12-31.
You can figure out yourself if you change your first c.setTimeInMillis(l); in c.clear();