如何计算Java中的时间跨度并格式化输出?

时间:2009-03-11 19:11:10

标签: java date time formatting

我想拍两次(自纪元以来几秒钟)并以两种格式显示两者之间的差异:

  • 2分钟
  • 1小时15分钟
  • 3小时9分钟
  • 1分钟前
  • 1小时,2分钟前

我怎样才能完成这个?

18 个答案:

答案 0 :(得分:67)

因为每个人都喊“YOODAA !!!”但没有人发布一个具体的例子,这是我的贡献。

您也可以使用Joda-Time执行此操作。使用Period表示句点。要格式化所需人工代表中的句点,请使用PeriodFormatter构建的PeriodFormatterBuilder

这是一个启动示例:

DateTime myBirthDate = new DateTime(1978, 3, 26, 12, 35, 0, 0);
DateTime now = new DateTime();
Period period = new Period(myBirthDate, now);

PeriodFormatter formatter = new PeriodFormatterBuilder()
    .appendYears().appendSuffix(" year, ", " years, ")
    .appendMonths().appendSuffix(" month, ", " months, ")
    .appendWeeks().appendSuffix(" week, ", " weeks, ")
    .appendDays().appendSuffix(" day, ", " days, ")
    .appendHours().appendSuffix(" hour, ", " hours, ")
    .appendMinutes().appendSuffix(" minute, ", " minutes, ")
    .appendSeconds().appendSuffix(" second", " seconds")
    .printZeroNever()
    .toFormatter();

String elapsed = formatter.print(period);
System.out.println(elapsed + " ago");

更清晰简洁,不是吗?

现在打印

32 years, 1 month, 1 week, 5 days, 6 hours, 56 minutes, 24 seconds ago

(咳嗽,老,咳)

答案 1 :(得分:44)

    Date start = new Date(1167627600000L); // JANUARY_1_2007
    Date end = new Date(1175400000000L); // APRIL_1_2007


    long diffInSeconds = (end.getTime() - start.getTime()) / 1000;

    long diff[] = new long[] { 0, 0, 0, 0 };
    /* sec */diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    /* min */diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    /* hours */diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    /* days */diff[0] = (diffInSeconds = (diffInSeconds / 24));

    System.out.println(String.format(
        "%d day%s, %d hour%s, %d minute%s, %d second%s ago",
        diff[0],
        diff[0] > 1 ? "s" : "",
        diff[1],
        diff[1] > 1 ? "s" : "",
        diff[2],
        diff[2] > 1 ? "s" : "",
        diff[3],
        diff[3] > 1 ? "s" : ""));

答案 2 :(得分:18)

