Java:计算持续时间

Java:计算持续时间,java,datetime,duration,Java,Datetime,Duration,我创建了以下代码来计算两个时间戳之间的持续时间,这两个时间戳可以有两种不同的格式: 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");

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

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小时? 什么是更好的方法 一种无需修改即可识别时间戳的方法 一天70号看起来不太好 getDay和setDay函数是 我也不赞成。
非常感谢,这个问题已经让我疯狂了好几个小时。

您正在格式化一天中的时间,而不是小时数和分钟数。由于您在冬季处于CET时区[中欧时间],因此与UTC GMT相差一小时


您可能希望使用日历而不是日期。或者。

您正在格式化一天中的时间,而不是小时数和分钟数。由于您在冬季处于CET时区[中欧时间],因此与UTC GMT相差一小时

您可能希望使用日历而不是日期。或者

在我的电脑上,这是关闭了2个小时,因为我在GMT+2,而你可能在GMT+1。注意formatNew.formatend.getTime-beg.getTime;接收日期,即将您的56分钟视为1970-01-01-00:56:00 GMT+1。要快速解决此问题,请调用formatNew.setTimeZone TimeZone.getTimeZone GMT

对于第二项,您可以检查格式yyyy-MM-dd是否未能捕获解析错误,这就是您知道没有年份的原因

在我的电脑上,这是关闭了2个小时,因为我在GMT+2,而你可能在GMT+1。注意formatNew.formatend.getTime-beg.getTime;接收日期,即将您的56分钟视为1970-01-01-00:56:00 GMT+1。要快速解决此问题,请调用formatNew.setTimeZone TimeZone.getTimeZone GMT

对于第二项,您可以检查格式yyyy-MM-dd是否未能捕获解析错误,这就是您知道没有年份的原因


简单回答:使用SimpleDataFormat来格式化表示一天中没有日期的时间的值是不合适的

更详细的回答:Java时间值是从1970年1月1日午夜UTC开始的毫秒计数

SimpleDataFormat假定您给它一个有效的时间戳,并对日期和时间应用本地化转换。我怀疑您所在的地区距离欧洲大陆GMT还有一个小时,所以这就是为什么您看到的结果距离GMT还有一个小时

虽然您可以通过设置时区GMT愚弄SimpleDateFormat,但使用显式数学显示持续时间可能会更好:

int duration = 90;
System.out.printf("%02d:%02d", duration / 60, duration % 60);

简单回答:使用SimpleDataFormat来格式化表示一天中没有日期的时间的值是不合适的

更详细的回答:Java时间值是从1970年1月1日午夜UTC开始的毫秒计数

SimpleDataFormat假定您给它一个有效的时间戳,并对日期和时间应用本地化转换。我怀疑您所在的地区距离欧洲大陆GMT还有一个小时,所以这就是为什么您看到的结果距离GMT还有一个小时

虽然您可以通过设置时区GMT愚弄SimpleDateFormat,但使用显式数学显示持续时间可能会更好:

int duration = 90;
System.out.printf("%02d:%02d", duration / 60, duration % 60);

首先,示例字符串不一致:晚上8:30缺少填充零。我假设这是一个打字错误,应该是晚上8:30

不需要的字符串格式 顺便说一下,这些输入字符串格式是不可取的。 -更好的方法是使用标准格式。 -带有AM/PM的12小时时钟很麻烦。标准格式使用24小时时钟,0-23小时。 -间隔的标准表示法是由斜杠分隔的一对日期时间字符串:2008-01-01T13:00/2008-01-01T13:56

您的输入字符串还有另一个严重的问题:没有时间或时区的指示。如果没有偏移量或时区,我们必须退回到一般的24小时工作制。这忽略了异常情况,如夏令时DST,可能导致23或25小时长的一天

如果知道传入字符串的时区,请将其作为第二个参数传递,以获得正确的结果

java.time 这个问题很老了。从那时起,Java已经用现代的Java.time类取代了麻烦的旧日期时间类date、Calendar等。我们在下面的示例代码中使用java.time

示例类 下面是一个完整的类,用于处理问题中给出的这些字符串。产生了一种新的方法

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方法,例如usages

首先是一对没有指定时区的呼叫。因此,将使用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 );
实时代码 看这个班的排练

dShort:PT56M

dLong:PT13H

Dshortzone:PT56M

分区名称:PT13H

如本页其他地方所述,使用时间样式(如00:56)的输出格式不明确且容易混淆,应避免使用。Duration类改为使用standard。上面,我们看到了56分13秒的结果 分钟

关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

从哪里获得java.time类

后来 内置的。 标准JavaAPI的一部分,带有捆绑实现。 Java9添加了一些次要功能和修复。 和 大部分java.time功能都在中向后移植到Java6和Java7。 该项目特别针对Android采用了上述Three-Ten Backport。 看见
该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,例如、、和。

首先,您的示例字符串不一致:8:30 pm缺少填充零。我假设这是一个打字错误,应该是晚上8:30

不需要的字符串格式 顺便说一下,这些输入字符串格式是不可取的。 -更好的方法是使用标准格式。 -带有AM/PM的12小时时钟很麻烦。标准格式使用24小时时钟,0-23小时。 -间隔的标准表示法是由斜杠分隔的一对日期时间字符串:2008-01-01T13:00/2008-01-01T13:56

您的输入字符串还有另一个严重的问题:没有时间或时区的指示。如果没有偏移量或时区,我们必须退回到一般的24小时工作制。这忽略了异常情况,如夏令时DST,可能导致23或25小时长的一天

如果知道传入字符串的时区,请将其作为第二个参数传递,以获得正确的结果

java.time 这个问题很老了。从那时起,Java已经用现代的Java.time类取代了麻烦的旧日期时间类date、Calendar等。我们在下面的示例代码中使用java.time

示例类 下面是一个完整的类,用于处理问题中给出的这些字符串。产生了一种新的方法

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方法,例如usages

首先是一对没有指定时区的呼叫。因此,将使用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 );
实时代码 看这个班的排练

dShort:PT56M

dLong:PT13H

Dshortzone:PT56M

分区名称:PT13H

如本页其他地方所述,使用时间样式(如00:56)的输出格式不明确且容易混淆,应避免使用。Duration类改为使用standard。上面,我们看到了56分钟和13分钟的结果

关于java.time 该框架内置于Java8及更高版本中。这些类取代了麻烦的旧日期时间类,例如,&

该项目现已启动,建议迁移到类

要了解更多信息,请参阅。并搜索堆栈溢出以获得许多示例和解释。规格是

从哪里获得java.time类

后来 内置的。 标准JavaAPI的一部分,带有捆绑实现。 Java9添加了一些次要功能和修复。 和 大部分java.time功能都在中向后移植到Java6和Java7。 该项目特别针对Android采用了上述Three-Ten Backport。 看见
该项目使用其他类扩展了java.time。这个项目是java.time将来可能添加的一个试验场。您可以在这里找到一些有用的类,如、、和。

输入不一致。一天中的大多数时间值都有一个填充零,但8:30 pm忽略了它。输入不一致。一天中的大多数时间值都有填充零,但8:30 pm忽略了这一点。