Как отформатировать прошедший интервал времени в формате hh: mm: ss.SSS в Java?
Я делаю секундомер, где я использую Java SimpleDateFormat для преобразования количества миллисекунд в хороший формат "hh: mm: ss: SSS". Проблема заключается в том, что в поле часов всегда есть случайное число. Вот код, который я использую:
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss.SSS");
String strDate = sdf.format(millis);
return strDate;
}
Если я сниму часть hh, тогда он отлично работает. В противном случае в части hh будет отображаться что-то случайное, например "07", даже если аргумент, переданный в (миллисекунды), равен нулю.
Я не очень разбираюсь в классе SimpleDateFormat. Спасибо за любую помощь.
Ответы
Ответ 1
Вот как я это сделал, используя только стандартный 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);
}
Ответ 2
Поддержка того, что вы хотите сделать, встроена в последние 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
Ответ 3
Более короткий способ сделать это - использовать класс DurationFormatUtils в Apache Commons Lang:
public static String formatTime(long millis) {
return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
Ответ 4
Почему не это?
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);
}
Ответ 5
Это первый бит работы 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)));
}
Ответ 6
Рассматривая другие ответы, я придумал эту функцию...
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 );
}
}
Ответ 7
Вот что происходит. Когда вы проходите миллисекунды, это число относится к 1 января 1970 года. Когда вы передаете 0, он принимает эту дату и преобразует ее в локальный часовой пояс. Если вы находитесь в центральном времени, то это 7PM. Если вы запустите это, все это имеет смысл.
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));
Ответ 8
Формат происходит в соответствии с вашим местным часовым поясом, поэтому, если вы передаете 0, он принимает 0 GMT, а затем преобразует его в свой часовой пояс.
Ответ 9
Вот еще один способ сделать это. Полностью автономный и полностью обратно совместимый. Неограниченное количество дней.
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";
}
Ответ 10
Использование простой Java Calendar
для интервалов до одного дня (24 часа) см. мой ответ на вопрос: Как отформатировать временные интервалы в Java?
Ответ 11
Вариант: до 24 часов
Простое форматирование для прошедшего времени менее 24 часов. За 24 часа код будет отображать только часы в течение следующего дня и не будет добавлять истекший день в часы.
public static String formatElapsedTime(long milliseconds) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(milliseconds);
}
Отсутствующие функции в примере кода:
- Устранить часовой пояс с помощью "UTC"
- Используйте 24-часовой формат "HH"
Вариант: более 24 часов
public static String formatElapsedTimeOver24h(long milliseconds) {
// Compiler will take care of constant arithmetics
if (24 * 60 * 60 * 1000 > milliseconds) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(milliseconds);
} else {
SimpleDateFormat sdf = new SimpleDateFormat(":mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// Keep long data type
// Compiler will take care of constant arithmetics
long hours = milliseconds / (60L * 60L * 1000L);
return hours + sdf.format(milliseconds);
}
}
Ответ 12
Это действительно работает, но похоже, что я настраиваю цель метода: -).
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String strDate = sdf.format(millis - 3600000);
return strDate;
}
Для тех из вас, кто действительно знает, как это работает, вы, вероятно, найдете некоторые оговорки.
Ответ 13
TL;DR
LocalTime // Represents a time-of-day, without date and without time zone.
.ofNanoOfDay( // Convert a count of nanoseconds into a time-of-day in a generic 24-hour day, ignoring real-world anomalies such as Daylight Saving Time (DST).
Duration // Class that represents a span-of-time not attached to the timeline.
.of( milliseconds ) // Parse your count of milliseconds as a span-of-time.
.toNanos() // Extract total number of nanoseconds in this span-of-time.
) // Returns a 'LocalDate' object.
.toString() // Generate text in standard ISO 8601 format for a time-of-day (*not* recommended by me).
java.time.Duration
Подходящим классом для представления промежутка времени, не привязанного к временной шкале со шкалой часов-минут-секунд, является Duration
. Для шкалы лет-месяцев-дней используйте Period
.
Duration d = Duration.of( milliseconds ) ;
ISO 8601 формат
Я предлагаю вам не сообщать интервал времени, используя формат времени дня: ЧЧ: ММ: СС. Я видел неоднозначность, которая присуща мне, чтобы привести к неправильной интерпретации и путанице в реальных бизнес-приложениях.
Существует стандарт для сообщения таких значений, , определенный в ISO 8601: PnYnMnDTnHnMnS
. P
отмечает начало, в то время как T
отделяет любую часть года-месяца-дня от любой части часа-минуты-секунды.
Классы java.time по умолчанию используют стандартные форматы ISO 8601 при разборе/генерации строк. Это включает в себя класс Duration
.
Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toString() ;
См. этот код в режиме реального времени на IdeOne.com.
PT1H23M45.678S
Эти строки ISO 8601 можно анализировать и генерировать.
Duration d = Duration.parse( "PT1H23M45.678S" ) ;
Формат времени суток
Но если вы настаиваете на использовании формата времени дня для своей продолжительности, вы можете собрать такое ограничение, вызвав методы to…Part
для объекта Duration
.
Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toHoursPart() + ":" + d.toMinutesPart() + ":" + d.toSecondsPart() + "." + TimeUnit.NANOSECONDS.toMillis( d.toNanosPart() ) ;
1:23:45.678
Или мы можем использовать класс LocalTime
для создания вашей строки.
Duration d = Duration.ofMillis( 5_025_678L ) ;
long nanoOfDay = d.toNanos() ;
LocalTime localTimeBogus = LocalTime.ofNanoOfDay( nanoOfDay ) ;
String output = localTimeBogus.toString() ;
Опять же, вы можете увидеть этот код , запущенный на IdeOne.com.
01:23:45.678