是的,Yup唤醒了我的死者,但这是基于@mtim发布的代码的改进实现,因为这个线程几乎在搜索之上,所以我正在弄乱困的空洞,

    public static String getFriendlyTime(Date dateTime) {
    StringBuffer sb = new StringBuffer();
    Date current = Calendar.getInstance().getTime();
    long diffInSeconds = (current.getTime() - dateTime.getTime()) / 1000;

    /*long diff[] = new long[]{0, 0, 0, 0};
    /* sec *  diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    /* min *  diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    /* hours *  diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    /* days * diff[0] = (diffInSeconds = (diffInSeconds / 24));
     */
    long sec = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);
    long min = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds;
    long hrs = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds;
    long days = (diffInSeconds = (diffInSeconds / 24)) >= 30 ? diffInSeconds % 30 : diffInSeconds;
    long months = (diffInSeconds = (diffInSeconds / 30)) >= 12 ? diffInSeconds % 12 : diffInSeconds;
    long years = (diffInSeconds = (diffInSeconds / 12));

    if (years > 0) {
        if (years == 1) {
            sb.append("a year");
        } else {
            sb.append(years + " years");
        }
        if (years <= 6 && months > 0) {
            if (months == 1) {
                sb.append(" and a month");
            } else {
                sb.append(" and " + months + " months");
            }
        }
    } else if (months > 0) {
        if (months == 1) {
            sb.append("a month");
        } else {
            sb.append(months + " months");
        }
        if (months <= 6 && days > 0) {
            if (days == 1) {
                sb.append(" and a day");
            } else {
                sb.append(" and " + days + " days");
            }
        }
    } else if (days > 0) {
        if (days == 1) {
            sb.append("a day");
        } else {
            sb.append(days + " days");
        }
        if (days <= 3 && hrs > 0) {
            if (hrs == 1) {
                sb.append(" and an hour");
            } else {
                sb.append(" and " + hrs + " hours");
            }
        }
    } else if (hrs > 0) {
        if (hrs == 1) {
            sb.append("an hour");
        } else {
            sb.append(hrs + " hours");
        }
        if (min > 1) {
            sb.append(" and " + min + " minutes");
        }
    } else if (min > 0) {
        if (min == 1) {
            sb.append("a minute");
        } else {
            sb.append(min + " minutes");
        }
        if (sec > 1) {
            sb.append(" and " + sec + " seconds");
        }
    } else {
        if (sec <= 1) {
            sb.append("about a second");
        } else {
            sb.append("about " + sec + " seconds");
        }
    }

    sb.append(" ago");


    /*String result = new String(String.format(
    "%d day%s, %d hour%s, %d minute%s, %d second%s ago",
    diff[0],
    diff[0] > 1 ? "s" : "",
    diff[1],
    diff[1] > 1 ? "s" : "",
    diff[2],
    diff[2] > 1 ? "s" : "",
    diff[3],
    diff[3] > 1 ? "s" : ""));*/
    return sb.toString();
}

显然可以改进。基本上它试图让时间跨度更友好,但有一些限制,即如果时间过去(参数)将来会出现奇怪的行为,并且它仅限于几天,几小时和几秒(几个月和几年没有处理,以便其他人可以; - )。

样本输出是:

  • 大约一秒钟之前
  • 8分34秒前
  • 一小时四分钟前
  • 一天前
  • 29天前
  • 一年零三个月前

,欢呼:D

编辑:现在支持数月和数年:P

答案 3 :(得分:12)

我建议您查看HumanTime

答案 4 :(得分:9)

避免遗留日期时间类

其他答案可能是正确的,但已过时。 Java中令人烦恼的旧日期时间类现在已经遗留下来,取而代之的是java.time类。

同样,现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

使用java.time

Instant

  

两次(自纪元以来的几秒钟内)

在java.time中,我们将UTC时间轴上的时刻表示为Instant。我将假设你的时代与java.time相同,这是1970年的第一个UTC时刻(1970-01-01T00:00:00Z)。请注意,Instant的分辨率为纳秒。便利工厂方法从整个秒开始构造Instant,如问题中所述。

Instant start = Instant.ofEpochSecond( … );
Instant stop = Instant.ofEpochSecond( … );

这里我们使用当前时刻,几分钟后。

Instant start = Instant.now() ;
Instant stop = start.plusSeconds( TimeUnit.MINUTES.toSeconds( 7L ) ) ;  // Seven minutes as a number of seconds.

Duration

在java.time中,未附加到时间轴的时间跨度以两种方式表示。对于数月 - 月,我们有Period。对于小时 - 分钟 - 秒,我们有Duration

Duration duration = Duration.between( start , stop );

半开

使用半开放方法计算经过的时间。在这种方法中,开头是包含,而结尾是独占。这种方法通常用于日期时间工作。我相信在您的代码库中始终如一地使用这种方法将有助于消除由于含糊不清而导致的错误和误解,并且可以减轻编程的认知负担。

ISO 8601

ISO 8601标准定义了许多用于将日期时间值表示为文本的实用格式。这些格式旨在避免歧义,易于通过机器解析,并且可以直观地跨文化阅读。

java.time类在解析和解析时默认使用这些格式。生成字符串。

