我正在努力计算每月 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(1 000 000000 000 年,一年一万亿或古典英语年一亿)超出了 Java 日期库的范围。这与设计不佳且长期过时的Date
和SimpleDateFormat
类相结合,没有给您任何错误通知,只是默认地为您提供不正确的数据。Arvind Kumar Avinash 的回答表明,java.time,现代Java日期和时间API,确实会通知您错误。确切的范围在利诺的答案中。如果您需要处理这些年,请按以下步骤操作。
由于标准库不能为我们完成这项工作,因此我们至少需要自己完成一些工作,可以说是"手动"。您当然可以从头开始编写自己的日期代码,但没有人愿意这样做。相反,观察:
- 闰年以400年为周期。
- 1900年1月1日是星期一。 400年后的1月1日,2300年也将是星期一。这意味着一周中的日子遵循相同的 400 年周期。
因此,要处理 2300 年之后的年份,首先计算整个 400 年周期中一个月第一天的星期日数量.java.time,现代 Java 日期和时间 API,可以做到这一点,如其他答案所示。现在从开始年份减去 400 的整数倍,得到 1900 到 2300 之间的年份。从此修改的开始日期到 2300 年 1 月 1 日计算星期日。同样,从结束年份中减去 400 的倍数。然后计算 1900 年的星期日。将这两个数字相加,并调整您从开始和结束年份中减去的 400 年的周期数。
请记住使用long
或BigInteger
多年,因为int
也不能容纳如此大的数字。
我假设年份总是1900年或更晚,因为我相信这也是原始欧拉计划挑战的前提(链接如下)。当回到过去时,日历系统往往不同意,因此给定的星期日是否落在一个月的 1 号不再明确定义。我对此不予考虑。
链接:项目 Euler.net 计数星期天问题 19
程序失败,因为输入1000000000000
超过了 Java API 中的最长有效日期。使用时:
- 已弃用的
java.util.Date
API 是 Year292278994
- 新的
java.time
API 是第999999999
年
因此,您需要使用简单的if
-else
语句自己验证您的输入。或者让新的 api 处理它,就像 Arvind Kumar Avinash 显示的答案一样
我建议您使用现代日期时间 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
在线演示
要了解有关现代日期时间 API 的更多信息,请参阅跟踪:日期时间。
注意:我使用LocalDate#parse
来保持演示干净,并专注于主要问题,即如何计算两个给定日期之间的星期日。如果需要使用年、月和日作为输入来创建LocalDate
实例,则将使用LocalDate#of
。而且,正如您从文档中了解到的那样,在这种情况下,您需要将DateTimeParseException
替换为DateTimeException
.解决方案的其余部分将保持不变。
*java.util
日期时间 API 及其格式化 APISimpleDateFormat
已过时且容易出错。建议完全停止使用它们并切换到现代日期时间 API。出于任何原因,如果你必须坚持使用 Java 6 或 Java 7,你可以使用ThreeTen-Backport,它将大部分java.time功能向后移植到 Java 6 和 7。如果您正在为 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请查看通过脱糖提供的 Java 8+ API 和如何在 Android Project 中使用 ThreeTenABP。
我会推荐一个带有新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 年之间的示例数据来获得您正在寻找的确切答案。 其他千禧一代的计数可能不会产生这个确切的结果。 尽管如此,您的测试数据有一个超过最长年份,因此此 API 将执行正确的操作并且无法运行,而不是静默溢出。