[英]How to format an elapsed time interval in hh:mm:ss.SSS format in 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 class 了解不多。 謝謝你的幫助。
TimeUnit
。您要使用的是java.util.concurrent.TimeUnit來處理interval 。
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])));
}
}
output 將被格式化為這樣
13:00:00.000
更短的方法是在Apache Commons Lang中使用DurationFormatUtils class :
public static String formatTime(long millis) {
return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
為什么不是這個?
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);
}
這是我完成的第一個 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)));
}
以下是我的做法,僅使用標准 JDK(通過將StringBuilder
改回StringBuffer
可以追溯到 Java 1.1):
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);
}
查看其他答案,我想出了這個 function ......
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 );
}
}
這是發生了什么。 當您傳遞毫秒時,該數字相對於 1970 年 1 月 1 日。當您傳遞 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));
這是另一種方法。 完全獨立且完全向后兼容。 不限天數。
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";
}
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
正確的 class 以小時-分鍾-秒的比例表示未附加到時間線的時間跨度是Duration
。 對於年-月-日的比例,請使用Period
。
Duration d = Duration.of( milliseconds ) ;
我建議您避免使用 HH:MM:SS 的時間格式報告時間跨度。 我已經看到固有的模棱兩可導致現實世界商業應用程序中的誤解和混亂。
有一個報告此類值的標准,在ISO 8601中定義: PnYnMnDTnHnMnS
。 P
標志着開始,而T
將任何年-月-日部分與任何時-分-秒部分分開。
java.time類在解析/生成字符串時默認使用 ISO 8601 標准格式。 這包括Duration
class。
Duration d = Duration.ofMillis( 5_025_678L ) ;
String output = d.toString() ;
PT1H23M45.678S
這些 ISO 8601 字符串可以被解析和生成。
Duration d = Duration.parse( "PT1H23M45.678S" ) ;
但是,如果您堅持在持續時間中使用時間格式,則可以通過調用Duration
object 上的to…Part
方法來組合這樣的刺痛。
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
class 來創建您的字符串。
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
org.apache.commons.lang.time.DurationFormatUtils.formatDuration(stopWatch.getTime(), "HH:mm:ss")
格式根據您當地的時區發生,因此如果您傳遞 0,則假定為 0 GMT,然后將其轉換為您當地的時區。
您有一個持續時間(經過的毫秒數)不同於日期時間,即時間軸上的瞬間。 您使用SimpleDateFormat
來格式化java.util.Date
,它表示時間軸上的一個瞬間。 通過以下示例可以更好地理解差異:
前者代表時間軸上的一個瞬間,而后者代表一個持續時間。
java.time
java.util
日期時間 API 及其格式 API, SimpleDateFormat
已過時且容易出錯。 建議完全停止使用它們並切換到現代日期時間 API * 。
另外,下面引用的是來自Joda-Time主頁的通知:
請注意,從 Java SE 8 開始,要求用戶遷移到 java.time (JSR-310) - JDK 的核心部分,它取代了這個項目。
使用java.time
的解決方案,現代日期時間 API:我建議您使用java.time.Duration
,它以ISO-8601 標准為模型,並作為JSR-310 實現的一部分與Java-8一起引入。 在Java-9中,引入了一些更方便的方法。
演示:
import java.time.Duration;
public class Main {
public static void main(String[] args) {
// A sample input
long millis = 123456789L;
Duration duration = Duration.ofMillis(millis);
// Default format
System.out.println(duration);
// Custom format
// ####################################Java-8####################################
String formattedElapsedTime = String.format("%2d:%02d:%02d.%d", duration.toHours() % 24,
duration.toMinutes() % 60, duration.toSeconds() % 60, duration.toMillis() % 1000);
System.out.println(formattedElapsedTime);
// ##############################################################################
// ####################################Java-9####################################
formattedElapsedTime = String.format("%2d:%02d:%02d.%d", duration.toHoursPart(), duration.toMinutesPart(),
duration.toSecondsPart(), duration.toMillisPart());
System.out.println(formattedElapsedTime);
// ##############################################################################
}
}
Output:
PT34H17M36.789S
10:17:36.789
10:17:36.789
從Trail: Date Time了解更多關於現代日期時間 API *的信息。
*出於任何原因,如果您必須堅持Java 6或Java 7 7,您可以使用Threeten- Backport,以備份java888.TIME功能的大部分功能。級別仍然不符合 Java-8,檢查Java 8+ APIs available through desugaring和How to use ThreeTenABP in Android Project 。 Android 8.0 Oreo 已經提供了對java.time
的支持。
使用普通的 Java Calendar
間隔最多一天(24 小時),請參閱我對問題的回答:如何在 Java 中格式化時間間隔?
經過時間少於 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);
}
示例代碼中缺少的功能:
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);
}
}
這個確實有效,但似乎我正在調整該方法的意圖:-)。
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String strDate = sdf.format(millis - 3600000);
return strDate;
}
對於那些真正了解其工作原理的人,您可能會發現一些警告。
您可以在 DateUtils class 中使用formatElapsedTime或formatSameDayTime方法。
此方法必須適用於所有不太舊的 Java 版本)
public static String formatNanoSeconds(long duration) {
StringBuilder sb = new StringBuilder(20);
long quotient = duration / 1_000_000;
long remainder = quotient % 1_000;
sb.insert(0, String.format(".%03d", remainder));
quotient = quotient / 1_000;
remainder = quotient % 60;
sb.insert(0, String.format(":%02d", remainder));
quotient = quotient / 60;
remainder = quotient % 60;
sb.insert(0, String.format(":%02d", remainder));
quotient = quotient / 60;
remainder = quotient % 24;
sb.insert(0, String.format(" %02d", remainder));
quotient = quotient / 24;
sb.insert(0, String.format("%d", quotient));
return sb.toString();
}
StringBuilder.insert()
雖然完成數組復制,但仍必須比字符串連接更好。
這是測試:
@Test
void formatNanoSeconds() {
long m = 1_000_000;
assertEquals("0 00:00:00.000", formatNanoSeconds(0));
assertEquals("0 00:00:00.000", formatNanoSeconds(1));
assertEquals("0 00:00:00.000", formatNanoSeconds(999_999));
assertEquals("0 00:00:00.001", formatNanoSeconds(1_000_000));
assertEquals("0 00:00:00.999", formatNanoSeconds(999 * m));
assertEquals("0 00:00:01.000", formatNanoSeconds(1000 * m));
assertEquals("0 00:00:59.000", formatNanoSeconds(59_000 * m));
assertEquals("0 00:01:00.000", formatNanoSeconds(60_000 * m));
assertEquals("0 00:01:01.000", formatNanoSeconds(61_000 * m));
assertEquals("0 00:59:00.000", formatNanoSeconds(59 * 60_000 * m));
assertEquals("0 01:00:00.000", formatNanoSeconds(60 * 60_000 * m));
assertEquals("0 01:01:00.000", formatNanoSeconds(61 * 60_000 * m));
assertEquals("0 23:00:00.000", formatNanoSeconds(23 * 60 * 60_000 * m));
assertEquals("1 00:00:00.000", formatNanoSeconds(24 * 60 * 60_000 * m));
assertEquals("1 01:00:00.000", formatNanoSeconds(25 * 60 * 60_000 * m));
assertEquals("125 17:28:58.819", formatNanoSeconds(
((((125L * 24 * 3600) + (17 * 3600) + (28 * 60) + 58) * 1000) + 819) * m + 1));
assertEquals("90 08:05:04.009", formatNanoSeconds(
((((90L * 24 * 3600) + (8 * 3600) + (5 * 60) + 4) * 1000) + 9) * m + 358));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.