Hot questions for Using Joda-Time in duration

Question:

For instance, I know of the .minusDays(int) and .plus(int) methods in the DateTime class of joda.

But when I'm working with a joda Duration, with no specific point of time attached to, is there a simple and nice way to subtract or add a specific amount of time, like a day?

Something like:

new Duration(startTime, endTime).minusDays(2);

Answer:

tl;dr

Joda-Time offers methods Duration::plus & Duration::minus.

myDuration.minus(                // A span of time unattached to the timeline. Consists of a number of milliseconds.
    Duration.standardDays( 2 )   // Generic 24-hour days.
)

Better to migrate to java.time:

myDuration.minus(                // A span of time unattached to the timeline. Consists of a number of whole seconds plus a number of nanoseconds.
    Duration.ofDays( 2 )         // Generic 24-hour days.
)
No such thing as days

Duration(startTime, endTime).minusDays(2)

A Duration in Joda-Time represents a count of milliseconds. This span of time in not attached to the timeline. So there is no notion of "days".

To quote the documentation:

An immutable duration specifying a length of time in milliseconds.

A duration is defined by a fixed number of milliseconds. There is no concept of fields, such as days or seconds, as these fields can vary in length.

So while you cannot add/subtract days, you can add/subtract the number of milliseconds is the 24-hour period of a generic day, without regard to issues of the calendar and without regard to anomalies such as Daylight Saving Time (DST).

Use the java.util.concurrent.TimeUnit enum to calculate that number of milliseconds.

long millis = TimeUnit.DAYS.toMilliseconds( 2 ) ; // 2 days of 24 hours * 60 minutes per hour * 60 seconds per minute * 1,000 milliseconds per second.

Duration twoGenericDays = Duration.millis( millis ) ;

The Duration class offers a shortcut for that work, the static method Duration.standardDays where "standard" means generic 24-hour chunks of time unrelated to the calendar.

Duration twoGenericDays = Duration.standardDays( 2 ) ;  // Two generic 24-hour days, unrelated to the calendar.

Add.

Duration d = myDuration.plus( twoGenericDays ) ;

Subtract.

Duration d = myDuration.minus( twoGenericDays ) ;
java.time

The Joda-Time project is now in maintenance-mode, and recommends migrating to its successor, the java.time classes.

Same concepts as discussed above, where a java.time.Duration represents a span of time unattached to the timeline. Except the resolution is finer: nanoseconds rather than milliseconds. Technically, a Duration consists of (a) a number of whole seconds and (b) a count of nanoseconds making up the fractional second.

Duration twoGenericDays = Duration.ofDays( 2 ) ;  // Two generic days, each being a 24 hours long.

Add.

Duration d = myDuration.plus( twoGenericDays ) ;

Subtract.

Duration d = myDuration.minus( twoGenericDays ) ;

Like Joda-Time, the java.time classes are built on the immutable objects pattern. Rather than alter ("mutate") an object, a fresh separate object is instantiated.


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.

Question:

I have a duration, e.g. like this:

PT-27900S = -27900000 milliseconds

and I want to convert this into a String with format (+/-HH:mm) Ich I convert the a negativ duration to String with:

final PeriodFormatter hoursAndMinutesFormatter = new PeriodFormatterBuilder()
                                                .printZeroAlways()
                                                .minimumPrintedDigits(2)
                                                .appendHours()
                                                .appendSeparator(":")
                                                .appendMinutes()
                                                .toFormatter();

I get e.g. this result:

-07:-45

but I want like this.

-07:45

Is there any possibility to format it like this?


Answer:

Well, there is no format support in Joda-Time for the way you want to see negative durations. The nearest what I can see per documentation (indicated by the name) might be the method rejectSignedValues(). I have tested it in every possible configuration (inserted it at the begin, directly before appendMinutes(), directly behind, with the undocumented argument false or true in all cases), but it does not do anything in my tests (see updated side-note).

So there are logically only two options left:

a) Write this hack:

PeriodFormatter hoursAndMinutesFormatter = new PeriodFormatterBuilder()
.printZeroAlways()
.minimumPrintedDigits(2)
.appendHours()
.appendSeparator(":")
//.rejectSignedValues(true) // no effect ?!
.appendMinutes()
.toFormatter();     

long millis = -27900000;
Duration d =  new Duration(millis);
Period p = d.toPeriod();
String s;
if (millis < 0) {
    s = "-" + hoursAndMinutesFormatter.print(p.negated());
} else {
    s = hoursAndMinutesFormatter.print(p);
}

System.out.println("Joda-Time: " + s); // -07:45

b) Alternatively switch to another library which has better support for negative durations.

