ArtsAutosBooksBusinessEducationEntertainmentFamilyFashionFoodGamesGenderHealthHolidaysHomeHubPagesPersonal FinancePetsPoliticsReligionSportsTechnologyTravel
  • »
  • Technology»
  • Computers & Software

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

Updated on April 1, 2015

Part 6 - Previous issues and in-between

After Part 1 - Part 5, you've probably noticed some flaws in the methods we've written. My famous line from Part 1 was "We will proceed to fixing any problems we may encounter and improve the code as we go." - Disclaimer. We will fix those flaws but not permanently and not in a perfect way. Since we'll be adding more methods until the library is finished (and we're not there yet).

In this part, we will do the following:

  • Add a conditional statement for getDay(), getMonth(), and getYear() methods to accept incomplete dates.
  • Create a quick formatting method to address the issue of single digit days.
  • Add formatting to the value returned by the nextDate() method.
  • Double the parameters, double the fun!
  • A method to count the number of days between two dates.

I recently downloaded the IntelliJ IDEA Community edition (free). I might stick to it and replace Eclipse.
I recently downloaded the IntelliJ IDEA Community edition (free). I might stick to it and replace Eclipse. | Source

The perfect code, the impossible

Why are we not fixing these flaws as soon as we've identified them? Well, flaws are discovered and sometimes "happen" on different times.

I can give you three:

  1. Before writing the code
  2. After writing and during tests
  3. As it's being used

We'll be wasting our time perfecting our methods because some future flaws are obvious and predictable before writing the code, some are discovered only after they are written and tested, and most when they are actually being used. Right now, we have to focus first on how to make things work and how to fix the errors.

Who wrote this thing?!

In the previous parts, you may have noticed that every time we invoke a method, we always pass the full date (e.g. 01-JAN-2015) and this is annoying. I annoyed myself as I write and test it, too but I had to make a point somewhere.

Remember that most of our methods start off by calling the following methods:

  • getDay()
  • getMonth()
  • getYear()

Method definition of getDay(), getMonth(), and getYear().
Method definition of getDay(), getMonth(), and getYear().

Methodception

This is to ensure that we get the correct value for day, month, and year which are then processed by other methods depending on what we want to do. An example is getting the next date where we use the nextDate() method but inside this method are invocations of other methods that do very specific tasks.

Method calls for nextDate().
Method calls for nextDate().

Adding a conditional statement

String[] splitDate = date.split("-");

For the getDay(), getMonth(), and getYear() methods, we split the date into three separate values and assign these values to the variables for day, month, and year. This means that we are limited to passing a complete date to these methods and since we use the delimiter "-", we are also limited to the format "DD-MON-YYYY".

We wouldn't touch the many different types of format just yet but for now, we'll add conditional statements for each method so we can pass an incomplete date and not get an ArrayIndexOutOfBoundsException.

The three methods should look like below:

    public int getDay(String date){
        int day = 0;
        String[] splitDate = date.split("-");

        if (date.contains("-"))
            day = Integer.parseInt(splitDate[0]);
        else
            day = Integer.parseInt(date);

        return day;
    }

    public String getMonth(String date){
        String month = date;
        String[] splitDate = date.split("-");

        if (date.contains("-"))
            month = splitDate[1];

        return month;
    }

    public int getYear(String date){
        int year = 0;
        String[] splitDate = date.split("-");

        if (date.contains("-"))
            year = Integer.parseInt(splitDate[2]);
        else
            year = Integer.parseInt(date);

        return year;
    }

In the code above, we simply added a condition that checks whether the argument passed to the method has a "-" character. If it does, then we will split the date into three parts and return only the value that we need. If it does not, then we simply return whatever we got.

You can test this using the code below, the ArrayIndexOutOfBoundsException should no longer occur.

SampleClass

public class SampleClass {

    public static void main(String[] args) {
        MyCalendar myCalendar = new MyCalendar();

        System.out.println("Day for '03-JAN-2015': " + myCalendar.getDay("03-JAN-2015"));
        System.out.println("Day for '03': " + myCalendar.getDay("03"));
        System.out.println("Month for '03-JAN-2015': " + myCalendar.getMonth("03-JAN-2015"));
        System.out.println("Month for 'JAN': " + myCalendar.getMonth("JAN"));
        System.out.println("Year for '03-JAN-2015': " + myCalendar.getYear("03-JAN-2015"));
        System.out.println("Year for '2015': " + myCalendar.getYear("2015"));
    }
}
No more exceptions.
No more exceptions.

What if I input the wrong value?

What if you pass the value 2015 to the getDay() method? Since it does not contain a "-" character, the value itself will be returned and our method has no way to verify if this is a day, month, or year. The value 2015 will be considered as "day" simply because it was passed to the getDay() method.

Why you should not worry

The truth is: you will not input the value.

Most of the methods in our "MyCalendar" class are not supposed to be public or visible to the users. These methods are used by other visible methods inside the class and were only written to do very specific tasks so we wouldn't have to re-write the code into every method that needs them. Three of these "hidden" methods are the getDay(), getMonth(), and getYear() methods.

This prevents the user from passing an invalid value to the methods. The complete date is still required on the user side except this time, the values are chopped into three parts for all the other methods but are still associated with each other.

For example, 28 is an individual value of "day" but in order to get the next day, we need to know what month it is. If the month is "FEB" then we need to know what year it is. If the year is 2015 then the next day is 1 (01-MAR-2015) because 2015 is not a leap year so it does not have a 29th day.

If it's not visible, why should we bother?

This may not make any difference to simply splitting the complete date but it will make sense in the next methods we will create. If you highlight the name of these methods, right-click, and click "Find usages". You will see where they are being used or invoked. As you can see in the screenshots below, they are used in a lot of visible methods in the "MyCalendar" class alone. These three methods are critical to the output of all other methods that the users can use.

Usages of getDay() method.
Usages of getDay() method.
Usages of getMonth() method.
Usages of getMonth() method.
Usages of getYear() method.
Usages of getYear() method.

A quick formatting method

I have a confession to make. Remember when I said in part 5 that the results of the nextDate() method was right? Well, I lied just a little bit but I'm confessing now so don't hate me. It was for your own good. Let me show you the screenshot of those results again.

The result after running the samples for the nextDate() method.
The result after running the samples for the nextDate() method. | Source

Did you notice something?

The value for day is not the same as we've entered it. The results returned for numbers 1 - 9 are always single digit or without the zero prefix. This is because the type we use for all "day" methods is int. This cuts off the zero prefix because the value is treated as an integer and it cannot make sense out of the zero prefix unlike if the data type is string where all characters are preserved.

Why did we not use string instead?

It is a lot easier to compute using integers because well, integers are numbers. If we declared day as a string, we would have to convert it to an integer every time we make computations and it will lose the preceding zero anyway. After that, we'll have to convert that integer into a string again and add a zero prefix before we return it.

That's just too much work so we'll stick to an int data type and since we'll be introducing different formats in the next parts, let's create a quick formatting method that simply adds a zero at the beginning.

The formatDate() method

    public String formatDate(String date){
        String formattedDate = date;

        if (date.length() < 11)
            formattedDate = "0" + date;

        return formattedDate;
    }

Fixed length

In the code above, we defined a method named formatDate(). We declared a variable called "formattedDate" and set its initial value to the value of the "date" parameter. Then we wrote an "if" statement that checks whether the length of the "date" parameter is less than 11. Currently, we only have one format, "DD-MON-YYYY" and counting the number of characters would result to 11. The number of characters for month is fixed with three letters, the year will always be four digits for a very long time.

The only value that changes in the number of digits is the day so every time the length of the date passed to the method is less than 11, it assumes that the value of day is the culprit for this shortcoming and it adds a zero at the beginning. That value is then assigned to the variable called "formattedDate".

Notice that I did not have to put an else statement because the value of "formattedDate" has been initialized with the default value of the "date" or whatever is passed to the method and will only change if it satisfies the "if" condition.

In-between

I guess the screenshots above revealed the methods we're about to create. In Part 1 to Part 5, we became comfortable with using only one parameter per method or a single date. Now comes the question "How do I know the difference between two dates?" It could be how many days between two dates, how many months, how many years. How many days from now until your birthday?

For that purpose, we will create three methods:

Method
Purpose
countDaysBetween()
Returns the number of days between two dates.
countMonthsBetween()
Returns the number of months between two dates.
countYearsBetween()
Returns the number of years between two dates.
Methods for counting.

Counting the number of days between two dates

Below is the definition of the method countDaysBetween(). It takes two parameters; "fromDate" and "toDate" where "fromDate" is the starting point and "toDate" is the end point. The reason why we fixed the format of the date first is that we have to get the dates in their correct and exact format because we will be comparing it to the other date.

Here's how the countDaysBetween method works:

Line 1
It receives two parameters; "fromDate" and "toDate".

Line 2
It sets the default value of "daysBetween" to zero.

