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

时间:2019-07-01 17:27:22

标签: java android date

我对日期计算有疑问。 这是我的情况;我有一个生日日期列表,像这样:

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

,现在要计算接下来的三个生日。我阅读了很多有关日期的信息,并在项目中实现了joda时间,因此我也尝试使用DateTime对象进行计算,但无法遇到一个有效的解决方案。有人可以在基本结构方面帮助我吗?

3 个答案:

答案 0 :(得分:1)

您的问题根本不清楚。但我会刺它。

java.time

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

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 ) ) {
    …
}

如果在此之后,请应用the current year编号以生成LocalDate

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

答案 1 :(得分:0)

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

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

  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

答案 2 :(得分:0)

定义一个自定义比较器,该比较器根据到该生日的时间比较日期,并考虑到可能是今年或明年。使用比较器排序。采取已排序列表的前三个元素。快乐的编码。 (我不是在为您编写代码,希望您从未期望过我们。)

还要查看是否可以将DateLocalDate对象填充到列表中,而不是MonthDay对象。尽管班级名称中的Date并不代表日期,但该班级的设计不佳且已过时。 LocalDateMonthDay是现代Java日期和时间API java.time中的类,与Date之类的旧类相比,它要好得多。

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

是的,java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6

  • 在Java 8和更高版本以及更新的Android设备(API级别26以上)中,内置了现代API。
  • 在Java 6和7中,获得了ThreeTen反向端口,这是现代类的反向端口(JSR 310的ThreeTen;请参见底部的链接)。
  • 在(较旧的)Android上,使用Android版本的ThreeTen Backport。叫做ThreeTenABP。并确保您使用子包从org.threeten.bp导入日期和时间类。

链接