0

I am fairly new to Java, and I am trying to build a program that prints a calendar in the format below. It is supposed to start the calendar using the current date and only creates the calendar 1 month ahead.

Example:

October 26, 2016.

   Su      M      T      W      T      F      S
               [October]
|      |      |      |      |  27  |  28  |  29  |
|  30  |  31  |      |      |      |      |      |
               [November]
|      |      |   1  |   2  |   3  |   4  |   5  |
|   6  |   7  |   8  |   9  |  10  |  11  |  12  |
|  13  |  14  |  15  |  16  |  17  |  18  |  19  |
|  20  |  21  |  22  |  23  |  24  |  25  |  26  |

I have written as much as I can, but I keep running into issues when getting to the actual coding. For example, I can't figure out how to start the calendar on the correct day of the week and I have trouble printing the correct amount of weeks in a month. Should I redo my code so that I use calendar = new GregorianCalendar()? Or should I just use instanceOf() and continue messing with the Calendar class?

This is as much code as I could write without breaking my calendar.

  StringBuilder calendarString = new StringBuilder();
  Calendar calendar = Calendar.getInstance();
  int maxDaysOfMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
  int dayOfMonth = Calendar.DAY_OF_WEEK;
  int weekOfMonth = Calendar.WEEK_OF_MONTH;
  int theMonth = Calendar.MONTH;
  String month;

  switch(theMonth) { // Months start with 0
    case 0: month = "January"; break;
    case 1: month = "February"; break;
    case 2: month = "March"; break;
    case 3: month = "April"; break;
    case 4: month = "May"; break;
    case 5: month = "June"; break;
    case 6: month = "July"; break;
    case 7: month = "August"; break;
    case 8: month = "September"; break;
    case 9: month = "October"; break;
    case 10: month = "November"; break;
    default: month = "December"; break;
  }
  calendarString.append(month + " " + calendar.get(Calendar.DAY_OF_MONTH) + ", ");
  calendarString.append(calendar.get(Calendar.YEAR) + "."); // First line ends here (Add Jobs this month)
  calendarString.append(System.getProperty("line.separator"));
  calendarString.append(System.getProperty("line.separator"));
  calendarString.append("   Su  ");
  calendarString.append("    M  ");
  calendarString.append("    T  ");
  calendarString.append("    W  ");
  calendarString.append("    T  ");
  calendarString.append("    F  ");
  calendarString.append("    S  ");
  calendarString.append(System.getProperty("line.separator"));

1 Answers1

1

You should not be using the Calendar & GregorianCalendar classes at all. They are troublesome, confusing, flawed, and poorly designed. Now legacy, supplanted by the java.time classes.

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

To increment through dates, simply add one day at a time.

LocalDate nextDay = today.plusDays( 1 ); 

DayOfWeek

You can interrogate for the day-of-week as a DayOfWeek enum object.

DayOfWeek dayOfWeek = today.getDayOfWeek();

Day-of-month

And you can interrogate for the day-of-month (the date number).

int dayOfMonth = today.getDayOfMonth();

Month name

You can get the name of the month automatically localized using the Month enum. So no need for that switch switch(theMonth) seen in your example code. By the way, note that java.time numbers months 1-12 for January-December rather than the crazy 0-11 seen in the legacy Calendar class.

String monthName = today.getMonth().getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH );

Weeks in a month

I have trouble printing the correct amount of weeks in a month

No need to know the number of weeks. As you increment one day at a time, check to see when the next day switches to the next month. One easy way to do that is to track the month being printed as a YearMonth object.

YearMonth ym = YearMonth.from( today );
…
YearMonth ymNextDay = YearMonth.from( nextDay );
if ( ymNextDay.isAfter( ym ) ) {
    … print your next month name here

Since you want more months that just the current month, you need to determine the ending month. In your case that would be the following month. You can do such math with the YearMonth class.

YearMonth ymStop = ymStart.plusMonths( 1 ); // Go from first month to the following month.

There are more than one way to solve your problem. Alternate approaches could indeed calculate number of weeks. I suspect doing so would complicate your particular problem.

Generating blanks

how to start the calendar on the correct day of the week

Simply count.

If the first date is a Thursday, and you consider Sunday as the first day of the week, then you must create "blank" (all SPACE characters) for the first four slots (columns) in your output.

If you considered Monday to be the first day of the week as is common around the world and in the ISO 8601 standard, you could simply ask the DayOfWeek for the order-in-week by calling DayOfWeek::getValue to get a value from 1 to 7. If not Monday, then you need another way to count the number of days from your start of week. You could use a List to track your intended day-of-week order.

List<DayOfWeek> daysOfWeekInOrder = new ArrayList<>( 7 );
daysOfWeekInOrder.add( DayOfWeek.SUNDAY );
daysOfWeekInOrder.add( DayOfWeek.MONDAY );
…
daysOfWeekInOrder.add( DayOfWeek.SATURDAY );

Asking for the position within the List returns a zero-based index number. So add one to get the ordinal position within the week.

int dayNumber = daysOfWeekInOrder.indexOf( today.getDayOfWeek() ) + 1 ;

For Thursday, you will get 5, so you know you need to loop 4 times ( 4 = 5 - 1 ) to generate blanks.

Likewise, if your last day of month is a Monday, you can ask for the day number of Monday, get 2, subtract from 7 to get 5, so you know you need to generate five blanks to complete the month.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

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