Tutorial: How to Write a Java Date Class (Basic OOP Concept)


The basic concept of OOP (Object Oriented Programming) is not difficult to understand and every Computer Science students should be able to demonstrate the OOP using Java, which is considered the most elegant programming language that has full OOP concepts.

The following will guide you how to write a basic Date class in Java with some useful methods, overloaded constructors, private attributes etc. A Class can be considered as the encapsulation of a bunch of methods and attributes.

So, we name the class Date and we should store the Java source code as file name exactly as the class name which is Date.java. There can only be one public class in one Java source file but can be several other classes (not marked as public). Use the command javac Date.java to compile into bytecode, which is Date.class and if it is a runnable class (that has a static main method as the program entry point), then you can use command java Date to run the Java application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Date {
    /**
     * private attribute for holding day
     */
    private int day;
    
    /**
     * private attribute for holding month
     */
    private int month;
    
    /**
     * private attribute for holding year
     */
    private int year;
}
public class Date {
    /**
     * private attribute for holding day
     */
    private int day;
    
    /**
     * private attribute for holding month
     */
    private int month;
    
    /**
     * private attribute for holding year
     */
    private int year;
}

We declare three private attributes that store the year, month and day in three integers. Attributes are recommended to be private but at the same time we should provide public getters to access these fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    /**
     * Getter for day
     * @return day
     */
    public int getDay() {
        return this.day;
    }
    
    /**
     * Getter for month
     * @return month
     */
    public int getMonth() {
        return this.month;
    }
    
    /**
     * Getter for year
     * @return year
     */
    public int getYear() {
        return this.year;
    }   
    /**
     * Getter for day
     * @return day
     */
    public int getDay() {
        return this.day;
    }
    
    /**
     * Getter for month
     * @return month
     */
    public int getMonth() {
        return this.month;
    }
    
    /**
     * Getter for year
     * @return year
     */
    public int getYear() {
        return this.year;
    }   

With these getters, we can have a compareTo method that compares this to another Date class.

1
2
3
4
5
6
7
8
9
10
11
    /**
     * Compare two dates
     * @param d Date
     * @return true if it is earlier than d
     */
    public boolean compareTo(Date d) {
        int day1 = d.getDay();
        int month1 = d.getMonth();
        int year1 = d.getYear();
        return (this.year <= year1) && (this.month <= month1) && (this.day <= day1);
    }
    /**
     * Compare two dates
     * @param d Date
     * @return true if it is earlier than d
     */
    public boolean compareTo(Date d) {
        int day1 = d.getDay();
        int month1 = d.getMonth();
        int year1 = d.getYear();
        return (this.year <= year1) && (this.month <= month1) && (this.day <= day1);
    }

Please note that the private fields can be directly accessed inside the same object (i.e. instance of class) without using public getter methods. The following returns the Date as a ISO 8601 Date format string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /**
     * return a string representation 
     * convert to ISO 8601 Date Format
     * http://en.wikipedia.org/wiki/ISO_8601
     * @return 
     */
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(String.valueOf(this.year));
        s.append("-");
        if (this.month < 10) s.append("0");
        s.append(String.valueOf(this.month));
        s.append("-");
        if (this.day < 10) s.append("0");
        s.append(String.valueOf(this.day));
        return s.toString();
    }
    /**
     * return a string representation 
     * convert to ISO 8601 Date Format
     * http://en.wikipedia.org/wiki/ISO_8601
     * @return 
     */
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(String.valueOf(this.year));
        s.append("-");
        if (this.month < 10) s.append("0");
        s.append(String.valueOf(this.month));
        s.append("-");
        if (this.day < 10) s.append("0");
        s.append(String.valueOf(this.day));
        return s.toString();
    }

We need to be able to check the given date input is valid or not, so the following method, marked as static, does this.

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
    /**
     * Check if given year/month/day is valid
     * @param year
     * @param month
     * @param day
     * @return true if it is valid date
     */
    public static boolean isValid(int year, int month, int day) {
        if (year < 0) return false;
        if ((month < 1) || (month > 12)) return false;
        if ((day < 1) || (day > 31)) return false;
        switch (month) {
            case 1: return true;
            case 2: return (isLeap(year) ? day <= 29 : day <= 28);
            case 3: return true;
            case 4: return day < 31;
            case 5: return true;
            case 6: return day < 31;
            case 7: return true;
            case 8: return true;
            case 9: return day < 31;
            case 10: return true;
            case 11: return day < 31;
            default: return true;
        }
    }
    /**
     * Check if given year/month/day is valid
     * @param year
     * @param month
     * @param day
     * @return true if it is valid date
     */
    public static boolean isValid(int year, int month, int day) {
        if (year < 0) return false;
        if ((month < 1) || (month > 12)) return false;
        if ((day < 1) || (day > 31)) return false;
        switch (month) {
            case 1: return true;
            case 2: return (isLeap(year) ? day <= 29 : day <= 28);
            case 3: return true;
            case 4: return day < 31;
            case 5: return true;
            case 6: return day < 31;
            case 7: return true;
            case 8: return true;
            case 9: return day < 31;
            case 10: return true;
            case 11: return day < 31;
            default: return true;
        }
    }

On Leap years, the Feb month are 29 days and 28 days otherwise. Therefore we need to be able to check if the year is leap or not.

1
2
3
4
5
6
7
8
9
10
11
    /**
     * Check given year is leap year
     * @param year
     * @return true if year is leap year
     */
    public static boolean isLeap(int year) {
        // using system library to do this, avoid re-invent the wheel
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;     
    }
    /**
     * Check given year is leap year
     * @param year
     * @return true if year is leap year
     */
    public static boolean isLeap(int year) {
        // using system library to do this, avoid re-invent the wheel
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;     
    }

Please note, we need to import java.util.Calendar; at the beginning of the Java source file. If the number of days in that year is 366 days then it is a leap year. We can of course do something similar but less straightforward.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    /**
     * Check given year is leap year
     * @param year
     * @return true if year is leap year
     */
    public static boolean isLeap(int year) {
            if (year % 4 != 0) {
              return false;
            } else if (year % 400 == 0) {
              return true;
            } else if (year % 100 == 0) {
              return false;
            } else {
              return true;
            }        
    }    
    /**
     * Check given year is leap year
     * @param year
     * @return true if year is leap year
     */
    public static boolean isLeap(int year) {
            if (year % 4 != 0) {
              return false;
            } else if (year % 400 == 0) {
              return true;
            } else if (year % 100 == 0) {
              return false;
            } else {
              return true;
            }        
    }    

The static methods are shared among all instances of the same class and therefore there cannot be any references to its non-static attributes inside static methods. Similarly, the static attributes are shared among all instances of the same class i.e. something like a global variable for all objects of the same class (only 1 copy in memory).

With this validation method, we can create overloaded constructors that take date input in different formats. If given data is not a valid date format, then IllegalArgumentException will be thrown out.

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
    /**
     * Constructor that takes year/month/day
     * @param year
     * @param month
     * @param day
     * @throws IllegalArgumentException if invalid
     */
    public Date(int year, int month, int day) throws IllegalArgumentException{
        if (!isValid(year, month, day)) throw new IllegalArgumentException();
        this.year = year;
        this.month = month;
        this.day = day;     
    }
    
    /**
     * Constructor that takes a ISO8601 date string
     * @param dateISO8601 
     * @throws IllegalArgumentException if invalid
     */
    public Date(String dateISO8601) throws IllegalArgumentException {
        String[] s = dateISO8601.split("-");
        if (s.length != 3) throw new IllegalArgumentException();
        int yy = Integer.parseInt(s[0]);
        int mm = Integer.parseInt(s[1]);
        int dd = Integer.parseInt(s[2]);
        if (!isValid(yy, mm, dd)) throw new IllegalArgumentException();
        this.year = yy;
        this.month = mm;
        this.day = dd;
    }
    /**
     * Constructor that takes year/month/day
     * @param year
     * @param month
     * @param day
     * @throws IllegalArgumentException if invalid
     */
    public Date(int year, int month, int day) throws IllegalArgumentException{
        if (!isValid(year, month, day)) throw new IllegalArgumentException();
        this.year = year;
        this.month = month;
        this.day = day;     
    }
    
    /**
     * Constructor that takes a ISO8601 date string
     * @param dateISO8601 
     * @throws IllegalArgumentException if invalid
     */
    public Date(String dateISO8601) throws IllegalArgumentException {
        String[] s = dateISO8601.split("-");
        if (s.length != 3) throw new IllegalArgumentException();
        int yy = Integer.parseInt(s[0]);
        int mm = Integer.parseInt(s[1]);
        int dd = Integer.parseInt(s[2]);
        if (!isValid(yy, mm, dd)) throw new IllegalArgumentException();
        this.year = yy;
        this.month = mm;
        this.day = dd;
    }

