我创建了以下代码来计算两个时间戳之间的持续时间,这两个时间戳可以有两种不同的格式:
public class dummyTime {
public static void main(String[] args) {
try {
convertDuration("2008-01-01 01:00 pm - 01:56 pm");
convertDuration("2008-01-01 8:30 pm - 2008-01-02 09:30 am");
} catch (Exception e) {
e.printStackTrace();
}
}
private static String convertDuration(String time) throws Exception {
String ts[] = time.split(" - ");
SimpleDateFormat formatNew = new SimpleDateFormat("HH:mm");
Date beg, end;
String duration = null;
beg = getDateTime(ts[0]);
end = getDateTime(ts[1], beg);
duration = formatNew.format(end.getTime() - beg.getTime());
System.out.println(duration + " /// " + time + " /// " + beg + " /// "
+ end);
return duration;
}
private static Date getDateTime(String dateTime) throws ParseException {
DateFormat formatOldDateTime = new SimpleDateFormat(
"yyyy-MM-dd hh:mm aa");
DateFormat formatOldTimeOnly = new SimpleDateFormat("hh:mm aa");
Date date = null;
try {
date = formatOldDateTime.parse(dateTime);
} catch (ParseException e) {
date = formatOldTimeOnly.parse(dateTime);
}
return date;
}
private static Date getDateTime(String dateTime, Date orig)
throws ParseException {
Date end = getDateTime(dateTime);
if (end.getYear() == 70) {
end.setYear(orig.getYear());
end.setMonth(orig.getMonth());
end.setDate(orig.getDate());
}
return end;
}
}
它生成的输出是:
01:56 /// 2008-01-01 01:00 pm - 01:56 pm /// Tue Jan 01 13:00:00 CET 2008 /// Tue Jan 01 13:56:00 CET 2008
14:00 /// 2008-01-01 8:30 pm - 2008-01-02 09:30 am /// Tue Jan 01 20:30:00 CET 2008 /// Wed Jan 02 09:30:00 CET 2008
我的问题是:
非常感谢,这个问题让我疯了几个小时。
答案 0 :(得分:4)
您正在格式化时间,而不是小时数和分钟数。因为你在冬季的CET时区[中欧时间],与UTC(“GMT”)相差一小时。
您可能希望使用Calendar
代替Date
。或Joda-Time。
答案 1 :(得分:2)
在我的电脑上这已经过了2个小时,因为我在GMT + 2,你可能在GMT + 1。请注意formatNew.format(end.getTime() - beg.getTime());
收到日期,即将您的56分钟视为1970-01-01-00:56:00 GMT + 1。要快速解决此问题,请致电formatNew.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
对于第二项,您可以检查format-yyyy-MM-dd是否失败(您发现解析错误),这就是您知道没有年份的方式。
答案 2 :(得分:1)
简单回答:使用SimpleDateFormat格式化表示没有日期的时间的值是不合适的。
更长的答案:Java时间值是自“epoch”以来的毫秒数:1970年1月1日午夜,UTC。
SimpleDateFormat假设您为其提供了有效的时间戳,并将本地化转换应用于日期和时间。我怀疑你的地方距离格林尼治标准时间(欧洲大陆)一小时,所以这就是为什么你看到的结果是一小时的结果。
虽然您可以通过设置时区GMT来欺骗SimpleDateFormat,但您可能最好使用显式数学显示持续时间:
int duration = 90;
System.out.printf("%02d:%02d", duration / 60, duration % 60);
答案 3 :(得分:0)
首先,您的示例字符串不一致:8:30 pm
缺少填充零。我会假设这是一个错字,应该是08:30 pm
。
顺便说一下,这些输入字符串格式是不可取的。
- 更好的方法是使用标准ISO 8601格式。
- 带AM / PM的12小时时钟很麻烦。标准格式使用24小时制,小时为0-23
- 间隔的标准表示法是由斜杠分隔的一对日期时间字符串:2008-01-01T13:00/2008-01-01T13:56
。
您的输入字符串还有另一个严重问题:没有offset-from-UTC或时区的指示。如果没有抵消或时区,我们必须回到假设通用的24小时工作日。这会忽略夏令时(DST)等异常情况,导致长达23或25小时的漫长天数。
如果您知道传入字符串的时区,请将其作为第二个参数传递,以获得正确的结果。
这个问题很老了。从那时起,Java已经用现代的java.time类取代了麻烦的旧日期时间类(Date
,Calendar
等)。我们在下面的示例代码中使用java.time。
这是一个完整的类,用于处理问题中给出的这些字符串。产生Duration
。
package javatimestuff;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
/**
*
* @author Basil Bourque
*/
public class DurationProcessor {
static final int SHORT = 30;
static final int LONG = 41;
static final DateTimeFormatter FORMATTER_LOCALDATETIME = DateTimeFormatter.ofPattern ( "uuuu-MM-dd hh:mm a" );
static final DateTimeFormatter FORMATTER_LOCALTIME = DateTimeFormatter.ofPattern ( "hh:mm a" );
static public Duration process ( String input ) {
return DurationProcessor.process ( input , ZoneOffset.UTC );
}
static public Duration process ( String input , ZoneId zoneId ) {
Duration d = Duration.ZERO; // Or maybe null. To be generated by the bottom of this code.
if ( null == input ) {
// …
System.out.println ( "ERROR - Passed null argument." );
return d;
}
if ( input.length () == 0 ) {
// …
System.out.println ( "ERROR - Passed empty string as argument." );
return d;
}
String inputModified = input.toUpperCase ( Locale.ENGLISH ); // Change `am` `pm` to `AM` `PM` for parsing.
String[] parts = inputModified.split ( " - " );
String inputStart = parts[ 0 ]; // A date-time sting.
String inputStop = parts[ 1 ]; // Either a date-time string or a time-only string (assume the same date).
ZonedDateTime start = null; // To be generated in this block of code.
try {
LocalDateTime ldt = LocalDateTime.parse ( inputStart , DurationProcessor.FORMATTER_LOCALDATETIME );
start = ldt.atZone ( zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The start failed to parse. inputStart: " + inputStart );
return d;
}
ZonedDateTime stop = null; // To be generated in this block of code.
switch ( input.length () ) {
case DurationProcessor.SHORT: // Example: "2008-01-01 01:00 pm - 01:56 pm"
try {
LocalTime stopTime = LocalTime.parse ( inputStop , DurationProcessor.FORMATTER_LOCALTIME );
stop = ZonedDateTime.of ( start.toLocalDate () , stopTime , zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The stop time failed to parse." );
return d;
}
break;
case DurationProcessor.LONG: // "2008-01-01 8:30 pm - 2008-01-02 09:30 am"
try {
LocalDateTime ldt = LocalDateTime.parse ( inputStop , DurationProcessor.FORMATTER_LOCALDATETIME );
stop = ldt.atZone ( zoneId );
} catch ( DateTimeParseException e ) {
// …
System.out.println ( "ERROR - The stop date-time failed to parse." );
return d;
}
break;
default:
// …
System.out.println ( "ERROR - Input string is of unexpected length: " + input.length () );
break;
}
d = Duration.between ( start , stop );
return d;
}
public static void main ( String[] args ) {
// Run with out time zone (assumes UTC).
Duration dShort = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" );
System.out.println ( "dShort: " + dShort );
Duration dLong = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" );
System.out.println ( "dLong: " + dLong );
// Run with specified time zone.
ZoneId z = ZoneId.of ( "America/Montreal" );
Duration dShortZoned = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" , z );
System.out.println ( "dShortZoned: " + dShortZoned );
Duration dLongZoned = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z );
System.out.println ( "dLongZoned: " + dLongZoned );
}
}
请注意类中的main
方法,例如用法。
首先是一对未指定时区的呼叫。因此,将使用UTC和24小时工作日。
Duration dShort = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" );
System.out.println ( "dShort: " + dShort );
Duration dLong = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" );
System.out.println ( "dLong: " + dLong );
我们确实指定了预定时区的另一对调用。
ZoneId z = ZoneId.of ( "America/Montreal" );
Duration dShortZoned = DurationProcessor.process ( "2008-01-01 01:00 pm - 01:56 pm" , z );
System.out.println ( "dShortZoned: " + dShortZoned );
Duration dLongZoned = DurationProcessor.process ( "2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z );
System.out.println ( "dLongZoned: " + dLongZoned );
请参阅live code in IdeOne.com中的此课程。
dShort:PT56M
dLong:PT13H
dShortZoned:PT56M
dLongZoned:PT13H
正如本页其他地方所述,使用00:56
等时间样式的输出格式含糊不清,应该避免使用。而Duration
类则使用标准ISO 8601 format for durations。在上面,我们看到了56分钟和13分钟的结果。
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。