I came across the most weird problem with dates on a Sony Xperia LT26i. A date format: "hh:mm:ss a" will print "01:00 p.m." and on most devices it prints "01:00 pm". Any clue why is this? It is messing my joda time as I cant parse times that come from the server.
JodaTime can parse the local am/pm-strings your device produces. What to do with different strings coming from server? Since you expect fortunately only the markers "am" or "pm" from server I suggest to use two specialized formatter objects using am/pm-literals for parsing. So that is the workaround:
static final DateTimeFormatter AM_PARSER = DateTimeFormat.forPattern("hh:mm:ss 'am'");
static final DateTimeFormatter PM_PARSER = DateTimeFormat.forPattern("hh:mm:ss 'pm'");
public static LocalTime parseServer(String input) {
if (input.endsWith("am")) {
return AM_PARSER.parseLocalTime(input);
} else {
LocalTime lt = PM_PARSER.parseLocalTime(input);
return lt.plusHours(12); // necessary because we parse pm only as literal
}
}
Explanation:
If you study the JodaTime-source code then you will find that am/pm-strings finally come from DateFormatSymbols.getInstance(locale).getAmPmStrings(). So the question arises why you have "p.m." instead of "pm" on Sony Xperia device. This leads to the question what is the data source of class DateFormatSymbols. This is dependent on the JVM-provider who manages such data in any resource directory (in your case dependent on your special Android configuration, in my case in resource bundle class sun.text.resources.FormatData). It is really different for every JVM (and Android is not even an official Java-VM).
After thinking a little bit I have come up with this solution
public static LocalTime localTimeParse(String date, DateTimeFormatter dateTimeFormatter) {
if(!StringUtils.hasText(date) || dateTimeFormatter == null)
return null;
LocalTime localTime = null;
try {
localTime = LocalTime.parse(date,dateTimeFormatter);
} catch(IllegalArgumentException exception) {
//This can happen on devices that have their time in the following format "01:00 p.m." insetad of "01:00 pm"
//Sony Xperia lT26i is one of them
String newDate = date.toLowerCase(Locale.getDefault()).contains("am") ? date.toLowerCase(Locale.getDefault()).replace("am", "a.m.") : date.toLowerCase(Locale.getDefault()).replace("pm", "p.m.");
localTime = LocalTime.parse(newDate,dateTimeFormatter);
}
return localTime;
}
This can be used like this:
DateTimeFormatter formatter = DateTimeFormat.forPattern("hh:mm a");
LocalTime localTime = DateUtils.localTimeParse("09:00 PM",formatter);
It works on Xperia a non Xperia
Related
I'm using Xamarin forms in my android app I'm getting the datetime that I converted to
Egypt time but some devices read it nulls or countries I don't know the reason really but some others read it?
and that is my code
DateTime date = DateTime.Now;
var test = TimeZoneInfo.ConvertTime(date, TimeZoneInfo.FindSystemTimeZoneById("Africa/Cairo"));
It might help you
DateTime now = DateTime.UtcNow;
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Africa/Cairo Standard Time");
TimeSpan utcOffset = timeZoneInfo.GetUtcOffset(now);
DateTime cairoTime = new DateTime(now.Ticks + utcOffset.Ticks, DateTimeKind.Local);
I am implementing the function to display the change of exercise time by date as a graph.
But there was a problem.
Before I explain the problem, I will briefly explain my code.
When a date is input from the user, the date is stored in the database and output by date.
For example, when the user enters 2020/06/26, it is displayed as 06/26 on the graph.
Now I will explain the problem.
The x value of the graph is overlapping. 07/01 does not appear immediately after 06/30, but very far.
I will attach my code and execution result. enter image description here
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyMMdd");
Date date = null;
try {
date = simpleDateFormat.parse(Float.toString(value));
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat newFormat = new SimpleDateFormat("MM/dd");
String dateString = newFormat.format(date);
return dateString;
}
});
Hard to tell from the code you provide. But most probably, the problem does not lie in the ValueFormatter, but in the actual x values you use. Having x values in the format yyMMdd ist most likely not what you want, because the difference between e.g. 2020-04-01 and 2020-04-02 is not the same as between 2020-03-31 and 2020-04-01, event if it should be exactly the same (1 day). You should use another representation for the x values, e.g. "days since 1970".
This still does not explain why 06-30 is displayed after 07-01 and even after 08-19, however. My guess is that your Entry list is not sorted properly in ascending order.
I'm implementing count down timer for the android app using JodaTime.
Depending of devices the output is different.
DateTime openingDateTime = new DateTime(2018, DateTimeConstants.JUNE, 14, 21, 0, 0, DateTimeZone.forID("Europe/Moscow"));
DateTime nowDateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"));
long difference = openingDateTime.getMillis() - nowDateTime.getMillis();
(...)
onTick(difference);
(...)
PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
.printZeroAlways()
.appendDays().appendSuffix(" day", " days")
.appendSeparator(" ")
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
(...)
#Override
public void onTick(long millisUntilFinished) {
Duration duration = new Duration(millisUntilFinished);
Period period = duration.toPeriod(PeriodType.dayTime());
tvCounter.setText(periodFormatter.print(period));
}
On the one device output is correct: 491 days 4:39:18
on the other is wrong: 0 days 11788:49:11.
What am I doing wrong?
Thanks to your comments, I can now reproduce your problem. Just add following static initializer to your test class (at first place) to simulate the device where you observe your expected output:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
According to the spec (see also the accepted answer on this SO-post), the conversion duration.toPeriod(periodType) should only use so-called precise duration fields, that is hours, minutes, seconds and milliseconds but not days.
My analysis of source code of Joda-Time (v2.9.6):
The internal class org.joda.time.chrono.BasicChronology contains following constant:
private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);
So we see that here this duration field is marked as "precise", but: The subclass ZonedChronology wraps it and override the behaviour of method isPrecise():
public boolean isPrecise() {
return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
}
This shows an extra zone dependency of the precision property of the days()-duration-field, namely precise for fixed zones like UTC and else imprecise.
I don't know if the analyzed and observed behaviour is a feature or a bug. Let's say, it is dangerous to expect the creation of Period-objects by duration.toPeriod(...) to be zone-independent. And it is not documented there to have a precise days-component if the system zone is fixed.
Unfortunately, the implicit dependency on the default time zone is deeply coded into Joda-Time via its chronology-design. As workaround, you can use:
Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());
I have a problem, I need the universal functions for showing date and time in any locale separately. But I can't find the way to do it without the checking the calendar.getLocale()
this function will give the date in US locale
static public String getDateFromCalendar(Calendar cal) {
return String.format("%tD", cal);
}
But if the Locale is russian I have to use istead this: String.format("%td.%tm.%tY", cal);
I don't want to use the conditional operations for every possible locale.
Please help to find the way to do is simplier.
Assuming you mean Java, I suggest you to consider the class java.text.DateFormat. Background is that every country/locale has its own typical date-time-format. For example:
public static String getDateFromCalendar(Calendar cal) {
// maybe get user-locale via ThreadLocal or via second method parameter
Locale locale = new Locale("ru", "Ru");
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
return dateFormat.format(cal.getTime());
}
You can adjust the format style by choosing between SHORT, MEDIUM, LONG or FULL. For MEDIUM the output is: 05.04.2014 Compare that with the output for Locale.US yielding: Apr 5, 2014.
I have a datepicker, and I pick up two dates, like 2012-04-08 and 2012-05-11. Because I have a database and need store dates as strings I convert them to 20120408 and 20120511 (strings so far). My code contains the next steps. I call my function with these strings:
public void durva(String datefrom, String dateto) throws ParseException {
datefrom = GlobalVars.getDateStringFrom();
dateto = GlobalVars.getDateStringTo();
Log.i("DateFrom", datefrom);
Log.i("Dateto", dateto);
SimpleDateFormat formatter2 = new SimpleDateFormat("yyyyMMdd"); //-de most yyMMdd
formatter2.setLenient(false);
Date dates1;
Date dates2;
long mili1;
long mili2;
long diff;
String dates="";
String convertedDates = "";
dates1 = formatter2.parse(datefrom);
mili1 = dates1.getTime();
Log.i("date1", String.valueOf(mili1));
dates2 = formatter2.parse(dateto);
mili2 = dates2.getTime();
Log.i("date2", String.valueOf(mili2));
diff = 1+((mili2-mili1)/86400000);
Log.i("diff", String.valueOf(diff));
long [] millis = new long[(int) diff];
for (int i=0;i<diff;i++)
{
millis[i] = mili1+(86400000*i);
Log.i("millii", String.valueOf(millis[i]));
dates = dates + String.valueOf(millis[i]) + " ";
SimpleDateFormat formatterX = new SimpleDateFormat("yyyyMMdd");
String dateString = formatterX.format(new Date(millis[i]));
convertedDates = convertedDates + dateString + " ";
}
Log.i("DATES", convertedDates);
}
I use the a created GlobalVars when I pick a date and covert them to this string format I mentioned above. Then I convert them to millisecs. Then I convert them back to my format but it is not important, since the millisecs are already messed up. With
for (int i=0;i<diff;i++)
{
millis[i] = mili1+(86400000*i);
Log.i("millii", String.valueOf(millis[i]));
I always increase the millisecs, but what happens after the 25th value? It travels back in time and continues from another value! In this example I get: 20120408 20120409 .. 20120502 20120314..20120322 . I add 86400000 (millisecs per day) for jumping a whole day.
What's happening here?
Thank you in advance!
You should use 86400000L, or declare i as long:
millis[i] = mili1+(86400000L*i);
Otherwise both i and 86400000 are 32 bit integers, so the result is calculated as a 32-bit integer. Unfortunately 86400000*25 is too big to fit in 32 bits, so it wraps around.
86400000*25 is -2134967296.
Another thing you should be careful about is that not all days have 24 hours thanks to DST.