tony9099
tony9099

Reputation: 4717

timezone clarification in java / android

I have the below code

public Long getEpochTime(String dateToGetItsEpoch) throws ParseException
{
    TimeZone timeZone = TimeZone.getTimeZone("UTC");
    final String REQUEST_DATE_FORMAT = "dd/MM/yyyy h:m";

    DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
    Date localDate = format.parse(dateToGetItsEpoch);

    Calendar cal = Calendar.getInstance(timeZone);
    cal.setTime(localDate);

    format.setTimeZone(timeZone);
    final String utcTime = format.format(cal.getTime());

    Date d = cal.getTime();

    return d.getTime();
}

If I change the locale of my device to whatever, I always get the UTC time as the return value. Which is correct, however I want to know how is this happening ? How does the device know Which timezone is the date I am giving to it so that it calculates accordingly ?

Upvotes: 1

Views: 241

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338875

java.time

The Answer by Jon Skeet is correct. Here is some code updated to use the modern java.time classes that have supplanted the troublesome legacy date-time classes.

Formatting pattern

Define a formatting pattern to match your inputs.

By the way, yours is a poor choice of formats. Instead I recommend using the standard ISO 8601 formats designed for exchanging date-time values as text.

12-hour versus 24-hour clock

Your input data or formatting pattern has a flaw. You used lowercase h which means one or two digits for an hour in the 12-hour clock (rather than 24-hour clock, which is uppercase H or HH). So your input makes no sense unless you add some indicator of AM or PM. I will assume you mistakenly omitted this from your Question's code.

Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu h:m a" ).withLocale( locale ) ;

LocalDateTime

Parse such strings as LocalDateTime objects, as they lack an indicator of the intended time zone or offset-from-UTC.

String input = "23/01/2020 4:5 PM" ;

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

ldt.toString(): 2020-01-23T16:05

Moment

The LocalDateTime object we obtained above does not represent a moment, is not a point on the timeline. We have a time of around 4 PM on the 23rd. But we cannot know if this was meant to be 4 PM in Tokyo, Toulouse, or Toledo — all very different moments several hours apart.

To determine a moment, we must know for certain the intended time zone. Then apply that zone as a ZoneId to get a ZonedDateTime. Then we have arrived at a moment.

Locale is not a time zone

locale of my device to whatever

A Locale has nothing to with time zone. A Locale is used for localizing generated text representing a date-time object.

To localize, specify:

  • FormatStyle to determine how long or abbreviated should the string be.
  • Locale to determine:
    • The human language for translation of name of day, name of month, and such.
    • The cultural norms deciding issues of abbreviation, capitalization, punctuation, separators, and such.

Example:

Locale l = Locale.CANADA_FRENCH ;   // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f = 
    DateTimeFormatter
    .ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( l )
;
String output = myZonedDateTime.format( f );

You could have an engineer from Québec who uses the Locale.CANADA_FRENCH for human language and cultural norms, but while visiting in Japan uses Asia/Tokyo time zone for scheduling appointments.

ZonedDateTime

Back to your LocalDateTime object. If you are certain it was meant to represent a moment as seen in the wall-clock time in Tunisia, then apply a time zone of Africa/Tunis.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

You asked:

How does the device know Which timezone is the date I am giving to it so that it calculates accordingly ?

You were using terrible date-time classes that failed to account for the concept of a date-time lacking an indicator of time zone or offset-from-UTC. So technically, your code is a mess, a hack, unavoidable in those days before Joda-Time and its successor, java.time.

I suggest spending no effort on trying to understand that behavior of Date & Calendar. Just move on to using java.time, the industry-leading date-time handling framework.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1501163

A Date doesn't have a time zone at all. A SimpleDateFormat does as a default for parsing and formatting; a Calendar does too; a Date doesn't.

Given this sequence of operations:

TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date localDate = format.parse(dateToGetItsEpoch);

Calendar cal = Calendar.getInstance(timeZone);
cal.setTime(localDate);

format.setTimeZone(timeZone);
final String utcTime = format.format(cal.getTime());

... you're initially parsing the string using the default time zone of the device, then you're formatting it in UTC. Note that the Calendar part is irrelevant here - you'd get the same result with:

TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date date = format.parse(dateToGetItsEpoch);
format.setTimeZone(timeZone);
final String utcTime = format.format(date);

I would personally recommend using Joda Time where possible for date/time work in Java, mind you. It's a much cleaner API than Calendar/Date.

Upvotes: 3

Related Questions