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

我想花两次(从纪元以来的秒数),以如下格式显示两者之间的差异:

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

我该怎么做?

Jeremy Logan asked 2020-08-01T13:23:35Z
17个解决方案
68 votes

由于所有人都喊“ YODA !!!” 但没有人举一个具体的例子,这是我的贡献。

您也可以使用Joda-Time进行此操作。 使用PeriodFormatterBuilder表示一个期间。 要将周期格式化为所需的人类表示形式,请使用PeriodFormatterBuilder,您可以通过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

(咳嗽,老咳嗽)

BalusC answered 2020-08-01T13:24:06Z
45 votes
    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" : ""));
Timour answered 2020-08-01T13:24:21Z
18 votes

是的,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秒前
  • 一个小时零4分钟前
  • 一天前
  • 29天前
  • 一年零三个月前

,欢呼声:D

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

Abduliam Rehmanius answered 2020-08-01T13:25:25Z
12 votes

我建议您看看HumanTime

Peter Lindholm answered 2020-08-01T13:25:45Z
9 votes

避免使用旧的日期时间类

其他答案可能是正确的,但已过时。 Java中麻烦的旧日期时间类现在已被遗留,由java.time类取代。

同样,现在处于维护模式的Joda-Time项目建议迁移到java.time类。

使用java.time

YearQuarter

两次(自纪元以来以秒为单位)

在java.time中,我们将UTC时间轴上的时刻表示为YearQuarter。我假设您的纪元与java.time相同,即UTC 1970年的第一时刻(toHoursPart)。 请注意,toMinutesPart的分辨率为纳秒。 如问题中所述,便利工厂方法从整秒中构造了toSecondsPart

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.

YearQuarter

在java.time中,以两种方式表示未附加到时间线上的时间跨度。 对于年月日,我们有YearQuarter。对于小时-分-秒,我们有toHoursPart

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

半开

用半开方法计算经过的时间。 在这种方法中,开始是包含在内的,而结尾是排斥的。 此方法通常用于日期时间工作。 我相信在您的代码库中一致地使用此方法将有助于消除由于歧义而引起的错误和误解,并减轻编程的认知负担。

ISO 8601

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

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

对于未附加到时间轴的时间范围,该标准定义了一种格式2992927567148729344,其中toHoursPart标记开始,而toMinutesPart将任何年-月-日与小时-分钟-秒分开。 因此,一个半小时是toSecondsPart

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

String outputStandard = duration.toString();

热情地

请参阅上面的示例代码,这些代码在IdeOne.com上实时运行。

我建议尽可能使用这些格式,当然在交换或序列化日期时间数据时一定要使用这些格式,但是也建议在适当的情况下考虑在用户界面中使用,并且可以训练您的用户使用它们。

我建议不要使用时钟小时格式(例如:半小时为01:30),因为该格式与一天中的时间完全模棱两可。

检索零件以构建字符串

如果您必须按照问题中的说明拼写时间跨度,则需要自己构建文本。

  • 对于YearQuarter,请致电YearQuarterYearQuartertoHoursPart检索每个零件。
  • 奇怪的是,YearQuarter类在Java 8中缺少此类吸气剂,但在Java 9及更高版本中获得了它们:YearQuartertoHoursParttoMinutesParttoSecondsParttoNanosPart

一些简单而基本的示例代码可以帮助您入门。

int 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();

或者,您可以使用YearQuarter以Balda C在“答案”中与Joda-Time一起显示的方式编写代码。


关于java.time

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

现在处于维护模式的Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程。 并在Stack Overflow中搜索许多示例和说明。 规格为JSR 310。

在哪里获取java.time类?

  • Java SE 8和SE 9及更高版本
    • 内置的
    • 标准Java API的一部分,具有捆绑的实现。
    • Java 9添加了一些次要功能和修复。
  • 从Java 6结束到7
    • 很多java.time功能都在ThreeTen-Backport中反向移植到Java 6和7。
  • 安卓系统
    • ThreeTenABP项目专门针对Android改编了ThreeTen-Backport(如上所述)。
    • 请参阅如何使用ThreeTenABP…。