=> Example for built-in XML-duration which has better sign handling but no real formatter beyond XML-Schema compatible representation (although you are free to query the single components of duration and format it with let's say java.util.Formatter).

long millis = -27900000;
javax.xml.datatype.Duration xmlDuration = 
    DatatypeFactory.newInstance().newDuration(millis);
System.out.println("XML: " + xmlDuration.toString()); // -P0Y0M0DT7H45M0.000S
System.out.println(
    String.format(
        "%1$s%2$02d:%3$02d", 
        xmlDuration.getSign() < 0 ? "-" : "+", 
        xmlDuration.getHours(), 
        xmlDuration.getMinutes())); // -07:45

=> Or use my library Time4J which has a pattern-based duration formatter:

long millis = -27900000;
Duration<ClockUnit> duration = Duration.of(millis, ClockUnit.MILLIS); // create
duration = duration.with(Duration.STD_CLOCK_PERIOD); // normalization to hours and minutes
String s = Duration.formatter("-hh:mm").format(duration);
System.out.println("Time4J: " + s); // -07:45

Side notes:

  1. The new Java-8 classes Duration and Period have the same sign handling as Joda-Time (preferring signs not in front but before every single component). You can see it when you watch the output of method toString(). Any special duration formatter is not offered.

  2. The sign handling of Joda-Time is not compatible with XML-Schema v1.1 (look especially at the section: 3.3.6.2 Lexical Mapping).

  3. Now I have found out what the method rejectSignedValues() is for. It is relevant for parsing only and just controls the exception behaviour (configurable via its boolean argument). However, parsing a value like "-07:45" would yield an unexpected millis-value different from your input (-22500000 != -27900000).

Question:

I am trying to sum durations in the following format: "hh:mm:ss" (e.g.: "08:55:12") using Joda Time:

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .printZeroAlways().minimumPrintedDigits(2).appendHours()
        .appendLiteral(":").printZeroAlways().printZeroAlways()
            .minimumPrintedDigits(2).appendMinutes().appendLiteral(":")
                .printZeroAlways().minimumPrintedDigits(2).appendSeconds()
                    .toFormatter();
Duration totalTime = Duration.ZERO;


for (Entry entry : entries) {
    Period period = formatter.parsePeriod(entry.getTime());
    Duration duration = period.toStandardDuration();
    totalTime = totalTime.plus(duration);
}

Period totalPeriod = totalTime.toPeriod();
if (totalPeriod.getHours() < 10) {
    hours = "0" + totalPeriod.getHours();
} else {
    hours = Integer.toString(totalPeriod.getHours());
}
mTextView.setTextView(hours
    + String.format("%02d:%02d", totalPeriod.getMinutes(),
        totalPeriod.getSeconds()));

For some reason, it is giving me wrong results (summed durations is way too long). Can you help me find the cause of this problem?


Answer:

I think you have just forgotten a colon between hour-part and minute part causing the total sum looking like 2701:44 instead of 27:01:44 (this is an example for the sum of the three elements "03:20:45", "00:40:11", "23:00:48" which is correctly calculated by Joda-Time).

So your solution should finally look like:

String output =
    hours + String.format(":%02d:%02d", totalPeriod.getMinutes(), totalPeriod.getSeconds());
System.out.println(output); // 27:01:44

But easier is just to reuse your formatter object for printing:

System.out.println(formatter.print(totalPeriod)); // 27:01:44

If you are interested in a pattern-based solution then check out my library Time4J with this example:

// input
String[] periods = { "03:20:45", "00:40:11", "23:00:48" };

// initialization
Duration.Formatter<ClockUnit> timeFormat = 
    Duration.Formatter.ofPattern(ClockUnit.class, "hh:mm:ss");
Duration<ClockUnit> dur = Duration.ofZero();

// calculate the sum
for (String entry : periods) { 
    dur = dur.plus(timeFormat.parse(entry));
}

dur = dur.with(Duration.STD_CLOCK_PERIOD); // normalization
System.out.println(timeFormat.format(dur)); // 27:01:44

Question:

I'm using the jodatime library. I want to display a count-down between now and an end time and display it to the user. I get the duration that I want to display via:

Interval time = new Interval(DateTime.now(), end_time);
Duration duration = time.toDuration();

Now I want to convert the duration into a string formatted as hh:mm:ss. What is the most straightforward way to do this?


Answer:

Use the DurationUtils from apache.commons.lang:

import org.apache.commons.lang.time.DurationFormatUtils;

DurationFormatUtils.formatDuration(duration.getMillis(), "HH:mm:ss")

Or you use:

DateFormat df = new SimpleDateFormat("hh:mm:ss");
String formatted = df.format(new Date(duration.getMillis()));

Or another very short solution:

String formatted = String.format("%1$tI:%1$tM:%1$tS", duration.getMillis());

Question:

I want to parse a string like 1d2h3m4s into a java.time.Duration. I can use a joda's PeriodFormatter to parse the string into a org.joda.time.Duration but I can't figure out how to convert that to a standard Java8's java.time.Duration.

I have to interface to some "legacy" code that already expects java.time.Duration as input, but I want to use joda's parsePeriod because the java.time.Duration.parse() only accepts ISO-8601 duration format (1d2h3m4s is not ISO-8601 duration compliant)

import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;

...

final PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
        .printZeroNever()
        .appendDays().appendSuffix("d")
        .appendHours().appendSuffix("h")
        .appendMinutes().appendSuffix("m")
        .appendSeconds().appendSuffix("s").toFormatter();

org.joda.time.Duration myduration = periodFormatter.parsePeriod("1d1s").toStandardDuration(); 
java.util.time myduration2 = XXXXX

Please bear in mind that I'm not trying to remove the usage of org.joda.time.Period from my code like in Converting org.joda.time.Period to java.time.Period. I still want to have a org.joda.time.Period because I have more parsing option to generate those, and I need a java.time.Period/java.time.Duration because I interact with some other API/libraries that expect java.time.*.

So, is there a way to convert a org.joda.time.Duration to a java.time.Duration?


Answer:

I am presenting three options

  1. Hand modify the string to ISO 8601 format so that java.time.Duration can parse it.
  2. Convert through count of milliseconds.
  3. Convert from joda.time.Duration.toString().
Modify the string

Personally I don’t think I would want to depend in Joda-Time for this. Instead I would modify your non-ISO 8601 string to ISO 8601 format.

    String durationString = "1d1s";
    // Convert to ISO 8601 format
    String isoString = durationString.replaceFirst("(.*?[Dd])?(.*)", "P$1T$2");
    java.time.Duration dur = java.time.Duration.parse(isoString);
    System.out.println(dur);

Output is:

PT24H1S

I admit that the regular expression is a bit hard to read. If there is an uppercase or lowercase D in the string, everything up to and including that D is placed before the T in the ISO string. Everything else is placed after the T.

Convert over milliseconds

This is an idea similar to the one in the answer by ecerulm. I didn’t want to lose the millisecond precision of the Joda-Time duration, so I rely of milliseconds rather than seconds (I know that for your example strings it will make no difference).

    final PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
            .printZeroNever()
            .appendDays().appendSuffix("d")
            .appendHours().appendSuffix("h")
            .appendMinutes().appendSuffix("m")
            .appendSeconds().appendSuffix("s").toFormatter();

    org.joda.time.Duration myduration = periodFormatter.parsePeriod("1d1s").toStandardDuration(); 

    java.time.Duration dur = java.time.Duration.ofMillis(myduration.getMillis());

The result is the same as before.

Convert over a string
    java.time.Duration dur = java.time.Duration.parse(myduration.toString());

Again the result is the same. Formatting the duration into an ISO 8601 string only to parse it back feels like a waste, though.

Question:

I have a list of Joda Duration objects. I want to convert them to a list of strings so that the strings are legible to humans and the list is sortable lexicographically. The range of data is anywhere from 5 minutes to a week. I have thought of the following options:

  1. Milliseconds. This is not sortable and so doesn't work (11 comes before 3 lexicographically) and also not readable.
  2. ISO 8601 (25 hours and 2 minutes would be 0000-00-01T01:02). This is sortable but very hard to read.
  3. ISO 8601 duration (same example as above would be P1DT1H2M). This is neither readable nor sortable.
  4. Something like DD:HH:mm (e.g. 01:01:02). This is a bit hard to read but it is sortable at least.

Is there a standard textual representation of durations for this purpose that won't surprise users? If so is there a standard function that knows how to generate this textual representation?


Answer:

Regarding the fact that you have ranges from minutes to weeks, I suggest you to apply the alternative duration format of ISO-8601. This standard was explicitly developed to supply lexicographically sorted representations. Joda-Time has some support here:

Duration d = new Duration(100000000L); // in milliseconds
Period p = d.toPeriodFrom(new DateTime(), PeriodType.yearWeekDayTime());
PeriodFormatter f = ISOPeriodFormat.alternateExtendedWithWeeks();
String s = f.print(p);
System.out.println(s); 
// P0000-W00-01T03:46:40 (one day, 3 hours, 46 minutes and 40 secs)

Maybe the year portion will still disturb you but that is just the ISO-representation and will always have the value 0000 with your duration values.

Update and minor correction:

Just to clarify, strictly speaking, the form "Pyyyy-Www-ddThh:mm:ss" is not really ISO. If you read the original ISO-8601-paper (section 4.4.3.3) then only four alternative forms are mentioned for the calendrical part:

  • PYYYY-MM-DD
  • PYYYYMMDD
  • PYYYY-DDD (extended ordinal form)
  • PYYYYDDD (basic ordinal form)

So what Joda-Time offers here is rather an invention not based on ISO-standard but might be convenient for your purpose.

Question:

I have a string in format dHHmm that stands for a duration of time, and want to get the equivalent minutes.

Therefore I'm trying to convert it into a org.joda.time.Duration.getMinutes() object. But there seems to be no kind of DurationParser that could be used for this.

How could I best convert this string?

This is what I tried, but did not work:

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendDays()
    .appendHours()
    .appendMinutes()
    .toFormatter();

assertTrue(formatter.parsePeriod("00310").getMinutes == 190); //failed with =0

Answer:

So, first you want to get org.joda.time.Duration from dHHmm...

import org.joda.time.Duration;

public Duration fromTheFormat( String str ) {
    Duration dayDuration = Duration.standardDays( parseInt( str.substring( 0, 1 ) ) );
    Duration hourDuration = Duration.standardHours( parseInt( str.substring( 1, 3 ) ) );
    Duration minuteDuration = Duration.standardMinutes( parseInt( str.substring( 3, 5 ) ) );

    return dayDuration.plus( hourDuration.getMillis() ).plus( minuteDuration.millis() );
}

or

import org.joda.time.Duration;
import org.joda.time.Period;
import org.joda.time.format.PeriodFormatter;

public Duration fromTheFormat( String str ) {
    Period period = new Period( /* years = */ 0,
                                /* months = */ 0,
                                /* weeks = */ 0,
                                /* days = */ parseInt( str.substring( 0, 1 ) ),
                                /* hours = */ parseInt( str.substring( 1, 3 ) ),
                                /* minutes = */ parseInt( str.substring( 3, 5 ),
                                /* seconds = */ 0,
                                /* millis = */ 0
    );

    // or best one is
    PeriodFormatter pf = new PeriodFormatterBuilder()
                           .minimumPrintedDigits( 1 )
                           .maximumParsedDigits( 1 )
                           .appendDays()
                           .minimumPrintedDigits( 2 )
                           .maximumParsedDigits( 2 )
                           .appendHours()
                           .appendMinutes()
                           .toFormatter();

    period = pf.parsePeriod( str );

    return period.toStandardDuration();
}

Question:

I am trying to get formatted string from JodaTime's duration class.

Duration duration = new Duration(durationInSecond * 1000);
PeriodFormatter formatter = new PeriodFormatterBuilder()
                .appendDays()
                .appendSuffix(" days, ")
                .appendHours()
                .appendSuffix(" hours, ")
                .appendMinutes()
                .appendSuffix(" minutes and ")
                .appendSeconds()
                .appendSuffix(" seconds")
                .toFormatter();
String formattedString = formatter.print(duration.toPeriod());

Value of formattedString should be

65 days, 3 hours, 5 minutes and 20 seconds

But It is

1563 hours, 5 minutes, 20 seconds

1563 hours are 65 days and 3 hours but formatter is not printing in that manner.

What I'm missing here?


Answer:

You may use a PeriodType along with Period.normalizedStandard(org.joda.time.PeriodType) to specify which fields you are interested in.

In your case PeriodType.dayTime()seems appropriate .

Duration duration = new Duration(durationInSecond * 1000);
PeriodFormatter formatter = new PeriodFormatterBuilder()
        .appendDays()
        .appendSuffix(" days, ")
        .appendHours()
        .appendSuffix(" hours, ")
        .appendMinutes()
        .appendSuffix(" minutes, ")
        .appendSeconds()
        .appendSuffix(" seconds")
        .toFormatter();

Period period = duration.toPeriod();
Period dayTimePeriod = period.normalizedStandard(PeriodType.dayTime());
String formattedString = formatter.print(dayTimePeriod);

System.out.println(formattedString);

Question:

I am trying to calculate the duration between two instances of LocalDateTime. The special thing here is that each instance of LocalDateTime could be from ANYWHERE in the world:

LocalDateTime start could be from Nevada and LocalDateTime end could be from Tokyo. Each "time" associated with the LocalDateTime is, obviously enough, local to that location.

So if I said...

LocalDateTime start = LocalDateTime.parse("2015-07-14T10:00:00"); and said that start represented Chicago, that would mean 10:00 AM in Chicago time. Then I could say...

LocalDateTime end = LocalDateTime.parse("2015-07-14T03:00:00"); and end represents Moscow, so it is 3:00AM in Moscow time.

Can I create a robust enough solution that will allow start and end to represent any cities in the world and still correctly calculate the duration between the two?


Answer:

"LocalDateTime" Does Not Mean A Particular Locality

I think you misunderstand the purpose of LocalDateTime. The "local" means any locality, not a specific locality. As in "Christmas starts at midnight on December 25, 2015" where we mean any locality’s midnight. Christmas starts in Paris several hours earlier than Montréal, for example.

If you know the date-time is meant to represent a date-time in Nevada, the use a Joda-Time DateTime assigned the proper time zone name of America/Boise. In the new java.time package (Tutorial) built into Java 8 and later, use a ZonedDateTime object with assigned time zone.

Similarly, if you know the date-time is local to Tokyo, do not use LocalDateTime. Use a Joda-Time DateTime with an assigned time zone of Asia/Tokyo.

Elapsed

Elapsed time between a pair of LocalDateTime instances makes no sense. For example, the times may be 14:00 and 18:00 on the same date, but that does not mean four hours difference. If you really meant 14:00 in Paris and 18:00 in Chicago, that would be several hours difference, not two.

I am not discussing calculating elapsed time as that has been handled many many times on StackOverflow. I'm trying to clarify some concepts here. Then you can move on to the existing Questions & Answers for calculating elapsed time.

Databases Store UTC

Generally in SQL databases you should be using the data type TIMESTAMP WITH TIME ZONE (a.k.a. TIMESTAMPZ with a Z for "zulu"). This misnomer actually means "with respect for time zone". Incoming data with an offset from UTC or other time zone information is adjusted to UTC. The data's offset or time zone is not preserved.

The SQL data type TIMESTAMP WITHOUT TIME ZONE (a.k.a. TIMESTAMP) means the same as a LocalDateTime in Java: no time zone at all. Not tied to the timeline. Any offset or time zone information with input data is ignored, no adjustment made.

Postgres doc may help explain.

Stick With UTC

When retrieving such a value from the database, the UTC value may be adjusted to a particular time zone by your admin tool (such as pgAdmin in Postgres) or your database driver or by your app.

In your app it is generally best to keep your date-time values in UTC as much as possible. Do nearly all of your storage, business logic, and data exchange in UTC. Only adjust to a particular time zone when expected by the user.

To Convert A LocalDateTime To A Time Zone

If you do have a LocalDateTime object, and you want to assign it a time zone, here is some example code. We also adjust to get the very same moment as seen in Montréal and in UTC. First the example is shown in Joda-Time, then in java.time.

Joda-Time

Example in Joda-Time 2.8.

LocalDateTime ldt = new LocalDateTime( "2015-07-14T10:00:00" );  // Nowhere in particular.
DateTimeZone zoneChicago = DateTimeZone.forID( "America/Chicago" );
DateTime dateTimeChicago = ldt.toDateTime( zoneChicago );
DateTime dateTimeMontreal = dateTimeChicago.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeUtc = dateTimeChicago.withZone( DateTimeZone.UTC );

Dump to console.

System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + dateTimeChicago );
System.out.println( "Montréal: " + dateTimeMontreal );
System.out.println( "UTC: " + dateTimeUtc);

When run.

LocalDateTime (nowhere): 2015-07-14T10:00:00.000
Chicago: 2015-07-14T10:00:00.000-05:00
Montréal: 2015-07-14T11:00:00.000-04:00
UTC: 2015-07-14T15:00:00.000Z
java.time

Example in java.time of Java 8 Update 51.

LocalDateTime ldt = LocalDateTime.parse( "2015-07-14T10:00:00" );  // Nowhere in particular.
ZoneId zoneChicago = ZoneId.of( "America/Chicago" );
ZonedDateTime zdtChicago = ZonedDateTime.of( ldt, zoneChicago );
ZonedDateTime zdtMontreal = zdtChicago.withZoneSameInstant( ZoneId.of( "America/Montreal" ) );
ZonedDateTime zdtUtc = zdtChicago.withZoneSameInstant( ZoneOffset.UTC ); // ZoneOffset is a subclass of ZoneId.

Dump to console.

System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + zdtChicago );
System.out.println( "Montréal: " + zdtMontreal );
System.out.println( "UTC: " + zdtUtc);

