【问题标题】:How to count Sundays between specific two dates in Java如何在 Java 中计算特定两个日期之间的星期日
【发布时间】:2021-03-11 13:26:55
【问题描述】:

我正在努力计算每月 1 日的星期日数。 就像输入 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;
    }

}

【问题讨论】:

  • 包括开始日期和结束日期吗?
  • 我不太确定(因为你没有回答我的问题),但我认为数字 1000000000000 和 1000000001000 应该是年。如果是这样,您将超出Date 支持的范围。这就是 Date 类的混乱程度,它不会提示您错误,它只是选择一些在其范围内的日期,并且与您想要的日期没有可识别的关系。
  • @OleV.V.这是测试输入 6 均值是测试用例 4699 年 12 月 12 天; 4710 结束日期等等。是的,包括开始日期。我找不到日期类的范围。能给个链接吗感谢您的贡献。
  • 你是对的,它没有直接记录。 Date 在内部表示为 long,表示与 1970-01-01T00:00:00Z (UTC) 的“纪元”之间的毫秒差。 new Date(Long.MAX_VALUE) 在我的电脑上打印 Sun Aug 17 08:12:55 CET 292278994,所以这是上限。下限是 new Date(Long.MIN_VALUE)Sun Dec 02 17:47:04 CET 292269055,其中年份是 BCE。

标签: java date date-format java-time


【解决方案1】:

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

正如我已经在评论中所说,您的问题是 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 日期和时间 API java.time 可以做到这一点。现在从开始年份减去 400 的整数倍,得到介于 1900 和 2300 之间的年份。从修改后的开始日期到 2300 年 1 月 1 日计算星期日。同样从结束年份减去 400 的倍数。然后从 1900 年开始计算星期天。将这两个数字相加并调整从开始年和结束年中减去的 400 年的周期数。

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

我假设年份总是 1900 年或更晚,因为我相信这也是最初的 Project Euler 挑战的前提(链接如下)。当时光倒流时,日历系统往往会不一致,因此给定的星期日是否在一个月的第一天不再是明确的定义。我不考虑这个。

链接: Project Euler.net Counting Sundays Problem 19

【讨论】:

    【解决方案2】:

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

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

    因此,您需要使用简单的if-else 语句自己验证您的输入。或者让新的api处理它,就像Arvind Kumar Avinash显示的答案一样

    【讨论】:

      【解决方案3】:

      我会推荐使用新的 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 会做正确的事情并且失败运行而不是静默溢出。

      【讨论】:

      • 我认为你可以直接使用currentDay = currentDay.plusMonths(1);,你只需要确保迭代前的第一天也是当月的1.
      【解决方案4】:

      我建议您使用现代日期时间 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 来保持演示的简洁性,并专注于主要问题,即如何计算两个给定日期之间的星期日。如果您需要使用 yearmonthday-of-month 作为输入来创建 LocalDate 的实例,您将使用 @ 987654324@。而且,您可以从文档中了解到,在这种情况下,您需要将 DateTimeParseException 替换为 DateTimeException。解决方案的其余部分将保持不变。


      * java.util 日期时间 API 及其格式化 API SimpleDateFormat 已过时且容易出错。建议完全停止使用它们并切换到modern date-time API。出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7。如果您正在为Android 项目和您的 Android API 级别仍然不兼容 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

      【讨论】:

      • 为了达到多年的正确结果,我们需要进行一些预先计算。不要忘记一些年份 Feb=29。
      • 捕获错误很好,但不足以处理大量数字。无法处理不想要的答案抱歉。
      • @Z_Z - 消息...can not be processed 仅用于记录目的。对于这种情况,我在函数中设置的行为是它返回0。顺便说一句,超出允许值的数字的预期行为是什么?
      猜你喜欢
      • 2013-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      • 1970-01-01
      • 2017-09-14
      • 2011-11-25
      相关资源
      最近更新 更多