ArtsAutosBooksBusinessEducationEntertainmentFamilyFashionFoodGamesGenderHealthHolidaysHomeHubPagesPersonal FinancePetsPoliticsReligionSportsTechnologyTravel
  • »
  • Technology»
  • Computers & Software

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

Updated on April 3, 2015

Part 2 - Leap years and number of days

We learned a few helpful lessons in Part 1 and finished it off with our own reusable library with three simple methods that parse a full date and return the value for three variables; day, month, and year.

In this part, we will discuss:

  • If and switch statements
  • String comparisons
  • Methods within a method
  • Algorithms; Pseudocode to an actual code
  • More arrays and for loops
  • ArrayIndexOutOfBoundsException
  • Boolean expressions

Let's look back at the main method of "SampleClass".

Test for all methods.
Test for all methods. | Source

Counting the number of days in a given month

		System.out.println("*** First Date: " + firstDate + " ***");
		System.out.println(myCalendar.getMonth(firstDate) + " has " + myCalendar.numOfDays(firstDate) + " days.");

We are using the variable "firstDate" again and here we pass its value to the following methods:

Method
Purpose
numOfDays()
Returns the number of days in a given month.
isLeapYear()
Checks if the given year is a leap year.
Methods and their purpose.

I will keep the line that displays the value of the variable "firstDate" so we can validate the results we will get from each method after running the program. We can see the ouput below:

The result after running the program.
The result after running the program.

The result is correct for the value "01-JAN-2016", the month JAN or January does have 31 days. This time we'll try different values (months that do not have 31 days) to see if we would still get a correct result.

		System.out.println(myCalendar.getMonth("09-NOV-2014") + " has " + myCalendar.numOfDays("09-NOV-2014") + " days.");
		System.out.println(myCalendar.getMonth("14-FEB-2015") + " has " + myCalendar.numOfDays("14-FEB-2015") + " days.");
Number of days for November and February.
Number of days for November and February.

The results are still correct. NOV or November has 30 days while FEB or February only has 28 days, at least for the non-leap year 2015. Later on, we will discuss how to identify if a year is a leap year or not and adjust the number of days for the month of February.

Let's take a look at the implementation of the numOfDays() method and use the value "01-JAN-2016" as an example.

The "if" statement version of the numOfDays() method.
The "if" statement version of the numOfDays() method.

This can actually be done in two different ways:

  • Switch statements
  • If statements

Why did I use "if" statements instead of "switch"?

A story you're free to ignore

I make automations for the company I work for when I finish my tasks early and the workload is not too heavy. The default date and time library in Java didn't get a few things done for me. Instead of downloading a third-party library, I decided to have fun and create my own library to see how it works and also to have a better grasp of its code. The code was written in my local computer and it worked fine. I initially used "switch" for this method but I encountered a problem after migrating it to the remote desktop where I need it to run. The server is old and so is the Java installed. I did a quick search on the internet and found out that before Java 1.7, switching on strings isn't possible yet so I had to somehow "downgrade" my code to fit the older version that runs in the system so I used "if" statements and it worked. You can use either of the two but I'll be posting both just in case you will encounter the same problem as I did.

The "switch" statement version of the numOfDays() method.
The "switch" statement version of the numOfDays() method.
	public int numOfDays(String date){	
		int numDays = 0;
	}

Again, the method has a parameter named "date" of string data type. Inside the method's body is the variable "numDays" of type int. The variable "numDays" is initialized or assigned an initial value of 0.

		if (getMonth(date).equalsIgnoreCase("FEB")){
			if (isLeapYear(getYear(date)) == true)
				numDays = 29;
				else
					numDays = 28;
		}

Comparing two strings

Next, we enter the first "if" statement where the value of "date" is passed to the getMonth() method. The value returned by the method (in this example, "JAN") is then compared to the string "FEB". Notice that the method used in the comparison is ".equalsIgnoreCase" which means that when the two strings, "JAN" and "FEB" are compared, their cases are disregarded so "JAN", "jan", and "jAn" are considered to be the same thing despite some or all of the letters being in uppercase or lowercase.

