Created
November 30, 2016 20:51
-
-
Save willianantunes/e0ff57f9406c849d4d8ca63a5c754bed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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