我对日期计算有疑问。 这是我的情况;我有一个生日日期列表,像这样:
List<Date> birthdays = getAllBirthdays(); //getAllBirthdays() returns full list of dates
,现在要计算接下来的三个生日。我阅读了很多有关日期的信息,并在项目中实现了joda时间,因此我也尝试使用DateTime对象进行计算,但无法遇到一个有效的解决方案。有人可以在基本结构方面帮助我吗?
答案 0 :(得分:1)
您的问题根本不清楚。但我会刺它。
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)
定义一个自定义比较器,该比较器根据到该生日的时间比较日期,并考虑到可能是今年或明年。使用比较器排序。采取已排序列表的前三个元素。快乐的编码。 (我不是在为您编写代码,希望您从未期望过我们。)
还要查看是否可以将Date
或LocalDate
对象填充到列表中,而不是MonthDay
对象。尽管班级名称中的Date
并不代表日期,但该班级的设计不佳且已过时。 LocalDate
和MonthDay
是现代Java日期和时间API java.time中的类,与Date
之类的旧类相比,它要好得多。
是的,java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6 。
org.threeten.bp
导入日期和时间类。Comparator
interface java.time
。java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。