Java'da bir Süreyi "güzel bir şekilde yazdırabilirim"?


95

C # ile aynı şekilde milisaniye cinsinden bir sayıyı yazdırabilen bir Java kitaplığı bilen var mı?

Örneğin, 123456 ms uzunluğunda 4d1h3m5s olarak yazdırılacaktır.


1
Bilginize, açıkladığınız biçim Duration, mantıklı standart ISO 8601'de tanımlanmıştır : PnYnMnDTnHnMnSburada P"Dönem" anlamına gelir ve başlangıcı işaretler T, tarih bölümünü saat bölümünden ayırır ve bunların arasında, bir sayının isteğe bağlı olarak tek bir -letter kısaltması. Örneğin, PT4H30M= dört buçuk saat.
Basil Bourque

1
Her şey başarısız olursa, bunu kendiniz yapmak çok basit bir mesele. Sadece ardışık uygulamaları kullanmak %ve /bölüme sayısını bölmek. Önerilen cevapların bazılarından neredeyse daha kolay.
Hot Licks

@HotLicks çok temiz ve kullanmaktan daha anlaşılır kodunu kütüphane yöntemlere bırakın /ve %.
Ole VV

Evet, aradan geçen 8 yıl içinde bu soru soruldu beri ben çok iyi benim kullanma durumu uyan joda zaman üzerinde hareket ettik (!)
phatmanace

@phatmanace Joda-Time projesi şu anda bakım modunda ve Java 8 ve sonraki sürümlerde yerleşik olan java.time sınıflarına geçişi tavsiye ediyor .
Fesleğen Bourque

Yanıtlar:


91

Joda Zaman bir kullanarak bunu yapmak için oldukça iyi bir yol var PeriodFormatterBuilder .

Hızlı Kazan: PeriodFormat.getDefault().print(duration.toPeriod());

Örneğin

//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
Aşağıdaki yanıttan bir Period örneğinin, önce bir Süre örneği oluşturmadan ve ardından onu Periyod'a dönüştürmeden doğrudan oluşturulabileceği görülmektedir. Örneğin Dönem dönemi = yeni Dönem (milisaniye); Dize formatlı = formatter.print (nokta);
Basil Vandegriend

7
Bu "duration.toPeriod ()" dönüşümüne dikkat edin. Süre oldukça uzunsa, günden sonraki kısım 0 olarak kalacaktır. Saat kısmı büyümeye devam edecektir. 25h10m23s alacaksın ama asla "d" alamayacaksın. Bunun nedeni, Joda'nın katı yöntemleriyle saatleri günlere dönüştürmenin tam olarak doğru bir yolu olmamasıdır. Çoğu durumda, iki anı karşılaştırıyor ve yazdırmak istiyorsanız, yeni Süre (t1, t2) .toPeriod () yerine yeni Dönem (t1, t2) yapabilirsiniz.
Boon

@Boon günlere göre süreyi döneme nasıl çevireceğinizi biliyor musunuz? Zamanlayıcımda ikinci değiştirebileceğim süreyi kullanıyorum. Etkinlik ayarı PeriodType.daysTime()veya .standard()yardım etmedi
murt

4
@murt Bir günün standart tanımını gerçekten istiyorsanız ve kabul edebiliyorsanız, yukarıdaki kodda "formatter.print (duration.toPeriod (). normalizedStandard ())" yapabilirsiniz. İyi eğlenceler!
Boon

78

Java Duration.toString()8'leri ve biraz regex kullanarak basit bir çözüm geliştirdim :

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

Sonuç şöyle görünecek:

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

Arada boşluk olmasını istemiyorsanız, kaldırmanız yeterlidir replaceAll.


6
Asla toStrong () çıktısına güvenmemelisiniz çünkü bu, sonraki sürümlerde değişebilir.
Weltraumschaf

14
@Weltraumschaf, javadoc'da belirtildiği için değişmesi muhtemel değil
aditsu SE EVIL olduğu için çıkıldı

9
@Weltraumschaf Çıktı Duration::toString, iyi tanımlanmış ve iyi kullanılmış ISO 8601 standardına göre biçimlendirildiği için değişmesi olası değildir .
Basil Bourque

1
Kesir istemiyorsanız .replaceAll("\\.\\d+", ""), çağrıdan önce ekleyin toLowerCase().
zb226

1
@Weltraumschaf Genel olarak amacınız doğrudur ve ben çoğu sınıfın toString () çıktısına güvenmem. Ama bu çok özel durumda, yöntem tanımı sen yöntemin de görebileceğiniz gibi devletler çıkış, ISO-8601 standardına izler Javadoc . Yani bu çoğu sınıfta olduğu gibi bir uygulama detayı değil, bu nedenle Java kadar olgun bir dilin böyle değişmesini beklediğim bir şey değil
lucasls

13

Apache commons-lang, bunu yapmak için de yararlı bir sınıf sağlar. DurationFormatUtils

örneğin DurationFormatUtils.formatDurationHMS( 15362 * 1000 ) )=> 4: 16: 02.000 (H: m: s.millis) DurationFormatUtils.formatDurationISO( 15362 * 1000 ) )=> P0Y0M0DT4H16M2.000S, cf. ISO8601


