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

时间:2019-05-20 01:27:34

标签: java calendar coding-style java-calendar

已经有一个初始程序,我使代码尽可能短且精确。但是经过检查,似乎未来两年仍会打印2018,而我预计是2019和2020。如何动态打印显示2018、2019和2020的年份。可能是我的代码中缺少一些迭代。

还可以随意批评我的代码,也可以通过尽可能使用Calendar 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("%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

2 个答案:

答案 0 :(得分:3)

现代Java

这里是一个完全不同的看法,供您比较。这段代码使用了现代Java的功能,包括java.timeJSR 310中定义的streams类,便捷的List factory方法和enums

所有这些代码都包含在单个.java文件中,以定义我们的CalendarMaker类。有关如何使用此类的示例,请参见main方法的演示。

该类具有两个通过构造函数注入的成员变量:end-of-line (newline)字符(在我们的结果文本中使用),以及Locale,我们通过该字符确定星期几,以及(b)本地化月份的名称和星期几的名称。

我们使用StringBuilder类通过调用其append方法来构建文本。

请尽可能使用特定类型,以使代码更self-documenting,确保有效值并提供type-safety。因此,我们从当前年份的Year对象列表以及上一年和下一年开始。

每年,我们循环显示月份。每个月都表示为YearMonth对象。我们通过调用Month.getDisplayName来本地化月份的名称。然后,通过首先本地化星期几的名称,然后截断以仅取前两个字母,来本地化星期几的列标题。

DayOfWeek枚举提供了现成的对象来表示每周的每一天。

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

TemporalAdjuster类中找到的TemporalAdjusters确定了我们每月网格中开始日期的日期。然后,我们以每周的块为单位逐日增加。 (可选)我们取消显示目标月份以外的日期。

要为每天的数字生成文本,请使用DateTimeFormatter。使用dd的格式化模式将零填充数字。要使用空格键,请使用ppd

更新:我用来自LocalDate.datesUntil的流替换了这段代码中的for循环。在内部,我们使用ternary operator来隐藏目标月份以外的日期。我并不是说这种重写一定更好。我只是想用stream&lambda展示漂亮的语法,以现代Java编程为例。

    // 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更改为Locale.FRANCE,以了解如何保留法语,但将文化规范从北美更改为欧洲,从而从星期一(lundi)而不是星期日(dimanche)开始这一周。

答案 1 :(得分:0)

要更正您当前使用的代码,请不要重复使用36个月以上的时间来获得3年的日历。无法使用 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()方法可以保持不变。