添加日历日未按预期工作

Calendar day adding not working as expected

我正在尝试编写一个算法,该算法基于 1 到 31(一个月中的一天)之间的数字,它将 return 之前那一刻的毫秒数 day/number。 该算法将始终使用今天日期。

例子: 输入:5 输出:1601845199000,即 2020 年 10 月 4 日 23:59:59

================

输入:31 输出:1601499599000,即 2020 年 9 月 30 日 23:59:59

因为月份没有 31 天,即使给定 31 天,算法也会采用最接近的可能日期。

二月 29/30 也是如此

    val calendar = Calendar.getInstance()        
    calendar.set(Calendar.HOUR_OF_DAY, 23)
    calendar.set(Calendar.MINUTE, 59)
    calendar.set(Calendar.SECOND, 59)
    calendar.clear(Calendar.MILLISECOND)

    val lastDayNumber = 31

    var found = false
    while (!found) {
        if (calendar.get(Calendar.DAY_OF_MONTH) <= lastDayNumber) {
            found = true
        }

        if (calendar.get(Calendar.DAY_OF_MONTH) < lastDayNumber) {
            calendar.add(Calendar.DATE, 1)
        }
    }

问题是 calendar.add(Calendar.DATE, 1) 根本不工作。

此方法应查找未来给定数字的前一天。我错过了什么吗?我已经阅读了文档和其他 Whosebug 答案,但我仍然无法理解。

Edit1:我也尝试过 calendar.add(Calendar.DAY_OF_MONTH, 1) 和 +1,但是,-1 工作得很好。

编辑2:

查看 GregorianCalendar.java 我看到以下内容:

        // Handle week, day and AM_PM fields which involves
        // time zone offset change adjustment. Convert the
        // given amount to the number of days.
        case WEEK_OF_YEAR:
        case WEEK_OF_MONTH:
        case DAY_OF_WEEK_IN_MONTH:
            delta *= 7;
            break;

        case DAY_OF_MONTH: // synonym of DATE
        case DAY_OF_YEAR:
        case DAY_OF_WEEK:
            break;

如果我以 WEEL_OF_YEAR 为例,相同的代码将 运行 也用于 week_of_month 和 day_of_week_in_month,因为没有中断所有 3 个字段 运行 通过相同的代码。

但是,我在 DAY_OF_MONTHDAY_OF_YEARDAY_OF_WEEK.

上完全没有看到 运行 的代码

P.S。我找不到 DATE 字段

============================================= =====

使用LocalDateTime

fun getLastDayOfMonth(date: LocalDateTime): Long {
    val lastDayNumber = lastDay?.filter { it.isDigit() }!!.toInt()

    var found = false
    while (!found) {
        if (date.dayOfMonth <= lastDayNumber) {
            found = true
        } else {
            date.plusDays(1)
        }
    }

    return date.toEpochSecond(ZoneOffset.UTC)
}

date.plusDays(1) is not changing the date variable to be next day ...

您可以使用 java.time,它为此提供了非常方便的方法。

看这个例子:

import java.time.LocalDate

fun main() {
    val fourDaysAgo = findDate(4)
    println(fourDaysAgo)
}

fun findDate(daysAgo: Long): LocalDate {
    return LocalDate.now().minusDays(daysAgo)
}

输出4天​​前的日期(今天执行/2020-09-07)

2020-09-03

LocalDate.now() 计算今天的日期(注意:只是日期部分,不涉及一天中的时间、偏移量或时区)和 returns 具有方法的 LocalDate minusDays(long days) 通过在必要时调整月份和年份来正确计算 日期前

如果您执行以下操作:

println(LocalDate.of(2020, 1, 1).minusDays(1))

输出将是

2019-12-31

如果您还需要时间部分,甚至可能需要偏移量或时区才能真正正确地减去天、小时、分钟或任何时间单位,请查看 类 java.time...

在您的问题下发表以下评论后进行编辑:
如果您输入 7 而今天是 9 月 7 日,您应该得到 10 月 6 日。如果您输入 8,您应该得到 9 月 7 日(今天)

fun findDate(dayOfMonth: Int): LocalDate {
    val now = LocalDate.now()
    val currentDayOfMonth = now.getDayOfMonth()
    
    if (currentDayOfMonth <= dayOfMonth) {
        // use the current month with the given day of month
        return now.withDayOfMonth(dayOfMonth)
                    // subtract a day (will change the month when dayOfMonth == 1)
                    .minusDays(1)
                    // and add a month
                    .plusMonths(1)
    } else {
        // otherwise just take the current month with the given day of month
        return now.withDayOfMonth(dayOfMonth)
                    // and subtract a day
                    .minusDays(1)
    }
}

注意:我猜的是输入的第几天小于今天的情况,因为你没有为它指定任何行为。这种情况的处理方式与每月的相等天数(参数和今天)相同。

我来了。与 deHaar 一样,我强烈建议您使用 java.time,现代 Java 日期和时间 API,作为您的日期和时间工作。您尝试使用的 Calendar class 设计不佳且已过时。

因为你想要午夜前 1 秒的时间,让我们先用这个值声明一个常量:

private static final Duration timeBeforeStartOfDay = Duration.ofSeconds(1); 

如果您更喜欢某一天,很容易将其更改为 1 分钟、1 毫秒或 1 纳秒。

我们需要考虑一些特殊情况。我认为这段代码可以做到:

    ZoneId zone = ZoneId.systemDefault();
    int dayNumber = 31;
    
    LocalDate today = LocalDate.now(zone);

    YearMonth ym = YearMonth.from(today);
    if (dayNumber <= today.getDayOfMonth()) {
        // Next month
        ym = ym.plusMonths(1);
    }
    ZonedDateTime targetDateTime;
    if (dayNumber > ym.lengthOfMonth()) {
        // Use end of month
        targetDateTime = ym.plusMonths(1)
                .atDay(1)
                .atStartOfDay(zone)
                .minus(timeBeforeStartOfDay);
    } else {
        // Use day number
        targetDateTime = ym.atDay(dayNumber)
                .atStartOfDay(zone)
                .minus(timeBeforeStartOfDay);
    }
    long milliseconds = targetDateTime.toInstant().toEpochMilli();
    
    System.out.println("Target date and time: " + targetDateTime);
    System.out.println("Target milliseconds: " + milliseconds);

我们 运行 在哪个时区会有所不同。我 运行 它在 Europe/Bucharest 时区,因为我想它可能是你的。今天(9 月 7 日)的输出是:

Target date and time: 2020-09-30T23:59:59+03:00[Europe/Bucharest]
Target milliseconds: 1601499599000

Link: Oracle tutorial: Date Time 解释如何使用 java.time.