Contar dias uteis

Estou fazendo uma classe para contar os dias uteis mas fica dando o seguinte erro:

java.lang.IllegalArgumentException: No partial converter found for type: java.time.LocalDate
at org.joda.time.convert.ConverterManager.getPartialConverter(ConverterManager.java:253)
at org.joda.time.LocalDate.(LocalDate.java:415)
at br.com.b2w.entregas.model.TestData.tempoEntreDatas(TestData.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:436)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:170)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:166)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:113)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:112)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:120)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:120)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:120)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

------------------------------------------CLASSE------------------------------------------------------

	DateTime dataInicial = new DateTime(2018, 07, 25, 0, 0);
	DateTime dataFinal = new DateTime(2018, 07, 1, 0, 0);

	int diasTotal = Days.daysBetween(dataInicial, dataFinal).getDays();
	
	HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(de.jollyday.HolidayCalendar.BRAZIL);
	Set<Holiday> feriados = gerenciadorDeFeriados.getHolidays(new DateTime().getYear());
	Set<LocalDate> dataDosFeriados = new HashSet<LocalDate>();
	for (Holiday h : feriados) {
		dataDosFeriados.add(new LocalDate(h.getDate(), ISOChronology.getInstance()));
	}
	
	// popula com os feriados brasileiros
	HolidayCalendar<LocalDate> calendarioDeFeriados
	  = new DefaultHolidayCalendar<LocalDate>(dataDosFeriados);
	 
	LocalDateKitCalculatorsFactory.getDefaultInstance()
	  .registerHolidays("BR", calendarioDeFeriados);
	 
	DateCalculator<LocalDate> calendario =
	  LocalDateKitCalculatorsFactory.getDefaultInstance().
	    getDateCalculator("BR", HolidayHandlerType.FORWARD);
	
	int diasNaoUteis = 0;
	 
	LocalDate dataInicialTemporaria = new LocalDate(dataInicial);
	LocalDate dataFinalTemporaria = new LocalDate(dataFinal);
	 
	while (!dataInicialTemporaria.isAfter(dataFinalTemporaria)) {
	    if (calendario.isNonWorkingDay(dataInicialTemporaria)) {
	       diasNaoUteis++;
	    }
	    dataInicialTemporaria = dataInicialTemporaria.plusDays(1);
	}

	System.out.println(diasTotal-diasNaoUteis);		DateTime dataInicial = new DateTime(2018, 07, 25, 0, 0);
	DateTime dataFinal = new DateTime(2018, 07, 1, 0, 0);

	int diasTotal = Days.daysBetween(dataInicial, dataFinal).getDays();
	
	HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(de.jollyday.HolidayCalendar.BRAZIL);
	Set<Holiday> feriados = gerenciadorDeFeriados.getHolidays(new DateTime().getYear());
	Set<LocalDate> dataDosFeriados = new HashSet<LocalDate>();
	for (Holiday h : feriados) {
		dataDosFeriados.add(new LocalDate(h.getDate(), ISOChronology.getInstance()));
	}
	
	// popula com os feriados brasileiros
	HolidayCalendar<LocalDate> calendarioDeFeriados
	  = new DefaultHolidayCalendar<LocalDate>(dataDosFeriados);
	 
	LocalDateKitCalculatorsFactory.getDefaultInstance()
	  .registerHolidays("BR", calendarioDeFeriados);
	 
	DateCalculator<LocalDate> calendario =
	  LocalDateKitCalculatorsFactory.getDefaultInstance().
	    getDateCalculator("BR", HolidayHandlerType.FORWARD);
	
	int diasNaoUteis = 0;
	 
	LocalDate dataInicialTemporaria = new LocalDate(dataInicial);
	LocalDate dataFinalTemporaria = new LocalDate(dataFinal);
	 
	while (!dataInicialTemporaria.isAfter(dataFinalTemporaria)) {
	    if (calendario.isNonWorkingDay(dataInicialTemporaria)) {
	       diasNaoUteis++;
	    }
	    dataInicialTemporaria = dataInicialTemporaria.plusDays(1);
	}

	System.out.println(diasTotal-diasNaoUteis);

esta dificil de ler e imaginar o q esta acontecendo.

