Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String.format prints wrong hours

I am using String.format to print elapsed time. This is working great for minutes:seconds...

long millis = 330000;
String.format("%tMm%tSs", millis, millis);

...will print 05m30s.

However, when I try to include hours in my elapsed time, hours is shown as 19! At first I thought it was my locale, but I've tried the following...

long millis = 330000;
String.format(Locale.ITALY, "%tHh%tMm%tSs", millis, millis, millis);

...and I still get 19h05m30s.

You can even try

long millis = 0;
String.format(Locale.ITALY, "%tHh", millis);

but you still get 19h.

What's going on?

like image 463
naugler Avatar asked Mar 06 '26 03:03

naugler


2 Answers

The long value is being treated as a java.util.Date, not a duration. That is why the timezone is affecting it. Your millis value is equivalent to a Date representing 00:05:30 January 1, 1970 UTC.

Try making the time relative to midnight in the local timezone:

long millis = 330000;

System.out.printf("%THh%<tMm%<tSs%n", millis);

Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);

cal.add(Calendar.MILLISECOND, (int) millis);

System.out.printf("%THh%<tMm%<tSs%n", cal);
like image 69
VGR Avatar answered Mar 07 '26 17:03

VGR


Joda-Time

The Joda-Time library has classes to intelligently handle a span of time, letting you avoid dealing with milliseconds.

ISO 8601

The ISO 8601 standard defines a format similar to what you are trying to use. The Joda-Time library uses ISO 8601 formats by default for both parsing and generating such strings.

Don’t Work So Hard

So in other words, you are working too hard. Let Joda-Time 2.7 do the work here.

Better to specify your desired time zone than to rely implicitly on the JVM’s current default time zone (which could change at any moment).

Use proper time zone names. Never use the 3 or 4 letter codes which are neither standardized nor unique.

DateTimeZone zone = DateTimeZone.forID ( "Europe/Rome" );

Fake some data by capturing the current moment, then back off some hours, minutes, and seconds.

DateTime now = DateTime.now ( zone );
DateTime then = now.minusHours(19).minusMinutes(5).minusSeconds ( 30 );

Represent a span of time in two ways. First, a pair of fixed points on the timeline. Second, a number of months, days, hours, etc.

Interval interval = new Interval( then, now );  // String representation defaults to the pair of date-time values displayed in ISO 8601 format.
Period period = interval.toPeriod();  // String representation defaults to ISO 8601 format of PnYnMnDTnHnMnS.

Note that the Period class’ toString method by default generates a string in the ISO 8601 standard format similar to yours, PnYnMnDTnHnMnS. The P marks the beginning while the T separates the date portion from the time-of-day portion.

Dump to console.

System.out.println ( "interval: " + interval );
System.out.println ( "period: " + period );

When run.

interval: 2015-04-18T12:16:55.700+02:00/2015-04-19T07:22:25.700+02:00
period: PT19H5M30S
like image 23
Basil Bourque Avatar answered Mar 07 '26 19:03

Basil Bourque