2

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

obiwit
  • 100
  • 1
  • 9
  • As gebuh suggested, there are better problems with which to teach yourself Java than date-time. Date-time is a gnarly problem, a wide range of problems really. Doing such exercises can lead to false confidence in handling date-time. Serious programmers rely upon proven libraries where they exist. Libraries such as [Joda-Time](http://www.joda.org/joda-time/) or, in Java 8, the new [JSR 310](http://jcp.org/en/jsr/detail?id=310) features. – Basil Bourque Dec 03 '13 at 05:19
  • Thanks for all the answers! And when I use date-time again, I'll make sure to use libraries like Joda-Time or JSR 310. – obiwit Dec 03 '13 at 13:58

4 Answers4

5

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).

ArturSkowronski
  • 1,722
  • 13
  • 17
1

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.

gebuh
  • 797
  • 14
  • 40
  • There is no need to use Joda Time for this. The Java 7 API has all the functionality you need to do this correctly, without reinventing any wheels. – Dawood ibn Kareem Dec 03 '13 at 02:04
  • I'm sure Java 7 API does have the required functionality (my understanding is that they've incorporated some of JT's practices), my point was that Joda-Time is a saner implementation. Feel free to post the code though, I'd like to see it. – gebuh Dec 03 '13 at 02:23
  • Actually, it's been possible since 1.1. If you want me to post a solution, you'll have to ask a new question. This question was "why are my dates out by 3", which ArturSkowronski has correctly answered. – Dawood ibn Kareem Dec 03 '13 at 03:02
  • 1
    and also "Any feedback is highly appreciated, but I do ask that you explain why you would do something another/a certain way" I posted another way. – gebuh Dec 03 '13 at 03:14
  • Don’t bother even starting with java.util.Date/Calendar since you have [Joda-Time](http://www.joda.org/joda-time/) at your disposal. Date/Calendar are notoriously bad, that's why they are being supplanted in Java 8 (not Java 7) by the [JSR 310](http://jcp.org/en/jsr/detail?id=310) project. JSR 310 is inspired by Joda-Time, but entirely re-architected. – Basil Bourque Dec 03 '13 at 05:23
0

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.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0
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;
}

}