Line 4
It compares the two strings; "fromDate" and "toDate" to check if they are not the same. If this is true (if they are not equal), the value of the "daysBetween" variable is incremented and the value of "fromDate" is replaced with fromDate + 1 or the next day. This goes on and on and until the condition is false (fromDate and toDate becomes equal). If the condition is false in the first place, the default value of "daysBetween" will be left unchanged.

Line 9
Returns the value of "daysBetween".

    public int countDaysBetween(String fromDate, String toDate){
        int daysBetween = 0;

        while (!fromDate.equals(toDate)){
            daysBetween ++;
            fromDate = nextDate(fromDate);
        }

        return daysBetween;
    }

Modification for the nextDate() method

Now, here's the crucial part. In order to make sure that the comparison works all the time, we need to use the formatDate() method inside the nextDate() method. This is to ensure that when we invoke the nextDate() method inside the "while" loop, the result that will be sent back to us is in the correct format. This is VERY important because that result becomes the new value of the "fromDate" variable which is compared to the value of the "toDate" variable by each loop. If the value of "fromDate" happens to have a day between 1 - 9 and the zero prefix is not there, the condition in the while loop will still be true or "fromDate" and "toDate" will not be considered equal.

Example

fromDate = "01-FEB-2015"
toDate = "02-FEB-2015"

After nextDate(fromDate), "fromDate" becomes "2-FEB-2015". This should terminate the loop but since they are not exactly equal because "toDate" has 02 and "fromDate" only has 2, the loop will keep going even after it surpassed toDate's value "02-FEB-2015". It will go on forever because there is no way to go back, the days, months, and years will just continue to increment and there's no going back to February 2, 2015.

WARNING!

You can try using the method countDaysBetween() with the two parameters in the example above to see what I'm talking about but since you will not really get a feedback on what's happening except a blank console as your program runs forever, you can add a "System.out.println(fromDate);" inside the "while" loop so you can see how many times "fromDate" transforms into different dates but never really reach its goal. A word of warning, though. This is an infinite loop and may crash. Click the "Stop" square button in the console to stop the program.

No infinite loop?

If you're lucky, this method can still work if the other end (toDate) has a day that originally has two digits or numbers between 10 - 31 or if "fromDate" itself contains these numbers as a value for day. That way, the loop will still terminate at some point but let's not risk that. After all, it takes a very small amount of modification in the nextDate() method.

In bold below is the change. We just invoke the formatDate() on the variable to be returned (nextDate) to make sure it is formatted correctly before our countDaysBetween() method receives it and assigns it to the "fromDate" variable.

return formatDate(nextDate);

Your new nextDate() method should look like the code below:

nextDate()

    public String nextDate(String date){
        String nextDate = "";

        if (nextDay(date) != 1){
            nextDate = nextDay(date) + "-" + getMonth(date) + "-" + getYear(date);
        }
        else if (nextDay(date) == 1 & monthID(date) != 12){
            nextDate = "1" + "-" + nextMonth(date) + "-" +  getYear(date);
        }
        else
            nextDate = "1" + "-" + nextMonth(date) + "-" + nextYear(date);

        return formatDate(nextDate);
    }

You can use the code below to test if the results are correct.

        System.out.println("Number of days from 11-MAR-2013 to 02-JUN-2013 is " + myCalendar.countDaysBetween("11-MAR-2013", "02-JUN-2013"));
        System.out.println("Number of days from 01-FEB-2015 to 02-FEB-2015 is " + myCalendar.countDaysBetween("01-FEB-2015", "02-FEB-2015"));

You should get something like this.

Results for countDaysBetween().
Results for countDaysBetween().

You can count it manually just to check if it's correct. The days between March 11, 2013 and June 2, 2013 is quite a gap so I highlighted the dates in my made-up calendar below:

I was bored. Don't judge me.
I was bored. Don't judge me.

End of Part 6

We fixed a few flaws in this part. That should get us ready for the next methods we're about to make. I wanted to include all the six methods here but this article has gotten too long and I don't want the readers to be intimidated by the length of a single part since I try to elaborate as much as I can so the information would be easier to digest.

In Part 7, the five remaining methods will be discussed. The other two counting methods; countMonthsBetween() and countYearsBetween(). The three display methods; daysBetween(), monthsBetween(), and yearsBetween().


Read Part 7

Look at all the activities I give.
Look at all the activities I give. | Source

GitHub repository

I have not done much update for a long time because I was stuck in the process of leaving my previous job. I'm also learning a few other languages; Ruby, Python, Perl and refreshing on CSS, HTML, JavaScript, and JQuery.

The GitHub repository can be found here.

© 2015 Joann Mistica

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.