我有一个字符串值映射,表示不同组件的停机时间。
dependencyMap.put ("sut", "14:26:12,14:27:19,00:01:07;15:01:54,15:02:54,00:01:00;15:44:30,15:46:30,00:02:00;16:10:30,16:11:30,00:01:00");
dependencyMap.put ("jms", "14:26:12,14:28:12,00:02:00;15:10:50,15:12:55,00:02:05;15:42:30,15:43:30,00:01:00;16:25:30,16:27:30,00:02:00");
字符串代表停机时间的开始,结束和持续时间。
(start)14:26:12,(end)14:27:19,(duration)00:01:07
我读取了其中的值,然后将它们添加到包含
DependencyDownTime
值startTime,endTime和duration的Long
对象列表中。jArray.forEach (dependency ->{
String downTimeValues = knownDowntimesMap.get(dependency);
final String[] downtime = downTimeValues.split (";");
for (final String str : downtime) {
final DependencyDownTime depDownTime = new DependencyDownTime ();
final String[] strings = str.split (",");
if (strings.length == 3) {
final DateFormat dateFormat = new SimpleDateFormat ("HH:mm:ss");
try {
depDownTime.setStartTime(dateFormat.parse (strings[0]).getTime ());
depDownTime.setEndTime (dateFormat.parse (strings[1]).getTime ());
depDownTime.setDuration (dateFormat.parse (strings[2]).getTime ());
downTimes.add (depDownTime);
} catch (final ParseException e) {
//logger.warn (e.getMessage (), e);
}
} else {
//logger.warn ("");
}
}
然后,我对这些值执行简单的算术运算,以计算每个组件的总停机时间。
// sort the list by start time
Collections.sort(downTimes, Comparator.comparing (DependencyDownTime::getStartTime));
int i = 1;
Long duration = 0L;
for(DependencyDownTime dts: downTimes){
Long curStart = dts.getStartTime ();
Long curEnd = dts.getEndTime();
Long nextStart = downTimes.get(i).getStartTime ();
Long nextEnd = downTimes.get(i).getEndTime ();
if(duration == 0){
duration = dts.getDuration();
}
if(curStart.equals(nextStart) && curEnd < nextEnd){
duration += (nextEnd - curEnd);
}
else if(nextStart > curEnd){
duration += downTimes.get(i).getDuration();
}
else if( curStart < nextStart && curEnd > nextStart){
duration += (nextEnd - curEnd);
}
else if(curEnd == nextStart){
duration += downTimes.get(i).getDuration();
}
i++;
if(i == downTimes.size ()){
componentDTimeMap.put (application, duration);
return;
}
期望值应类似于
1970-01-01T 00:14:35 .000+0100
,只需几分钟。实际结果通常非常高,相差1969-12-31T 15:13:35 .000+0100
几个小时我有两个问题。
我是否正确解析值?
如果我的计算与长值相减时有些偏差。当我将值转换回日期格式时,期望值会有很大的不同吗?
最佳答案
如your other question中所述,请不要误解这两个不同的概念:
一天中的某个时间:代表一天中的特定时间点,例如10 AM
或14:45:50
持续时间:代表amount of time,例如“ 1小时10分钟”或“ 2年3个月4天”。持续时间不会告诉您开始或结束的时间(相对于什么是“ 1小时10分钟”?),它未附加到时间顺序中,也不对应于时间轴中的特定点。只是时间本身。
在输入中,您具有:
(start)14:26:12,(end)14:27:19,(duration)00:01:07
start
和end
代表一天中的时间,而duration
代表时间。 SimpleDateFormat
旨在用于一天中的日期和时间,但不能用于持续时间。将持续时间视为一天中的某个时间可能会起作用,但这是this answer中说明的问题。另一个问题是,当
SimpleDateFormat
仅解析时间时,它将默认日期设置为1970年1月1日的JVM默认时区,从而导致您看到所有奇怪的结果。不幸的是,没有办法避免这种情况,因为java.util.Date
具有完整的时间戳。更好的选择是使用新的日期/时间API。就像在your other question中使用Java 8一样,我假设您也可以在此处使用它(但是,如果您使用的是Java ThreeTen Backport,这是Java 8新日期的一个很好的反向端口/ time类。唯一的区别是包名称(在Java 8中为
java.time
,在ThreeTen Backport中(或Android的ThreeTenABP)为org.threeten.bp
),但类和方法的名称相同。由于您只使用时间,因此无需考虑日期字段(日/月/年),因此可以使用
LocalTime
代替。您可以直接解析字符串,因为它们在ISO861 compliant format中:LocalTime start = LocalTime.parse("14:26:12");
LocalTime end = LocalTime.parse("14:27:19");
不幸的是,在一段时间内没有内置的解析器,因此您必须手动解析它:
// parse the duration manually
String[] parts = "00:01:07".split(":");
Duration d = Duration
// get hours
.ofHours(Long.parseLong(parts[0]))
// plus minutes
.plusMinutes(Long.parseLong(parts[1]))
// plus seconds
.plusSeconds(Long.parseLong(parts[2]));
另一种选择是从输入中删除持续时间(或忽略它们)并使用开始和结束进行计算:
Duration d = Duration.between(start, end);
两者都会给您1分钟7秒的持续时间。
我的建议是更改
DependencyDownTime
以将开始和结束存储为LocalTime
对象,将持续时间存储为Duration
对象。这样,您的算法将如下所示:Duration total = Duration.ZERO;
for (...) {
LocalTime curStart = ...
LocalTime curEnd = ...
LocalTime nextStart = ...
LocalTime nextEnd = ...
if (total.toMillis() == 0) {
duration = dts.getDuration();
}
if (curStart.equals(nextStart) && curEnd.isBefore(nextEnd)) {
total = total.plus(Duration.between(curEnd, nextEnd));
} else if (nextStart.isAfter(curEnd)) {
total = total.plus(downTimes.get(i).getDuration());
} else if (curStart.isBefore(nextStart) && curEnd.isAfter(nextStart)) {
total = total.plus(Duration.between(curEnd, nextEnd));
} else if (curEnd.equals(nextStart)) {
total = total.plus(downTimes.get(i).getDuration());
}
i++;
if (i == downTimes.size()) {
// assuming you want the duration as a total of milliseconds
componentDTimeMap.put(application, total.toMillis());
return;
}
}
您可以存储
Duration
对象,也可以存储相应的毫秒值。不要尝试将其转换为Date
,因为日期并非设计成也不应该与持续时间一起使用。如果需要,您可以adapt this code格式化持续时间(不幸的是,持续时间没有本机格式化程序)。局限性
上面的代码假定所有
start
和end
时间都在同一天。但是,如果您在start
处有23:50
并在end
处有00:10
,则持续时间应为20分钟吗?如果真是这样,那就有点棘手了,因为
LocalTime
不知道日期(因此它认为23:50 > 00:10
并且它们之间的持续时间为“减去23小时40分钟”)。在这种情况下,您可以做一个技巧,并假设日期都在当前日期,但是当
start
大于end
时,这意味着end
时间在第二天:LocalTime start = LocalTime.parse("23:50");
LocalTime end = LocalTime.parse("00:10");
// calculate duration
Duration d;
if (start.isAfter(end)) {
// start is after end, it means end is in the next day
// current date
LocalDate now = LocalDate.now();
// start is at the current day
LocalDateTime startDt = now.atTime(start);
// end is at the next day
LocalDateTime endDt = now.plusDays(1).atTime(end);
d = Duration.between(startDt, endDt);
} else {
// both start and end are in the same day
// just calculate the duration in the usual way
d = Duration.between(start, end);
}
在上面的代码中,结果将为20分钟的
Duration
。不要将日期格式化为持续时间
这是为什么
SimpleDateFormat
和Date
不好处理持续时间的一些示例。假设我的持续时间为10秒。如果我尝试将值10转换为日期,将其转换为
java.util.Date
(又称持续时间为日期):// a 10 second duration (10000 milliseconds), treated as a date
Date date = new Date(10 * 1000);
System.out.println(date);
这将获得一个对应于“ unix纪元(
1970-01-01T00:00Z
)之后10000毫秒”的日期,即1970-01-01T00:00:10Z
。但是当我打印日期对象时,toString()
方法被隐式调用(如here所述)。并且此方法将此Millis值转换为JVM默认时区。在我使用的JVM中,默认时区为
America/Sao_Paulo
,因此上面的代码输出:1969年12月31日星期三21:00:10
这不是所期望的:UTC即时时间
1970-01-01T00:00:10Z
对应于1969年12月31日晚上9点在圣保罗时区。发生这种情况是因为我将持续时间错误地视为一个日期(并且输出将有所不同,具体取决于JVM中配置的默认时区)。
java.util.Date
不能(必须)用于持续时间。实际上,既然我们有了better API's,则应尽可能避免使用它。有too many problems和design issues,只要可以就不要使用。如果将持续时间作为日期来处理,
SimpleDateFormat
也将无法正常工作。在此代码中:SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date d = dateFormat.parse("10:00:00");
输入仅具有时间字段(小时,分钟和秒),因此
SimpleDateFormat
将日期设置为JVM默认时区的1970年1月1日。如果我System.out.println
这个日期,结果将是:1970年1月1日星期四10:00:00
那是1970年1月1日在圣保罗时区的上午10点,在UTC中它等效于
1970-01-01T13:00:00Z
-因此d.getTime()
返回46800000
。如果我将JVM默认时区更改为
Europe/London
,它将创建一个对应于1970年1月1日在伦敦(或UTC 1970-01-01T09:00:00Z
)上午10点的日期-并且d.getTime()
现在返回32400000
(因为在上午10点)伦敦和圣保罗的上午10点发生在不同的时刻。SimpleDateFormat
不是适合持续时间的正确工具-实际上是it isn't even the best tool to work with dates。