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);
Advertisement
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 intorunPayroll
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?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later – Part of the standard Java API with a bundled implementation.
- Java 9 brought some minor features and fixes.
- Java SE 6 and Java SE 7
- Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android (26+) bundle implementations of the java.time classes.
- For earlier Android (<26), a process known as API desugaring brings a subset of the java.time functionality not originally built into Android.
- If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. See How to use ThreeTenABP….