0

I am trying to convert IST to UTC epoch in Java But instead of subtracting 5.30 hours from IST, it adds 5.30 in IST

public static long convertDateToEpochFormat(String date) {
    Date convertedDate = null;
    try {
        LOGGER.info(date);
        DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        LOGGER.info(date);
        convertedDate = formatter.parse(date);
        LOGGER.info(convertedDate);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return convertedDate.getTime() / 1000L;
}

The log statements I obtained is :

2017-01-01 00:00:00
2017-01-01 00:00:00
Sun Jan 01 05:30:00 IST 2017

It should ideally be Dec 31 18:30:00 because of UTC conversion.

Can anyone tell me whats wrong ?

Paul Karam
  • 4,052
  • 8
  • 30
  • 53
Vishal A
  • 144
  • 3
  • 17
  • 2
    When the time is `0:00` in London (UTC), it is `5:30` in the morning in India (IST). Why do you expect otherwise? You are parsing the date string, and telling the system to treat that date string as being UTC time. You then print it using the default time zone, so it will print that *same time* in that zone. – Andreas Jun 15 '17 at 06:47
  • 2
    You're not converting IST to UTC. You're parsing in UTC and printing in the system default, which is presumably IST. – shmosel Jun 15 '17 at 06:47
  • Possible duplicate of [Changing Java Date one hour back](https://stackoverflow.com/questions/8833399/changing-java-date-one-hour-back) – patrik Jun 15 '17 at 06:57
  • 1
    @Andreas You make a valid point. But I suggest refraining from calling UTC “London” time. London time has anomalies such as Daylight Saving Time (DST), so it is not in fact UTC. This misnomer creates much confusion. – Basil Bourque Jun 15 '17 at 07:12
  • Related: [Timezone conversion](https://stackoverflow.com/questions/6567923/timezone-conversion). – Ole V.V. Jun 15 '17 at 08:59
  • @patrik, while the way of the linked answer would work, I rather encourage using the information of time zone offset built into the JVM. If IST is always +5;30 (I don’t know whether it is, do you?) it will work, but for a time zone that has historically had a different offset and/or has or has had summer time (DST), you will get incorrect results. – Ole V.V. Jun 15 '17 at 09:37
  • @OleV.V. Actually it is not really clear whether the issue is that the OP wants go back in time or display the time in another time zone. The title was about subtracting dates, which is what my linked post do. I normally prefer to always work in UTC time, since this always is aligned with the system clock on the machine. This does however not say anything about representation. – patrik Jun 15 '17 at 12:43
  • I would encourage the user who has downvoted this question to please leave an explanation of the reason for downvoting so the asker and anyone else may learn. – Ole V.V. Jun 15 '17 at 20:18
  • 1
    @OleV.V. I agree it would be great! However, I only flagged it as a duplicate. I did not downvote it. Still, the question itself is fairly good quality I would say, so I see no reason for downvoting. However in case it is a dupe I believe it is better to keep all the answers in the same post. – patrik Jun 22 '17 at 08:02

2 Answers2

5

tl;dr

Why does util.Date forwards the date instead of subtracting it?

Because India time is ahead of UTC, not behind.

Instant.parse(
    "2017-01-01 00:00:00".replace( " " , "T" ) + "Z" 
).atZone(
    ZoneId.of( "Asia/Kolkata" )
).toString()

2017-01-01T05:30+05:30[Asia/Kolkata]

Using java.time

You are using troublesome old date-time classes that are now legacy, supplanted by the java.time classes.

ISO 8601

Your input string is almost in standard ISO 8601 format. To comply fully, replace that SPACE in the middle with a T. The java.time classes use standard formats when parsing/generating strings. So no need to specify a formatting pattern.

String input = "2017-01-01 00:00:00".replace( " " , "T" ) ;

If that input is meant to represent a moment in UTC, append a Z, short for Zulu, means UTC.

String input = "2017-01-01 00:00:00".replace( " " , "T" ) + "Z" ;  // Assuming this input was intended to be in UTC.

2017-01-01T00:00:00Z

When possible, just use the ISO 8601 formats in the first place when serializing date-time values to strings.

Instant

Parse that input string as an Instant, a moment on the timeline in UTC with a resolution of nanoseconds.

Instant instant = Instant.parse( input ) ;

instant.toString(): 2017-01-01T00:00:00Z

ZonedDateTime

You seem to want this value adjusted into India time. Apply a ZoneId to get a ZonedDateTime.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

zdt.toString(): 2017-01-01T05:30+05:30[Asia/Kolkata]

See this code run live at IdeOne.com.

India time is ahead of UTC

Your Question expects the India time to go backwards, behind the UTC value. This makes no sense. India time is ahead of UTC, not behind UTC. The Americas have time zones behind UTC as they lay westward. East of the Prime Meridian in Greenwich are offsets ahead of UTC. In modern times, ISO 8601 and most other protocols mark such offsets with a plus sign: +05:30. Note that some old protocols did the opposite (used a negative sign).

Midnight UTC = 5:30 AM India

So midnight in UTC, 00:00:00 at the Prime Meridian, is simultaneously five-thirty in the morning in India.

So all three of these represent the same simultaneous moment, the same point in the timeline:

  • 2017-01-01T00:00:00Z
  • 2017-01-01T05:30+05:30[Asia/Kolkata]
  • 2016-12-31T16:00-08:00[America/Los_Angeles]

Avoid count-from-epoch

Do not handle time as an integer count from epoch as you are doing by returning a long from your method as seen in the Question. In your Java code pass around date-time values using date-time objects, java.time objects specifically. When passing date-time values outside your Java code, serialize to strings using the practical ISO 8601 formats.

Relying on an integer count-from-epoch values is confusing, difficult to debug, impossible to read by humans, and will lead to frustration and errors (even worse: unobserved errors).


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

The answer by Basil Bourque is not only correct, it is also very informative. I have already upvoted it. I’ll try just a little bit of a different angle.

As I understand your question, your date-time string 2017-01-01 00:00:00 should be interpreted in IST, AKA Asia/Kolkata time, and you want to convert it to seconds (not milliseconds) since the epoch. And you are asking why you are getting an incorrect result.

I think the answer is rather banal: When the date-time string is in India time, you should not set UTC time on the formatter you use for parsing it. This is sure to get an incorrect result (if you were to format the date-time into UTC, you would do well in setting UTC as time zone on the formatter used for formatting, but this is a different story).

I agree with Basil Bourque that you should avoid the outdated classes Date and SimpleDateFormat. So here’s my suggestion (assuming you do need epoch seconds and cannot use an Instant as Basil Bourque recommends).

private static DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

public static long convertDateToEpochFormat(String date) {
    return LocalDateTime.parse(date, parseFormatter)
            .atZone(ZoneId.of("Asia/Kolkata"))
            .toInstant()
            .getEpochSecond();
}

This will convert your example string into an instant of 2016-12-31T18:30:00Z and return 1483209000. Please check for yourself that it is correct.

I have been assuming all the way that by IST you meant Indian Standard Time. Please be aware that three and four letter time zone abbreviations are ambiguous. For example, my JVM thinks that IST means Israel Standard Time. If you intended the same, please substitute Asia/Jerusalem for Asia/Kolkata. If you meant Irish Standard Time (another recognized/semi-official interpretation), please use Europe/Dublin. You will of course get different output in each case.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161