简体   繁体   English

如何在Java中计算时间?

[英]How to sum times in Java?

I'm working on a report that calculates a sum of the data in it and some of the data are timestamps, for example: 我正在处理一个报告,该报告计算其中的数据总和,其中一些数据是时间戳,例如:

----------------------
|  Activity |   Time   |
 ----------------------
|     1     | 11:00:00 |
 -----------------------
|     2     | 12:00:00 |
 -----------------------
|     3     | 13:00:00 |
 -----------------------
| Total     | 36:00:00 |
 ----------------------

I'm trying to sum timestamps as below: 我正在尝试将时间戳汇总如下:

final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
final Calendar c = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
c.setTimeInMillis(0);
for (final String t : timestampsList) {
    c.add(Calendar.MILLISECOND, (int) dt.parse(t).getTime());
}

The variable timestampsList is an ArrayList of String 's, all respecting the pattern used by the SimpleDateFormat object. 变量timestampsListStringArrayList ,所有这些都遵循SimpleDateFormat对象使用的模式。 The problem with the given code is that I can't generate the value of the sum of the timestamps, by using the same SimpleDateFormat what I get is an hour in the pattern informed in a future date. 给定代码的问题是,我无法通过使用相同的SimpleDateFormat生成时间戳总和的值,而我得到的只是将来日期中通知的模式中的一个小时。

I also have seen Joda Time Duration class but I'm not familiar with this lib and I 'don't know if I'm in a correct path that will lead me to the right answer. 我也看过Joda Time Duration类,但是我对这个lib并不熟悉,我不知道自己是否走对了,会带给我正确的答案。

Does anyone know how to handle it by using J2SE or Joda Time ? 有谁知道如何使用J2SE或Joda Time处理它?

I would just parse these Strings myself, convert them to 我自己解析这些字符串,将它们转换为
seconds or milliseconds and sum them up. 秒或毫秒,然后将它们加起来。 See answer 2 below. 请参阅下面的答案2。

ANSWER 1 答案1

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;


public class Test051 {

    public static void main(String[] args) throws Exception {
        String pt = "1970-01-01-";
        ArrayList<String> timestampsList = new ArrayList<String>();
        timestampsList.add("01:00:05");
        timestampsList.add("01:00:05");
        timestampsList.add("10:00:05");
        final DateFormat dt = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
        final Calendar sum = Calendar.getInstance();
        sum.setTimeInMillis(0);

        long tm0 = new SimpleDateFormat("yyyy-MM-dd").parse(pt).getTime();

        System.out.println("tm0 = " + tm0);

        for (final String t : timestampsList) {
            // System.out.println(dt.parse(pt + t).getTime());
            Date x = dt.parse(pt + t);
            // System.out.println(x.getTime());
            sum.add(Calendar.MILLISECOND, (int)x.getTime());
            sum.add(Calendar.MILLISECOND, (int)-tm0);
        }

        long tm = sum.getTime().getTime();
        System.out.println("tm = " + tm);

        tm = tm / 1000;


        long hh = tm / 3600;
        tm %= 3600;
        long mm = tm / 60;
        tm %= 60;
        long ss = tm;
        System.out.println(format(hh) + ":" + format(mm) + ":" + format(ss));
    }

    private static String format(long s){
        if (s < 10) return "0" + s;
        else return "" + s;
    }
}

ANSWER 2 答案2

import java.util.ArrayList;

public class Test051 {

    public static void main(String[] args) throws Exception {
        ArrayList<String> timestampsList = new ArrayList<String>();
        timestampsList.add("01:00:05");
        timestampsList.add("01:00:05");
        timestampsList.add("10:00:05");

        long tm = 0;
        for (String tmp : timestampsList){
            String[] arr = tmp.split(":");
            tm += Integer.parseInt(arr[2]);
            tm += 60 * Integer.parseInt(arr[1]);
            tm += 3600 * Integer.parseInt(arr[0]);
        }

        long hh = tm / 3600;
        tm %= 3600;
        long mm = tm / 60;
        tm %= 60;
        long ss = tm;
        System.out.println(format(hh) + ":" + format(mm) + ":" + format(ss));
    }

    private static String format(long s){
        if (s < 10) return "0" + s;
        else return "" + s;
    }
}

