Search code examples
javalocaldate

Determine what day of the week the first of that month falls on and store the day numbers starting at that position


I am trying to populate an array with the date in there,

For example, this month August, it should show

Sunday Monday Tuesday Wednesday  ...  Saturday
null    1        2        3      ...     6
...     ...      ...     ...     ...     ...

It is working with this:

import java.time.LocalDate;
import java.util.Scanner;

public class buildCalendar {
    String[] calendar = new String[48];
    private final int firstDayOfMonth = 1;
    Scanner sc = new Scanner(System.in);
    int month, year;

    public buildCalendar() {
        System.out.println("Month: ");
        month = sc.nextInt();
        sc.nextLine();
        System.out.println("Year: ");
        year = sc.nextInt();

        newCalendar(month, year);
    }

    public void newCalendar(int month, int year) {
        LocalDate inputDate = LocalDate.of(year, month, firstDayOfMonth);
        int dayOfWeek = inputDate.withDayOfMonth(firstDayOfMonth).getDayOfWeek().getValue();


        // populate String array
        int i = 0;

        while (i <= calendar.length) {      
            if (i == dayOfWeek) {       
                for (int j = 0; j < inputDate.lengthOfMonth(); j++) {
                    calendar[i] = Integer.toString(i);
                    i++;                                    
                }
            }
            i++;
        }

        for (String string : calendar) {
            System.out.println(string);
        }
    }

}

But if I decide to change the month to January 2016, it will be wrong.

Sunday Monday Tuesday Wednesday  Thursday Friday Saturday
null    null   null     null      null     5       6
...     ...      ...     ...     ...    ...     ...

5 is suppose to be 1. How would you guys change this around?


