I'm making a stop watch where I'm using Java's SimpleDateFormat to convert the number of milliseconds into a nice "hh:mm:ss:SSS" format. The problem is the hours field always has some random number in it. Here's the code I'm using:
我正在制作一个秒表,我正在使用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;
}
If I take off the hh part then it works fine. Otherwise in the hh part it'll display something random like "07" even if the argument passed in (number of milliseconds) is zero.
如果我脱掉hh部分那么它工作正常。否则在hh部分,即使传入的参数(毫秒数)为零,它也会显示随机的内容,如“07”。
I don't know much about the SimpleDateFormat class though. Thanks for any help.
我对SimpleDateFormat类知之甚少。谢谢你的帮助。
12 个解决方案
#1
6
Here's how I've done it, using only the standard JDK (this will work as far back as Java 1.1 by changing StringBuilder
back to StringBuffer
):
这是我如何使用标准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);
}
#2
65
Support for what you want to do is built in to the latest JDKs with a little known class called TimeUnit
.
What you want to use is java.util.concurrent.TimeUnit to work with intervals.
您想要使用的是java.util.concurrent.TimeUnit来处理间隔。
SimpleDateFormat
does just what it sounds like it does, it formats instances of java.util.Date
, or in your case it converts the long
value into the context of a java.util.Date
and it doesn't know what to do with intervals which is what you apparently are working with.
SimpleDateFormat就像它听起来那样,它格式化java.util.Date的实例,或者在你的情况下,它将long值转换为java.util.Date的上下文,并且它不知道如何处理间隔这是你显然正在使用的。
You can easily do this without having to resort to external libraries like JodaTime.
您可以轻松地执行此操作,而无需使用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])));
}
}
The output will be formatted something like this
输出将格式化为这样
13:00:00.000
#3
24
A shorter way to do this is to use the DurationFormatUtils class in Apache Commons Lang:
更简单的方法是在Apache Commons Lang中使用DurationFormatUtils类:
public static String formatTime(long millis) {
return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
#4
5
This is the first bit of Joda work I've done where it seemed more tedious than the JDK support. A Joda implementation for the requested format (making a few assumptions about zero fields) is:
这是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)));
}
#5
5
Why not this ?
为什么不呢?
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
3
Reviewing the other answers, I came up with this 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 );
}
}
#7
2
Here is what's going on. When you pass milliseconds, that number is relative to Jan 1st, 1970. When you pass 0, it takes that date and converts it to your local time zone. If you are in Central time then that happens to be 7PM. If you run this then it all makes sense.
这是正在发生的事情。当您传递毫秒时,该数字相对于1970年1月1日。当您传递0时,它将获取该日期并将其转换为您当地的时区。如果你在中部时间那么恰好是晚上7点。如果你运行它,那一切都有意义。
new SimpleDateFormat().format(0) => 12/31/69 7:00 PM
Edit, I think what you want to do is get elapsed time. For this I recommend using JodaTime which already does this pretty well. You do something like
编辑,我想你想要做的是获得经过的时间。为此,我建议使用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
1
The format happens according to your local timezone, so if you pass 0, it assumes 0 GMT and then converts it in your local timezone.
格式根据您当地的时区发生,因此如果您传递0,则假设为0 GMT,然后在您当地的时区进行转换。
#9
0
Using a plain Java Calendar
for intervals up to one day (24 hours) see my answer to the question: How to format time intervals in Java?
使用普通Java日历最多间隔一天(24小时),请参阅我对问题的回答:如何格式化Java中的时间间隔?
#10
0
Variant: Up to 24 hours
Simple formatting for elapsed time less than 24h. Over 24h the code will only display the hours within the next day and won't add the elapsed day to the hours.
经过时间少于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);
}
Missing features in sample code:
示例代码中缺少功能:
- Eliminate the timezone with "UTC"
- Use the 24h format "HH"
用“UTC”消除时区
使用24小时格式“HH”
Variant: Over 24 hours
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);
}
}
#11
0
Here's another way to do it. Fully self-contained and fully backwards-compatible. Unlimited number of days.
这是另一种方法。完全独立且完全向后兼容。无限天数。
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";
}
#12
0
This one actually works, but it seems like I'm tweaking the intent of the method :-).
这个实际上有效,但似乎我正在调整方法的意图:-)。
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String strDate = sdf.format(millis - 3600000);
return strDate;
}
For those of you who really knows how this works you'll probably find some caveats.
对于那些真正了解其工作原理的人,您可能会发现一些警告。
#1
6
Here's how I've done it, using only the standard JDK (this will work as far back as Java 1.1 by changing StringBuilder
back to StringBuffer
):
这是我如何使用标准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);
}
#2
65
Support for what you want to do is built in to the latest JDKs with a little known class called TimeUnit
.
What you want to use is java.util.concurrent.TimeUnit to work with intervals.
您想要使用的是java.util.concurrent.TimeUnit来处理间隔。
SimpleDateFormat
does just what it sounds like it does, it formats instances of java.util.Date
, or in your case it converts the long
value into the context of a java.util.Date
and it doesn't know what to do with intervals which is what you apparently are working with.
SimpleDateFormat就像它听起来那样,它格式化java.util.Date的实例,或者在你的情况下,它将long值转换为java.util.Date的上下文,并且它不知道如何处理间隔这是你显然正在使用的。
You can easily do this without having to resort to external libraries like JodaTime.
您可以轻松地执行此操作,而无需使用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])));
}
}
The output will be formatted something like this
输出将格式化为这样
13:00:00.000
#3
24
A shorter way to do this is to use the DurationFormatUtils class in Apache Commons Lang:
更简单的方法是在Apache Commons Lang中使用DurationFormatUtils类:
public static String formatTime(long millis) {
return DurationFormatUtils.formatDuration(millis, "HH:mm:ss.S");
}
#4
5
This is the first bit of Joda work I've done where it seemed more tedious than the JDK support. A Joda implementation for the requested format (making a few assumptions about zero fields) is:
这是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)));
}
#5
5
Why not this ?
为什么不呢?
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
3
Reviewing the other answers, I came up with this 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 );
}
}
#7
2
Here is what's going on. When you pass milliseconds, that number is relative to Jan 1st, 1970. When you pass 0, it takes that date and converts it to your local time zone. If you are in Central time then that happens to be 7PM. If you run this then it all makes sense.
这是正在发生的事情。当您传递毫秒时,该数字相对于1970年1月1日。当您传递0时,它将获取该日期并将其转换为您当地的时区。如果你在中部时间那么恰好是晚上7点。如果你运行它,那一切都有意义。
new SimpleDateFormat().format(0) => 12/31/69 7:00 PM
Edit, I think what you want to do is get elapsed time. For this I recommend using JodaTime which already does this pretty well. You do something like
编辑,我想你想要做的是获得经过的时间。为此,我建议使用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
1
The format happens according to your local timezone, so if you pass 0, it assumes 0 GMT and then converts it in your local timezone.
格式根据您当地的时区发生,因此如果您传递0,则假设为0 GMT,然后在您当地的时区进行转换。
#9
0
Using a plain Java Calendar
for intervals up to one day (24 hours) see my answer to the question: How to format time intervals in Java?
使用普通Java日历最多间隔一天(24小时),请参阅我对问题的回答:如何格式化Java中的时间间隔?
#10
0
Variant: Up to 24 hours
Simple formatting for elapsed time less than 24h. Over 24h the code will only display the hours within the next day and won't add the elapsed day to the hours.
经过时间少于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);
}
Missing features in sample code:
示例代码中缺少功能:
- Eliminate the timezone with "UTC"
- Use the 24h format "HH"
用“UTC”消除时区
使用24小时格式“HH”
Variant: Over 24 hours
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);
}
}
#11
0
Here's another way to do it. Fully self-contained and fully backwards-compatible. Unlimited number of days.
这是另一种方法。完全独立且完全向后兼容。无限天数。
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";
}
#12
0
This one actually works, but it seems like I'm tweaking the intent of the method :-).
这个实际上有效,但似乎我正在调整方法的意图:-)。
public static String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String strDate = sdf.format(millis - 3600000);
return strDate;
}
For those of you who really knows how this works you'll probably find some caveats.
对于那些真正了解其工作原理的人,您可能会发现一些警告。