When run.

LocalDateTime (nowhere): 2015-07-14T10:00
Chicago: 2015-07-14T10:00-05:00[America/Chicago]
Montréal: 2015-07-14T11:00-04:00[America/Montreal]
UTC: 2015-07-14T15:00Z

Question:

Can someone explain why the following tests fails?

@Test
public void testDuration() {
    Duration duration = Duration.standardDays(1);
    assertTrue(duration.getStandardMinutes() == 1440); //OK
    assertTrue(duration.toPeriod().getMinutes() == 1440); //NOK. result = 0
    assertTrue(new Period(duration.getMillis()).getMinutes() == 1400); //NOK. result = 0
}

Answer:

A Period in JodaTime represents a "set of duration field values". Therefore, the methods getMinutes(), getHours(), ... are returning the value of those fields rather than computing the minutes, hours, ...

Furthermore, the conversion from a Duration sets the fields according to a PeriodType and a chronology.

From the API (http://joda-time.sourceforge.net/apidocs/org/joda/time/ReadableDuration.html#toPeriod%28%29):

Period toPeriod()

Converts this duration to a Period instance using the standard period type and the ISO chronology.

Only precise fields in the period type will be used. Thus, only the hour, minute, second and millisecond fields on the period will be used. The year, month, week and day fields will not be populated.

If the duration is small, less than one day, then this method will perform as you might expect and split the fields evenly. If the duration is larger than one day then all the remaining duration will be stored in the largest available field, hours in this case.

This means, as a Duration's day has exactly 24 hours, all information is stored in the hours field and minutes stays at 0.

See this question for a good explanation of the differences between Interval, Duration and Period.

Question:

Joda is a must as Java SE8 is not available in this environment.

How do I go about finding the difference in hours between two LocalTime objects, to store as a Duration? Tried the following with no luck:

LocalTime startTime = new LocalTime(8, 0);
LocalTime endTime = new LocalTime(16, 0);

Period shiftDuration = new Period(0, 
endTime.getHourOfDay() - startTime.getHourOfDay(), 0, 0);

System.out.println(shiftDuration.getHours()); // expected 8, get 0.

Answer:

If you want specifically the number of hours between those 2 LocalTime instances, you can use the org.joda.time.Hours class:

LocalTime startTime = new LocalTime(8, 0);
LocalTime endTime = new LocalTime(16, 0);

int hours = Hours.hoursBetween(startTime, endTime).getHours();

The result will be 8.

You can also use a org.joda.time.Period:

Period period = new Period(startTime, endTime);
System.out.println(period.getHours()); // 8

The result is also 8.

The difference between them is that Hours rounds the value, but Period doesn't. Example:

// difference between 08:00 and 16:30
LocalTime startTime = new LocalTime(8, 0);
LocalTime endTime = new LocalTime(16, 30);

Period period = new Period(startTime, endTime);
System.out.println(period); // PT8H30M
System.out.println(period.getHours()); // 8
System.out.println(period.getMinutes()); // 30

int hours = Hours.hoursBetween(startTime, endTime).getHours();
System.out.println(hours); // 8

While the Period keeps all the fields (8 hours and 30 minutes), the Hours takes care only about the hours and discard the other fields.

Question:

I am trying to calculate the difference between two dates. This is my code:

DateTime today = new DateTime();
DateTime yesterday = today.minusDays(1);

Duration duration = new Duration(yesterday, today);

Unfortunately I can't run the above code. Before I run it, I get this error in eclipse:

 Cannot instantiate the type Duration

What should I do to fix it?


Answer:

You shoud use org.joda.time.Duration class.

import org.joda.time.DateTime;
import org.joda.time.Duration;

DateTime today = new DateTime();
DateTime yesterday = today.minusDays(1);

Duration duration = new Duration(yesterday, today);
System.out.println(duration.getStandardDays());

Question:

I am using jodatime's Duration class to add a duration like 02:10 to a DateTime like 2014-08-02T11:34 using withDurationAdded(duration, 1).

I created duration using Duration.parse("02:10"). I get an IllegalFormatException for the "02:10". I don't see a formatSpecifier argument; how do I properly create this duration? The jodatime quickstart guide didnt provide an example of parse: http://joda-time.sourceforge.net/key_duration.html


Answer:

The javadoc specifies the required format for Duration.parse:

Parses a Duration from the specified string.

This parses the format PTa.bS, as per AbstractDuration.toString().

You'll need to build a custom formatter - here's some sample code:

import org.joda.time.*;
import org.joda.time.format.*;

public class Test {
    public static void main(String[] args) {
        PeriodFormatter formatter = new PeriodFormatterBuilder()
            .appendHours()
            .appendLiteral(":")
            .appendMinutes()
            .toFormatter();
        Duration duration = formatter.parsePeriod("02:10").toStandardDuration();
        System.out.println(duration);
    }
}

Question:

scala.concurrent.duration package gives one the ability to initialize a duration like so:

Duration("5 minutes")

I cannot figure out an equivalent in Java that would allow me to do the same easily. Any suggestions (other than using scala from Java?)


Answer:

Java itself does not handle any duration formatting. You will need 3rd-party-libraries like Joda-Time or my lib Time4J in order to do what you want.

Joda-Time (builder-based):

PeriodFormatter pf = 
    new PeriodFormatterBuilder()
    .appendMinutes()
    .appendSuffix(" minute", " minutes")
    .toFormatter();
Period p = pf.parsePeriod("5 minutes");
System.out.println(p); // PT5M

Time4J (pattern-based):

Duration<ClockUnit> d = 
  Duration.formatter(ClockUnit.class, "{m: :en:ONE=minute:OTHER=minutes}")
  .parse("5 minutes");
System.out.println(d); // PT5M

However, there are in general following restrictions with such solutions.

  • Such procedures are usually only for one language because literals are fixed.
  • The inner format structure is often not flexible (input might contain unexpected units).

My recommendation for storage of a duration:

You should best use ISO-8601-formats for storage and user-defined formats for representation. This way is language-neutral and flexible for handling several units. Examples:

Joda-Time with usage of ISO:

Period joda = ISOPeriodFormat.standard().parsePeriod("PT5M");
System.out.println(PeriodFormat.wordBased(Locale.ENGLISH).print(joda)); 
// 5 minutes

Time4J with usage of ISO:

Duration<ClockUnit> time4j = Duration.parseClockPeriod("PT5M");
System.out.println(PrettyTime.of(Locale.ENGLISH).print(time4j)); 
// 5 minutes

Question:

I want to serialize a Joda-Time Duration instance as a long representing the number of seconds using Gson. My serializer class is:

private class DurationSerializer implements JsonSerializer<Duration>
{
  public JsonElement serialize(Duration duration,
                               Type durationType,
                               JsonSerializationContext context)
    {
    return new JsonPrimitive(duration.getStandardSeconds());
    }
}

The output of this is {"iMillis":900000}. I just want the number of seconds, not the iMillis tag. Is that possible?


Answer:

I don't recommend using JsonDeserializer as it's been deprecated in favor of the Streaming API. I'm not sure what your problem is, but I don't think it's in the Serializer.

Try using TypeAdapter instead:

public class DurationTypeAdapter extends TypeAdapter<Duration> {
  public void write(JsonWriter writer, Duration value) throws IOException {
    if (value == null) {
      writer.nullValue();
      return;
    }

    writer.value(duration.getStandardSeconds());
  }

  // implementation of read() is left as an exercise to you
}

Register it like this:

GsonBuidler builder = new GsonBuilder();
builder.registerTypeAdapter(new DurationTypeAdapter());
Gson g = builder.create();

Question:

I'm trying to shift datetime by a Duration which I get in the format "HHmm", for example "0010" meaning 10 minutes.

I've found similar ticket (Parsing time strings like "1h 30min") but I can't make it work properly.

Here's the code:

PeriodFormatter hoursMinutes = new PeriodFormatterBuilder().appendHours().appendMinutes().toFormatter();
Duration duration = hoursMinutes.parsePeriod("0010").toStandardDuration();
duration.getStandardMinutes(); //Returns 600

For some reason, I get 600 minutes instead of 10. So it looks like the minutes were interpreted as hours, but I can't understand why. I've tried adding .maximumParsedDigits(2) for hours, but the result was the same.

Why is my code wrong? Is there some other way to initialize duration parser? Something, where I could just use the standard format like "HHmm"?


Answer:

So the issue was really with the maximum parseddigits. I only had to add it before hours, not minutes. So the solution is this:

PeriodFormatter hoursMinutes =
                new PeriodFormatterBuilder().maximumParsedDigits(2).appendHours().appendMinutes().toFormatter();
Duration duration = hoursMinutes.parsePeriod("0010").toStandardDuration();
duration.getStandardMinutes(); //Returns 10

Question:

I'm trying to use Joda-Time's duration class to calculate the exact days different between two days. Meaning I would like the result to be something like 25.48 days rather than just 25 days. Right now I have:

private static final double MILLISECONDS_IN_DAY = 86400000;

DateTime today = new DateTime();
Duration dur = new Duration(foo.getExpirationDate(), today);
double diff_days = dur.getMillis()/MILLISECONDS_IN_DAY;

But I'm wondering if there's a way to combine the 1st and last line into one that just gives me a double.


Answer:

You could eliminate the first line by using Joda-Time's constant instead of declaring your own.

org.joda.time.DateTimeConstants.MILLIS_PER_DAY

Don't forget that if you're trying to calculate another date, there may be a better way to do this. The irregularities in calendars such as leap years are well accounted for within Joda-Time.

Question:

I'm using Joda-Time Duration to get the duration between two DateTime:

DateTime startTimeDate = new DateTime(startTimeDateInLong, DateTimeZone.UTC);
DateTime endTimeDate = new DateTime(endTimeDateInLong, DateTimeZone.UTC);

Duration duration = new Duration(startTimeDate, endTimeDate);

I want to convert per following rules:

0-60 seconds --> 1 minute ..
1.5 - 1 hour --> 1 hour
1.6 hour - 2 hour --> 2 hour

I am using duration.toStandardHours(), but for 96 minutes it gives 1 hour instead I want 2 hours.


Answer:

The Duration class doesn't round the values the way you want. Even if you get a duration of 1 hour, 59 minutes, 59 seconds and 999 milliseconds, toStandardHours() will return 1.

To get the results you want, you must get the total in seconds, and then manipulate this value accordingly. You can use the java.math.BigDecimal class, with a java.math.RoundingMode to control how the values are rounded:

// 96-minutes duration
Duration duration = new Duration(96 * 60 * 1000);
long secs = duration.toStandardSeconds().getSeconds();
if (secs >= 3600) { // more than 1 hour
    BigDecimal secondsPerHour = new BigDecimal(3600);
    int hours = new BigDecimal(secs).divide(secondsPerHour, RoundingMode.HALF_DOWN).intValue();

    System.out.println(hours + " hour" + (hours > 1 ? "s" : "")); // 2 hours
} else {
    int mins;
    if (secs == 0) { // round zero seconds to 1 minute
        mins = 1;
    } else {
        // always round up (1-59 seconds = 1 minute)
        BigDecimal secondsPerMin = new BigDecimal(60);
        mins = new BigDecimal(secs).divide(secondsPerMin, RoundingMode.UP).intValue();
    }
    System.out.println(mins + " minute" + (mins > 1 ? "s" : ""));
}

This will print 2 hours for a 96-minutes duration, 1 minute for durations between 0 and 60 seconds, and so on.

To get the difference in seconds, you can also use the org.joda.time.Seconds class:

long secs = Seconds.secondsBetween(startTimeDate, endTimeDate).getSeconds();

Java new Date/Time API

Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely "finished" project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".

If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

First, to get the corresponding instant from an epoch milliseconds value, you can use the Instant class (no need to set timezone to UTC, as Instant represents an UTC instant). Then, to calculate the difference, you can use a Duration:

long startTimeDateInLong = // long millis value
long endTimeDateInLong = // long millis value

// get the corresponding Instant
Instant start = Instant.ofEpochMilli(startTimeDateInLong);
Instant end = Instant.ofEpochMilli(endTimeDateInLong);

// get the difference in seconds
Duration duration = Duration.between(start, end);
long secs = duration.getSeconds();

// perform the same calculations as above (with BigDecimal)

You can also use a ChronoUnit to get the difference in seconds:

long secs = ChronoUnit.SECONDS.between(start, end);

Question:

I've searched around but cannot find an answer for this.

I have a Duration:

Duration dur = Duration.ofMillis(50);

and I have a DateTime:

DateTime dt = DateTime.now();

How do I subtract the Duration from the DateTime? I can see the method DateTime.Minus(ReadableDuration) so I thought I could do this:

DateTime dt = DateTime.now().minus(dur);

But apparently Duration does not extend ReadableDuration.


Answer:

From the Joda time 2.2 API, the class Duration implements ReadableDuration. So, Duration is a subtype of ReadableDuration.

public final class Duration
       extends BaseDuration
       implements ReadableDuration, Serializable

See http://joda-time.sourceforge.net/apidocs/org/joda/time/Duration.html.

Question:

The following:

java.time.Duration.ofDays(30).toString

produces this output:

PT720H

Is there a simple way to pretty print this, for example, as "30 days".

Likewise, for something like "P2DT3H4M", I'd like to output this as "2 days, 3 hours and 4 minutes".

I'm aware of the answer here "pretty print" duration in java, but not sure if I can use/convert Java's Duration to Joda's Duration to be able to use this?


Answer:

I ended up using this (extended from this answer https://stackoverflow.com/a/49628638/5300930).

I'm sure that there should be a simpler way to do this using the Java/Scala libraries? Note, my input will always be a java.time.Duration, I can use the Joda libraries, but they must be able to start with a java.time.Duration as input.

  /** Encodes a [[java.time.Duration]] into a more readable format.
    *
    * @example
    * encodeDuration(Duration.ofDays(30).plusHours(3))  // returns "30 days, 3 hours"
    * encodeDuration(Duration.ofDays(1))                // returns "1 day"
    * encodeDuration(Duration.parse("PT720H"))          // returns "30 days"
    * encodeDuration(Duration.parse("PT12000H10S"))     // returns "1 year, 4 months, 15 days, 10 seconds"
    */
implicit val encodeDuration: Encoder[java.time.Duration] = (duration: Duration) => {
    val oneMinute = 60
    val twoMinutes = oneMinute * 2
    val oneHour = oneMinute * 60
    val twoHours = oneHour * 2
    val oneDay = oneHour * 24
    val twoDays = oneDay * 2
    val oneMonth = oneDay * 30
    val twoMonths = oneMonth * 2
    val oneYear = oneDay * 365
    val twoYears = oneYear * 2

    // scalastyle:off cyclomatic.complexity
    def encodeDuration(result: List[String], seconds: Long): List[String] = {

      seconds match {
        case seconds if seconds <= 0                                 =>
          List.empty[String]
        case seconds if seconds == 1                                 =>
          result ::: List(s"${seconds} second")
        case seconds if seconds < oneMinute                          =>
          result ::: List(s"${seconds} seconds")
        case seconds if seconds >= oneMinute && seconds < twoMinutes =>
          List(s"${seconds / oneMinute} minute") ::: encodeDuration(result, seconds % oneMinute)
        case seconds if seconds >= oneMinute && seconds < oneHour =>
          List(s"${seconds / oneMinute} minutes") ::: encodeDuration(result, seconds % oneMinute)
        case seconds if seconds >= oneHour && seconds < twoHours     =>
          List(s"${seconds / oneHour} hour") ::: encodeDuration(result, seconds % oneHour)
        case seconds if seconds >= twoHours && seconds < oneDay      =>
          List(s"${seconds / oneHour} hours") ::: encodeDuration(result, seconds % oneHour)
        case seconds if seconds >= oneDay && seconds < twoDays       =>
          List(s"${seconds / oneDay} day") ::: encodeDuration(result, seconds % oneDay)
        case seconds if seconds >= twoDays && seconds < oneMonth     =>
          List(s"${seconds / oneDay} days") ::: encodeDuration(result, seconds % oneDay)
        case seconds if seconds >= oneMonth && seconds < twoMonths   =>
          List(s"${seconds / oneMonth} month") ::: encodeDuration(result, seconds % oneMonth)
        case seconds if seconds >= twoMonths && seconds < oneYear    =>
          List(s"${seconds / oneMonth} months") ::: encodeDuration(result, seconds % oneMonth)
        case seconds if seconds >= oneYear && seconds < twoYears     =>
          List(s"${seconds / oneYear} year") ::: encodeDuration(result, seconds % oneYear)
        case seconds if seconds >= twoYears                          =>
          List(s"${seconds / oneYear} years") ::: encodeDuration(result, seconds % oneYear)
      }
    }
    // scalastyle:on cyclomatic.complexity

    Encoder.encodeString(encodeDuration(List.empty[String], duration.getSeconds).mkString(", "))
  }

Question:

I use joda in my Java application. I have four DateTime objects, e.g.:

a: from 2018/01/07 06:00 until 2018/01/07 07:00 (6:00 - 07:00)

b: from 2018/01/07 06:40 until 2018/01/07 11:25 (6:40 - 11:25)

And I need the Duration where a is in b - in this case 0:20. Is there a way to do this in a simple way with joda?


Answer:

Duration result = 
    new Interval(aStart, aEnd).overlap(new Interval(bStart, bEnd)).toDuration();

Question:

I am trying to display the difference in time between two DateTimes in the following format:

? years, ? months, ? weeks, ? days, ? hours, ? minutes, ? seconds and ? milliseconds

I've got it displaying almost like this, however it does not display the weeks. I have tried using PeriodType.standard(), but that omits months and years. Is it possible to do this?

Here is the code I am using to achieve the current result:

private String getPeriodBetween(DateTime from, DateTime to, boolean showMilliseconds) {
    Period period;

    if (from.isAfter(to)) {
        period = new Period(to, from);
    } else {
        period = new Period(from, to);
    }

    PeriodFormatterBuilder builder = new PeriodFormatterBuilder().appendYears()
        .appendSuffix(" year, ", " years, ")
        .appendMonths()
        .appendSuffix(" month, ", " months, ")
        .appendWeeks()
        .appendSuffix(" week, ", " weeks, ")
        .appendDays()
        .appendSuffix(" day, ", " days, ")
        .appendHours()
        .appendSuffix(" hour, ", " hours, ")
        .appendMinutes()
        .appendSuffix(" minute, ", " minutes, ")
        .appendSeconds()
        .appendSuffix(" second, ", " seconds, ");

    if (showMilliseconds) {
        builder = builder.appendMillis().appendSuffix(" millisecond", " milliseconds");
    }

    return builder.toFormatter()
        .print(period.normalizedStandard(PeriodType.yearMonthDayTime()))
        .replaceAll("[,]\\s*$", "")
        .replaceAll("(?:,\\s*)([^,]+)$", " and $1")
        .replaceAll("^(?![\\s\\S])", "No difference");
}

If I use PeriodType.forFields(); and specify an array of DurationFieldType containing all required fields, it omits year and month.


Answer:

Figured it out. The problem was that I was originally using Duration and converting it to a Period. I had tried to use the following code to fix it, but it didn't give me the result I expected. After I changed it to Period, I could specify all of the fields I required using Period.forFields();, accepting the following:

private static final DurationFieldType[] DURATION_FIELD_TYPES = {
    DurationFieldType.years(),
    DurationFieldType.months(),
    DurationFieldType.weeks(),
    DurationFieldType.days(),
    DurationFieldType.hours(),
    DurationFieldType.minutes(),
    DurationFieldType.seconds(),
    DurationFieldType.millis(),
};

Never mind, you don't even need to normalise the standard as it already includes all fields by default. Now that I think about it, it makes sense. Here's my complete working code for reference:

private String getDifference(DateTime from, DateTime to, boolean showMilliseconds) {
    Period period;

    if (from.isAfter(to)) {
        period = new Period(to, from);
    } else {
        period = new Period(from, to);
    }

    PeriodFormatterBuilder builder = new PeriodFormatterBuilder()
        .appendYears()
        .appendSuffix(" year, ", " years, ")
        .appendMonths()
        .appendSuffix(" month, ", " months, ")
        .appendWeeks()
        .appendSuffix(" week, ", " weeks, ")
        .appendDays()
        .appendSuffix(" day, ", " days, ")
        .appendHours()
        .appendSuffix(" hour, ", " hours, ")
        .appendMinutes()
        .appendSuffix(" minute, ", " minutes, ")
        .appendSeconds()
        .appendSuffix(" second, ", " seconds, ");

    if (showMilliseconds) {
        builder = builder.appendMillis().appendSuffix(" millisecond", " milliseconds");
    }

    return builder
        .toFormatter()
        .print(period)
        .replaceAll("[,]\\s*$", "")
        .replaceAll("(?:,\\s*)([^,]+)$", " and $1")
        .replaceAll("^(?![\\s\\S])", "No difference");
}

Question:

I am getting incorrect results whatever I do. Could anyone give me some advise please :).

Here is the code of my program the is responsible for getting the time between two dates.

I am getting a result but the problem is that it is incorrect and I have having trouble finding the problem.

I have to Joda library in my project.

if (timerightnow.isSelected()) {
            DateFormat getdate = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
            Date getdate1 = new Date(); 

            String datenow = getdate1.toString();

            String monthnow = datenow.substring(4, 7);
            String daynow = datenow.substring(8,10);
            String hournow = datenow.substring(11,19);
            String yearnow = datenow.substring(25, 29);

            if (monthnow.equalsIgnoreCase("jan"))
                monthnow = "01";
            if (monthnow.equalsIgnoreCase("feb"))
                monthnow = "02";
            if (monthnow.equalsIgnoreCase("mar"))
                monthnow = "03";
            if (monthnow.equalsIgnoreCase("apr"))
                monthnow = "04";
            if (monthnow.equalsIgnoreCase("may"))
                monthnow = "05";
            if (monthnow.equalsIgnoreCase("jun"))
                monthnow = "06";
            if (monthnow.equalsIgnoreCase("jul"))
                monthnow = "07";
            if (monthnow.equalsIgnoreCase("agu"))
                monthnow = "08";
            if (monthnow.equalsIgnoreCase("sep"))
                monthnow = "09";
            if (monthnow.equalsIgnoreCase("oct"))
                monthnow = "10";
            if (monthnow.equalsIgnoreCase("nov"))
                monthnow = "11";
            if (monthnow.equalsIgnoreCase("dec"))
                monthnow = "12";

            String timenow = monthnow + "/" + daynow + "/" + yearnow + " " + hournow;
            String timetotext = timeto.getText();
            SimpleDateFormat date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");


            Date datestart = null;
            Date dateend = null;



             try {

                 datestart = date.parse(timenow);
                 dateend = date.parse(timetotext);

                 DateTime datestartdaytime = new DateTime(datestart);
                 DateTime dateenddaytime = new DateTime(dateend);

                 JOptionPane.showMessageDialog(null, datestartdaytime + "\n" + dateenddaytime);

                int monthoutput = Months.monthsBetween(datestartdaytime, dateenddaytime).getMonths();
                int daysoutput = Days.daysBetween(datestartdaytime, datestartdaytime).getDays() % 30;
                int hoursoutput = Hours.hoursBetween(datestartdaytime, datestartdaytime).getHours() % 24;
                int minutsoutput = Minutes.minutesBetween(datestartdaytime, dateenddaytime).getMinutes() % 60;
                int secondsoutput = Seconds.secondsBetween(datestartdaytime, dateenddaytime).getSeconds() % 60;

                answer.setText(monthoutput + "Months" + daysoutput + "Days" + hoursoutput + "Hours" + minutsoutput + "Minutes" + secondsoutput + "Seconds");

Thanks a lot :)


Answer:

Your bug is in these two lines:

            int daysoutput = Days.daysBetween(datestartdaytime, datestartdaytime).getDays() % 30;
            int hoursoutput = Hours.hoursBetween(datestartdaytime, datestartdaytime).getHours() % 24;

You intended to use dateenddaytime as the second argument:

            int daysoutput = Days.daysBetween(datestartdaytime, dateenddaytime).getDays() % 30;
            int hoursoutput = Hours.hoursBetween(datestartdaytime, dateenddaytime).getHours() % 24;