ArtsAutosBooksBusinessEducationEntertainmentFamilyFashionFoodGamesGenderHealthHolidaysHomeHubPagesPersonal FinancePetsPoliticsReligionSportsTechnologyTravel
  • »
  • Technology»
  • Computers & Software

[Part 9] Create your own Calendar (Date/Time) library from scratch using Java

Updated on August 29, 2016

Part 9 - A fancy calendar display

In Part 8, we created the methods to add days, months, or years to a given date; addDays(), addMonths(), addYears(). We also created the method nameOfDay() to find out which day of the week a given date falls on. Aside from doing all these computation stuffs using our library, we may find it useful if it can display a calendar based on the date range we will provide.

In this part, we will do the following:

  • Create a new class named "FancyCalendar"
  • A method to display the calendar
  • Methods to create headers and borders
  • Formatting methods including indents

The result of what we're about to create.
The result of what we're about to create.

A fancy calendar

I separated the methods to display a calendar from the "MyCalendar" and created the class "FancyCalendar" just for that purpose. This is so we will not clutter the previous one by putting all the methods there. This also ensures that all methods that will be written in "FancyCalendar" are all for the sake of the display.

Method
Purpose
displayCalendar()
Displays the ASCII calendar.
monthHeader()
Generates headers for each month.
dayHeader()
Generates headers for each day of the week.
line()
Creates a line to separate the headers from the actual days.
monthFiller()
Creates a unique filler for each month.
filler()
Generates the whole template and containers for each day to replace.
formatDay()
Formats the day to strictly have two digits.
indents()
Creates an indentation for every starting date or first day of the month.
fillerValues()
Adds the actual values to the template.
Methods and their purpose.

Month headers

Let's do this from top to bottom. Boxed in blue in the screenshot below are the months where each calendar "block" belongs to. The method we're about to create assumes that the only format for month names is "three letters, all caps". Once we've created several formats for date, we can tweak this to make it more flexible.

For now, we'll create a simple one.

The month headers.
The month headers.

In the method below, we simply initialized the variable named "monthHeaders" with the month name of the date passed to our method with surrounding spaces put each month name to the center of its respective block.

    public String monthHeader(String date){
        String monthHeaders = "           " + myCalendar.getMonth(date) + "           ";

        return monthHeaders;
    }

Day headers

In this method, we initialized the variable "dayHeader" with the value of each day in a week enclosed in two vertical lines and a single space before the first vertical line to separate the blocks from each other.

The day headers.
The day headers.
    public String dayHeader(){
        String dayHeader = " | Su Mo Tu We Th Fr Sa |";

        return dayHeader;
    }

Line

This is just an additional line to separate the headers with the actual values as seen in the screenshot below.

The line separates the headers from the actual day values.
The line separates the headers from the actual day values.
    public String line(){
        String line = " +----------------------+";

        return line;
    }

Why not use a single method instead?

The three previous methods all generate a single line. They can be combined into just one method if you like. Aside from being more flexible, the purpose of creating a single method for each is to make the display more customizable. If for example, we want to give the user a choice to choose his own lines, borders, or format, we can tweak this method to accept a parameter that it will then use to generate a different output based on what the user likes.

Feel free to combine them into a single method if you like.

The month filler

I used the term "fillers" to refer to automatically created containers of each day in a calendar. These containers are just characters that will be replaced eventually with the actual values. Below is a screenshot of what the calendar looks like prior to replacing its values with the month filler and actual values.

Initial form.
Initial form.
    public String monthFiller(String month){
        String monthFiller = "d" + myCalendar.monthID(month) + "x";

        return monthFiller;
    }

Individual fillers

