user3011902
user3011902

Reputation:

Getting Time Until a Date in Android

I am using a Date and Time Dialog to get a Date and Time for an Event Specifed by the User. That data is the Converted by doing the following:

int yearE = Integer.valueOf(evntDate.split("/")[2]);
int monthE = Integer.valueOf(evntDate.split("/")[1]);
int dayE = Integer.valueOf(evntDate.split("/")[0]); 
int hour = Integer.valueOf(evntTm.split(":")[0]);
int min = Integer.valueOf(evntTm.split(":")[1]);

With the Values of:

eventDate = "3/5/2015";
eventTime = "13:2";

I then get that data and COnvert it into Milliseconds and Store that in the Database:

newCalendar.set(yearE, monthE, dayE,hour, min, 0);
startTime = newCalendar.getTimeInMillis();
...

When I load the Info from the Database, I try to calculate the amount of time left until the Specified date. So I do the following:

Long timeL = Long.valueOf(time);
Calendar eventDay = Calendar.getInstance();
eventDay.setTimeInMillis(timeL);
Calendar today = Calendar.getInstance();
long diff = eventDay.getTimeInMillis() - today.getTimeInMillis();

// CONVERT:
int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(diff);
int minutes = (int) TimeUnit.MILLISECONDS.toMinutes(diff);
int hours = (int) TimeUnit.MILLISECONDS.toHours(diff);
int days = (int) TimeUnit.MILLISECONDS.toDays(diff);
...

When I log the above data, the days are usually around 30-32 and the rest of the data is incorrect as well. What am I doing wrong? Or what are some alternatives?

Upvotes: 2

Views: 2576

Answers (4)

Vikram
Vikram

Reputation: 51571

Are you expecting to see something similar:

