I'm using Joda library in my android application to calculate the duration between two given dates. Using this application to calculate a person's age.
The following code gives this output : 23 years 11 months and 6 days
DateTime from_readable_dateTime = new DateTime(from_date_dt);
DateTime to_readable_dateTime = new DateTime(to_date_dt);
Period period = new Period(from_readable_dateTime, to_readable_dateTime);
from_date_dt in this case is 1990/01/06 and to_date_dt is 2014/09/15. (date format is yyyy/mm/dd). As I mentioned earlier the output of this piece of code with the given inputs must be 24 years, 7 months and 20 days while I get 23 years 11 months and 6 days. What's the problem? Am I doing something wrong or Joda is faulty?
UPDATE-1
I get 3 numbers as year, month and day from 3 number pickers I make a single string as date(start date variable is named from_date_string and end date is named to_date_string), I convert these two strings to date variables (from_date_dt and to_date_dt):
from_date_dt = null;
to_date_dt = null;
diff_dt = null;
dateFormat = new SimpleDateFormat("yyyy/mm/dd");
try {
from_date_dt = dateFormat.parse(from_date_string);
to_date_dt = dateFormat.parse(to_date_string);
} catch (ParseException e) {
e.printStackTrace();
}
BTW, I'm working with Persian calendar. Since I can't use the default date picker, I'm using number pickers as date pickers.
The standard period type which you implicitly use contains weeks. The class Period has another constructor with 3 arguments where you can specify PeriodType.yearMonthDay() as third argument.
LocalDate d1 = new LocalDate(1990, 1, 6);
LocalDate d2 = new LocalDate(2014, 9, 15);
Period p = new Period(d1, d2, PeriodType.yearMonthDay());
System.out.println(p); // output: P24Y8M9D
To explain the result in fine-granular steps:
[1990-01-06] + 24 years = [2014-01-06]
[2014-01-06] + 8 months = [2014-09-06]
[2014-09-06] + 9 days = [2014-09-15]
Another thing to consider, don't use DateTime if your input is just a plain date format. Do you really want to take into account timezone effects? And why do you use SimpleDateFormat although JodaTime has its own formatters?
UPDATE after updated question of OP:
Now the question has become a lot clearer.
First to note generally, if you use number pickers then your original input for year, month and day-of-month are just integers. In that case I would normally not use a formatter at all, but just pass the numbers to the constructor of LocalDate. This constructor will also verify the input automatically. The complex conversion you try is very error-prone (number to string, then string concatenation to a date string, then parsing it with your default timezone and then passing java.util.Date to DateTime-ctor). It can be completely avoided.
Second to note and most important: You write that you use a persian calendar. Then the reason why you cannot use the default date picker is simply that this default date picker does not support the calendrical rules of persian calendar. Is my speculation right? And here the very bad news for you: Jodas classes do NOT support the persian calendar, too, especially LocalDate or DateTime are only designed for the ISO-8601-standard which is based on the modern proleptic gregorian calendar. Month lengths for example are different in gregorian calendar and in persian calendar, hence a completely different calendar arithmetic is required which is not supported by Joda-Time.
So your strange period results are probably explainable by the fact that you tried to let the user define persian year, month and day. And then you try to parse this input leniently (otherwise Joda-Time would immediately complain about odd day-values for example). But the last step - period calculation - must fail because it is based on ISO-8601 in Joda-Time and not persian calendar rules.
Am I right? Or have I misinterpreted your updated question?
A work-around is difficult. If you really want to have period calculation for a persian calendar then you have to build it from the scratch. As far as I know there is actually no library which supports this feature. A guideline for writing a persian solution can be the algorithm discussed in this SO-post however.
UPDATE indicating a solution:
Meanwhile I have implemented the Persian calendar in Time4A, see also this SO-post. So if you are able to use Time4A and combine the PersianCalendar with the algorithm for a multi-unit-period mentioned above then this will solve your problem. Time4A-v3.15-2016a explicitly supports special Persian calendar units which use different rules than gregorian calendar units.
Related
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;
}
I am trying to parse date string with timezone using this code for tests:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZZZZZ", Locale.US);
Calendar calendar = Calendar.getInstance();
calendar.setTime(sdf.parse("2017-07-26T06:00-06:00"));
int offset = calendar.getTimeZone().getRawOffset();
I am trying to change timezone from -06 to +09, but offset always contains 10800000.
How to parse date with timezone correctly (I need time and timezone both)?
Note: -06:00 is an offset, not a timezone - those 2 concepts are related, but they are different things (more on that below).
The problem with SimpleDateFormat and Calendar is that they use the system's default timezone, so even though you parse a date with a different offset (like -06:00), the resulting Calendar will have the default timezone (you can check what zone is by calling TimeZone.getDefault()).
That's just one of the many problems and design issues of this old API.
Fortunately, there's a better alternative, if you don't mind adding a dependency to your project (in this case, I think it's totally worth it). In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP to make it work (more on how to use it here).
To work with offsets, you can use the org.threeten.bp.OffsetDateTime class:
// parse the String
OffsetDateTime odt = OffsetDateTime.parse("2017-07-26T06:00-06:00");
This will parse all the fields correctly (date/time and offset). To get the offset value, similar to calendar.getTimeZone().getRawOffset(), you can do:
// get offset in milliseconds
int totalSeconds = odt.getOffset().getTotalSeconds() * 1000;
I had to multiply by 1000 because calendar returns the value in milliseconds, but ZoneOffset returns in seconds.
To convert this to another offset (+09:00), it's straightforward:
// convert to +09:00 offset
OffsetDateTime other = odt.withOffsetSameInstant(ZoneOffset.ofHours(9));
As I said, timezone and offset are different things:
offset is the difference from UTC: -06:00 means "6 hours behind UTC" and +09:00 means "9 hours ahead UTC"
timezone is a set of all the different offsets that a region had, has and will have during its history (and also when those changes occur). The most common cases are Daylight Saving Time shifts, when clocks change 1 hour back or forward in a certain region. All these rules about when to change (and what's the offset before and after the change) are encapsulated by the timezone concept.
So, the code above works fine if you're working with offsets and wants to convert to a different one. But if you want to work with a timezone, you must convert the OffsetDateTime to a ZonedDateTime:
// convert to a timezone
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// get the offset
totalSeconds = zdt.getOffset().getTotalSeconds() * 1000;
The getOffset() method above will check the history of the specified timezone and get the offset that was active in that corresponding instant (so, if you take a date during DST, for example, the offset (and also date and time) will be adjusted accordingly).
The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
Example, from 1 min ago
i use api and time response result from Api service as:
{
"date_time":"2016-03-10 03:20:30"
}
Please discuss step by step, And if available how can programticly display it in Arabic format
منذ 15 دقيقة
My Code for date in list view adapter
TextView date_time = (TextView) convertView.findViewById(R.id.date_time_list_home);
date_time.setText(m.dateTime());
First, for any date/time manipulation on Android, I highly recommend using the ThreeTenABP library. This is a back port of the Java 8 java.time.* package, circumventing the notoriously disappointing java.util.Date and java.util.Calendar classes.
To parse your "date_time" using this library, you can use the following code:
// I set the ZoneId to systemDefault, but you should really use the ZoneId of the server
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
.withZone(ZoneId.systemDefault());
Instant instant = formatter.parse(m.dateTime(), Instant.FROM);
Android provides the DateUtils class to display date and time information. This class takes into account system settings such as Locale and 12/24-hour format. Therefore, if the Locale of the device is set to any of the Arabic locales (ar_), the date/time will be displayed in a format suited for it.
String display = DateUtils.getRelativeTimeSpanString(
instant.toEpochMilli(),
Instant.now().toEpochMilli(),
DateUtils.MINUTE_IN_MILLIS);
date_time.setText(display);
The last parameter in getRelativeTimeSpanString is the minimum resolution, so setting DateUtils.MINUTE_IN_MILLIS will not display the difference in seconds.
If you insist on using the Java 7 classes, here is the same code using them:
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = format.parse(m.dateTime());
String display = DateUtils.getRelativeTimeSpanString(
date.getTime(),
new Date(),
DateUtils.MINUTE_IN_MILLIS);
date_time.setText(display);
If you need a "transition resolution" greater than a single day (i.e. you want to display a date/time that is further than one day in the past as "… days ago") you can use the DateUtils.getRelativeDateTimeString() method instead:
String display = DateUtils.getRelativeDateTimeString(
mContext,
instant.toEpochMilli(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,
DateUtils.FORMAT_ABBREV_ALL);
Any date that is further back than the transitionResolution (in this case, one week) will be displayed in an appropriate date format, instead of the relative format. The minResolution and transitionResolution can be any long value, DateUtils contains other convenient constants such as MONTH_IN_MILLIS and YEAR_IN_MILLIS.
The last parameter takes an integer for formatting flags. These flags override the default formatting that DateUtils uses for each Locale; view the documentation for more information.
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.
I have a database in my Android app that has dates formatted like this: 201274 for July 4, 2012 and 20121016 for October 16, 2012. I display the date of the DB row using SimpleDateFormat so that for today, it grabs the date 20121016 and displays 2012-10-16. The code is like so:
private void convertDBdate() {
convertDateTextView.setText(gotDt);
String dateStr = convertDateTextView.getText().toString();
SimpleDateFormat inputFormatter = new SimpleDateFormat("yyyyMd");
SimpleDateFormat outputFormatter = new SimpleDateFormat("yyyy-MM-dd");
try {
DBdate = inputFormatter.parse(dateStr);
dashDateStr = outputFormatter.format(DBdate);
convertFinalDateTV.setText(dashDateStr);
} catch (ParseException e ) {
e.printStackTrace();
}
With this code, 201274 displays fine as 2012-07-04, but two digit months display incorrectly, 20121016 shows as 2012-01-16 (January 16). The problem is in the MM and dd. I've tried yyyy-M-dd as the output date format, but that shows 2012-1-16 (again, January 16).
Do I have to somehow isolate the M value and do that month + 1 thing? If so how would that be written, and where would it go?
I don't want to have to re-write the dates in the database to 20120704 for July 4, 2012, I'd like to be able to fix it in code.
Your problem isn't that the input formatter should always be yyyyMMdd but that sometimes it should be yyyyMMdd
in the case of 201274 then an input format of MM won't apply but in the case of 20121016 then it will.
You'll need to add some logic parsing the length of the input and choosing the appropriate formatter.
Before I could suggest some logic I'd need to ask two questions
How do you represent 2012-10-01 in the database? I guess that it's 2012101 in which case...
How do you distinguish between 2012111 and 2012111? i.e. 2012-11-01 and 2012-01-11
maybe to late for this project but for future projects I would always recommend to use
Date.getTime()
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Date.html#getTime()
for storing even dates as it will take less space in the database, will make sorting faster and you just avoid problems you have right now.