Since "JAN" and "FEB" are not equal, the first condition is not satisfied and the program will no longer reach the other condition (the "if" statement below the first "if" statement) inside it. The program moves on to the "else if" condition and compares "JAN" with four other strings; "APR", "JUN", "SEP", and "NOV". If satisfied, the variable numDays will be assigned the value 30.

		else if (getMonth(date).equalsIgnoreCase("APR")
				|| getMonth(date).equalsIgnoreCase("JUN")
				|| getMonth(date).equalsIgnoreCase("SEP")
				|| getMonth(date).equalsIgnoreCase("NOV")){
			numDays = 30;
		}

Again, none of these strings match "JAN" and the program moves on to its last resort; the "else" statement. The "else" statement is where everything goes when the previous "if" and "else if" conditions are not met. You may consider this as the "default" since there are no more conditions here.

Since our month "JAN" falls here, the value of the variable "numDays" is assigned the value 31.

		else
			numDays = 31;

Leap years and February

February is quite a unique month. It's the only month that has less than 30 days. It is also the only month that changes the number of days depending on whether the current year is a leap year, in which case, the number of days becomes 29 instead of the usual 28.

		if (getMonth(date).equalsIgnoreCase("FEB")){
			if (isLeapYear(getYear(date)) == true)
				numDays = 29;
				else
					numDays = 28;
		}

Checking for leap years

In our numOfDays() method, "FEB" does not share the "if" condition with any other months. If we use the date "14-FEB-2016" as an example, the first "if" statement will be satisfied and the program will proceed to the other "if" inside. In the second line, the value of "date" is passed into the method getYear() which returns the year of a given date. In this case, the year is 2016. The value 2016 is then passed to the isLeapYear() method. Based on the condition, if the value returned by the isLeapYear method is true, it means that the given year is a leap year and the numDays variable will be assigned the value 29. If false, the given year is not a leap year and the value that will be assigned to numDays is 28.

A few notes you're free to ignore

When a method uses the result of another method as an argument, the code is read starting from the inner most. The inner most methods are invoked first and moves outwards.

So how does the code exactly know if the given date is a leap year? Let's take a look at the declaration of the method isLeapYear().

Tip

A quick way to find the declaration of any variable, methods, etc. is by holding down the "Ctrl" key and pressing the specific element.

The isLeapYear() method.
The isLeapYear() method.

Algorithm and Pseudocode

As you can see, the body of this method contains only one line. The code responsible for finding out whether a year is a leap year or not is just a single line of code. Searching on the internet for "leap year" should give you instant results including the criteria to find out if a year is a leap year. In order for the computer to understand and follow the criteria, you must convert it into an algorithm or a set of instructions in the form of a pseudocode or a high-level description of an algorithm.

Pseudocode is not concerned with syntax like an actual code is so you don't have to worry about what programming language you're gonna use. You can have one pseudocode and implement it in several different programming languages. It is more concerned on the logic and it is very useful to write down a pseudocode first before writing the actual code. This way you wouldn't have to think about two problems at the same time; logic and syntax.

In short, it's a "code" readable by you, human!

Criteria

Let's see if we can convert the criteria below into a pseudocode:

  • The year is evenly divisible by 4;
  • If the year can be evenly divided by 100, it is NOT a leap year, unless;
  • The year is also evenly divisible by 400. Then it is a leap year.

The criteria above was taken from www.timeanddate.com.

Pseudocode

We can create the pseudocode using if statements.

if (year is divisible by 4) then it is a leap year
else if (year is divisible by 100) and (year is divisible by 400) then it is a leap year
else it is not a leap year

Spoon-feeding our machine

Humans know what "divisible" means. It is a number that can be divided by another number and have no remainder. The computer does not know what "divisible" means but we can teach it by feeding instructions to find out when a number is divisible by another number or not.

Basic Math

We can put it this way first:

  • Divide the year by 4. If the remainder is not 0, it's not a leap year. If the remainder is 0, proceed to the next step.
  • Divide the year by 100. If the remainder is not 0, it is a leap year. If the remainder is 0, proceed to the next step.
  • Divide the year by 400. If the remainder is 0, it is a leap year. If the remainder is not 0, it is not a leap year.