ThreeTen-Extra项目使用其他类扩展了java.time。 该项目为将来可能在java.time中添加内容提供了一个试验场。 您可能会在这里找到一些有用的类,例如YearQuarterYearQuarterYearQuarter和fz more。

Basil Bourque answered 2020-08-01T13:28:59Z
1 votes

我不是Java方面的专家,但是您可以执行t1-t2 = t3(以秒为单位),然后将其除以60,得到分钟,再得到60则得到秒。 然后,只需弄清楚您需要多少个部门。

希望能帮助到你。

TuxMeister answered 2020-08-01T13:29:23Z
1 votes

如果您的时间跨过夏时制(夏令时),您是否要报告天数?

例如,第二天的23:00到23:00始终是一天,但可能是23、24或25小时,具体取决于您是否经过了夏时制转换。

如果您对此感到担心,请确保将其纳入选择范围。

Adrian Pronk answered 2020-08-01T13:29:52Z
1 votes

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

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();
    } 
OWADVL answered 2020-08-01T13:30:13Z
0 votes

我总是从Joda Time开始。 在Java中使用日期和时间总是很有趣,但是Joda Time减轻了压力。

他们有Interval和Duration类,它们只满足您所寻找的一半。 虽然不确定它们是否具有以可读格式输出的功能。 我会继续寻找。

高温超导

mr_urf answered 2020-08-01T13:30:42Z
0 votes

Calendar类可以处理大多数与日期相关的数学。 您将必须获取compareTo的结果并自己输出格式。 没有标准库可以完全满足您的需求,尽管可能有第3方库。

Ben S answered 2020-08-01T13:31:03Z
0 votes

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

  1. 创建一些表示开始时间和结束时间的ReadableInstant。
  2. 使用Hours.hoursBetween获取小时数
  3. 使用Minutes.minutesBetween获取分钟数
  4. 在分钟上使用mod 60以获取剩余的分钟
  5. 然后你去!

高温超导

mr_urf answered 2020-08-01T13:31:49Z
0 votes

“ Abduliam Rehmanius”的解决方案看起来非常不错,或者您可以使用上面的一些库,或者可以创建新的简单内容,请在此处参考JS解决方案:[http://www.datejs.com/。] 转换为Java lang :-)

希望我的链接对您有用!

Tam Vo answered 2020-08-01T13:32:13Z
0 votes

在Java中格式化时间跨度在ETL或数据库请求中经常弹出。 以下代码段是如何获得操作脉冲的。

    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++;
    }
teserecter answered 2020-08-01T13:32:33Z
0 votes

我想您正在寻找类似PrettyTime的产品
PrettyTime是一个开源时间格式库。 它完全可定制,可创建人类可读的相对时间戳,类似于Digg,Twitter和Facebook上显示的时间戳,并提供30多种语言版本!
链接:[https://github.com/ocpsoft/prettytime]

也可以在android中使用

例如

System.out.println(p.format(new Date(System.currentTimeMillis() + 1000*60*10)));
        //prints: "10 minutes from now"
Habel Philip answered 2020-08-01T13:33:11Z
0 votes

最近,当我需要一种基本方法来打印持续时间时,我得到了相同的要求。 我没有选择使用任何第三方库的选择。 所以我只是进一步完善了@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

i_am_zero answered 2020-08-01T13:33:35Z
0 votes

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

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

它需要参数:长时间/整数时间,当前时间以及您希望的方式,_month,minute,second等

Lucem answered 2020-08-01T13:34:01Z
-2 votes
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分钟”的字符串,应使用DateFormatter / DateFormat。 您可以在Java API规范(java.sun.com)中找到关于此的更多详细信息。

Timo answered 2020-08-01T13:34:21Z
translate from https://stackoverflow.com:/questions/635935/how-can-i-calculate-a-time-span-in-java-and-format-the-output