[英]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
定义一个自定义比较器,根据距该生日的时间来比较您的日期,同时考虑到可能是今年或明年。 使用比较器排序。 取排序列表的前三个元素。 快乐编码。 (我不是为您编写代码,希望您永远不会期望我们这样做。)
还要看看您是否可以将LocalDate
或MonthDay
对象填充到您的列表中,而不是Date
对象。 尽管有类名,但Date
并不代表日期,而且该类的设计很差,而且已经过时了。 LocalDate
和MonthDay
是来自 java.time 的类,现代 Java 日期和时间 API,它比Date
类的旧类更易于使用。
是的,java.time 在较旧和较新的 Android 设备上都能很好地工作。 它只需要至少Java 6 。
org.threeten.bp
导入日期和时间类。Comparator
接口的文档java.time
。java.time
到 Java 6 和 7 的 backport(ThreeTen for JSR-310)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.