Jak przekonwertować milisekundy na „X min, x sekund” w Javie?

571

Chcę zarejestrować czas, w System.currentTimeMillis()którym użytkownik zaczyna coś w moim programie. Kiedy skończy, odejmę prąd System.currentTimeMillis()od startzmiennej i chcę pokazać im upływ czasu, używając czytelnego dla człowieka formatu, takiego jak „XX godzin, XX minut, XX sekund” lub nawet „XX minut, XX sekund”, ponieważ jego raczej nie zajmie to komuś godziny.

Jak najlepiej to zrobić?

Kliknij opcję Upvote
źródło
5
Jeśli zajmie to więcej niż godzinę, nadal możesz wydrukować coś takiego; 90 minut, 53 sek.
Peter Lawrey
1
moja odpowiedź spóźnia się o 6 lat, ale myślę, że jest bardziej ogólna niż zaakceptowana: stackoverflow.com/a/35082080/82609
Sebastien Lorber
1
zapoznaj się z prostym przykładem konwertowania milisekund na dni, godziny, minuty i sekundy w Javie
sumit kumar pradhan

Odpowiedzi:

1227

Użyj java.util.concurrent.TimeUnitklasy:

String.format("%d min, %d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);

Uwaga: TimeUnitjest częścią specyfikacji Java 1.5, ale toMinuteszostała dodana od wersji Java 1.6.

Aby dodać wiodące zero dla wartości 0–9, po prostu wykonaj:

String.format("%02d min, %02d sec", 
    TimeUnit.MILLISECONDS.toMinutes(millis),
    TimeUnit.MILLISECONDS.toSeconds(millis) - 
    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);

Jeśli TimeUnitlub toMinutesnie są obsługiwane (np. W systemie Android przed wersją API 9), użyj następujących równań:

int seconds = (int) (milliseconds / 1000) % 60 ;
int minutes = (int) ((milliseconds / (1000*60)) % 60);
int hours   = (int) ((milliseconds / (1000*60*60)) % 24);
//etc...
Siddhadev
źródło
5
int miesiące = (int) ((zmiennoprzecinkowe) dni / 30.4368499f); int lata = (int) ((zmiennoprzecinkowe) dni / 365.242199f);
Nathan Schwermann
7
Sugeruję użycie mod 60 zamiast odejmowania minut, wygląda to na czystsze. Jednak trzeba wiedzieć, że 1 min = 60 sekund, aby to zrobić ... =)
Per Alexandersson,
2
@AndreasDietrich SimpleDateFormat nie działa w przypadku różnic czasowych, ponieważ zwraca tylko datę z epoki!
Muhammad Babar
5
int hours = (int) ((milliseconds / (1000*60*60)) % 24)to nie działa! zamiast tego powinno być takint hours = (int) (milliseconds / (1000*60*60)) ;
Muhammad Babar
5
Jeśli ktoś chce formatu plików wideo ( h: mm: ss ):String.format("%01d:%02d:%02d", hours, minutes, seconds);
kazy
124

Na podstawie odpowiedzi @ siddhadev napisałem funkcję, która konwertuje milisekundy na sformatowany ciąg:

   /**
     * Convert a millisecond duration to a string format
     * 
     * @param millis A duration to convert to a string form
     * @return A string of the form "X Days Y Hours Z Minutes A Seconds".
     */
    public static String getDurationBreakdown(long millis) {
        if(millis < 0) {
            throw new IllegalArgumentException("Duration must be greater than zero!");
        }

        long days = TimeUnit.MILLISECONDS.toDays(millis);
        millis -= TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(millis);
        millis -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
        millis -= TimeUnit.MINUTES.toMillis(minutes);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);

        StringBuilder sb = new StringBuilder(64);
        sb.append(days);
        sb.append(" Days ");
        sb.append(hours);
        sb.append(" Hours ");
        sb.append(minutes);
        sb.append(" Minutes ");
        sb.append(seconds);
        sb.append(" Seconds");

        return(sb.toString());
    }
