Working days and Java 8 API, how to check?

Asked

Viewed 5,107 times

6

How do I verify that a day is useful using the Java 8 API? It is possible to check whether it is Saturday or Sunday, but how to check for example holiday like September 7 (Independence of Brazil) or Good Friday?

int ano = 2014;
int mes = 5;
YearMonth anoMes = YearMonth.of(ano, mes);


List<LocalDate> listaDosDiasUteisDoMes = new ArrayList<>();

for(int dia=1; dia <= anoMes.lengthOfMonth(); dia++){ 
  LocalDate data = anoMes.atDay(dia); 

  if(!data.getDayOfWeek().equals(DayOfWeek.SATURDAY) &&   
    !data.getDayOfWeek().equals(DayOfWeek.SUNDAY)){

      listaDosDiasUteisDoMes.add(data);
  }
}
  • You plan to do this offline or have an internet connection?

  • Initially offline. But if so, I change it to online.

3 answers

9


For this you need to define your own list of holidays. There are several Apis that provide this type of information, such as the jollyday (never used, but seems to have the holidays of various countries), and several other listed at this link.

If you don’t want to use an API, you can have your own registration as well (and in this case, it’s something you’ll have to do manually).

Another problem with holidays is that not all holidays have fixed dates (such as Easter, Carnival, etc., which fall every year on different dates), and should be calculated according to the year. In addition, there are national, state and municipal holidays, and you should decide whether or not to include them on your list.

Anyway, regardless of the chosen solution (use an external API, manually register, include only national holidays, etc.), just keep all the holidays in one java.util.Set, and then check if the date is on Set (using the method contains), sort of like this:

Set<LocalDate> feriados = // feriados, ver mais abaixo como montar este Set
for (int dia = 1; dia <= anoMes.lengthOfMonth(); dia++) {
    LocalDate data = anoMes.atDay(dia);

    if (data.getDayOfWeek() != DayOfWeek.SATURDAY
        && data.getDayOfWeek() != DayOfWeek.SUNDAY
        && !feriados.contains(data)) {
        listaDosDiasUteisDoMes.add(data);
    }
}

Like java.time.DayOfWeek is a enum, I can compare them using == and != (don’t need to use equals).


To ride the Set with the holidays, I suggest separating into 2 methods: one for the fixed holidays (which is easier, since they always fall in the same day every year) and another for the mobile holidays.

I suggest you spend the year as a parameter for these methods, so you can build a list of java.time.LocalDate, which is what you need.

// feriados que acontecem todo ano na mesma data, gerar lista para o ano específico
public Set<LocalDate> getFeriadosFixos(int year) {
    Set<LocalDate> dates = new HashSet<>();

    // 7 de setembro
    dates.add(LocalDate.of(year, 9, 7));
    // natal
    dates.add(LocalDate.of(year, 12, 25));
    // ... adicione todos os outros

    return dates;
}

This method does not consider the "amendments" of holidays (if it falls on Tuesday, amend the second, then these two days are not useful). If you want, just check if the holiday is a Tuesday or Thursday (comparing the DayOfWeek):

// 7 de setembro
LocalDate seteSetembro = LocalDate.of(year, 9, 7);
dates.add(seteSetembro);
// se cai na terça, inclui a segunda ("emenda" de feriado)
if (seteSetembro.getDayOfWeek() == DayOfWeek.TUESDAY) {
    dates.add(seteSetembro.minusDays(1));
}
// se cai na quinta, inclui a sexta ("emenda" de feriado)
if (seteSetembro.getDayOfWeek() == DayOfWeek.THURSDAY) {
    dates.add(seteSetembro.plusDays(1));
}

Do this for all holidays that can be "amended".

For mobile holidays, there is a formula to calculate them (search Google and you will find several websites explaining). I will use one I found:

// calcula páscoa, carnaval, corpus christi e sexta-feira santa
public Set<LocalDate> getFeriadosMoveis(int year) {
    Set<LocalDate> dates = new HashSet<>();

    LocalDate pascoa;
    LocalDate carnaval;
    LocalDate corpusChristi;
    LocalDate sextaFeiraSanta;

    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;

    pascoa = LocalDate.of(year, month, day);

    // Carnaval 47 dias antes da pascoa (sempre cai na terça)
    carnaval = pascoa.minusDays(47);

    // CorpusChristi 60 dias apos a pascoa
    corpusChristi = pascoa.plusDays(60);

    sextaFeiraSanta = pascoa.minusDays(2);

    // páscoa cai sempre no domingo, entao não precisaria adicionar como feriado
    // dates.add(pascoa);

    // carnaval: adicionar um dia antes e depois (emenda de segunda e quarta-feira de cinzas)
    dates.add(carnaval);
    dates.add(carnaval.minusDays(1)); // emenda a segunda-feira
    dates.add(carnaval.plusDays(1)); // quarta-feira de cinzas

    // corpus christi, emendar (adicionar a sexta)
    dates.add(corpusChristi);
    // if apenas para confirmar se é quinta-feira
    if (corpusChristi.getDayOfWeek() == DayOfWeek.THURSDAY) {
        dates.add(corpusChristi.plusDays(1)); // adicionar a sexta-feira
    }

    dates.add(sextaFeiraSanta);

    return dates;
}

Note that I have also included the holiday "amendments". If you do not want, simply remove the respective lines.

To create the Set with all holidays, just use the above two methods and put it all together in one Set:

Set<LocalDate> feriados = new HashSet<>();
feriados.addAll(getFeriadosFixos(year));
feriados.addAll(getFeriadosMoveis(year));

The parameter year may be the year you are using (anoMes.getYear()), for example.