在未附加到时间轴的时间段内PnYnMnDTnHnMnS P T PT1H30M标记开头,String outputStandard = duration.toString(); 分隔任何时段 - 几个月 - 天-minutes秒。所以一个半小时是getMonths

在我们使用七分钟的示例代码中:

getDays
  

PT7M

请参阅上面的示例standard defines a format

我建议尽可能坚持使用这些格式,当然在交换或序列化日期时间数据时,还要考虑在适当的用户界面中使用,并且可以训练用户使用它们。

我建议永远使用时钟小时格式(例如:01:30为一小时半),因为该格式与时间完全不一致。

检索零件以构建String

如果你必须拼出问题中所见的时间范围,你需要自己建立文本。

  • 对于code running live at IdeOne.com,请致电PeriodDurationtoHoursPart以检索每个部分。
  • 奇怪的是,toMinutesPart类在getYears中缺少此类getter,但在Java 8及更高版本中获得了Java 9toSecondsPart,{{1} },toNanosPartint days = duration.toDaysPart() ; int hours = duration.toHoursPart() ; int minutes = duration.toMinutesPart() ; int seconds = duration.toSecondsPart() ; int nanos = duration.toNanosPart() ; StringBuilder sb = new StringBuilder( 100 ); if( days != 0 ) { sb.append( days + " days" ) ; }; if( hours != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( hours + " hours" ) ; }; if( minutes != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( minutes + " minutes" ) ; }; if( seconds != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( seconds + " seconds" ) ; }; if( nanos != 0 ) { sb.append( ( sb.length = 0 ) ? ( "" ) : ( ", " ) ) ; // Append comma if any existing text. sb.append( nanos + " nanos" ) ; }; String output = sb.toString();

一些示例代码,简单和基本的入门。

to

或许您可以使用toDaysPart以Joda-Time在DateTimeFormatterBuilder中显示的方式使用the Answer by Balus C编写更流畅的代码。

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和&amp; SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuarter和fz more

答案 5 :(得分:1)

您可以使用 java.time.Duration,它以 ISO-8601 standards 为模型,并作为 JSR-310 implementation 的一部分与 Java-8 一起引入。 Java-9 引入了一些更方便的方法。

演示:

import java.time.Duration;
import java.time.Instant;

class Main {
    public static void main(String[] args) {
        // Test
        System.out.println(getFormattedDuration(1619575035, 1619810961));
    }

    public static String getFormattedDuration(long start, long end) {
        Instant startInstant = Instant.ofEpochSecond(start);
        Instant endInstant = Instant.ofEpochSecond(end);

        Duration duration = Duration.between(startInstant, endInstant);

        // Custom format
        // ####################################Java-8####################################
        return String.format("%d days, %d hours, %d minutes, %d seconds", duration.toDays(), duration.toHours() % 24,
                duration.toMinutes() % 60, duration.toSeconds() % 60);
        // ##############################################################################

        // ####################################Java-9####################################
        // return String.format("%d days, %d hours, %d minutes, %d seconds",
        // duration.toDaysPart(), duration.toHoursPart(),
        // duration.toMinutesPart(), duration.toSecondsPart());
        // ##############################################################################
    }
}

输出:

2 days, 17 hours, 32 minutes, 6 seconds

答案 6 :(得分:1)

我不是Java的专家,但是你可以做t1-t2 = t3(以秒为单位)然后除以60,会给你分钟,另外60分会给你秒。然后,只需要确定你需要多少分部。

希望它有所帮助。

答案 7 :(得分:1)

一些使用日期格式播放的代码...及时间之前。

SimpleDateFormat curFormater = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss"); 
    try {
        Date dateObj = curFormater.parse(pubDate);

        SimpleDateFormat postFormater = new SimpleDateFormat("MMMM dd, yyyy ss:mm:hh");              
        String newDateStr = postFormater.format(dateObj); 
        Log.v("THE NEW DATE IS",newDateStr);            
        long pubdateinmilis = dateObj.getTime();            
        pubDate = (String) DateUtils.getRelativeTimeSpanString(pubdateinmilis, System.currentTimeMillis(),DateUtils.HOUR_IN_MILLIS,DateUtils.FORMAT_ABBREV_RELATIVE);

    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } 

答案 8 :(得分:1)

如果您的时间跨越夏令时(夏令时)边界,您想报告天数吗?

例如,第二天的23:00到23:00总是一天,但可能是23,24或25小时,具体取决于您是否跨越日光节约转换。

如果您关心这一点,请确保将其考虑在您的选择中。

答案 9 :(得分:0)

好的,在短暂阅读API后,您似乎可以执行以下操作: -

  1. 创建一些表示开始时间和结束时间的ReadableInstants。
  2. 使用Hours.hoursBetween获取小时数
  3. 使用Minutes.minutesBetween获取分钟数
  4. 在分钟上使用mod 60来获得剩余的分钟数
  5. et voila!
  6. HTH

答案 10 :(得分:0)

“Abduliam Rehmanius”的解决方案似乎相当不错,或者你可以使用上面的一些库,或者你可以创建新的简单的东西,请参考这里的JS解决方案:http://www.datejs.com/。转换成Java语言并不困难: - )

希望我的链接对您有用!

答案 11 :(得分:0)

java中格式化时间跨度在ETL或DB pull中经常弹出。以下代码片段是我获取操作脉冲的方法。

    SimpleDateFormat ymdhms=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    DecimalFormat formatter = new DecimalFormat("#,###"); /* ("#,###.00"); gives you the decimal fractions */
    int counter=0;
    Date beginTime0=new Date();
    while (<some end condition>) {

        <some time consuming operation>

        /*// uncomment this section durring initial exploration of the time consumer
        if(counter>100000){
            System.out.println("debug exiting due to counter="+counter);
            System.exit(0);
        }
        */

        if(0==counter%1000){
            Date now = new Date();
            long diffInSeconds = (now.getTime() - beginTime0.getTime()) / 1000;
            long diff[] = new long[] { 0, 0, 0, 0 };
            diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds);                        /* sec   */ 
            diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; /* min   */ 
            diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; /* hours */ 
            diff[0] = (diffInSeconds = (diffInSeconds / 24));                                            /* days  */ 
            String delta = String.format(
                "%s%s%s%s"
                ,(diff[0]>0?String.format("%d day%s "    ,diff[0],(diff[0]!=1?"s":"")):"")
                ,(diff[1]>0?String.format("%2d hour%s "  ,diff[1],(diff[1]!=1?"s":" ")):"")
                ,(diff[2]>0?String.format("%2d minute%s ",diff[2],(diff[2]!=1?"s":" ")):"")
                ,           String.format("%2d second%s ",diff[3],(diff[3]!=1?"s":" "))
            );
            System.out.println(String.format(
                "%12s %s delta= %s"
                ,formatter.format(counter)
                ,ymdhms.format(new Date())
                ,delta
                )
            );
        }
        counter++;
    }