Actual code

We can now write the actual code. The syntax will depend on the programming language you want to use.

		if (year % 4 == 0)
			return true;
		else if (year % 100 == 0 & year % 400 == 0)
			return true;
		else
			return false;

We can also fit the "true" condition in a single line with the return statement. We only need to write the conditions that will result to "true". Since the method is boolean, it will just return the opposite which is "false" if the condition is, well, not true.

Below is the shortcut, feel free to use whichever you understand the most or more comfortable with.

    public boolean isLeapYear(int year){
        return (year % 4 == 0 || year % 400 == 0 & year % 100 != 0);
    }

Tests

I've written some code in "SampleClass" to test if our method works. I declared two int array variables and put a list of leap years for the variable "leapYears" and a list of non-leap years for the variable "nonLeapYears".

Then I created a "for" loop to access the elements inside the array variable. I initialized the int variable "arrayIndex" to represent the array index starting with zero (0) then I looped through each one and passed the value to the method isLeapYear().

The "for" loop starts at the first element of an array or zero (0) and stops before it reaches the number of elements in an array. I used ".length" to get the number of items inside an array. Note that the length does not start with zero (0) like an index does, it starts with 1 like we normally count numbers. This is why we tell the "for" loop to stop before it reaches the number of elements in an array.

For example:

for (arrayIndex = 0; arrayIndex < leapYears.length; arrayIndex ++)

When we execute "leapYears.length", we can see that the number of elements it contains is 13 but in order to get the elements from the beginning, we must start with the index zero (0) as we have discussed in Part 1. The "for" loop above stops at index 12 (less than 13).

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Count
1
2
3
4
5
6
7
8
9
10
11
12
13
?
Index
0
1
2
3
4
5
6
7
8
9
10
11
12
13
Elements.

In the table above, index 12 is equivalent to the 13th element of the array so our code is good enough to fetch all the elements, starting from the beginning to the last, of an array.

Replacing "<" or less than with a "<=" less than or equal to will result to an ArrayIndexOutOfBoundsException, an exception encountered when the index we are trying to access is invalid or non-existent based on the array.

For example:

for (arrayIndex = 0; arrayIndex <= leapYears.length; arrayIndex ++)

In the table above, index 13 does not have a corresponding element in the array. This is the reason why we stop before we reach the index 13 or the length of an array. The index of the last element in an array is always less than the actual number of elements in an array by 1.

Last index = Length of an array - 1

ArrayIndexOutOfBoundsException.
ArrayIndexOutOfBoundsException.

Leap years

		int[] leapYears = {1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040};
		int arrayIndex = 0;
		
		System.out.println("Number of leap years from 1992 to 2040: " + leapYears.length);
		for (arrayIndex = 0; arrayIndex < leapYears.length; arrayIndex ++){
			System.out.println(leapYears[arrayIndex] + ": " + myCalendar.isLeapYear(leapYears[arrayIndex]));
		}
The isLeapYear() method returns "true" for all leap years.
The isLeapYear() method returns "true" for all leap years.

Non-leap years

		int[] nonLeapYears = {2001,2002,2003,2005,2006,2007,2009,2010,2011,2013,2014,2015};
		int arrayIndex = 0;

		System.out.println("Number of non-leap years from 2001 to 2015: " + nonLeapYears.length);
		for (arrayIndex = 0; arrayIndex < nonLeapYears.length; arrayIndex ++){
			System.out.println(nonLeapYears[arrayIndex] + ": " + myCalendar.isLeapYear(nonLeapYears[arrayIndex]));
		}
The isLeapYear() method returns "false" for all non-leap years.
The isLeapYear() method returns "false" for all non-leap years.

Pseudocode to actual code

So we got the criteria to identify a leap year, we used that information to create an algorithm in a form of pseudocode and from there, we turned it into an actual code following the syntax of our chosen programming language; Java.

That's pretty nice. You can then convert the code into a shorter one but this is a matter of preference and responsibility. Short code is not always better. You can do it either way as long as you still understand what it does.

Longer version

if (year % 4 == 0)
return true;
else if (year % 100 == 0 & year % 400 == 0)
return true;
else
return false;

Shorter version

return (year % 4 == 0 || year % 400 == 0 & year % 100 != 0);

You may have observed that the expression inside the if statements were simply transferred to a single line where we put an "||" (OR) in between the conditions since both of them returns "true". We put "||" (OR) since either of the two and not necessarily both conditons should be met in order to return "true".

Methods with a boolean return type automatically returns "true" when the condition included in the return statement is met. In our code, the conditon is "(year % 4 == 0 || year % 400 == 0 & year % 100 != 0)". There is no need to specify when it will return "false", it is automatically returned when the condition is not met.

MyCalendar

I assume that you've written the code below for the "MyCalendar" class. This includes the numOfDays() and isLeapYear() method. You can simply add the methods in the "MyCalendar" class we've created in Part 1.

public class MyCalendar {
	
	public int numOfDays(String date){	
		int numDays = 0;
		
		if (getMonth(date).equalsIgnoreCase("FEB")){
			if (isLeapYear(getYear(date)) == true)
				numDays = 29;
				else
					numDays = 28;
		}
		else if (getMonth(date).equalsIgnoreCase("APR")
				|| getMonth(date).equalsIgnoreCase("JUN")
				|| getMonth(date).equalsIgnoreCase("SEP")
				|| getMonth(date).equalsIgnoreCase("NOV")){
			numDays = 30;
		}
		else
			numDays = 31;
		
		return numDays;		
	}
	
	public boolean isLeapYear(int year){
		return (year % 4 == 0 || year % 400 == 0 & year % 100 != 0);
	}
	
}

SampleClass

You can use the code below for "SampleClass" to test if the code you've written works. Try playing around with the values and see if you get the expected results.

public class SampleClass {
	
	public static void main(String[] args) {
		MyCalendar myCalendar = new MyCalendar();
		String firstDate = "01-JAN-2016";
		int[] leapYears = {1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040};
		int[] nonLeapYears = {2001,2002,2003,2005,2006,2007,2009,2010,2011,2013,2014,2015};
		int arrayIndex = 0;
		
		System.out.println("************** Values returned by methods **************");
		System.out.println("*** First Date: " + firstDate + " ***");
		System.out.println(myCalendar.getMonth("09-NOV-2014") + " has " + myCalendar.numOfDays("09-NOV-2014") + " days.");
		System.out.println(myCalendar.getMonth("14-FEB-2015") + " has " + myCalendar.numOfDays("14-FEB-2015") + " days.");
		
		System.out.println("Number of leap years from 1992 to 2040: " + leapYears.length);
		for (arrayIndex = 0; arrayIndex < 13; arrayIndex ++){
			System.out.println(leapYears[arrayIndex] + ": " + myCalendar.isLeapYear(leapYears[arrayIndex]));
		}
		
		System.out.println("Number of non-leap years from 2001 to 2015: " + nonLeapYears.length);
		for (arrayIndex = 0; arrayIndex < 12; arrayIndex ++){
			System.out.println(nonLeapYears[arrayIndex] + ": " + myCalendar.isLeapYear(nonLeapYears[arrayIndex]));
		}
	}
	
}

Important note

The codes above simply serve as a summary of what we have discussed in the current part. You have to make sure that the methods we have created in the previous parts is added into the "MyCalendar" class. Methods like getMonth() are especially crucial for some parts of the code to work.

End of Part 2

We now have a method that gives us the number of days of a given month. We also have a method that tells us if a given year is a leap year and adjusts the number of days for the month of February. In the next part, we will add more "identity" to our months and teach the computer the order of these months so we can move from one month to another.

Read Part 3

© 2014 Joann Mistica

