I'm doing a project that consists on: 'Write a program that prompts for a date (month, day, year) and reports the day of the week for that date. It might be
helpful to know that January 1, 1601 was a Monday.'. This is an exercise of 'Building Java Programs - A Back to Basics Approach, 2nd Edition', a book which I bought to teach myself Java. Any feedback is highly appreciated, but I do ask that you explain why you would do something another/a certain way. Thanks!
So, my problem is that while for the dates nearer to 1600's it's giving the correct day (I believe), the same is not true for more recent days, with them having an offset of three days (at least the ones I checked). Why does this happen and how do I fix it? Thanks!
My code:
// finds the day of the week of the given date
public static String dayFinder(int month, int day, int year) {
// handle invalid input
if (month > 12 || month < 1 || day > 31 || day < 1) {
throw new IllegalArgumentException("Month must be between "
+ "1 and 12 and Day must be between 1 and 31.");
}
// convert to "absolute" day, covering day and month
int absoluteDay = monthToDay(month, day, year);
// convert year to days and add to "absolute" day
absoluteDay += yearToDay(year);
if (absoluteDay % 7 == 1) {
return "Monday";
} else if (absoluteDay % 7 == 2) {
return "Tuesday";
} else if (absoluteDay % 7 == 3) {
return "Wednesday";
} else if (absoluteDay % 7 == 4) {
return "Thursday";
} else if (absoluteDay % 7 == 5) {
return "Friday";
} else if (absoluteDay % 7 == 6) {
return "Saturday";
} else { // absoluteDay % 7 == 0
return "Sunday";
}
}
// calculates the number of days present in a given
// date since the beginning of the year
public static int monthToDay(int month, int day, int year) {
// convert to "absolute" day
int absoluteDay = 0, daysTo31 = 0;
// iterate through months
for (int i = 0, loopMonth = month; i < month; i++) {
if (loopMonth == 1 || loopMonth == 3 || loopMonth == 5
|| loopMonth == 7 || loopMonth == 8 || loopMonth == 10
|| loopMonth == 12) {
absoluteDay += 31;
daysTo31 = 0;
} else if (loopMonth == 2) {
if (year % 4 != 0) {
absoluteDay += 28;
daysTo31 = 3;
} else { // leap year
absoluteDay += 29;
daysTo31 = 2;
}
} else { // month = 4, 6, 9 or 10
absoluteDay += 30;
daysTo31 = 1;
}
loopMonth--;
}
// adjust to specific day
absoluteDay -= (31 - day - daysTo31);
return absoluteDay;
}
// calculates the number of days between
// (the beginning of) the given year and
// (the beginning of) the reference year 1601
public static int yearToDay(int year) {
// convert to "absolute" day
int absoluteDay = 0;
year -= 1601;
// iterate through years
for (int i = 0, loopYear = year; i < year; i++) {
if (loopYear % 4 != 0) {
absoluteDay += 365;
} else { // leap year
absoluteDay += 366;
}
loopYear--;
}
return absoluteDay;
}
// Year 1604 (MDCIV) was a leap year starting on Thursday
Your problem is probably connected with the leap years.
Due to Wikipedia:
Every year that is exactly divisible by four is a leap year, except for years that are exactly divisible by 100; the centurial years that are exactly divisible by 400 are still leap years. For example, the year 1900 is not a leap year; the year 2000 is a leap year. [link]
It's the reason why you have three day too much (1700, 1800, 1900).
Java.util date/calendar functionality is fraught with craziness. I think Java7 has improved it a bit (I've heard, but not investigated), but I recommend a 3rd party library Joda-Time. To do what you're trying to do with Joda would be:
DateTime anyDateTime = new DateTime(
1800, //year
4, //month
19, //day
0, //hour
0); // minutes
System.out.println("DOW = " + anyDateTime.dayOfWeek().getAsText());
prints "DOW = Saturday"
I realize your goal is to learn Java, but it's not always necessary to reinvent the wheel.
tl;dr
int dayOfWeekNumber =
LocalDate.of( 2016 , 12 , 22 ) // A date-only object, no time-of-day nor time zone.
.getDayOfWeek() // `DayOfWeek` enum object.
.getValue() ; // 1-7 for Monday-Sunday.
java.time
The Question is really about algorithm. But FYI, this functionality is built into Java with the java.time classes.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone.
LocalDate ld = LocalDate.of( 2016 , 12 , 22 );
DayOfWeek
The DayOfWeek enum defines seven objects for each day of the week.
DayOfWeek dow = ld.getDayOfWeek() ;
Generally best to use the DayOfWeek object. But if you absolutely need an integer number, you can ask for one. Numbered 1-7 for Monday-Sunday per ISO 8601.
int dowNumber = dow.getValue();
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to useā¦).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
import java.util.*;
This code is working for any date.
Maybe it will help someone.
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner console = new Scanner(System.in);
System.out.println("Give me a special date !!!");
System.out.print("Month: ");
int month = console.nextInt();
System.out.print("Day: ");
int day = console.nextInt();
System.out.print("Year:");
int year = console.nextInt();
String date = dayFinder(month, day, year);
System.out.println("It is the date " + month + "/" + date + "/" + year);
console.close();
}
public static String dayFinder(int month, int day, int year) {
// handle invalid input
if (month > 12 || month < 1 || day > 31 || day < 1) {
throw new IllegalArgumentException("Month must be between "
+ "1 and 12 and day must be between 1 and 31");
} else {
// convert to "absolute" day, covering day and month
int absoluteDay = monthToDay(month, day, year);
// convert year to days and add to "absolute" day
absoluteDay += yearToDay(year);
if (absoluteDay % 7 == 2) {
return "Monday";
} else if (absoluteDay % 7 == 3) {
return "Tuesday";
} else if (absoluteDay % 7 == 4) {
return "Wednesday";
} else if (absoluteDay % 7 == 5) {
return "Thursday";
} else if (absoluteDay % 7 == 6) {
return "Friday";
} else if (absoluteDay % 7 == 0) {
return "Saturday";
} else { // absoluteDay % 7 == 1
return "Sunday";
}
}
}
// calculates the number of days between
// (the beginning of) the given year
// (the beginning of) the reference year 1601;
public static int yearToDay(int years) {
// covert to "absolute" day;
int absoluteDay = 0;
int leapYears = 0;
// iterate through years;
for (int i = 0; i < years; i++) {
if (((i % 4) == 0) && ((i % 100) != 0)) {
leapYears +=1;
} else if (i % 400 == 0) {
leapYears ++;
}
}
absoluteDay = (leapYears * 366) + (((years - 1) - leapYears) * 365);
return absoluteDay;
}
// Calculates the numbers of days present in a given
// date since the beginning of the year;
public static int monthToDay (int month, int day, int year) {
// convert to absolute day;
int absoluteDay = 0;
// iterate through months
for (int i = 1; i < month; i++) {
if ((i == 4) || (i == 6) || (i == 9) || (i == 11)) { // 30 day
absoluteDay += 30;
} else if (i == 2) {
if ((year % 4 == 0) && (year % 100 != 0)) {
absoluteDay += 29;
} else if (year % 400 == 0) {
absoluteDay += 29;
} else {
absoluteDay += 28;
}
} else {
absoluteDay += 31; // 1,3,5,7,8,10,12 months
}
}
return absoluteDay + day;
}
}
Related
How can I do a date validation with leap years in Java? Unfortunately, I can't use regular expressions or calendar. I will explain step by step!
This is my main component (where I implement most things):
An example of the implement I have to use:
and in another component called utility (I put the methods that will be used in the implementation, all this to carry an order, my utility code is as follows:
I put the data separately in a LOCAL ENVIRONMENT online, where I put a date that is valid in any respective year. enter image description here
For example in the image, I am putting the date 02/29/2012 which is validated and is in the correct order, but if I put a date 02/30/2012 which is incorrect data, it will throw me an error called 10005.
This is my last kick at the cat. No Calendar, no regex, and no try/catch. date range is from the year 1200 to the year 2200 (can be changed in code). The concept is used from Gilbert Le Blanc's answer:
public boolean isDateStringValid(String dateString) {
if (dateString == null || dateString.isEmpty()) {
return false;
}
// Maximum days in each month starting from January
int[] maxDays = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Parse the supplied date string
String m = dateString.substring(0, dateString.indexOf("/"));
String d = dateString.substring(dateString.indexOf("/") + 1, dateString.lastIndexOf("/"));
String y = dateString.substring(dateString.lastIndexOf("/") + 1);
//convert days, month, and year to integer.
int month = Integer.valueOf(m);
int day = Integer.valueOf(d);
int year = Integer.valueOf(y);
// Make sure year is in range
if (year < 1200 || year > 2200) {
return false;
}
/* If the year provided is a Leap year then change the Maximum
days for February (in maxDays[]) from 28 days to 29 days. */
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
maxDays[1] = 29;
}
// Is the provided month is range.
if (month < 1 || month > 12) {
return false;
}
// Is the day in range for the specific month
if (day < 1 || day > maxDays[month - 1]) {
return false;
}
// If everything is good we return true.
else {
return true;
}
}
The above code assumes the date format of "MM/dd/yyyy". If however it is suppose to be a format of "dd/MM/yyyy" then where the date string is parsed, swap m and d.
I should think that you would place this method into the Utility class. To use this method for your particular use-case, I believe it would be:
if(parameterIn.get("date") != null) {
String dateString = (String) parameterIn.get(Constants.DATE.getValue());
if (dateString != null && !dateString.trim().isEmpty()
&& util.isDateStringValid(dateString)) {
// Whatever you want here IF date is valid...
}
else {
// Whatever you want here IF the date is Invalid...
}
}
else {
this.addAdvice(Constants.MGBD100005.getValue());
}
Since you already have a Utility class, write one more utility method. Now all you have to do is see if the day portion of your date falls in between 1 and maxDays.
public int getMaximumDays(int month, int year) {
int[] maxDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month < 1 || month > 12) {
return -1;
}
if (month == 2 && isLeapYear(year)) {
return 29;
} else {
return maxDays[month - 1];
}
}
You can use the lib code
public static boolean isLeapYear(int yr) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, yr);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
But if you are going to reinvent this wheel then:
public static boolean isLeapYear(int year) {
if (year % 4 != 0) {
return false;
} else if (year % 400 == 0) {
return true;
} else if (year % 100 == 0) {
return false;
} else {
return true;
}
}
This question already has answers here:
Find Quarter to which a particular date belong for a Fiscal year
(3 answers)
Closed 2 years ago.
I am using java 8. I need to write a java code to display the Quarter based on given date. But the Q1 is from April 1st to June 30th.
Given Date :- between 2020/01/01 and 2020/03/31 --> Q42019
between 2020/04/01 and 2020/06/30 --> Q12020
between 2020/07/01 and 2020/09/30 --> Q22020
between 2020/10/01 and 2020/12/31 --> Q32020
between 2021/01/01 and 2021/03/31 --> Q42020
Create an instance of YearMonth, and subtract 3 months:
YearMonth ym = YearMonth.of(year, month).subtractMonths(3);
Then the quarter number is obtained by arithmetic:
int q = (ym.getMonthValue() - 1) / 3 + 1;
Then just access the fields:
String s = "Q" + q + ym.getYear();
This function can solve your problem
public String getQuarter(int month, int year) {
String quarterCode = "";
if(month >=1 && month <= 3) {
quarterCode = "Q4";
year = year - 1;
}
else if(month >=4 && month <= 6) {
quarterCode = "Q1";
}
else if(month >=7 && month <= 9) {
quarterCode = "Q2";
}
else if(month >=10 && month <= 12) {
quarterCode = "Q3";
}
return quarterCode + year;
}
I am trying to write a program in Java (this is a school assignment that tells you what day of the week a certain date is. (The date should be written on the form yyyy-mm-dd.) I thought I had come up with a solution with the code below, but then I found an error.
When you run the code, and type in 1999-12-31 in the dialog, the program tells you that the entered date (1999-12-31) is a Friday. But when you type in the date 2000-01-01 (which is one day after 1999-12-31), the program tells you that the day is a Sunday! What happened with Saturday? A similar problem happens when you type in 2000-02-29 and 2000-03-01, they both give Wednesday as an answer!
What I have yet noticed, this error appears only when you enter a date between 2000-01-01 and 2000-02-29. I would be very grateful if someone could please help me to find the cause of the error and to solve the problem!
import static javax.swing.JOptionPane.*;
import static java.lang.Math.*;
public class DateCalc {
// Here, between the DateCalc class declaration and the main method, several methods used in the program are
// constructed.
// The method isLeapYear tests whether the entered year is a leap year or not.
private static boolean isALeapYear(int year) {
// If the year is a multiple of 4 and not a multiple of 100, or a multiple of 400, then it is a leap year.
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
return true;
}
else {
return false;
}
}
// A method that tests whether a given string is written as a valid date.
private static boolean isAValidDate(int year, int month, int day) {
int maxValidYear = 9999;
int minValidYear = 1754;
if (year > maxValidYear || year < minValidYear) {
return false;
}
if (month < 1 || month > 12) {
return false;
}
if (day < 1 || day > 31) {
return false;
}
// Handle the February Month
if (month == 2) {
if (isALeapYear(year)) {
return (day <= 29); // This statement is true if the value of the day is less than or equal to 29 if the month is February within a leap year.
// Otherwise the statement is false and the method will return the boolean value false.
}
else {
return (day <= 28); // This statement is true if the value of the day is less than or equal to 28 if the month is February within a non-leap year.
// Otherwise the statement is false and the method will return the boolean value false.
}
}
// Month of April, June, September and November must have number of days less than or equal to 30.
if (month == 4 || month == 6 || month == 9 || month == 11) {
return (day <= 30);
}
return true;
}
// A method that calculates the day number within the year.
private static int dayNumberWithinYear(int year, int month, int day) {
// An array which stores the number of days in the different months (when the year is not a leap year).
// (Index 0 is the number of days in January, index 1 is the number of days in February, etc.)
int[] monthStructure = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// If the entered year is a leap year, then the monthStructure array will be initialized with an extra day in February, i.e the leap day.
if (isALeapYear(year)) {
monthStructure[1] = 29;
}
int sumDaysInPreviousMonths = 0;
int daysInTheCurrentMonth = day;
int dayNumber = 0;
// Loops through all the months (index 0 is January, index 1 is February, etc.).
for (int i = 0; i < month - 1; i++) {
sumDaysInPreviousMonths += monthStructure[i];
}
dayNumber = sumDaysInPreviousMonths + daysInTheCurrentMonth;
return dayNumber;
}
// A method that decides the day of the week of an entered date.
private static void weekDay(int year, int month, int day) {
// The number of days that have passed since January 1, 1754, excluding the days of the entered year and
// excluding the leap days.
int sumDaysInOrdinaryYears = (year - 1754) * 365;
int sumLeapDaysInLeapYears = 0;
// Suppose the entered year is n. The for-loop goes through all the years from year n-1 to year 1754, and
// checks if the current year in the loop is a leap year. The number of leap years between year 1754 and n-1
// is equal to the number of days that will get added (beside from the days in ordinary years) to the total
// days from January 1, 1754 to the entered date.
for (; year > 1754; year -= 1) {
if (isALeapYear(year)) {
sumLeapDaysInLeapYears += 1;
}
}
// The sum of all days from year 1754 to year n-1 (if the entered year is n), is equal to the sum of days in
// the ordinary years and the leap days in the years.
int sumDaysInEveryYearExcludingTheEntered = sumDaysInOrdinaryYears + sumLeapDaysInLeapYears;
int sumDaysInTotalYears = sumDaysInEveryYearExcludingTheEntered + dayNumberWithinYear(year, month, day);
int weekDay = sumDaysInTotalYears % 7;
if (weekDay == 0) {
showMessageDialog(null, "The date is a monday.");
}
else if (weekDay == 1) {
showMessageDialog(null, "The date is a tuesday.");
}
else if (weekDay == 2) {
showMessageDialog(null, "The date is a wednesday.");
}
else if (weekDay == 3) {
showMessageDialog(null, "The date is a thursday.");
}
else if (weekDay == 4) {
showMessageDialog(null, "The date is a friday.");
}
else if (weekDay == 5) {
showMessageDialog(null, "The date is a saturday.");
}
// If weekDay == 6
else {
showMessageDialog(null, "The date is a sunday.");
}
}
public static void main(String[] args) {
// This is step 3 in the laboratory instruction.
while (true) {
String date = showInputDialog("Please, enter a date on the form yyyy-mm-dd");
// If the user clicks 'Cancel' or clicks 'OK' when the dialog box is empty, the program will exit.
if (date == null || date.length() == 0) {
break;
}
int y = Integer.parseInt(date.substring(0,4));
int m = Integer.parseInt(date.substring(5,7));
int d = Integer.parseInt(date.substring(8));
if (!isAValidDate(y, m, d)) {
showMessageDialog(null, "Error! The entered date is invalid. " +
" Please enter a valid date on the form yyyy-mm-dd");
}
else {
weekDay(y, m, d);
}
}
}
}
Instead of asking us to debug through your entire code, perhaps consider LocalDate to get the desired result:
LocalDate ldt = LocalDate.parse("1999-12-31");
System.out.println(ldt.getDayOfWeek());
LocalDate ldt2 = LocalDate.parse("2000-01-01");
System.out.println(ldt2.getDayOfWeek());
Output:
FRIDAY
SATURDAY
The Problem is with finding the number of leap year. Your logic is counting the year 2000 also. The number of leap years should be same for 1999-12-31 and 2000-01-01. You need to consider year 2000 only if the month is greater than February. Increment the sumLeapDaysInLeapYears only if the input date is greater than Feb 28th
So basically we have this question to do : Write a method dayNumber that determines the number of days in a year up to and including the current day. The method should have three int parameters: year, month, and day. If the value of any parameter is invalid, the method should print a warning message and return the value zero. The table gives some examples of the action of the method. Accept any non-negative year as being valid. You may want to assume the existence of a method numberOfDays that returns the number of days in a given month of a given year. And you should have a method call isLeapYear, if the user enter a year that is a leap year.
This is what I did so far....
class dayMonthYear {
public static void main(String[] args) {
System.out.println("Enter a year");
int year = In.getInt();
System.out.println("Enter the month for '1' to be January - '12' to be december");
int month = In.getInt();
System.out.println("Enter the day");
int day = In.getInt();
dayNumber(year, month, day);
System.out.println(dayNumber(year, month, day));
}
public static int dayNumber(int year, int month, int day) {
int total = 0;
for (int m = 1; m < month; m++)
total += (numberOfDays(month, year));
return total + day;
}
public static boolean isLeapYear(int yearB) {
return (yearB % 4 == 0 && yearB % 100 != 0) || yearB % 400 == 0;
}
public static int numberOfDays(int monthA, int yearA) {
int days = 0;
if (monthA == 4 || monthA == 6 || monthA == 9 || monthA == 11)
days = 30;
if (monthA == 1 || monthA == 3 || monthA == 5 || monthA == 7
|| monthA == 8 || monthA == 10 || monthA == 12)
days = 31;
else if (monthA == 2 && isLeapYear(yearA))
days = 29;
else if (monthA == 2)
days = 28;
return days;
}
}
It works and compiles but my problem is that: let say I enter "12" for December and December has 31 days, so what my program will do since December has 31 days, it thinks each month has 31 days and add them up which will give me 372 when it's suppose to give me 365. How do I make it that it won't do that and that it will work if the year is a leap year too.
Basically, this...
for (int m= 1; m < month; m++)
total += (numberOfDays(month, year));
is wrong, you are passing the value of month to this method each time it is called (12 for example), meaning that each time you call it, it thinks the number of days in the month is 31 (because it is)...
Instead, pass m
for (int m= 1; m < month; m++)
total += (numberOfDays(m, year));
Spoiler alert!! for project euler problem 19.
The problem is how many sundays fall on the first of the month from 1901 to 2000. Given information: Jan 1st 1900 was a monday, Jan & march etc have 31 days, April, June etc 30 days and a leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400. which in this case applies to year 2000.
I got the correct answer, but when I checked my results with a real calendar, it turns out my code is counting saturdays. I don't see why it is doing that, can someone help?!
public class Sunday {
public static void main(String[] args) {
int sundayCount = 0;
int currentday = 0; // monday is 0, sunday will be 6
int dayLimit = 0;
for (int year = 1900; year < 2001; year++) {
for (int month = 1; month < 13; month++) {
// 30 days April, June, September, November
if (month == 4 || month == 6 || month == 9 || month == 11)
dayLimit = 30;
// leap year
else if (month == 2 && year % 4 == 0)
dayLimit = 29;
// February not leap year
else if (month == 2 && year % 4 != 0)
dayLimit = 28;
// Jan, March, May, July, August, October, December
else
dayLimit = 31;
for (int day = 1; day <= dayLimit; day++) {
if (day == 1 && currentday == 6 && year > 1900) {
System.out.println("year: " + year);
System.out.println("month: " + month);
sundayCount++;
}
if (currentday < 6)
currentday++;
else
currentday = 0;
}
}
}
System.out.println(sundayCount);
}
}
You forget to add into the code to check for if febuary 1900 should have 29 or 28 days. As 1900 wasn't a leap year it shouldn't have 29 days in 1900.
else if (month == 2 && year % 4 == 0 && year != 1900)
dayLimit = 29;
// February not leap year
else if (month == 2)
dayLimit = 28;
This should fix it.