Java Obter todos os dias úteis em um ano no formato AAAAMMDD

8

Meu desafio de domingo é obter e persistir dias úteis em um ano específico em um arquivo CSV etc.

Tenho o seguinte código e o problema que enfrento é: como imprimir datas em um formato específico, ou seja, AAAAMMDD, pois o código atualmente imprime algo como Sáb Jan 19 00:00:00 CET 2019.

Além disso, se eu puder excluir fins de semana e, geralmente, se houver uma maneira melhor de escrever um código muito menor no Java 8.

import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;

public class DatesInYear
{

    public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

    public static void main (String[] args) throws java.lang.Exception
    {

        Date dt = new Date();
        System.out.println(dt);

        List<Date> dates = printDates("20190101","20191231");


        Collections.reverse(dates);
        System.out.println(dates.size());
        for(Date date:dates)
        {
            SimpleDateFormat format1 = new SimpleDateFormat("yyyyMMdd");
            System.out.println(format1.format(date));

        }
    }
    public static List<Date> printDates(String fromDate, String toDate)
    {
        ArrayList<Date> dates = new ArrayList<Date>();

        try {

            Calendar fromCal = Calendar.getInstance();
            fromCal.setTime(dateFormat .parse(fromDate));

            Calendar toCal = Calendar.getInstance();
            toCal.setTime(dateFormat .parse(toDate));

            while(!fromCal.after(toCal))
            {
                dates.add(fromCal.getTime());
                fromCal.add(Calendar.DATE, 1);
            }


        } catch (Exception e) {
            System.out.println(e);
        }
        return dates;
    }
}
JavaMan
fonte
1
Não basta System.out.println(date), use o dateFormatque você já criou para formatá-lo da maneira que você precisar.
Thilo
2
String date1 = format1.format(date);então você não faz nada com date1. Mas você está no caminho certo.
Federico klez Culloca
1
Calendartambém pode ser usado para verificar o dia da semana.
Thilo
1
Qual é a sua versão do Java? 8 ou 9+?
ernest_k 23/02
1
Estou usando o Java 8
JavaMan 23/02

Respostas:

3

Aqui estão duas tomadas ligeiramente diferentes para o seu problema:

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class DatesInYear {

    public static void main(final String[] args) throws Exception {

        listWorkingDays(2020).forEach(System.out::println);
    }

    private static List<LocalDate> listWorkingDays(final int year) {

        IntStream
            .rangeClosed(1,      LocalDate.ofYearDay(year + 1, 1).minusDays(1).getDayOfYear())
            .mapToObj   (day  -> LocalDate.ofYearDay(year, day))
            .filter     (date -> date.getDayOfWeek().getValue() <= 5)
            .forEach    (System.out::println);

        return IntStream
            .rangeClosed(1,      LocalDate.ofYearDay(year + 1, 1).minusDays(1).getDayOfYear())
            .mapToObj   (day  -> LocalDate.ofYearDay(year, day))
            .filter     (date -> date.getDayOfWeek().getValue() <= 5)
            .collect    (Collectors.toList());
    }
}
Dave The Dane
fonte
Obrigado Dave, isso parece realmente elegante. Estou apenas montando alguns testes de unidade, mas parece realmente interessante. Cheers
JavaMan 23/02
Qual é o propósito do primeiro IntStream? Ele exibirá as datas duas vezes. Ou por que você não deixa apenas o primeiro IntStreame altera o tipo de retorno para nulo? Eu só estou curioso.
Sebastian I.
Como mencionei, são apenas duas abordagens do problema: uma imprime e outra retorna. O JavaMan pode escolher o que ele quiser. :-)
Dave The Dane
Eu não gosto que esses novos idiomas sejam ineficazes em geral, o LocalDate.ofYearDay criará uma nova instância toda vez que github.com/frohoff/jdk8u-jdk/blob/… , se você colocar esse código em um método de uso intenso , ele não será o mais eficiente.
Leo
7

Desde 2020, você realmente deve estar adotando a java.time.*API.

Embora eu tenha certeza de que provavelmente há uma maneira realmente interessante de obter os dias "úteis" entre as datas, eu segui o método da força bruta ...

LocalDate ld = LocalDate.of(2020, Month.JANUARY, 1);
LocalDate endDate = ld.plusYears(1);

// You don't "have" to put into a list, but I like to seperate my
// code responsbilities ;)
List<LocalDate> workDays = new ArrayList<>(365);
System.out.println(endDate);
while (ld.isBefore(endDate)) {
    // This would be a good place for a delegate to determine if we want the specific day
    // as it could then take into account public holidays
    if (ld.getDayOfWeek() == DayOfWeek.SATURDAY || ld.getDayOfWeek() == DayOfWeek.SUNDAY) {
        // NOOP
    } else {
        workDays.add(ld);
    }
    ld = ld.plusDays(1);
}

Em seguida, você pode simplesmente usar a DateTimeFormatterpara formatar o LocalDateem um formato desejado, por exemplo ...

DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMdd");
List<String> formats = workDays.stream().map(value -> value.format(format)).collect(Collectors.toList());