Brent pisze kod
źródło
2
Powinieneś zrobić tam kontrolę pod kątem pojedynczego / wielokrotnego, aby uniknąć tego rodzaju sytuacji:1 Days 1 Hours 1 Minutes 1 Seconds
Daniel
7
Możemy użyć funkcji modułu zamiast odejmowania: long days = TimeUnit.MILLISECONDS.toDays(millis); long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24; long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60; long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60; long milliseconds = millis % 1000; a także metody String.format:return String.format("%d Days %d Hours %d Minutes %d Seconds %d Milliseconds", days, hours, minutes, seconds, milliseconds);
Jose Tepedino,
78
long time = 1536259;

return (new SimpleDateFormat("mm:ss:SSS")).format(new Date(time));

Wydruki:

25: 36: 259

Damien
źródło
1
Najbardziej podoba mi się powyższe podejście, jeśli na próżno jego prostota! Ponieważ mamy do czynienia z czasem i czasem trwania, zwykle używam Jody. Przykład, jeśli masz dwa DateTimes, odpowiednio start i koniec:Duration dur = new Duration(start, end); long millis = dur.getMillis();
TechTrip
1
Powinienem zanotować 2 sposoby korzystania z formatów Jody. Pierwsza odmiana motywu: DateTimeFormat.forPattern("mm:ss:SSS").print(new DateTime(time)); lub zmień Czas trwania na okres, który można automatycznie wydrukować za pomocą Jody PeriodFormatter. Konwersja może mieć utratę precyzji, jeśli jest inna niż chronologia ISO. Załóżmy czas trwania reprezentowany przez zmienną duration. Period period = duration.toPeriod().normalizedStandard(PeriodType.time()); PeriodFormat.getDefault().print(period)) Wydajność jest niesamowita: 1 sekunda i 581 milisekund, odpowiadając na główne pytanie powyżej.
TechTrip
2
Trochę późno tutaj :) Ale czy nie musiałbyś tutaj umieszczać simpleDateFormat.setTimezone (TimeZone.getTimeZone („GMT”)), chyba że jest to Twoja rzeczywista strefa czasowa?
Olle Söderström
@ OlleSöderström Rzeczywiście istnieje tutaj problem ze strefą czasową. Ale ustawienie go na GMT lub UTC powoduje, że część godzinowa ma wartość 12 zamiast 0 dla wszystkich czasów trwania krótszych niż 1 godzina.
Episodex,
2
Strefa czasowa nie jest tutaj jedynym problemem ... ta odpowiedź jest myląca i może poważnie wprowadzić w błąd początkujących użytkowników Javy. Nawet ignorując zbędne nawiasy zewnętrzne, OP zapytał, jak przedstawić czas trwania (różnica między dwoma punktami w czasie, mierzona w ms). Nazywanie tego czasu „czasem” i traktowanie go jako przesunięcia od 1 stycznia 1970 r. Tylko ze względu na formatowanie jego mniejszych komponentów jest po prostu… złym imo. Co więcej, to samo podejście zaproponowano już 3 lata wcześniej w tej odpowiedzi (zobacz komentarze tam, aby uzyskać więcej informacji na temat problemów związanych ze strefą czasową).
Amos M. Carpenter
36

Uhm ... ile milisekund trwa sekunda? A za minutę? Podział nie jest taki trudny.

int seconds = (int) ((milliseconds / 1000) % 60);
int minutes = (int) ((milliseconds / 1000) / 60);

Kontynuuj tak przez godziny, dni, tygodnie, miesiące, rok, dekady, cokolwiek.

Bombe
źródło
2
W rzeczywistości robienie tego dłużej niż godzinę nie jest dobrym pomysłem, ponieważ wyniki mogą być niepoprawne / nieintuicyjne, gdy w grę wchodzą czas letni (dni 23 lub 24 godziny) lub lata przestępne. Jeśli przeczytam „X nastąpi za 1 rok / miesiąc”, oczekiwałbym, że będzie to ta sama data i godzina.
Michael Borgwardt,
5
System.currentTimeMillis () jest odporny na DST, więc nie będą mylone przez dodatkowe lub brakujące godziny. Jeśli chcesz pokazać różnicę między dwiema konkretnymi datami, lepiej jest budować obiekty Date z podanym czasem i pokazać różnicę między tymi dwiema datami.
Bombe,
1
Poza tygodniami jest niezdefiniowany, ponieważ długość miesiąca jest zmienna. Rzeczywiście, musisz obliczyć w odniesieniu do określonego odniesienia czasu.
PhiLho,
31

