Already had an initial program for this one, I made the code as much shorter and precise as possible. But upon checking it seems that the next 2 years still prints 2018 and I am expecting 2019 and 2020. How can I possible dynamically prints the year when it shows 2018, 2019, and 2020. might be I am missing some iterations in my code.
Also feel free to criticize my code, you can also suggest more shorter codes by using the Calendar API or Java 8 utilities as much as you can.
See code below:
package calendarjava;
import java.util.Calendar;
import java.util.Scanner;
import java.util.GregorianCalendar;
import java.util.Locale;
public class CalendarJava {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Enter a year: ");
int year = sc.nextInt();
Calendar cal = new GregorianCalendar();
int startDay;
int numberOfDays;
for (int i=0; i<36; i++){
cal.set(year, i, 1);
startDay = cal.get(Calendar.DAY_OF_WEEK);
numberOfDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
System.out.print(cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US));
System.out.println( " " + year);
printMonth(numberOfDays,startDay);
System.out.println();
}
}
private static void printMonth(int numberOfDays, int startDay) {
int weekdayIndex = 0;
System.out.println("Su Mo Tu We Th Fr Sa");
for (int day = 1; day < startDay; day++) {
System.out.print(" ");
weekdayIndex++;
}
for (int day = 1; day <= numberOfDays; day++) {
System.out.printf("%1$2d", day);
weekdayIndex++;
if (weekdayIndex == 7) {
weekdayIndex = 0;
System.out.println();
} else {
System.out.print(" ");
}
}
System.out.println();
}
}
Enter a year: 2018
January 2018
Su Mo Tu We Th Fr Sa
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 27
28 29 30 31
February 2018
Su Mo Tu We Th Fr Sa
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 27 28
March 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
April 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30
May 2018
Su Mo Tu We Th Fr Sa
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
27 28 29 30 31
June 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
July 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30 31
August 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
September 2018
Su Mo Tu We Th Fr Sa
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 27 28 29
30
October 2018
Su Mo Tu We Th Fr Sa
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 27
28 29 30 31
November 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
December 2018
Su Mo Tu We Th Fr Sa
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 27 28 29
30 31
January 2018
Su Mo Tu We Th Fr Sa
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
27 28 29 30 31
February 2018
Su Mo Tu We Th Fr Sa
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 27 28
March 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
31
April 2018
Su Mo Tu We Th Fr Sa
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 27
28 29 30
May 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
June 2018
Su Mo Tu We Th Fr Sa
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 27 28 29
30
July 2018
Su Mo Tu We Th Fr Sa
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 27
28 29 30 31
August 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
September 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30
October 2018
Su Mo Tu We Th Fr Sa
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
27 28 29 30 31
November 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
December 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30 31
January 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
February 2018
Su Mo Tu We Th Fr Sa
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 27 28 29
March 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30 31
April 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
May 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30
31
June 2018
Su Mo Tu We Th Fr Sa
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 27
28 29 30
July 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
August 2018
Su Mo Tu We Th Fr Sa
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 27 28 29
30 31
September 2018
Su Mo Tu We Th Fr Sa
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
27 28 29 30
October 2018
Su Mo Tu We Th Fr Sa
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 27 28 29 30 31
November 2018
Su Mo Tu We Th Fr Sa
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 27 28
29 30
December 2018
Su Mo Tu We Th Fr Sa
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
27 28 29 30 31
Here is an entirely different take, for your comparison. This code uses the features of modern Java including the java.time classes defined in JSR 310, streams, convenient List
factory method, and enums.
All of this code in contained in a single .java
file, to define our CalendarMaker
class. See the main
method as a demo for how to make use of this class.
The class has two member variables you inject via the constructor: the end-of-line (newline) character(s) to use in our resulting text, and the Locale
by which we (a) determine the order of the days-of-week, and (b) localize the name of the month and the name of the day-of-week.
We use StringBuilder
class to build up our text by calling its append
method.
Use specific types whenever possible, to make your code more self-documenting, ensure valid values, and provide type-safety. So we start with a list of Year
objects, for the current year along with previous and following years.
For each year, we loop the months. Each month is represented as a YearMonth
object. We localize the name of the month by calling Month.getDisplayName
. Then we localize the day-of-week column headers by first localizing the name of the day-of-week, and then truncating to take only the first two letters.
The DayOfWeek
enum provides ready-made objects to represent each day-of-week.
Note that we also localize the order of the days in the week. In the US, Sunday starts the week on most calendars. But in Europe and elsewhere you will often see Monday first. Any day-of-week is tolerated by our code to start the week, in case there are other choices by some cultural norms.
A TemporalAdjuster
found in the TemporalAdjusters
class determines a date for the starting date in our monthly grid. Then we increment day by day, in weekly chunks. Optionally, we suppress the display of dates that lay outside our target month.
To generate the text for each day number, use a DateTimeFormatter
. Use a formatting pattern of dd
to pad single-digit numbers with a zero. To pad with a SPACE, use ppd
.
Update: I replaced the for
loop in this block of code with a stream from LocalDate.datesUntil
. Inside we use a ternary operator to suppress the dates outside our target month. I am not saying this rewrite is necessarily better; I just want to show off the spiffy syntax with stream & lambda as an example of modern Java programming.
// Rows (each week)
LocalDate localDate = yearMonth.atDay( 1 ).with( TemporalAdjusters.previousOrSame( firstDayOfWeek ) );
while ( ! localDate.isAfter( yearMonth.atEndOfMonth() ) ) // "Not after" is a shorter way of saying "is equal to or sooner than".
{
for ( int i = 0 ; i < 7 ; i++ )
{
// If we want to suppress the out-of-month dates that may exist in first and last rows.
if ( ! YearMonth.from( localDate ).equals( yearMonth ) )
{
sb.append( " " ); // Use 2 spaces rather than 2 digits of day-of-month number.
} else // Else the date is inside our target year-month.
{
sb.append( localDate.format( CalendarMaker.DAY_FORMATTER ) );
}
if ( i < 6 )
{
sb.append( " " ); // Pad with a SPACE between columns.
}
localDate = localDate.plusDays( 1 ); // Increment one day at a time.
}
sb.append( this.eol );
}
…became:
// Rows (each week)
LocalDate localDate = yearMonth.atDay( 1 ).with( TemporalAdjusters.previousOrSame( firstDayOfWeek ) ); // Get the first date of the month, then move backwards in time to determine the first date that fits our calendar grid. May be the same as the first, or may be earlier date from the previous month.
while ( ! localDate.isAfter( yearMonth.atEndOfMonth() ) ) // "Not after" is a shorter way of saying "is equal to or sooner than".
{
String week =
localDate
.datesUntil( localDate.plusWeeks( 1 ) ) // Get a stream of dates via `LocalDate::datesUntil`. The ending date is exclusive (half-open).
.map( ld -> ( YearMonth.from( ld ).equals( yearMonth ) ? ld.format( CalendarMaker.DAY_FORMATTER ) : " " ) ) // Display the day-of-month number if within the target month, otherwise display a pair of SPACE characters.
.collect( Collectors.joining( " " ) ); // Separate columns with a SPACE in our calendar grid.
sb.append( week ).append( this.eol ); // Add this row of text for the week, and wrap to next line for next loop.
localDate = localDate.plusWeeks( 1 ); // Increment one week at a time to set up our next loop.
}
CalendarMaker.java
package work.basil.example;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class CalendarMaker
{
// Member variables.
private String eol;
private Locale locale;
static private DateTimeFormatter DAY_FORMATTER = DateTimeFormatter.ofPattern( "ppd" ); // Use `dd` to pad single-digits values with a leading zero. Use `ppd` to pad with a SPACE.
// Constructor
public CalendarMaker ( String eol , Locale locale )
{
this.eol = eol;
this.locale = locale;
}
private CharSequence generateYear ( final Year year )
{
// Year header.
StringBuilder sb = new StringBuilder();
sb.append( "|------ " + year + " ------|" ).append( this.eol ).append( this.eol );
// Each month.
for ( Month month : EnumSet.allOf( Month.class ) )
{
YearMonth ym = YearMonth.of( year.getValue() , month );
CharSequence monthCalendar = this.generateMonth( ym );
sb.append( monthCalendar );
}
return sb;
}
private CharSequence generateMonth ( final YearMonth yearMonth )
{
// Title
StringBuilder sb = new StringBuilder();
String monthName = yearMonth.getMonth().getDisplayName( TextStyle.FULL , this.locale );
sb.append( yearMonth.getYear() ).append( " " ).append( monthName ).append( this.eol );
// Column headers.
DayOfWeek firstDayOfWeek = WeekFields.of( this.locale ).getFirstDayOfWeek();
List < DayOfWeek > dows =
IntStream
.range( 0 , 7 )
.mapToObj( firstDayOfWeek :: plus )
.collect( Collectors.toList() );
String columnHeaders =
dows
.stream()
.map( dayOfWeek -> dayOfWeek.getDisplayName( TextStyle.SHORT_STANDALONE , this.locale ).substring( 0 , 2 ) )
.collect( Collectors.joining( " " ) );
sb.append( columnHeaders ).append( this.eol );
// Rows (each week)
LocalDate localDate = yearMonth.atDay( 1 ).with( TemporalAdjusters.previousOrSame( firstDayOfWeek ) ); // Get the first date of the month, then move backwards in time to determine the first date that fits our calendar grid. May be the same as the first, or may be earlier date from the previous month.
while ( ! localDate.isAfter( yearMonth.atEndOfMonth() ) ) // "Not after" is a shorter way of saying "is equal to or sooner than".
{
String week =
localDate
.datesUntil( localDate.plusWeeks( 1 ) ) // Get a stream of dates via `LocalDate::datesUntil`. The ending date is exclusive (half-open).
.map( ld -> ( YearMonth.from( ld ).equals( yearMonth ) ? ld.format( CalendarMaker.DAY_FORMATTER ) : " " ) ) // Display the day-of-month number if within the target month, otherwise display a pair of SPACE characters.
.collect( Collectors.joining( " " ) ); // Separate columns with a SPACE in our calendar grid.
sb.append( week ).append( this.eol ); // Add this row of text for the week, and wrap to next line for next loop.
localDate = localDate.plusWeeks( 1 ); // Increment one week at a time to set up our next loop.
}
// Footer (for the month)
sb.append( this.eol ); // Put a blank line after every month.
return sb;
}
// Demonstrate this class with a psvm method.
public static void main ( String[] args )
{
CalendarMaker calendarMaker = new CalendarMaker( "\n" , Locale.CANADA_FRENCH );
// Demonstrate 3 years: previous year, current, and next year.
Year currentYear = Year.now( ZoneId.of( "America/Boise" ) );
List < Year > years = List.of( currentYear.minusYears( 1 ) , currentYear , currentYear.plusYears( 1 ) );
for ( Year year : years )
{
CharSequence calendar = calendarMaker.generateYear( year );
System.out.println( "" );
System.out.println( calendar );
}
}
}
When run.
|------ 2018 ------|
2018 janvier
di lu ma me je ve sa
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 27
28 29 30 31
2018 février
di lu ma me je ve sa
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 27 28
…
Switch the locale from Locale.CANADA_FRENCH
to Locale.FRANCE
to see how we keep the French language but switch cultural norms from North American to European to start the week with Monday (lundi) rather than Sunday (dimanche).