Làm thế nào để định dạng thời lượng trong java? (ví dụ: định dạng H: MM: SS)


164

Tôi muốn định dạng thời lượng tính bằng giây bằng cách sử dụng một mẫu như H: MM: SS. Các tiện ích hiện tại trong java được thiết kế để định dạng thời gian nhưng không phải là thời lượng.

Câu trả lời:


84

Nếu bạn đang sử dụng phiên bản Java trước 8 ... bạn có thể sử dụng Joda TimePeriodFormatter. Nếu bạn thực sự có một khoảng thời gian (tức là một khoảng thời gian đã trôi qua, không có tham chiếu đến hệ thống lịch) thì có lẽ bạn nên sử dụng Durationphần lớn - sau đó bạn có thể gọi toPeriod(chỉ định bất cứ điều gì PeriodTypebạn muốn phản ánh cho dù 25 giờ có trở thành 1 ngày và 1 giờ hoặc không, v.v.) để có được Periodđịnh dạng mà bạn có thể định dạng.

Nếu bạn đang sử dụng Java 8 trở lên: Tôi thường đề xuất sử dụng java.time.Durationđể thể hiện thời lượng. Sau đó, bạn có thể gọi getSeconds()hoặc muốn lấy số nguyên để định dạng chuỗi tiêu chuẩn theo câu trả lời của bobince nếu bạn cần - mặc dù bạn nên cẩn thận với tình huống trong đó thời lượng là âm, vì bạn có thể muốn có một dấu âm duy nhất trong chuỗi đầu ra . Vì vậy, một cái gì đó như:

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;
}

Định dạng theo cách này là hợp lý đơn giản, nếu thủ công gây khó chịu. Để phân tích cú pháp , nói chung trở thành một vấn đề khó hơn ... Bạn vẫn có thể sử dụng Joda Time ngay cả với Java 8 nếu bạn muốn, tất nhiên.


Bạn có còn khuyến nghị sử dụng thư viện Joda Time khi sử dụng Java 8 không?
Tim Büthe

1
@ TimBüthe: Không, tôi sẽ bắt đầu sử dụng java.time trong trường hợp đó. (Mặc dù tôi không thể tìm thấy ngay một phần tương đương với periodFormatter ...)
Jon Skeet

1
Không bao gồm periodFormatter. Đó là một trong những khác biệt giữa API thời gian ngày 8 của Java và Thời gian Joda.
Tim Büthe

1
@RexKerr: Tất nhiên, vẫn còn "sử dụng Joda Time" nếu bạn muốn, tất nhiên. Sẽ chỉnh sửa lại. (Nhìn lại, dù sao tôi cũng nên giới thiệu Thời lượng, bằng âm thanh của nó. Yêu cầu chỉnh sửa đáng kể ...)
Jon Skeet

4
@MarkJeronimus Thậm chí dễ dàng hơn sẽ được sử dụng các Durationphương pháp s: duration.toHours(), duration.toMinutesPart()duration.toSecondsPart()trong đó có sẵn từ Java 9. Và để có được giá trị một tuyệt đối có thể sử dụng duration.abs().
recke96

195

Nếu bạn không muốn kéo vào thư viện, thì đủ đơn giản để bạn tự sử dụng Trình định dạng hoặc phím tắt có liên quan, vd. số nguyên đã cho của giây s:

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

4
Không phải là số nguyên. Nếu bạn có hai ngày, chỉ cần cắm date1.getTime () - date2.getTime () vào phương trình trên, sử dụng nguyên hàm dài và vẫn hoạt động.
Josh

Nếu chênh lệch thời gian dài hơn 24h thì sao?
Rajish

2
@Rajish: bạn phải hỏi OP họ muốn gì trong trường hợp đó! Tất nhiên bạn có thể tách nó ra trong s/86400, (s%86400)/3600...nhiều ngày nếu cần thiết ...
bobince

Giải pháp của tôi về s > 86400 (một ngày): "\u221E"- vô cùng
QED

Nếu chênh lệch thời gian là hơn 24 giờ, nó vẫn định dạng độc đáo thời lượng tính bằng giờ, phút và giây. Đối với trường hợp sử dụng của tôi, điều này là như mong đợi.
Audrius Meskauskas


29

Điều này dễ dàng hơn kể từ Java 9. Một Durationvẫn không thể định dạng được, nhưng các phương thức để lấy giờ, phút và giây được thêm vào, điều này làm cho nhiệm vụ có phần đơn giản hơn:

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

Đầu ra từ đoạn trích này là:

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
Hừ! Chỉ cần nhấn tường với bình thường hóa múi giờ khi sử dụng phương pháp này. Bạn phải thêm sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));trước khi sử dụng formatchức năng.
Rajish

7