Korzystanie z pakietu java.time w Javie 8:

Instant start = Instant.now();
Thread.sleep(63553);
Instant end = Instant.now();
System.out.println(Duration.between(start, end));

Dane wyjściowe są w formacie czasu trwania ISO 8601 : PT1M3.553S(1 minuta i 3,553 sekundy).

Vitalii Fedorenko
źródło
1
(ANDROID) Wymaga interfejsu API na poziomie 26
Ktoś gdzieś
1
@SomeoneSomewhere Dla Androida poniżej poziomu API 26 użyj backportu, zobacz Jak korzystać z ThreeTenABP w Android Project .
Ole VV
1
To niesamowite :) dzięki za heads-up!
Ktoś gdzieś
27

Nie wciągałbym w to dodatkowej zależności (przecież podział nie jest wcale taki trudny), ale jeśli mimo wszystko używasz Commons Lang, istnieją DurationFormatUtils .

Przykładowe użycie (dostosowane tutaj ):

import org.apache.commons.lang3.time.DurationFormatUtils

public String getAge(long value) {
    long currentTime = System.currentTimeMillis();
    long age = currentTime - value;
    String ageString = DurationFormatUtils.formatDuration(age, "d") + "d";
    if ("0d".equals(ageString)) {
        ageString = DurationFormatUtils.formatDuration(age, "H") + "h";
        if ("0h".equals(ageString)) {
            ageString = DurationFormatUtils.formatDuration(age, "m") + "m";
            if ("0m".equals(ageString)) {
                ageString = DurationFormatUtils.formatDuration(age, "s") + "s";
                if ("0s".equals(ageString)) {
                    ageString = age + "ms";
                }
            }
        }
    }
    return ageString;
}   

Przykład:

long lastTime = System.currentTimeMillis() - 2000;
System.out.println("Elapsed time: " + getAge(lastTime)); 

//Output: 2s

Uwaga : Aby uzyskać millis z dwóch obiektów LocalDateTime, możesz użyć:

long age = ChronoUnit.MILLIS.between(initTime, LocalDateTime.now())
Thilo
źródło
1
Świetnie, właśnie pytałem siebie, czy istnieje solidna biblioteka, która zawiera tego rodzaju rzeczy.
Lajcik
Ponieważ link nie jest na ogół bardzo przydatny, dodałem przykład, w jaki sposób można go użyć.
lepe
26

Wybierz podział ręki lub użyj interfejsu API SimpleDateFormat .

long start = System.currentTimeMillis();
// do your work...
long elapsed = System.currentTimeMillis() - start;
DateFormat df = new SimpleDateFormat("HH 'hours', mm 'mins,' ss 'seconds'");
df.setTimeZone(TimeZone.getTimeZone("GMT+0"));
System.out.println(df.format(new Date(elapsed)));

Edytuj przez Bombe : W komentarzach wykazano, że to podejście działa tylko przez krótsze okresy (tj. Krócej niż jeden dzień).

Cadrian
źródło
Łał. To zły hack zależny od strefy czasowej. Zniszczy się bezlitośnie, gdy masz przesunięcie strefy czasowej, która nie jest wielokrotnością 60 minut (a my mamy kilka tych irytujących 30-minutowych stref czasowych na świecie).
Bombe,
Zepsuje się równie źle, jak tylko podasz godziny w ciągu formatu i nie będzie GMT + 0, z tych samych powodów.
Bombe,
My robimy? Naprawdę? Gdzie? Nie wątpię w to, nigdy przedtem o tym nie słyszałem - trochę nowej pułapki do rozważenia ;-)
Treb
Tak. Sprawdź „Lista stref czasowych” na Wikipedii, np. Nepal jest na GMT + 05: 45.
Bombe,
2
cadrian, teraz będzie działać tylko przez czas krótszy niż jeden dzień. Liczba godzin zawsze będzie wynosić od 0 do 23 włącznie.
Bombe,
21