Comments

    0 of 8192 characters used
    Post Comment

    No comments yet.

    working

    This website uses cookies

    As a user in the EEA, your approval is needed on a few things. To provide a better website experience, hubpages.com uses cookies (and other similar technologies) and may collect, process, and share personal data. Please choose which areas of our service you consent to our doing so.

    For more information on managing or withdrawing consents and how we handle data, visit our Privacy Policy at: "https://hubpages.com/privacy-policy#gdpr"

    Show Details
    Necessary
    HubPages Device IDThis is used to identify particular browsers or devices when the access the service, and is used for security reasons.
    LoginThis is necessary to sign in to the HubPages Service.
    Google RecaptchaThis is used to prevent bots and spam. (Privacy Policy)
    AkismetThis is used to detect comment spam. (Privacy Policy)
    HubPages Google AnalyticsThis is used to provide data on traffic to our website, all personally identifyable data is anonymized. (Privacy Policy)
    HubPages Traffic PixelThis is used to collect data on traffic to articles and other pages on our site. Unless you are signed in to a HubPages account, all personally identifiable information is anonymized.
    Amazon Web ServicesThis is a cloud services platform that we used to host our service. (Privacy Policy)
    CloudflareThis is a cloud CDN service that we use to efficiently deliver files required for our service to operate such as javascript, cascading style sheets, images, and videos. (Privacy Policy)
    Google Hosted LibrariesJavascript software libraries such as jQuery are loaded at endpoints on the googleapis.com or gstatic.com domains, for performance and efficiency reasons. (Privacy Policy)
    Features
    Google Custom SearchThis is feature allows you to search the site. (Privacy Policy)
    Google MapsSome articles have Google Maps embedded in them. (Privacy Policy)
    Google ChartsThis is used to display charts and graphs on articles and the author center. (Privacy Policy)
    Google AdSense Host APIThis service allows you to sign up for or associate a Google AdSense account with HubPages, so that you can earn money from ads on your articles. No data is shared unless you engage with this feature. (Privacy Policy)
    Google YouTubeSome articles have YouTube videos embedded in them. (Privacy Policy)
    VimeoSome articles have Vimeo videos embedded in them. (Privacy Policy)
    PaypalThis is used for a registered author who enrolls in the HubPages Earnings program and requests to be paid via PayPal. No data is shared with Paypal unless you engage with this feature. (Privacy Policy)
    Facebook LoginYou can use this to streamline signing up for, or signing in to your Hubpages account. No data is shared with Facebook unless you engage with this feature. (Privacy Policy)
    MavenThis supports the Maven widget and search functionality. (Privacy Policy)
    Marketing
    Google AdSenseThis is an ad network. (Privacy Policy)
    Google DoubleClickGoogle provides ad serving technology and runs an ad network. (Privacy Policy)
    Index ExchangeThis is an ad network. (Privacy Policy)
    SovrnThis is an ad network. (Privacy Policy)
    Facebook AdsThis is an ad network. (Privacy Policy)
    Amazon Unified Ad MarketplaceThis is an ad network. (Privacy Policy)
    AppNexusThis is an ad network. (Privacy Policy)
    OpenxThis is an ad network. (Privacy Policy)
    Rubicon ProjectThis is an ad network. (Privacy Policy)
    TripleLiftThis is an ad network. (Privacy Policy)
    Say MediaWe partner with Say Media to deliver ad campaigns on our sites. (Privacy Policy)
    Remarketing PixelsWe may use remarketing pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to advertise the HubPages Service to people that have visited our sites.
    Conversion Tracking PixelsWe may use conversion tracking pixels from advertising networks such as Google AdWords, Bing Ads, and Facebook in order to identify when an advertisement has successfully resulted in the desired action, such as signing up for the HubPages Service or publishing an article on the HubPages Service.
    Statistics
    Author Google AnalyticsThis is used to provide traffic data and reports to the authors of articles on the HubPages Service. (Privacy Policy)
    ComscoreComScore is a media measurement and analytics company providing marketing data and analytics to enterprises, media and advertising agencies, and publishers. Non-consent will result in ComScore only processing obfuscated personal data. (Privacy Policy)
    Amazon Tracking PixelSome articles display amazon products as part of the Amazon Affiliate program, this pixel provides traffic statistics for those products (Privacy Policy)