Business days and API Java 8, how to check?

3

How to check if a day is useful using the Java 8 API? It is possible to check if 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);
  }
}

Thankful,

    
asked by anonymous 30.07.2018 / 20:25

2 answers

4

For this you need to define your own list of holidays. There are several APIs that provide this kind of information, such as jollyday (I have never used it, but it seems to have holidays from several countries), and several other listed this link .

If you do not want to use an API, you can have your own registry too (and in this case, it's something you'll have to do manually).

Another problem with holidays is that not everyone has fixed dates (such as Easter, Carnival, etc.), and they 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 in your list.

However, regardless of the solution you choose (use an external API, manually register, include only national holidays, etc.), you just have to save all holidays in java.util.Set , and then check if the date is in Set (using the contains method), something 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);
    }
}

Since DayOfWeek is enum , I can compare them using == and != ( you do not have to use equals ).

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

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

// feriados que acontecem todo ano na mesma data, gerar lista para o ano especifico
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 holiday "amendments" (if it falls on Tuesday, then amend the second, then those two days are not useful). If you want, just check if the holiday is a Tuesday or Thursday (comparing 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 "mended."

For mobile holidays, there is a formula to calculate them (google search and you will find several websites explaining). I'll 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);

    // pascoa cai sempre no domingo, entao nao 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;
}

Please note that I also include holiday "amendments". If you do not want to, just remove the respective lines.

To create Set with all holidays, simply use the two methods above and merge all into a single Set :

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

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

Just as an add-on, you can encapsulate the scan logic in a java.time.temporal.TemporalQuery . The difference is that this interface works with java.time.temporal.TemporalAccessor (instead of a specific type like LocalDate ), so 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 this the class VerificaDiaUtil is responsible for maintaining the list of holidays. If you wish, you can change the builder to receive the year as a parameter (and then it only builds the holidays that year), or else it loads the multi-year holidays at once. It's up to you.

To use this class, simply 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);
    }
}
As TemporalQuery gets a java.time.temporal.TemporalAccessor , it works with other classes as well (as long as they have day, month and year - and consequently a day of the week), as LocalDateTime or ZonedDateTime .

    
06.08.2018 / 15:52
0

You can use 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);
            }
        }
    }
}
    
30.07.2018 / 20:37