Aby dodać więcej informacji, jeśli chcesz sformatować: HH: mm: ss

0 <= HH <= nieskończony

0 <= mm <60

0 <= ss <60

Użyj tego:

int h = (int) ((startTimeInMillis / 1000) / 3600);
int m = (int) (((startTimeInMillis / 1000) / 60) % 60);
int s = (int) ((startTimeInMillis / 1000) % 60);

Właśnie miałem ten problem i wymyśliłem to

fredcrs
źródło
1
Najlepsza odpowiedź tutaj. Dlaczego wszyscy chcą, aby sprawy były tak skomplikowane? To prosta matematyka, ludzie.
Lambart
11

Myślę, że najlepszym sposobem jest:

String.format("%d min, %d sec", 
    TimeUnit.MILLISECONDS.toSeconds(length)/60,
    TimeUnit.MILLISECONDS.toSeconds(length) % 60 );
Xtera
źródło
przez kilka minut możesz użyć TimeUnit.MILLISECONDS.toMinutes (długość)
EminenT
11

Najkrótsze rozwiązanie:

Oto prawdopodobnie najkrótszy, który dotyczy również stref czasowych.

System.out.printf("%tT", millis-TimeZone.getDefault().getRawOffset());

Które wyjścia na przykład:

00:18:32

Wyjaśnienie:

%tTto czas sformatowany dla zegara 24-godzinnego jako %tH:%tM:%tS.

%tTakceptuje również longs jako dane wejściowe, więc nie ma potrzeby tworzenia Date. printf()wypisze po prostu czas określony w milisekundach, ale w bieżącej strefie czasowej, dlatego musimy odjąć surowe przesunięcie bieżącej strefy czasowej, aby 0 milisekund wynosiło 0 godzin, a nie wartość przesunięcia czasowego bieżącej strefy czasowej.

Uwaga 1: Jeśli potrzebujesz wyniku jako String, możesz uzyskać go w następujący sposób:

String t = String.format("%tT", millis-TimeZone.getDefault().getRawOffset());

Uwaga 2: Daje to poprawny wynik tylko, jeśli millisjest krótszy niż jeden dzień, ponieważ część dzienna nie jest uwzględniona w danych wyjściowych.

icza
źródło
To świetny skrót, ale kiedy go używam, nie osiągam oczekiwanych rezultatów. Więc mam: long millis = 8398;a kiedy String.format("%tT", millis)przekażę to do mojego wyjścia jest 19:00:08. Skąd pochodzi 19?
Nelda.techspiress
1
@ Nelda.techspiress To przesunięcie strefy czasowej. Musisz go odjąć TimeZone.getDefault().getRawOffset(), dokładnie tak, jak napisano w odpowiedzi:String.format("%tT", millis-TimeZone.getDefault().getRawOffset())
icza
który się tym zajął. Dziękuję za wyjaśnienie.
Nelda.techspiress
8

Joda-Time

Korzystanie z Joda-Time :

DateTime startTime = new DateTime();

// do something

DateTime endTime = new DateTime();
Duration duration = new Duration(startTime, endTime);
Period period = duration.toPeriod().normalizedStandard(PeriodType.time());
System.out.println(PeriodFormat.getDefault().print(period));
dogbane
źródło
1
Nie ma takiej potrzeby PeriodFormatw ostatnim wierszu. Po prostu zadzwoń, Period::toStringaby uzyskać domyślnie ciąg czasu trwania ISO 8601 .
Basil Bourque,
8

Przeglądając wkład @ brent-nash, moglibyśmy użyć funkcji modułu zamiast odejmowania i użyć metody String.format dla ciągu wynikowego:

  /**
   * Convert a millisecond duration to a string format
   * 
   * @param millis A duration to convert to a string form
   * @return A string of the form "X Days Y Hours Z Minutes A Seconds B Milliseconds".
   */
   public static String getDurationBreakdown(long millis) {
       if (millis < 0) {
          throw new IllegalArgumentException("Duration must be greater than zero!");
       }

       long days = TimeUnit.MILLISECONDS.toDays(millis);
       long hours = TimeUnit.MILLISECONDS.toHours(millis) % 24;
       long minutes = TimeUnit.MILLISECONDS.toMinutes(millis) % 60;
       long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) % 60;
       long milliseconds = millis % 1000;

       return String.format("%d Days %d Hours %d Minutes %d Seconds %d Milliseconds",
                            days, hours, minutes, seconds, milliseconds);
   }