Solution

  • Because I was bored, I decided to write a full class for this, but slightly different.

    Instead of a String[48] array for the day numbers, I create an int[weeks][7] 2D array for the weeks of the month, where weeks is between 4 and 6, depending on the number of days in the month, and the weekday of the first day of the month. A value of 0 is the "blank" day.

    The code has been enhanced to be Locale aware, i.e. to determine whether a week starts on Sunday (e.g. US) or Monday (e.g. most of Europe).

    I also adding a nice print() method, which will print the month in the given Locale, with automatic sizing of the output.

    The core input is a YearMonth identifying the month, and a Locale. The base logic similar to your code is these 7 lines:

    this.firstWeekdayOfWeek = WeekFields.of(this.locale).getFirstDayOfWeek();
    DayOfWeek firstWeekdayOfMonth = this.yearMonth.atDay(1).getDayOfWeek();
    int startWeekDay = (firstWeekdayOfMonth.getValue() - this.firstWeekdayOfWeek.getValue() + 7) % 7;
    int endWeekDay = startWeekDay + this.yearMonth.lengthOfMonth();
    this.weekdays = new int[(endWeekDay + 6) / 7][7];
    for (int weekDay = startWeekDay, dayOfMonth = 1; weekDay < endWeekDay; weekDay++, dayOfMonth++)
        this.weekdays[weekDay / 7][weekDay % 7] = dayOfMonth;
    

    As you can see, the for loop uses 2 iterator variables, one for the position in the 2D array (weekDay), and one for the day number to be assigned (dayOfMonth).

    This is how I handled the problem you seem to have.

    Here is the full class:

    public final class CalendarMonth implements Comparable<CalendarMonth> {
        private final YearMonth yearMonth;
        private final Locale    locale;
        private final DayOfWeek firstWeekdayOfWeek;
        private final int[][]   weekdays;
    
        public static CalendarMonth of(int year, int month) {
            return new CalendarMonth(YearMonth.of(year, month), Locale.getDefault());
        }
        public static CalendarMonth of(int year, int month, Locale locale) {
            Objects.requireNonNull(locale, "locale");
            return new CalendarMonth(YearMonth.of(year, month), locale);
        }
        public static CalendarMonth of(int year, Month month) {
            return new CalendarMonth(YearMonth.of(year, month), Locale.getDefault());
        }
        public static CalendarMonth of(int year, Month month, Locale locale) {
            Objects.requireNonNull(locale, "locale");
            return new CalendarMonth(YearMonth.of(year, month), locale);
        }
        public static CalendarMonth of(YearMonth yearMonth) {
            Objects.requireNonNull(yearMonth, "yearMonth");
            return new CalendarMonth(yearMonth, Locale.getDefault());
        }
        public static CalendarMonth of(YearMonth yearMonth, Locale locale) {
            Objects.requireNonNull(yearMonth, "yearMonth");
            Objects.requireNonNull(locale, "locale");
            return new CalendarMonth(yearMonth, locale);
        }
    
        private CalendarMonth(YearMonth yearMonth, Locale locale) {
            this.yearMonth = yearMonth;
            this.locale = locale;
    
            // Build weekdays array
            this.firstWeekdayOfWeek = WeekFields.of(this.locale).getFirstDayOfWeek();
            DayOfWeek firstWeekdayOfMonth = this.yearMonth.atDay(1).getDayOfWeek();
            int startWeekDay = (firstWeekdayOfMonth.getValue() - this.firstWeekdayOfWeek.getValue() + 7) % 7;
            int endWeekDay = startWeekDay + this.yearMonth.lengthOfMonth();
            this.weekdays = new int[(endWeekDay + 6) / 7][7];
            for (int weekDay = startWeekDay, dayOfMonth = 1; weekDay < endWeekDay; weekDay++, dayOfMonth++)
                this.weekdays[weekDay / 7][weekDay % 7] = dayOfMonth;
        }
    
        public void print() {
            // Get day names and determine width of longest name
            String[] dayName = new String[7];
            for (int i = 0; i < 7; i++)
                dayName[i] = this.firstWeekdayOfWeek.plus(i).getDisplayName(TextStyle.FULL, this.locale);
            int width = Arrays.stream(dayName).mapToInt(String::length).max().getAsInt();
    
            // Print month name
            String title = this.yearMonth.format(DateTimeFormatter.ofPattern("MMMM uuuu", this.locale));
            System.out.println(rightTrim(center(title, width * 7 + 6)));
    
            // Print day names
            StringBuilder line = new StringBuilder();
            for (int i = 0; i < 7; i++)
                line.append(center(dayName[i], width)).append(' ');
            System.out.println(rightTrim(line.toString()));
    
            // Print day numbers
            for (int[] week : this.weekdays) {
                line.setLength(0);
                for (int i = 0; i < 7; i++)
                    line.append(center((week[i] == 0 ? "" : String.format("%2d", week[i])), width)).append(' ');
                System.out.println(rightTrim(line.toString()));
            }
        }
        private static String center(String text, int width) {
            if (text.length() >= width)
                return text;
            char[] buf = new char[width];
            Arrays.fill(buf, ' ');
            System.arraycopy(text.toCharArray(), 0, buf, (width - text.length() + 1) / 2, text.length());
            return new String(buf);
        }
        private static String rightTrim(String text) {
            return text.replaceFirst("\\s+$", "");
        }
    
        @Override
        public String toString() {
            return this.yearMonth.toString();
        }
        @Override
        public int compareTo(CalendarMonth that) {
            int cmp = this.yearMonth.compareTo(that.yearMonth);
            if (cmp == 0)
                cmp = this.locale.toLanguageTag().compareTo(that.locale.toLanguageTag());
            return cmp;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj instanceof CalendarMonth) {
                CalendarMonth other = (CalendarMonth) obj;
                return (this.yearMonth.equals(other.yearMonth) &&
                        this.locale.equals(other.locale));
            }
            return false;
        }
        @Override
        public int hashCode() {
            return Objects.hash(this.yearMonth, this.locale);
        }
    }
    

    Test

    CalendarMonth.of(2016, Month.AUGUST).print();
    CalendarMonth.of(2016, Month.JANUARY).print();
    CalendarMonth.of(2016, Month.JANUARY, Locale.FRANCE).print();
    

    Output

                                 August 2016
      Sunday    Monday   Tuesday  Wednesday  Thursday   Friday   Saturday
                   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 2016
      Sunday    Monday   Tuesday  Wednesday  Thursday   Friday   Saturday
                                                           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
    
                             janvier 2016
      lundi    mardi  mercredi   jeudi  vendredi  samedi  dimanche
                                            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
    

    As you can see, it adjusts to a week starting on Monday, which happens to reduce the number of weeks from 6 to 5.