答案 12 :(得分:0)

我认为你正在寻找像 PrettyTime 这样的东西 PrettyTime是一个OpenSource时间格式库。完全可定制,它创建人类可读相对时间戳,如在Digg,Twitter和Facebook上看到的那样。它有30多种语言版本! 链接: https://github.com/ocpsoft/prettytime

也可以在Android中使用

例如

final class Patient: Object {

    dynamic var name: String = ""

    var parameters = List<Parameter>()

}

final class Parameter: Object {

    dynamic var name: String = ""

    dynamic var patient: Patient? {
        return LinkingObjects(fromType: Patient.self, property: "parameters").first
    }

}

答案 13 :(得分:0)

最近我得到了相同的要求,我需要一个基本方法来打印持续时间。我没有选择使用任何第三方库。所以我进一步完善了@mtim的想法。

public static void main(String[] args) throws IOException, ParseException {
        String since = "2017-02-24T04:44:05.601Z";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        TimeZone utc = TimeZone.getTimeZone("UTC");
        dateFormat.setTimeZone(utc);

        Date sinceDate = dateFormat.parse(since);
        Date now = new Date();
        long durationInSeconds  = TimeUnit.MILLISECONDS.toSeconds(now.getTime() - sinceDate.getTime());

        long SECONDS_IN_A_MINUTE = 60;
        long MINUTES_IN_AN_HOUR = 60;
        long HOURS_IN_A_DAY = 24;
        long DAYS_IN_A_MONTH = 30;
        long MONTHS_IN_A_YEAR = 12;

        long sec = (durationInSeconds >= SECONDS_IN_A_MINUTE) ? durationInSeconds % SECONDS_IN_A_MINUTE : durationInSeconds;
        long min = (durationInSeconds /= SECONDS_IN_A_MINUTE) >= MINUTES_IN_AN_HOUR ? durationInSeconds%MINUTES_IN_AN_HOUR : durationInSeconds;
        long hrs = (durationInSeconds /= MINUTES_IN_AN_HOUR) >= HOURS_IN_A_DAY ? durationInSeconds % HOURS_IN_A_DAY : durationInSeconds;
        long days = (durationInSeconds /= HOURS_IN_A_DAY) >= DAYS_IN_A_MONTH ? durationInSeconds % DAYS_IN_A_MONTH : durationInSeconds;
        long months = (durationInSeconds /=DAYS_IN_A_MONTH) >= MONTHS_IN_A_YEAR ? durationInSeconds % MONTHS_IN_A_YEAR : durationInSeconds;
        long years = (durationInSeconds /= MONTHS_IN_A_YEAR);

        String duration = getDuration(sec,min,hrs,days,months,years);
        System.out.println(duration);
    }
    private static String getDuration(long secs, long mins, long hrs, long days, long months, long years) {
        StringBuffer sb = new StringBuffer();
        String EMPTY_STRING = "";
        sb.append(years > 0 ? years + (years > 1 ? " years " : " year "): EMPTY_STRING);
        sb.append(months > 0 ? months + (months > 1 ? " months " : " month "): EMPTY_STRING);
        sb.append(days > 0 ? days + (days > 1 ? " days " : " day "): EMPTY_STRING);
        sb.append(hrs > 0 ? hrs + (hrs > 1 ? " hours " : " hour "): EMPTY_STRING);
        sb.append(mins > 0 ? mins + (mins > 1 ? " mins " : " min "): EMPTY_STRING);
        sb.append(secs > 0 ? secs + (secs > 1 ? " secs " : " secs "): EMPTY_STRING);
        sb.append("ago");
        return sb.toString();
    }

