Manipulating and comparing dates with GregorianCalendar and can’t get the code to work properly

Tags: , ,



I’m struggling to find the error in my code here. paytotal is coming out 0 when it should have a number.

firstDayOfPaycheck is the date Oct. 23rd 2020.

lastDayOfPaycheck is the date Nov. 6 2020.

My Simple date format sdf is “MMM dd, yyyy”.

string dateInQuestion passed into runPayroll is “Oct. 31, 2020” which came originally from the same sdf as above.

I’m new to java and haven’t dealt with manipulating the calendar like this. It feels like the code below should work.

 private void runPayroll(String dateInQuestion, long payForTask){   
     c.setTime(firstDayOfPaycheck);
        
        //loop through days of paycheck. number from time.compareTo(time2) is negative while time is before time2 
        while(c.getTime().compareTo(lastDayOfPaycheck)<=0){

            if(dateInQuestion != null) {

                Date questionDate = sdf.parse(dateInQuestion, new ParsePosition(0));
                if (c.getTime().compareTo(questionDate) == 0) {
                    payTotal += payForTask;
                }
            }
            c.add(Calendar.DAY_OF_YEAR, 1);
        }
} //ran inside the query to get the total pay

private void buildPayrollDash(){
    String strPayrollAmt = "$" + payTotal;


    String startDate = sdf.format(firstDayOfPaycheck);
    String trimmedStart = startDate.split(",")[0]; //cuts out the year in the date
    String endDate = sdf.format(lastDayOfPaycheck);
    String trimmedEnd = endDate.split(",")[0];
    int holdBack = sharedPreferences.getInt("payroll holdback", 7);
    c.setTime(lastDayOfPaycheck);
    c.add(Calendar.DAY_OF_YEAR, holdBack);
    String payDate = sdf.format(c.getTime());
    String trimmedPaydate = payDate.split(",")[0];

    tvPayrollTimefame.setText("Pay from " + trimmedStart + " - " + trimmedEnd);
    tvPayrollAmount.setText(strPayrollAmt + " due " + trimmedPaydate);

Answer

I’m struggling to find the error in my code here.

You are using terrible date-time classes that were supplanted years ago by the modern java.time classes. Never use Date, Calendar, GregorianCalendar, or their relatives.

firstDayOfPaycheck is the date Oct. 23rd 2020.

Use LocalDate to represent a date without time-of-day and without time zone.

LocalDate firstDayOfPayPeriod = LocalDate.of( 2020 , Month.OCTOBER , 23 ) ;

lastDayOfPaycheck is the date Nov. 6 2020.

You’ll find date-time handling much easier if you define your spans-of-time using the Half-Open approach. The beginning is inclusive while the ending is exclusive. So instead of focusing on the last day of the pay period, focus on the first day of the following period.

LocalDate firstDayOfSuccessivePayPeriod = LocalDate.of( 2020 , 11 , 7 ) ;

Tip: You can represent the date range of the pay period as a LocalDateRange object if you add the ThreeTen-Extra library to your Java project.

My Simple date format sdf is “MMM dd, yyyy”.

You should not be mixing business logic with localization code. Custom formatting of date-times should only be done for presentation to the user.

When exchanging date-time values textually as data, use the standard ISO 8601 formats. For a date-only value, the standard format is YYYY-MM-DD. The java.time use ISO 8601 formats by default, so no need to specify any formatting pattern.

string dateInQuestion passed into runPayroll is “Oct. 31, 2020” which came originally from the same sdf as above.

LocalDate dateInQuestion = LocalDate.parse( "2020-10-31" ) ;

If you must accommodate an input of formatted date string rather than standard ISO 8601 format, use DateTimeFormatter. This has been covered many many times already on Stack Overflow, so search for more info.

And rather than check for valid data later, check your inputs early in your code. “Fail fast” is the saying.

try
{
    LocalDate dateInQuestion = LocalDate.parse( "2020-10-31" );
}
catch ( DateTimeParseException e )
{
    // … Handle faulty input.
    e.printStackTrace();
}

I’m new to java and haven’t dealt with manipulating the calendar like this. It feels like the code below should work.

Your code will be much simpler when using java.time. For one thing, the java.time classes offer convenient isBefore, isAfter, and isEqual methods, so no need for clumsy compareTo calls.

LocalDate firstDayOfPayPeriod = LocalDate.of( 2020 , Month.OCTOBER , 23 );
LocalDate firstDayOfSuccessivePayPeriod = LocalDate.of( 2020 , 11 , 7 );

String input = "2020-10-31";
LocalDate dateInQuestion = null;
try
{
    dateInQuestion = LocalDate.parse( input );
}
catch ( DateTimeParseException e )
{
    // Handle faulty input.
    e.printStackTrace();
}

// Validate dates.
Objects.requireNonNull( firstDayOfPayPeriod );
Objects.requireNonNull( firstDayOfSuccessivePayPeriod );
Objects.requireNonNull( dateInQuestion );
if ( ! firstDayOfPayPeriod.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    throw new IllegalStateException( "…" );
}
if ( dateInQuestion.isBefore( firstDayOfPayPeriod ) )
{
    throw new IllegalStateException( "…" );
}
if ( ! dateInQuestion.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    throw new IllegalStateException( "…" );
}


long payPerDay = 100;
long partialPay = 0;

LocalDate localDate = firstDayOfPayPeriod;
while ( localDate.isBefore( firstDayOfSuccessivePayPeriod ) )
{
    if ( localDate.isBefore( dateInQuestion ) )
    {
        partialPay = ( partialPay + payPerDay );
    }

    // Set up the next loop.
    // Notice that java.time uses immutable objects. So we generate a new object based on another’s values rather than alter (mutate) the original.
    localDate = localDate.plusDays( 1 ); // Increment to next date.
}

System.out.println( "Partial pay earned from firstDayOfPayPeriod " + firstDayOfPayPeriod + " to dateInQuestion " + dateInQuestion + " is " + partialPay );

See this code run live on IdeOne.com.

Partial pay earned from firstDayOfPayPeriod 2020-10-23 to dateInQuestion 2020-10-31 is 800

With more experience in programming Java, you may want to do this kind of work using streams. See LocalDate::datesUntil.

By the way, if you want to skip weekends, add something like this:

Set< DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;
…
if ( weekend.contains( localDate.getDayOfWeek() ) ) { … }

About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Table of which java.time library to use with which version of Java or Android



Source: stackoverflow