Calculate Remaning Date Without Joda Time - android

I've spent an hour going through stackoverflow trying to find a proper way of calculating the remaining Days, hours, minutes and seconds remaining without using JODA-Time. I wish to keep things simple as possible.
I am seeking to do the following (Please provide declarations of instances, I am not sure as to whether to us Longs, Time or Calendar objects etc):
difference = endingDate-currentTime
Then set a textView to the time remaining with DD:HH:MM:SS format
In other words, what is the best method to use? (Timezone is not important) How can I set the ending date to for example December 31, 2013 and what type is my ending date? Is it a time, date or calendar object? I want to then subtract my current date from my ending date to display the remaining days left until December 31, 2013. In the format of DD:HH:MM:SS
Thank you! The help is much appreciated.

I'll leave you to work out how to divide the difference variable to get days, hours, etc. But this is how I'd do the rest of it.
Calendar endCalendar = new Calendar();
// Set end to 31th Dec 2013 10:15:30 am local time
endCalendar.set(2013, 11, 31, 10, 15, 30);
long localEndTimeInMillis = endCalendar.getTimeInMillis();
long localCurrentTimeInMillis = Calendar.getInstance().getTimeInMillis();
// Convert to UTC.
// Easy way to compensate if the current and end times are in different DST times
utcEndTimeInMillis = getUTCTimeInMillis(localEndTimeInMillis);
utcCurrentTimeInMillis = getUTCTimeInMillis(localCurrentTimeInMillis);
long difference = utcEndTimeInMillis - utcCurrentTimeInMillis;
The method to convert to UTC...
public long getUTCTimeInMillis(long localTimeInMillis) {
return localTimeInMillis - TimeZone.getDefault().getRawOffset() - (TimeZone.getDefault().inDaylightTime(new Date(localTimeInMillis)) ? TimeZone.getDefault().getDSTSavings() : 0);
}

Related

getRelativeTimeSpanString() is returning the start date, instead of time ago

I am trying to calculate the time between two dates and show the difference in time in the UI as x mins ago, x days ago, x weeks ago etc (depending on the length of time).
The first date is a published date which is given to me by the api. I then format this date to yyyy-MM-dd HH:mm:ss. Second date is the current time.
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.setTimeZone(TimeZone.getTimeZone("GMT"))
val time = sdf.parse("$year-$month-$day $hours").time // 2019-03-14 13:00:55
val now = System.currentTimeMillis()
val relative = DateUtils.getRelativeTimeSpanString(time, now, 0L, DateUtils.FORMAT_ABBREV_ALL)
However, when I print relative, I get Mar 14 when I would expect x months ago.
Can't tell what I'm missing apart from I have read somewhere that getRelativeTimeSpanString() only works for 7 days, but the documentation doesn't state this so not sure if that's the case?
Any help to solve this would be appreciated!
It's a feature.
You have minimum resolution of 0 milliseconds and hence the time is printed exactly. It doesn't make sense to have output like "16 weeks ago 13:00:55".
You could change the minimum resolution to DateUtils.WEEK_IN_MILLIS to get output like "16 weeks ago".
There's also another DateUtils method that allows you to specify the transition resolution:
the elapsed time (in milliseconds) at which to stop reporting relative measurements. Elapsed times greater than this resolution will default to normal date formatting. For example, will transition from "7 days ago" to "Dec 12" when using WEEK_IN_MILLIS
In theory that would work, but in practice it is capped at WEEK_IN_MILLIS.
Under the hood, it's RelativeDateTimeFormatter doing the work and you can learn more by inspecting its source.
If you really need "months ago" resolution, forget about DateUtils and roll your own implementation. For example with java.time / ThreeTenBP APIs: https://stackoverflow.com/a/56450659/101361

Difference between 2 dates in days and in double

