【问题标题】:Counting number of days of a particular month when given a duration在给定持续时间时计算特定月份的天数
【发布时间】:2019-12-28 18:43:08
【问题描述】:

给出了一个持续时间。 例如:1 月 15 日至 3 月 15 日

我想在给定的持续时间内计算属于每个月的天数。 在这个例子中, 在该期间内一月份的天数; 15 在此期间二月的天数; 28 在这段时间内,三月的天数; 15

我正在寻找一个解决方案,除了遍历持续时间的每个日期并检查 Date.getMonth() = "Month I want to check against"

是否有更简单的方法来使用 Java Date 或 Java SQL Date 中的方法或使用任何其他 Date 类型?

【问题讨论】:

  • SQL 在这种情况下显得无关紧要
  • 它是一个给定的字符串,您需要计算天数吗?你需要每个月的天数吗?字符串中没有年份?
  • 包括开始日期吗?结束日期呢?在示例中,您要计算比赛的 14 天还是 15 天? 2 月的 28 天还是 29 天?我们可以告诉年份,以便我们确定它是否是闰年吗?
  • 是的,这是可能的,但这个问题似乎非常广泛且研究不足。使用LocalDateChronoUnit.DAYS.between()。不要使用java.util.Date。该类设计不良且早已过时,尽管名称不代表日期。
  • (1-) 这和 Swing 有什么关系?

标签: java date java.util.date


【解决方案1】:

Map < YearMonth , Long > 使用 lambda 语法

这是一个使用流和 lambda 的简洁代码的解决方案。虽然此解决方案确实会遍历时间范围的每个日期,但代码的简单性和清晰性可能会超过这种低效率。

使用LocalDate 作为开始和结束日期。使用YearMonth 跟踪每个月。

LocalDate start = LocalDate.of( 2019 , 1 , 15 );
LocalDate stop = LocalDate.of( 2019 , 3 , 16 );

创建一个Map 以保留每个月的天数。

Map < YearMonth, Long > map =
        start
                .datesUntil( stop )
                .collect(
                        Collectors.groupingBy(
                                ( LocalDate localDate ) -> YearMonth.from( localDate ) ,
                                TreeMap::new ,
                                Collectors.counting()
                        )
                );

转储到控制台。

{2019-01=17, 2019-02=28, 2019-03=15}

System.out.println( map );
  1. 给定一个开始日期,LocalDate::datesUntil 提供LocalDate 对象的Stream,递增days

  2. 然后只需对SortedMapTreeMap)进行分组,以保持月份按时间顺序排列,按YearMonth分类并计算该范围内该月的天数。

如果你想要总天数,你可以这样做

long totalDays = d.datesUntil(LocalDate.of(2019, 3, 16)).count();

【讨论】:

  • 令人印象深刻。它是遍历持续时间的每个日期,提问者想避免这种情况,但考虑到简洁性,在这种情况下它可能是值得的。
  • 聪明的解决方案。我将您的代码更改为使用 YearMonth 而不是 Month,以防日期范围超过 12 个月。
  • 好主意。在我发布答案之前,我真的没有时间考虑这个问题,并查看了localDate。请注意,我处理了datesUntilexclusive 部分相当笨拙,这也应该改进。也许通过使用 plus() 添加一天
  • 作为一个选项,groupingBy 的第二个参数可以是 LinkedHashMap::new,以允许按时间顺序显示日期。
  • @WJS 如果您的意思是 datesUntil 运行到但不包括 3 月 16 日的限制,这是一个功能,而不是一个错误。 Half-Open 方法通常用于日期时间处理,通常是最明智的方法。
【解决方案2】:

这只是一个简单的例子,我结合一些基础研究。

LocalDate from = LocalDate.of(2019, Month.JANUARY, 15);
LocalDate to = LocalDate.of(2019, Month.MARCH, 15);

DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MMM");

