Is Android Wear 2.0's version of SimpleDateFormat broken? - java

I updated to Android Wear 2.0 today. My watch face, developed by myself, now shows ", 005 14". Before the update, it had said "Sun, May 14".
The code which gets the date format:
String pattern = android.text.format.DateFormat.getBestDateTimePattern(locale, "EEEddMMM");
delegate = new SimpleDateFormat(pattern);
delegate.setCalendar(calendar);
From debugging, the value it fetched was "EEE, MMM dd".
The code which does the actual formatting:
calendar.set(Calendar.DAY_OF_WEEK, time.getDayOfWeek() + 1);
calendar.set(Calendar.DAY_OF_MONTH, time.getDayOfMonth());
// Android hard-codes in a + 1 when formatting as a number.
calendar.set(Calendar.MONTH, time.getMonthOfYear() - 1);
return delegate.format(dummyDate);
I checked the current docs for SimpleDateFormat and both E and M are still supposedly supported. So is Wear 2.0 just broken?
I figure for later API versions it might be possible to work around this by using the android.icu alternative. But this means supporting both the old and new code indefinitely, so if it turns out that it's something they're planning to fix in the platform, I'd rather not waste any more time.

Related

Date.plus not working in 2.5.4 Groovy Runtime, what is the alternative?

We want to add days to the current date and format it in a specific way. This was solved in Groovy 2.4.13 and the following date manipulation works fine:
​today = new Date()+90;today.format('yyyy-MM-dd HH:mm:ss.S');
Result: 2019-12-02 08:07:15.294
In Groovy 2.5.4 the same expression throws this exception:
groovy.lang.MissingMethodException: No signature of method:
java.util.Date.plus() is applicable for argument types: (Integer)
values: [90] Possible solutions: parse(java.lang.String),
split(groovy.lang.Closure), use([Ljava.lang.Object;),
is(java.lang.Object), wait(), clone() at
Script1.run(Script1.groovy:3)
I was able to reproduce this behaviour in "Groovy sandboxes" online:
Working fine here: groovy-playground (Version 2.4.1.5)
Failing here: groovyconsole (Version 2.5.7)
What is the working alternative in this case? I have read about a new Date API, but couldn't find the details about how to use it, with date manipulation (+ 90 days for example).
Take a look at TimeCategory
import groovy.time.TimeCategory
def theDate = use(TimeCategory){new Date() + 90.days}.format('yyyy-MM-dd HH:mm:ss.S')
I agree with Ole V.V.'s recommendations to use the new Date/Time API. Here is how you would write his Java sample in a more Groovy style.
// you can assemble aggregate types by left shifting the aggregates
// I'm not endorsing this approach, necessarily, just pointing it out as an alternative
ZonedDateTime now = LocalDate.now() << LocalTime.now() << ZoneId.of('Africa/Bamako')
// the plus operator is overloaded
ZonedDateTime in90Days = now + 90
// you can pass a String to format without needed a full DateTimeFormatter instance
println in90Days.format('uuuu-MM-dd HH:mm:ss.S')
While Groovy adds some further support for the old Java Date class, I still believe that you should not use it. It was always poorly designed and is now long outdated. Instead use java.time, the modern Java date and time API. I am sorry that I will have to trust you to translate from Java code.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.S");
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Africa/Bamako"));
ZonedDateTime in90Days = now.plusDays(90);
System.out.println(in90Days.format(formatter));
Output when running just now was:
2020-01-01 08:37:13.3
Please substitute your desired time zone if it didn’t happen to be Africa/Bamako.
Link: Oracle tutorial: Date Time explaining how to use java.time.
You can use Calendar to achieve that
Calendar cal = new GregorianCalendar();
cal.add(Calendar.DATE, 90);
Date date = cal.getTime();
All steps must be separate and not in a single line.

Java DateFormat.SHORT in Android not working as expected

I want the "correct" localized time for users with US or German system locale. Meaning "1:00 PM" and "13:00"
Java API says: https://docs.oracle.com/javase/tutorial/i18n/format/dateFormat.html
Formats U.S. Locale German Locale
DEFAULT 7:03:47 AM 7:03:47
SHORT 7:03 AM 07:03
Correct so far.
Android API: https://developer.android.com/reference/java/text/DateFormat.html
SHORT is completely numeric, such as 12.13.52 or 3:30pm
Correct for US, if you set a German Locale, Android will "translate" AM/PM and not remove it, how the correct way is and how Java did it.
My question, why does Google do that? Am I stupid and lacking sleep, not to understand "the Google logic"? This is a trivial request, yet I tried for 2 hours to get a correct German short time presentation, that would also work for US localization. "13:57 nachm." is NOT a German time representation. No one uses that, that's why we have a 24 hour formating system. It's so awkward that it breaks every reading attempt.
Test code:
private void testGoogleLocaleLogic() {
TimeZone tz_de = TimeZone.getTimeZone("Europe/Berlin");
Calendar c_us = Calendar.getInstance(tz_de,Locale.US);
Calendar c_de = Calendar.getInstance(tz_de,Locale.GERMAN);
java.text.DateFormat df_date_us_short_ = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT,Locale.US);
java.text.DateFormat df_date_de_short = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT,Locale.GERMAN);
c_us.set(Calendar.YEAR,2018);
c_us.set(Calendar.MONTH,2);
c_us.set(Calendar.DAY_OF_YEAR,6);
c_us.set(Calendar.HOUR_OF_DAY,13);
c_de.set(Calendar.YEAR,2018);
c_de.set(Calendar.MONTH,2);
c_de.set(Calendar.DAY_OF_YEAR,6);
c_de.set(Calendar.HOUR_OF_DAY,13);
Log.d("localeTest","Android Dateformat getTimeInstance SHORT US: " + df_date_us_short_.format(c_us.getTime()));
Log.d("localeTest","Android Dateformat getTimeInstance SHORT DE: " + df_date_de_short.format(c_de.getTime()));
Log.d("localeTest","df_date_de_short is type of: " + df_date_de_short.getClass().getName());
}
Results in
Android Dateformat SHORT US: 1:57 PM
Android Dateformat SHORT DE: 1:57 nachm.
Why it's not 13:57 for German locale, although I set it two times in Calendar and DateFormat is also beyond my knowledge.
A solution to print out minutes and hours manually and then switch case between system locales to add or hide "PM/AM" is exactly why people invented Locales in the first place. To avoid that. Please tell me this is not the case.
UPDATES/MORE TESTING/MORE RESEARCH (forced use of java.text....):
My Moto X Style, Android 7, German locale prints:
Android Dateformat getTimeInstance SHORT US: 1:29 PM
Android Dateformat getTimeInstance SHORT DE: 1:29 nachm.
df_date_de_short is type of: java.text.SimpleDateFormat
Android Emulator NEXUS_5_API_26, US locale
Android Dateformat getTimeInstance SHORT US: 1:18 PM
Android Dateformat getTimeInstance SHORT DE: 13:18
df_date_de_short is type of: java.text.SimpleDateFormat
So the forced use of "java.text.SimpleDateFormat" works, but only on an emulator, not in real world? I'm close, maybe someone has the last 5 cents!
My guess earlier was right, sadly. If you don't search for it with this "guess", you'll never find it as date formatting is such a common topic. Long story short, it's an Android bug:
https://issuetracker.google.com/issues/37054851
It was fixed over a year ago. That's why the emulator works, but devices before API27 won't have the fixes as OEM don't care.
Google employee added this workaround in above bug report:
boolean use24Hour = android.text.format.DateFormat.is24HourFormat(context);
final String skeleton = use24Hour ? "Hm" : "hm";
final String pattern = android.text.format.DateFormat.getBestDateTimePattern(locale, skeleton);
DateFormat formatter = new SimpleDateFormat(pattern, locale);
... for SHORT date. MEDIUM would use skeletons of "Hms" / "hms" instead.
The code above bypasses the internal code that keep the (incorrect) in-memory state that tracks whether the user prefers 12 or 24 hour time formatting. AFAIK, android.text.format.DateFormat.is24HourFormat has always used the underlying user setting.
is24HourFormat() was added in API 3. getBestDateTimePattern() was only added in API 18.
That's exactly the switch case crap I feared humanity has to use in the year of 2018. We want to live on Mars, yet we can't figure out how to print time on planet Earth!
why don't u try using SimpleDateFormat
instead of using default DateFormat? maybe you can do something like this:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
dateFormat.setTimeZone(tz_de);
Log.d("localeTest",dateFormat.format(c_us.getTime())));