Just as a complement, it is possible to encapsulate the verification logic in a java.time.temporal.TemporalQuery. The difference is that this interface works with a java.time.temporal.TemporalAccessor (rather than a specific type like LocalDate), then the code to check the day of the week is a little different:

public class VerificaDiaUtil implements TemporalQuery<Boolean> {

    private Set<LocalDate> feriados = new HashSet<>();

    public VerificaDiaUtil() {
        this.feriados = // constrói a lista de feriados
    }

    @Override
    public Boolean queryFrom(TemporalAccessor temporal) {
        // obter o dia da semana
        DayOfWeek dow = DayOfWeek.from(temporal);
        return dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY
               // extrair o LocalDate e verificar se está no Set de feriados
               && !feriados.contains(LocalDate.from(temporal));
    }
}

With that the class VerificaDiaUtil is responsible for maintaining the holiday list. If you want, you can change the constructor to receive the year as a parameter (and then it only builds the holidays of that year), or it already carries the holidays of several years at once. At your discretion.

To use this class, just use the method query of LocalDate:

VerificaDiaUtil diaUtil = new VerificaDiaUtil();
for (int dia = 1; dia <= anoMes.lengthOfMonth(); dia++) {
    LocalDate data = anoMes.atDay(dia);
    if (data.query(diaUtil)) {
        listaDosDiasUteisDoMes.add(data);
    }
}

Like TemporalQuery gets a java.time.temporal.TemporalAccessor, it works with other classes as well (provided they have day, month and year - and consequently, a day of the week), as LocalDateTime or ZonedDateTime. If you pass a class that has no day of the week (like LocalTime, for example, which only has the hours), this code will make an exception.

4

You can use the Google Calendar API as proposed by the documentation:

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.DateTime;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.Event;
import com.google.api.services.calendar.model.Events;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

public class CalendarQuickstart {
    private static final String APPLICATION_NAME = "Google Calendar API Java Quickstart";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    private static final String TOKENS_DIRECTORY_PATH = "tokens";

    /**
    * Global instance of the scopes required by this quickstart.
    * If modifying these scopes, delete your previously saved credentials/ folder.
    */
    private static final List<String> SCOPES = Collections.singletonList(CalendarScopes.CALENDAR_READONLY);
    private static final String CREDENTIALS_FILE_PATH = "credentials.json";

    /**
    * Creates an authorized Credential object.
    * @param HTTP_TRANSPORT The network HTTP Transport.
    * @return An authorized Credential object.
    * @throws IOException If the credentials.json file cannot be found.
    */
    private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
        // Load client secrets.
        InputStream in = CalendarQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));

        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
                .setAccessType("offline")
                .build();
        return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
    }

    public static void main(String... args) throws IOException, GeneralSecurityException {
        // Build a new authorized API client service.
        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
        Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
                .setApplicationName(APPLICATION_NAME)
                .build();

        // List the next 10 events from the primary calendar.
        DateTime now = new DateTime(System.currentTimeMillis());
        Events events = service.events().list("primary")
                .setMaxResults(10)
                .setTimeMin(now)
                .setOrderBy("startTime")
                .setSingleEvents(true)
                .execute();
        List<Event> items = events.getItems();
        if (items.isEmpty()) {
            System.out.println("No upcoming events found.");
        } else {
            System.out.println("Upcoming events");
            for (Event event : items) {
                DateTime start = event.getStart().getDateTime();
                if (start == null) {
                    start = event.getStart().getDate();
                }
                System.out.printf("%s (%s)\n", event.getSummary(), start);
            }
        }
    }
}

0

In my case I have a table of holidays and with it I used the routines below:

private LocalDate calculaDia(LocalDate data, int diaMesSemanaAno, boolean trataFinalDeSemana, boolean trataFeriado) {
    int maisOuMenosUm = 1;
    // trata quando quiser subtrair uma data
    if (diaMesSemanaAno < 0) {
        diaMesSemanaAno = diaMesSemanaAno * -1;
        maisOuMenosUm = -1;
    }
    while (diaMesSemanaAno > 0) {
        data = data.plusDays(maisOuMenosUm);
        // quando for final de semana
        if (trataFinalDeSemana) {
            // verifica se e final de semana...
            data = eFinalSemana(data, maisOuMenosUm);
        }

        // quando tem feriado
        if (trataFeriado) {
            data = eFeriado(data, maisOuMenosUm);
        }

        diaMesSemanaAno--;
    }
    return data;
}

private LocalDate eFeriado(LocalDate data, int somaSubtraiUm) {
    String feriado = DateUtil.getData(data, DateUtil.DATA_BR_PADRAO_BARRAS);
    TwebferiadosTO twebferiadosTO = new TwebferiadosTO();
    twebferiadosTO.setDataFormatada(feriado);
    twebferiadosTO = consultar(twebferiadosTO);
    if (twebferiadosTO != null) {
        data = data.plusDays(somaSubtraiUm);
        data = eFeriado(data, somaSubtraiUm);
        //verifica se nao e final de semana
        data = eFinalSemana(data, somaSubtraiUm);
    }
    return data;
}

private LocalDate eFinalSemana(LocalDate data, int somaSubtraiUm) {
    if (data.getDayOfWeek() == DayOfWeek.SATURDAY
            || data.getDayOfWeek() == DayOfWeek.SUNDAY) {
        data = data.plusDays(somaSubtraiUm);
        data = eFinalSemana(data, somaSubtraiUm);
    }
    return data;
}
  • Validates holiday in sequence. Ex. Carnival
  • Validates holiday falling on the weekend

Browser other questions tagged

You are not signed in. Login or sign up in order to post.