Как отформатировать прошедший интервал времени в формате чч: мм: сс.сс в Java? - PullRequest
34 голосов
/ 15 июля 2011

Я делаю секундомер, где использую Java SimpleDateFormat для преобразования количества миллисекунд в хороший формат "чч: мм: сс: SSS". Проблема в том, что в поле часов всегда есть случайное число. Вот код, который я использую:

public static String formatTime(long millis) {
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.SSS");

    String strDate = sdf.format(millis);
    return strDate;
}

Если я снимаю часть чч, то она работает нормально. В противном случае в части hh будет отображаться что-то случайное, например «07», даже если переданный аргумент (число миллисекунд) равен нулю.

Хотя я не знаю много о классе SimpleDateFormat. Спасибо за любую помощь.

Ответы [ 12 ]

69 голосов
/ 15 июля 2011

Поддержка того, что вы хотите сделать, встроена в последние JDK с малоизвестным классом под названием TimeUnit.

. То, что вы хотите использовать, это java.util.concurrent.TimeUnit для работы с интервалами .

SimpleDateFormat делает только то, что звучит так, как он это делает, он форматирует экземпляры java.util.Date, или в вашем случае он конвертирует значение long в контекст java.util.Date и незнать, что делать с интервалами , с которыми вы, очевидно, работаете.

Вы можете легко сделать это, не прибегая к внешним библиотекам, таким как JodaTime.

import java.util.concurrent.TimeUnit;

public class Main
{        
    private static String formatInterval(final long l)
    {
        final long hr = TimeUnit.MILLISECONDS.toHours(l);
        final long min = TimeUnit.MILLISECONDS.toMinutes(l - TimeUnit.HOURS.toMillis(hr));
        final long sec = TimeUnit.MILLISECONDS.toSeconds(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min));
        final long ms = TimeUnit.MILLISECONDS.toMillis(l - TimeUnit.HOURS.toMillis(hr) - TimeUnit.MINUTES.toMillis(min) - TimeUnit.SECONDS.toMillis(sec));
        return String.format("%02d:%02d:%02d.%03d", hr, min, sec, ms);
    }

    public static void main(final String[] args)
    {
        System.out.println(formatInterval(Long.parseLong(args[0])));
    }
}

Вывод будет отформатирован примерно так

13:00:00.000
28 голосов
/ 03 октября 2013

Более короткий способ сделать это - использовать класс DurationFormatUtils в Apache Commons Lang :

public static String formatTime(long millis) {
    return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
7 голосов
/ 15 июля 2015

Почему не это?

public static String GetFormattedInterval(final long ms) {
    long millis = ms % 1000;
    long x = ms / 1000;
    long seconds = x % 60;
    x /= 60;
    long minutes = x % 60;
    x /= 60;
    long hours = x % 24;

    return String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, millis);
}
6 голосов
/ 15 июля 2011

Вот как я это сделал, используя только стандартный JDK (это будет работать еще до Java 1.1, если изменить StringBuilder обратно на StringBuffer):

static public String formatMillis(long val) {
    StringBuilder                       buf=new StringBuilder(20);
    String                              sgn="";

    if(val<0) { sgn="-"; val=Math.abs(val); }

    append(buf,sgn,0,(val/3600000)); val%=3600000;
    append(buf,":",2,(val/  60000)); val%=  60000;
    append(buf,":",2,(val/   1000)); val%=   1000;
    append(buf,".",3,(val        ));
    return buf.toString();
    }

/** Append a right-aligned and zero-padded numeric value to a `StringBuilder`. */
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
    tgt.append(pfx);
    if(dgt>1) {
        int pad=(dgt-1);
        for(long xa=val; xa>9 && pad>0; xa/=10) { pad--;           }
        for(int  xa=0;   xa<pad;        xa++  ) { tgt.append('0'); }
        }
    tgt.append(val);
    }
5 голосов
/ 15 июля 2011

Это первая часть работы Joda, которая мне показалась более утомительной, чем поддержка JDK.Реализация Joda для запрошенного формата (с некоторыми предположениями о нулевых полях):

public void printDuration(long milliSecs)
{
    PeriodFormatter formatter = new PeriodFormatterBuilder()
        .printZeroIfSupported()
        .appendHours()
        .appendSeparator(":")
        .minimumPrintedDigits(2)
        .appendMinutes()
        .appendSeparator(":")
        .appendSecondsWithMillis()
        .toFormatter();

    System.out.println(formatter.print(new Period(milliSecs)));
}
3 голосов
/ 28 июня 2017

Просматривая другие ответы, я придумал эту функцию ...

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 );
    }
}
2 голосов
/ 15 июля 2011

Вот что происходит.Когда вы передаете миллисекунды, это число относится к 1 января 1970 года. Когда вы передаете 0, оно берет эту дату и преобразует ее в ваш местный часовой пояс.Если вы находитесь в центральном времени, то это будет 7 вечера.Если вы запустите это, тогда все это имеет смысл.

new SimpleDateFormat().format(0) => 12/31/69 7:00 PM

Правка, я думаю, что вы хотите сделать, это получить истекшее время.Для этого я рекомендую использовать JodaTime , который уже делает это довольно хорошо.Вы делаете что-то вроде

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendHours()
    .appendSuffix(" hour", " hours")
    .appendSeparator(" and ")
    .appendMinutes()
    .appendSuffix(" minute", " minutes")
    .appendSeparator(" and ")
    .appendSeconds()
    .appendSuffix(" second", " seconds")
    .toFormatter();

String formattedText = formatter.print(new Period(elapsedMilliSeconds));
1 голос
/ 15 июля 2011

Формат происходит в соответствии с вашим местным часовым поясом, поэтому, если вы передадите 0, он принимает 0 по Гринвичу, а затем преобразует его в ваш местный часовой пояс.

0 голосов
/ 06 октября 2017

Этот на самом деле работает, но, похоже, я настраиваю смысл метода: -).

public static String formatTime(long millis) {
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");

    String strDate = sdf.format(millis - 3600000);
    return strDate;
}

Для тех из вас, кто действительно знает, как это работаетвы, вероятно, найдете некоторые предостережения.

0 голосов
/ 09 апреля 2017

Вот еще один способ сделать это. Полностью автономный и полностью обратно совместимый. Неограниченное количество дней.

private static String slf(double n) {
  return String.valueOf(Double.valueOf(Math.floor(n)).longValue());
}

public static String timeSpan(long timeInMs) {
  double t = Double.valueOf(timeInMs);
  if(t < 1000d)
    return slf(t) + "ms";
  if(t < 60000d)
    return slf(t / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  if(t < 3600000d)
    return slf(t / 60000d) + "m " +
      slf((t % 60000d) / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  if(t < 86400000d)
    return slf(t / 3600000d) + "h " +
      slf((t % 3600000d) / 60000d) + "m " +
      slf((t % 60000d) / 1000d) + "s " +
      slf(t % 1000d) + "ms";
  return slf(t / 86400000d) + "d " +
    slf((t % 86400000d) / 3600000d) + "h " +
    slf((t % 3600000d) / 60000d) + "m " +
    slf((t % 60000d) / 1000d) + "s " +
    slf(t % 1000d) + "ms";
}
...