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

我想要两次(以秒为单位),并显示两者之间的区别如下:

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

我怎样才能做到这一点?

既然大家都喊“YOODAA !!!” 但没有人发表具体的例子,这是我的贡献。

你也可以用Joda-Time来做到这一点。 使用Period来表示一段时间。 要在期望的人工表示中设置句点格式,可以使用PeriodFormatter ,您可以使用PeriodFormatter构build的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年,1个月,1周,5天,6小时,56分钟,24秒前

(咳嗽,老,咳嗽)

  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" : "")); 

Yup唤醒了我已经死了,但这是我的改进基于@mtim发布代码的实现,因为这个线程几乎在search的顶部,所以我正在打乱沉睡的空洞,

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

这显然是可以改进的。 基本上它试图让时间跨度更友好,虽然有一些限制,即如果时间过去(参数)在将来会有奇怪的performance,而且它的时间有限,只有几天,几小时和几秒处理,让别人可以;-)。

样本输出是:

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

,欢呼声:D

编辑:现在支持几个月和几年:P

我build议你看看HumanTime

避免遗留的date时间类

其他答案可能是正确的,但已经过时了。 Java中令人讨厌的旧date时间类现在是遗留的,被java.time类取代。

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

使用java.time

Instant

两次(以秒为单位)

在java.time中,我们用UTC表示UTC时间轴上的时刻。 我将假设你的时代与1970年代的第一时间( 1970-01-01T00:00:00Z )的java.time是一样的。 请注意, Instant有一个毫微秒的分辨率。 问题中提到,一个便捷工厂方法可以在整秒内构build一个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 ); 

半开

所用时间以半开式方法计算。 在这种方法中,开始是包容性的,而结尾是排他性的 。 这种方法通常用于date时间的工作。 我相信,在整个代码库中一致地使用这种方法将有助于消除由于含糊不清造成的错误和误解,并且会减轻编程的认知负担。

ISO 8601

ISO 8601标准定义了许多用于将date时间值表示为文本的实际格式。 这些格式旨在避免模糊不清,易于被机器分析,并能让人类跨文化阅读。

在parsing和生成string时,java.time类默认使用这些格式。

标准定义了一个 PnYnMnDTnHnMnS 格式 ,其中P表示开始, T从任何小时 – 分钟秒开始的任意年 – 月 – 日。 所以一个半小时是PT1H30M

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

 String outputStandard = duration.toString(); 

PT7M

请参阅IdeOne.com上运行的上述示例代码 。

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

我build议不要使用时钟格式(例如:01:30一个半小时),因为这种格式与时间的关系是完全不明确的。

检索零件以构buildstring

如果您必须拼出问题所示的时间跨度,则需要自行构build文本。

  • 对于Period ,调用getYearsgetMonthsgetDays来检索每个部分。
  • 奇怪的是, toDaysPart类在Java 8中缺less这样的getter,但是在Java 9和更高版本中获得了这些getter: toDaysParttoHoursParttoMinutesParttoSecondsParttoNanosPart

一些示例代码,简单和基本的让你开始。

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

或者,也许你可以使用DateTimeFormatterBuilder以Joda-Time所显示的方式在Balus C的答案中写出更清晰的代码 。


关于java.time

java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的遗留date时间类,如java.util.DateCalendarSimpleDateFormat

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

要了解更多信息,请参阅Oracle教程 。 并search堆栈溢出了很多例子和解释。 规范是JSR 310 。

从何处获取java.time类?

  • Java SE 8SE 9及更高版本
    • 内置。
    • 带有捆绑实现的标准Java API的一部分。
    • Java 9增加了一些小function和修复。
  • Java SE 6SE 7
    • 大部分的java.timefunction都被移植到了ThreeTen-Backport中的 Java 6&7中。
  • Android的
    • ThreeTenABP项目专门针对Android,采用了ThreeTen-Backport (上文提到)。
    • 请参阅如何使用ThreeTenABP …。

ThreeTen-Extra项目将java.time扩展到其他类。 这个项目是未来可能增加java.time的一个试验场。 您可能会在这里find一些有用的课程,例如IntervalYearWeekYearQuarter和ff。

我不是Java的专家,但你可以做t1-t2 = t3(以秒为单位),然后除以60,会给你几分钟,另外60分钟会给你秒。 那么只需要弄清楚你需要多less个部门。

希望它有帮助。

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

例如,第二天的23:00到23:00总是一天,但是可能是23,24或25小时,这取决于你是否过渡了日光节约。

如果你在意这一点,确保你将其纳入你的select。

一些与date格式和时间播放的代码。

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

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

他们有间隔和持续时间的类别,你所要找的一半。 不知道他们是否有可读格式的输出function。 我会继续寻找。

HTH

Calendar类可以处理大部分date相关的math。 你将不得不得到compareTo的结果,并自己输出格式。 没有一个标准的图书馆正在做你正在寻找什么,虽然可能有一个第三方库。

好了,在简短地了解API之后,看起来你可以做到以下几点:

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

HTH

“Abduliam Rehmanius”的解决scheme似乎很不错,或者你可以使用上面的库,或者你可以创build新的简单的东西,请参考JS解决scheme在这里: http : //www.datejs.com/ 。 转换成Java lang并不困难:-)

希望我的链接对你有用!

Java中的格式化时间跨度在我经常用ETL或DB拉取。 下面的代码片段是我如何获得操作的脉搏。

  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++; } 

我想你正在寻找像PrettyTime这样的东西
PrettyTime是一个开源的时间格式库。 完全可定制的,它创build了像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" 

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

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

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

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

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

要创build一个像“2分钟”的string,你应该使用DateFormatter / DateFormat。 您可以在Java API规范(java.sun.com)中find更多详细信息。