Jose Tepedino
źródło
6

dla Androida poniżej API 9

(String.format("%d hr %d min, %d sec", millis/(1000*60*60), (millis%(1000*60*60))/(1000*60), ((millis%(1000*60*60))%(1000*60))/1000)) 
Pradeep
źródło
5

Na małe czasy, mniej niż godzinę, wolę:

long millis = ...

System.out.printf("%1$TM:%1$TS", millis);
// or
String str = String.format("%1$TM:%1$TS", millis);

dla dłuższych interwałów:

private static final long HOUR = TimeUnit.HOURS.toMillis(1);
...
if (millis < HOUR) {
    System.out.printf("%1$TM:%1$TS%n", millis);
} else {
    System.out.printf("%d:%2$TM:%2$TS%n", millis / HOUR, millis % HOUR);
}
użytkownik85421
źródło
To cenna wskazówka, istnieje wiele takich możliwości dla daty i godziny z API Formatter API ( docs.oracle.com/javase/8/docs/api/java/util/Formatter.html )! Możesz również otrzymać wynik warunku else z: System.out.printf ("% 1 $ tH:% 1 $ tM:% 1 $ tS% n", millis); lub nawet System.out.printf ("% 1 $ tT% n", millis);
Jose Tepedino
@JoseTepedino, ale wiesz, że pokazuje %tH tylko od 0 do 23 godzin? Oznacza to, że na przykład 24 godziny będą wyświetlane jako 00, 25 godzin jako 01... tak samo ważne przez %tT- dni zostaną po cichu odrzucone. Jasne, może również wyświetlać dni, ale byłoby to przesadą w stosunku do problemu, który miałem, kiedy to napisałem (w 2011 roku) [:-)
user85421
Rozumiem ... to naprawdę działałoby tylko w czasach krótszych niż jeden dzień (24h). Dziękuję Ci.
Jose Tepedino
5

Moje proste obliczenia:

String millisecToTime(int millisec) {
    int sec = millisec/1000;
    int second = sec % 60;
    int minute = sec / 60;
    if (minute >= 60) {
        int hour = minute / 60;
        minute %= 60;
        return hour + ":" + (minute < 10 ? "0" + minute : minute) + ":" + (second < 10 ? "0" + second : second);
    }
    return minute + ":" + (second < 10 ? "0" + second : second);
}

Miłego kodowania :)

Shohan Ahmed Sijan
źródło
Przypuszczam, że długie jest bardziej odpowiednie dla tej funkcji, ponieważ System.currentTimeMillis () zwraca długą wartość.
aprodan
To nie jest poprawne, czy sprawdziłeś wynik?
Jorgesys
4

Oto odpowiedź oparta na odpowiedzi Brenta Nasha, Mam nadzieję, że to pomaga!

public static String getDurationBreakdown(long millis)
{
    String[] units = {" Days ", " Hours ", " Minutes ", " Seconds "};
    Long[] values = new Long[units.length];
    if(millis < 0)
    {
        throw new IllegalArgumentException("Duration must be greater than zero!");
    }

    values[0] = TimeUnit.MILLISECONDS.toDays(millis);
    millis -= TimeUnit.DAYS.toMillis(values[0]);
    values[1] = TimeUnit.MILLISECONDS.toHours(millis);
    millis -= TimeUnit.HOURS.toMillis(values[1]);
    values[2] = TimeUnit.MILLISECONDS.toMinutes(millis);
    millis -= TimeUnit.MINUTES.toMillis(values[2]);
    values[3] = TimeUnit.MILLISECONDS.toSeconds(millis);

    StringBuilder sb = new StringBuilder(64);
    boolean startPrinting = false;
    for(int i = 0; i < units.length; i++){
        if( !startPrinting && values[i] != 0)
            startPrinting = true;
        if(startPrinting){
            sb.append(values[i]);
            sb.append(units[i]);
        }
    }

    return(sb.toString());
}
Reda Bouaichi
źródło
4
    long startTime = System.currentTimeMillis();
    // do your work...
    long endTime=System.currentTimeMillis();
    long diff=endTime-startTime;       
    long hours=TimeUnit.MILLISECONDS.toHours(diff);
    diff=diff-(hours*60*60*1000);
    long min=TimeUnit.MILLISECONDS.toMinutes(diff);
    diff=diff-(min*60*1000);
    long seconds=TimeUnit.MILLISECONDS.toSeconds(diff);
    //hour, min and seconds variables contains the time elapsed on your work
