1

I am getting the 4 hours difference on time zone from below lines of code on my device:

I am getting the time in such a way like 2018-09-30T13:45:00Z

My start and End Date is as follow: -

"start_date":"2017-09-13T12:15:00Z",
"end_date":"2018-09-30T13:45:00Z",

Calendar c = Calendar.getInstance(Locale.getDefault());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm");

dateFormatter.setTimeZone(c.getTimeZone());
localStartDate = dateFormatter.parse(startTime);
localEndDate = dateFormatter.parse(endTime);
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String monthName = monthFormat.format(localStartDate);
eventDate.setMonth(monthName);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String dateName = dateFormat.format(localStartDate);
eventDate.setDate(dateName);
SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));


String dayName1 = dayNameFormat.format(localStartDate);
String dayName2 = dayNameFormat.format(localEndDate);
eventDate.setDayName1(dayName1);
eventDate.setDayName2(dayName2);
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));



String startTimeName = timeFormat.format(localStartDate);
String endTimeName = timeFormat.format(localEndDate);


System.out.println("My Start date and end date==>>>"+startTimeName+"  " +endTimeName );

Problem: Getting the 4 hours difference from above code, as I am setting my time zone to BOSTON(US), getting error.

My result from the below @Hugo solution is as below enter image description here

And i am expecting the result as below enter image description here

Please check it once..I have also set the TimeZone of Eastern DayLight Time but not getting proper solution..please check it once..And let me know

Ravindra Kushwaha
  • 7,846
  • 14
  • 53
  • 103
  • 1
    What's the value of `TimeZone.getDefault().getDisplayName()`? Is `2018-09-30T13:45:00Z` the input `String`? What's the output you're getting and the expected output? –  Sep 13 '17 at 15:42
  • @Hugo..Will post my output and expected output on tomorrow.. Right now I am away from office – Ravindra Kushwaha Sep 13 '17 at 16:05
  • Well, I've posted an answer. If it's not what you need, just let me know and I'll update it accordingly. –  Sep 13 '17 at 17:34
  • 2
    Beware of the case of format pattern letters. Lowercase `hh` is for hour within AM or PM in the range 01 through 12. For hour in day from 00 through 23 use uppercase `HH`. – Ole V.V. Sep 13 '17 at 20:36
  • Would it be possible to demonstrate your problem in fewer lines of code, please? See http://www.sscce.org, please. – Ole V.V. Sep 13 '17 at 20:37
  • 1
    @Hugo, the last pattern in the code (`hh:mm a`) contains `a`. The first two contain `hh` without `a`. It may not be part of what is causing the problem that is being asked about, but I still suspect it’s a bug and believe it is worth mentioning. – Ole V.V. Sep 14 '17 at 02:43
  • @OleV.V. I really hadn't noticed `hh` in the first formatter (and the second is redundant, so I removed it). I've updated my answer, thanks a lot! –  Sep 14 '17 at 12:12

1 Answers1

2

SimpleDateFormat and Calendar uses the JVM default timezone (unless you set a different one on them), and the default timezone can be different in each device/machine/environment. Not only that, this default can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.

When you do things like:

Calendar c = Calendar.getInstance(Locale.getDefault());
dateFormatter.setTimeZone(c.getTimeZone());
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));

The Calendar is created with the default timezone, so dateFormatter will also have the same zone. So does monthFormat, and also the other formatters you created. The only formatter set to a different zone is the first one (which is set to UTC).

Also, the second formatter is redundant (it does the same thing that the first one is already doing: parsing the String to a Date), so you can remove it.

Assuming that your input is a String with the value 2018-09-30T13:45:00Z: the Z in the end indicates that this date is in UTC. So you should parse it using a formatter set to UTC. So, instead of using c.getTimeZone() and TimeZone.getDefault(), you should use only TimeZone.getTimeZone("UTC").

For the output, you must set the formatters with the timezone you want to convert to. If the timezone is "EDT", set to it (but don't use exactly "EDT", see below). If you want to use the JVM default, use TimeZone.getDefault() - just check this value before, to make sure the default is what you need.

Just keep in mind that short names like "EDT" and "EST" are not real timezones. Those abbreviations are ambiguous and not standard. Prefer to use IANA timezones names (always in the format Region/City, like America/New_York or Europe/Berlin).

So, when you do TimeZone.getTimeZone("EDT"), it usually returns "GMT" (because "EDT" is not recognized, and "GMT" is returned as default). That's because "EDT" is used by more than one timezone, so you must choose specifically which one you're using (I'm using America/New_York in these examples).

Another detail is that in the first 2 formatters you use hh, which means "hour of am/pm" (values from 1 to 12), but the input doesn't have AM/PM designators to properly resolve this. You need to change it to HH ("hour of day", with values from 0 to 23).

    // input is in UTC
TimeZone inputZone = TimeZone.getTimeZone("UTC");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
formatter.setTimeZone(inputZone);
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
...

// removed the second formatter (it was redundant)

// output is in EST (America/New_York)
// or use TimeZone.getDefault() to get JVM default timezone
TimeZone outputZone = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(outputZone);
...

SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(outputZone);
...

SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(outputZone);
...

SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(outputZone);
...

System.out.println("My Start date and end date==>>>" + startTimeName + "  " + endTimeName);

With this, you're explicity using UTC for input and a specific timezone for output, instead of relying on the JVM default timezone (which can be different in each device and you can't control).

The output is:

My Start date and end date==>>>08:15 AM 09:45 AM


Java new Date/Time API

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.

In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).

