如何计算 Java 中特定两个日期之间的星期日

How to count Sundays between specific two dates in Java

我正在努力计算每个月的第一个星期日的数量。 如输入 1(测试用例)1900 1 1 1902 1 1(年、月、日)输出应为 4。包括开始日期。

解释:

1 April 1900
1 July 1900
1 September 1901
1 December 1901

然而,当我尝试这个时:

6 4699 12 12 4710 1 1 1988 3 25 1989 7 13 1924 6 6 1925 6 16 1000000000000 2 2 1000000001000 3 2 1925 6 16 1924 6 6 1905 1 1 1905 1 1

输出应该是:18 2 2 1720 0 1

我的代码输出是:18 2 2 **1714** 0 1

在测试中输入6表示6个测试用例。 4699 年 12 月 12 日,即 4699 年 12 月 12 日; 4710结束日期等等。

你能帮我解决这个问题吗?

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Calendar;

public class nineteen {
    
    public static void main(String[] args) throws ParseException {
        SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
        Scanner sc = new Scanner(System.in);
        int loop = sc.nextInt();
        for (int i = 0; i < loop; i++) {
            long year = sc.nextLong(), month = sc.nextLong(), day = sc.nextLong(), yearto = sc.nextLong(),
                    monthto = sc.nextLong(), dayto = sc.nextLong();
            String day1 = String.valueOf(day);
            String month1 = String.valueOf(month);
            String year1 = String.valueOf(year);
            String dayt = String.valueOf(dayto);
            String montht = String.valueOf(monthto);
            String yeart = String.valueOf(yearto);
            String input_date = month1 + "/" + day1 + "/" + year1; // month day year
            String out_date = montht + "/" + dayt + "/" + yeart; // month day year
            long count = 0;

            Date d1 = formatter.parse(input_date);
            Date d2 = formatter.parse(out_date);
            count = saturdayscount(d1, d2);

            // TODO Auto-generated method stub

        }
        sc.close();
    }

    public static long saturdayscount(Date d1, Date d2) {
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);

        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);

        long sundays = 0;

        while (!c1.after(c2)) {

            if (c1.get(Calendar.DAY_OF_MONTH) != 1) {
                c1.add(Calendar.MONTH, 1);
                c1.set(Calendar.DAY_OF_MONTH, 1);

            }

            if (c1.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {

                sundays++;
            }

            c1.add(Calendar.MONTH, 1);
            c1.set(Calendar.DAY_OF_MONTH, 1);
        }

        System.out.println(sundays);
        return sundays;
    }

}

我建议你使用现代日期时间 API*

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        // Tests
        System.out.println("Count: " + countSundaysBetween("1900 1 1", "1902 1 1"));
        System.out.println();
        System.out.println("Count: " + countSundaysBetween("1000000000000 2 2", "1000000001000 3 2"));
    }

    static int countSundaysBetween(String strStartDate, String strEndDate) {
        DateTimeFormatter dtfInput = DateTimeFormatter.ofPattern("uuuu M d", Locale.ENGLISH);
        DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("d MMMM uuuu", Locale.ENGLISH);
        int count = 0;

        LocalDate start, end;

        System.out.printf("Processing start date: %s and end date: %s%n", strStartDate, strEndDate);

        try {
            start = LocalDate.parse(strStartDate, dtfInput);
        } catch (DateTimeParseException e) {
            System.out.printf("The start date %s can not be processed%n", strStartDate);
            return 0;
        }

        try {
            end = LocalDate.parse(strEndDate, dtfInput);
        } catch (DateTimeParseException e) {
            System.out.printf("The end date %s can not be processed%n", strEndDate);
            return 0;
        }

        for (LocalDate date = start.with(TemporalAdjusters.firstDayOfMonth()); !date.isAfter(end); date = date
                .plusMonths(1)) {
            if (date.getDayOfWeek() == DayOfWeek.SUNDAY) {
                System.out.println(date.format(dtfOutput));
                count++;
            }
        }

        return count;
    }
}

输出:

Processing start date: 1900 1 1 and end date: 1902 1 1
1 April 1900
1 July 1900
1 September 1901
1 December 1901
Count: 4

Processing start date: 1000000000000 2 2 and end date: 1000000001000 3 2
The start date 1000000000000 2 2 can not be processed
Count: 0

ONLINE DEMO

Trail: Date Time.

了解有关现代日期时间的更多信息 API

