Java에서 Duration을 어떻게 "예쁘게 인쇄"할 수 있습니까?


95

C #과 같은 방식으로 밀리 초 단위로 숫자를 인쇄 할 수있는 Java 라이브러리를 아는 사람이 있습니까?

예를 들어 123456 ms는 4d1h3m5s로 인쇄됩니다.


1
참고로, 설명하는 형식 Duration은 합리적인 표준 인 ISO 8601에 정의되어 있습니다 . PnYnMnDTnHnMnS여기서는 P"기간"을 의미하고 시작을 표시 T하고 날짜 부분을 시간 부분과 구분하며 그 사이는 단일 항목이있는 숫자의 선택적 발생입니다. -문자 약어. 예 : PT4H30M= 4 시간 30 분.
Basil Bourque 2014 년

1
다른 모든 방법이 실패하면 직접 수행하는 것이 매우 간단합니다. 단지의 연속 응용 프로그램을 사용 %하고 /부분으로 번호를 분할 할 수 있습니다. 제안 된 답변 중 일부보다 거의 쉽습니다.
Hot Licks 2014 년

@HotLicks /%.
Ole VV

예, 중간 팔년에 나는이 질문을하기 때문에 나는 아주 잘 내 사용 사례에 맞는 joda 시간 동안 이동 한 (!)
phatmanace

@phatmanace Joda-Time 프로젝트는 현재 유지 관리 모드에 있으며 Java 8 이상에 빌드 된 java.time 클래스 로의 마이그레이션을 권장 합니다.
Basil Bourque

답변:


91

Joda TimePeriodFormatterBuilder를 사용하여이를 수행하는 꽤 좋은 방법을 가지고 있습니다.

빠른 승리: 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 인스턴스를 직접 만들 수 있습니다. 예 : 기간 기간 = 새 기간 (밀리 초); 형식화 된 문자열 = formatter.print (period);
Basil Vandegriend

7
이 "duration.toPeriod ()"변환에주의하십시오. 기간이 상당히 크면 이후의 일 부분은 0으로 유지됩니다. 시간 부분은 계속 증가합니다. 25h10m23s는 얻을 수 있지만 "d"는 얻을 수 없습니다. 그 이유는 Joda의 엄격한 방법으로 시간을 일로 변환하는 완전히 올바른 방법이 없기 때문입니다. 대부분의 경우 두 순간을 비교하고이를 인쇄하려면 new Duration (t1, t2) .toPeriod () 대신 new Period (t1, t2)를 수행 할 수 있습니다.
Boon

@Boon 일과 관련하여 기간을 기간으로 변환하는 방법을 알고 있습니까? 타이머에서 초로 대체 할 수있는 기간을 사용합니다. 이벤트 설정 PeriodType.daysTime()또는 .standard()도움이되지 않음
murt

4
@murt 당신이 정말로 원하고 하루의 표준 정의를 받아 들일 수 있다면, 위의 코드에서 "formatter.print (duration.toPeriod (). normalizedStandard ())"를 할 수 있습니다. 즐기세요!
Boon

77

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
그것이 자바 독에 지정된 이후로는, 변화 가능성이 아니다 @Weltraumschaf
SE 악이기 때문에 aditsu 종료

9
@Weltraumschaf Duration::toString잘 정의되고 잘 착용 된 ISO 8601 표준 에 따라 출력 형식이 지정 되므로 변경 될 가능성이 거의 없습니다 .
Basil Bourque

1
분수를 원하지 않으면을 .replaceAll("\\.\\d+", "")호출하기 전에 추가하십시오 toLowerCase().
zb226

1
@Weltraumschaf 귀하의 요점은 일반적으로 정확하며 대부분의 클래스의 toString () 출력에 의존하지 않습니다. 그러나이 매우 특정한 경우, 메소드 정의 는 메소드의 Javadoc 에서 볼 수 있듯이 출력이 ISO-8601 표준을 따른다고 명시합니다 . 따라서 대부분의 클래스 에서처럼 구현 세부 사항이 아니므로 Java만큼 성숙한 언어가 그렇게 변경 될 것으로 기대하지 않습니다.
lucasls

13

Apache commons-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);

해당 형식이 원하는 형식이 아닌 경우 PeriodFormatterBuilderC # 스타일을 포함하여 임의의 레이아웃을 조합 할 수 있습니다 4d1h3m5s.


3
메모처럼 기본적으로를 new Period(millis).toString()사용합니다 ISOPeriodFormat.standard().
Thomas Beauvais

9

Java 8 에서는 PT8H6M12.345S와 같은 ISO 8601 초 기반 표현을 사용하여 외부 라이브러리없이 형식화 하는 toString()방법을 사용할 수도 있습니다 .java.time.Duration


4
이 날짜는 인쇄되지 않습니다
Wim Deblauwe

60
이 형식이 예쁜 인쇄물에 대한 나의 정의를 충족하는지 확실하지 않습니다 . :-)
james.garriss

@ james.garriss 좀 더 예쁘게 만들려면 erickdeoliveiraleal 의 답변 을 참조하십시오 .
Basil Bourque

8

다음은 순수한 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.timeJoda-시간 ,가 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
와, 알아서 반가워요. 그 기능을 본 적이 없습니다. 하지만 한 가지 큰 결함이 있습니다. 옥스포드 쉼표가 없습니다 .
Basil Bourque

4
@BasilBourque Oxford 쉼표는 미국에서는 일반적이지만 흥미롭게도 영국에서는 그렇지 않습니다. 내 lib Time4J (훨씬 더 나은 국제화가 있음)는 net.time4j.PrettyTime 클래스에서 이러한 구분을 지원하며 원하는 경우 압축 출력을 제어 할 수도 있습니다.
Meno Hochschild

6

자바 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 and Chips

5

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

"123456 ms가 4d1h3m5s로 인쇄 될 것"이라는 OP의 예상은 분명히 잘못된 방식으로 계산됩니다. 나는 엉성함을 이유로 생각합니다. 위에서 정의한 동일한 기간 포맷터를 파서로 사용할 수도 있습니다. 다음 코드는 "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 쉼표를 사용하지만 UK는 사용하지 않습니다. Time4J의 최신 버전 v5.5는 90 개 이상의 언어를 지원하며 CLDR-repository (산업 표준)를 기반으로 한 번역을 사용합니다.


2
이 PrettyTime은 다른 답변의 것보다 훨씬 낫습니다! 너무 나쁘다 나는 이것을 먼저 찾지 못했다.
ycomp

이 잘 작동하는 솔루션에 대해 지금 두 개의 반대표가있는 이유를 모르겠으므로 구문 분석 (포맷의 반대)에 대한 더 많은 예제를 제공하고 OP의 잘못된 기대를 강조하여 대답을 확장하기로 결정했습니다.
Meno Hochschild

4

user678573의 답변을 기반으로 한 Java 8 버전 :

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

기간 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 용 코드를 작성하고 있었기 때문에 해당 언어로 작동했을 수도 있습니다. 지금 자바로 수정했습니다.
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 분"만 가질 수 있습니까?
Johnny
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.