Skip to content

Instantly share code, notes, and snippets.

@willianantunes
Created November 30, 2016 20:51
Show Gist options
  • Save willianantunes/e0ff57f9406c849d4d8ca63a5c754bed to your computer and use it in GitHub Desktop.
Save willianantunes/e0ff57f9406c849d4d8ca63a5c754bed to your computer and use it in GitHub Desktop.
package br.com.willianantunes.utils;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.joda.time.DateTimeConstants;
import org.joda.time.LocalDate;
/**
* This is designed only for Brazil and is based on Joda-Time library.
* @author Willian Antunes
*/
public class HolidayHelper {
private final static Map<LocalDate, Map<Scope, String>> holidaysConsolidated;
static {
holidaysConsolidated = new HashMap<LocalDate, Map<Scope, String>>();
// Federal holidays for the next 3 years
int[] years = new int[3];
years[0] = LocalDate.now().getYear();
years[1] = years[0] + 1;
years[2] = years[1] + 1;
constructFederalHolidays(years);
}
/**
* It constructs holidays which are changeable and include fixed ones as well.
*
* @param years
* The year which the holidays will be constructed.
* @see <a href=
* "https://pt.wikipedia.org/wiki/C%C3%A1lculo_da_P%C3%A1scoa">Cálculo
* da Páscoa</a>
*/
private static void constructFederalHolidays(int... years) {
if (years == null || years.length == 0)
throw new IllegalArgumentException("At least one year must be provided");
for (int year : years) {
int a = year % 19;
int b = year / 100;
int c = year % 100;
int d = b / 4;
int e = b % 4;
int f = (b + 8) / 25;
int g = (b - f + 1) / 3;
int h = (19 * a + b - d - g + 15) % 30;
int i = c / 4;
int k = c % 4;
int l = (32 + 2 * e + 2 * i - h - k) % 7;
int m = (a + 11 * h + 22 * l) / 451;
int month = (h + l - 7 * m + 114) / 31;
int day = ((h + l - 7 * m + 114) % 31) + 1;
// Páscoa
LocalDate pascoaDate = new LocalDate(year, month, day);
// Carnaval is 47 days prior Páscoa
// LocalDate carnavalDate = pascoaDate.minusDays(47);
// Corpus Christi is 60 days after Páscoa
LocalDate corpusChristiDate = pascoaDate.plusDays(60);
// Sexta-feira Santa is two days before Páscoa
LocalDate sextaFeiraSantaDate = pascoaDate.minusDays(2);
Map<Scope, String> holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Páscoa");
holidaysConsolidated.put(pascoaDate, holidaysRegion);
// holidaysRegion = new HashMap<Scope, String>();
// holidaysRegion.put(Scope.FEDERAL, "Carnaval");
// holidaysConsolidated.put(carnavalDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Corpus Christi");
holidaysConsolidated.put(corpusChristiDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Sexta-feira Santa");
holidaysConsolidated.put(sextaFeiraSantaDate, holidaysRegion);
// Confraternização universal
LocalDate confraternizacaoUniversalDate = new LocalDate(year, 1, 1);
// Tiradentes
LocalDate tiradentesDate = new LocalDate(year, 4, 21);
// Dia do trabalhador
LocalDate trabalhadorDate = new LocalDate(year, 5, 1);
// Independência do Brasil
LocalDate independenciaBrasilDate = new LocalDate(year, 9, 7);
// Nossa Sra. Aparecida
LocalDate nossaSraAparecidaDate = new LocalDate(year, 10, 12);
// Finados
LocalDate finadosDate = new LocalDate(year, 11, 2);
// Proclamação da República
LocalDate proclaRepubDate = new LocalDate(year, 11, 15);
// Natal
LocalDate natalDate = new LocalDate(year, 12, 25);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Confraternização Universal");
holidaysConsolidated.put(confraternizacaoUniversalDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Tiradentes");
holidaysConsolidated.put(tiradentesDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Dia do trabalhador");
holidaysConsolidated.put(trabalhadorDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Independência do Brasil");
holidaysConsolidated.put(independenciaBrasilDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Nossa Sra. Aparecida");
holidaysConsolidated.put(nossaSraAparecidaDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Finados");
holidaysConsolidated.put(finadosDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Proclamação da República");
holidaysConsolidated.put(proclaRepubDate, holidaysRegion);
holidaysRegion = new HashMap<Scope, String>();
holidaysRegion.put(Scope.FEDERAL, "Natal");
holidaysConsolidated.put(natalDate, holidaysRegion);
}
}
/**
* It'll insert the remaining holidays in order to make the map complete for
* other scopes such as Rio de Janeiro, Minas Gerais and so on.
*
* @param holidays
*/
public synchronized static void includeRemainingHolidays(Collection<Holiday> holidays) {
Map<Scope, String> temp;
for (Holiday holiday : holidays) {
if (holidaysConsolidated.containsKey(holiday.getDate())) {
// If the date is registered, then I can see if there is holiday
// for the particular region
temp = holidaysConsolidated.get(holiday.getDate());
if (!temp.containsKey(holiday.getScope())) {
// If it isn't then I can add it and refresh the content
// with the new map
temp.put(holiday.getScope(), holiday.getDescription());
holidaysConsolidated.remove(holiday.getDate());
holidaysConsolidated.put(holiday.getDate(), temp);
}
} else {
// As far as the date is not available, then it's necessary to
// add it
temp = new HashMap<Scope, String>();
temp.put(holiday.getScope(), holiday.getDescription());
holidaysConsolidated.put(holiday.getDate(), temp);
}
}
}
/**
* Verify if the particular region and its date have a holiday.
*
* @param date
* Any valid LocalDate.
* @param scope
* FEDERAL scope is always counted.
* @return A booleans statement if the date/region whether have holiday.
*/
public static boolean isHoliday(LocalDate date, Scope scope) {
if (!holidaysConsolidated.containsKey(date))
return false;
else {
Map<Scope, String> temp = holidaysConsolidated.get(date);
if (temp.containsKey(Scope.FEDERAL))
return true;
if (temp.containsKey(scope))
return true;
}
return false;
}
/**
* Verify if the particular region and its date have a holiday or whether
* the date is Saturday or Sunday.
*
* @param date
* Any valid LocalDate.
* @param scope
* FEDERAL scope is always counted even if you insert null.
* @return A booleans statement if the date/region whether have holiday.
*/
public static boolean isHolidayOrWeekend(LocalDate date, Scope scope) {
if (holidaysConsolidated.containsKey(date)) {
Map<Scope, String> temp = holidaysConsolidated.get(date);
if (temp.containsKey(Scope.FEDERAL))
return true;
if (temp.containsKey(scope))
return true;
}
if (isOnWeekend(date))
return true;
return false;
}
/**
*
* @param date
* @return
*/
public static boolean isOnWeekend(LocalDate date) {
if (date.getDayOfWeek() == DateTimeConstants.SATURDAY || date.getDayOfWeek() == DateTimeConstants.SUNDAY)
return true;
return false;
}
/**
* Starting from now, it'll jump a certain quantity of working days
* retrieving a new date. It can only consider weekends instead of holidays
* with weekends if you want so.
*
* @param numberOfWorkingDays
* @param onlyWeekends
* @return A new jumped date.
*/
public static LocalDate jumpWorkingDay(int numberOfWorkingDays, boolean onlyWeekends) {
LocalDate tmp = LocalDate.now();
int countWorkingDays = 0;
while (countWorkingDays < numberOfWorkingDays) {
if (onlyWeekends) {
if (isOnWeekend(tmp)) {
tmp = tmp.plusDays(1);
continue;
}
} else {
if (isHolidayOrWeekend(tmp, null)) {
tmp = tmp.plusDays(1);
continue;
}
}
tmp = tmp.plusDays(1);
countWorkingDays++;
}
return tmp;
}
public enum Scope {
FEDERAL, SAO_PAULO, RIO_DE_JANEIRO, MINAS_GERAIS, BAHIA
}
public interface Holiday {
String getDescription();
Scope getScope();
LocalDate getDate();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment