วิธีการจัดรูปแบบระยะเวลาใน java? (เช่นรูปแบบ H: MM: SS)


165

ฉันต้องการจัดรูปแบบระยะเวลาเป็นวินาทีโดยใช้รูปแบบเช่น H: MM: SS ยูทิลิตี้ปัจจุบันในจาวาถูกออกแบบมาเพื่อจัดรูปแบบเวลา แต่ไม่ใช่ช่วงเวลา

คำตอบ:


84

หากคุณกำลังใช้รุ่นของ Java ก่อน 8 ... คุณสามารถใช้Joda เวลาPeriodFormatterและ หากคุณมีเวลาจริง ๆ (เช่นระยะเวลาที่ผ่านไปโดยไม่มีการอ้างอิงถึงระบบปฏิทิน) ดังนั้นคุณควรใช้Durationเป็นส่วนใหญ่ - จากนั้นคุณสามารถโทรtoPeriod(ระบุสิ่งที่PeriodTypeคุณต้องการเพื่อสะท้อนให้เห็นว่า 25 ชั่วโมงกลายเป็น 1 วันและ 1 ชั่วโมงหรือไม่ ฯลฯ ) เพื่อให้ได้Periodรูปแบบที่คุณสามารถ

หากคุณใช้ Java 8 หรือใหม่กว่า: ปกติแล้วฉันจะแนะนำให้ใช้java.time.Durationเพื่อแสดงระยะเวลา จากนั้นคุณสามารถโทรgetSeconds()หรือชอบที่จะได้รับจำนวนเต็มสำหรับการจัดรูปแบบสตริงมาตรฐานตามคำตอบของ bobince ถ้าคุณต้องการ - แม้ว่าคุณควรระวังสถานการณ์ที่ระยะเวลาเป็นค่าลบเนื่องจากคุณอาจต้องการเครื่องหมายลบเดียวในสตริงเอาต์พุต . ดังนั้นสิ่งที่ชอบ:

public static String formatDuration(Duration duration) {
    long seconds = duration.getSeconds();
    long absSeconds = Math.abs(seconds);
    String positive = String.format(
        "%d:%02d:%02d",
        absSeconds / 3600,
        (absSeconds % 3600) / 60,
        absSeconds % 60);
    return seconds < 0 ? "-" + positive : positive;
}

การจัดรูปแบบด้วยวิธีนี้คือเหตุผลที่ง่ายๆถ้ารำคาญคู่มือ สำหรับการแยกวิเคราะห์มันจะกลายเป็นเรื่องที่ยากกว่าโดยทั่วไป ... คุณยังคงสามารถใช้ Joda Time แม้จะใช้ Java 8 แน่นอนถ้าคุณต้องการ


คุณยังแนะนำให้ใช้ไลบรารี Joda Time เมื่อใช้ Java 8 หรือไม่?
Tim Büthe

1
@ TimBüthe: ไม่ฉันจะเริ่มใช้ java.time ในกรณีนั้น (ถึงแม้ว่าฉันจะไม่สามารถหา PeriodFormatter ได้ในทันที ... )
Jon Skeet

1
ไม่รวมช่วงเวลาแบบฟอร์ม หนึ่งในข้อแตกต่างระหว่าง Java 8 Date Time API และ Joda Time
ทิมBüthe

1
@RexKerr: ยังมี "ใช้ Joda Time" ถ้าคุณต้องการแน่นอน จะแก้ไขอีกครั้ง (มองย้อนกลับไปฉันควรได้รับการแนะนำระยะเวลาด้วยเสียงของมันจำเป็นต้องมีการแก้ไขที่สำคัญ ... )
Jon Skeet

4
@MarkJeronimus ง่ายยิ่งขึ้นที่จะใช้Durationวิธีการ s: duration.toHours(), duration.toMinutesPart()และduration.toSecondsPart()ที่มีอยู่ตั้งแต่ Java 9 duration.abs()และเพื่อให้ได้หนึ่งค่าสัมบูรณ์สามารถใช้
recke96

195

หากคุณไม่ต้องการลากในไลบรารีมันง่ายพอที่จะทำด้วยตัวคุณเองโดยใช้ Formatter หรือทางลัดที่เกี่ยวข้องเช่น รับจำนวนเต็มวินาทีของ s:

  String.format("%d:%02d:%02d", s / 3600, (s % 3600) / 60, (s % 60));

