Javaで期間を「きれいに印刷」するにはどうすればよいですか?


93

C#と同じ方法でミリ秒単位で数値を出力できるJavaライブラリを知っている人はいますか?

たとえば、長さが123456ミリ秒の場合、4d1h3m5sとして出力されます。


1
参考までに、あなたが説明しているように見えるフォーマットDurationは、賢明な標準ISO 8601で定義されています。PnYnMnDTnHnMnSここで、P「期間」とは開始を示しT、日付の部分と時間の部分を区切り、その間にオプションで数字が1つ出現します。 -略語。たとえば、PT4H30M= 4時間半です。
バジルブルク2014年

1
他のすべてが失敗した場合、それを自分で行うのは非常に簡単なことです。ただの連続したアプリケーションを使用%して/部品に番号を分割します。提案された回答のいくつかよりもほとんど簡単です。
Hot Licks

@HotLicksはずっとクリーンで使用するよりも明確にコードのライブラリのメソッドに任せ/%
Ole VV

はい、この質問をしてから8年間で(!)私のユースケースに非常に適したjoda時間に移動しました
phatmanace

@phatmanace Joda -Timeプロジェクトはメンテナンスモードになり、Java 8以降に組み込まれたjava.timeクラスへの移行を推奨します。
バジルブルク2018

回答:


91

Joda Timeには、PeriodFormatterBuilderを使用してこれを行うためのかなり良い方法があります。

迅速な勝利: PeriodFormat.getDefault().print(duration.toPeriod());

例えば

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

Duration duration = new Duration(123456); // in milliseconds
PeriodFormatter formatter = new PeriodFormatterBuilder()
     .appendDays()
     .appendSuffix("d")
     .appendHours()
     .appendSuffix("h")
     .appendMinutes()
     .appendSuffix("m")
     .appendSeconds()
     .appendSuffix("s")
     .toFormatter();
String formatted = formatter.print(duration.toPeriod());
System.out.println(formatted);

2
以下の回答から、最初にDurationインスタンスを作成してからそれをPeriodに変換しなくても、Periodのインスタンスを直接作成できることがわかります。たとえば、Period period = new Period(millis); 文字列フォーマット= formatter.print(period);
Basil Vandegriend 2013

7
この「duration.toPeriod()」変換に注意してください。期間が非常に大きい場合、日部分以降は0のままになります。時間部分は増加し続けます。あなたは25h10m23sを取得しますが、「d」を取得することはありません。その理由は、Jodaの厳密な方法で時間を日に変換する完全に正しい方法がないためです。ほとんどの場合、2つのインスタントを比較して印刷したい場合は、新しいDuration(t1、t2).toPeriod()の代わりに新しいPeriod(t1、t2)を実行できます。
ブーン

@Boonあなたは日数に関して期間を期間に変換する方法を知っていますか?私は自分のタイマーで2番目に置き換えることができる期間を使用しています。イベントの設定PeriodType.daysTime().standard()助けていない
MURT

4
@murt本当に必要で、日の標準定義を受け入れることができる場合は、上記のコードで「formatter.print(duration.toPeriod()。normalizedStandard())」を使用できます。楽しんで!
ブーン

74

私は、Java 8 Duration.toString()と少しの正規表現を使用して、単純なソリューションを構築しました。

public static String humanReadableFormat(Duration duration) {
    return duration.toString()
            .substring(2)
            .replaceAll("(\\d[HMS])(?!$)", "$1 ")
            .toLowerCase();
}

結果は次のようになります。

- 5h
- 7h 15m
- 6h 50m 15s
- 2h 5s
- 0.1s

間にスペースを入れたくない場合は、単に削除しreplaceAllます。


6
これは今後のバージョンで変更される可能性があるため、決してtoStrong()の出力に依存しないでください。
Weltraumschaf

14
それはjavadocの指定なので、それは、変更される可能性はありません@Weltraumschaf
SEは悪であるためaditsuは終了

9
@Weltraumschafの出力はDuration::toString、明確で使い古されたISO 8601標準に従ってフォーマットされているため、変更される可能性はほとんどありません。
バジルブルク2017

1
分数が必要ない場合は.replaceAll("\\.\\d+", "")、の呼び出しの前に追加しtoLowerCase()ます。
zb226