for (String value : formats) {
    System.out.println(value);
}
MadProgrammer
fonte
@Eugene Então - é essencialmente uma versão resumida do que eu fiz de qualquer maneira - é bom saber
MadProgrammer
mais ou menos, sim, em última análise LongStream.rangeClosed(0, steps).mapToObj( n -> this.plusMonths(months * n).plusDays(days * n));... Mas em relação ao seu código, isso ArrayListpode ter o tamanho de 240uma estimativa aproximada de 360 - 2 * 40. Além disso, quando você tem um, if(condition) {NO-OP} else {OP}deve ser alterado paraif(!condition){OP}
Eugene
1
Entendo.
Eugene
1
@Eugene Como o feedback embora Mad
MadProgrammer
7
    Set<DayOfWeek> weekend = EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
    LocalDate.of(2019, Month.JANUARY, 1)
             .datesUntil(LocalDate.of(2020, Month.DECEMBER, 31), Period.ofDays(1))
             .filter(x -> !weekend.contains(x.getDayOfWeek()))
             .forEachOrdered(System.out::println);
Eugene
fonte
2

Uma maneira lúcida de exibir todos os dias da semana em um período usando o Java 8 e a API de data e hora moderna é a seguinte:

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        List<LocalDate> dates = getDates("2019-01-01", "2019-12-31");
        for (LocalDate date : dates) {
            System.out.println(DateTimeFormatter.BASIC_ISO_DATE.format(date));
        }
    }

    public static List<LocalDate> getDates(String fromDate, String toDate) {
        LocalDate startDate = LocalDate.parse(fromDate);
        LocalDate endDate = LocalDate.parse(toDate).plusDays(1);
        long range = ChronoUnit.DAYS.between(startDate, endDate);
        return Stream.iterate(startDate, date -> date.plusDays(1)).limit(range)
                .filter(d -> !(d.getDayOfWeek() == DayOfWeek.SATURDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY))
                .collect(Collectors.toList());
    }
}

Resultado:

20190101
20190102
20190103
20190104
20190107
20190108
...
...
...
20191226
20191227
20191230
20191231
Arvind Kumar Avinash
fonte
1
Muito agradável. Duas coisas que eu mudaria incluem o armazenamento ChronoUnit.DAYS.between(startDate, endDate)em uma variável em vez de recalcular o tempo todo. Além disso, para o código Java 9+, pode-se usar em .takeWhile(endDate::isAfter)vez de limit(). Além disso, você pode usar a comparação enum em vez de comparar os nomes dos dias da semana:!(d -> d.getDayOfWeek() == DayOfWeek.SUNDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY)
ernest_k
Obrigado, @ernest_k pelo feedback positivo. Seu comentário significa muito para mim. Vou melhorar como você sugeriu.
Arvind Kumar Avinash
@ernest_k - incorporei seu primeiro comentário, mas deixei o segundo para os leitores se incorporarem, porque o OP mencionou que ele está usando o Java 8. Além disso, meu entendimento era que a JVM otimizaria em ChronoUnit.DAYS.between(startDate, endDate)vez de recalcular o tempo todo. Preciso da sua ajuda para entender se não estava correto.
Arvind Kumar Avinash
Não sei se chegar ao ponto de fazer d.getDayOfWeek().name().equals("SATURDAY")quando d.getDayOfWeek() == DayOfWeek.SATURDAYo que seria mais robusto - IMHO
MadProgrammer
1
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
    List<LocalDate> dates = getDates("2019-01-01", "2019-12-31");
    for (LocalDate date : dates) {
        System.out.println(DateTimeFormatter.BASIC_ISO_DATE.format(date));
    }
}

public static List<LocalDate> getDates(String fromDate, String toDate) {
    LocalDate startDate = LocalDate.parse(fromDate);
    LocalDate endDate = LocalDate.parse(toDate).plusDays(1);
    long range = ChronoUnit.DAYS.between(startDate, endDate);
    return Stream.iterate(startDate, date -> date.plusDays(1)).limit(range)
            .filter(d -> !(d.getDayOfWeek() == DayOfWeek.SATURDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY))
            .collect(Collectors.toList());
}

}

Yasser Abdel-Maksoud
fonte
O código é lindo. Você pode adicionar algumas explicações sobre o porquê e como ele realiza a tarefa.
Ole VV
-1

tente isto:

// Usando Java 8

 DateTimeFormatter oldPattern = DateTimeFormatter
        .ofPattern("yyyy-MM-dd HH:mm:ss");
    DateTimeFormatter newPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    LocalDateTime datetime = LocalDateTime.parse(input, oldPattern);
    output = datetime.format(newPattern);

    System.out.println("old format date: " + input);
    System.out.println("new format date : " + output);
Raghavendra
fonte
Código legal, mas sua relação com a pergunta está saindo de mim?
Ole VV
Acabei de dar o meu código como exemplo aqui e ele pode seguir o caminho que usei. mas apenas ele precisa usar o novo formato de data do meu código.
Raghavendra 23/02