2009-01-25 158 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