Vc esta se baseando nesse exemplo?

Se sim, qual a linha que está lançando a exception?

Sim, estou me baseando nesse mesmo. A exception está acontecendo nessa linha:

dataDosFeriados.add(new LocalDate(h.getDate(), ISOChronology.getInstance()));

Estou com o mesmo problema, como resolveu?

Vou dar um procurada por que faz muito tempo.

PS: Desculpa a demora em responder, faz tempo que não entro no GUJ

Sei que o tópico é antigo, mas tem várias coisas que dá pra comentar (e quem sabe ser útil para quem for ler futuramente).

As datas inicial e final estão com valores fixos (no caso, 2018), mas depois é feita a chamada getHolidays(new DateTime().getYear()), ou seja, pega os feriados do ano atual. Então esse código só funcionou em 2018, pois depois disso ele pega os feriados do ano atual e portanto desconsidera os das datas envolvidas.

Outro detalhe é que a data inicial é posterior à data final (repare bem, o início é 25/07/2018, e o final é 01/07/2018).

E new LocalDate(h.getDate(), ISOChronology.getInstance()) não é necessário, pois h.getDate() já retorna um LocalDate, então poderia fazer simplesmente dataDosFeriados.add(h.getDate()). Mas na verdade acho que nem precisa criar este Set, pois dá para ver se uma data é feriado usando apenas o método isHoliday.

E para a contagem, em vez de calcular o total de dias entre as datas e subtrair dos dias não-úteis, por que não contar apenas os dias úteis? Assim:

DateTime dataInicial = new DateTime(2018, 07, 1, 0, 0);
DateTime dataFinal = new DateTime(2018, 07, 25, 0, 0);

HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(ManagerParameters.create(HolidayCalendar.BRAZIL));

int diasUteis = 0;
DateTime data = dataInicial;
while (!data.isAfter(dataFinal)) {
    if (data.getDayOfWeek() != DateTimeConstants.SATURDAY && data.getDayOfWeek() != DateTimeConstants.SUNDAY
            && !gerenciadorDeFeriados.isHoliday(data.toLocalDate())) {
        // se não é fim de semana nem feriado, conta como dia útil
        diasUteis++;
    }
    data = data.plusDays(1);
}
System.out.println(diasUteis);

Outro ponto é que getInstance(HolidayCalendar.BRAZIL) atualmente está deprecated (testei com Jollyday 0.4.9 e Joda-Time 2.10.14), então troquei para o código acima, que usa ManagerParameters.

Depois, no loop, basta ver se a data não é sábado, domingo ou feriado. Se não for nenhum desses, eu atualizo o contador de dias úteis. Inclusive, daria para usar um for no lugar do while:

int diasUteis = 0;
for (DateTime data = dataInicial; !data.isAfter(dataFinal); data = data.plusDays(1)) {
    if (data.getDayOfWeek() != DateTimeConstants.SATURDAY && data.getDayOfWeek() != DateTimeConstants.SUNDAY
            && !gerenciadorDeFeriados.isHoliday(data.toLocalDate())) {
        // se não é fim de semana nem feriado, conta como dia útil
        diasUteis++;
    }
}

Se quiser, podemos usar apenas LocalDate em vez de DateTime, já que o horário não importa:

LocalDate dataInicial = new LocalDate(2018, 7, 1);
LocalDate dataFinal = new LocalDate(2018, 7, 25);

HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(ManagerParameters.create(HolidayCalendar.BRAZIL));

int diasUteis = 0;
LocalDate data = dataInicial;
while (!data.isAfter(dataFinal)) {
    if (data.getDayOfWeek() != DateTimeConstants.SATURDAY && data.getDayOfWeek() != DateTimeConstants.SUNDAY
            && !gerenciadorDeFeriados.isHoliday(data)) {
        // se não é fim de semana nem feriado, conta como dia útil
        diasUteis++;
    }
    data = data.plusDays(1);
}
System.out.println(diasUteis);