1
@Weltraumschafあなたのポイントは一般的に正しいので、私はほとんどのクラスのtoString()出力に依存しません。ただし、この非常に特殊なケースでは、メソッドのJavadocを見るとわかるように、メソッドの定義には、その出力がISO-8601標準に従うことが記載されています。したがって、ほとんどのクラスのような実装の詳細ではないため、Javaのように成熟した言語がそのように変更することを期待するものではありません
lucasls

13

Apacheのコモンズ-langが、これは、井戸として行わ取得するために有用なクラスを提供しDurationFormatUtils

DurationFormatUtils.formatDurationHMS( 15362 * 1000 ) )=> 4:16:02.000(H:m:s.millis) DurationFormatUtils.formatDurationISO( 15362 * 1000 ) )=> P0Y0M0DT4H16M2.000S、cf。ISO8601


9

JodaTimeを有するPeriodような量を表すことができるクラスを、及び(介してレンダリングすることができるIsoPeriodFormatで)ISO8601フォーマット、例えばPT4D1H3M5S、例えば

Period period = new Period(millis);
String formatted = ISOPeriodFormat.standard().print(period);

その形式が必要な形式でない場合はPeriodFormatterBuilder、C#スタイルを含む任意のレイアウトをアセンブルできます4d1h3m5s


3
ちょうどメモとして、デフォルトでをnew Period(millis).toString()使用ISOPeriodFormat.standard()します。
Thomas Beauvais

9

Java 8では、のtoString()メソッドを使用して、PT8H6M12.345SなどのISO 8601秒ベースの表現java.time.Durationを使用する外部ライブラリなしでフォーマットすることもできます。


4
これは日数を表示しないことに注意してください
Wim Deblauwe '28

58
このフォーマットが私のきれいな印刷の定義を満たしているかどうかはわかりません。:-)
james.garriss 16

@ james.garriss少し見栄えを良くするには erickdeoliveiralealによる回答を参照してください。
バジルブルク2018

7

純粋なJDKコードを使用してそれを行う方法を次に示します。

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;

long diffTime = 215081000L;
Duration duration = DatatypeFactory.newInstance().newDuration(diffTime);

System.out.printf("%02d:%02d:%02d", duration.getDays() * 24 + duration.getHours(), duration.getMinutes(), duration.getSeconds()); 

7

org.threeten.extra.AmountFormats.wordBased

ThreeTen-エクストラスティーブンColebourne、JSR 310、の著者によって維持されているプロジェクト、java.time、およびジョダタイムは、持っているAmountFormats標準的なJava 8日付時刻クラスで動作するクラスを。ただし、これはかなり冗長で、よりコンパクトな出力のオプションはありません。

Duration d = Duration.ofMinutes(1).plusSeconds(9).plusMillis(86);
System.out.println(AmountFormats.wordBased(d, Locale.getDefault()));

1 minute, 9 seconds and 86 milliseconds


2
わあ、わかりました。その機能は見たことがありません。ただし、大きな欠点が1つありますオックスフォードのカンマがありません。
バジルブルク

4
@BasilBourqueオックスフォードのカンマは米国では一般的ですが、興味深いことに英国ではそうではありません。私のlib Time4J(はるかに優れた国際化対応)は、net.time4j.PrettyTimeクラスでこの区別をサポートし、必要に応じてコンパクト出力を制御することもできます。
Meno Hochschild、

6

Java 9以降

Duration d1 = Duration.ofDays(0);
        d1 = d1.plusHours(47);
        d1 = d1.plusMinutes(124);
        d1 = d1.plusSeconds(124);
System.out.println(String.format("%s d %sh %sm %ss", 
                d1.toDaysPart(), 
                d1.toHoursPart(), 
                d1.toMinutesPart(), 
                d1.toSecondsPart()));

2日1時間6分4秒


「toHoursPart」以上はどのように入手していますか?
Ghotiとチップス

4

