【问题标题】:Calendar day adding not working as expected日历日添加未按预期工作
【发布时间】:2020-09-07 12:52:58
【问题描述】:

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

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

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

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

因为该月没有 31 天,所以即使给出 31 天,算法也会采用最接近的日期。

2 月 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) 根本不工作。

这个方法应该找到未来给定数字的前一天。我错过了什么吗?我已经阅读了文档和其他 StackOverflow 答案,但我仍然无法得到它。

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_MONTH、DAY_OF_YEAR 和 DAY_OF_WEEK 上运行。

附:我找不到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 ...

【问题讨论】:

  • 您为什么要使用这个过时的日期时间 API?您不使用java.time 的任何具体原因?你到底想做什么?只是找到一个日期或日期?能不能解释的详细一点?
  • 什么是lastDayNumber?
  • 没问题,但我还是没有得到你想要的?收到一个数字(它到底代表什么?)并找到前一天?我很乐意在这里提供帮助,但我没有得到输入,也没有想要的结果。请您为此添加示例吗?
  • @OleV.V.如果您输入 7 而今天是 9 月 7 日,您应该得到 10 月 6 日。如果你输入 8,你应该得到 9 月 7 日(今天)
  • 感谢您提供所有信息以回答 cmets 中的问题。最好编辑问题并将其添加到那里,这样我们就可以将所有内容集中在一个地方。

标签: android date kotlin calendar


【解决方案1】:

您可以使用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() 计算今天的日期(注意:只是日期部分,不涉及一天中的时间、偏移量或时区)并返回一个 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)
    }
}

注意:我猜到月份输入日期小于今天的情况,因为您没有为其指定任何行为。这种情况的处理方式与一个月中的相等天数(参数和今天)相同。

【讨论】:

  • 我遇到的问题与日历实例完全相同。我用代码示例更新了我的问题。
  • 我想我找到了答案,看来我必须重新分配。 stackoverflow.com/questions/50543514/… 可能我也有同样的问题
  • 好的,虽然我的算法错了,但我成功了 :)),谢谢你的帮助
【解决方案2】:

这是我的出发点。与 deHaar 一样,我强烈建议您使用现代 Java 日期和时间 API java.time 来进行日期和时间工作。您尝试使用的 Calendar 类设计不佳且早已过时。

既然你想要午夜前 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);

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

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

链接: Oracle tutorial: Date Time 解释如何使用 java.time。

【讨论】:

    猜你喜欢
    • 2019-10-29
    • 2023-04-03
    • 1970-01-01
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多