Fathah Rehman P.
źródło
3
Wzór w wierszu 6 jest nieprawidłowy. diff=diff-(hours*60*1000);powinno być diff=diff-(hours*60*60*1000);. Próbowałem go edytować, ale irytująca polityka edycji StackOverflow mówi, że nie ma wystarczającej liczby znaków do edycji.
quux00
4

Po pierwsze, System.currentTimeMillis()i Instant.now()nie są idealne do pomiaru czasu. Obaj zgłaszają czas naścienny, którego komputer nie zna dokładnie i który może poruszać się nieregularnie, w tym cofać się, jeśli na przykład demon NTP skoryguje czas systemowy. Jeśli twój pomiar czasu odbywa się na jednym komputerze, powinieneś zamiast tego użyć System.nanoTime () .

Po drugie, począwszy od Java 8, java.time.Duration jest najlepszym sposobem na przedstawienie czasu trwania:

long start = System.nanoTime();
// do things...
long end = System.nanoTime();
Duration duration = Duration.ofNanos(end - start);
System.out.println(duration); // Prints "PT18M19.511627776S"
System.out.printf("%d Hours %d Minutes %d Seconds%n",
        duration.toHours(), duration.toMinutes() % 60, duration.getSeconds() % 60);
// prints "0 Hours 18 Minutes 19 Seconds"
MikeFHay
źródło
3

Jeśli wiesz, że różnica czasu byłaby mniejsza niż godzina, możesz użyć następującego kodu:

    Calendar c1 = Calendar.getInstance();
    Calendar c2 = Calendar.getInstance();

    c2.add(Calendar.MINUTE, 51);

    long diff = c2.getTimeInMillis() - c1.getTimeInMillis();

    c2.set(Calendar.MINUTE, 0);
    c2.set(Calendar.HOUR, 0);
    c2.set(Calendar.SECOND, 0);

    DateFormat df = new SimpleDateFormat("mm:ss");
    long diff1 = c2.getTimeInMillis() + diff;
    System.out.println(df.format(new Date(diff1)));

Wynik: 51:00

Gourav Singla
źródło
3

Ta odpowiedź jest podobna do niektórych odpowiedzi powyżej. Uważam jednak, że byłoby to korzystne, ponieważ w przeciwieństwie do innych odpowiedzi, usunie to dodatkowe przecinki lub białe znaki i będzie obsługiwać skrót.

/**
 * Converts milliseconds to "x days, x hours, x mins, x secs"
 * 
 * @param millis
 *            The milliseconds
 * @param longFormat
 *            {@code true} to use "seconds" and "minutes" instead of "secs" and "mins"
 * @return A string representing how long in days/hours/minutes/seconds millis is.
 */
public static String millisToString(long millis, boolean longFormat) {
    if (millis < 1000) {
        return String.format("0 %s", longFormat ? "seconds" : "secs");
    }
    String[] units = {
            "day", "hour", longFormat ? "minute" : "min", longFormat ? "second" : "sec"
    };
    long[] times = new long[4];
    times[0] = TimeUnit.DAYS.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[0], TimeUnit.DAYS);
    times[1] = TimeUnit.HOURS.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[1], TimeUnit.HOURS);
    times[2] = TimeUnit.MINUTES.convert(millis, TimeUnit.MILLISECONDS);
    millis -= TimeUnit.MILLISECONDS.convert(times[2], TimeUnit.MINUTES);
    times[3] = TimeUnit.SECONDS.convert(millis, TimeUnit.MILLISECONDS);
    StringBuilder s = new StringBuilder();
    for (int i = 0; i < 4; i++) {
        if (times[i] > 0) {
            s.append(String.format("%d %s%s, ", times[i], units[i], times[i] == 1 ? "" : "s"));
        }
    }
    return s.toString().substring(0, s.length() - 2);
}