4
ไม่จำเป็นต้องเป็นจำนวนเต็ม หากคุณมีสองวันเพียงแค่เสียบ date1.getTime () - date2.getTime () ลงในสมการข้างต้นซึ่งใช้แบบดั้งเดิมและยังคงใช้งานได้
Josh

เกิดอะไรขึ้นถ้าความแตกต่างของเวลานานกว่า 24 ชั่วโมง
Rajish

2
@Rajish: คุณต้องถาม OP สิ่งที่พวกเขาต้องการในกรณีนั้น! แน่นอนคุณสามารถแยกมันออกเป็นs/86400, (s%86400)/3600...วัน ๆ ถ้าจำเป็น ...
34417

วิธีแก้ปัญหาของฉันเป็นs > 86400 (หนึ่งวัน): "\u221E"- อินฟินิตี้
QED

หากความแตกต่างของเวลามากกว่า 24 ชั่วโมงมันยังคงจัดรูปแบบระยะเวลาเป็นชั่วโมงนาทีและวินาที สำหรับกรณีการใช้งานของฉันนี่เป็นไปตามที่คาดไว้
Audrius Meskauskas

123

ฉันใช้DurationFormatUtilsของ Apache Common อย่างเช่น:

DurationFormatUtils.formatDuration(millis, "**H:mm:ss**", true);

13
วิธีที่ง่ายที่สุดที่เป็นไปได้ (ในรูปแบบ apache คอมมอนส์) คุณยังมีวิธีการ formatDurationHMS ซึ่งคุณอาจใช้เวลาส่วนใหญ่
Marko

ที่สมบูรณ์แบบ! นอกจากนี้คุณยังสามารถเพิ่มข้อความอื่น ๆ ภายในเครื่องหมายวรรคตอน: DurationFormatUtils.formatDuration (DurationInMillis, "m 'นาที', 'วินาที'")
mihca

29

สิ่งนี้ง่ายกว่าเนื่องจาก Java 9 A Durationยังไม่สามารถจัดรูปแบบได้ แต่มีวิธีการเพิ่มชั่วโมงนาทีและวินาทีซึ่งทำให้งานค่อนข้างตรงไปตรงมา:

    LocalDateTime start = LocalDateTime.of(2019, Month.JANUARY, 17, 15, 24, 12);
    LocalDateTime end = LocalDateTime.of(2019, Month.JANUARY, 18, 15, 43, 33);
    Duration diff = Duration.between(start, end);
    String hms = String.format("%d:%02d:%02d", 
                                diff.toHours(), 
                                diff.toMinutesPart(), 
                                diff.toSecondsPart());
    System.out.println(hms);

ผลลัพธ์จากตัวอย่างนี้คือ:

24:19:21


26
long duration = 4 * 60 * 60 * 1000;
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault());
log.info("Duration: " + sdf.format(new Date(duration - TimeZone.getDefault().getRawOffset())));

14
ฮะ! เพียงแค่ชนกำแพงด้วยการปรับสภาพโซนเวลาเมื่อใช้วิธีนี้ คุณต้องเพิ่มsdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));ก่อนที่จะใช้formatฟังก์ชั่น
Rajish

7

นี่อาจเป็นเรื่องแฮ็ค แต่มันก็เป็นทางออกที่ดีถ้าใครที่พยายามทำสิ่งนี้โดยใช้ Java 8 java.time:

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.UnsupportedTemporalTypeException;

public class TemporalDuration implements TemporalAccessor {
    private static final Temporal BASE_TEMPORAL = LocalDateTime.of(0, 1, 1, 0, 0);

    private final Duration duration;
    private final Temporal temporal;

    public TemporalDuration(Duration duration) {
        this.duration = duration;
        this.temporal = duration.addTo(BASE_TEMPORAL);
    }

    @Override
    public boolean isSupported(TemporalField field) {
        if(!temporal.isSupported(field)) return false;
        long value = temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
        return value!=0L;
    }

    @Override
    public long getLong(TemporalField field) {
        if(!isSupported(field)) throw new UnsupportedTemporalTypeException(new StringBuilder().append(field.toString()).toString());
        return temporal.getLong(field)-BASE_TEMPORAL.getLong(field);
    }

    public Duration getDuration() {
        return duration;
    }

    @Override
    public String toString() {
        return dtf.format(this);
    }