输出将是用例如单词打印的时间间隔(持续时间)。 1 day 2 hours 51 mins 41 secs ago

答案 14 :(得分:0)

Calendar类可以处理大多数与日期相关的数学。您必须获得compareTo的结果并自行输出格式。虽然可能有第三方库,但没有一个标准库可以完全满足您的要求。

答案 15 :(得分:0)

这个问题很老,已经有了答案,但我有一个更好的解决方案。 从date utils

使用relativeSpan
                dateHere.setText(DateUtils.getRelativeTimeSpanString(timeInMillis,
                        System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS));

它需要参数:long / int time,当前时间以及你想要的方式,_month,minute,second等

答案 16 :(得分:0)

我总是从Joda Time开始。在Java中使用日期和时间总是“有趣”,但Joda Time消除了压力。

他们有Interval和Duration类,它们只占你想要的一半。不确定它们是否具有以可读格式输出的功能。我会继续寻找。

HTH

答案 17 :(得分:-2)

long time1, time2;
time1 = System.currentMillis();

.. drink coffee

time2 = System.currentMillis();

long difference = time2 - time1 // millies between time1 and time2

java.util.Date differneceDate = new Date(difference);

要创建类似“2 Minutes”的字符串,您应该使用DateFormatter / DateFormat。您可以在Java API规范(java.sun.com)中找到有关此内容的更多详细信息。