java - 我试图手动计算两个日期之间的天数差异

标签 java

我使用这两个网站来衡量我的代码的接近程度。

我使用零年以来的天数来标准化两个输入的日期,然后找到这些日期之间的差异。

import java.util.Scanner;

public class DateDiff {
    private static final int[] monthsDay = {31,28,31,30,31,30,31,31,30,31,30,31};

    public static String dateChecker() {
        boolean b = true;
        int dateC = 0;
        String date = "";

        do {
            Scanner scanner = new Scanner(System.in);
            date = scanner.nextLine();
            try {
                if (date.charAt(2) == '/' && date.charAt(5) == '/') {
                    date = date.replace("/", "");
                    dateC = Integer.parseInt(date);
                    b = false;
                } else {
                    System.out.println("Reenter date in the dd/mm/yyyy format");
                }
            } catch (Exception e) {
                System.out.println("Reenter date in the dd/mm/yyyy format");
            }

        } while (b);

        return date;
    }

    public static int daysForMonth(int months, int year) {

        int days = 0;

        for (int i = 0; i < months; i++)
            if (i == 1)
                days += ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)
                        ? monthsDay[i] + 1
                        : monthsDay[i];
            else
                days += monthsDay[i];

        return days;
    }

    public static int daysForYears(int year) {

        int days = 0;

        for (int i = 0; i < year; i++)
            if ((i % 4 == 0 && (i % 100 != 0)) || (i % 400 == 0))
                days += 366;
            else
                days += 365;

        return days;
    }

    public static int daysSinceYearZero(String date) {
        int day = Integer.parseInt(date.substring(0,2));
        int month = Integer.parseInt(date.substring(2,4));
        int year = Integer.parseInt(date.substring(4,8));

        int daysMonth = daysForMonth(month-1, year);
        int daysYear = daysForYears(year);

        return day + daysMonth + daysYear;
    }

    public static void main(String[] args) {
        System.out.println("Enter first date");
        String date1 = dateChecker();
        System.out.println("Enter second date");
        String date2 = dateChecker();

        int firstDate = daysSinceYearZero(date1);
        int secondDate = daysSinceYearZero(date2);

        System.out.println("First Date days since Year Zero: " + firstDate);
        System.out.println("Second Date days since Year Zero: " + secondDate);
        System.out.println("Difference: " + Math.abs(firstDate-secondDate));

    }
}

我的代码很接近,但似乎总是会错过几天,我不明白为什么。我已经确认 days 和 daysMonth 是正确的,但不明白使用年份(daysYear 变量)计算自零年以来的天数时出错的地方

编辑:不允许使用任何库。不过,扫描仪很好,因为它仅用于用户输入。

最佳答案

由于您没有解释任何要求或限制,您可以这样做。

LocalDate earliest = LocalDate.parse("2012-05-17");
LocalDate latest = LocalDate.parse("2022-06-22");
System.out.println(latest.toEpochDay()-earliest.toEpochDay());

打印

3688 (exclusive of the latest date day)

但是,这是一种在家种植它的方法。我使用 lambda 来简化该过程。并且计算中不需要循环。所以这会以恒定的时间运行。

  • 首先,我创建了一个 IntTrinaryOperator 接口(interface)。
interface IntTrinaryOperator {
    public int applyAsInt(int a, int b, int c);
}

然后创建了一个月日数组(闰年稍后处理),第一个单元格将被忽略,但以下操作是必需的。

