I'm trying to iterate over a range of weekdays, where each weekday maps to an integer (Sunday = 1, Monday = 2, ..., Saturday = 7).
The range is defined by a start and end day. This is fine for a start and end day like Monday(2) - Thursday(5), as I can just do:
for(int i = startDay; i <= endDay; i++) { ... }
I'm having difficulty when the range is split across the end and start of a week, like Friday(6) - Monday(2). This obviously doesn't work with the above for loop - e.g.
for(int i = 6; i <= 2; i++) { ... } // wouldn't even execute once.
The only solution I can see is implementing some sort of circular buffer, which I feel is overkill for such a simple problem. I'm just stumped, and most likely missing something obvious here. I think a solution may have something to do with modular arithmetic, but I'm not quite sure.
You can do:
int numberOfDays = endDay >= startDay ? endDay - startDay : 8 - (startDay - endDay);
for (int i = startDay; i <= startDay + numberOfDays; i++) {
int day = (i - 1) % 7 + 1;
}
This makes use of the % modulo operator to ensure all values remain within 1 - 7.
For example, once i becomes 8 the calculation will wrap day back to 1: (8 - 1) % 7 + 1 == 1.
It's probably clearest just to use a break, then you don't have to worry about all the different cases:
for (int day = startDay; ; day = (day - 1) % 7 + 1) {
// ... do your stuff
if (day == endDay) {
break;
}
}
or, some might prefer this:
int day = startDay;
while (true) {
// ... do something
if (day == endDay) {
break;
}
day = (day - 1) % 7 + 1;
}
or:
int day = startDay;
while (true) {
// ... do something
if (day == endDay) {
break;
}
if (++day > 7) {
day = 1;
}
}
Trying to get all the different cases right with a for loop can be a headache. You have to make sure these are handled:
startDay == 1, endDay == 7
endDay == startDay
endDay == startDay - 1 (which should go through every day of the week in some order)
endDay > startDay
endDay < startDay
In Java 8:
// input section
DayOfWeek start = DayOfWeek.FRIDAY;
DayOfWeek end = DayOfWeek.MONDAY;
// execution section
DayOfWeek dow = start;
DayOfWeek stop = end.plus(1);
do {
// ... your code
dow = dow.plus(1);
} while (dow != stop);
The do-while-construction ensures that for the condition start == end the loop will be executed at least once.
UPDATE due to use of your localized indices (Sunday = 1 instead of 7)
You can convert the indices to DayOfWeek-objects this way:
private static DayOfWeek convert(int localIndex) {
int iso = localIndex - 1;
if (iso == 0) {
iso = 7;
}
return DayOfWeek.of(iso);
}
You can use a % operator for some sort of circularity
for (int i=6; i!=2; i=(i+1)%7) { ... }
That will index days from 0, so Sunday = 0 ... Saturday = 6. If you really want to have index starting from 1 it's only interpretation problem. You can for example do:
for (int i=6; i!=2; i=(i+1)%7) {
int day=i+1;
// use day here
}
Related
So I got an assignment to build a Date class. When I am receiving a day, month and a year as parameters, I need to make sure that the Date is valid. I do it by calling a function named isDateValid. According to the instructions, if the date is valid, the parameters will be set has the properties of the class, otherwise, I need to create the date with the default value of 1/1/2000. however, when I run the testers the university has provided, it seems that even if the Date is valid, the actual Date will be set the default - meaning that the function isDateValid is returning false when it is supposed to return true.
Here is one of the objects created by the tester:
Date d1 = new Date(3, 5, 1998);
, when I run it, I get the default date.
the function is:
private boolean isDateValid(int day, int month, int year)
{
return (((month == JANUARY || month == MARCH || month == JULY || month == AUGUST || month == OCTOBER || month == DECEMBER || month == MAY) &&
day <= DAYS_IN_JAN_MAR_MAY_JUL_AUG_OCT_DEC) ||
((month == APRIL || month == JUNE || month == SEPTEMBER || month == NOVEMBER) && day <= DAYS_IN_JUN_APR_SEP_NOV) ||
(((month == FEBRUARY && day <= DAYS_IN_FEB_LEAP)) || (isLeapYear(year) && day <= DAYS_IN_FEB_NO_LEAP && month == FEBRUARY)) ) &&
( day >= MINIMUM_DAY && month >= MINIMUM_MONTH && month <= MAXIMUM_MONTH && year >= MINIMUM_YEAR && year <= MAXIMUM_YEAR);
}
Here are the contants being used:
private final int MINIMUM_YEAR = 1000;
private final int MINIMUM_MONTH = 1;
private final int MINIMUM_DAY = 1;
private final int MAXIMUM_DAY = 31;
private final int MAXIMUM_MONTH = 12;
private final int MAXIMUM_YEAR = 9999;
private final int JANUARY = 1;
private final int FEBRUARY = 2;
private final int MARCH = 3;
private final int APRIL = 4;
private final int MAY = 5;
private final int JUNE = 6;
private final int JULY = 7;
private final int AUGUST = 8;
private final int SEPTEMBER = 9;
private final int OCTOBER = 10;
private final int NOVEMBER = 11;
private final int DECEMBER = 12;
private final int DAYS_IN_JAN_MAR_MAY_JUL_AUG_OCT_DEC = 31;
private final int DAYS_IN_JUN_APR_SEP_NOV = 30;
private final int DAYS_IN_FEB_NO_LEAP = 28;
private final int DAYS_IN_FEB_LEAP = 29;
Edit:
I got a comment suggesting that the source of the error can be somewhere else, so I will provide all related code to this function:
this is the function I use in order to determent whether or not it is a leap year
private 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;
}
}
and here is the constructor of the Date class:
public Date (int day, int month, int year)
{
if(isDateValid(day, month, year))
{
_day = day;
_month = month;
_year= year;
}
else
{
_day = DEFULTE_DAY;
_month = DEFULTE_MONTH;
_year = DEFULTE_YEAR;
}
}
I really can't determent what is the problem, and the fact that it is one complex condition and not multiple if statements make it hard to debug.
Since it is an assignment I am not allowed to use "actual java" - the java date class etc...
Where did I was mistaken?
Thank you in advance!
As you've sort of identified, that return statement is hard to debug. I'd break it up into manageable sections.
private boolean isDateValid(int day, int month, int year) {
bool retVal = true;
retVal = retVal && (month >= JANUARY) && (month <= DECEMBER);
retVal = retVal && (day >= 1) && (day <= DAYS_IN_JAN_MAR_MAY_JUL_AUG_OCT_DEC);
if (month == APRIL || month == JUNE ...) {
retVal = retVal && (day < DAYS_IN_JUN_APR_SEP_NOV);
}
if (month == FEBRUARY) {
.. handle the complex case of February
}
return retVal;
}
Note also that because January through December are values in the range [1..12] without gaps, you don't need to check month against each value but can just check within January..December. That makes this a little simpler.
That weird-ass complicated return statement is just begging for something problematic. It's too easy to get something that complicated wrong.
I have tested your isDateValid method with every valid date from year 1799 through 2100 and with every valid date in year 1000 and year 9999. It returns true every time. Also for the example date you gave, isDateValid(3, 5, 1998) returns true. So I believe that the cause for returning a default date doesn’t lie within the isDateValid method that you have posted. I can’t tell you where then, but you need to look elsewhere.
You have got a bug, though: isDateValid(29, 2, 1999) returns true. 1999 was not a leap year, so it should return false. So let’s examine the code dealing with February:
((month == FEBRUARY && day <= DAYS_IN_FEB_LEAP))
|| (isLeapYear(year) && day <= DAYS_IN_FEB_NO_LEAP && month == FEBRUARY)
(I have broken it over two lines to fit it better into Stack Overflow.) In case of 29th February in a leap year, the first line evaluates to true. Since there is || (logical or) between the two lines, true is the result of the entire expression. Java uses lazy evaluation here and doesn’t even evaluate the second line, but even if it did, the result of the logical or operation would still be true.
You could probably find a way to fix it, but you shouldn’t. Instead rewrite your entire method along the lines in the other answer.
this is what I've got so far, but all I get is zero for the output. I've tried for loops that are separate for the first year and last year to calculate just the months in those years then a separate for loop for the years inbetween since they would be full years and still I get the wrong output. I just need some guidance and as the title says I can't use calender or date or any other similar class just loops and logic.
package cop2250.spring18.week3.ledesma;
import java.util.Scanner;
public class Week3assignment1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int days1 = 0, days2 = 0, days3 = 0, totalDays = 0, month3 = 0, betweenYears, year3;
System.out.println("Enter a starting Date in the format mm-dd-yyyy: ");
String[] startDate = input.next().split("-");
int month1 = Integer.parseInt(startDate[0]);
int day1 = Integer.parseInt(startDate[1]);
int year1 = Integer.parseInt(startDate[2]);
System.out.println("Enter an ending Date in the format mm-dd-yyyy: ");
String[] endDate = input.next().split("-");
int month2 = Integer.parseInt(endDate[0]);
int day2 = Integer.parseInt(endDate[1]);
int year2 = Integer.parseInt(endDate[2]);
for(year3 = 0; year3 > year1 && year3 < year2; year3++) {
for (month3 = 0; month3 < 12; month3++) {
days1 = daysInMonth(year3, month3);
}
totalDays =+ days1;
}
System.out.println("Days is : "+ totalDays);
}
private static boolean isLeapYear(int year) {
return year % 400 == 0 || (year % 4 == 0 || year % 100 == 0);
}
private static int daysInMonth(int year, int month) {
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
return 31;
if (month == 4 || month == 6 || month == 9 || month == 11)
return 30;
if (month == 2)
return isLeapYear(year) ? 29 : 28;
return 0;
}
}
I see some issues here:
The outer loop starts from 0, shouldn't it start from year1?
The inner loop numbers months from 0 to 11 but the method you call numbers them from 1 to 12.
x =+ y is quite different from x += y.
If I give a date in 2016 and one in 2017 it looks to me like you are counting the days of all the months in those two years. So the inner loop also need to consider the given months (and days):
I am new to programming and java and I am trying to solve the following problem:
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
Here is my code:
int count, sum = 0;
for (int i = 1901; i < 2001; i++) {
LocalDate test = LocalDate.of(i, 1, 1);
sum += test.lengthOfYear();
}
for (int i = 1; i < sum; i++) {
LocalDate date1 = LocalDate.of(1901, 1, 1);
date1 = date1.plusDays(i);
if (date1.getMonth() == JANUARY && date1.getDayOfWeek() == SUNDAY) {
count++;
}
}
System.out.println(count);
If I print the results, it seems to be working fine.
My result is 443, but the correct answer is 171. What am I doing wrong?
Thank you!
I suspect 443 is the total number of Sundays in January in the twentieth century. This happens because you walk over all possible days of the twentieth century and then check if the current month is January and if the current day is Sunday.
This is not what you want.
I would use a different approach:
Walk over the 1st day of each month of each year.
And then check if it's a Sunday.
The code will probably be much faster.
// Each year
for (int y = 1901; y < 2001; y++) {
// Each month of the year
for (int m = 1; m <= 12; m++) {
if (LocalDate.of(y, m, 1).getDayOfWeek() == DayOfWeek.SUNDAY) {
count++;
}
}
}
Your code would have been correct if you changed date1.getMonth() == JANUARY to date1.getDayOfMonth() == 1. However, the method is very inefficient, because it checks each day of the twentieth century, while it only needs to check the first day of each month. The abovementioned code is approximately 40 times faster on my machine.
Here is an equivalent of the abovementioned code, with functional style:
long count = Stream.iterate(YearMonth.of(1901, 1), ym -> ym.plusMonths(1))
.takeWhile(ym -> ym.isBefore(YearMonth.of(2001, 1)))
.map(ym -> ym.atDay(1).getDayOfWeek())
.filter(DayOfWeek.SUNDAY::equals)
.count();
Using Todd's Java-TimeStream, with functional style:
YearMonthStream
.from(YearMonth.of(1901, 1))
.until(YearMonth.of(2000, 12))
.stream()
.map(ym -> ym.atDay(1).getDayOfWeek())
.filter(DayOfWeek.SUNDAY::equals)
.count();
I see some mistakes:
public static void main(String[] args) {
int count, sum = 0;
for (int i = 1901; i < 2001; i++) { // There is a mistake here, I dont know what you want to compute in this loop!
LocalDate test = LocalDate.of(i,1,1);
sum += test.lengthOfYear();
}
for (int i = 1; i < sum; i++) {
LocalDate date1 = LocalDate.of(1901,1,1); // There is a mistake here, date1 must be outside of this loop
date1 = date1.plusDays(i); // There is a mistake here, plusDays why??
if(date1.getMonth() == JANUARY && date1.getDayOfWeek() == SUNDAY) { // There is a mistake here, why are you cheking this: date1.getMonth() == JANUARY ?
count++;
}
}
System.out.println(count);
}
A simple solution:
public static void main(String[] args) {
int count = 0;
LocalDate date1 = LocalDate.of(1901, Month.JANUARY, 1);
LocalDate endDate = LocalDate.of(2001, Month.JANUARY, 1);
while (date1.isBefore(endDate)) {
date1 = date1.plusMonths(1);
if (date1.getDayOfWeek() == DayOfWeek.SUNDAY) {
count++;
}
}
System.out.println(count);
}
Apart from the error that has already been flagged, you could reconsider your design and use the YearMonth class which seems better suited to your use case than LocalDate:
public static void main(String[] args) {
YearMonth start = YearMonth.of(1901, 1);
YearMonth end = YearMonth.of(2000, 12);
int count = 0;
for (YearMonth ym = start; !ym.isAfter(end); ym = ym.plusMonths(1)) {
//is first day of month a sunday?
if (ym.atDay(1).getDayOfWeek() == SUNDAY) count ++;
}
System.out.println(count); //171
}
You start by finding the number of days in the period you're checking, and then start a for loop to run through that period. So far so good. But your condition for incrementing the count is wrong:
if(date1.getMonth() == JANUARY && date1.getDayOfWeek() == SUNDAY)
This means that as you loop over each day, you increase count if that date is a Sunday in January. You don't check whether that Sunday was the first of January, and you don't count anything from February to December.
You should be checking that it's the first day of the month and Sunday, not the first month of the year and Sunday.
The following code should output the correct value.
public static void main(String[] args) {
int count = 0, sum = 0;
for (int i = 1901; i < 2001; i++) {
LocalDate test = LocalDate.of(i, 1, 1);
sum += test.lengthOfYear();
}
for (int i = 1; i < sum; i++) {
LocalDate date1 = LocalDate.of(1901, 1, 1);
date1 = date1.plusDays(i);
if (date1.getDayOfMonth() == 1 && date1.getDayOfWeek() == java.time.DayOfWeek.SUNDAY) {
count++;
}
}
System.out.println(count);
}
Note that:
You don't need to check that month is January, because your requirement is about any 1st of the month.
You must also ensure that the current day is the first day of the month.
443 is number of Sundays in January, if you want the Sundays on the first of the month, you have to change your code to:
if(date1.getDayOfMonth() == 1 && date1.getDayOfWeek() == SUNDAY) {
count++;
}
}
Sugestion: instead of walk over all days of each year, you can just add 1 month to the date util 12 month:
for (int i = 1901; i < 2001; i++) {
for(int mon =0; mon<12; mon++){
LocalDate date1= LocalDate.of(i,mon,1);
if(date1.getDayOfWeek() == SUNDAY) {
count++;
}
}
}
You are considering only January month from 1901 to 2000, which is incorrect, you should consider whether first day of every month from 1901 to 2000 was sunday or not.
I would like to ask someone if he/she can help me find the bug where I lose 2 solutions in this problem. My code isn't very pretty and readable but I think it is simple enough to understand logic here. I am sitting on this one for hour and even made up different solution which works but can't find problem in this one.
private static int _year = 1900;
private static int[] _months = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
private static int _month = 0;
private static int _day = 7; //we start from Sunday
private static int counter = 0;
public static void main(String[] args) {
while(_year !=1901)
nextSunday(_day, _month, _year);
while(true) {
if(_year == 2000 && _month == 11 && _day > 31) break;
nextSunday(_day, _month, _year);
}
System.out.println(counter);
}
private static void nextSunday(int day, int month, int year) {
if(isSundayFirst(_day)) counter++;
if(year == 2000 && month == 11 && day + 7 > _months[month]) { //to break loop
_day = day + 7;
} else if(month == 11 && day + 7 > _months[month]) { // new year, end of month
_day = day + 7 - _months[month];
_month = 0;
_year++;
} else if(month == 1 && isLeapYear(year) && day + 7 > 29) { // leap year end of february
_day = day + 7 - 29;
_month++;
} else if(month == 1 && !isLeapYear(year) && day + 7 > _months[month]) { // not leap year end of february
_day = day + 7 - _months[month];
_month++;
} else if(day + 7 > _months[month]) { // end of month
_day = day + 7 - _months[month];
_month++;
} else { // next in same month
_day = day + 7;
}
}
private static boolean isSundayFirst(int day) {
return day == 1;
}
private static boolean isLeapYear(int year) {
if(isCentury(year)) {
if(year % 400 == 0) return true;
} else {
return year % 4 == 0;
}
return false;
}
private static boolean isCentury(int year) {
return year % 100 == 0;
}
I got 169 such Sundays. I should get 2 more.
Here is the problem:
You are given the following information, but you may prefer to do some
research for yourself.
1 Jan 1900 was a Monday.
Thirty days has September,
April, June and November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth
century (1 Jan 1901 to 31 Dec 2000)?
I will appreciate any effort. Thank you.
PS I know this _name style isn't Java like but I was writing it fast without intention of posting it anywhere tbh.
There are two issues with your code, one that causes you to over count by 2 and another that causes you to undercount by 4.
Issue #1 The problem to tells you to start counting in 1901, but you start counting in 1900.
Change
if(isSundayFirst(_day)) counter++;
to
if(isSundayFirst(_day) && _year >= 1901) counter++;
to fix this.
Issue #2 The issue lies in this condition:
else if(day + 7 > _months[month]) { // end of month
_day = day + 7 - _months[month];
_month++;
}
You've handled the case where it is February in the previous two conditions, so you need to check to make sure it is not February here. Changing the condition to
else if(month != 1 && day + 7 > _months[month])
will fix your issue.
Sidenote: The structure of your nextSunday method made it quite difficult to decipher, so here is my effort to simplify it (you will now break when _year > 2000):
private static void nextSunday(int day, int month, int year) {
int febNum = isLeapYear(_year) ? 29 : 28;
int dayCount = (month == 1) ? febNum : _months[_month]; //days in current month
_day += 7;
if (_day > dayCount) { //new month
_month++;
_day -= dayCount;
}
if (_month == 12) { //new year
_month = 0;
_year++;
}
}
I'm trying to write a program that makes each month of the year an array. Each month has different charges, some occur every month, some only occur every few months. We have to write this program using only loops, so I'm wondering how I can assign a certain value to multiple months (for example, + $200 for January and June) using only 1 "for" statement.
Below is the code I have so far dealing with this array:
//This line creates an array for 12 months and a variable called "month"
Double[] yearlyExpenses = new Double[12];
int month;
//These lines assign values to the correct months using loops
for (month = 0; month < yearlyExpenses.length; month++) {
yearlyExpenses[month] = rent + miscExpenses;
}
for (month = 0; month == 0 & 5; month++) {
yearlyExpenses[month] = yearlyExpenses[month] + 200;
}
You can see that the second "for" statement is where I'm trying to assign the value to the two months.
The code works fine if I were to just say "month == 0", but when I try to include a different month using "&" or "&&" or anything of the sort, I get a compiling error.
Sorry if this is a dumb question, I'm fairly new to Java. Thanks in advance!
Your second loop wouldn't work like you expect, instead perform the math in your first loop. Something like,
for (month = 0; month < yearlyExpenses.length; month++) {
yearlyExpenses[month] = rent + miscExpenses;
if (month == 0 || month == 5) {
yearlyExpenses[month] = yearlyExpenses[month] + 200;
}
}
If you need just for month 0 and 5 try this
for (month = 0; month < 6; month+=5) {
yearlyExpenses[month] = yearlyExpenses[month] + 200;
}