    private static final DateTimeFormatter dtf = new DateTimeFormatterBuilder()
            .optionalStart()//second
            .optionalStart()//minute
            .optionalStart()//hour
            .optionalStart()//day
            .optionalStart()//month
            .optionalStart()//year
            .appendValue(ChronoField.YEAR).appendLiteral(" Years ").optionalEnd()
            .appendValue(ChronoField.MONTH_OF_YEAR).appendLiteral(" Months ").optionalEnd()
            .appendValue(ChronoField.DAY_OF_MONTH).appendLiteral(" Days ").optionalEnd()
            .appendValue(ChronoField.HOUR_OF_DAY).appendLiteral(" Hours ").optionalEnd()
            .appendValue(ChronoField.MINUTE_OF_HOUR).appendLiteral(" Minutes ").optionalEnd()
            .appendValue(ChronoField.SECOND_OF_MINUTE).appendLiteral(" Seconds").optionalEnd()
            .toFormatter();

}

ฉันพยายามนี้และผมสังเกตเห็นว่าถ้าผมเรียกว่ารูปแบบสำหรับ "HH: mm: ss" เมื่อฉันไม่ได้มีเวลาหรือนาทีในรูปแบบ (เช่นใด ๆDuration.ofSeconds(20)) แล้วผมได้รับUnsupportedTemporalTypeException) == 01ผมเคยเอาออกเพียงแค่การตรวจสอบรหัสถ้าแตกต่างกันคือ ฉันคิดว่า01มันมีคุณค่าในการปิดบังที่ฉันไม่เข้าใจคุณสามารถอธิบายได้หรือไม่?
Groostav

มันใช้งานได้ดีมาก ในกรณีของฉันฉันยังเพิ่มมิลลิวินาที ... เกี่ยวกับisSupportedวิธีการคืนข้อมูลถ้ามีฟิลด์ที่ถูกต้องเพื่อแสดงในสตริงมนุษย์อ่านได้ อย่างไรก็ตาม "masking" นั้นไม่ใช่หน้ากาก รหัสการแสดงไม่ได้return value!=0l return value!=01โปรดใช้ประโยชน์จากการคัดลอกวางในครั้งต่อไป
MrJames

1
อินเทอร์เฟซTemporalAccessorไม่ควรถูกใช้ในระยะเวลาที่ผิด JSR-310 ได้ออกแบบอินเตอร์เฟสTemporalAmountสำหรับวัตถุประสงค์นี้
Meno Hochschild

1
ฉันแนะนำให้คุณใช้ตัวพิมพ์ใหญ่ทุกครั้งLเพื่อแสดงlongค่า มันง่ายมากที่จะอ่านตัวพิมพ์เล็กผิดที่lเลข 1 เช่นเดียวกับที่แสดงให้เห็น
Ole VV

6

นี่คืออีกหนึ่งตัวอย่างวิธีการจัดรูปแบบระยะเวลา โปรดทราบว่าตัวอย่างนี้แสดงทั้งระยะเวลาบวกและลบเป็นระยะเวลาบวก

import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.HOURS;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.time.temporal.ChronoUnit.SECONDS;

import java.time.Duration;

public class DurationSample {
    public static void main(String[] args) {
        //Let's say duration of 2days 3hours 12minutes and 46seconds
        Duration d = Duration.ZERO.plus(2, DAYS).plus(3, HOURS).plus(12, MINUTES).plus(46, SECONDS);

        //in case of negative duration
        if(d.isNegative()) d = d.negated();

        //format DAYS HOURS MINUTES SECONDS 
        System.out.printf("Total duration is %sdays %shrs %smin %ssec.\n", d.toDays(), d.toHours() % 24, d.toMinutes() % 60, d.getSeconds() % 60);

        //or format HOURS MINUTES SECONDS 
        System.out.printf("Or total duration is %shrs %smin %sec.\n", d.toHours(), d.toMinutes() % 60, d.getSeconds() % 60);

        //or format MINUTES SECONDS 
        System.out.printf("Or total duration is %smin %ssec.\n", d.toMinutes(), d.getSeconds() % 60);

        //or format SECONDS only 
        System.out.printf("Or total duration is %ssec.\n", d.getSeconds());
    }
}

5

คำตอบนี้ใช้Durationวิธีการและทำงานกับ Java 8 เท่านั้น:

