有时候,需要计算一个过去时间在xxx年/月/周/天/时/分/秒之前。比如我们经常看到的文章评论时间、动态发表时间、代码提交时间等,如下
怎么实现呢?逻辑是这样的:
1、判断时间差是否超过1年,是的话直接计算xxx年前,否则往下走;
2、判断时间差是否超过1个月,是的话直接计算xxx月前,否则往下走;
3、判断时间差是否超过1个星期,是的话直接计算xxx周前,否则往下走;
4、判断时间差是否超过1天,是的话直接计算xxx天前,否则往下走;
5、判断时间差是否超过1小时,是的话直接计算xxx小时前,否则往下走;
6、判断时间差是否超过1分钟,是的话直接计算xxx分钟前,否则往下走;
7、判断时间差是否超过1秒,是的话直接计算xxx秒前,否则往下走;
8、返回“刚刚”或者其它毫秒等。
有了方案,顿时精神百倍准备撸码,没有if...else解决不了的哈哈哈。。。
不过,好的代码要经得起理解、修改和扩展。哪一天我突然不要星期了,你是不是要找到星期那部分代码注释掉?哪一天我需要增加半天,你是不是要加个if?我一个地方要分钟,一个地方又不需要,你咋办?
这里直接秀出责任链模式(之前写过详细介绍)的实现,就能解决以上问题。
1)统一定义这条链上处理者的责任,即接口(可优化点:定义返回执行顺序的方法)。
public interface IStrategy {
/**
* 把期间转换成具体的表示存放到结果里面。并由自己确定是否交给下一个处理者
*
* @param duration 两个时间的间隔
* @param result 结果包装对象
* @param strategyChain 责任链对象,用于内部控制请求的传递
*/
void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain);
}
结果包装对象定义如下
public class ObjectWrapper<V> {
private V value;
public ObjectWrapper() {}
public ObjectWrapper(V value) {
this.value = value;
}
public V getValue() {
return this.value;
}
public void setValue(V value) {
this.value = value;
}
}
2)定义责任链。由链上的处理者控制请求的传递。
public final class DurationStrategyChain {
/** 请求到达链上的位置 */
private int pos = 0;
/** 链上处理者的数量 */
private int n;
/** 链上处理者 */
private IStrategy[] strategies;
private DurationStrategyChain() {
}
/**
* 构建责任链对象
*
* @param strategies 处理者数组
* @return 责任链对象
*/
public static DurationStrategyChain build(IStrategy[] strategies) {
DurationStrategyChain instance = new DurationStrategyChain();
instance.strategies = Objects.requireNonNull(strategies, "DurationStrategyChain构造参数strategies不能为空!");
instance.n = strategies.length;
return instance;
}
/**
* 责任链执行
*
* @param duration 两个时间的间隔
* @param result 结果包装对象
*/
public void doCompute(Duration duration, ObjectWrapper<String> result) {
if (pos < n) {
strategies[pos++].compute(duration, result, this);
}
}
}
3)定义责任链的处理者(可优化点:可以把它们定义成单例)。
年
public class YearStrategy implements IStrategy {
private static final int DAY_OF_YEAR = 365;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long days = duration.toDays();
if (days >= DAY_OF_YEAR) {
long years = days / DAY_OF_YEAR;
result.setValue(years + "年前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
月
public class MonthStrategy implements IStrategy {
private static final int DAY_OF_MONTH = 30;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long days = duration.toDays();
if (days >= DAY_OF_MONTH) {
long months = days / DAY_OF_MONTH;
result.setValue(months + "月前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
周
public class WeekStrategy implements IStrategy {
private static final int DAY_OF_WEEK = 7;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long days = duration.toDays();
if (days >= DAY_OF_WEEK) {
long weeks = days / DAY_OF_WEEK;
result.setValue(weeks + "周前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
天
public class DayStrategy implements IStrategy {
private static final int HOUR_OF_DAY = 24;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long hours = duration.toHours();
if (hours >= HOUR_OF_DAY) {
long days = hours / HOUR_OF_DAY;
result.setValue(days + "天前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
时
public class HourStrategy implements IStrategy {
private static final int MINUTE_OF_HOUR = 60;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long minutes = duration.toMinutes();
if (minutes >= MINUTE_OF_HOUR) {
long hours = minutes / MINUTE_OF_HOUR;
result.setValue(hours + "小时前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
分
public class MinuteStrategy implements IStrategy {
private static final int SECOND_OF_MINUTE = 60;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long seconds = duration.getSeconds();
if (seconds >= SECOND_OF_MINUTE) {
long minutes = seconds / SECOND_OF_MINUTE;
result.setValue(minutes + "分钟前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
秒
public class SecondStrategy implements IStrategy {
private static final int MILLISECOND_OF_SECOND = 1000;
@Override
public void compute(Duration duration, ObjectWrapper<String> result, DurationStrategyChain strategyChain) {
long millis = duration.toMillis();
if (millis >= MILLISECOND_OF_SECOND) {
long seconds = millis / MILLISECOND_OF_SECOND;
result.setValue(seconds + "秒前");
} else {
strategyChain.doCompute(duration, result);
}
}
}
后面也可以再定义一个默认的,走到这里表示异常或者“刚刚”什么的。
4)测试走起
这里面7个处理者我们全部都用了。你也可以只选一部分,不过要保证顺序。
public class Test {
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
IStrategy[] strategies = {new YearStrategy(), new MonthStrategy(), new WeekStrategy(), new DayStrategy(), new HourStrategy(), new MinuteStrategy(), new SecondStrategy()};
DurationStrategyChain durationStrategyChain = DurationStrategyChain.build(strategies);
ObjectWrapper<String> result = new ObjectWrapper<>();
durationStrategyChain.doCompute(getDuration("2018-10-05 00:00:00", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 2年前
durationStrategyChain.doCompute(getDuration("2020-02-05 00:00:00", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 11月前
durationStrategyChain.doCompute(getDuration("2020-12-12 00:00:00", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 2周前
durationStrategyChain.doCompute(getDuration("2020-12-29 05:00:00", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 2天前
durationStrategyChain.doCompute(getDuration("2020-12-31 12:30:00", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 11小时前
durationStrategyChain.doCompute(getDuration("2020-12-31 23:15:02", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 44分钟前
durationStrategyChain.doCompute(getDuration("2020-12-31 23:59:10", "2021-01-01 00:00:00"), result);
System.out.println(result.getValue());// 50秒前
}
private static Duration getDuration(String beforeDateTime, String nowDateTime) {
return Duration.between(LocalDateTime.parse(beforeDateTime, DTF), LocalDateTime.parse(nowDateTime, DTF));
}
}
执行结果如下,也把结果注释到测试代码里面了