Calendar set WEEK_OF_YEAR don't work properly

I have built a function to return me a date (as a String) providing the week number and the day.
My function is working perfectly on my device (Samsung Galaxy S6 under LineageOS android 7.1.2), but one of my friend, using another android device (android 5.1.x, that's all I know), reported me an error : the function returns him a date in the current week (the week of the real time when the function is called) whatever the week number he give.
To not bother my friends, I'm using an emulator that have the same problem. It's the default one in Android studio (Emulator Nexus_5_API_23 android 6.0).
Here is my function :
public static String getDayString(int weekNumber, int dayPosition) {
DateFormat sdf = DateFormat.getDateInstance();
Calendar cal = Calendar.getInstance();
cal.set(Calendar.WEEK_OF_YEAR, weekNumber);
cal.set(Calendar.DAY_OF_WEEK, dayPosition + 2);
return sdf.format(cal.getTime());
}
In my first attempts to understand the problem, I discovered that cal.set(Calendar.WEEK_OF_YEAR, weekNumber); wasn't working.
Then I putted Log.d("ClassName", String.valueOf(cal.get(Calendar.WEEK_OF_YEAR))); to see if the week number was properly changed from the current week number (set by getInstance()) to the desired week number (weekNumber). But the problem disappeared and the function was working perfectly...
It turns out that when I put cal.get(Calendar.WEEK_OF_YEAR); right after cal.set(Calendar.WEEK_OF_YEAR, weekNumber); the week number is updated but cal.set(...) alone is not working.
Here is my "fixed" function :
public static String getDayString(int weekNumber, int dayPosition) {
DateFormat sdf = DateFormat.getDateInstance();
Calendar cal = Calendar.getInstance();
cal.set(Calendar.WEEK_OF_YEAR, weekNumber);
cal.get(Calendar.WEEK_OF_YEAR);
cal.set(Calendar.DAY_OF_WEEK, dayPosition + 2);
return sdf.format(cal.getTime());
}
I'm extremely surprised and confused by this result. How cal.get(...) can change anything ? How I can find another way to fix this "problem" ? What is the problem with the cal.set(...) method ? Did I do something wrong or am I missing something ?
This strange "quantum observer effect" problem doesn't appear to be present in Java in general. But only on some (not all) android devices.

SimpleDateFormat doesn't work properly on Android 2.3 and earlier

I got a SimpleDateFormat like this.
SimpleDateFormat dateFormat=new SimpleDateFormat("dd MMMM", new Locale("tr"));
txtDateText.setText(dateFormat.format(selectedDate).toUpperCase(new Locale("tr")));
When I try it on Android 3.0, 3.1, 3.2, 4.0 and other latest versions I get this result.
02 KASIM
But when trying on Android 2.2, 2.3 and others.., get this result.
02 11
How can I resolve it?
I had the same problem once. You may check if turkish locale exists.
BTW it is better to get month names from strings.xml. It will be easier to add multiple language support to your application in the future. Just get month number with default locale from the date object and its reference from your months array.
Edit: I wondered the reason for this difference between api levels and downloaded the source code for api level 10 and 14, then started debugging.
Reason: Shortly, difference comes from java.text.DateFormatSymbols class. Both apis have same code there but they are using different versions (or have different behaviours) of libcore.icu.LocaleData. Which is responsible to initialize DateFormatSymbols instace.
Detail:
If SimpleDateFormat instance is created with a locale, this parameter is used to instantiate its property called formatData (which is an instance of DateFormatSymbols).
Its constructor:
From API Level 10 and 14 java.text.DateFormatSymbols
public DateFormatSymbols(Locale locale) {
this.locale = locale;
this.localPatternChars = SimpleDateFormat.PATTERN_CHARS;
LocaleData localeData = LocaleData.get(locale);
this.ampms = localeData.amPm;
this.eras = localeData.eras;
this.months = localeData.longMonthNames;
this.shortMonths = localeData.shortMonthNames;
this.weekdays = localeData.longWeekdayNames;
this.shortWeekdays = localeData.shortWeekdayNames;
// ICU/Android extensions.
this.longStandAloneMonths = localeData.longStandAloneMonthNames;
this.shortStandAloneMonths = localeData.shortStandAloneMonthNames;
this.longStandAloneWeekdays = localeData.longStandAloneWeekdayNames;
this.shortStandAloneWeekdays = localeData.shortStandAloneWeekdayNames;
}
I couldn't debug on libcore.icu.LocaleData but difference comes from that class. Values are loaded diffently from LocalData instance for api levels 10 and 14. Change log can be investigated to find the differences on LocaleData
Sum:
If a SimpleDateFormat is created with an undefined locale(e.g. "xx") prior to Api Level 10, system uses numeric representation (for month names [1..12], day names[1..6]) of default locale's output.
After API Level 14, output is shown based on default locale if locale is not found.
(I haven't tested on api levels 11,12,13 yet.)
I suggest you to use system's default locale. If you have to use any locale, first check that if it is defined or not. If it is not defined write your own logic.
If you want to dislay a date, consider my 2. line on this answer.
Could it be that the "tr" Locale is not available on those devices?
I think you can check it with:
for (Locale locale : Locale.getAvailableLocales()) {
System.out.println(locale.toString());
}
It would appear that the older devices do not have the Turkish locale installed.

Why does java.text.DateFormat return the same date format for en_US and en_GB on Android?

I have a small Android application which I'm using to print a specific date in different formats based on locale.
Here is my code (using java.text.DateFormat):
Locale[] locales = {new Locale("en", "US"), new Locale("en", "GB"), new Locale("en", "AU"), new Locale("en", "NZ"), new Locale("en", "ZA")};
for(int i = 0; i < locales.length; ++i) {
Log.d(logKey, locales[i].toString() + " - " + DateFormat.getDateInstance(DateFormat.SHORT, locales[i]).format(Calendar.getInstance().getTime()));
}
The output from this in LogCat is thus:
D/FormatPoC( 390): en_US - 4/27/12
D/FormatPoC( 390): en_GB - 4/27/12
D/FormatPoC( 390): en_AU - 4/27/12
D/FormatPoC( 390): en_NZ - 4/27/12
D/FormatPoC( 390): en_ZA - 4/27/12
So my question is - why are all of these the same? In Java SE I get:
en_US - 4/27/12
en_GB - 27/04/12
en_AU - 27/04/12
en_NZ - 27/04/12
en_ZA - 2012/04/27
Which is what I would expect. I know that I can use android.text.format.DateFormat to get correct results based on the user's current locale and date order setting, but that doesn't explain why using java.text.DateFormat to get the format for a programmatically specified locale doesn't return the right results.
Additionally, it's not just the SHORT date format - MEDIUM and LONG show inconsistencies between Android and Java SE as well (i.e. Android returns the same format for all 5 Locales I specified).
I've tested it on 3 different devices (2.3 and 4.0) and on the emulator (2.3 and 4.0), all with the same results. I've also tested using Locale.US and Locale.UK just to see if they're somehow different, but the results are the same.
Has anyone else run into this, or know why this would be?
UPDATE: 2012-07-18
It appears that this is an issue with the emulator, as well as many devices manufactured in the US. Using Dalvik Explorer:
https://play.google.com/store/apps/details?id=org.jessies.dalvikexplorer&hl=en
I've been able to see what the system returns for en_GB on different devices (including the emulator). Some return the appropriate formats, some return the en_US format. I assume this is simply an issue of what format resources are built into the OS for each device, though being that the emulator returns the wrong formats as well as many of my US-manufactured devices, I wonder what British developers think, or if they've seen this problem.
This is not an answer (I do not yet have enough rep to add a comment...)
As a UK developer I have come across this problem. I am seeing this problem with my Galaxy S3, exactly as you have described.
I'm having to resort to allowing the user to choose the date format as a preference. Not very good.
The DalvikExplorer program also shows the problem:
try this:
int style = DateFormat.MEDIUM;
//Also try with style = DateFormat.FULL and DateFormat.SHORT
Date date = new Date();
DateFormat df;
df = DateFormat.getDateInstance(style, Locale.UK);
Log.d("Locale.UK","Locale.UK - "+df.format(date));
System.out.println("United Kingdom: " + df.format(date));
df = DateFormat.getDateInstance(style, Locale.US);
Log.d("Locale.US","Locale.US - "+df.format(date));
df = DateFormat.getDateInstance(style, Locale.FRANCE);
Log.d("Locale.FRANCE","Locale.FRANCE - "+df.format(date));
df = DateFormat.getDateInstance(style, Locale.ITALY);
Log.d("Locale.ITALY","Locale.ITALY - "+df.format(date));
df = DateFormat.getDateInstance(style, Locale.JAPAN);
Log.d("Locale.JAPAN","Locale.JAPAN - "+df.format(date));

Categories