public static String format(Duration d) {
    long days = d.toDays();
    d = d.minusDays(days);
    long hours = d.toHours();
    d = d.minusHours(hours);
    long minutes = d.toMinutes();
    d = d.minusMinutes(minutes);
    long seconds = d.getSeconds() ;
    return 
            (days ==  0?"":days+" jours,")+ 
            (hours == 0?"":hours+" heures,")+ 
            (minutes ==  0?"":minutes+" minutes,")+ 
            (seconds == 0?"":seconds+" secondes,");
}

4

นี่คือตัวเลือกที่ใช้งานได้

public static String showDuration(LocalTime otherTime){          
    DateTimeFormatter df = DateTimeFormatter.ISO_LOCAL_TIME;
    LocalTime now = LocalTime.now();
    System.out.println("now: " + now);
    System.out.println("otherTime: " + otherTime);
    System.out.println("otherTime: " + otherTime.format(df));

    Duration span = Duration.between(otherTime, now);
    LocalTime fTime = LocalTime.ofNanoOfDay(span.toNanos());
    String output = fTime.format(df);

    System.out.println(output);
    return output;
}

เรียกวิธีการด้วย

System.out.println(showDuration(LocalTime.of(9, 30, 0, 0)));

ผลิตสิ่งที่ชอบ:

otherTime: 09:30
otherTime: 09:30:00
11:31:27.463
11:31:27.463

2
สิ่งนี้จะล้มเหลวสำหรับระยะเวลาที่มากกว่า 23: 59: 59.999
Michel Jung

4

วิธีการเกี่ยวกับฟังก์ชั่นต่อไปนี้ซึ่งจะส่งกลับอย่างใดอย่างหนึ่ง + H: MM: SS หรือ + H: MM: SS.sss

public static String formatInterval(final long interval, boolean millisecs )
{
    final long hr = TimeUnit.MILLISECONDS.toHours(interval);
    final long min = TimeUnit.MILLISECONDS.toMinutes(interval) %60;
    final long sec = TimeUnit.MILLISECONDS.toSeconds(interval) %60;
    final long ms = TimeUnit.MILLISECONDS.toMillis(interval) %1000;
    if( millisecs ) {
        return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
    } else {
        return String.format("%02d:%02d:%02d", hr, min, sec );
    }
}

4

มีวิธีการที่เรียบง่ายและหรูหรา (IMO) อย่างน้อยสำหรับระยะเวลาน้อยกว่า 24 ชั่วโมง:

DateTimeFormatter.ISO_LOCAL_TIME.format(value.addTo(LocalTime.of(0, 0)))

ตัวจัดรูปแบบต้องการวัตถุชั่วคราวเพื่อจัดรูปแบบเพื่อให้คุณสามารถสร้างได้โดยการเพิ่มระยะเวลาในเวลาท้องถิ่นของ 00:00 (เช่นเที่ยงคืน) สิ่งนี้จะให้ LocalTime เป็นตัวแทนของคุณในช่วงเวลาตั้งแต่เที่ยงคืนถึงเวลานั้นซึ่งเป็นรูปแบบที่ง่ายต่อการจัดรูปแบบในรูปแบบ HH: mm: ss มาตรฐาน สิ่งนี้มีข้อดีคือไม่ต้องใช้ไลบรารีภายนอกและใช้ไลบรารี java.time เพื่อทำการคำนวณแทนที่จะคำนวณชั่วโมงนาทีและวินาทีด้วยตนเอง


2
String duration(Temporal from, Temporal to) {
    final StringBuilder builder = new StringBuilder();
    for (ChronoUnit unit : new ChronoUnit[]{YEARS, MONTHS, WEEKS, DAYS, HOURS, MINUTES, SECONDS}) {
        long amount = unit.between(from, to);
        if (amount == 0) {
            continue;
        }
        builder.append(' ')
                .append(amount)
                .append(' ')
                .append(unit.name().toLowerCase());
        from = from.plus(amount, unit);
    }
    return builder.toString().trim();
}

1

ใช้งาน func นี้

private static String strDuration(long duration) {
    int ms, s, m, h, d;
    double dec;
    double time = duration * 1.0;

    time = (time / 1000.0);
    dec = time % 1;
    time = time - dec;
    ms = (int)(dec * 1000);

    time = (time / 60.0);
    dec = time % 1;
    time = time - dec;
    s = (int)(dec * 60);

    time = (time / 60.0);
    dec = time % 1;
    time = time - dec;
    m = (int)(dec * 60);

    time = (time / 24.0);
    dec = time % 1;
    time = time - dec;
    h = (int)(dec * 24);
    
    d = (int)time;
    
    return (String.format("%d d - %02d:%02d:%02d.%03d", d, h, m, s, ms));
}

