Java获取一年中以YYYYMMDD格式表示的所有工作日

7

我的星期天的挑战是将一个特定年份的工作日保存到CSV、等文件中。

我有以下代码,我面临的问题是:如何以特定格式打印日期,即YYYYMMDD,因为当前的代码输出的是类似于Sat Jan 19 00:00:00 CET 2019这样的格式。

此外,如果可以排除周末,或者是否存在更短的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;
    }
}

1
不要只是使用 System.out.println(date),而是使用你已经创建的 dateFormat 来按照你需要的方式进行格式化。 - Thilo
2
String date1 = format1.format(date); 然后你没有对 date1 进行任何操作。但是你走在了正确的轨道上。 - Federico klez Culloca
1
我正在使用Java 8。 - JavaMan
1
你使用老旧的、繁琐的遗留日期 API,而不是 java.time,有特定的原因吗? - Zabuzard
1
如果您正确地标记了您的问题(添加日期和时间),那么日期/时间专家将能够找到您的问题,您将更快地获得良好的回复。 - Zabuzard
显示剩余3条评论
6个回答

6
    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);

6

既然已经是2020年了,你真的应该拥抱java.time.* API。

虽然我相信可能有一种非常巧妙的方法来获取两个日期之间的“工作日”,但我选择了蛮力法...

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);
}

然后,您可以简单地使用 DateTimeFormatterLocalDate 格式化为所需的格式,例如...
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);
}

在2020年已经存在一种迭代的方法。 - Eugene
@Eugene So - 这本质上是我已经做过的事情的简化版本 - 很高兴知道。 - MadProgrammer
有点像,是的,最终它会执行 LongStream.rangeClosed(0, steps).mapToObj( n -> this.plusMonths(months * n).plusDays(days * n));... 但关于你的代码,那个 ArrayList 可能会有 240 的大小,这是一个大约为 360 - 2 * 40 的粗略估计。此外,当你有一个 if(condition) {NO-OP} else {OP} 时,应该将其更改为 if(!condition){OP} - Eugene
@Eugene,“no OP”只是因为我不想两次执行plusDay,而且我也试图避免金字塔式的嵌套 - schematics。预设数组列表大小只是为了提高性能 - 当然我们可以争论范围,但365保证覆盖核心基础知识 - 我倾向于一个可重用的代码解决方案,在此情况下标准并不事先确定。 - MadProgrammer
1
我明白了,可以理解。 - Eugene
1
@Eugene 虽然喜欢反馈 - MadProgrammer

2

使用Java 8和现代日期时间API清晰地显示日期范围内所有工作日的方法如下:

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());
    }
}

输出:

20190101
20190102
20190103
20190104
20190107
20190108
...
...
...
20191226
20191227
20191230
20191231

1
非常好。我会改变两件事,包括将 ChronoUnit.DAYS.between(startDate, endDate) 存储在变量中而不是每次重新计算。此外,对于 Java 9+ 代码,可以使用 .takeWhile(endDate::isAfter) 替代 limit()。另外,您可以使用枚举比较来代替比较星期几的名称:!(d -> d.getDayOfWeek() == DayOfWeek.SUNDAY || d.getDayOfWeek() == DayOfWeek.SUNDAY) - ernest_k
谢谢 @ernest_k 的积极反馈。你的评论对我来说意义重大。我会按照你的建议进行改进。 - Arvind Kumar Avinash
@ernest_k - 我已经采纳了你的第一个意见,但将第二个意见留给读者自己补充,因为原帖中提到使用的是Java 8。同时,我的理解是JVM会优化 ChronoUnit.DAYS.between(startDate, endDate) 而不是每次重新计算。我需要你的帮助来理解这个是否正确。 - Arvind Kumar Avinash
我不确定为什么要执行 d.getDayOfWeek().name().equals("SATURDAY"),因为 d.getDayOfWeek() == DayOfWeek.SATURDAY 更加健壮 - 在我看来。 - MadProgrammer
谢谢@MadProgrammer的宝贵建议,我已根据您的建议更新了我的答案。 - Arvind Kumar Avinash
显示剩余2条评论

2

以下是两种略有不同的解决方案:

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,这看起来非常流畅。我正在组织一些单元测试,但这看起来非常有趣。干杯 - JavaMan
第一个 IntStream 的目的是什么?它会将日期显示两次。或者为什么不只保留第一个 IntStream 并将返回类型更改为 void?我只是好奇。 - user1234SI.
正如我所提到的,这只是对问题的两种不同看法:一种打印,一种返回。JavaMan可以选择任何一种。 :-) - Dave The Dane
我不喜欢这些新的惯用语通常是低效的,LocalDate.ofYearDay每次都会创建一个新的实例。如果您将此代码放在高度使用的方法中,它可能不是最有效的。 - Leo

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());
}

}


代码很漂亮。您可能需要添加一些解释,说明它是如何完成任务的以及为什么这样做。 - Ole V.V.

-1

试试这个:

// 使用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);

不错的代码,但它与所提问的问题之间的关系有点模糊了? - Ole V.V.
我只是举了我的代码作为例子,并且他可以按照我使用的方式进行。但是他只需要从我的代码中获取新的日期格式。 - GRVPrasad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接