LocalDate date = from;
while (date.isBefore(to)) {
    LocalDate endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
    if (endOfMonth.isAfter(to)) { 
        endOfMonth = to;
    }

    // Inclusive to exclusive comparison
    long days = ChronoUnit.DAYS.between(date, endOfMonth.plusDays(1));
    System.out.println(days + " days in " + date.format(monthFormatter));

    date = date.plusMonths(1).withDayOfMonth(1);
}

这将输出

17 days in Jan.
28 days in Feb.
15 days in Mar.

可能有更好的方法可以实现相同的结果,但正如我所说,我只是将它与一点谷歌搜索和反复试验结合在一起。

如前所述,您应避免使用较旧、过时且已被有效弃用的 DateCalendar 和相关类。

【讨论】:

  • 当问题在一月份问了 15 天时,那一定是问题中的错误,而不是答案中的错误。 1 月 15 日至 31 日(含)为 17 天。
  • @OleV.V.老实说,我真的无法对那部分做出正面或反面
  • 没有。 :-)
【解决方案3】:

试试这个。可能是你想要的这样的东西。所以它设置了一个开始日期和结束日期,然后循环每个飞蛾直到结束日期并计算天数。我没有彻底测试过,但应该接近你的概念。

public static void main(String[] args) throws ParseException {
    String startDateS = "01/15/2019";
    String endDateS = "03/15/2019";

    SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
    Date startDate = dateFormat.parse(startDateS);
    Date endDate = dateFormat.parse(endDateS);

    while (endDate.compareTo(startDate) > 0) {
        Calendar c = Calendar.getInstance();
        c.setTime(startDate);
        c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date endOfMonth = c.getTime();
        if( endDate.compareTo(endOfMonth) > 0 )
            System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endOfMonth));
        else
            System.out.println("Count Month " + getMonthForInt(c.get(Calendar.MONTH)) + " " + getDifferenceDays(startDate, endDate));
        c.add(Calendar.DAY_OF_MONTH, 1);
        startDate = c.getTime();
    }
}

static String getMonthForInt(int num) {
    String month = "wrong";
    DateFormatSymbols dfs = new DateFormatSymbols();
    String[] months = dfs.getMonths();
    if (num >= 0 && num <= 11) {
        month = months[num];
    }
    return month;
}

public static int getDifferenceDays(Date d1, Date d2) {
    int daysdiff = 0;
    long diff = d2.getTime() - d1.getTime();
    long diffDays = diff / (24 * 60 * 60 * 1000) + 1;
    daysdiff = (int) diffDays;
    return daysdiff;
}

你可以在 Java 8 中使用 Java.time 来做同样的事情。

public static void main(String[] args) throws ParseException {
        String startDateS = "01/15/2019";
        String endDateS = "03/15/2019";

        DateTimeFormatter format1 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
        LocalDate startDate = LocalDate.parse(startDateS, format1);
        LocalDate endDate = LocalDate.parse(endDateS, format1);

        while (endDate.compareTo(startDate) > 0) {
            LocalDate endOfMonth = startDate.minusDays(startDate.getDayOfMonth()).plusMonths(1);
            if( endDate.compareTo(endOfMonth) > 0 )
                System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endOfMonth));
            else
                System.out.println("Count Month " + getMonthForInt(startDate) + " " + getDifferenceDays(startDate, endDate));
            startDate = endOfMonth.plusDays(1);
        }
    }

    static String getMonthForInt(LocalDate startDate) {
        return startDate.getMonth().getDisplayName(
                TextStyle.FULL , 
                Locale.US 
            );
    }

    public static long getDifferenceDays(LocalDate d1, LocalDate d2) {
       // return Duration.between(d2.atStartOfDay(), d1.atStartOfDay()).toDays();
        return ChronoUnit.DAYS.between(d1, d2) + 1;
    }

【讨论】:

  • 请不要教年轻人使用早已过时且臭名昭著的SimpleDateFormat类。至少不是第一选择。而且不是没有任何保留。今天我们在java.time, the modern Java date and time API, 和它的DateTimeFormatter 中做得更好。
猜你喜欢
  • 1970-01-01
  • 2022-10-23
  • 2011-06-20
  • 2018-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多