ไปที่github.com/ipserc/durationเพื่อรับฟังก์ชั่นเวอร์ชั่นแพคเกจนี้
ipserc

นี่คือรหัสที่ฉันกำลังมองหาคำตอบที่ได้รับการยอมรับเป็นสิ่งที่ดี แต่มันไม่มีวันที่ฉันต้องการ
Filnor

0

ห้องสมุดTime4Jของฉันนำเสนอโซลูชันที่ใช้รูปแบบ (คล้ายกับApache DurationFormatUtilsแต่มีความยืดหยุ่นมากกว่า):

Duration<ClockUnit> duration =
    Duration.of(-573421, ClockUnit.SECONDS) // input in seconds only
    .with(Duration.STD_CLOCK_PERIOD); // performs normalization to h:mm:ss-structure
String fs = Duration.formatter(ClockUnit.class, "+##h:mm:ss").format(duration);
System.out.println(fs); // output => -159:17:01

รหัสนี้แสดงให้เห็นถึงความสามารถในการจับล้นชั่วโมงและการจัดการสัญญาณเห็น API ของระยะเวลา-จัดรูปแบบขึ้นอยู่กับรูปแบบ


0

ในสกาล่า (ฉันเห็นความพยายามอื่น ๆ และไม่ประทับใจ):

def formatDuration(duration: Duration): String = {
  import duration._ // get access to all the members ;)
  f"$toDaysPart $toHoursPart%02d:$toMinutesPart%02d:$toSecondsPart%02d:$toMillisPart%03d"
}

ดูน่ากลัวใช่มั้ย นั่นคือเหตุผลที่เราใช้ IDE เพื่อเขียนสิ่งนี้เพื่อให้วิธีการเรียก ($toHoursPartฯลฯ ) เป็นสีที่แตกต่างกัน

f"..."เป็นprintf/ String.formatinterpolator สตริงสไตล์ (ซึ่งเป็นสิ่งที่จะช่วยให้$การฉีดรหัสในการทำงาน) ได้รับการส่งออกของ1 14:06:32.583ผู้fสตริงหยันจะเทียบเท่ากับString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)


-1

ใน Scala การสร้างโซลูชันของ YourBestBet จะทำให้ง่ายขึ้น:

def prettyDuration(seconds: Long): List[String] = seconds match {
  case t if t < 60      => List(s"${t} seconds")
  case t if t < 3600    => s"${t / 60} minutes" :: prettyDuration(t % 60)
  case t if t < 3600*24 => s"${t / 3600} hours" :: prettyDuration(t % 3600)
  case t                => s"${t / (3600*24)} days" :: prettyDuration(t % (3600*24))
}

val dur = prettyDuration(12345).mkString(", ") // => 3 hours, 25 minutes, 45 seconds

-2

ในสกาล่าไม่จำเป็นต้องใช้ห้องสมุด:

def prettyDuration(str:List[String],seconds:Long):List[String]={
  seconds match {
    case t if t < 60 => str:::List(s"${t} seconds")
    case t if (t >= 60 && t< 3600 ) => List(s"${t / 60} minutes"):::prettyDuration(str, t%60)
    case t if (t >= 3600 && t< 3600*24 ) => List(s"${t / 3600} hours"):::prettyDuration(str, t%3600)
    case t if (t>= 3600*24 ) => List(s"${t / (3600*24)} days"):::prettyDuration(str, t%(3600*24))
  }
}
val dur = prettyDuration(List.empty[String], 12345).mkString("")

3
นี่ไม่ใช่โฆษณาที่ยอดเยี่ยมสำหรับ Scala ใช่ไหม ไม่จำเป็นต้องใช้ไลบรารี แต่มีรหัสมากพอ ... ?
อดัม

ฉันชอบวิธีแบบเรียกซ้ำ แต่สามารถทำให้ง่ายขึ้นมาก: stackoverflow.com/a/52992235/3594403
dpoetzsch

-2

ฉันไม่เห็นอันนี้ดังนั้นฉันคิดว่าฉันจะเพิ่ม:

Date started=new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
task
long duration=new Date().getTime()-started.getTime();
System.out.println(format.format(new Date(duration));

มันใช้งานได้แค่ 24 ชั่วโมง แต่นั่นเป็นสิ่งที่ฉันมักจะต้องการระยะเวลา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.