int daysPerMonth[] =
        { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int monthSums[] = daysPerMonth.clone();
// this creates a running sum
// looks like [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
// the last cell is not used. 
Arrays.parallelPrefix(monthSums, (a, b) -> a + b);

闰年函数

Function<Integer, Boolean> isLeapYear =
        a -> a % 400 == 0 || (a % 100 != 0 && a % 4 == 0);

以及定义的用于实际计算的 Trinary。

  • (y-1)*365-(y-1)/100+(y-1)/4 +(y-1)/400 - 计算从上一年开始的总闰年。
    • 每年 365 天的第一个总天数
    • 然后减去世纪年
    • 然后添加可被 400 整除的年份。
  • monthSums[m-1]+d - 添加今年的天数
  • ((m > 2) && isLeapYear.apply(y) ? 1 : 0) - 1 - 如果在 二月 之后,则再添加一天,但减去 1 排除当前日期(如 Java 中的大多数范围)
IntTrinaryOperator daysToEpoch = (y, m, d) -> (y - 1) * 365
        - (y - 1) / 100 + (y - 1) / 4 + (y - 1) / 400 +
        + monthSums[m - 1] + d
        + ((m > 2) && isLeapYear.apply(y) ? 1 : 0) - 1;

测试

  • 生成一些日期。日期不是按时间顺序排列的,因此天数可能为负数,因此 Math.abs()
Random r = new Random();

for (int i = 0; i < 10; i++) {
    int eYear = r.nextInt(2022) + 1;
    int eMonth = r.nextInt(12) + 1;
    int eDay = r.nextInt(daysPerMonth[eMonth])
            + (eMonth == 2 && isLeapYear.apply(eYear) ? 1 :
                    0);
    
    int sYear = r.nextInt(2022) + 1;
    int sMonth = r.nextInt(12) + 1;
    int sDay = r.nextInt(daysPerMonth[sMonth])
            + (sMonth == 2 && isLeapYear.apply(sYear) ? 1 :
                    0);
    
    int eDaysToEpoch =
            daysToEpoch.applyAsInt(eYear, eMonth, eDay);
    int sDaysToEpoch =
            daysToEpoch.applyAsInt(sYear, sMonth, sDay);
    
    System.out.printf("%02d/%02d/%04d - %02d/%02d/%04d - %,9d total days%n",
            eMonth, eDay, eYear, sMonth, sDay, sYear, Math.abs(eDaysToEpoch-sDaysToEpoch));
}

以及原始日期

System.out.println(daysToEpoch.applyAsInt(2022, 6, 22)- 
                   daysToEpoch.applyAsInt(2012, 5, 17));

打印类似的内容。

04/10/1377 - 12/03/1486 -    40,048 total days
02/12/0727 - 03/27/0196 -   193,899 total days
11/26/0457 - 12/09/0307 -    54,775 total days
02/25/0691 - 10/23/1596 -   330,785 total days
03/28/0404 - 01/16/1567 -   424,705 total days
10/18/0372 - 01/15/1316 -   344,512 total days
08/01/1374 - 01/23/1484 -    39,986 total days
03/21/0622 - 07/24/0495 -    46,260 total days
02/05/1167 - 08/05/1558 -   142,991 total days
12/02/1824 - 07/21/0976 -   309,859 total days
3688

这已经使用上面首先显示的 API 方法进行了测试。经过超过 100 万次随机测试,没有发现任何差异。

这是一个日期验证方法。它检查闰年和闰日与月份。它还允许使用个位数表示月份和日期。它不会产生详细的错误消息。我继续重新提示,直到输入有效日期。否则,日、月、年将以数组形式返回。

public static int[] getDate(Scanner scanner) {
    String stringDate = "\\d\\d?/\\d\\d?/\\d{4}";
    
    while (true) {
        System.out.println(
                "Please enter date in dd/mm/yyyy format.");
        String date = scanner.nextLine();
        if (date.matches(stringDate)) {
            int[] dmy = Arrays.stream(date.split("/"))
                    .mapToInt(Integer::parseInt).toArray();
            System.out.println(Arrays.toString(dmy));
            int d = dmy[0];
            int m = dmy[1];
            int y = dmy[2];
            if (d > 0 && m > 0 && m < 13 && y > 0) {
                boolean isLeap = isLeapYear.apply(y);
                if (isLeap && d <= 29 && m == 2) {
                    return dmy;
                }
                if (d <= daysPerMonth[m]) {
                    return dmy;
                }
            }
        }
        System.out.print("Illegal date: ");
    }
}

关于java - 我试图手动计算两个日期之间的天数差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71717253/

相关文章:

Java算法

java - 使用 drawString() java 时会跳过 Color.White

java - 通过PHP脚本获取minecraft uuid,无需API

java - Apache Pulsar - 主题授权失败 - 无权管理此租户上的资源

java - 我正在尝试在单个服务器和多个客户端中使用套接字编程开发聊天应用程序

java - Neo4J 遍历跳过关系类型

java - 在 postman 帖子请求中发送 map

java - Hadoop错误,找不到类

java.lang.NullPointerException 在 org.apache.jsp.init_jsp.jspInit(init_jsp.java :39)

java - 当我用鼠标点击按钮时如何创建 JLabel