I am working on an app and i need to get the difference between the actual date and a date inserted by the user, in days and in double.
Any idea on how to make this? I've tried some things but without success.
First you must decide if you want to consider the time of the day and the timezone to calculate the difference, because this can lead to different results.
Example: current date (AKA "today") is April 17th or 18th, depending on where in the world you are. Actually, depending on the time of the day, there might be 3 different "todays" in the world, at the same time. What timezone are you using to calculate the difference?
the user will enter a date: only day, month and year? Will it enter the hours? Are you using the user's device's timezone or some specific zone?
the same questions apply to the current date
Depending on the choices you make, you might get a different result.
Anyway, I'd use this lib: http://www.threeten.org/threetenbp/
or java.time classes, if available in your API level. In both API's you can use the following.
To use a date (day-month-year only) and the device's default timezone, I'd choose the LocalDate class:
// current date in device's default timezone
LocalDate now = LocalDate.now();
// some date from input values (May 10th 2018)
LocalDate dt = LocalDate.of(2018, 5, 10);
// difference in days
long diff = ChronoUnit.DAYS.between(now, dt); // 23
If you want to consider the time of the day (hours, minutes, etc), use a LocalDateTime. But in this case, ChronoUnit.DAYS considers a day has passed when the time is >= the other (ex: the difference between April 17th at 10 AM and April 18th 9:59 AM is zero days, because the time of the day didn't reach 10 AM, so it didn't complete 1 day - with LocalDate this doesn't happen because this class doesn't have time-of-the-day fields and considers only the day, month and year).
If you want to consider everything (date, time, and timezone), including Daylight Saving Time transitions, use a ZonedDateTime instead (the code is very similar, the only difference is that you can choose a timezone to work with):
// current date/time in device's default timezone
ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault());
// some date from input values (May 10th 2018, 10 AM in New York timezone)
ZonedDateTime dt = ZonedDateTime.of(2018, 5, 10, 10, 0, 0, 0, ZoneId.of("America/New_York"));
// difference in days
long diff = ChronoUnit.DAYS.between(now, dt); // 23
You can choose between the device's default timezone (ZoneId.systemDefault()) or a specific one (ZoneId.of("America/New_York")). You can check all the available timezones with ZoneId.getAvailableZoneIds().
Maybe it doesn't make sense to use current date in one timezone and user's date in another (I'd use the same for both), but that's up to you to decide.
Calendar c = Calendar.getInstance();
Calendar c2 // = what you will get from the user
long diff = c.getTimeInMillis()-c2.
double days = (double) diff/(1000*60*60*24);
that is what i have in mind.
I hope this helps
use this way
public static double getTimeDiffBetweenDate(Date startDateTime, Date finishDateTime) {
long diffInMilliseconds = finishDateTime.getTime() - startDateTime.getTime();
return TimeUnit.MILLISECONDS.toMinutes(diffInMilliseconds) / 60.0;
}

Custom the display value of SimpleDateFormat in android

I currently work on a double value that represent the total consumed time
for example, I have a 260 that means 260 second is consumed
To display to user, I would like to format it
for example , it should be something like 0year,0month,0day,1hr,2min,30sec
But I found the SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); is not quite suit my case (seems the "h" in "hr" is conflicted with the hour symbol?)
So , how to change the HH:mm:ss to the case mentioned above?
Thanks for helping
DateFormat is useful to format dates, not an absolute value of time.
To achieve your goal, you can take a look to Formatter
Hope this sample helps you:
String total_consumed_time = String.format("%01d year, %01d month, %01d day, %01d hr, %01d min, %01d sec", time_year, time_month, time_day, time_hour, time_min, time_seg);
I didn't try that code, but I use similar workaround with an absolute time in milliseconds:
long time = 260000; // time in mseg
long time_hour = TimeUnit.MILLISECONDS.toHours(time);
time -= TimeUnit.HOURS.toMillis(time_hour);
long time_min = TimeUnit.MILLISECONDS.toMinutes(time);
time -= TimeUnit.MINUTES.toMillis(time_min);
long time_seg = TimeUnit.MILLISECONDS.toSeconds(time);
String total_time = String.format("%02d:%02d:%02d", time_hour, time_min, time_seg);
With a result of "00:04:20" (4 minutes and 20 seconds).
Accepted answer is in most cases okay for solving your problem, but gives wrong reason why not to use the class SimpleDateFormat. This format class is well suited for objects of type java.util.Date (which are kind of unix timestamps in milliseconds hence absolute value of time, NOT dates). In order to treat letters like "hr" as literals you need to escape them. Example code:
// create timestamp
java.util.Date jud = new java.util.Date(260 * 1000); // milliseconds
// create format for timestamp
SimpleDateFormat sdf =
new SimpleDateFormat("yyyy'year',M'month',d'day',H'hr',m'min',s'sec'");
// otherwise you will get extra offset time (example: in England +1 hour DST)
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
// output: 1970year,1month,1day,0hr,4min,20sec
String formatted = sdf.format(jud);
System.out.println(formatted);
Even with the applied and tricky time zone correction in code you face the problem that you have an output for the year 1970, a point in time. Hereby you can see that SimpleDateFormat does format timestamps well (absolute values in time) but NOT durations (amount resp. length of time). This semantic problem can also not be solved by the approach to use java.util.Formatter as soon as the input increases the day limit of 86400 seconds.
Old JDK and Android don't offer a built-in solution for evaluating time differences expressed in years, months and days. Java 8 does offer (limited) support with new API (class 'Period' only for date part, not time part). External libraries like JodaTime or my own one (actually only as alpha-version) give more support. JodaTime even offers a special PeriodFormatter which is ideal for solving your problem.

