-3

i have a problem about a date calculation. Here my situation; I have a list of birthday-dates, like this:

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

and want now to calculate the next three birthdays. I read much about dates and also implemented joda time to my project, so i also tried to calculate with DateTime objects, but cant came across a good solution, that worked. Can anybody help me with a base structure or else?

  • 4
    can you share some examples please about what you want? – Youcef LAIDANI Jul 01 '19 at 17:28
  • This Question is not clear. Please put more effort into your writing before posting. Be very *specific* about your given input, your desired processing, and your expected output. And always **search Stack Overflow** before posting. Your issues on this topic are almost certainly covered by existing posts. – Basil Bourque Jul 01 '19 at 20:00

3 Answers3

1

java.time

The Joda-Time project is now in maintenance mode. Its creator and leader, Stephen Colebourne, went on to lead the JSR 310 effort and its java.time implementation built into Java 8 and later.

MonthDay

Sounds like your list of annual birthdays should be holding objects of MonthDay class. This class represents a month and a day-of-month without a year and without a time zone.

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

Get the current month.

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

Loop the list. Compare each MonthDay to our current month-day md.

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

If after, apply the current year number to generate a LocalDate.

if( md.isAfter( mdCurrent ) ) {
    int y = Year.now( z ).getValue() ;
    LocalDate ld = md.atYear( y ) ;
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

There are several ways you can tackle this problem. One way is to create yourself a sorted index of the birth month and year so you can quickly find the following birthdays for a given day.

The first thing you need to realise is that "next" birthdays shouldn't take into account the year. If you just sort or compare on the date, the year is going going to give you invalid dates. One way you to get round this to create a key for a birthday that is uniquely identifies the birthday irrespective of the year. More specifically you want a key that can be ordered in "chronological order". For example:

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

So now that you have a way of generating a key, you will want to generate a map of the keys to the birthdays. You can do this easily enough by using a groupingBy collector.

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

Note that we are collecting birthdays on the same days as a list, as it may be the case that there are multiple birthdays on the same day, in the same or different years.

Finally you will want a way to find the next three birthdays. One way to do this is to sort the keys, search for the position of the next entry and then read off the following two. Because this is an order-able list of keys we can create an index of the keys, sort it and use a binary search to find the next entry. So let's create the index:

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

So now we can use the searchIndex to find the nearest key to the key for the input date. We can then use that key to find the list of birthdays for that key from the lookup.

We can get the following two dates by just looking at the next two keys in the index. Though note that you will want to wrap to the beginning of the index when you get to the end. For example if you search for next three birthdays on the 31st of December, you will want it to continue checking in January.

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++)));
}

I've used java.util.LocalDate in the above code, but you could as easily use joda's LocalDate. Both are preferable to using java.util.Date

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Marc G. Smith
  • 876
  • 6
  • 8
0

Define a custom comparator that compares your dates according to how long it is until that birthday, taking into account that it may be this year or next year. Sort using the comparator. Take the first three elements of the sorted list.

Also see if instead of Date objects you can fill LocalDate or MonthDay objects into your list. A Date despite the class name doesn’t represent a date, and the class is poorly designed and long outdated. LocalDate and MonthDay are classes from java.time, the modern Java date and time API, which is much nicer to work with than the old classes like Date.

Question: Can I use java.time on Android?

Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

Links

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161