8 minutes ago
8 hours ago
8 days ago
8 months ago
8 years ago
有没有在Java中简单的方法来做到这一点?
8 minutes ago
8 hours ago
8 days ago
8 months ago
8 years ago
有没有在Java中简单的方法来做到这一点?
import org.ocpsoft.prettytime.PrettyTime;
PrettyTime p = new PrettyTime();
System.out.println(p.format(new Date()));
// prints "moments ago"
您还可以传递区域设置以获取国际化信息:
PrettyTime p = new PrettyTime(new Locale("fr"));
System.out.println(p.format(new Date()));
// prints "à l'instant"
正如评论中所述,Android已经将此功能内置到android.text.format.DateUtils
类中。
你考虑过使用TimeUnit枚举吗?它对这种情况非常有用。
try {
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date past = format.parse("01/10/2010");
Date now = new Date();
System.out.println(TimeUnit.MILLISECONDS.toMillis(now.getTime() - past.getTime()) + " milliseconds ago");
System.out.println(TimeUnit.MILLISECONDS.toMinutes(now.getTime() - past.getTime()) + " minutes ago");
System.out.println(TimeUnit.MILLISECONDS.toHours(now.getTime() - past.getTime()) + " hours ago");
System.out.println(TimeUnit.MILLISECONDS.toDays(now.getTime() - past.getTime()) + " days ago");
}
catch (Exception j){
j.printStackTrace();
}
我参考了RealHowTo和Ben J的回答,并自己总结出了我的版本:
public class TimeAgo {
public static final List<Long> times = Arrays.asList(
TimeUnit.DAYS.toMillis(365),
TimeUnit.DAYS.toMillis(30),
TimeUnit.DAYS.toMillis(1),
TimeUnit.HOURS.toMillis(1),
TimeUnit.MINUTES.toMillis(1),
TimeUnit.SECONDS.toMillis(1) );
public static final List<String> timesString = Arrays.asList("year","month","day","hour","minute","second");
public static String toDuration(long duration) {
StringBuffer res = new StringBuffer();
for(int i=0;i< TimeAgo.times.size(); i++) {
Long current = TimeAgo.times.get(i);
long temp = duration/current;
if(temp>0) {
res.append(temp).append(" ").append( TimeAgo.timesString.get(i) ).append(temp != 1 ? "s" : "").append(" ago");
break;
}
}
if("".equals(res.toString()))
return "0 seconds ago";
else
return res.toString();
}
public static void main(String args[]) {
System.out.println(toDuration(123));
System.out.println(toDuration(1230));
System.out.println(toDuration(12300));
System.out.println(toDuration(123000));
System.out.println(toDuration(1230000));
System.out.println(toDuration(12300000));
System.out.println(toDuration(123000000));
System.out.println(toDuration(1230000000));
System.out.println(toDuration(12300000000L));
System.out.println(toDuration(123000000000L));
}}
将打印以下内容
0 second ago
1 second ago
12 seconds ago
2 minutes ago
20 minutes ago
3 hours ago
1 day ago
14 days ago
4 months ago
3 years ago
.append(temp != 1 ? "s" : "")
而不是.append(temp > 1 ? "s" : "")
,因为0也应该有s
后缀。 - berkustoDuration
方法是否将持续时间参数作为毫秒进行处理?如果是,那么传递1502531374441给我返回的是47年前的时间,但这是我的当前时间。 - Shajeel Afzal public class TimeUtils {
public final static long ONE_SECOND = 1000;
public final static long SECONDS = 60;
public final static long ONE_MINUTE = ONE_SECOND * 60;
public final static long MINUTES = 60;
public final static long ONE_HOUR = ONE_MINUTE * 60;
public final static long HOURS = 24;
public final static long ONE_DAY = ONE_HOUR * 24;
private TimeUtils() {
}
/**
* converts time (in milliseconds) to human-readable format
* "<w> days, <x> hours, <y> minutes and (z) seconds"
*/
public static String millisToLongDHMS(long duration) {
StringBuilder res = new StringBuilder();
long temp = 0;
if (duration >= ONE_SECOND) {
temp = duration / ONE_DAY;
if (temp > 0) {
duration -= temp * ONE_DAY;
res.append(temp).append(" day").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_HOUR;
if (temp > 0) {
duration -= temp * ONE_HOUR;
res.append(temp).append(" hour").append(temp > 1 ? "s" : "")
.append(duration >= ONE_MINUTE ? ", " : "");
}
temp = duration / ONE_MINUTE;
if (temp > 0) {
duration -= temp * ONE_MINUTE;
res.append(temp).append(" minute").append(temp > 1 ? "s" : "");
}
if (!res.toString().equals("") && duration >= ONE_SECOND) {
res.append(" and ");
}
temp = duration / ONE_SECOND;
if (temp > 0) {
res.append(temp).append(" second").append(temp > 1 ? "s" : "");
}
return res.toString();
} else {
return "0 second";
}
}
public static void main(String args[]) {
System.out.println(millisToLongDHMS(123));
System.out.println(millisToLongDHMS((5 * ONE_SECOND) + 123));
System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR));
System.out.println(millisToLongDHMS(ONE_DAY + 2 * ONE_SECOND));
System.out.println(millisToLongDHMS(ONE_DAY + ONE_HOUR + (2 * ONE_MINUTE)));
System.out.println(millisToLongDHMS((4 * ONE_DAY) + (3 * ONE_HOUR)
+ (2 * ONE_MINUTE) + ONE_SECOND));
System.out.println(millisToLongDHMS((5 * ONE_DAY) + (4 * ONE_HOUR)
+ ONE_MINUTE + (23 * ONE_SECOND) + 123));
System.out.println(millisToLongDHMS(42 * ONE_DAY));
/*
output :
0 second
5 seconds
1 day, 1 hour
1 day and 2 seconds
1 day, 1 hour, 2 minutes
4 days, 3 hours, 2 minutes and 1 second
5 days, 4 hours, 1 minute and 23 seconds
42 days
*/
}
}
这基于RealHowTo的答案,如果您喜欢它,请也关注一下他/她。
这个精简版本允许您指定您可能感兴趣的时间范围。
它还以略微不同的方式处理了“和”部分。我经常发现,在使用分隔符连接字符串时,跳过复杂逻辑并在结束时仅删除最后一个分隔符会更容易。
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class TimeUtils {
/**
* Converts time to a human readable format within the specified range
*
* @param duration the time in milliseconds to be converted
* @param max the highest time unit of interest
* @param min the lowest time unit of interest
*/
public static String formatMillis(long duration, TimeUnit max, TimeUnit min) {
StringBuilder res = new StringBuilder();
TimeUnit current = max;
while (duration > 0) {
long temp = current.convert(duration, MILLISECONDS);
if (temp > 0) {
duration -= current.toMillis(temp);
res.append(temp).append(" ").append(current.name().toLowerCase());
if (temp < 2) res.deleteCharAt(res.length() - 1);
res.append(", ");
}
if (current == min) break;
current = TimeUnit.values()[current.ordinal() - 1];
}
// clean up our formatting....
// we never got a hit, the time is lower than we care about
if (res.lastIndexOf(", ") < 0) return "0 " + min.name().toLowerCase();
// yank trailing ", "
res.deleteCharAt(res.length() - 2);
// convert last ", " to " and"
int i = res.lastIndexOf(", ");
if (i > 0) {
res.deleteCharAt(i);
res.insert(i, " and");
}
return res.toString();
}
}
这是一个小代码,让我们试试:
import static java.util.concurrent.TimeUnit.*;
public class Main {
public static void main(String args[]) {
long[] durations = new long[]{
123,
SECONDS.toMillis(5) + 123,
DAYS.toMillis(1) + HOURS.toMillis(1),
DAYS.toMillis(1) + SECONDS.toMillis(2),
DAYS.toMillis(1) + HOURS.toMillis(1) + MINUTES.toMillis(2),
DAYS.toMillis(4) + HOURS.toMillis(3) + MINUTES.toMillis(2) + SECONDS.toMillis(1),
DAYS.toMillis(5) + HOURS.toMillis(4) + MINUTES.toMillis(1) + SECONDS.toMillis(23) + 123,
DAYS.toMillis(42)
};
for (long duration : durations) {
System.out.println(TimeUtils.formatMillis(duration, DAYS, SECONDS));
}
System.out.println("\nAgain in only hours and minutes\n");
for (long duration : durations) {
System.out.println(TimeUtils.formatMillis(duration, HOURS, MINUTES));
}
}
}
下面将输出以下内容:
0 seconds
5 seconds
1 day and 1 hour
1 day and 2 seconds
1 day, 1 hour and 2 minutes
4 days, 3 hours, 2 minutes and 1 second
5 days, 4 hours, 1 minute and 23 seconds
42 days
Again in only hours and minutes
0 minutes
0 minutes
25 hours
24 hours
25 hours and 2 minutes
99 hours and 2 minutes
124 hours and 1 minute
1008 hours
如果有人需要,这里有一个类可以把类似上面的任何字符串转换为毫秒。 这对于允许人们以可读文本指定各种超时时间非常有用。
有一种简单的方法可以做到这一点:
假设你想要 20 分钟前的时间:
Long minutesAgo = new Long(20);
Date date = new Date();
Date dateIn_X_MinAgo = new Date (date.getTime() - minutesAgo*60*1000);
就这么多了...
关于内置解决方案:
Java没有任何内置的支持格式化相对时间的功能,即使是Java-8和其新的java.time
包也不支持。如果你只需要英语而不需要其他语言,那么手工制作的解决方案可能是可以接受的 - 参见@RealHowTo的答案(尽管它有一个强烈的缺点,即不考虑时区将瞬时差转换为本地时间单位!)。无论如何,如果您想避免为其他语言环境编写复杂的自定义解决方案,则需要使用外部库。
在这种情况下,我建议使用我的库Time4J(或Android上的Time4A)。它提供了最大的灵活性和最强大的国际化能力。类net.time4j.PrettyTime有七个方法printRelativeTime...(...)
用于此目的。以下是使用测试时钟作为时间源的示例:
TimeSource<?> clock = () -> PlainTimestamp.of(2015, 8, 1, 10, 24, 5).atUTC();
Moment moment = PlainTimestamp.of(2015, 8, 1, 17, 0).atUTC(); // our input
String durationInDays =
PrettyTime.of(Locale.GERMAN).withReferenceClock(clock).printRelative(
moment,
Timezone.of(EUROPE.BERLIN),
TimeUnit.DAYS); // controlling the precision
System.out.println(durationInDays); // heute (german word for today)
另一个使用 java.time.Instant
作为输入的例子:
String relativeTime =
PrettyTime.of(Locale.ENGLISH)
.printRelativeInStdTimezone(Moment.from(Instant.EPOCH));
System.out.println(relativeTime); // 45 years ago
java.time.Instant
或java.time.Period
),因此与Java-8兼容。
有什么缺点吗?只有两个。
(紧凑)替代品:
如果您寻找一个更小的解决方案,不需要太多功能,并且愿意容忍与i18n数据相关的可能的质量问题,则:
我建议使用 ocpsoft/PrettyTime(支持实际上 32 种语言(很快 34 种?)适用于仅使用 java.util.Date
的工作 - 请参见 @ataylor 的答案)。行业标准 CLDR(来自 Unicode 联盟)及其庞大的社区背景不幸地不是 i18n-data 的基础,因此进一步的数据增强或改进可能需要一段时间...
如果您在 Android 上,则可以使用辅助类 android.text.format.DateUtils 作为精简内置替代方案(请参见其他评论和答案,在年份和月份方面缺点是没有支持。我确信只有极少数人喜欢这个辅助类的 API 风格。
如果您是 Joda-Time 的粉丝,则可以查看其类 PeriodFormat(在发布 v2.9.4 中支持 14 种语言,另一方面:Joda-Time 当然也不紧凑,所以我在这里提到它只是为了完整性)。这个库不是一个真正的答案,因为根本不支持相对时间。您至少需要添加字面量“ago”(并手动剥离生成的列表格式中的所有较低单元 - 令人尴尬)。与 Time4J 或 Android-DateUtils 不同,它没有特殊的缩写支持或自动从相对时间切换到绝对时间表示。像 PrettyTime 一样,它完全依赖于 Java 社区的私人成员的未经确认的贡献来进行 i18n-data。
Habsq的回答的想法是正确的,但方法错误。
对于与年月日时间轴无关的时间跨度,请使用Period
。对于表示24小时时间块而不涉及日历和时分秒的天数,请使用Duration
。混合这两个尺度很少有任何意义。
Duration
Instant now = Instant.now(); // Capture the current moment as seen in UTC.
Instant then = now.minus( 8L , ChronoUnit.HOURS ).minus( 8L , ChronoUnit.MINUTES ).minus( 8L , ChronoUnit.SECONDS );
Duration d = Duration.between( then , now );
生成小时、分钟和秒的文本。
// Generate text by calling `to…Part` methods.
String output = d.toHoursPart() + " hours ago\n" + d.toMinutesPart() + " minutes ago\n" + d.toSecondsPart() + " seconds ago";
将内容输出到控制台。
System.out.println( "From: " + then + " to: " + now );
System.out.println( output );
从: 2019-06-04T11:53:55.714965Z 到: 2019-06-04T20:02:03.714965Z
8小时前
8分钟前
8秒钟前
周期
首先获取当前日期。
时区是确定日期的关键。对于任何给定的时刻,日期在全球范围内因时区而异。例如,法国巴黎午夜后几分钟就是新的一天,而在魁北克省蒙特利尔仍然是“昨天”。
如果没有指定时区,JVM会隐式地应用其当前默认时区。在运行时,该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/期望的时区明确指定为参数。如果关键,请与用户确认时区。大陆/地区
的格式指定正确的时区名称,例如America/Montreal
、Africa/Casablanca
或Pacific/Auckland
。永远不要使用2-4个字母的缩写,如EST
或IST
,因为它们不是真正的时区,没有标准化,甚至不是唯一的。ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
LocalDate then = today.minusYears( 8 ).minusMonths( 8 ).minusDays( 7 ); // Notice the 7 days, not 8, because of granularity of months.
计算经过的时间。
Period p = Period.between( then , today );
String output = p.getDays() + " days ago\n" + p.getMonths() + " months ago\n" + p.getYears() + " years ago";
将内容输出到控制台。
System.out.println( "From: " + then + " to: " + today );
System.out.println( output );
从2010年09月27日至2019年06月04日
8天前
8个月前
8年前
java.time框架内置于Java 8及更高版本中。这些类替代了麻烦的旧遗留日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
要了解更多信息,请参见Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范为JSR 310。
Joda-Time项目现已进入维护模式, 建议迁移到java.time类。
您可以直接与数据库交换java.time对象。 使用符合JDBC 4.2或更高版本的JDBC驱动程序即可。不需要字符串,也不需要java.sql.*
类。
如何获取java.time类?
ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是java.time可能未来添加的潜在试验场。您可能会在这里找到一些有用的类,例如Interval
, YearWeek
, YearQuarter
和更多。
使用内置于Java 8及其后续版本中的java.time框架。
LocalDateTime t1 = LocalDateTime.of(2015, 1, 1, 0, 0, 0);
LocalDateTime t2 = LocalDateTime.now();
Period period = Period.between(t1.toLocalDate(), t2.toLocalDate());
Duration duration = Duration.between(t1, t2);
System.out.println("First January 2015 is " + period.getYears() + " years ago");
System.out.println("First January 2015 is " + period.getMonths() + " months ago");
System.out.println("First January 2015 is " + period.getDays() + " days ago");
System.out.println("First January 2015 is " + duration.toHours() + " hours ago");
System.out.println("First January 2015 is " + duration.toMinutes() + " minutes ago");
Duration
方法报告了整个持续时间作为总小时数和总分钟数。在Java 8中,该类奇怪地缺乏获取每个小时、分钟和秒的部分的方法。Java 9引入了这些方法,to...Part
。 - Basil Bourque根据这里的许多答案,我为我的用例创建了以下内容。
示例用法:
String relativeDate = String.valueOf(
TimeUtils.getRelativeTime( 1000L * myTimeInMillis() ));
import java.util.Arrays;
import java.util.List;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* Utilities for dealing with dates and times
*/
public class TimeUtils {
public static final List<Long> times = Arrays.asList(
DAYS.toMillis(365),
DAYS.toMillis(30),
DAYS.toMillis(7),
DAYS.toMillis(1),
HOURS.toMillis(1),
MINUTES.toMillis(1),
SECONDS.toMillis(1)
);
public static final List<String> timesString = Arrays.asList(
"yr", "mo", "wk", "day", "hr", "min", "sec"
);
/**
* Get relative time ago for date
*
* NOTE:
* if (duration > WEEK_IN_MILLIS) getRelativeTimeSpanString prints the date.
*
* ALT:
* return getRelativeTimeSpanString(date, now, SECOND_IN_MILLIS, FORMAT_ABBREV_RELATIVE);
*
* @param date String.valueOf(TimeUtils.getRelativeTime(1000L * Date/Time in Millis)
* @return relative time
*/
public static CharSequence getRelativeTime(final long date) {
return toDuration( Math.abs(System.currentTimeMillis() - date) );
}
private static String toDuration(long duration) {
StringBuilder sb = new StringBuilder();
for(int i=0;i< times.size(); i++) {
Long current = times.get(i);
long temp = duration / current;
if (temp > 0) {
sb.append(temp)
.append(" ")
.append(timesString.get(i))
.append(temp > 1 ? "s" : "")
.append(" ago");
break;
}
}
return sb.toString().isEmpty() ? "now" : sb.toString();
}
}