1

I am having an issue while parsing day and time to get the total hours and minutes in java.

If I calculate total hours starting from 'Mon 22:00' to 'Tue 22:00' then I am getting correct total hours 24. But If I calculate total hours starting from 'Wed 22:00' to current day and time like 'Thu 12:45' then I am getting 'Hours : -153 Min : -15'.

This is the case only for Wednesday and Thursday. For rest of the days it is working fine.

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Test {

    private static DateFormat dateFormat = new SimpleDateFormat("EEE HH:mm");
    private static DateFormat dayFormat = new SimpleDateFormat("E");
    private static Calendar calendar = Calendar.getInstance();

    public static void main(String[] args) {        

        try {

            Date date1 = dateFormat.parse("Wed 22:00");
            Date date2 = dateFormat.parse(dayFormat.format(calendar.getTime()) + " " + calendar.getTime().getHours() + ":"
                    + calendar.getTime().getMinutes());

            long hours2 = getDurationInHours(date1, date2);
            long min = getDurationInMin(date1, date2);

            System.out.println("Hours : " + hours2 + " Min : " + min);

        } catch (Exception e) {
            e.printStackTrace();
        }       
    }

    public static long getDurationInHours(Date returnTime, Date leaveTime) {
        long durationInMillis = leaveTime.getTime() - returnTime.getTime();
        long hours = TimeUnit.MILLISECONDS.toHours(durationInMillis);
        return hours;
    }

    public static long getDurationInMin(Date returnTime, Date leaveTime) {
        long durationInMillis = leaveTime.getTime() - returnTime.getTime();
        long min = TimeUnit.MILLISECONDS.toMinutes(durationInMillis) % 60;
        return min;
    }

}
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    Did you try printing the two dates? – BackSlash Aug 23 '18 at 07:23
  • I recommend you avoid the `SimpleDateFormat` class. It is not only long outdated, it is also notoriously troublesome. Today we have so much better in [`java.time`, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Aug 23 '18 at 08:00
  • What is the desired result exactly? The hours and minutes from the first day and time to *the following* occurrence of the second day of week at the given time? What if both days are the same, for instance, from Wed 14:00 to Wed 22:00, or from Wed 22:00 to Wed 14:00? – Ole V.V. Aug 23 '18 at 12:56

3 Answers3

4

That's because when you define your date as Wed 22:00 it's not the Wednesday the current week. It is actually 7th of January 1970 ;)

As you probably know the dates in java work as long number since 01.01.1970. So when you say Wednesday and not an actual date it gets the first Wednesday after 1970 which is the 7th. When you say Mon 22:00 it works properly because it uses 5th of January 1970 and the difference is 2 days.

When you use "Thu 22:00" in the same logic it uses 1st of January 1970 (which is a Thursday) and that's why you get negative numbers. Because it's actually 6 days ahead

Veselin Davidov
  • 7,031
  • 1
  • 15
  • 23
1

java.time

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE H:mm", Locale.ENGLISH);

    ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Dominica"));
    TemporalAccessor fromTa = formatter.parse("Wed 22:00");
    ZonedDateTime from = now
            .with(TemporalAdjusters.previous(DayOfWeek.from(fromTa)))
            .with(LocalTime.from(fromTa));
    Duration dur = Duration.between(from, now);
    System.out.println(dur);

Running this code just now gave the following output:

PT11H41M25.284611S

This means a duration of 11 hours 41 minutes 25.284611 seconds. If you need to use this duration further in your program, you will probably want to keep the Duration object. For printing to the user it’s not so nice, so do that this way:

    long hours = dur.toHours();
    int min = dur.toMinutesPart();
    System.out.println("" + hours + " hours " + min + " min");

11 hours 41 min

Java doesn’t have a concept of “Wednesday at 22:00”. So we need to pick a specific Wednesday and a specific Thursday. SimpleDateFormat was trying to be helpful to you and picked for you, but didn’t pick the Wednesday and the Thursday you had expected, so didn’t do you any favour. java.time, the modern Java date and time API, forces us to pick ourselves. I much prefer this. It makes it much clearer exactly what happens in the code, which in turn will also make it easier to spot and fix any errors.

My use of TemporalAdjusters.previous ensures that I get last Wednesday (not next Wednesday). And my use of ZonedDateTime ensures that the hours and minutes are correct. Even across transitions to and from summer time (daylight saving time) and other such time transitions. You should of course fill in your own time zone if it didn’t happen to be America/Dominica.

Link: Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.

You can do it in an intuitive way with java.time.Duration which is modelled on ISO-8601 standards and was introduced with Java-8 as part of JSR-310 implementation. With Java-9 some more convenience methods were introduced.

Note that a weekday and time e.g. Mon 22:00 is not sufficient to create a date-time object directly. A weekday e.g. Monday comes in every week and therefore we will have to create a date-time object defaulting to a particular date e.g. today. From this date as a reference, you can find the first occurrence (LocalDate) of the given weekday (e.g. Monday) and from that object, you can obtain a date-time object with the given time.

Duration#between gives you the Duration object between the two date-time objects. Using the Duration object, you can create a string formatted as per your requirement, by getting days, hours, minutes, seconds from it.

Demo:

import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        // Test
        displayDuration(getDuration("Mon 22:00", "Tue 22:00"));
        displayDuration(getDuration("Wed 22:00", "Thu 12:45"));
    }

    static Duration getDuration(String strStartDayTime, String strEndDayTime) {
        LocalDate today = LocalDate.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE HH:mm", Locale.ENGLISH);
        TemporalAccessor taStart = dtf.parse(strStartDayTime);
        TemporalAccessor taEnd = dtf.parse(strEndDayTime);

        LocalDateTime ldtStartDateTime = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.from(taStart)))
                .atTime(LocalTime.from(taStart));
        LocalDateTime ldtEndDateTime = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.from(taEnd)))
                .atTime(LocalTime.from(taEnd));

        return Duration.between(ldtStartDateTime, ldtEndDateTime);
    }

    static String formatDurationJava8(Duration duration) {
        return String.format("Days: %d, Hours: %02d, Minutes: %02d, Seconds: %02d", duration.toDays(),
                duration.toHours() % 24, duration.toMinutes() % 60, duration.toSeconds() % 60);
    }

    static String formatDurationJava9(Duration duration) {
        return String.format("Days: %d, Hours: %02d, Minutes: %02d, Seconds: %02d", duration.toDaysPart(),
                duration.toHoursPart(), duration.toMinutesPart(), duration.toSecondsPart());
    }

    static void displayDuration(Duration duration) {
        // Default format
        System.out.println(duration);
        // Custom format
        System.out.println(formatDurationJava8(duration));
        System.out.println(formatDurationJava9(duration));
    }
}

Output:

PT24H
Days: 1, Hours: 00, Minutes: 00, Seconds: 00
Days: 1, Hours: 00, Minutes: 00, Seconds: 00
PT14H45M
Days: 0, Hours: 14, Minutes: 45, Seconds: 00
Days: 0, Hours: 14, Minutes: 45, Seconds: 00

Learn about the modern date-time API from Trail: Date Time.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110