1. 概述

在Java中进行时间和持续时间计算时,TimeUnit枚举提供了一种便捷的方式在不同时间单位间进行转换。

无论是将秒转换为分钟、毫秒转换为小时,还是执行其他任何时间单位转换,我们都可以使用TimeUnit来简化代码、获得准确结果并提升可读性。

本教程将探讨如何使用TimeUnit在Java中进行时间转换。

2. 理解TimeUnit

TimeUnitjava.util.concurrent包中的一个枚举,表示从纳秒到天的各种时间单位。它提供了一组预定义常量,每个常量对应特定的时间单位,包括:

  • DAYS
  • HOURS
  • MINUTES
  • SECONDS
  • MILLISECONDS
  • MICROSECONDS
  • NANOSECONDS

这些常量是时间转换的基础。

3. 使用TimeUnit转换时间

执行时间转换时,我们需要一个表示待转换持续时间的值,并指定目标单位。**TimeUnit提供了多种方法用于单位间转换**,如convert()toXXX()(其中XXX表示目标单位)。

3.1. 使用convert()方法

首先看convert(long sourceDuration, TimeUnit sourceUnit)方法,它将给定单位的时间持续时间转换为枚举值指定的单位。

将简单整数转换为分钟:

long minutes = TimeUnit.MINUTES.convert(60, TimeUnit.SECONDS);
assertThat(minutes).isEqualTo(1);

本例中,我们从60秒开始,将其转换为分钟。源时间单位作为第二个参数指定。输出单位始终由枚举值决定。

再看反向转换的例子:

long seconds = TimeUnit.SECONDS.convert(1, TimeUnit.MINUTES); 
assertThat(seconds).isEqualTo(60);

可见,**convert()方法可在不同时间单位间进行转换**。

3.2. 使用toXXX()方法

现在探索toXXX(long sourceDuration)方法。签名中的XXX指定目标单位。

可选择toNanos()toMicros()toMillis()toSeconds()toMinutes()toHours()toDays()

使用toXXX()方法重写之前的代码片段:

long minutes = TimeUnit.SECONDS.toMinutes(60);
assertThat(minutes).isEqualTo(1);

同样,我们将60秒转换为分钟。

反向转换:

long seconds = TimeUnit.MINUTES.toSeconds(1);
assertThat(seconds).isEqualTo(60);

结果与之前一致,两种签名等效。但与convert()不同,使用toXXX()时,枚举值代表源时间单位。

4. 特定用例

了解TimeUnit方法的转换原理后,我们深入探讨一些具体场景。

4.1. 负输入处理

首先检查转换方法是否支持负输入值:

long minutes = TimeUnit.SECONDS.toMinutes(-60);
assertThat(minutes).isEqualTo(-1);

示例表明,转换方法也能正确处理负输入,返回结果依然准确。

4.2. 舍入处理

当将较小单位转换为较大单位(预期结果为非整数)时会发生什么?所有方法仅声明返回long类型,因此无法返回小数结果。

通过秒转分钟测试舍入规则:

long positiveUnder = TimeUnit.SECONDS.toMinutes(59);
assertThat(positiveUnder).isEqualTo(0);
long positiveAbove = TimeUnit.SECONDS.toMinutes(61);
assertThat(positiveAbove).isEqualTo(1);

再测试负输入:

long negativeUnder = TimeUnit.SECONDS.toMinutes(-59);
assertThat(negativeUnder).isEqualTo(0);

long negativeAbove = TimeUnit.SECONDS.toMinutes(-61);
assertThat(negativeAbove).isEqualTo(-1);

所有转换均正常执行,无错误抛出。

注意:所有方法均实现向零舍入规则,直接截断小数部分

4.3. 溢出处理

基本类型都有其值限制。当结果超出限制时会发生什么?

检查将天的最大/最小long值转换为毫秒的结果:

long maxMillis = TimeUnit.DAYS.toMillis(Long.MAX_VALUE);
assertThat(maxMillis).isEqualTo(Long.MAX_VALUE);
long minMillis = TimeUnit.DAYS.toMillis(Long.MIN_VALUE);
assertThat(minMillis).isEqualTo(Long.MIN_VALUE);

代码执行无异常抛出。需注意:结果无效,因为溢出结果总是被截断为long原语定义的最小/最大值

5. 转换为最细粒度单位

有时需要将持续时间转换为TimeUnit中最细粒度的单位组合(如将秒转换为小时、分钟和剩余秒)。可惜没有现成方法,因为所有转换方法总是返回给定持续期内完整周期的总数

要转换为最细粒度单位,需自定义实现。以3672秒为例,期望得到1小时1分钟12秒的组合。

提取小时部分:

long inputSeconds = 3672;
long hours = TimeUnit.SECONDS.toHours(inputSeconds);
assertThat(hours).isEqualTo(1);

提取剩余分钟时,需从输入值中减去小时对应的秒数,再用差值计算:

long secondsRemainingAfterHours = inputSeconds - TimeUnit.HOURS.toSeconds(hours);
long minutes = TimeUnit.SECONDS.toMinutes(secondsRemainingAfterHours);
assertThat(minutes).isEqualTo(1);

成功计算出小时和分钟。最后提取剩余秒数:

long seconds = secondsRemainingAfterHours - TimeUnit.MINUTES.toSeconds(minutes);
assertThat(seconds).isEqualTo(12);

示例中,我们将3672毫秒转换为包含小时、分钟和秒的最细粒度表示。

6. 总结

本文探讨了使用Java的TimeUnit枚举进行时间转换的多种方式。

TimeUnit枚举通过convert()toXXX()方法,提供了便捷高效的不同单位转换机制

此外,它能正确处理负输入并返回准确结果。无论从小单位转大单位还是反向操作,均采用向零舍入规则。它还通过将结果截断为边界值,实现了基本的溢出保护。

若需将源持续时间转换为其他单位的组合(如天、小时、分钟和秒),可轻松实现附加逻辑。所有转换器均返回指定持续期内指定周期的总数。

如往常一样,示例源代码可在GitHub获取。


原始标题:Time Conversions Using TimeUnit