1

I tried the below code. java Time difference outputs negative value and wrong values when there is hours/date difference between starttime and endtime

    Date d3 = null;
    Date d4 = null;
    List<String> activeTimeList = new ArrayList();
    int c = Integer.parseInt(cycle.get("Cycle No:").toString());
    try {
        for(int i = 0; i<c;i++) {
            SimpleDateFormat format = new SimpleDateFormat("MM-dd-yyyy HH:mm", Locale.US); //("MM/dd/yyyy HH:mm:ss");
            format.setTimeZone(TimeZone.getTimeZone("GMT"));
            String cycleEndTm = cycle.get("End Time"+i+"").toString().trim();
            String cycleStartTm = cycle.get("Start Time"+i+"").toString().trim();

            d3 = format.parse(cycleEndTm);
            d4 = format.parse(cycleStartTm);
            long diff =  d3.getTime() - d4.getTime();
            long diffSeconds = diff / 1000 % 60;
            long diffMinutes = diff / (60 * 1000) % 60;
            long diffHours = diff / (60 * 60 * 1000) % 24;
            long diffDays = diff / (24 * 60 * 60 * 1000);
            String time1 = diffDays+"."+diffHours+"."+diffMinutes+"."+diffSeconds;

Log :
0.-10.-7.0

d4 =02-11-2017 16:47
d3 =02-11-2017 17:27 Diff = 0.-10.-7.0 Not able to get correct time difference? What I am missing here? Note ** : cycleEndTm -cycleStarttime should be giving positive result. I am reading cycleEndTime and start time from a map. Thats the requirement.

user10449394
  • 27
  • 1
  • 8
  • 1
    Verify actual value of variable diff , also try https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html class to get time diff – MIK Oct 05 '18 at 19:39
  • 1
    FYI, the terribly troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Oct 05 '18 at 20:08
  • I cannot reproduce your problem. From your sample strings I expect a difference of 40 minutes. [I tried running your code online](https://ideone.com/ErRzJI) and it printed `0.0.40.0`, that is, exactly as expected. – Ole V.V. Oct 06 '18 at 06:51
  • Could your problem lie somewhere else? Could your map contain other values than you expect? Or some third or fourth issue interfere? If you need our help, it’s a very good idea to [create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). – Ole V.V. Oct 08 '18 at 08:42

3 Answers3

3

tl;dr

Use modern java.time classes, never Date. Convert any Date to java.time.Instant.

Duration                              // Represent a span-of-time as 24-hour days, hours, minutes, seconds, and nanoseconds.
.between(                             // Calculate elapsed time.
    javaUtilDateStart.toInstant() ,   // Convert any `java.util.Date` to `java.time.Instant`. Never use `Date`; it is a terrible class, ditto for `Calendar`. 
    javaUtilDateStop.toInstant()
)                                      // Returns a `Duration` object.
.toString()                            // Generate text in standard ISO 8601 format.

Or call the to…Part methods to extract number of days, hours, and so on.

java.time

You are using terrible old date-time classes ( Date, Calendar, SimpleDateFormat ) that were supplanted years ago by the java.time classes.

Instant

Apparently you are being handed a java.util.Date object. If possible, rewrite that code to use its replacement, the java.time.Instant object. Both represent a moment in UTC, but Instant has a finer resolution of nanoseconds rather than milliseconds. If not possible, immediately convert the Date to an Instant. Call new conversion methods added to the old classes.

Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy class to modern class. Same moment in UTC, same point on the timeline.

Generate text representing that moment in standard ISO 8601 format.

Capture the current moment in UTC.

Instant now = Instant.now() ;  // Capture the current moment in UTC.

Duration

Calculate elapsed time as a Duration object. Again, this has a resolution of nanoseconds.

Duration d = Duration.between( instant , now ) ;

Generate text representing that elapsed time in standard ISO 8601 format.

String output = d.toString() ; 

Or create your string, defining “days” as 24-hour chunks of time without regard for the calendar.

long days = d.toDaysPart() ;
int hours = d.toHoursPart() ;
int minutes = d.toMinutesPart() ;
int seconds = d.toSecondsPart() ;

String output = days + "." + hours + "." + minutes + "." + seconds ;

Parsing strings

Your Question is not clear about your inputs. If starting with strings, parse. Firstly, if possible, change the source of those strings to use the wisely-defined ISO 8601 standard formats. If not possible, define a formatting pattern to match the input.

String inputStart = "02-10-2018 10.30";

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu HH.mm" );

Parse your input as a LocalDateTime because it lacks any indicator of a time zone or offset-from-UTC.

LocalDateTime ldtStart = LocalDateTime.parse( inputStart , f ) ;

A LocalDateTime is not a moment, does not represent a point on the timeline. This class represents potential moments along a range of about 26-27 hours, the range of time zones around the globe.

Assign the zone or offset intended by the source of that string, to give the context needed to determine a moment.

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(!).

// Transform the indefinite `LocalDateTime` to a definite moment by assigning the time zone intended by that input string.
ZoneId z = ZoneId.of( "Africa/Tunis" );
ZonedDateTime zdtStart = ldtStart.atZone( z );

Calculate your span of time elapsed.

Duration d = Duration.between( zdtStart , zdtStop ) ;

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?

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thanks for showing the good way. As I read the quesiton the start and end datetimes come from strings originally, and are only then parsed into `java.util.Date`s. I know that your suggestion (and mine) would be to avoid that step and parse into a class from *java.time* instead. I believe you can make this point yet clearer. – Ole V.V. Oct 06 '18 at 06:16
  • @Basil Bourque : How will I parse the string date in this case? I tried as below and getting "Exception in thread "main" java.time.format.DateTimeParseException:" String s1 = "02-10-2018 10.30"; String s2 = "02-10-2018 17.35"; Instant instant1 = Instant.parse(s1); Instant instant2 = Instant.parse(s2); and also tried DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT; final ZonedDateTime date1 = ZonedDateTime.parse(s1, formatter.withZone(ZoneId.of("UTC"))); final ZonedDateTime date2 = ZonedDateTime.parse(s2, formatter.withZone(ZoneId.of("UTC"))); – user10449394 Oct 07 '18 at 19:29
  • @user10449394 Your Question is not clear about exactly what are your inputs. Are you working with strings or with `java.util.Date` objects? Edit the Question to clarify. – Basil Bourque Oct 07 '18 at 19:38
  • FYI, parsing strings into date-time values has been covered thousands of times already on Stack Overflow. Always search Stack Overflow thoroughly before posting. – Basil Bourque Oct 07 '18 at 19:40
  • 1
    @user10449394 I added a brief section on parsing input strings. Search Stack Overflow for more discussion and examples. – Basil Bourque Oct 07 '18 at 19:47
  • @user10449394 Reread my Answer more carefully. And read the class documentation. `toHoursPart` is very different from `toHours`. – Basil Bourque Oct 07 '18 at 20:22
  • `Call requires API level 26 (current min is 21): java.util.Date#toInstant` (: – Abu Saeed Jan 06 '22 at 06:16
  • @Unknown2433 No longer true. The latest Android tooling uses “API desugaring” to bring most of the *java.time* functionality to earlier Android. Alternatively, add the ThreeTenABP library to your project for a back-port of most of *java.time*. – Basil Bourque Jan 06 '22 at 06:56
  • I have just tested it in latest version of android studio – Abu Saeed Jan 06 '22 at 07:36
  • 1
    @Unknown2433 [A quick search](https://duckduckgo.com/?q=android+api+desugaring+java.time&t=iphone&ia=web) reveals this Android Studio documentation page, [*Java 8+ APIs available through desugaring*](https://developer.android.com/studio/write/java8-support-table). – Basil Bourque Jan 06 '22 at 16:12
1

java.time

This answer does not solve the problem you asked about. In any case I recommend using java.time, the modern Java date and time API. The first version here requires at least Java 9. Java 6, 7 and 8 follow further down.

    Map<String, Object> cycle = Map.of("Cycle No:", 1, 
            "Start Time0", "02-11-2017 16:47",
            "End Time0", "02-11-2017 17:27");

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm", Locale.US);
    List<String> activeTimeList = new ArrayList<>();
    int c = Integer.parseInt(cycle.get("Cycle No:").toString());
    try {
        for(int i = 0; i < c; i++) {
            String cycleEndTm = cycle.get("End Time" + i).toString().trim();
            String cycleStartTm = cycle.get("Start Time" + i).toString().trim();

            LocalDateTime d4 = LocalDateTime.parse(cycleStartTm, formatter);
            LocalDateTime d3 = LocalDateTime.parse(cycleEndTm, formatter);

            Duration diff = Duration.between(d4, d3);
            String time1 = String.format(Locale.US, "%d.%d.%d.%d", 
                    diff.toDays(), diff.toHoursPart(), 
                    diff.toMinutesPart(), diff.toSecondsPart());

            activeTimeList.add(time1);
        }
        System.out.println(activeTimeList);
    } catch (DateTimeParseException dtpe) {
        System.err.println(dtpe);
    }

This prints:

[0.0.40.0]

The toXxxPart methods of the Duration class that I use for formatting are not in Java 8. So if you are using Java 8, formatting can be done in this way instead:

            long days = diff.toDays();
            diff = diff.minusDays(days);
            long hours = diff.toHours();
            diff = diff.minusHours(hours);
            long minutes = diff.toMinutes();
            diff = diff.minusMinutes(minutes);
            long seconds = diff.getSeconds();
            String time1 = String.format(Locale.US, "%d.%d.%d.%d", 
                    days, hours, minutes, seconds);

The output is the same as before.

If anyone reading along is using Java 6 or 7, get the ThreeTen Backport library, and the Java 8 code should work there too.

What went wrong in your code?

The only problem in the code you’ve posted is that you are using the long outdated and poorly designed date and time classes, Date, SimpleDateFormat and TimeZone. There is nothing in your code explaining how you got wrong, negative values, so I suspect that you weren’t parsing the strings you thought you were parsing.

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

You can use 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.

Demo:

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String d4 = "02-11-2017 16:47";
        String d3 = "02-11-2017 17:27";

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd-MM-uuuu HH:mm", Locale.ENGLISH);

        // The given date-times in UTC
        OffsetDateTime odtD4 = LocalDateTime.parse(d4, dtf).atOffset(ZoneOffset.UTC);
        OffsetDateTime odtD3 = LocalDateTime.parse(d3, dtf).atOffset(ZoneOffset.UTC);

        // Duration between the date-times in UTC
        Duration duration = Duration.between(odtD4, odtD3);
        System.out.println(duration);    
        
        // Custom format
        // ####################################Java-8####################################
        String formattedDuration = String.format("%02d.%02d.%02d.%02d", duration.toDays(), duration.toHours() % 24,
                duration.toMinutes() % 60, duration.toSeconds() % 60);
        System.out.println(formattedDuration);
        // ##############################################################################

        // ####################################Java-9####################################
        formattedDuration = String.format("%02d.%02d.%02d.%02d", duration.toDaysPart(), duration.toHoursPart(),
                duration.toMinutesPart(), duration.toSecondsPart());
        System.out.println(formattedDuration);
        // ##############################################################################
    }
}

Output:

PT40M
00.00.40.00
00.00.40.00

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

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