seconds = 36 (always less than 60)
minutes = 12 (always less that 60)
hours   = 17 (always less than 24)
days    = 45 (always less that 31 if # of months is used, else < 366 if # of years is used)
...
...

from:

// CONVERT:
int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(diff);
int minutes = (int) TimeUnit.MILLISECONDS.toMinutes(diff);
int hours = (int) TimeUnit.MILLISECONDS.toHours(diff);
int days = (int) TimeUnit.MILLISECONDS.toDays(diff);

Its a logical error then. TimeUnit.MILLISECONDS.toXXXX(long) converts the whole time-difference into the specified units. This is of no value to you.

As an example, say you set the event's date to 32 days from now - and time to 13:15.

Millisecond difference =

 2764800000 (32 days in millis) 
+  46800000 (13 hours in millis) 
+    900000 (15 minutes in millis)
= 2812500000

Using this time-difference, the following log:

int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(diff);
int minutes = (int) TimeUnit.MILLISECONDS.toMinutes(diff);
int hours = (int) TimeUnit.MILLISECONDS.toHours(diff);
int days = (int) TimeUnit.MILLISECONDS.toDays(diff);

produces:

Seconds left: 2812500
Minutes left: 46875
Hours left: 781
Days left: 32

These figures are not off. A quick check would be: time difference in millis was: 2812500000 => in seconds would be diff/1000 = 2812500 => in minutes would be => diff/1000/60 = 46875 and so on.

Relative time:

To get relative time such as 32 days, 13 hours and 15 minutes left, you will have to do the heavy-lifting yourself. As an example:

// I will use the actual values instead of defined 
// variables to make this easier to follow
long timeDiff = 2812500000L;

// Simple division // we don't care about the remainder
// Result: 32
int days = 2812500000 / DateUtils.DAY_IN_MILLIS;

// This is what's left over after we take the days out.
// We'll use this to get the number of hours.
// Result: 47700000
long remainderFromDays = 2812500000 % DateUtils.DAY_IN_MILLIS;

// Simple division // we don't care about the remainder
// Result: 13
int hours = 47700000 / DateUtils.HOUR_IN_MILLIS;

// This is what's left over after we take the hours out.
// We'll use this to get the number of minutes.
// Result: 900000
long remainderFromHours = 47700000 % DateUtils.HOUR_IN_MILLIS;

// Result: 15
int minutes = 900000 / DateUtils.MINUTE_IN_MILLIS;

// Result: 0
long remainderFromMinutes = 900000 % DateUtils.MINUTE_IN_MILLIS;

// Result: 0
int seconds = 0 / 1000; // 1000 ms = 1 sec

Log.i("Time-Difference", "Event in: " + String.format("Event in %d days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds));

Output:

Event in: 32 days, 13 hours, 15 minutes and 0 seconds

This is the very reason everyone here is suggesting Joda Time. The computation above is just off the top of my head. I cannot guarantee its correctness. If you also need relative month difference (such as 3 months, 2 days ....), a lot of work will be required. There isn't a DateUtils.MONTH_IN_MILLIS constant - varying number of days - 28, 29, 30, 31.

On the other hand, Joda Time is a tried & tested product. But, if all you need is one kind of computation, used scarcely (if ever), I'd say spend some time and come up with your own implementation rather than under-employ Joda Time.

Upvotes: 2

Keith Pinson
Keith Pinson

Reputation: 1725

Your code looks fine to me for what you are doing. Using org.joda.time as others have suggested is a best-practice, but it won't fix the problem. Instead you need to do two things:

  1. Verify the Month that the user entered is in range (they may have entered the date in MM/DD/YYYY format). Month values greater than 12 won't throw an exception and your diff will be way off.
  2. The line where you construct your date, subtract 1 from the month since months should be from 0 to 11, like:

    newCalendar.set(yearE, monthE-1, dayE,hour, min, 0);
    

Upvotes: 0

Jimmy
Jimmy

Reputation: 16428

Consider using the Joda time library instead of Calendar, it's much easier to work with.

As you're on android, I'll assume that you're using gradle, so go ahead and drop this in your dependencies

compile 'joda-time:joda-time:2.3'

I've created a small psvm to demo how you can use it

import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.PeriodFormat;

import static java.lang.String.format;

public class DateTimeDemo {

    public static void main(String[] args) {

        // Your date/time values, I'll assume you missed a digit off the time ;)
        String eventDate = "3/5/2015";
        String eventTime = "13:20";

        // convert these to a DateTime object
        DateTime targetDateTime = DateTime.parse(format("%s %s", eventDate, eventTime), DateTimeFormat.forPattern("dd/MM/yyyy HH:mm"));

        // print out the millis, or in your case, save it to DB
        System.out.println("targetDateTime in millis is " + targetDateTime.getMillis());

        // grab a timestamp
        DateTime now = DateTime.now();

        // print it out, just for demo
        System.out.println("millis for now is " + now.getMillis());

        // create a period object between the two
        Period period = new Period(now, targetDateTime);

        // print out each part
        System.out.println("seconds " + period.getSeconds());
        System.out.println("hours " + period.getHours());
        System.out.println("months " + period.getMonths());

        // convert the period to a printable String
        String prettyPeriod = PeriodFormat.getDefault().print(period);

        // write it out!
        System.out.println(prettyPeriod);


    }
}

Output is

targetDateTime in millis is 1430623200000
millis for now is 1425527593584
seconds 46
hours 22
months 1
1 month, 3 weeks, 6 days, 22 hours, 26 minutes, 46 seconds and 416 milliseconds

Upvotes: 5

kichik
kichik

Reputation: 34704

You can use Joda for that.

long dbTime = 1425525415837L;
Period period = new Period( dbTime, System.currentTimeMillis() );
String formatted = PeriodFormat.getDefault().print(period);
System.out.println( formatted );

If you want more control of the format use PeriodFormatter.

If you want to get the seconds, minutes, hours, etc. and not just print them, you can use the various available methods. For example:

period.getSeconds();
period.getHours();
period.getMonths();

More formatting options are described in this question.

Upvotes: 2

Related Questions