2009-01-25 88 views
1

我创建了下面的代码来计算两个时间戳之间的持续时间,可以有两种不同的格式:爪哇:计算持续时间

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 

我的问题是:

  1. 为什么结果总是错的 (always + 1h)?
  2. 什么是更好的 如何识别没有 一天的时间戳? == 70看起来不太好,而且 getDay & setDay函数也是 也不推荐使用。

很多很多谢谢,这个问题一直让我疯狂了好几个小时。

+0

输入不一致。大多数时间值的填充为零,但“8:30 pm”省略了这个值。 – 2017-01-28 23:58:25

回答

2
  1. 在我的电脑,这是关闭了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"));

  2. 对于第二项,您可以检查format-yyyy-MM-dd是否失败(您发现解析错误),这就是您如何知道没有年份。

+0

JODA时间http://joda-time.sourceforge.net/ – basszero 2009-01-25 16:50:40

4

您正在格式化一天中的时间,而不是时间和分钟数。由于您在冬季的中欧时区[中欧时间],与UTC(“GMT”)的时差不同一个小时。

您可能想要使用Calendar而不是Date。或者Joda-Time

1

简单的答案:使用SimpleDateFormat格式化表示无日期时间的值是不合适的。

更长的回答:Java时间值是从1970年1月1日午夜UTC的“时代”开始计算的毫秒数。

SimpleDateFormat假定您给它一个有效的时间戳,并将本地化转换应用于日期和时间。我怀疑你的地区是GMT(欧洲大陆)的一个小时,所以你看到的结果是一个小时。

虽然你可以通过设置时区GMT傻瓜SimpleDateFormat的,你可能会更好过使用明确的数学显示持续时间:

int duration = 90; 
System.out.printf("%02d:%02d", duration/60, duration % 60); 
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.time

这个问题很老了。从那以后,Java用现代java.time类取代了麻烦的旧日期时间类(DateCalendar等)。我们在下面的示例代码中使用java.time。

例类

这里是因为你的问题给出处理这些字符串一个完整的类。 A 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.time框架是建立在Java 8和更高版本。这些类取代了日期时间类legacy,如java.util.Date,Calendar,& SimpleDateFormat

Joda-Time项目现在位于maintenance mode,建议迁移到java.time类。请参阅Oracle Tutorial。并搜索堆栈溢出了很多例子和解释。规格是JSR 310

从何处获取java.time类?

ThreeTen-Extra项目与其他类扩展java.time。这个项目是未来可能增加java.time的一个试验场。您可以在这里找到一些有用的类,如IntervalYearWeekYearQuartermore