9

JodaTime , Periodbu tür miktarları temsil edebilen IsoPeriodFormatve ISO8601 formatında (aracılığıyla ) oluşturulabilen bir sınıfa sahiptir PT4D1H3M5S, ör.

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

Bu format istediğiniz format değilse, PeriodFormatterBuilderC # stiliniz de dahil olmak üzere rastgele düzenleri bir araya getirmenize izin verir 4d1h3m5s.


3
Bir not gibi, varsayılan olarak new Period(millis).toString()kullanır ISOPeriodFormat.standard().
Thomas Beauvais


8

Saf JDK kodunu kullanarak bunu şu şekilde yapabilirsiniz:

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-Ekstra Stephen Colebourne, JSR 310, yazarı tarafından korunur proje, java.time ve Joda-Time , bir sahip AmountFormatsstandart bir Java 8 tarih saat sınıfları ile çalışır sınıfı. Yine de oldukça ayrıntılı, daha kompakt çıktı seçeneği yok.

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
Vay be, bilmek güzel, bu özelliği görmedim. Yine de büyük bir kusuru var: Oxford virgülünün eksik olması .
Basil Bourque

4
@BasilBourque Oxford virgülü ABD için tipiktir, ancak ilginç bir şekilde İngiltere için değildir. Benim lib Time4J (çok daha iyi bir uluslararasılaştırmaya sahip) bu ayrımı net.time4j.PrettyTime sınıfında destekler ve ayrıca istenirse kompakt çıktı üzerinde kontrol sağlar.
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 gün 1s 6d 4sn


"ToHoursPart" ve üstünü nasıl elde ediyorsunuz?
Ghoti ve Chips

5

Joda-Time'ın oluşturucu yaklaşımına bir alternatif, model tabanlı bir çözüm olabilir . Bu, kütüphanem Time4J tarafından sunulmaktadır . Duration.Formatter sınıfını kullanan örnek (daha fazla okunabilirlik için bazı boşluklar eklendi - boşlukların kaldırılması istenen C #-stilini verecektir):

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

Bu formatlayıcı, java.time-API sürelerini de yazdırabilir (ancak, bu türün normalleştirme özellikleri daha az güçlüdür):

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

OP'nin "123456 ms uzunluğunda 4d1h3m5s olarak yazdırılacağı" beklentisi açıkça yanlış bir şekilde hesaplanmıştır. Uyumsuzluğu sebep olarak kabul ediyorum. Yukarıda tanımlanan aynı süre biçimlendiricisi, ayrıştırıcı olarak da kullanılabilir. Aşağıdaki kod, "4d1h3m5s" değerinin aşağıdakilere karşılık geldiğini gösterir 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

Başka bir yol da sınıfı kullanmaktır net.time4j.PrettyTime(yerelleştirilmiş çıktı ve "dün", "sonraki Pazar", "4 gün önce" gibi göreli zamanları yazdırmak için de iyidir):

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

Metin genişliği, kısaltmaların kullanılıp kullanılmadığını denetler. Liste formatı da uygun yerel ayar seçilerek kontrol edilebilir. Örneğin, standart İngilizce Oxform virgülünü kullanırken Birleşik Krallık kullanmaz. Time4J'nin en son v5.5 sürümü 90'dan fazla dili destekler ve CLDR deposuna (endüstri standardı) dayalı çeviriler kullanır.


2
bu PrettyTime, diğer cevaptaki olandan çok daha iyi! çok kötü, önce bunu bulamadım.
ycomp

Neden şimdi bu iyi çalışan çözüm için iki olumsuz oy var bilmiyorum, bu yüzden yanıtı ayrıştırma (biçimlendirmenin tersi) hakkında daha fazla örnek vererek ve OP'nin yanlış beklentisini vurgulayarak genişletmeye karar verdim.
Meno Hochschild

4

User678573'ün cevabına dayalı bir Java 8 sürümü :

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'de PeriodFormatter olmadığı ve getHours, getMinutes gibi yöntemler olmadığı için ...

Java 8 için daha iyi bir sürüm görmekten mutlu olurum.


java.util.IllegalFormatConversionException: f! = java.lang.Long
erickdeoliveiraleal

Süre d1 = Günün Süresi (0); d1 = d1.plusSaat (47); d1 = d1.plusMinutes (124); d1 = d1.plusSeconds (124); System.out.println (String.format ("% s günler ve% sh% sm% 1.0fs", d1.toDays (), d1.toHours () - TimeUnit.DAYS.toSaatler (d1.toDays ()), d1 .toMinutes () - TimeUnit.HOURS.toMinutes (d1.toSaat ()), d1.toSeconds () - TimeUnit.MINUTES.toSeconds (d1.toMinutes ()));
erickdeoliveiraleal

@erickdeoliveiraleal Bunu işaret ettiğiniz için teşekkürler. Başlangıçta harika için kod yazıyordum, bu yüzden o dilde işe yarayabilirdi. Şimdi java için düzelttim.
Torsten

3

Bunun sizin kullanım durumunuza tam olarak uymayabileceğinin farkındayım, ancak PrettyTime burada yararlı olabilir.

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
Sadece "10 dakika" olması mümkün mü?
Johnny
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.