user10449394
user10449394

Reputation: 27

java Time difference outputs negative value and wrong values

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.

Upvotes: 1

Views: 3092

Answers (3)

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79005

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.

Upvotes: 0

Anonymous
Anonymous

Reputation: 86203

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.

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 338306

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.

Upvotes: 3

Related Questions