0

I'm getting my object's createdAt timestamp back from parse.com as 2014-08-01T01:17:56.751Z. I have a class that converts it to relative time.

public static String timeAgo(String time){
  PrettyTime mPtime = new PrettyTime();

  long timeAgo = timeStringtoMilis(time);

  return mPtime.format( new Date( timeAgo ) );
}

public static long timeStringtoMilis(String time) {
  long milis = 0;

  try {
    SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date   = sd.parse(time);
    milis       = date.getTime();
  } catch (Exception e) {
    e.printStackTrace();
  }

  return milis;
}

The problem is that this parses the date wrongly. Right now the result says 4 decades ago and this very wrong. What I'm I doing wrong?

Mr Smith
  • 331
  • 1
  • 4
  • 14
  • The Joda-Time library directly converts such ISO 8601 strings to `DateTime` objects. – Basil Bourque Oct 26 '14 at 17:55
  • possible duplicate of [Converting ISO 8601-compliant String to java.util.Date](http://stackoverflow.com/questions/2201925/converting-iso-8601-compliant-string-to-java-util-date) – Basil Bourque Oct 26 '14 at 17:58

2 Answers2

3

Your current date format "yyyy-MM-dd HH:mm:ss" does not work for the given example 2014-08-01T01:17:56.751Z. The format is missing the characters T and Z and the milliseconds. Change it to:

new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

to fix it.

Also check the examples in the JavaDoc of SimpleDateFormat, because it also shows the correct date format for your example: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html.

Tom
  • 16,842
  • 17
  • 45
  • 54
3

Expanding @Tom's answer:

The problem

When hardcoding 'Z', you assume that all dates were saved as UTC - which doesn't necessarily have to be the case.

The problem is that SimpleDateFormat does not recognize the literal 'Z'as an alias for UTC's '-0000' offset (For whatever reason, since it claims to be ISO-8601 compliant).

So you can't do

new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

since this wrongly assumes all dates will always be written as in UTC, but you can't do

new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

either, since this would not be able to parse the date when the literal 'Z' occurs.

Solution 1: Use javax.xml.bind.DatatypeConverter

This datatype converter actually is ISO8601 compliant and can be used as easy as

import javax.xml.bind.DatatypeConverter;

public Long isoToMillis(String dateString){
  Calendar calendar = DatatypeConverter.parseDateTime(dateString);
  return calendar.getTime().getTime();
}

If you use JAXB anyway, that would be the way to go.

Solution 2: Use conditional formats

final static String ZULUFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
final static String OFFSETFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

/* This is a utility method, so you want the calling method
 * to be informed that something is wrong with the input format
 */
public static Long isoToMillis(String dateString) throws ParseException{

  /* It is the default, so we should use it by default */
  String formatString = ZULUFORMAT;

  if(! dateString.endsWith("Z") ) {
    formatString = OFFSETFORMAT;
  }
  
  SimpleDateFormat sd = new SimpleDateFormat(formatString);
  return sd.parse(dateString).getTime();

}

If you don't already use JAXB, you might want to put this method into a utility class.

Community
  • 1
  • 1
Markus W Mahlberg
  • 19,711
  • 6
  • 65
  • 89