First you can use a org.threeten.bp.Instant to parse the input, because it's in UTC (designated by the Z in the end). Then you use a org.threeten.bp.ZoneId to convert it to a org.threeten.bp.ZonedDateTime:

// output timezone
// or use ZoneId.systemDefault() to get JVM default timezone
ZoneId zone = ZoneId.of("America/New_York");
// parse the inputs
ZonedDateTime startDate = Instant.parse(startTime).atZone(zone);
ZonedDateTime endDate = Instant.parse(endTime).atZone(zone);

Then you can use these objects to get the other fields:

// get month name
System.out.println(startDate.getMonth().getDisplayName(TextStyle.SHORT, Locale.getDefault()));

This is equivalent to MMM pattern, and it will print the month name in the default locale. If you want the month name in a specific language, just use another java.util.Locale value (such as Locale.ENGLISH or any other one as described in the javadoc).

The org.threeten.bp.format.TextStyle defines if the month name will be narrow (usually just one letter), short (usually 2 or 3 letters) or full (the full name). The output varies according to the locale used.

I personally prefer to not use the default locale, because it can be changed without notice, even at runtime. It's always better to specify the locale you want.

To get the day of month, you can choose to get it as an int or as a formatted String (using a org.threeten.bp.format.DateTimeFormatter):

// get day of month as int
int day = startDate.getDayOfMonth(); // 30
// get day of month as formatted string
String dayStr = DateTimeFormatter.ofPattern("dd").format(startDate); // 30

To get the day of week, it's similar to the code used to get the month:

// get day of week
System.out.println(startDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault()));

The same logic applies here: the TextStyle defines how the name will be (in this case, FULL is equivalen to EEEE, and it prints the full name), and the locale defines the language used.

Finally, to get the corresponding time, you can use another DateTimeFormatter:

// get time
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("hh:mm a");
System.out.println(fmt.format(startDate)); // 08:15 AM
System.out.println(fmt.format(endDate)); // 09:45 AM

This will the date/time in the timezone you chose for the output.

If you're going to use the JVM default (ZoneId.systemDefault()), just check its value before to make sure it's the one you want (it might not be because this can be changed at runtime, so it's always better to specify one).

  • Thanks for urs solution..It is `NOT` working for me..I have edited the question please check it once..I have done the code as u suggested.But now i am getting the time +4 hours..Please help me on it – Ravindra Kushwaha Sep 14 '17 at 05:53
  • I have used [link](https://github.com/JakeWharton/ThreeTenABP) But did not get the solution.Please check it once. – Ravindra Kushwaha Sep 14 '17 at 06:07
  • Please help me , i have tried urs solution , but it is not working :( – Ravindra Kushwaha Sep 14 '17 at 09:14
  • @RavindraKushwaha I'm not in front of a computer right now, but using threetenbp, use `startDate.atZoneSameInstant(ZoneId.of("America/New_York")).format(fmt)` and see if it works. I'll update the answer as soon as I get to a computer to properly test (and also to add a detailed explanation) –  Sep 14 '17 at 09:18
  • Ok.. @Hugo ..My main concern is that i do not want to set the TimeZone `hardcodded` ,It should be device timezone..Thanks anyway..Waiting for urs input.. – Ravindra Kushwaha Sep 14 '17 at 09:20
  • @RavindraKushwaha To use the device timezone, use `ZoneId.systemDefault()` instead of `ZoneId.of` –  Sep 14 '17 at 09:21
  • Ok dost i am testing and let u know soon – Ravindra Kushwaha Sep 14 '17 at 09:26
  • Sorry @Hugo Not worked..Please look it whenever u will get the time' – Ravindra Kushwaha Sep 14 '17 at 09:31
  • @RavindraKushwaha What output did you get now? –  Sep 14 '17 at 09:34
  • @RavindraKushwaha Also, what's the value of `ZoneId.systemDefault().getId()`? –  Sep 14 '17 at 09:38
  • Sorry for late reply..I am getting the same output as i was getting on my question's Image..For your effort +1 Dost..Please let me know when u get the another way..I am eagerly waiting for marking answer as useful :) – Ravindra Kushwaha Sep 14 '17 at 11:00
  • @RavindraKushwaha When you print the value of `ZoneId.systemDefault().getId()` and `Timezone.getDefault()`, what's the output? –  Sep 14 '17 at 11:35
  • @RavindraKushwaha I've updated the answer. There's some details about `SimpleDateFormat` (`hh` vs `HH` and timezones), timezone creation ("EDT") and another approach with ThreeTen Backport. But if you could please confirm the values of `ZoneId.systemDefault().getId()` and `TimeZone.getDefault().getID()`, so we can have a better idea of what's happening... –  Sep 14 '17 at 12:04
  • I have used these lines of code `System.out.println("Here is the Yime==>>"+ ZoneId.systemDefault().getId() +" "+TimeZone.getDefault().getID());` **Getting output** `Here is the Yime==>>America/New_York America/New_York` ` – Ravindra Kushwaha Sep 14 '17 at 12:17
  • @RavindraKushwaha Then it should work (in my tests, the output is 08:15 AM and 09:45 AM) –  Sep 14 '17 at 12:30
  • @Hugo..IS it working on urs end..I think i have used urs previous code..Will try urs updated code..And let u know soon.. – Ravindra Kushwaha Sep 14 '17 at 12:36