0

I'm using following code. Values (hour, minute, second, date, month, year, day_of_week) are hardcoded for simplicity.

Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 10);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.DATE, 2);
c.set(Calendar.MONTH, Calendar.APRIL);
c.set(Calendar.YEAR, 2018);
c.setFirstDayOfWeek(Calendar.MONDAY);
c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

String date = String.format(Locale.US, "%1$tA %1$tb %1$td %1$tY at %1$tI:%1$tM %1$Tp", c);
Log.e("date", date);

Observation 1

This code prints the the following on the Titanium S1 (Android 4.1.2)

E/date: Monday Mar 26 2018 at 10:00 AM

This code prints the the following on the Moto G4+ (Android 7.0), Moto G5+ (Android 7.0)

E/date: Monday Apr 02 2018 at 10:00 AM

Observation 2 This difference appears only on 2nd April & 2nd July to my knowledge. I am seeing the first day of month is Sunday for both the April and July month. It works fine for rest of the days

Question 1) Does the first day of month (MONDAY) cause this difference? Changing it to SUNDAY seems working on these two days. But I am not sure this works in all the scenarios.

This is affecting users on my app. Please help. Thanks.

Harsh4789
  • 677
  • 7
  • 17

1 Answers1

2

I've made some test in JDK 7 and the results don't seem to be affected by setFirstDayOfWeek. Actually, it seems to be related to the JVM default locale (java.util.Locale).

When the default locale is Japanese or Thai, the final result is March. Somehow, the JVM default locale affects the internals of Calendar, in some misterious ways that, I must admit, are beyond my understanding. But anyway, one way to fix it is to set in the Calendar the same locale you used in the output:

Calendar c = Calendar.getInstance(Locale.US);

This fixed the issue for me.

java.time API

This bizarre behaviour is one of the reasons to not use the old Calendar API (there are many others, btw).

In Android, it's possible to use the java.time API. In API level 26, the java.time classes are available. For lower levels, you can use this backport - and in this link there are instuctions to configure it in Android.

With this new API, it's much easier - and less error prone - to get what you want. To build a specific date/time, use a ZonedDateTime class, and a DateTimeFormatter to convert it to a String in a specific format:

// April 2nd 2018, 10 AM
ZonedDateTime dt = ZonedDateTime.of(2018, 4, 2, 10, 0, 0, 0, ZoneId.systemDefault());
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEEE MMM dd yyyy 'at' hh:mm a", Locale.US);
String formattedDate = dt.format(fmt); // Monday Apr 02 2018 at 10:00 AM

Note that the day of week is adjusted automatically, based on the day/month/year values provided.

To get the current date/time, just use ZonedDateTime.now(ZoneId.systemDefault()) (to get the current date/time in JVM's default timezone), or use ZoneId.of("zone name") to use a specific timezone - replace "zone name" with any valid IANA zone's names.

uilon
  • 135
  • 4
  • Thanks for your response. I am about to try Local.US first. :) – Harsh4789 Apr 02 '18 at 13:11
  • In regards to 'Note that the day of week is adjusted automatically, based on the day/month/year values provided.' - My database has day of week values. I am suppose to get the time in millis from the the hour, minute, second, day of week. And time in millis required for the AlarmManager. – Harsh4789 Apr 02 '18 at 13:13
  • You first suggestion worked for me. More specifically, Local.US didn't work. According to my local constants, Local.France worked for me. The reason is I need MONDAY as a first day of week & the Local.France does the same. May be taking Local.US & MONDAY as a first day of week messing up the things because Local.US has SUNDAY as a first day of week. I am testing more scenarios. If everything works alright, I will accept your answer. Thank you very much for pointing me out a way. – Harsh4789 Apr 02 '18 at 14:59
  • Good Answer except one crucial issue: **Never use `LocalDateTime` for current moment**. That class lacks any concept of time zone or offset-from-UTC. That class does *not* represent a moment, is *not* a point on the timeline. To capture the current moment, use only `Instant` or `ZonedDateTime`. I must down-vote until that wrong advice is corrected. – Basil Bourque Apr 02 '18 at 16:15
  • 1
    @BasilBourque You're right, I should've used `ZonedDateTime`. Fixed, thanks! – uilon Apr 02 '18 at 16:25
  • @uilon. Finally Local.France worked for me in all scenarios. I have accepted the answer. Thanks. – Harsh4789 Apr 02 '18 at 17:02