ANSWER 3 答案3

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class Test051 {

    public static void main(String[] args) throws Exception {
        ArrayList<String> timestampsList = new ArrayList<String>();
        timestampsList.add("01:00:00");
        timestampsList.add("02:00:00");
        timestampsList.add("03:00:00");
        timestampsList.add("04:00:00");
        timestampsList.add("02:00:00");
        timestampsList.add("04:00:00");

        Date dt0 = new SimpleDateFormat("yyyy-MM-dd").parse("1970-01-01");

        // Check very carefully the output of this one.
        System.out.println(dt0.getTime());

        final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
        final Calendar c = Calendar.getInstance();
        c.setTimeInMillis(0);
        for (final String t : timestampsList) {
            c.add(Calendar.MILLISECOND, (int) dt.parse(t).getTime());
            c.add(Calendar.MILLISECOND, (int)-dt0.getTime());
        }

        // We need to add this back. This is basically the time zone offset.
        c.add(Calendar.MILLISECOND, (int)dt0.getTime());

        System.out.println(c.getTime().getTime());
        System.out.println(c.getTimeInMillis());

        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(c.getTime()));
    }

}

If you don't wanna use peter petrov solution to parse your String yourself, the way to do it with Calendar and SimpleDateFormat is as follow : 如果您不想使用peter petrov解决方案自己解析String ,则使用CalendarSimpleDateFormat的方法如下:

List<String> timestampsList = new ArrayList<String>();
timestampsList.add("11:00:00");
timestampsList.add("12:00:00");
timestampsList.add("13:00:00");
final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
final Calendar c = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
long milliseconds = 0;
c.clear();
long startingMS = c.getTimeInMillis();
for (final String t : timestampsList) {
  milliseconds = milliseconds + (dt.parse(t).getTime() - startingMS);
}

System.out.println(milliseconds + " milliseconds");
System.out.println(milliseconds / 1000 + " seconds");
System.out.println(milliseconds / 1000 / 60 + " minutes");
System.out.println(milliseconds / 1000 / 60 / 60 + " hours");

Or use 或使用

long startingMS = dt.parse("00:00:00").getTime();
for (final String t : timestampsList) {
  milliseconds = milliseconds + (dt.parse(t).getTime() - startingMS);
}

instead, removing the need for the Calendar . 相反,无需使用Calendar Both result in : 两者都导致:

129600000 milliseconds 129600 seconds 2160 minutes 36 hours

Note that you might wanna make the results a double not to miss part of the time. 请注意,您可能想使结果double不要错过部分时间。

This is a original code from petrov with some edits made by me. 这是petrov的原始代码,我进行了一些编辑。 Since it's quite dificult to discuss in comments providing big snippets of code I posted it as an answer so we can discuss petrov's other considerations. 由于很难在提供大量代码片段的注释中进行讨论,因此我将其发布为答案,以便我们讨论petrov的其他注意事项。

public static void somaTempos(final String[] listaTempos) throws ParseException {
    long tm = 0;
    final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
    final Calendar c = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
    for (String tmp : listaTempos) {
        c.setTime(dt.parse(tmp));
        tm += c.get(Calendar.SECOND) + 60 * c.get(Calendar.MINUTE) + 3600 * c.get(Calendar.HOUR_OF_DAY);
    }

    final long l = tm % 3600;
    System.out.println(SIGRUtil.format(tm / 3600) + ':' + SIGRUtil.format(l / 60) + ':' + SIGRUtil.format(l % 60));
}

private static String format(long s) {
    if (s < 10) {
        return "0" + s;
    }
    return String.valueOf(s);
}

UPDATE: An alternative that also solves my problem: 更新:一种替代方案也解决了我的问题:

public static String sumTimes(final String[] timestampList) {
    long milliseconds = 0;
    final DateFormat dt = new SimpleDateFormat("HH:mm:ss");
    dt.setLenient(false);
    try {
        final long timezoneOffset = dt.parse("00:00:00").getTime();
        for (final String t: timestampList) {
            milliseconds += (dt.parse(t).getTime() - timezoneOffset);
        }
    } catch (final ParseException e) {
        throw new BusinessException(
                "One of the timestamps in the timestamp list cannot be applied to the HH:mm:ss pattern.", e);
    }

    ((SimpleDateFormat) dt).applyPattern(":mm:ss");
    return new StringBuilder(8).append(milliseconds / 3600000).append(
            dt.format(new Date(milliseconds))).toString();
}

