-1

I am trying to parse a String with a date into a Date object and then convert it into millis.

But whatever I am doing - the output result in millis is always lower in 2 hours than the input date.

For example if the input date is 2018-1-10 11:30 - the output date will be 2018-01-10 9:30.

This is my code:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String dateString = "2018-1-10 11:30";
Date resultDate = sdf.parse(dateString);
long millis = resultDate.getTime();
// millis = 1515576600000
// When i am trying to convert millis to normal date via online       
// converters i am always getting 
//  this result 10.01.18 9:30

I cannot understand why this is happening. How can I get back valid result after converting millis back? Why I got this difference in two hours?

Community
  • 1
  • 1
bukka.wh
  • 913
  • 2
  • 16
  • 29
  • 3
    It looks like a timezone problem. In which time zone you are ? – Valentin Michalak Jan 10 '18 at 12:28
  • Please provide your code with more details. I am getting correct output. Also, the values for millis is coming as 1515564000000 , which is different from what you are getting – akshaya pandey Jan 10 '18 at 12:29
  • 1
    Time zone is crucial. You need to specify in which time zone your 11:30 are to be interpreted. – Ole V.V. Jan 10 '18 at 12:30
  • https://www.epochconverter.com gives me: GMT: Wednesday, January 10, 2018 9:30:00 AM. Your time zone: Wednesday, January 10, 2018 10:30:00 AM GMT+01:00. If your 11:30 are at zone offset UTC+02:00, the result agrees with it. – Ole V.V. Jan 10 '18 at 12:32
  • As an aside consider throwing away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends, and adding [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project in order to use `java.time`, the modern Java date and time API. It is so much nicer to work with. – Ole V.V. Jan 10 '18 at 12:33
  • yes - my timezone offset UTC+02:00/ And is it possible - to convert dates without using offsets? – bukka.wh Jan 10 '18 at 12:41
  • @bukka.wh check my answer below. I give you the solution to set the timezone to UTC (without offset) ;) – Valentin Michalak Jan 10 '18 at 12:44

4 Answers4

1

It looks like a timezone problem.

Check in which timezone you are. If you are in UTC +02:00 the result is good !

If you want to set manually the timezone to match with UTC (without offset), check this stack overflow question: How to set time zone of a java.util.Date?

I hope this help you :)

Valentin Michalak
  • 2,089
  • 1
  • 14
  • 27
1

One more example of where java.time, the modern Java date and time API, excels. It forces you to specify zone offset or time zone for operations like this one, which solves the problem:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-M-d H:mm");
    String dateString = "2018-1-10 11:30";
    long epochMillis = LocalDateTime.parse(dateString, dtf)
            .atOffset(ZoneOffset.UTC)
            .toInstant()
            .toEpochMilli();

The result is 1515583800000 (maybe more readable as 1 515 583 800 000). Remove the last three zeros to convert to seconds and enter them on, for example, http://www.onlineconversion.com/unix_time.htm. The result is:

Wed, 10 Jan 2018 11:30:00 GMT

The GMT in the end confirms that the time is at the expected offset (for now we can consider GMT and UTC equivalent).

Question: Can I use java.time on Android?

You certainly can (I haven’t got the experience myself, though).

  • For the general Android market, use the Android edition of ThreeTen Backport (mentioned below). It’s called ThreeTenABP. Then import org.threeten.bp.format.DateTimeFormatter and org.threeten.bp.LocalDateTime.
  • For a newer Android device with Java 8 it should work out-of-the-box if you import java.time.format.DateTimeFormatter and java.time.LocalDateTime.

And on non-Android Java?

  • In Java 8 and later the new API comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).

