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?
- Java SE 8 and SE 9 and later
- Built-in.
- Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and SE 7
- Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
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.