Actually, the API gives me for free the minutes and the seconds by only reaplying another pattern in the DateFormat after calculating the sum of the time stamps, without forgetting to consider the timezone offset in this calculation, my real problem was how to calculate the number of hours which really is the less dificult part. 实际上,API仅通过在计算时间戳记之和后重新占用DateFormat中的另一种模式来免费提供分钟和秒,而在计算时不会忘记考虑时区偏移,我真正的问题是如何计算数字数小时,这实际上是难度较小的部分。

Any suggestions of improvements? 有什么改进建议吗?

If those data input Strings represent durations in hours:minutes:seconds without any date or time-of-day, then the other answers are working much too hard. 如果这些数据输入字符串以小时:分钟:秒表示的持续时间,没有任何日期或一天中的时间,则其他答案的作用就太大了。

Generally, the old java.util.Date and .Calendar classes are notoriously troublesome and should be avoided. 通常,旧的java.util.Date和.Calendar类非常麻烦,应避免使用。 Specifically here, those classes have no notion of a span of time. 特别是在这里,这些类没有时间跨度的概念。 Instead you should be using either Joda-Time or maybe java.time. 相反,您应该使用Joda-Time或java.time。

Joda-Time 乔达时间

Joda-Time offers three classes to represent a span of time: Interval , Period , and Duration . Joda-Time提供了三种表示时间跨度的类: IntervalPeriodDuration The first is tied to points along the timeline of the Universe. 第一个与沿宇宙时间线的点相关。 The other two are not. 其他两个不是。

The Period and Duration classes are very close cousins. Period和Duration类是非常接近的表亲。 Period is a tuple with a number of years, months, days, hours, minutes, and seconds. 期间是一个元组,具有若干年,月,日,小时,分钟和秒。 Duration is a number of milliseconds with no concept of fields such as days or seconds. 持续时间是毫秒数,没有诸如天或秒之类的字段的概念。

Joda-Time uses the ISO 8601 standard for its defaults in parsing and generating strings. Joda-Time使用ISO 8601标准作为其解析和生成字符串的默认值。 For period/duration time, this means the PnYnMnDTnHnMnS format. 对于周期/持续时间,这表示PnYnMnDTnHnMnS格式。 The P means "period" and the T is a separator between date and time portions. P表示“句点”, T表示日期和时间部分之间的分隔符。

Here is some example code in Joda-Time 2.3. 这是Joda-Time 2.3中的一些示例代码。 Basically a couple of lines: parsePeriod & durationSum.plus seen below. 基本上有两行: parsePerioddurationSum.plus如下所示。

Simulate input strings. 模拟输入字符串。

List<String> durationStrings = new ArrayList<String>();
durationStrings.add( "11:00:00" ); // Number of hours/minutes/seconds. Not time-of-day.
durationStrings.add( "12:00:00" );
durationStrings.add( "13:00:00" ); // Expect sum of 36 hours = 11 + 12 + 13.

Define a formatter to parse those strings. 定义一个格式化程序以解析那些字符串。 Joda-Time might have such a formatter built-in, but I could not locate it. Joda-Time可能内置了这样的格式化程序,但是我找不到它。 So I defined one. 所以我定义了一个。

PeriodFormatter formatter = new PeriodFormatterBuilder()
        .appendHours()
        .appendSeparator( ":" )
        .appendMinutes()
        .appendSeparator( ":" )
        .appendSeconds()
        .toFormatter();

Loop the input strings, parsing each one, then adding its duration to the sum. 循环输入字符串,解析每个字符串,然后将其持续时间添加到总和中。

Duration durationSum = Duration.ZERO;  // Initializing to empty amount. Add to this in loop below.
for ( String durationString : durationStrings ) {
    Period period = formatter.parsePeriod( durationString );
    Duration duration = period.toStandardDuration();
    durationSum = durationSum.plus( duration );
    System.out.println( "period: " + period );
    System.out.println( "duration: " + duration );
}
System.out.println( "durationSum: " + durationSum );
System.out.println( "durationSum as Period: " + durationSum.toPeriod() );

When run… 运行时...

period: PT11H
duration: PT39600S
period: PT12H
duration: PT43200S
period: PT13H
duration: PT46800S
durationSum: PT129600S
durationSum as Period: PT36H

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM