4
\$\begingroup\$

I'm trying to make a program where user will put in his birth date in DD/MM/YYYY form and he will get back amount of seconds between his birth day and 01/01/2015. It counts with fact that different months have different amounts of days and also with leap years. What should be improved or focused on more?

I already found one little bug, when user input date like 01/01/2002, putting in it array and then loading it to int variables will cause the zeroes to be cut off and validation will fail.

Main class:

package AgeInSeconds;

import java.util.Calendar;
import java.util.Scanner;

public class AgeInSeconds {
    private static final String INCORRECT_FORMAT = "Date has incorrect format, please try again."; //message to display when format is incorrect
    private static final Calendar calendar = Calendar.getInstance(); // shortcut to calendar instance

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in); //initialize scanner
        System.out.println("Enter your birth date in form DD/MM/YYYY:"); //ask user to put in his birth date
        String birthDate = input.next(); // load the date from console
        String[] date = birthDate.split("/"); //split date to days, months and years
        //if date wasn't divided to three segments, format is incorrect
        if (date.length != 3) {
            System.out.println(INCORRECT_FORMAT);
            input.close();
            return;
        }
        //load day, month and year from date
        int day = Integer.parseInt(date[0]);
        int month = Integer.parseInt(date[1]);
        int year = Integer.parseInt(date[2]);
        //put day,month and year to the validator to check if they have correct lengths and are in correct intervals
        if (!Validator.validate(day, month, year)) {
            System.out.println(INCORRECT_FORMAT);
            input.close();
            return;
        }
        //calculate amount of seconds (currently from BirthDate - 1.1.2015)
        System.out.println(calculateSeconds(day,month,year));
        input.close();

    }


    public static long calculateSeconds(int day, int month, int year) {
        //get the current year
        int currentYear = calendar.get(Calendar.YEAR);

        //get seconds from years, months and days from birth date to 1.1.2015
        long secondsFromDays = getSecondsFromDays(day, month, year);
        long secondsFromMonths = getSecondsFromMonths(month, year);
        long secondsFromYears = getSecondsFromYears(year, currentYear);


        long totalSeconds = secondsFromDays + secondsFromMonths + secondsFromYears;
        return totalSeconds;
    }

    //return seconds from user's birth day to end of user's birth month
    public static long getSecondsFromDays(int day, int month, int year) {
        long secondsFromDays = (numberOfDaysInMonth(month, year) - day)*24*60*60;
        return secondsFromDays; 
    }

    //return seconds from user's birth month to end of user's birth year
    public static long getSecondsFromMonths(int month, int year) {
        int secondsFromMonths = 0;
        for (int i = month; i <= 12; i++) {
            secondsFromMonths += numberOfDaysInMonth(month, year)*24*60*60;
        }
        return secondsFromMonths;
    }

    //return seconds from next year from user's birth year - current year
    public static long getSecondsFromYears(int year, int currentYear) {
        int secondsFromYears = 0;
        int days = 0;
        for (int i = year+1; i <= currentYear; i++) {
            days = (isLeapYear(i)) ? 366 : 355;
            secondsFromYears += days*24*60*60;
        } 
        return secondsFromYears;
    }

    //return number of days in specific month
    public static int numberOfDaysInMonth(int month, int year) {
        if (month == 4 || month == 6 || month == 9 || month == 11) {
            return 30;
        }
        if (month == 2) {
            return (isLeapYear(year)) ? 29 : 28;
        }
        return 31;
    }

    //check if year is leap
    public static boolean isLeapYear(int year) {
        return ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0));
    }
}

Validator class:

package AgeInSeconds;

import java.util.Calendar;

public class Validator {

    public static boolean validate(int day, int month, int year) {
        //return true or false based on if date format is correct
        return (validateDay(day) && validateMonth(month) && validateYear(year));
    }

    //validate day - it must be 2 digits long and in interval between 01-31
    private static boolean validateDay(int day) {
        if (String.valueOf(day).length() != 2) {
            return false;
        }
        if (day <= 0 || day > 31) {
            return false;
        }

        return true;
    }

    //validate month - it must be 2 digits long and in interval between 01-12
    private static boolean validateMonth(int month) {
        if (String.valueOf(month).length() != 2) {
            return false;
        }
        if (month <= 0 || month > 12 ) {
            return false;
        }
        return true;
    }

    //validate year - it must be 4 digits long and in interval 0000 - current year
    private static boolean validateYear(int year) {
        int currentYear = Calendar.getInstance().get(Calendar.YEAR);
        if (String.valueOf(year).length() != 4) {
            return false;
        }
        if (year <= 0 || year > currentYear) {
            return false;
        }
        return true;
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Are you able to at least use the Calendar (< Java 8) class, Joda-Time library (also < Java 8, recommended), or the new Time APIs in Java 8? \$\endgroup\$ – h.j.k. Jun 17 '15 at 1:23
  • \$\begingroup\$ Also, it's slightly odd that you are asking for a date but you'll compute a seconds-based result... do you need to account for rounding? Time zone differences? :D \$\endgroup\$ – h.j.k. Jun 17 '15 at 1:24
  • \$\begingroup\$ other than that little edge case this code works right? \$\endgroup\$ – Malachi Jun 17 '15 at 13:04
2
\$\begingroup\$

Validation

As you have realized, your validation approach is incorrect. You seem to be conflating two different validations:

  1. That each date field is only two digits.
  2. That the numeric value of each field 'makes sense'.

Given your current method signatures, your methods are already expected to work on the numeric values, so my suggestion is to just do that and leave the first validation out of the picture.

Even so, it's not a bullet-proof validation, since you allow 'nonsensical' values like the 30th February or 31st June.

Javadoc comments for methods should be in this format:

/**
 * Checks that the month is between 1 and 12, inclusive.
 * @param month the input
 * @return true if the month is between 1 and 12, inclusive.
 */
private static boolean validateMonth(int month) {
    // ...
}

Calculation

24*60*60 is repeated often, I suggest you extract that out as a static final field.

As for the rest...

Are you able to at least use Joda-Time library (< Java 8), or the new Time APIs in Java 8? - myself

I didn't scrutinize your calculation logic here because there simply are better and easier ways of doing date-time calculations. In fact, I am not even sure whether you are doing it right from a brief scan. You are doing purely additions, whereas I would imagine there must be a subtraction somewhere for the month and day. A quick test shows that there are 34732800 seconds between 1st January 2014 and 1st January 2015, which is clearly wrong.

Please look into having unit testing to ensure your calculation logic works as it should.

\$\endgroup\$
0
\$\begingroup\$

From my point of view it will be enough to use just java.lang.Date and java.text.SimpleDateFormat classes for all operations.

DateFormatt.parse() method for parsing input string.

and Date.getTime() for calculation, getTime() returns milliseconds from Jan 1, 1900 as long, you can just divide it for 1000 to translate it into seconds.

For you case it will be looks like this:

    SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
    Date birthDay = format.parse(inputString);
    Date startOfYear = format.parse("01/01/2015");

    long secondsTillCurrentYear = (startOfYear.getTime() - birthDay.getTime())/1000L;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.