What the monthFiller() method does is take a month value and generate a monthFiller out of that. In this case, we created the variable named "monthFiller". The letter "d" (for day) is used as a prefix. We use the method monthID() from the "MyCalendar" class to get the ID of the month we got (e.g. JAN's month ID is 1). Then we add "x" as a suffix.

This will generate the following values:

 
 
 
 
 
 
 
 
 
 
 
 
 
month
JAN
FEB
MAR
APR
MAY
JUN
JUL
AUG
SEP
OCT
NOV
DEC
monthFiller
d1x
d2x
d3x
d4x
d5x
d6x
d7x
d8x
d9x
d10x
d11x
d12x
Months and their corresponding fillers.

Why use a prefix and suffix?

As you can see in the table above, without either of the two ("d" and "x") enclosing the monthID, some months can automatically be duplicates of each other. From October to December, the monthID starts with a number 1 (OCT = 10, NOV = 11, DEC = 12) and can be mistaken as January (1). This would mess up the calendar once we try to replace the month fillers with the actual values.

The filler() method

The filler() method generates most of the template where the actual day values will be placed. This method uses the monthFiller() method to find out what filler to use for a given month. Below is a screenshot of what it looks like when replaced by the month fillers but not yet the actual value.

Second form.
Second form.

We replace "xx" with a unique filler for each month so that the replaceFirst() method doesn't confuse the "xx" characters from a month with another because we laid out the template line by line. I took a screenshot to show you what I mean.

Confused replaceFirst() is confused. :D
Confused replaceFirst() is confused. :D

Parameters

The filler() method has two parameters:

  • The "fromMonth" variable contains the month of the "fromDate" (e.g. JAN for 01-JAN-2015).
  • The "numOfMonths" variable is the number of months between the two dates, "fromDate" and "toDate" including the two said dates (e.g. 01-JAN-2015 to 31-MAY-2015 = 5). This identifies the scope and is used for the "while" loop.

    public String filler(String fromMonth, int numOfMonths){
        int rows = 6;
        String currentMonth = fromMonth;
        String filler = "";
        String completeFiller = "";

        while (numOfMonths > 0){
            filler = filler + " | xx xx xx xx xx xx xx |";
            filler = filler.replaceAll("xx", monthFiller(currentMonth));
            currentMonth = myCalendar.nextMonth(currentMonth);

            numOfMonths --;
        }

        for (rows = 6; rows > 0; rows --){
            completeFiller = completeFiller + filler + "\n";
        }

        return completeFiller;
    }

While loop (Initial row)

What happens in the "while" loop is boxed in the screenshot below. Using the dates "01-JAN-2015" and "31-MAY-2015" which scopes 5 months, the five blocks with fillers is created. Since we call monthFiller(currentMonth) every time it loops and use the value it returns as replacement for the "xx" value, we get a row with different fillers: d1x, d2x, d3x, d4x, d5x.

In the while loop.
In the while loop.

For loop (Rows)

When the "while" loop has finished and the initial row has been created. The "for" loop runs to repeat the rows 6 times until the whole template is finished. The completed template will then be returned to whoever called the filler() method.

Rows, in the for loop.
Rows, in the for loop.

The formatDay() method

This method makes sure that days 1 - 9 occupies two characters. It simply adds a zero at the beginning if the number of digits (length of the integer value converted to string) is less than 2.

    public String formatDay(int day){
        String formattedDay = String.valueOf(day);

        if (String.valueOf(day).length() < 2)
            formattedDay = "0" + String.valueOf(day);

        return formattedDay;
    }

The indents() method

This method checks how many times it will skip adding values (actual day values) and add blanks (replacing the month filler with " ") instead. Without this method, every beginning of the month would, by default, fall on "Su" or Sunday. The ArrayList contains each day of the week. They are added in an order where their position in the array (index) is also the number of indents.

    public int indents(String currentDate){
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("Su");
        arrayList.add("Mo");
        arrayList.add("Tu");
        arrayList.add("We");
        arrayList.add("Th");
        arrayList.add("Fr");
        arrayList.add("Sa");

        return arrayList.indexOf(myCalendar.nameOfDay(currentDate));
    }

Example

  • Sunday is the beginning of the week and it has the index 0 which means it should immediately add a value and not put blanks.
  • Monday goes after Sunday, since it is added second to the array list and the index starts at 0, its index would be 1. The index number is also the number of indents so it would put one blank or replace the first month filler with " ".

The indents() method is simply used to identify how many times a day should be pushed so that it lands on the correct day of the week.

The fillerValues() method

The fillerValues() method utilizes most of the methods we created earlier. In this method, the indentation is applied, the actual values are supplied, and all the extra characters are removed. This completes the boxed information below.

The result of the fillerValues() method.
The result of the fillerValues() method.

Dissecting the fillerValues() method

The fillerValues() method has three parameters; "filler", "fromDate", and "toDate". The variable "filler" contains the template created using the filler() method. The variables "fromDate" and "toDate" determines the values that will be placed into the template.

    public String fillerValues(String filler, String fromDate, String toDate)

Variables

The list below contains the explanation of each variable found in the code. Reading through each item before discussing the next blocks is necessary to understand how things work.

  1. The number of indents needed for the starting point (fromDate) is assigned to the variable "indents".
  2. The number of days between the two dates is assigned to the variable "numOfDays", 1 is added since the countDaysBetween() method excludes the starting point from the count. Since we will be displaying the values, we will need that value included.
  3. The variable "currentDate", by default, is assigned the value of "fromDate".
  4. The variable "currentMonth" is assigned the value of the month of "currentDate".
  5. The variable "currentDay" is assigned the formatted and converted to string value of the day of "currentDate".
  6. The variable "fillerValues" is assigned the value of "filler" or the completed template without the actual values.

        int indents = indents(fromDate);
        int numOfDays = myCalendar.countDaysBetween(fromDate, toDate) + 1;
        String currentDate = fromDate;
        String currentMonth = myCalendar.getMonth(currentDate);
        String currentDay = formatDay(myCalendar.getDay(currentDate));
        String fillerValues = filler;

Outer while loop

The next blocks of code that will be discussed are executed inside this outer "while" loop. This loop runs until "numOfDays" reaches 0 or reaches the end point (toDate).

while (numOfDays > 0){

//Next blocks that will be discussed.

    numOfDays --;
}

Indents for each month

As the loop goes, the value of "currentDate" increments by a day. Aside from initially assigning a value for "indents" based on the starting point, we also need to identify the number of indents when we switch between months. The "if" statement below checks when the "currentDate" switched to a new month by checking if its day is equivalent to 1. If it is true, the "currentDate" will be passed to the indents() method to identify how many indents it needs.

            if (myCalendar.getDay(currentDate) == 1)
                indents = indents(currentDate);

Adding indents

The "while" loop below replaces a month filler with blank depending on how many indents are required.

            while (indents > 0){
                fillerValues = fillerValues.replaceFirst(monthFiller(currentMonth), "  ");
                indents --;
            }
Every first day of the month falls on a Sunday if indents are not added.
Every first day of the month falls on a Sunday if indents are not added.

Adding the actual values

After adding the indents, the month fillers replaced with blanks will no longer be detected by the replaceFirst() method. This gives an effect of "pushing" the actual values to the correct day of the week. The remaining month fillers will be replaced with the actual day values.

            fillerValues = fillerValues.replaceFirst(monthFiller(currentMonth), currentDay);

Removing extra fillers

The "if" condition below checks for either of the two conditions:

  • If the current month is not equivalent to the month of the next date (tomorrow) or if it's the end of the current month.
  • If the current date is equivalent to the end point (toDate).

If any of these is true, the remaining month fillers will be replaced with blanks.

            if (!currentMonth.equals(myCalendar.getMonth(myCalendar.nextDate(currentDate)))
                    || currentDate.equals(toDate))
                fillerValues = fillerValues.replaceAll(monthFiller(currentMonth), "  ");
The calendar will be cluttered if the extra fillers are not removed.
The calendar will be cluttered if the extra fillers are not removed.

The next three lines below changes the value of "currentDate", "currentMonth", and "currentDay" as it increments by 1 day using the nextDate() method before the loops starts over again.

            currentDate = myCalendar.nextDate(currentDate);
            currentMonth = myCalendar.getMonth(currentDate);
            currentDay = formatDay(myCalendar.getDay(currentDate));

Once the outer "while" loop terminates, the final value of "fillerValues" is returned.

        return fillerValues;

The complete code for the fillerValues() method

    public String fillerValues(String filler, String fromDate, String toDate){
        int indents = indents(fromDate);
        int numOfDays = myCalendar.countDaysBetween(fromDate, toDate) + 1;
        String currentDate = fromDate;
        String currentMonth = myCalendar.getMonth(currentDate);
        String currentDay = formatDay(myCalendar.getDay(currentDate));
        String fillerValues = filler;

        while (numOfDays > 0){
            // identify indents
            if (myCalendar.getDay(currentDate) == 1)
                indents = indents(currentDate);

            // adds indents
            while (indents > 0){
                fillerValues = fillerValues.replaceFirst(monthFiller(currentMonth), "  ");
                indents --;
            }

            // replace with actual values
            fillerValues = fillerValues.replaceFirst(monthFiller(currentMonth), currentDay);

            // removes extra fillers
            if (!currentMonth.equals(myCalendar.getMonth(myCalendar.nextDate(currentDate)))
                    || currentDate.equals(toDate))
                fillerValues = fillerValues.replaceAll(monthFiller(currentMonth), "  ");

            currentDate = myCalendar.nextDate(currentDate);
            currentMonth = myCalendar.getMonth(currentDate);
            currentDay = formatDay(myCalendar.getDay(currentDate));

            numOfDays --;
        }

        return fillerValues;
    }

The displayCalendar() method

In order to display the calendar, we must use the methods we created together. The picture below shows every part of the calendar with labels of which method is in-charge of the creation.

Fancy calendar dissected.
Fancy calendar dissected.

Variables

The displayCalendar() method has two parameters; "fromDate" and "toDate". The variables used for the displayCalendar() method and the explanation for each are itemized below.

  1. The "numOfMonths" variable is assigned the number of months between the two dates, 1 is added since the countMonthsBetween() method excludes the starting point by default. We need it to be included since we want to display it.
  2. The "currentDate" variable is assigned the value of the starting point "fromDate".
  3. The "fromMonth" variable is assigned the month of the starting point "fromDate".
  4. The variables "monthHeader", "dayHeader", and "line" are empty strings.
  5. The variable "filler" is assigned the value or template generated from using the filler() method with the parameters "fromDate" and "numOfMonths".
  6. The variable "fillerValues" is assigned the value returned from invoking the fillerValues() method with the parameters "filler", "fromDate", and "toDate".

        int numOfMonths = myCalendar.countMonthsBetween(fromDate, toDate) + 1;
        String currentDate = fromDate;
        String fromMonth = myCalendar.getMonth(fromDate);
        String monthHeader = "", dayHeader = "", line = "";
        String filler = filler(fromMonth, numOfMonths);
        String fillerValues = fillerValues(filler, fromDate, toDate);

The monthHeaders, dayHeaders, and lines

The "while" loop below generates the headers for each "block" or month. Headers are added depending on the number of months. In the screenshot after the code below, the parts generated during this loop are boxed in purple and separated for each month. Since we used the dates "01-JAN-2015" to "31-MAY-2015", it gave us the "numOfMonths" value of 5.

        while (numOfMonths > 0){
            monthHeader = monthHeader + monthHeader(currentDate);
            dayHeader = dayHeader + dayHeader();
            line = line + line();

            currentDate = myCalendar.addMonths(currentDate, 1);
            numOfMonths --;
        }
Parts added inside the loop.
Parts added inside the loop.

Displaying all the values

After the "while" loop terminates, the headers, lines, and actual values are displayed in order. Try commenting out each line to see what happens.

        System.out.println(monthHeader);
        System.out.println(dayHeader);
        System.out.println(line);
        System.out.println(fillerValues);

The complete code for the displayCalendar() method

    public void displayCalendar(String fromDate, String toDate){
        int numOfMonths = myCalendar.countMonthsBetween(fromDate, toDate) + 1;
        String currentDate = fromDate;
        String fromMonth = myCalendar.getMonth(fromDate);
        String monthHeader = "", dayHeader = "", line = "";
        String filler = filler(fromMonth, numOfMonths);
        String fillerValues = fillerValues(filler, fromDate, toDate);

        while (numOfMonths > 0){
            monthHeader = monthHeader + monthHeader(currentDate);
            dayHeader = dayHeader + dayHeader();
            line = line + line();

            currentDate = myCalendar.addMonths(currentDate, 1);
            numOfMonths --;
        }

        System.out.println(monthHeader);
        System.out.println(dayHeader);
        System.out.println(line);
        System.out.println(fillerValues);
    }

Tests

Now that the "FancyCalendar" class is finished, you can pass it different date ranges as you wish. Take note that the initial point we used in the "MyCalendar" class for identifying the name of day was January 1, 2015 so if you want to use dates that are earlier than that, feel free to adjust that first.

The sample code below displays the calendar for the year 2015.

public class SampleClass {
    public static void main(String[] args) {
        FancyCalendar fancyCalendar = new FancyCalendar();
        fancyCalendar.displayCalendar("01-JAN-2015", "31-DEC-2015");
    }
}

End of Part 9

Yey! We can display a calendar now. You can tweak the code to do whatever you want. Try using the command-line to run "SampleClass", it looks nice as long as the months fit. You can also write additional parameters for the methods to make a more customizable calendar or use different character templates.

For example, int variables called "rows" and "columns" to customize the display. If rows = 4 and columns = 3, the calendar will be displayed in a 3x4 format where only three months per row will be displayed.

A sample run using a command-line with a different format.
A sample run using a command-line with a different format.

Most of the codes are already uploaded to the repository on GitHub before they are discussed here.

© 2015 Joann Mistica

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.