2

I am trying to implement something that can countdown how many days left before an event occurs. While dealing with days within the same year is fine. But now I am having a problem with showing a number of days which includes days that are not in the current year. For instance, if I input the target date as 1 January 2020 and current date as 30 September 2019. It will return -272 (days) which does not make any sense in general.

I have tried understanding and using code online but none of them worked for me.

What the code below does is to return a number of days left but only work when the days are within the same year. The labeledDate variable is a part of an inputted string (dateStamp) that shows only date that has the format of "dd MMMM yyyy".

// Show how many days left before event occurs
        try {
            String labeledDate = viewHolder.dateStamp.getText().toString().substring(5);

            Calendar currentDay = Calendar.getInstance();
            final Calendar targetDay = Calendar.getInstance();

            SimpleDateFormat ourDateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.getDefault());
            targetDay.setTime(ourDateFormat.parse(labeledDate));

            if (targetDay.compareTo(currentDay) > 0) {

                int daysLeft = targetDay.get(Calendar.DAY_OF_YEAR) - currentDay.get(Calendar.DAY_OF_YEAR);

                if (daysLeft == 1) {
                    viewHolder.dayCounter.setText("Tomorrow");
                }
                else {
                    viewHolder.dayCounter.setText(daysLeft + " days");
                }
            }
            else if (targetDay.compareTo(currentDay) == 0) {
                viewHolder.dayCounter.setText("Today");
            }
            else {
                viewHolder.dayCounter.setText("Expired");
            }
        } catch (ParseException e) {
            Log.d("PARSE EXCEPTION", e.getMessage());
        }

According to my current understanding of Calendar.DAY_OF_YEAR is that it consists of (maybe) 365 days. And if I enter the date 1st January 2020, it won't listen to the year whereas it will just focus on the day and month. Hence, if I entered the target date as 1st January 2020 and current date as 30th September 2019, then it would return -272 (days) from the day on 1st January minus the day on 30th September (years ignored).

What I want is that if I enter the inputs above then it will return 93 days instead of -272 days. Anything that can help me achieve this is appreciated.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Suri
  • 693
  • 7
  • 16
  • 1
    As an aside consider throwing away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends, and adding [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project in order to use `java.time`, the modern Java date and time API. It is so much nicer to work with. – Ole V.V. Sep 30 '19 at 15:11

3 Answers3

2

You logic is working for same year just because calendar.get(Calendar.DAY_OF_YEAR) returns day count from 1 Jan to given date for the year. So in case of same year, target-date and current-date are working as expected. Lets explore it by an example

targetCalendar.set(2019, 11, 31);

System.out.println(currentCalendar.get(java.util.Calendar.DAY_OF_YEAR)); // Day of year since 1 Jan 2019, which is 273
System.out.println(targetCalendar.get(java.util.Calendar.DAY_OF_YEAR));  // Day of year since 1 Jan 2019, which is 365
// So your logic as per question works fine, gives 92 days (365 - 273) as result for below
System.out.println(targetCalendar.get(java.util.Calendar.DAY_OF_YEAR) - currentCalendar.get(java.util.Calendar.DAY_OF_YEAR));


// No lets change date for targetCalendar into next year
targetCalendar.set(2020, 0, 1);

System.out.println(currentCalendar.get(java.util.Calendar.DAY_OF_YEAR)); // Day of year since 1 Jan 2019, which is 273
System.out.println(targetCalendar.get(java.util.Calendar.DAY_OF_YEAR));  // Day of year since 1 Jan 2020, which is 1
// So your logic as per question works fine, gives -272 days (1 - 273) as result for below
System.out.println(targetCalendar.get(java.util.Calendar.DAY_OF_YEAR) - currentCalendar.get(java.util.Calendar.DAY_OF_YEAR));

Hope you got the problem in your logic.


You can get result by doing like

long msDiff = targetDay.getTimeInMillis() - currentDay.getTimeInMillis();
long daysDiff = TimeUnit.MILLISECONDS.toDays(msDiff);
Pankaj Kumar
  • 81,967
  • 29
  • 167
  • 186
2

This is how I always get the days difference:

String cDate = "09/30/2019";
String fDate = "09/30/2020";
SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy");
Date currentDate = df.parse(cDate);
Date futureDate = df.parse(fDate);

long diffMill = Math.abs(currentDate.getTime() - futureDate.getTime());
long diff = TimeUnit.DAYS.convert(diffMill, TimeUnit.MILLISECONDS);
user1506104
  • 6,554
  • 4
  • 71
  • 89
1

java.time and ThreeTenABP

With java.time, the modern Java date and time API, it’s simpler:

    DateTimeFormatter dateFormatter
            = DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.ENGLISH);

    String labeledDate = "01 January 2020";
    LocalDate currentDay = LocalDate.now(ZoneId.of("America/Louisville"));
    try {
        LocalDate targetDay = LocalDate.parse(labeledDate, dateFormatter);
        long daysLeft = ChronoUnit.DAYS.between(currentDay, targetDay);
        if (daysLeft < 0) {
            System.out.println("Expired");
        } else if (daysLeft == 0) {
            System.out.println("Today");
        } else if (daysLeft == 1) {
            System.out.println("Tomorrow");
        } else {
            System.out.println("" + daysLeft + " days");
        }
    } catch (DateTimeParseException dtpe) {
        System.out.println("Parse exception: " + dtpe.getMessage());
    }

When I ran this code snippet just now, the output was:

92 days

Please substitute your desired time zone if it didn’t happen to be America/Louisville. Because it is never the same date in all places on Earth.

If you don’t want to base the if-else construct on the number of days left alone, you may also use the isEqual, isBefore and/or isAfter method of LocalDate.

But doesn’t it require Android API level 26?

It doesn’t require API level 26. 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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161