Outro detalhe é que assim só verificamos os feriados nacionais. Se quiser os feriados estaduais ou municipais, tem que passar parâmetros a mais para isHolyday. Por exemplo, se fosse para considerar feriados do estado de São Paulo, teria que trocar para gerenciadorDeFeriados.isHoliday(data, "s"). Não sei porque diabos o Jollyday escolheu "s" em vez de "sp" para o estado de São Paulo, mas fazer o que, está assim no arquivo de configuração deles:

<tns:SubConfigurations hierarchy="s" description="Sao Paulo">

Curioso que os outros estados estão certos, com duas letras. Vai entender…

E se quiser da cidade de São Paulo, tem que passar o identificador do estado e da cidade: gerenciadorDeFeriados.isHoliday(data, "s", "cs"). A sigla "cs" é totalmente arbitrária, e está no mesmo arquivo já citado:

<tns:SubConfigurations hierarchy="s" description="Sao Paulo">
    <tns:Holidays>
        <tns:Fixed month="JULY" day="9" descriptionPropertiesKey="CONST_REVOLUTION"/>
        <tns:Fixed month="NOVEMBER" day="20" descriptionPropertiesKey="BLACK_AWARENESS"/>
    </tns:Holidays>
    <tns:SubConfigurations hierarchy="cs" description="City of Sao Paulo">
        <tns:Holidays>
            <tns:Fixed month="JANUARY" day="25" descriptionPropertiesKey="FOUNDATION"/>
            <tns:Fixed month="NOVEMBER" day="4" descriptionPropertiesKey="FOUNDATION"/>
        </tns:Holidays>
    </tns:SubConfigurations>
</tns:SubConfigurations>

Repare que “cs” está dentro de “s”, por isso precisa passar ambos. Um exemplo de como faz diferença:

HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(ManagerParameters.create(HolidayCalendar.BRAZIL));

LocalDate aniversarioSaoPaulo = new LocalDate(2017, 1, 25);
System.out.println(gerenciadorDeFeriados.isHoliday(aniversarioSaoPaulo)); // false
System.out.println(gerenciadorDeFeriados.isHoliday(aniversarioSaoPaulo, "s")); // false
System.out.println(gerenciadorDeFeriados.isHoliday(aniversarioSaoPaulo, "s", "cs")); // true

LocalDate conscienciaNegra = new LocalDate(2022, 11, 20);
System.out.println(gerenciadorDeFeriados.isHoliday(conscienciaNegra)); // false
System.out.println(gerenciadorDeFeriados.isHoliday(conscienciaNegra, "s")); // true
System.out.println(gerenciadorDeFeriados.isHoliday(conscienciaNegra, "s", "cs")); // true

Você pode verificar quais as hierarquias existentes usando gerenciadorDeFeriados.getCalendarHierarchy().

Por fim, você pode criar seu próprio arquivo de configuração, seguindo as instruções da documentação.


Java 8 e Joda-Time obsoleto

No site do Joda-Time tem um aviso dizendo que o projeto está basicamente “encerrado”, não há melhorias planejadas, e recomenda-se que se use a nova API do Java 8. Então a menos que você esteja preso à versões anteriores do Java, eu diria para seguir a recomendação.

No caso, para o Java 8, temos que mudar o Jollyday para 0.5.x - eu fiz os testes abaixo com a versão 0.5.10.

O código fica muito parecido, inclusive no java.time também tem uma classe chamada LocalDate, mas repare nas diferenças:

// ***ATENÇÃO***, não é o org.joda.time.LocalDate do Joda-Time, e sim o java.time.LocalDate (para Java >= 8)
LocalDate dataInicial = LocalDate.of(2018, 7, 1);
LocalDate dataFinal = LocalDate.of(2018, 7, 25);

HolidayManager gerenciadorDeFeriados = HolidayManager.getInstance(ManagerParameters.create(HolidayCalendar.BRAZIL));
int diasUteis = 0;
LocalDate data = dataInicial;
while (!data.isAfter(dataFinal)) {
    if (data.getDayOfWeek() != DayOfWeek.SATURDAY && data.getDayOfWeek() != DayOfWeek.SUNDAY
            && !gerenciadorDeFeriados.isHoliday(data)) {
        // se não é fim de semana nem feriado, conta como dia útil
        diasUteis++;
    }

    data = data.plusDays(1);
}
System.out.println(diasUteis);