注:我用过LocalDate#parse to keep the demo clean and focussed on the main problem i.e. how to count Sundays between two given dates. If you need to create an instance of LocalDate using year, month and day-of-month as the input, you will use LocalDate#of。而且,正如您从文档中了解到的那样,在这种情况下,您需要将 DateTimeParseException 替换为 DateTimeException。解决方案的其余部分将保持不变。


* java.util 日期时间 API 及其格式 API、SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到 modern date-time API. For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and

您的程序失败,因为输入 1000000000000 超过 Java API 中的最大有效日期。使用时:

  • 弃用的 java.util.Date API 是年份 292278994
  • 新的java.timeAPI是999999999

因此您需要使用简单的 if-else 语句自行验证您的输入。或者让新的 api 处理它,就像

显示的答案一样

我会推荐使用新 java.time API 的解决方案。您可以计算您正在查看的日期是否是该月的第一天,如果是星期日,然后增加一个简单的日期计数器。

下面是一些示例代码:

class nineteen {
    // 6 4699 12 12 4710 1 1 1988 3 25 1989 7 13 1924 6 6 1925 6 16 2020 2 2 3020 3 2 1925 6 16 1924 6 6 1905 1 1 1905 1 1
    public static void main(String[] args) throws ParseException {
        Scanner sc = new Scanner(System.in);
        int loop = sc.nextInt();
        for (int i = 0; i < loop; i++) {
            long year = sc.nextLong();
            int month = sc.nextInt();
            int day = sc.nextInt();
            long yearto = sc.nextLong();
            int monthto = sc.nextInt();
            int dayto = sc.nextInt();
            LocalDate startDate = LocalDate.of(0, month, day).plusYears(year);
            LocalDate endDate = LocalDate.of(0, monthto, dayto).plusYears(yearto);
            LocalDate currentDay = startDate;
            int sundaysOnFirstDayOfMonth = 0;
            while (currentDay.isBefore(endDate) || currentDay.isEqual(endDate)) {
                if (currentDay.getDayOfMonth() == 1 && currentDay.getDayOfWeek().equals(DayOfWeek.SUNDAY)) {
                    sundaysOnFirstDayOfMonth++;
                }
                currentDay = currentDay.plusDays(1L);
            }
            System.out.println(sundaysOnFirstDayOfMonth);
        }
    }
}

请注意,我使用 2020 年到 3020 年之间的示例数据来获得您正在寻找的确切答案。其他千禧一代的统计可能不会产生这个确切的结果。尽管如此,there is a maximum year 你的测试数据超出了,所以这个 API 会做正确的事情并且 失败 到 运行 而不是默默地溢出。

解决方案:如何处理超出范围的日期

正如我在评论中所说,您的问题是 1000000000000(第 1 000 000 000 000 年,第 1 万亿年或经典英语中的第 10 亿年)超出了 Java 的日期范围图书馆。这与设计不佳且过时已久的 DateSimpleDateFormat 类 相结合,不会给您任何错误通知,只是默认给您不正确的数据。 Arvind Kumar Avinash 的回答表明 java.time,现代 Java 日期和时间 API, 确实 通知您错误。确切的范围在 Lino 的回答中。如果您需要处理这样的年份,请按以下方法处理。

由于标准库不为我们做这项工作,我们至少需要自己做一些工作,可以说是“手工”。您当然可以从头开始编写自己的日期代码,但没有人愿意这样做。而是观察:

  • 闰年以 400 年为周期。
  • 1900 年 1 月 1 日是星期一。 400 年后的 2300 年 1 月 1 日也将是星期一。这意味着星期几遵循相同的 400 年周期。

所以要处理 2300 年之后的年份,首先要计算整个 400 年周期中每个月的第一个星期日的数量。 java.time,现代 Java 日期和时间 API,可以做到这一点,如其他答案所示。现在从开始年份减去 400 的整数倍,得到 1900 到 2300 之间的年份。计算从这个修改后的开始日期到 2300 年 1 月 1 日的星期日。类似地从结束年份减去 400 的倍数。然后计算从 1900 年开始的星期日。将这两个数字相加并调整您从开始和结束年份中减去的 400 年的周期数。

请记住使用 longBigInteger 多年,因为 int 也无法容纳如此大的数字。

我假设年份总是 1900 年或更晚,我相信这也是最初的欧拉计划挑战的前提(link 下面)。当回到过去时,日历系统往往不一致,因此某个给定的星期日是否在一个月的 1 号不再明确定义。我不考虑这一点。

Link: Project Euler.net Counting Sundays Problem 19