Time plus minutes

I have a bunch of data containing a time value, e.g. 09:30am, and a duration, either 15,30,45 or 60. What would be the best way of getting the end time from these values? Will it be some use of a Date / Calendar or would just a custom function be quicker to do?
The Joda Time library (http://www.joda.org/joda-time/) is popular, easy to work with, and handles these kinds of calculations in a straight-forward manner.
DateTime start = new DateTime(2013, 10, 15, 9, 30); // create a DateTime representing Oct 15, 2013 at 9:30
Duration dur = Duration.standardMinutes(15); // create a duration of 15 minutes
DateTime calc = start.plus(dur); // add; result is 9:45 on 10/15/2013
The standard java/android way for this is to use a calendar:
final Calendar c = Calendar.getInstance(); // now
c.add(Calendar.DAY_OF_MONTH, 10); // add 10 days
There are other constants if you want to add minutues, years, seconds, ....
Check out this solution: Java string to date conversion
You may need to change the "MMMM d, yyyy" portion to fit your data set.
Here is the Javadoc: http://developer.android.com/reference/java/text/SimpleDateFormat.html
edit: For the second part of your question, there are several questions on here about how to add to a Date/Calendar object.

-1469913 days left when calculating reoccurring events in Android

For reoccurring events, I want to show the number of days left until the next occurrence in my Android calendar application.
Example:
Today: 2012-06-12
Reoccurring event: 19th June
=> 13 days left
In order to achieve this, I save the first occurrence in an object of data type Calendar:
private Calendar cal;
...
cal = new GregorianCalendar();
cal.set(Calendar.YEAR, USER_INPUT_YEAR);
cal.set(Calendar.MONTH, USER_INPUT_MONTH);
...
To calculate the days left I use this function:
public int getDaysLeft() {
Date next = this.getNextOccurrence();
if (next == null) {
return -1;
}
else {
long differenceInMilliseconds = next.getTime()-System.currentTimeMillis();
double differenceInDays = (double) differenceInMilliseconds/DateUtils.DAY_IN_MILLIS;
return (int) Math.ceil(differenceInDays);
}
}
Which uses this function:
public Date getNextOccurrence() {
if (this.cal == null) {
return null;
}
else {
Calendar today = new GregorianCalendar();
Calendar next = new GregorianCalendar();
next.setTime(this.cal.getTime());
next.set(Calendar.YEAR, today.get(Calendar.YEAR));
if ((today.get(Calendar.MONTH) > this.cal.get(Calendar.MONTH)) || ((today.get(Calendar.MONTH) == this.cal.get(Calendar.MONTH)) && (today.get(Calendar.DAY_OF_MONTH) > this.cal.get(Calendar.DAY_OF_MONTH)))) {
next.add(Calendar.YEAR, 1);
}
return next.getTime();
}
}
By the way, to get the initial date, I expect to find a YYYY-MM-DD value and parse it like this:
(new SimpleDateFormat("yyyy-MM-dd")).parse(INPUT_DATE_STRING)
This works fine in most cases, but some users report that they see numbers such as -1469913 as "days left". How can this happen?
I thought the date (cal) might be not set or invalid, but then it would show -1 or something like this, as there are null checks in all parts, right?
-1469913 means something like -4027 years ago! As it is a reoccurring event, I thought the "days left" information should always be between 0 and 366. What could cause this code to produce such a number? Does this mean that getNextOccurrence() returns a data that is 4027 years in the past? I can't explain this behaviour.
I hope you can help me. Thank you so much in advance!
Edit: As it may be helpful: The wrong dates' year is always output as 1 when using DateFormat.getDateInstance().format(), e.g. Jan 3, 1. Nevertheless, the result of getDaysLeft() is something like 4k years.
Edit #2: I found out a date like 1--22199-1 is one that produces the output of "4k years left". Nevertheless, it is successfully parsed by (new SimpleDateFormat("yyyy-MM-dd")).parse(). Similarly, -1-1-1-91- is correctly parsed as Jan 1, 2.
Edit #3: It turned out that a date as simple as "0000-01-03" was causing all the trouble. When I output the time in milliseconds it says -62167222800000. When I then output it to a GMT string it says 0001-01-03 - strange, isn't it? And when I set the year to 1900 the time in millis is suddenly -122095040400000. Why?
Working with dates it can be really difficult to figure out those obscure errors before they happen to a user in the wild. In many cases, it can be worth your time to make a little unit test that throws a few tens of million dates in the machinery and see if any extreme answers pop up.
Also this might be worth reading. You wont realize how bad the java date-class are before you have tried something that is way better. :)
EDIT: If the users give a very high input value, then there can be a number overflow when you throw your result to an integer in getDaysLeft(). Just keep it as a long. Or even better: Only accept sensible input value, warn the user if they input the year 20120 or something like that :)
EDIT2: I was wrong in my last edit, .ceil() protects against number overflows. To be honest I have no longer any idea how this bug can happen.
EDIT3: Responding to your third edit: Remember, Date and Calendar uses Unix time. That means that the time represented by a zero is 1970. Everything before 1970 will be represented by a negative value.
EDIT4: Remember that javas calendar-classes sucks. This code snippet demonstrates that the error is in fact in the Calendar-class:
Calendar next = new GregorianCalendar();
long date1 = -62167222800000L;
long date2 = -62135600400000L;
next.setTimeInMillis(date1);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());
next.setTimeInMillis(date2);
next.set(Calendar.YEAR, 2012);
System.out.println(next.getTimeInMillis());
Output:
-125629491600000
1325545200000
It will however be very hard to track down the exact bug that causes this. The reason all those bugs remains are because fixing them might break legacy systems all over the world. My guess is that the bug originates from the inability to give negative years. This, for example, will give the output "2013":
Calendar next = new GregorianCalendar();
next.set(Calendar.YEAR, -2012);
System.out.println(next.get(Calendar.YEAR));
I would simply recommend you to not allow such extreme values in your input. Decide on an acceptable span and give an error message if the value is outside of those boundaries. If you would like to handle all possible dates in some futher application, just use joda time. You wont regret it :)
You got negative values in days that might be because user have entered the date of next occurance which is any previous date.
I think you should calculate your daysLeft like this way,
String inputDateString = "19/06/2012";
Calendar calCurr = Calendar.getInstance();//current date
Calendar calNext = Calendar.getInstance();// for next date
calNext.setTime(new Date(inputDateString)); // or do set Day,Month,Year like in your Question
if(calNext.after(calCurr)) // if the next date is after current
{
long timeDiff = calNext.getTimeInMillis() - calCurr.getTimeInMillis(); // time of next year if today is 15 june and some one enter 16 june for next occurance
int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
}
else
{
long timeDiff = calCurr.getTimeInMillis() - calNext.getTimeInMillis();
timeDiff = DateUtils.YEAR_IN_MILLIS - timeDiff; // time of next year if today is 15 june and some one enter 14 june for next occurance
int daysLeft = (int) (timeDiff/DateUtils.DAY_IN_MILLIS); // Days Left
}

Categories

Resources