根据自定义时间单位计算天数

时间:2019-05-06 20:56:48

标签: java datetime java-8

我有一个带有number和timeUnit的类

import Prelude hiding (product)
import Data.Functor.Foldable

product :: ListF Int Int -> Int
product Nil = 1
product (Cons a b) = a * b

countDown :: Int -> ListF Int Int
countDown 0 = Nil
countDown n = Cons n (n - 1)

fact :: Int -> Int
fact = hylo product countDown

TimeUnit是一个枚举

public static class Time {
    private int number;
    private Unit unit;
}

所以时间的可能值是

public enum Unit {
    days,
    months,
    years;
}

我想减去今天起的时间,但现在确定如何实现。

这里是一个例子,

  

如果TimePeriod为30天,则resultDate = Instant.now()-30天。

     

如果TimePeriod为15个月,则resultDate = Instant.now()-450   天

     

如果TimePeriod为2年,则resultDate = Instant.now()-730天

4 个答案:

答案 0 :(得分:2)

您可以这样做:

ServiceBusTrigger

*)在出现数字溢出的情况下使用multiplyExact()失败。

测试

public enum TimeUnit {
    DAYS(1),
    MONTHS(30),
    YEARS(365);

    private final int days;

    private TimeUnit(int days) {
        this.days = days;
    }

    public <R extends Temporal> R addTo(R temporal, long amount) {
        return ChronoUnit.DAYS.addTo(temporal, Math.multiplyExact(amount, this.days));
    }
}

样本输出

System.out.println(Instant.now());
System.out.println(TimeUnit.DAYS.addTo(Instant.now(), -30));
System.out.println(TimeUnit.MONTHS.addTo(Instant.now(), -15));
System.out.println(TimeUnit.YEARS.addTo(Instant.now(), -2));

您的示例输出将2019-05-06T21:04:50.781231200Z 2019-04-06T21:04:50.781231200Z 2018-02-10T21:04:50.781231200Z 2017-05-06T21:04:50.781231200Z 替换为Instant

LocalDate

答案 1 :(得分:2)

向您的TimePeriod类添加方法

@AllArgsConstructor
@Builder(toBuilder = true)
@EqualsAndHashCode
@Getter
@NoArgsConstructor
public static class TimePeriod implements Serializable {
    private static final long serialVersionUID = 1L;
    @PositiveOrZero
    private int number;
    @NonNull
    private TimeUnit timeUnit;

    public Instant getSubstractedDate() {
        int totalDays = number;
        switch (timeUnit) {
            case MONTHS: totalDays*=30; break;
            case YEARS: totalDays*=365; break;
        }
        return Instant.now().minus(number, ChronoUnit.DAYS);
    }
}

使用java.time.LocalDate会更好,因为您可以使用LocalDate#minusDays方法。在我们的案例中,这可能也更有意义。对于Instant使用DAYS作为最小粒度并不是很理想。

答案 2 :(得分:1)

  

我意识到我需要考虑12个月而不是360天,而且   计算leap年。有什么想法吗?

首先,您需要为此确定一个时区。为什么?假设您在(UTC)2019-02-28T23:00:00Z处运行此代码,并且要减去1个月。现在是墨西哥城的2月28日,所以结果应该在1月28日。相比之下,在上海,已经是3月1日,所以结果应该在2月1日。相差3天(72小时)。 / p>

确定了时区后,最简单的方法就是使用ZonedDateTime。减去后,您始终可以转换为Instant,这也很简单。

例如:

    ZoneId zone = ZoneId.of("Asia/Ashkhabad");
    Map<Unit, ChronoUnit> units = Map.of(Unit.days, ChronoUnit.DAYS,
            Unit.months, ChronoUnit.MONTHS, Unit.years, ChronoUnit.YEARS);

    Time timeToSubtract = new Time(15, Unit.months);

    Instant result = ZonedDateTime.now(zone)
            .minus(timeToSubtract.getNumber(), units.get(timeToSubtract.getUnit()))
            .toInstant();
    System.out.println("15 months ago was " + result);

刚运行时(2019-05-08T07:06:45Z)我得到以下输出:

  

15个月前是2018-02-08T07:06:45.353746Z

我使用的ZonedDateTime.minus方法可以理解ChronoUnit,但不能理解您的Unit枚举,因此,既然您获得了后者,我们就需要翻译(否则您的枚举必须实施TemporalUnit,但这太过分了)。我正在使用地图进行翻译。我正在以Java 9方式初始化地图。如果您仍然使用Java 8,我相信您可以用其他方式来填充它。

在我的代码中,我假设您的Time类具有通常的构造函数和吸气剂。

ZonedDateTime还考虑到一天并非总是24小时。例如,在春季夏季时间(DST)开始并且时钟向前调时,一天只有23个小时。然后在秋天的一天中将时钟调回25小时。

答案 3 :(得分:0)

这就是我最终要做的。谢谢@Yasin Hajaj和@ OleV.V提供正确的答案。但是Olev V.V的答案更准确地解决了这个问题。

ChronoUnit添加到Unit枚举

public enum Unit {
    days(ChronoUnit.DAYS),
    months(ChronoUnit.MONTHS),
    years(ChronoUnit.YEARS);

    private final ChronoUnit chronoUnit;

    Unit(final ChronoUnit chronoUnit) {
        this.chronoUnit = chronoUnit;
    }

    public ChronoUnit toChronoUnit() {
        return chronoUnit;
    }
} 

还有计算日期的地方,我这样称呼

Time timeToSubtract = new Time(15, Unit.months);

Instant result = ZonedDateTime.now(ZoneId.of("America/New_York"))
        .minus(timeToSubtract.getNumber(), units.get(timeToSubtract.getUnit()))
        .toInstant();

System.out.println("15 months ago was " + result);

这将返回减去的时间段,其中包括the年和30/31天的月份。