使用 Java 打印 12 个月和接下来的 2 年

Print 12 Month Year and the next 2 Years using Java

我已经有了这个的初始程序,我把代码写得尽可能短和精确。但是经过检查,似乎接下来的 2 年仍会打印 2018 年,我期待 2019 年和 2020 年。当它显示 2018、2019 和 2020 时,我怎么可能动态打印年份。可能是我的代码中缺少一些迭代。

也可以随意批评我的代码,您也可以尽可能多地使用日历 API 或 Java 8 个实用程序来建议更短的代码。

查看下面的代码:

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("%1d", 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

要更正您当前使用的代码,请不要迭代超过 36 个月来获取您三年的日历。 Calendar#set() 方法无法增加年份。而是将外部 for 循环添加到包含在 main() 方法中的现有 for 循环中,以便遍历所需的年份,现在的内部循环仅遍历 12 个月(不是 36)。通过这种方式,年份由 Calendar#set() 方法的外循环递增。它看起来像这样:

Scanner sc = new Scanner(System.in);
System.out.print("Enter a year: ");
int year = sc.nextInt();

Calendar cal = new GregorianCalendar();

int startDay;
int numberOfDays;

// Display Calendars for 3 years only!
for (int yr = year; yr <= (year + 2); yr++) {
    // The Months for current year determined by yr...
    for (int i = 0; i < 12; i++) {
        cal.set(yr, i, 1);
        startDay = cal.get(Calendar.DAY_OF_WEEK);
        numberOfDays = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
        String month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US);
        // A Ternary Operator is used below to append an asterisks (**) 
        // the end of the month display for February on leap years.
        System.out.println(month + " " + yr + (numberOfDays == 29 ? " **" : ""));
        printMonth(numberOfDays, startDay);
        System.out.println();
    }
}

您的 printMonth() 方法可以保持不变。

现代Java

这是一个完全不同的观点,供您比较。此代码使用现代 Java 的功能,包括 java.time classes defined in JSR 310, streams, convenient List factory method, and enums.

所有这些代码都包含在一个 .java 文件中,用于定义我们的 CalendarMaker class。请参阅 main 方法作为演示,了解如何使用此 class.

class 有两个成员变量,您可以通过构造函数注入:end-of-line (newline) 字符用于我们生成的文本,Locale 我们(a ) 确定 days-of-week 的顺序,并且 (b) 本地化月份的名称和 day-of-week.

的名称

我们使用 StringBuilder class 通过调用它的 append 方法来构建我们的文本。

尽可能使用特定类型,使您的代码更多 self-documenting, ensure valid values, and provide type-safety. So we start with a list of Year object,适用于当前年份以及之前和之后的年份。

对于每一年,我们循环月份。每个月表示为 YearMonth object。我们通过调用 Month.getDisplayName 来本地化月份的名称。然后我们通过首先本地化 day-of-week 的名称来本地化 day-of-week 列 headers,然后 t运行cating 只取前两个字母。

DayOfWeek 枚举提供 ready-made object 来表示每个 day-of-week.

请注意,我们还本地化了星期几的顺序。在美国,星期日在大多数日历上开始一周。但在欧洲和其他地方,您通常会先看到星期一。我们的代码允许任何 day-of-week 开始一周,以防某些文化规范有其他选择。

A TemporalAdjuster found in the TemporalAdjusters class 确定我们每月网格中的开始日期。然后我们每天增加,每周增加一次。或者,我们禁止显示目标月份之外的日期。

要为每一天生成文本,请使用 DateTimeFormatter。使用 dd 格式模式用零填充 single-digit 数字。要用 SPACE 填充,请使用 ppd

更新:我用来自 LocalDate.datesUntil. Inside we use a ternary operator 的流替换了此代码块中的 for 循环,以抑制目标月份之外的日期。我并不是说这种重写一定更好;作为现代 Java 编程的一个例子,我只是想用流和 lambda 来展示漂亮的语法。

    // 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 );
    }

…变成:

    // 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 );
        }
    }

}

当运行.

|------  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      
…

将语言环境从 Locale.CANADA_FRENCH to Locale.FRANCE 切换,看看我们如何保留法语,但将文化规范从北美切换到欧洲,以星期一 (lundi) 而不是星期日 (dimanche) 开始一周。