繁体   English   中英

从日期列表中获取下一个生日

[英]Get next birthday from lists of dates

我有一个关于日期计算的问题。 这是我的情况; 我有一个生日日期列表,如下所示:

List<Date> birthdays = getAllBirthdays(); //getAllBirthdays() returns full list of dates

现在想计算接下来的三个生日。 我阅读了很多关于日期的内容,并在我的项目中实现了 joda 时间,所以我也尝试使用 DateTime 对象进行计算,但找不到一个很好的解决方案,有效。 任何人都可以帮助我建立基本结构或其他吗?

你的问题一点都不清楚。 但我会试一试。

时间

Joda-Time 项目现在处于维护模式。 它的创建者和领导者 Stephen Colebourne 继续领导 JSR 310 工作,并将其java.time实现内置到 Java 8 及更高版本中。

MonthDay

听起来您的年度生日列表应该包含MonthDay类的对象。 此类表示没有年份和时区的月份和月份中的某一天。

List< MonthDay > birthdays = new ArrayList<>() ;
birthdays.add( MonthDay.of( Month.MARCH , 27 ) ) ;
birthdays.add( MonthDay.of( Month.DECEMBER , 13 ) ) ;

获取当前月份。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
MonthDay mdCurrent = MonthDay.now( z ) ;

循环列表。 将每个MonthDay与我们当前的月日md

if( md.isAfter( mdCurrent ) ) {
    …
}

如果之后,则应用当前年份编号以生成LocalDate

if( md.isAfter( mdCurrent ) ) {
    int y = Year.now( z ).getValue() ;
    LocalDate ld = md.atYear( y ) ;
}

有几种方法可以解决这个问题。 一种方法是为自己创建一个出生月份和年份的排序索引,以便您可以快速找到给定日期的以下生日。

您需要意识到的第一件事是“下一个”生日不应该考虑年份。 如果您只是对日期进行排序或比较,那么年份将给您无效的日期。 绕过此问题的一种方法是为生日创建一个密钥,该密钥唯一标识生日,而与年份无关。 更具体地说,您需要一个可以按“时间顺序”排序的密钥。 例如:

  LocalDate d1 = LocalDate.parse("1980-10-31');
  int key1 = d1.getMonthValue() * 100 + d1.getDayOfMonth();

  LocalDate d2 = LocalDate.parse("2001-10-31');
  int key2 = d2.getMonthValue() * 100 + d2.getDayOfMonth();

  key1 == key2

因此,现在您有了一种生成密钥的方法,您将需要生成生日密钥的映射。 您可以使用 groupingBy 收集器轻松完成此操作。

  Function<LocalDate, Integer> key = (d) -> d.getMonthValue() * 100 + d.getDayOfMonth();
  Map<Integer, List<LocalDate>> lookup = birthdays.stream().collect(Collectors.groupingBy(key));

请注意,我们正在收集同一天的生日作为列表,因为可能存在同一天、同一年或不同年份的多个生日。

最后,您将需要一种方法来查找接下来的三个生日。 一种方法是对键进行排序,搜索下一个条目的位置,然后读出以下两个。 因为这是一个可排序的键列表,我们可以创建键的索引,对其进行排序并使用二分搜索来查找下一个条目。 所以让我们创建索引:

    List<Integer> searchIndex = lookup.keySet().stream().sorted().collect(Collectors.toList());

所以现在我们可以使用searchIndex来查找与输入日期的键最近的键。 然后,我们可以使用该密钥找到生日列表从该键lookup

只需查看索引中的下两个键,我们就可以得到以下两个日期。 但请注意,当你到达索引的末尾时,你会想要换到索引的开头。 例如,如果您在 12 月 31 日搜索接下来的三个生日,您会希望它在 1 月继续检查。

        List<LocalDate> birthdays = getAllBirthdays();

        Function<LocalDate, Integer> key = (d) -> d.getMonthValue() * 100 + d.getDayOfMonth();
        Map<Integer, List<LocalDate>> lookup = birthdays.stream().collect(Collectors.groupingBy(key));
        List<Integer> searchIndex = lookup.keySet().stream().sorted().collect(Collectors.toList());
        int index = Collections.binarySearch(searchIndex, key.apply(LocalDate.now()));

        // Find the positive index if the index doesn't contain the current MMdd 
        if (index < 0) {
            index = -index -1;
        }

        for (int i = 0; i < 3; ++i) {
            // Wrap around to the start of the year
            if (index >= searchIndex.size()) {
                index = 0;
            }
            System.out.println(lookup.get(searchIndex.get(index++)));
        }

我在上面的代码中使用了java.util.LocalDate ,但您可以轻松地使用joda's LocalDate 两者都优于使用java.util.Date

定义一个自定义比较器,根据距该生日的时间来比较您的日期,同时考虑到可能是今年或明年。 使用比较器排序。 取排序列表的前三个元素。 快乐编码。 (我不是为您编写代码,希望您永远不会期望我们这样做。)

还要看看您是否可以将LocalDateMonthDay对象填充到您的列表中,而不是Date对象。 尽管有类名,但Date并不代表日期,而且该类的设计很差,而且已经过时了。 LocalDateMonthDay是来自 java.time 的类,现代 Java 日期和时间 API,它比Date类的旧类更易于使用。

问题:我可以在 Android 上使用 java.time 吗?

是的,java.time 在较旧和较新的 Android 设备上都能很好地工作。 它只需要至少Java 6

  • 在 Java 8 及更高版本和更新的 Android 设备(从 API 级别 26)中,现代 API 是内置的。
  • 在 Java 6 和 7 中获得 ThreeTen Backport,现代类的 backport(ThreeTen for JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。 它被称为 ThreeTenABP。 并确保使用子包从org.threeten.bp导入日期和时间类。

链接

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM