日期计算没有那么难——java简易实现日期跨度计算

日期计算没有那么难——java简易实现日期跨度计算

小序

在java中,我们计算日期跨度通常可以使用各种工具包,比如

java.util.Calendar
java.time.LocalDate

有时候会看看它的实现,发现还蛮复杂的,

	@Override
    public long toEpochDay() {
        long y = year;
        long m = month;
        long total = 0;
        total += 365 * y;
        if (y >= 0) {
            total += (y + 3) / 4 - (y + 99) / 100 + (y + 399) / 400;
        } else {
            total -= y / -4 - y / -100 + y / -400;
        }
        total += ((367 * m - 362) / 12);
        total += day - 1;
        if (m > 2) {
            total--;
            if (isLeapYear() == false) {
                total--;
            }
        }
        return total - DAYS_0000_TO_1970;
    }

老外写的,源码 LocalDate:line:1828,这计算看得有点头疼,于是就想,咱能不能自己写个简单的算法,来实现日期跨度的计算。

定理 & 依据

关于日期的计算,最关键以及最复杂的莫过于闰年:

年份为4的倍数,且不是100的倍数,是闰年。
年份为400的倍数,是闰年。

其他月份则亘古不变(实际历史上有过修正,此文省略不计),1至12月天数分别为 31, 28(29), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31。

简单实现

根据基本定理,直接上代码:

	// 平年各月天数。只包含前11个月,最后一个月用不到
    private static final int[] COMMON_MONTH_DAY = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30}; 

	/**
     * 获取标准日
     * @param year 年份(正整数)
     * @param month  月份(0~11)
     * @param day  日(月的实际日期,比如28号)
     * @return
     */
    private int getEpoDays(int year, int month, int day) {
        // 正常天数减去闰年天数:逢4、400加1天
        int total = (year - 1) * 365;
        total += ((year - 1) / 4);
        total -= (year - 1) / 100;
        total += (year - 1) / 400;
        for (int i = 0; i < COMMON_MONTH_DAY.length; i++) {
            if (i < month) {
                total += COMMON_MONTH_DAY[i];
            } else {
                break;
            }
        }
        total += day;
        if (month > Calendar.FEBRUARY && isBigYear(year)) {
            total++;
        }
        return total;
    }

	// 是否闰年
	private boolean isBigYear(int year) {
        if (year % 4 == 0 && year % 100 != 0) {
            return true;
        }
        if (year % 400 == 0) {
            return true;
        }
        return false;
    }

year-1

因为最后一年手动计算[滑稽脸],这几个包含 year-1 的公式只计算最后一年之前的天数。

+=、-=、+=

第一个“+=”是假设所有4的倍数的都是闰年;
第二个"-="是减去100倍数的年份多的那一天,因为100必是4的倍数:100=4*25【因式分解】;
第三个“+=”是加上上一步误减的400倍数,因为400必是100的倍数。
至此,整年计算结束。

i < month

最后计算当前年份的天数,这就比较好办了,只需要先按平年把已经过去的各月天数相加,最后判断是否闰年相机加一天就可以了。

检查 & 测试

使用 LocalDate#until 方法作为对照,来检查我们自定义的计算是否有误:

	@Test
    public void calcDays() {
        int fromYear = 2000;
        int fromMonth = 0; // 注意,Calendar中month起始为0
        int fromDay = 1;
        int toYear = 2000;
        int toMonth = 6;
        int toDay = 7;
        // LocalDate.until, 在中国的月份是从1开始的
        LocalDate from = LocalDate.of(fromYear, fromMonth + 1, fromDay);
        LocalDate to = LocalDate.of(toYear, toMonth + 1, toDay);
        long sampleDays = from.until(to, ChronoUnit.DAYS);
        System.out.println("sampleDays:" + sampleDays);
        int calcDays = getEpoDays(toYear, toMonth, toDay) - getEpoDays(fromYear, fromMonth, fromDay);
        System.out.println("calcDays:" + calcDays);
        System.out.println("equal:" + (sampleDays == calcDays));
    }

随机改变日期,多次测试后证明我们的计算没有问题。


后续各位大佬如果发现任何代码问题,欢迎留言或者私信,谢谢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值