Or, if no parameters are given, the Today’s date will be used.

1
2
3
4
5
6
7
8
9
10
11
12
13
    /**
     * default constructor that uses today's Date()
     */
    public Date() {
        Calendar currentDate = Calendar.getInstance(); //Get the current date
        java.util.Date x = currentDate.getTime();
        SimpleDateFormat formatyear = new SimpleDateFormat("yyyy");
        this.year = Integer.parseInt(formatyear.format(x));
        SimpleDateFormat formatmonth = new SimpleDateFormat("MM");
        this.month = Integer.parseInt(formatmonth.format(x));     
        SimpleDateFormat formatdd = new SimpleDateFormat("dd");
        this.day = Integer.parseInt(formatdd.format(x));       
    }
    /**
     * default constructor that uses today's Date()
     */
    public Date() {
        Calendar currentDate = Calendar.getInstance(); //Get the current date
        java.util.Date x = currentDate.getTime();
        SimpleDateFormat formatyear = new SimpleDateFormat("yyyy");
        this.year = Integer.parseInt(formatyear.format(x));
        SimpleDateFormat formatmonth = new SimpleDateFormat("MM");
        this.month = Integer.parseInt(formatmonth.format(x));     
        SimpleDateFormat formatdd = new SimpleDateFormat("dd");
        this.day = Integer.parseInt(formatdd.format(x));       
    }

We need to import java.text.SimpleDateFormat; for corresponding Date Format handling utilities.

To test the above functions, we can create another Test Class that has a static main method serving as the Java application entry. Use command java Test to start the test after javac Test.java.

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
public class Test {
 
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        // get today
        Date today = new Date();
        System.out.println(today.toString());
        
        // tomorrow
        Date tomorrow = new Date("2014-05-09");
        System.out.println(today.compareTo(tomorrow)); // today is earlier than tomorrow
        
        // yesterday
        Date yesterday = new Date(2014, 5, 7);
        System.out.println(today.compareTo(yesterday)); // today is NOT earlier than tomorrow
        
        // 2013 is not leap year
        System.out.println(Date.isLeap(2013) ? "2013 is leap": "2013 is not leap");
        
        // 2013-1-31 is a valid date
        System.out.println(Date.isValid(2013, 1, 31));
        
        // 2014 - 2 - 29 is not valid and will throw IllegalArgumentException
        Date invalid = new Date(2014, 2, 29); // throws exception
        System.out.println(invalid.toString());
    }
    
}
public class Test {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        // get today
        Date today = new Date();
        System.out.println(today.toString());
        
        // tomorrow
        Date tomorrow = new Date("2014-05-09");
        System.out.println(today.compareTo(tomorrow)); // today is earlier than tomorrow
        
        // yesterday
        Date yesterday = new Date(2014, 5, 7);
        System.out.println(today.compareTo(yesterday)); // today is NOT earlier than tomorrow
        
        // 2013 is not leap year
        System.out.println(Date.isLeap(2013) ? "2013 is leap": "2013 is not leap");
        
        // 2013-1-31 is a valid date
        System.out.println(Date.isValid(2013, 1, 31));
        
        // 2014 - 2 - 29 is not valid and will throw IllegalArgumentException
        Date invalid = new Date(2014, 2, 29); // throws exception
        System.out.println(invalid.toString());
    }
    
}

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
1251 words
Last Post: Interview Question: The Number of Bits Required to Convert One Integer to Another (C/C++ function)
Next Post: The Other Array Access Notation in C/C++

The Permanent URL is: Tutorial: How to Write a Java Date Class (Basic OOP Concept)

2 Comments

  1. online java training
  2. nisha

Leave a Reply