/**
 * Converts milliseconds to "x days, x hours, x mins, x secs"
 * 
 * @param millis
 *            The milliseconds
 * @return A string representing how long in days/hours/mins/secs millis is.
 */
public static String millisToString(long millis) {
    return millisToString(millis, false);
}
Jared Rummler
źródło
3

Tam jest problem. Gdy milisekundy wynoszą 59999, w rzeczywistości jest to 1 minuta, ale zostanie obliczona jako 59 sekund, a 999 milisekund zostanie utraconych.

Oto zmodyfikowana wersja oparta na poprzednich odpowiedziach, która może rozwiązać tę stratę:

public static String formatTime(long millis) {
    long seconds = Math.round((double) millis / 1000);
    long hours = TimeUnit.SECONDS.toHours(seconds);
    if (hours > 0)
        seconds -= TimeUnit.HOURS.toSeconds(hours);
    long minutes = seconds > 0 ? TimeUnit.SECONDS.toMinutes(seconds) : 0;
    if (minutes > 0)
        seconds -= TimeUnit.MINUTES.toSeconds(minutes);
    return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
}
Heksise
źródło
2

W Javie 9 jest to łatwiejsze:

    Duration elapsedTime = Duration.ofMillis(millisDiff );
    String humanReadableElapsedTime = String.format(
            "%d hours, %d mins, %d seconds",
            elapsedTime.toHours(),
            elapsedTime.toMinutesPart(),
            elapsedTime.toSecondsPart());

To daje ciąg podobny do 0 hours, 39 mins, 9 seconds.

Jeśli chcesz zaokrąglić całe sekundy przed formatowaniem:

    elapsedTime = elapsedTime.plusMillis(500).truncatedTo(ChronoUnit.SECONDS);

Aby pominąć godziny, jeśli wynoszą 0:

    long hours = elapsedTime.toHours();
    String humanReadableElapsedTime;
    if (hours == 0) {
        humanReadableElapsedTime = String.format(
                "%d mins, %d seconds",
                elapsedTime.toMinutesPart(),
                elapsedTime.toSecondsPart());

    } else {
        humanReadableElapsedTime = String.format(
                "%d hours, %d mins, %d seconds",
                hours,
                elapsedTime.toMinutesPart(),
                elapsedTime.toSecondsPart());
    }

Teraz możemy mieć na przykład 39 mins, 9 seconds.

Aby wydrukować minuty i sekundy z wiodącym zerem, tak aby zawsze były dwiema cyframi, po prostu wstaw 02do odpowiednich specyfikatorów formatu, a zatem:

    String humanReadableElapsedTime = String.format(
            "%d hours, %02d mins, %02d seconds",
            elapsedTime.toHours(),
            elapsedTime.toMinutesPart(),
            elapsedTime.toSecondsPart());

Teraz możemy mieć na przykład 0 hours, 39 mins, 09 seconds.

Ole VV
źródło
Jakiekolwiek formatowanie, aby usunąć godziny / minuty / sekundy, gdy poprzednia liczba to 0? Mam na myśli, że jest to możliwe do osiągnięcia pod warunkiem, że w innym przypadku, ale co z istnieniem takiego formatowania ciągu lub czegoś bliskiego, czy istnieje?
Farid,
1
Nie, przepraszam, @FARID, potrzebujesz if- elsetam.
Ole VV,
1

dla poprawnych ciągów („1 godzina, 3 sekundy”, „3 minuty”, ale nie „0 godzin, 0 minut, 3 sekundy”) piszę ten kod:

int seconds = (int)(millis / 1000) % 60 ;
int minutes = (int)((millis / (1000*60)) % 60);
int hours = (int)((millis / (1000*60*60)) % 24);
int days = (int)((millis / (1000*60*60*24)) % 365);
int years = (int)(millis / 1000*60*60*24*365);

