I am currently using the DatePicker in Android to let the user select a date. Is there a possibility to only allow the user to select Mondays and disable all other days for selection? I did not find anything in the Documentation.
Currently I am using this to show the DatePicker:
DatePickerDialog datePickerDialog = new DatePickerDialog(AddAppointment.this,
new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
// Do stuff with the info
}
}, setYear, setMonth, setDay);
datePickerDialog.show();
You can use this library Material Date Time Picker, here you can set an option to show specific dates, For Example:
datePicker.setSelectableDays(Calendar[] days)
And pass the array of Calendar as a parameter which contains all the selectable date.
for finding monday array you can use this logic:- Get all Fridays in a date Range in Java
You can calculate the calendar week of the chosen date or calculate the most recent Monday using one of the methods below. They are commented, so I keep the text short.
public class ExampleDateCalculation {
public static void main(String[] args) {
int dayOfMonth = 4;
int monthOfYear = 3;
int year = 2018;
// create a java.time.LocalDate of the given integers
LocalDate localDate = LocalDate.of(year, monthOfYear, dayOfMonth);
// calculate the calendar week of it
int calendarWeekTheLocalDateIsIn = getCalendarWeek(localDate);
// calculate the last Monday before this date
LocalDate lastMonday = getLastFrom(DayOfWeek.MONDAY, localDate);
// create a formatter for your locale
DateTimeFormatter germanDateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
System.out.println(localDate.format(germanDateFormatter)
+ " is in calendar week "
+ calendarWeekTheLocalDateIsIn
+ " of the system locale and the last Monday before was at "
+ lastMonday.format(germanDateFormatter));
}
/**
* <p>
* Gets the calendar week number of the given {#link LocalDate} based on the
* {#link Locale} of the operating system.
* </p>
*
* #param localDate the date of the day
* #return the calendar week number the day is in
*/
public static int getCalendarWeek(LocalDate localDate) {
WeekFields weekFields = WeekFields.of(Locale.getDefault());
return localDate.get(weekFields.weekOfWeekBasedYear());
}
/**
* <p>
* Gets the date of the last given weekday or day of week starting from the
* weekday of the given date. The method calculates the date of the nearest
* weekday chronologically backwards.
* </p>
* <p>
* <strong>For example:</strong><br>
* If the weekday of the given date is a Monday and the given day of week is a
* Tuesday, then this method will return the date of the Tuesday before today,
* which is 6 days back in the past.
* </p>
*
* #param weekday the day of week whose date is to be determined
* #param from the date to start from calculating backwards
* #return the date of the last given day of week starting from the given date
*/
public static LocalDate getLastFrom(DayOfWeek weekday, LocalDate from) {
DayOfWeek fromWeekday = from.getDayOfWeek();
int fromWeekdayValue = fromWeekday.getValue();
int weekdaysValue = weekday.getValue();
int daysToSubtract = 0;
/*
* Calculate the days to go back and be beware of negative values by means of
* case differentiation. Get the positive difference by subtracting the smaller
* value from the larger one and subtract a week if the result would be 0.
*/
if (fromWeekdayValue < weekdaysValue) {
daysToSubtract = 7 - (weekdaysValue - fromWeekdayValue);
} else if (fromWeekdayValue > weekdaysValue) {
daysToSubtract = fromWeekdayValue - weekdaysValue;
} else {
daysToSubtract = 7;
}
return from.minusDays(daysToSubtract);
}
}
If you want the user to only see calendar weeks or Mondays, follow the suggestions given in Uday Nayak's answer.
If anyone finds errors in or knows disadvantages of this code, please let me know.
You can try to do it little bit differently. First, change font color of days user don't want to pick (all except Mondays) and than filter active day selected and disable functionality until Monday is selected.
Related
The scenario is as follows.
You have to check if the current time is between a given start day + time and end day + time. A specific date range is not given, and the range can be in two different days.
Example:
String startDayAndTime = "SATURDAY 17:00";
String endDayAndTime = "SUNDAY 17:00";
SimpleDateFormat simpleDateFormatForTime = new SimpleDateFormat("HH:mm");
SimpleDateFormat simpleDateFormatForDay = new SimpleDateFormat("EEEE");
simpleDateFormatForTime.setTimeZone(timeZone);
simpleDateFormatForDay.setTimeZone(timeZone);
String deviceTimeString = simpleDateFormatForTime.format(date);
String deviceDayString = simpleDateFormatForDay.format(date).toUpperCase();
// TODO boolean isValidDayRange =
boolean withInRange = deviceTimeString.compareTo(startDayAndTime.split(" ")[1]) >= 0 && deviceTimeString.compareTo(endDayAndTime.split(" ")[1]) <= 0;
if (isValidDayRange && withInRange) {
//do something
}
This above code I'm working on can check for the time but not the date.
How to achieve this? TIA.
PS: Real world example: A shop offers discount for a product on every weekend from 5PM Saturday to 5PM Sunday. Trying to check the current time is eligible for that.
Requirement Update: Has to be in Java 1.7 or lesser
There is nothing in java.time to represent a day and a time (à la MonthDay) but we can quite easily define our own. I have chosen a DayOfWeek and a Duration. Make sure you validate that the time is non-negative and is less than 24 hours.
Note that DayOfWeek has a natural ordering of Monday (lowest) to Sunday (highest).
class DayTime implements Comparable<DayTime>
{
private final DayOfWeek dayOfWeek;
private final Duration time;
//...
public int compareTo(final DayTime o) { /* ... */ }
}
We can then define an event which specifies a start and end time. Make sure to validate that the start is not after the end.
class Event
{
private final DayTime start;
private final DayTime end;
//...
boolean isTimeDuringEvent(final DayTime dayTime) { /* ... */ }
}
I have left the implementation details up to you. It's your assignment.
We can use these classes like so:
final DayTime start = new DayTime(DayOfWeek.SATURDAY, Duration.ofHours(17));
final DayTime end = new DayTime(DayOfWeek.SUNDAY, Duration.ofHours(17));
final Event event = new Event(start, end);
final LocalDateTime now = LocalDateTime.now();
final DayTime test = new DayTime(
now.getDayOfWeek(),
Duration.ofNanos(now.toLocalTime().toNanoOfDay())
);
System.out.println(event.isTimeDuringEvent(test));
If you need to take user input as a string then I would advise you to work on getting the logic correct first (as above, with hardcoded values such as DayOfWeek.SATURDAY) and then when you're sure that that works, work on parsing the input. (you'll probably want DateTimeFormatter for this)
Using TemporalAccessor
You can use TemporalAccessors and a DateTimeFormatter to read the values and then build simple comparators to check your work.
String startDayAndTime = "SATURDAY 17:00";
String endDayAndTime = "SUNDAY 17:00";
DateTimeFormatter format = DateTimeFormatter.ofPattern("EEEE HH:mm");
TemporalAccessor tempNow = LocalDateTime.now();
TemporalAccessor tempStart = format.parse(startDayAndTime.toLowerCase());
TemporalAccessor tempEnd = format.parse(endDayAndTime.toLowerCase());
Comparator<TemporalAccessor> dayCompare = (a, b) -> a.get(ChronoField.DAY_OF_WEEK) - b.get(ChronoField.DAY_OF_WEEK);
Comparator<TemporalAccessor> timeCompare = (a, b) -> LocalTime.from(a).compareTo(LocalTime.from(b));
Comparator<TemporalAccessor> dateCompare = dayCompare.thenComparing(timeCompare);
if (dateCompare.compare(tempStart, tempNow) >= 0 && dateCompare.compare(tempNow, tempEnd) >= 0) {
//do something
}
You can access the date as below
LocalDateTime today = LocalDateTime.now();
LocalDate ld = LocalDate.now();
ld = ld.with(TemporalAdjusters.previous(DayOfWeek.SATURDAY));
LocalDateTime previousMonday = ld.atTime(17, 00);
LocalDate ld2 = LocalDate.now();
ld2 = ld2.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
LocalDateTime nextSunday = ld2.atTime(17, 00);
You can write your logic after that.
As above you can find previousMonday and nextSunday.
if previousMonday and nextSunday time gap less than a week, you are in the gap. Otherwise you are in out.
You can use calendar here
Calendar startDateTime=Calendar.getInstance();
startDateTime.set(Calendar.DAY_OF_WEEK,Calendar.SUNDAY);
startDateTime.set(Calendar.HOUR_OF_DAY,1);
startDateTime.set(Calendar.MINUTE,0);
startDateTime.set(Calendar.SECOND,0);
System.out.println("start Date : "+startDateTime.getTime());
Calendar endDateTime=Calendar.getInstance();
endDateTime.set(Calendar.DAY_OF_WEEK,Calendar.FRIDAY);
endDateTime.set(Calendar.HOUR_OF_DAY,17);
endDateTime.set(Calendar.MINUTE,0);
endDateTime.set(Calendar.SECOND,0);
System.out.println("end Date : "+endDateTime.getTime());
Calendar today = Calendar.getInstance();
if(today.after(startDateTime) && today.before(endDateTime))
{
System.out.println("Yes");
}
But here you have need to maintain order of day because calendar WEEKS start with SUNDAY and ends with SATURDAY. So you have to follow this Order. Smaller is startdate and bigger is end date.
Order is like this
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Sunday.
*/
public final static int SUNDAY = 1;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Monday.
*/
public final static int MONDAY = 2;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Tuesday.
*/
public final static int TUESDAY = 3;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Wednesday.
*/
public final static int WEDNESDAY = 4;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Thursday.
*/
public final static int THURSDAY = 5;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Friday.
*/
public final static int FRIDAY = 6;
/**
* Value of the {#link #DAY_OF_WEEK} field indicating
* Saturday.
*/
public final static int SATURDAY = 7;
But I think it is enough to serve your requirements.
thanks for the answers and the conversations. I managed to solve the issue from both TemporalAccessor and Calendar options given. Later a new requirement was given as to the util class I was working on has to be written in Java 1.7 or lesser since it was to be a library for an android app which didn't support Java 1.8. Hence the answer by #flopcoder was accepted, on the grounds where Sunday was taken as the beginning of the week and the logic was only to be applied on ranges within one week only.
You have written the code to check only day and time, not written the code to check date use simpleDateFormat and send the system current date to check and compare, you are passing "HH: mm" Which will give you system current time and "EEEE" Will give you day, .... So u need to pass "dd" to get current system date only and then compare as per you requirement
Here is my try to do date minus for GWT:
Date from = new Date();
Date to = new Date();
if(filter.equals(DATE_FILTER.PAST_HOUR)){
minusHoursToDate(to, 1);
} else if(filter.equals(DATE_FILTER.PAST_24_HOURS)){
minusHoursToDate(to, 1 * 24);
} else if(filter.equals(DATE_FILTER.PAST_WEEK)){
minusHoursToDate(to, 1 * 24 * 7);
} else if(filter.equals(DATE_FILTER.PAST_MONTH)){
minusHoursToDate(to, 1 * 24 * 7 * 4);
} else if(filter.equals(DATE_FILTER.PAST_YEAR)){
minusHoursToDate(to, 1 * 24 * 7 * 4 * 12);
}
public static void minusHoursToDate(Date date, int hours){
date.setTime(date.getTime() - (hours * 3600000));
}
The problem I see here is with the calculation in terms of month and year. As months is not always 4-week aligned and a year is also affected.
What could be the best calculation for subtracting month & year?
Since java.util.Calendar is unsupported in GWT because of the complexity needed for its implementation, the final JS size, etc, I would go with a simple and lightweight solution based on JS.
Apart from the java Date implementation, in GWT we have the JsDate wrapper which includes all the methods available in the native JS date, so subtracting a month or a year should be as simpler as:
int months = -2;
int years = -3;
JsDate j = JsDate.create(new Date().getTime());
j.setMonth(j.getMonth() + months);
j.setFullYear(j.getFullYear() + years);
Date d = new Date((long)j.getTime());
You can do the same to manipulate other units:
getDate() Returns the day of the month (from 1-31)
getDay() Returns the day of the week (from 0-6)
getFullYear() Returns the year (four digits)
getHours() Returns the hour (from 0-23)
getMilliseconds() Returns the milliseconds (from 0-999)
getMinutes() Returns the minutes (from 0-59)
getMonth() Returns the month (from 0-11)
getSeconds() Returns the seconds (from 0-59)
public class Dating
{
// Note: this class has no instance variables!
/**
* Creates an empty Dating object so that you can call the methods
*/
public Dating()
{
// Empty constructor
}
/**
* Computes and returns the next year in which New Year's Day will
* fall on the same day of the week as in a given year
* #param theYear the given year
* #return the next year in which New Year's day is the same day
* of the week as in parameter theYear
*/
public int newYears(int theYear)
{
// TO DO: write body of this method here
}
/**
* Computes and returns the Date on which Election Day will fall
* in the USA for a given year.
*
* NOTE: By law, Thanksgiving Day is the first Tuesday after the first
* Monday in November.
*
* #param year the year for which to compute the date of Election Day
* #return the Date of Election Day for the specified year
*/
public Date electionTime(int year)
{
INSERT CODE HERE
}
I feel like I got the electionTime part correct but I am confused as in to where to begin for newYears. Any suggestions? I'm uncertain how to put together a code that would calculate not only when the date is but when it will happen again. I was not given a specific year to start with either.
/*
* Computes and returns the next year in which New Year's Day will
* fall on the same day of the week as in a given year.
*/
public int newYears(int year)
{
// First, find out what day of the week it falls on in year X
Calendar calendar = new GregorianCalendar(); // create a calendar object
calendar.set(year, 0, 1); // calendar.set([year], January, 1st)
int day = calendar.get(Calendar.DAY_OF_WEEK); // store this value for later
// The code between the curly braces below will be executed 30 times,
// the first time i = 1, the second i = 2, third i = 3, etc...
for(int i = 1; true; i++)
{
calendar.set(year + i, 0, 1); // set the calendar to the next year
if(calendar.get(Calendar.DAY_OF_WEEK) == day) // compare to the value we stored earlier, and if it's the same day...
{
return year + i; // we have the correct year!
}
}
}
EDIT
Okay I'm going overboard here but I must follow the calling of my inner geek.
I took a for loop and looped through and ran a bunch of sequential years through the function, subtracted to find the difference, and got this table:
in | out | difference
2004 2009 5
2005 2011 6
2006 2012 6
2007 2018 11
2008 2013 5
2009 2015 6
2010 2016 6
2011 2022 11
2012 2017 5
2013 2019 6
2014 2020 6
2015 2026 11
There's a very clear pattern that repeats every four years (because of leap year I suppose). Using this, we can write a sneaky/condensed version of this function:
public int sneakyNewYears(int year)
{
int diff = year % 4;
int add = -1;
if(diff == 0) add = 5;
if(diff == 1) add = 6;
if(diff == 2) add = 6;
if(diff == 3) add = 11;
return year + add;
}
This works fine for 98.6% percent of years, but testing this 'sneaky' function against the working function shows that there are a few years that this doesn't work for, for some odd reason... These years: 1575, 1577, 1578, 1579, 1580, 1581, 1582, 1691, 1695, 1696, 1697, 1698, 1699, 1700, 1791, 1795, 1796, 1797, 1798, 1799, 1800, 1891, 1895, 1896, 1897, 1898, 1899 and 1900.
Anyway.
I'm trying to write a simple utility method for adding aninteger number of days to a Joda time instant. Here is my first stab.
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
Days days = Days.days(numberOfDaysToAdd);
Interval interval = new Interval(instant, days);
return interval.getEnd().toInstant();
}
This works fine for the most part except when you consider the example when the number of days added takes you across the BST / GMT boundary. Here is a small example.
public class DateAddTest {
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Formatter used to translate Instant objects to & from strings.
*/
private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern(DATE_FORMAT).withZone(ZONE);
/**
* Date format to be used
*/
private static final String DATE_FORMAT = "dd/MM/yyyy";
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant toAdd = dateTime.toInstant();
Instant answer = JodaTimeUtils.addNumberOfDaysToInstant(toAdd, 2);
System.out.println(answer.toString(FORMATTER)); //25/10/2009
}
}
I think this problem is because the interval does not take into acount the fact that it has crossing the bst boundary. Any ideas of a better way to implement this would be appreciated.
If you want to deal with dates, don't use instants. I suspect it's correctly adding 48 hours to the instant.
Use a LocalDate instead, and then the plusDays method.
If you want to know the instant that occurs n days after the specified instant, at the same time of day, we could no doubt work out a way of doing that (split the instant into a LocalDate and a LocalTime, advance the LocalDate and then reassemble, or check whether LocalDateTime does what you want) but you need to work out what you want to happen if the original time occurs twice on the new day, or doesn't occur at all.
EDIT: Okay, so you need to work with an instant. Does that have to be in an original time zone? Could you use UTC? That would take away the DST issues. If not, what do you want it to do in cases of ambiguity or non-existence (e.g. at 12.30am before each of the transitions).
Assuming the rest of your code:
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant pInstant = dateTime.withFieldAdded(DurationFieldType.days(),2).toInstant();
System.out.println("24/10/2009 + 2 Days = " + pInstant.toString(FORMATTER));
}
This is the solution that was chosen.
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
return instant.toDateTime(ZONE).withFieldAdded(DurationFieldType.days(), numberOfDaysToAdd).toInstant();
}
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 8 years ago.
Improve this question
Does anyone knows a good business calendar library in java?
It should handle easy :) date calculations, taking holidays into account.
Ideally, besides configuring holidays and company off days, we should also be able to configure 'working hours' on a day basis so we can calculate SLA's and KPI's on working hours.
I know something like this is part of jboss jBpm, but I was wondering if their was any other project doing this.
Off course, open source is a big plus point!
Check out this library, it has functionality for holidays and such, it's built around joda.
http://objectlabkit.sourceforge.net/
Below is a very longwinded answer. It's something that I put together for exactly this purpose. It's not super user friendly, but it should give you want you are looking for.
It relies on the Apache commons project which can be acquired here: http://commons.apache.org/lang/
package com.yourPackageName;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class BusinessDayUtil {
private static Log log = LogFactory.getLog(BusinessDayUtil.class);
private static transient Map<Integer, List<Date>> computedDates = new HashMap<Integer, List<Date>>();
/*
* This method will calculate the next business day
* after the one input. This means that if the next
* day falls on a weekend or one of the following
* holidays then it will try the next day.
*
* Holidays Accounted For:
* New Year's Day
* Martin Luther King Jr. Day
* President's Day
* Memorial Day
* Independence Day
* Labor Day
* Columbus Day
* Veterans Day
* Thanksgiving Day
* Christmas Day
*
*/
public static boolean isBusinessDay(Date dateToCheck)
{
//Setup the calendar to have the start date truncated
Calendar baseCal = Calendar.getInstance();
baseCal.setTime(DateUtils.truncate(dateToCheck, Calendar.DATE));
List<Date> offlimitDates;
//Grab the list of dates for the year. These SHOULD NOT be modified.
synchronized (computedDates)
{
int year = baseCal.get(Calendar.YEAR);
//If the map doesn't already have the dates computed, create them.
if (!computedDates.containsKey(year))
computedDates.put(year, getOfflimitDates(year));
offlimitDates = computedDates.get(year);
}
//Determine if the date is on a weekend.
int dayOfWeek = baseCal.get(Calendar.DAY_OF_WEEK);
boolean onWeekend = dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY;
//If it's on a holiday, increment and test again
//If it's on a weekend, increment necessary amount and test again
if (offlimitDates.contains(baseCal.getTime()) || onWeekend)
return false;
else
return true;
}
/**
*
* This method will calculate the next business day
* after the one input. This leverages the isBusinessDay
* heavily, so look at that documentation for further information.
*
* #param startDate the Date of which you need the next business day.
* #return The next business day. I.E. it doesn't fall on a weekend,
* a holiday or the official observance of that holiday if it fell
* on a weekend.
*
*/
public static Date getNextBusinessDay(Date startDate)
{
//Increment the Date object by a Day and clear out hour/min/sec information
Date nextDay = DateUtils.truncate(addDays(startDate, 1), Calendar.DATE);
//If tomorrow is a valid business day, return it
if (isBusinessDay(nextDay))
return nextDay;
//Else we recursively call our function until we find one.
else
return getNextBusinessDay(nextDay);
}
/*
* Based on a year, this will compute the actual dates of
*
* Holidays Accounted For:
* New Year's Day
* Martin Luther King Jr. Day
* President's Day
* Memorial Day
* Independence Day
* Labor Day
* Columbus Day
* Veterans Day
* Thanksgiving Day
* Christmas Day
*
*/
private static List<Date> getOfflimitDates(int year)
{
List<Date> offlimitDates = new ArrayList<Date>();
Calendar baseCalendar = GregorianCalendar.getInstance();
baseCalendar.clear();
//Add in the static dates for the year.
//New years day
baseCalendar.set(year, Calendar.JANUARY, 1);
offlimitDates.add(offsetForWeekend(baseCalendar));
//Independence Day
baseCalendar.set(year, Calendar.JULY, 4);
offlimitDates.add(offsetForWeekend(baseCalendar));
//Vetrans Day
baseCalendar.set(year, Calendar.NOVEMBER, 11);
offlimitDates.add(offsetForWeekend(baseCalendar));
//Christmas
baseCalendar.set(year, Calendar.DECEMBER, 25);
offlimitDates.add(offsetForWeekend(baseCalendar));
//Now deal with floating holidays.
//Martin Luther King Day
offlimitDates.add(calculateFloatingHoliday(3, Calendar.MONDAY, year, Calendar.JANUARY));
//Presidents Day
offlimitDates.add(calculateFloatingHoliday(3, Calendar.MONDAY, year, Calendar.FEBRUARY));
//Memorial Day
offlimitDates.add(calculateFloatingHoliday(0, Calendar.MONDAY, year, Calendar.MAY));
//Labor Day
offlimitDates.add(calculateFloatingHoliday(1, Calendar.MONDAY, year, Calendar.SEPTEMBER));
//Columbus Day
offlimitDates.add(calculateFloatingHoliday(2, Calendar.MONDAY, year, Calendar.OCTOBER));
//Thanksgiving Day and Thanksgiving Friday
Date thanksgiving = calculateFloatingHoliday(4, Calendar.THURSDAY, year, Calendar.NOVEMBER);
offlimitDates.add(thanksgiving);
offlimitDates.add(addDays(thanksgiving, 1));
return offlimitDates;
}
/**
* This method will take in the various parameters and return a Date objet
* that represents that value.
*
* Ex. To get Martin Luther Kings BDay, which is the 3rd Monday of January,
* the method call woudl be:
*
* calculateFloatingHoliday(3, Calendar.MONDAY, year, Calendar.JANUARY);
*
* Reference material can be found at:
* http://michaelthompson.org/technikos/holidays.php#MemorialDay
*
* #param nth 0 for Last, 1 for 1st, 2 for 2nd, etc.
* #param dayOfWeek Use Calendar.MODAY, Calendar.TUESDAY, etc.
* #param year
* #param month Use Calendar.JANUARY, etc.
* #return
*/
private static Date calculateFloatingHoliday(int nth, int dayOfWeek, int year, int month)
{
Calendar baseCal = Calendar.getInstance();
baseCal.clear();
//Determine what the very earliest day this could occur.
//If the value was 0 for the nth parameter, incriment to the following
//month so that it can be subtracted alter.
baseCal.set(year, month + ((nth <= 0) ? 1 : 0), 1);
Date baseDate = baseCal.getTime();
//Figure out which day of the week that this "earliest" could occur on
//and then determine what the offset is for our day that we actually need.
int baseDayOfWeek = baseCal.get(Calendar.DAY_OF_WEEK);
int fwd = dayOfWeek - baseDayOfWeek;
//Based on the offset and the nth parameter, we are able to determine the offset of days and then
//adjust our base date.
return addDays(baseDate, (fwd + (nth - (fwd >= 0 ? 1 : 0)) * 7));
}
/*
* If the given date falls on a weekend, the
* method will adjust to the closest weekday.
* I.E. If the date is on a Saturday, then the Friday
* will be returned, if it's a Sunday, then Monday
* is returned.
*/
private static Date offsetForWeekend(Calendar baseCal)
{
Date returnDate = baseCal.getTime();
if (baseCal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
{
if (log.isDebugEnabled())
log.debug("Offsetting the Saturday by -1: " + returnDate);
return addDays(returnDate, -1);
}
else if (baseCal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
{
if (log.isDebugEnabled())
log.debug("Offsetting the Sunday by +1: " + returnDate);
return addDays(returnDate, 1);
}
else
return returnDate;
}
/**
* Private method simply adds
* #param dateToAdd
* #param numberOfDay
* #return
*/
private static Date addDays(Date dateToAdd, int numberOfDay)
{
if (dateToAdd == null)
throw new IllegalArgumentException("Date can't be null!");
Calendar tempCal = Calendar.getInstance();
tempCal.setTime(dateToAdd);
tempCal.add(Calendar.DATE, numberOfDay);
return tempCal.getTime();
}
}
jBPM (v3 at least) has a good business calendar implementation.
If you don't want the whole dependency on JBPM, I think you can take out just the calendar package
for date calculations try joda-time.sourceforge.net
but i have no idea about what you mean by configuring holidays. because each country has different holidays. but try that one first, it is good for date and time calculation.
I would suggest creating your own domestic holiday class that you can manage each of the holidays in. All of the holidays have rules on which day they will be. It is easy enough to program for these dates each year.
Martin Luther King day for example:
private static Date holidayHumanRights(int parmYear)
{
Date tempDate = new Date(parmYear, 0, 1); //January 1st...
try
{
tempDate = getNextDayofWeek(tempDate, "Monday");
//now point towards the 3rd Monday, which would be 2 weeks from
//current Monday date...
tempDate.advanceDays(2*7);
}
catch (Exception ex)
{
//throw or suppress the error, your choice
System.err.println(ex.toString());
}
return tempDate;
}
While thinking of the same problem I found out a Quartz Calendar. It has several problems like:
It is an implementation part of a scheduling library - using it apart from all quartz just as a holiday calendar is a bit hackish.
It has getNextIncludeTime method but no getPrevIncludeTime.
It has ugly and inconsistent API - AnnualCalendar has getter and setter that takes ArrayList, MonthlyCalendar has getter and setter that takes boolean[], both of them just expose class internals.
It has some poorly documented issues - you can chain calendars, but order of chaining is important - DailyCalendar created on AnnualCalendar is OK, AnnualCalendar created on DailyCalendar will break (hang, I think).
Still it is the best thing I could find. So maybe just take the source code, fix what's wrong and add what's missing?
I recently developed this open source project http://lamma.io which is designed for date generation.
For example:
Date(2015, 10, 5) to Date(2015, 10, 15) by 2 except Weekends
will yield
List(2015-10-05, 2015-10-07, 2015-10-09, 2015-10-13, 2015-10-15)
The project is licensed under DO WHAT YOU WANT TO PUBLIC LICENSE, so feel free to use / redistribute :)