0

My client/browser is in India and I get the timezoneoffset from javascript using the following code:

var now = new Date();
var localOffSet = now.getTimezoneOffset(); -330 // for India
int localOffSetMin = (localOffSet)*(-1); 

My server is located in New York so I get the offset for it using:

 TimeZone timeZone = now.getTimeZone();
 int serverOffset = timeZone.getRawOffset();
 int serverOffSetMinutes = serverOffset / 60000; // -300 for America/New York

In order to find the local time on my machine, I use this:

int offSets = Math.abs(serverOffSetMinutes-localOffSetMin); 

now.setTime(createDt); // createDt is date field value for some column
now.add(Calendar.MINUTE, offSets); // adds offset  
Date localDt = now.getTime(); 

But the date/time I get is 1 hour ahead of the expected time. What am I missing?

user3772144
  • 101
  • 2
  • 10
  • Use this `getDefault` -> https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html – notphilphil Jul 03 '17 at 15:28
  • 1
    Could you provide examples of expected values and what you got? –  Jul 03 '17 at 16:51
  • I have to get the browser timezone from JavaScript . So using new Date.getTimezoneOffset(). How do we get the timezone string or id in JavaScript? e.g. If the server time is 7:21 AM (EST) , the local time should show 4:51 PM but after the offset calculation, it shows 5:51 PM – user3772144 Jul 03 '17 at 19:44
  • 1
    [How can I get the timezone name in JavaScript?](https://stackoverflow.com/questions/9772955/how-can-i-get-the-timezone-name-in-javascript) It’s a good idea since your code only gets the offset right now and doesn’t detect summer time (DST) or other anomalies. – Ole V.V. Jul 03 '17 at 22:08
  • 1
    The server time zone shouldn’t matter if you got the time as an `Instant` or a `Date` since these are just points in time independent of any time zones or offsets. Just format into the client time zone before displaying in the browser. – Ole V.V. Jul 03 '17 at 22:15

2 Answers2

3

Date and Time manipulation with Java SE

You can print a list of supported TimeZones by using the following code.

System.out.println(TimeZone.getAvailableIDs().toString());

You can then find and print the difference between the timezones with the following code. You must be mindful of daylight savings time.

public void printTimeZoneDifference(String from, String to) {
   TimeZone easternStandardTime = TimeZone.getTimeZone(from);
   TimeZone indiaStandardTime = TimeZone.getTimeZone(to);

   long milliseconds = easternStandardTime.getRawOffset() - indiaStandardTime.getRawOffset() + easternStandardTime.getDSTSavings() - indiaStandardTime.getDSTSavings();
   String difference = String.format("%02d min, %02d sec", TimeUnit.MILLISECONDS.toMinutes(milliseconds), TimeUnit.MILLISECONDS.toSeconds(milliseconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));

   System.out.println("The difference in time between" + easternStandardTime.getDisplayName() + " and " + indiaStandardTime.getDisplayName() + " is " + difference);
 }

Although if I were to write something like this I would probably pass a TimeZone object as a parameter and keep the method solely responsible for substraction. Then I would either print the results or make it part of a different method. I didn't structure the post that way because I wanted to include all relevant code in the post.

Date and Time manipulation with Joda

This type of manipulation has already been solved in Java. The Joda Time Library is probably your best bet if you are doing a lot of date manipulation. If you are only manipulating time in this one instance then it would be a bit over kill to include the dependency in your runtime.

Again print out the TimeZones.

public void printDateTimeZones() {
   for(String zone : DateTimeZone.getAvailableIDs()) {
      System.out.println(zone);
   }
}

Then you can return a String of the period (difference) between the two DateTimeZones using the default formatting with the following code.

public String printPeriod(String from, String to) {
   Period period = new Period(new DateTime(DateTimeZone.forID(to)), new DateTime(DateTimeZone.forID(from)));
   return PeriodFormat.getDefault().print(period);
}

Similarly Joda provides a format builder class which allows you to specify your preferred formatting.

public String printPeriod(String from, String to) {
   PeriodFormatter formatter = new PeriodFormatterBuilder()
     .printZeroRarelyFirst()
     .appendYears().appendSuffix(" Years").appendSeparator(",")
     .appendMonths().appendSuffix(" Months").appendSeparator(",")
     .appendWeeks().appendSuffix(" Weeks").appendSeparator(",")
     .appendDays().appendSuffix(" Days").appendSeparator(",")
     .appendHours().appendSuffix(" Hours").appendSeparator(",")
     .appendSeconds().appendSuffix(" Seconds").appendSeparator(",")
     .appendMillis().appendSuffix(" Milliseconds")
   .toFormatter();

   return formatter.print(new Period(new DateTime(DateTimeZone.forID(from)), new DateTime(DateTimeZone.forID(to))));
}
Chris Maggiulli
  • 3,375
  • 26
  • 39
  • I have to make the code generic so that it can pick up timezone from any location and do the math. I cannot hardcode EST and IST. – user3772144 Jul 03 '17 at 15:23
  • I modified accordingly. I also added a bit of code that lets you see all supported time zones for your version of Java. Please be mindful of the tips at the bottom of the post. For code quality purposes I would utilize a 3rd party library for date/time manipulation if it suits your application. Also I would modify the method you use so that it has a single concern. The method should find the difference between two time zones and return that as a date or a string. The string to date conversion and the printing should be done elsewhere – Chris Maggiulli Jul 03 '17 at 15:38
  • I believe I would prefer Joda-Time over the outdated `TImeZone` class. And `java.time` over Joda-Time. [The Joda-Time home page](http://www.joda.org/joda-time/) says “Users are now asked to migrate to `java.time` (JSR-310).” – Ole V.V. Jul 03 '17 at 22:03
2

A java.util.Date object has no timezone information. It has only a long value, which is the number of milliseconds from 1970-01-01T00:00:00Z (also known as "unix epoch" or just "epoch"). This value is absolutely independent of timezone (you can say "it's in UTC" as well).

To convert this value to another timezone, you don't need to do all these math between the timezones. You just get this millis value and convert it to the desired timezone.

To get the value from javascript, just do:

var d = new Date();
var millis = d.getTime();

The variable millis will contain the number of milliseconds from epoch. In the test I've made, this value is 1499101493296.

To create a java.util.Date object, just do:

Date date = new Date(1499101493296L);

To format this date in the timezone you want, use a SimpleDateFormat:

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
System.out.println(sdf.format(date));

The output will be:

03/07/2017 22:34:53

If you want a different format, check the javadoc for more information.

Also note that I used a timezone name using IANA format (always in the format Continent/City, like America/Sao_Paulo or Europe/Berlin). Avoid using the 3-letter abbreviations (like IST or EST) because they are ambiguous and not standard.

To use another timezone, you can use one the IANA's names - check all the available names using TimeZone.getAvailableIDs().


New Java 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.

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

Although you can also use Joda-Time, it is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

Once you have the millis value, the code for creating a date and converting to some timezone is very similar:

ZoneId zone = ZoneId.of("Asia/Kolkata");
ZonedDateTime z = Instant.ofEpochMilli(1499101493296L).atZone(zone);
System.out.println(z); // 2017-07-03T22:34:53.296+05:30[Asia/Kolkata]

The output will be:

2017-07-03T22:34:53.296+05:30[Asia/Kolkata]

If you want a different format, use a DateTimeFormatter:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss x");
System.out.println(z.format(fmt)); // 03/07/2017 22:34:53 +0530

The output will be:

03/07/2017 22:34:53 +0530

If you want a different format, check the javadoc for more details.

To use another timezone, you can use one the IANA's names - check all the available names using ZoneId.getAvailableZoneIds().