ArrayList<String> timeArray = new ArrayList<String>();

if(years > 0)   
    timeArray.add(String.valueOf(years)   + "y");

if(days > 0)    
    timeArray.add(String.valueOf(days) + "d");

if(hours>0)   
    timeArray.add(String.valueOf(hours) + "h");

if(minutes>0) 
    timeArray.add(String.valueOf(minutes) + "min");

if(seconds>0) 
    timeArray.add(String.valueOf(seconds) + "sec");

String time = "";
for (int i = 0; i < timeArray.size(); i++) 
{
    time = time + timeArray.get(i);
    if (i != timeArray.size() - 1)
        time = time + ", ";
}

if (time == "")
  time = "0 sec";
Jan
źródło
1

Zmodyfikowałem odpowiedź @MyKuLLSKI i dodałem obsługę plurlizacji. Wyjąłem sekundy, ponieważ nie potrzebowałem ich, ale możesz je dodać ponownie, jeśli potrzebujesz.

public static String intervalToHumanReadableTime(int intervalMins) {

    if(intervalMins <= 0) {
        return "0";
    } else {

        long intervalMs = intervalMins * 60 * 1000;

        long days = TimeUnit.MILLISECONDS.toDays(intervalMs);
        intervalMs -= TimeUnit.DAYS.toMillis(days);
        long hours = TimeUnit.MILLISECONDS.toHours(intervalMs);
        intervalMs -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(intervalMs);

        StringBuilder sb = new StringBuilder(12);

        if (days >= 1) {
            sb.append(days).append(" day").append(pluralize(days)).append(", ");
        }

        if (hours >= 1) {
            sb.append(hours).append(" hour").append(pluralize(hours)).append(", ");
        }

        if (minutes >= 1) {
            sb.append(minutes).append(" minute").append(pluralize(minutes));
        } else {
            sb.delete(sb.length()-2, sb.length()-1);
        }

        return(sb.toString());          

    }

}

public static String pluralize(long val) {
    return (Math.round(val) > 1 ? "s" : "");
}
strumień bitów
źródło
int intervalMins vs long millis
Kiquenet
1

Omówiłem to w innej odpowiedzi, ale możesz zrobić:

public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
    long diffInMillies = date2.getTime() - date1.getTime();
    List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
    Collections.reverse(units);
    Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
    long milliesRest = diffInMillies;
    for ( TimeUnit unit : units ) {
        long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
        long diffInMilliesForUnit = unit.toMillis(diff);
        milliesRest = milliesRest - diffInMilliesForUnit;
        result.put(unit,diff);
    }
    return result;
}

Wyjście jest coś w rodzaju Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}z zamówionymi jednostkami.

Od Ciebie zależy, jak internacjonalizować te dane zgodnie z docelowymi ustawieniami regionalnymi.

Sebastien Lorber
źródło
1

Użyj java.util.concurrent.TimeUnit i użyj tej prostej metody:

private static long timeDiff(Date date, Date date2, TimeUnit unit) {
    long milliDiff=date2.getTime()-date.getTime();
    long unitDiff = unit.convert(milliDiff, TimeUnit.MILLISECONDS);
    return unitDiff; 
}

Na przykład:

SimpleDateFormat sdf = new SimpleDateFormat("yy/MM/dd HH:mm:ss");  
Date firstDate = sdf.parse("06/24/2017 04:30:00");
Date secondDate = sdf.parse("07/24/2017 05:00:15");
Date thirdDate = sdf.parse("06/24/2017 06:00:15");

System.out.println("days difference: "+timeDiff(firstDate,secondDate,TimeUnit.DAYS));
System.out.println("hours difference: "+timeDiff(firstDate,thirdDate,TimeUnit.HOURS));
System.out.println("minutes difference: "+timeDiff(firstDate,thirdDate,TimeUnit.MINUTES));
System.out.println("seconds difference: "+timeDiff(firstDate,thirdDate,TimeUnit.SECONDS));
Aramis Rodríguez Blanco
źródło