7

I am new with using java.calendar.api. I want to point to the previous working day for a given day using java. BUT the conditions goes on increasing when i am using calendar.api to manipulate dates since I had to consider the usual weekends and the pointing to the previous month and also i had to consider the regional holidays in my region......

for ex:say i had to consider the U.S holidays and point to the day before that.

Is there any way i can define my own calendar and use it so that date manipulation senses all those usual changes?

crazypaladin
  • 453
  • 4
  • 7
  • 17
  • See [this Answer](http://stackoverflow.com/a/42147195/642706) about the [Nager.Date](https://github.com/tinohager/Nager.Date) project. – Basil Bourque Feb 17 '17 at 03:37

4 Answers4

10

While you should consider using the Joda Time library, here's a start with the Java Calendar API:

public Date getPreviousWorkingDay(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);

    int dayOfWeek;
    do {
        cal.add(Calendar.DAY_OF_MONTH, -1);
        dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
    } while (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY);

    return cal.getTime();
}

This only considers weekends. You'll have to add additional checks to handle days you consider holidays. For instance you could add || isHoliday(cal) to the while condition. Then implement that method, something like:

public boolean isHoliday(Calendar cal) {
    int year = cal.get(Calendar.YEAR);
    int month = cal.get(Calendar.MONTH) + 1;
    int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);

    if (month == 12 && dayOfMonth == 25) {
        return true;
    }

    // more checks

    return false;
}
WhiteFang34
  • 70,765
  • 18
  • 106
  • 111
4

tl;dr

LocalDate.now ( ZoneId.of ( "America/Montreal" ) )
         .with ( org.threeten.extra.Temporals.previousWorkingDay ()  )

java.time

Java 8 and later has the java.time framework built-in. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.

These new classes replace the notoriously troublesome old date-time classes bundled with the earliest versions of Java, java.util.Date/.Calendar. Avoid the old classes where possible. When you must interface look for newly added conversion methods to switch into java.time for most of your work. Also, the makers of Joda-Time have told us to move to java.time as soon as is convenient.

Basics of java.time… An Instant is a moment on the timeline in UTC. Apply a time zone (ZoneId) to get a ZonedDateTime. For a date-only value without a time-of-day nor a time zone, use LocalDate.

First we get "today" as an example date value. Note how a time zone is required in order to determine the current date even though a LocalDate does not contain a time zone. The date is not simultaneously the same around the globe, as a new day dawns earlier in the east.

LocalDate today = LocalDate.now ( ZoneId.of ( "America/Los_Angeles" ) );

Adjustors

The ThreeTen-Extra project extends java.time with additional or experimental features. These features may or may not eventually be folded into java.time proper. This project provides a Temporals class which provides implementations of adjustors including a couple for nextWorkingDay and previousWorkingDay. Easy to use as seen here.

// The 'Temporals' class is from the ThreeTen-Extra library, not built into Java.
LocalDate previousWorkingDay = today.with ( Temporals.previousWorkingDay () );
LocalDate nextWorkingDay = today.with ( Temporals.nextWorkingDay () );

When Run

Dump to console. Notice how today is a Friday, so the previous working day is -1 (yesterday, Thursday) and the next working day is +3 (Monday).

System.out.println ( "today: " + today + " | previousWorkingDay: " + previousWorkingDay + " | nextWorkingDay: " + nextWorkingDay );

today: 2016-01-22 | previousWorkingDay: 2016-01-21 | nextWorkingDay: 2016-01-25

Saturday & Sunday

This pair of adjustors simply skips over every Saturday and Sunday. It knows nothing of holidays. Nor does it know about other definitions of the working week and weekend. The class documentation suggests writing your own java.time.temporal.TemporalAdjuster is easy if you want to handle other definitions.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • thanks for the great answer! I am using `Threeten` libraries for Time and I've imported the `Temporal` class using `import org.threeten.bp.temporal.Temporal` statement. But I can't see any methods you mentioned in the answer- `Temporals.previousWorkingDay ()` and `Temporals.nextWorkingDay ()`. Where can I find them? Do I have to import some other class? – Hemanth Annavarapu Nov 06 '17 at 17:50
  • @HemanthAnnavarapu Your mentioned package name included `.bp.` which means you are using the ThreeTen-Backport project, a back-port of much of the java.time functionality built into Java 8 and later for use in Java 6 and Java 7. The ThreeTen-Extra projects adds classes to complement and extend java.time. Those "extra" classes run only in Java 8 and later, and have not been back-ported to Java 6/7 as far as I know. – Basil Bourque Nov 06 '17 at 23:27
3

Consider using Joda Time combined with a list of regional holidays in your region.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
0

You may define a class as below:

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author j.james
 */
public class MyCalendar {

    private static Map<String, String> holidays = null;
    private static MyCalendar myCalendar = null;
    private static final int WEEKEND_1 = Calendar.SATURDAY;
    private static final int WEEKEND_2 = Calendar.SUNDAY;

    private MyCalendar() {
        holidays = new HashMap<String, String>();
        holidays.put("7,4", "Independence Day");
        holidays.put("12,25", "Christmas");

        //holidays.putAll(DBUtils.readAnyDynamicHolidaysFromDB());
    }

    public static Date getPreviousWorkingDay(Date date) {

        Date previousWorkingDate = null;
        try {
            if (myCalendar == null) {
                myCalendar = new MyCalendar();
            }

            if(date != null) {
                Calendar calInstance = Calendar.getInstance();
                calInstance.setTime(date);
                int weekDay = 0;

                do {
                    calInstance.add(Calendar.DATE, -1);
                    weekDay = calInstance.get(Calendar.DAY_OF_WEEK);
                } while(weekDay == WEEKEND_1 || weekDay == WEEKEND_2 ||
                        holidays.get((calInstance.get(Calendar.MONTH) + 1)
                        + "," + calInstance.get(Calendar.DATE)) != null);

                previousWorkingDate = calInstance.getTime();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return previousWorkingDate;
    }
}

You can make a call as

public static void main(String[] args) {
        System.out.println(MyCalendar.getPreviousWorkingDay(new Date(2011-1900,6,5))); //July 5, 2011 which returns July 1 as the working day because July 4th 2011 is Monday
}
James Jithin
  • 10,183
  • 5
  • 36
  • 51