Joda-Timeのビルダーアプローチに代わるものは、パターンベースのソリューションです。これは私のライブラリTime4Jによって提供されています。クラスDuration.Formatterを使用した例(読みやすくするためにいくつかのスペースを追加-スペースを削除すると、希望のC#スタイルが生成されます):

IsoUnit unit = ClockUnit.MILLIS;
Duration<IsoUnit> dur = // normalized duration with multiple components
  Duration.of(123456, unit).with(Duration.STD_PERIOD);
Duration.Formatter<IsoUnit> f = // create formatter/parser with optional millis
  Duration.Formatter.ofPattern("D'd' h'h' m'm' s[.fff]'s'");

System.out.println(f.format(dur)); // output: 0d 0h 2m 3.456s

このフォーマッターはjava.time-APIの期間も出力できます(ただし、そのタイプの正規化機能はそれほど強力ではありません)。

System.out.println(f.format(java.time.Duration.ofMillis(123456))); // output: 0d 0h 2m 3.456s

OPの「123456ミリ秒は4d1h3m5sとして出力される」という期待は、明らかに間違った方法で計算されます。だらしないと思う。上記で定義した同じ期間フォーマッターをパーサーとして使用することもできます。次のコードは、「4d1h3m5s」がどちらかに相当することを示しています349385000 = 1000 * (4 * 86400 + 1 * 3600 + 3 * 60 + 5)

System.out.println(
  f.parse("4d 1h 3m 5s")
   .toClockPeriodWithDaysAs24Hours()
   .with(unit.only())
   .getPartialAmount(unit)); // 349385000

別の方法はクラスを使用することです net.time4j.PrettyTimeことです(ローカライズされた出力や、「昨日」、「次の日曜日」、「4日前」などの相対時間の印刷にも適しています)。

String s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.NARROW);
System.out.println(s); // output: 2m 3s 456ms

s = PrettyTime.of(Locale.ENGLISH).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds, and 456 milliseconds

s = PrettyTime.of(Locale.UK).print(dur, TextWidth.WIDE);
System.out.println(s); // output: 2 minutes, 3 seconds and 456 milliseconds

テキスト幅は、略語を使用するかどうかを制御します。リストの形式も、適切なロケールを選択することで制御できます。たとえば、標準の英語ではOxformカンマが使用されますが、英国では使用されません。Time4Jの最新バージョンv5.5は90を超える言語をサポートし、CLDRリポジトリ(業界標準)に基づいた翻訳を使用します。


2
このPrettyTimeは、他の回答よりもはるかに優れています!残念ながら、これは最初に見つかりませんでした。
ycomp

このうまく機能するソリューションに2つの反対票が出た理由がわからないので、構文解析(フォーマットの反対)についても例を挙げて回答を拡張し、OPの誤った期待を強調することにしました。
Meno Hochschild

4

Javaの8に基づくバージョンuser678573の答え

private static String humanReadableFormat(Duration duration) {
    return String.format("%s days and %sh %sm %ss", duration.toDays(),
            duration.toHours() - TimeUnit.DAYS.toHours(duration.toDays()),
            duration.toMinutes() - TimeUnit.HOURS.toMinutes(duration.toHours()),
            duration.getSeconds() - TimeUnit.MINUTES.toSeconds(duration.toMinutes()));
}

... Java 8にはPeriodFormatterがなく、getHours、getMinutesなどのメソッドもないため...

Java 8のより良いバージョンが見られたら嬉しいです。


java.util.IllegalFormatConversionExceptionをスローします:f!= java.lang.Long
erickdeoliveiraleal 2018

期間d1 = Duration.ofDays(0); d1 = d1.plusHours(47); d1 = d1.plusMinutes(124); d1 = d1.plusSeconds(124); System.out.println(String.format( "%s日と%sh%sm%1.0fs"、d1.toDays()、d1.toHours()-TimeUnit.DAYS.toHours(d1.toDays())、d1 .toMinutes()-TimeUnit.HOURS.toMinutes(d1.toHours())、d1.toSeconds()-TimeUnit.MINUTES.toSeconds(d1.toMinutes())));
erickdeoliveiraleal

@erickdeoliveiralealそれを指摘してくれてありがとう。私はもともとgroovyのコードを書いていたので、その言語で動作した可能性があります。今はJava用に修正しました。
Torsten

3

これはユースケースに完全に適合しないかもしれませんが、PrettyTimeはここで役立つかもしれません。

PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
//prints: “right now”

System.out.println(p.format(new Date(1000*60*10)));
//prints: “10 minutes from now”

4
「10分」だけでもいいですか?
ジョニー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.