Đây có thể là một loại hacky, nhưng nó là một giải pháp tốt nếu người ta cố gắng hoàn thành việc này bằng cách sử dụng 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();

}

Tôi chỉ cố gắng này, và tôi nhận thấy rằng nếu tôi gọi là định dạng cho "hh: mm: ss" khi tôi didnt có bất kỳ giờ hoặc phút để định dạng (ví dụ Duration.ofSeconds(20)), sau đó tôi muốn có được một UnsupportedTemporalTypeException). Tôi chỉ đơn giản là loại bỏ mã kiểm tra nếu sự khác biệt là == 01. Tôi hình dung 01là một loại giá trị che giấu mà tôi không hiểu hết, bạn có thể giải thích nó không?
Groostav

Điều này thực sự làm việc rất tốt. Trong trường hợp của tôi, tôi thậm chí đã thêm mili giây ... Về isSupportedphương thức nó trả lại thông tin nếu có trường hợp lệ để hiển thị trên chuỗi có thể đọc được của con người. Dù sao thì "mặt nạ" thực sự không phải là mặt nạ. Các mã cho thấy return value!=0lkhông return value!=01. Vui lòng sử dụng bản sao-dán lần sau.
MrJames 8/11/2016

1
Giao diện TemporalAccessorkhông được sử dụng sai cho thời lượng. JSR-310 đã thiết kế giao diện TemporalAmountcho mục đích này.
Meno Hochschild

1
Tôi khuyên bạn luôn luôn sử dụng chữ hoa Lđể biểu thị một longgiá trị. Thật dễ dàng để đọc sai chữ thường lcho chữ số 1, như vừa trình bày.
Ole VV

6

Đây là một mẫu nữa làm thế nào để định dạng thời lượng. Lưu ý rằng mẫu này cho thấy cả thời gian dương và âm là thời lượng dương.

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

Câu trả lời này chỉ sử dụng Durationcác phương thức và hoạt động với 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

Làm thế nào về chức năng sau, trả về + H: MM: SS hoặc + 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

Có một cách tiếp cận khá đơn giản và (IMO), ít nhất là trong khoảng thời gian dưới 24 giờ:

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

Trình định dạng cần một đối tượng tạm thời để định dạng, vì vậy bạn có thể tạo một đối tượng bằng cách thêm thời lượng vào LocalTime là 00:00 (tức là nửa đêm). Điều này sẽ cung cấp cho bạn một LocalTime biểu thị thời lượng từ nửa đêm đến thời điểm đó, sau đó dễ dàng định dạng theo ký hiệu HH: mm: ss tiêu chuẩn. Điều này có lợi thế là không cần thư viện bên ngoài và sử dụng thư viện java.time để thực hiện phép tính, thay vì tính toán thủ công giờ, phút và giây.


3

Đây là một lựa chọn làm việc.

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;
}

Gọi phương thức với

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

Sản xuất một cái gì đó như:

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

2
Điều này sẽ thất bại trong thời lượng lớn hơn 23: 59: 59.999.
Michel Jung

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();
}

0

Thư viện của tôi Time4J cung cấp một giải pháp dựa trên mẫu (tương tự Apache DurationFormatUtils, nhưng linh hoạt hơn):

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

Mã này thể hiện các khả năng xử lý tràn giờ và xử lý dấu hiệu, xem thêm API của trình định dạng thời lượng dựa trên mẫu .


0

Trong scala (tôi đã thấy một số nỗ lực khác và không ấn tượng):

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

Có vẻ kinh khủng đúng không? Đó là lý do tại sao chúng tôi sử dụng IDE để viết nội dung này sao cho các cuộc gọi phương thức ( $toHoursPartvv) có màu khác.

Bộ nội suy chuỗi f"..."printf/ String.formatkiểu (là thứ cho phép phép $tiêm mã hoạt động) Với đầu ra là 1 14:06:32.583, fchuỗi nội suy sẽ tương đương vớiString.format("1 %02d:%02d:%02d.%03d", 14, 6, 32, 583)


0

sử dụng chức năng này

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));
}

Tới github.com/ipserc/duration để có được một phiên bản đóng gói của chức năng này
ipserc

-1

Trong Scala, xây dựng giải pháp của YourBestBet nhưng được đơn giản hóa:

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

trong scala, không cần thư viện:

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
Đây không phải là một quảng cáo tuyệt vời cho Scala? Không cần thư viện, nhưng chỉ cần nhiều mã ...?
Adam

Tôi thích cách tiếp cận đệ quy, nhưng nó có thể được đơn giản hóa rất nhiều: stackoverflow.com/a/52992235/3594403
dpoetzsch

-2

Tôi đã không nhìn thấy cái này vì vậy tôi nghĩ tôi muốn thêm nó:

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

Nó chỉ hoạt động trong 24 giờ nhưng đó là những gì tôi thường muốn trong thời gian.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.