2
\$\begingroup\$

This is an implementation of mine to an exercise found in the book Core Java SE 9 for the Impatient. Goal is basically to print a monthly calendar by accepting month and year information from the user.

Here is a sample run in my local:

java App 1984 4
                        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  

Here is my implementation, which I am happy to hear your criticisms on:

import java.time.LocalDate;
import java.time.Month;
import java.time.DayOfWeek;
import java.lang.StringBuilder;
import java.util.stream.IntStream;

class App {

    public static final String SEPERATOR = "\t";

    public static void main(String[] args) {
        // First day of the month that we will be printing - this is our reference date
        final LocalDate referenceDate = LocalDate.of(Integer.valueOf(args[0]), Integer.valueOf(args[1]), 1);

        // Temporary instance we will be using, initially set to our reference date
        // We will be assigning next dates to this reference
        LocalDate nextDate = referenceDate;

        // Temporary instance we will be using to print a row
        // Row will start with this value
        int weekStart = nextDate.getDayOfMonth();

        // offset might be gt 0 for the first printed week (imagine 1st of a month is wednesday..)
        int offset = nextDate.getDayOfWeek().getValue() - 1;

        // We need to do this as long as the date we are processing is in the same month with our reference date
        while (nextDate.getMonth() == referenceDate.getMonth()) {
            // Print a week if we hit a SUNDAY or if the next date would be in a different month..
            if (nextDate.getDayOfWeek() == DayOfWeek.SUNDAY || nextDate.plusDays(1).getMonth() != referenceDate.getMonth()) {
                printWeek(offset, weekStart, nextDate.getDayOfMonth());
                offset = 0; // offset will always be zero after printing the first week in a month
                weekStart = nextDate.getDayOfMonth() + 1;
            }
            // keep incrementing
            nextDate = nextDate.plusDays(1);
        }
    }

    static void printWeek(int offset, int weekStart, int weekEnd) {
        StringBuilder sb = new StringBuilder();
        IntStream.range(0, offset).forEach(i -> sb.append(SEPERATOR));
        IntStream.rangeClosed(weekStart, weekEnd).forEach(i -> sb.append(i).append(SEPERATOR));
        System.out.println(sb);
    }
}

Here is another approach I have taken, the output is not exactly the same but can be made same very easily:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class App {
    static final List<int[]> weeks = new ArrayList<>();

    public static void main(String[] args) {
        final LocalDate firstDay = LocalDate.of(Integer.valueOf(args[0]), Integer.valueOf(args[1]), 1);

        int[] weekDays = new int[7];
        LocalDate aDate = firstDay;
        while (aDate.getMonth() == firstDay.getMonth()) {
            weekDays[aDate.getDayOfWeek().getValue() - 1] = aDate.getDayOfMonth();
            if (aDate.getDayOfWeek() == DayOfWeek.SUNDAY || aDate.plusDays(1).getMonth() != firstDay.getMonth()) {
                weeks.add(weekDays);
                weekDays = new int[7];
            }
            aDate = aDate.plusDays(1);
        }

        weeks.forEach(week -> System.out.println(Arrays.toString(week)));
    }
}

where the output is:

[0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0]

I came up with a 3rd option, leaving here just for future reference:

import java.time.LocalDate;
import java.util.stream.IntStream;
import java.lang.StringBuilder;

class App {
    public static void main(String[] args) {
        LocalDate ld = LocalDate.of(Integer.valueOf(args[0]), Integer.valueOf(args[1]), 1);

        int offset = ld.getDayOfWeek().getValue() - 1;
        int monthLength = ld.lengthOfMonth();

        StringBuilder sb = new StringBuilder();
        IntStream.rangeClosed(-offset, monthLength).forEach(i -> {
            if (i > 0) sb.append(i);
            sb.append("\t"); 
            if ((i + offset) % 7 == 0) sb.append("\n");
        });

        System.out.println(sb);
    }
}
\$\endgroup\$
3
\$\begingroup\$

First thing I'd like to say is get into the habit of putting your code into functions outside of Main. Use Main to run the functions. In this case, I would recommend passing in PrintStream and LocalDate objects.

You seem to be doing a lot of work with the LocalDate fields. Basically all the printout is is a list of numbers in a tabular format. All you need is the weekday of the first day of the month to get the offset. Use a loop to get the numbers and when (weekday - 1 + loop counter) % 7 equals 0 print a line break.

Using printf to format the numbers with leading 0's, makes the print out much easier to read.

Typically when printing out the month one would expect to see the name of the month and the year, as well as the days of the week. The days of the week can be pulled from the DayOfWeek enum.

Here's one way the function could look like:

private static void printMonth(PrintStream out, LocalDate date){
    date = date.minusDays(date.getDayOfMonth()-1);
    var month = date.getMonth();
    int days = month.length(date.isLeapYear());
    int firstWeekDay = date.getDayOfWeek().getValue()-1;
    out.printf("%1$s, %2$d\n",month.toString(),date.getYear());        
    for(var weekday :DayOfWeek.values()){
        out.printf("%s\t",weekday.name().substring(0, 2));
    }
    out.println();
    for(int i = 0; i < firstWeekDay;++i){
        out.print("  \t");
    }
    for(int day = 1; day <= days;++day){
        out.printf("%02d\t",day );
        if((day + firstWeekDay)% 7 ==0){
            out.println();
        }
    }
    out.println();
}

The print out looks like this:

NOVEMBER, 2018
MO  TU  WE  TH  FR  SA  SU  
            01  02  03  04  
05  06  07  08  09  10  11  
12  13  14  15  16  17  18  
19  20  21  22  23  24  25  
26  27  28  29  30
\$\endgroup\$
4
  • \$\begingroup\$ I think your suggestion is pretty much same with my 3rd implementation. \$\endgroup\$ Nov 26 '18 at 13:49
  • \$\begingroup\$ Except it doesn't store all the days, just prints them out, and includes more formatting \$\endgroup\$
    – user33306
    Nov 26 '18 at 13:53
  • \$\begingroup\$ Well yes but you are changing the requirements. No one asked for MO, TU etc and in which calendar did you see 01 instead of 1? And I do not store all days either. (Store where anyway?) \$\endgroup\$ Nov 26 '18 at 13:56
  • \$\begingroup\$ I covered the extra information in my answer as an improvement. Adding the days to a StringBuilder is storing them. \$\endgroup\$
    – user33306
    Nov 26 '18 at 14:01
-1
\$\begingroup\$

I made yet another implementation, this one lets you define which weekday you want to consider as week start:

class Calendar {
    public List<CalendarWeek> getWeeks(YearMonth yearMonth, DayOfWeek weekStart) {
        ArrayList<CalendarWeek> calenderweeks = new ArrayList<>();

        int monthStart = LocalDate.of(yearMonth.getYear(), yearMonth.getMonth(), 1).getDayOfWeek().getValue();
        int calendarWeekStart = weekStart.getValue();

        int offset = monthStart - calendarWeekStart;
        if (offset < 0) {
            offset = offset + 7;
        }

        int start = 1;
        int end = 8 - (start + offset);

        int monthLength = yearMonth.lengthOfMonth();
        do {
            calenderweeks.add(new CalendarWeek(offset, start, end));
            start = end + 1;
            end = monthLength < start + 6 ? monthLength : start + 6;
            offset = 0;
        } while (start < monthLength);

        return calenderweeks;
    }
}

class CalendarWeek {
    int offset;
    int start;
    int end;

    public CalendarWeek(int offset, int start, int end) {
        this.offset = offset;
        this.start = start;
        this.end = end;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < offset; i++) {
            stringBuilder.append("    ");
        }

        for (int i = start; i < end + 1; i++) {
            stringBuilder.append(String.format("  %2d", i));
        }

        return stringBuilder.toString();
    }
}

Sample run:

class App {
    public static void main(String[] args) {
        List<CalendarWeek> weeks;

        weeks = new Calendar().getWeeks(YearMonth.of(2019, 2), DayOfWeek.SATURDAY);
        weeks.forEach(System.out::println);

        System.out.println("");

        weeks = new Calendar().getWeeks(YearMonth.of(2019, 2), DayOfWeek.SUNDAY);
        weeks.forEach(System.out::println);

        System.out.println("");

        weeks = new Calendar().getWeeks(YearMonth.of(2019, 2), DayOfWeek.MONDAY);
        weeks.forEach(System.out::println);
    }
}

Sample output

                           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

                       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

                   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
\$\endgroup\$
0
-2
\$\begingroup\$

Here is a different possible approach:

import java.time.*;

class Cal {
    public static void main(String[] args) {
        final LocalDate firstDate = LocalDate.of(Integer.valueOf(args[1]), Integer.valueOf(args[0]), 1);

        int firstDayOffset = firstDate.getDayOfWeek().getValue() - 1;
        for (int i = 0; i < firstDayOffset; i++) {
            System.out.print("   ");
        }

        LocalDate aDate = firstDate.plusDays(0);
        while (aDate.getMonth() == firstDate.getMonth()) {
            System.out.printf("%2d ",aDate.getDayOfMonth());
            if ((aDate.getDayOfMonth() + firstDayOffset) % 7 == 0) {
                System.out.println("\n");
            } 
            aDate = aDate.plusDays(1);
        }

        System.out.println("\n");
    }
}

A sample run: javac Cal.java; java Cal 12 2018;

                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 
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.