Links

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thank you, but - DateTimeFormatter.ofPattern is not very suitable for me because my min API level is 15((( – bukka.wh Jan 10 '18 at 13:15
  • 2
    I am not quite sure, isn’t level 15 enough that you can use [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP), the backport of the modern API (also known as JSR-310) to Android? More here: [How to use ThreeTenABP in Android Project](https://stackoverflow.com/questions/38922754/how-to-use-threetenabp-in-android-project). In that case, make sure you import from `org.threeten.bp`. – Ole V.V. Jan 10 '18 at 13:35
  • Adding ThreeTen-Backport & ThreetenABP libraries to your project is absolutely worth the bother. The legacy date-time classes are a wretched mess, and should be avoided. – Basil Bourque Jan 11 '18 at 05:10
0

By parsing the string it is assumed that the given time is based on your local time, whereas Date is in UTC. SimpleDateFormat does the conversion for you using your pcs time zone if you don't specify it.

https://docs.oracle.com/javase/7/docs/api/java/text/DateFormat.html#setTimeZone(java.util.TimeZone)

https://docs.oracle.com/javase/8/docs/api/java/util/Date.html

To convert it back you should use new Date (millis) This considers your timezone as well and your result should be correct.

Madhonix
  • 26
  • 1
  • 2
0

I think you are aware now that it is a timezone issue, but I figured I'll supply you a helper class that I wrote and use in most my applications as client server applications always have to take in account timezones and should be storing GMT on the server.

Hope this helps, you can copy and paste this class and handle a lot of your date formatting and timezone issues to and from the server.

public class DateHelper {

/*///////////////////////////////////////////////////////////////
// MEMBERS
*////////////////////////////////////////////////////////////////
public static SimpleDateFormat MY_APPS_CUSTOM_FORMATTER;
public final static String MMMM_dd = "MMMM, dd";
public final static String MMM_dd_yyyy = "MMM dd yyyy";
public final static String MMSlashddSlashyy = "MM/dd/yy";
public final static String MMSlashddSlashyy_hhColonmm_a = "MM/dd/yy hh:mm a";
public final static Calendar mCalender = Calendar.getInstance();
public final static TimeZone mLocalTimezone = mCalender.getTimeZone();


/*///////////////////////////////////////////////////////////////
// PROPERTIES
*////////////////////////////////////////////////////////////////
public synchronized static SimpleDateFormat getMyAppsDateCustomFormatter(boolean toServer, String format){
    MY_APPS_CUSTOM_FORMATTER = new SimpleDateFormat(format);
    if(toServer){
        MY_APPS_CUSTOM_FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));

    }else{
        MY_APPS_CUSTOM_FORMATTER.setTimeZone(mLocalTimezone);

    }

    return MY_APPS_CUSTOM_FORMATTER;

}


/*///////////////////////////////////////////////////////////////
// EXTRA HELPER METHODS
*////////////////////////////////////////////////////////////////
public static String getNowLocalTime(String formatToReturn){
    return getMyAppsDateCustomFormatter(false, formatToReturn).format(new Date());

}


/*///////////////////////////////////////////////////////////////
// FROM SERVER FORMATTING
*////////////////////////////////////////////////////////////////
public static String getLocalDateStringFromGMTLong(long gmtTimestamp, String formatToReturn){
    return getMyAppsDateCustomFormatter(false, formatToReturn).format(new Date(gmtTimestamp * 1000));

}
public static Date getDateFromLocalFormattedString(String date, String formatToUse, boolean toServer) throws Exception{
    Date parsedDate = null;

    try {
        parsedDate = getMyAppsDateCustomFormatter(toServer, formatToUse).parse(date);

    } catch (ParseException e) { //developer error, do NOT localize
        throw new Exception(Globals.DEV_ERROR_STRINGS.INVALID_DATE_SUPPLIED_FOR_DEFAULT_FORMATTER);

    }

    return parsedDate;
}
public static String getFormattedStringFromLocalDate(Date date, String formatToUse) throws Exception{
    return getMyAppsDateCustomFormatter(false, formatToUse).format(date);

}


/*///////////////////////////////////////////////////////////////
// TO SERVER FORMATTING
*////////////////////////////////////////////////////////////////
public static long getGMTLongFromLocalDate(Date date){
    //Get Local Timezone
    TimeZone tz = TimeZone.getDefault();
    //Create new date for offset to GMT
    Date ret = new Date(date.getTime() - tz.getRawOffset() );

    // if we are now in DST, back off by the delta.  Note that we are checking the GMT date, this is the KEY.
    if ( tz.inDaylightTime( ret )){
        Date dstDate = new Date( ret.getTime() - tz.getDSTSavings() );

        // check to make sure we have not crossed back into standard time
        // this happens when we are on the cusp of DST (7pm the day before the change for PDT)
        if ( tz.inDaylightTime( dstDate )){
            ret = dstDate;

        }

    }

    return ret.getTime();

}
public static long getGMTLongFromLocalDateString(String date, String formatUsed) throws Exception {
    Date passedDate = getDateFromLocalFormattedString(date, formatUsed, true);
    //Get Local Timezone
    TimeZone tz = TimeZone.getDefault();
    //Create new date for offset to GMT
    Date ret = new Date(passedDate.getTime() - tz.getRawOffset() );

    // if we are now in DST, back off by the delta.  Note that we are checking the GMT date, this is the KEY.
    if ( tz.inDaylightTime( ret )){
        Date dstDate = new Date( ret.getTime() - tz.getDSTSavings() );

        // check to make sure we have not crossed back into standard time
        // this happens when we are on the cusp of DST (7pm the day before the change for PDT)
        if ( tz.inDaylightTime( dstDate )){
            ret = dstDate;

        }

    }

    return ret.getTime() / 1000;

}
public static long getNowGMTTime(){


       return getGMTLongFromLocalDate(new Date());

    }

}
Sam
  • 5,342
  • 1
  • 23
  • 39