Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does SimpleDateFormat.parse().getTime() return an incorrect (negative) value?

Tags:

java

date

I have a time-stamp of type String and I am trying to convert it to a double (and find the result in seconds) and here is what I have done:

double mytimeStamp = 0;

String timeStamp = new SimpleDateFormat(" mm ss S").format(new Date( ));   

SimpleDateFormat dateFormat = new SimpleDateFormat(" mm ss S");

try {
  mytimeStamp = ((double)dateFormat.parse(timeStamp).getTime())/1000;
} catch (ParseException e1) {
  // TODO Auto-generated catch block
  e1.printStackTrace();
}

System.out.println("timeStamp is: "+ mytimeStamp);

The problem is that I obtain a value such as -2722.515 and I don't know why.

Why is it negative?

Is there something wrong with the code?

When I convert this time-stamp to mm ss S does not match with the real time and this seems to be another problem!

like image 597
user2052015 Avatar asked Feb 21 '13 13:02

user2052015


People also ask

What does SimpleDateFormat return?

Return Value: The method returns Date or time in string format of mm/dd/yyyy.

What is parse in SimpleDateFormat parse do?

The parse() Method of SimpleDateFormat class is used to parse the text from a string to produce the Date. The method parses the text starting at the index given by a start position.

What can I use instead of SimpleDateFormat?

DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.

Is SimpleDateFormat deprecated?

Class SimpleDateFormat. Deprecated. A class for parsing and formatting dates with a given pattern, compatible with the Java 6 API.


2 Answers

It's a time zone discrepancy issue.

Since you only specified the minute and second, the date will be on 1 Jan 1970 00:mm:ss (mm and ss being the minutes and seconds of the current time).

I simplified your example to:

String timeStamp = "00 00 00";
SimpleDateFormat dateFormat = new SimpleDateFormat("HH mm ss");
double hour = dateFormat.parse(timeStamp).getTime()/1000.0/60/60;
System.out.println("hour is: "+ hour);

The hour printed out should be GMT's offset from the local time zone.

The reason for this is:

SimpleDateFormat is locale-sensitive, so dateFormat.parse(timeStamp) will return create a Date object for a given time zone (the default is the local time zone). Then getTime() gets the number of milliseconds from midnight 1 Jan 1970 **GMT**. So the value will be offset by how far the local time zone is from GMT.

How to fix it:

You could fix it by setting the time zone of the dateFormat object before parse is called as follows:

dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
like image 79
Bernhard Barker Avatar answered Sep 27 '22 22:09

Bernhard Barker


--- Actually there's a much better way to do this, but if you want to use dates, skip to the pre-edit answer ---

Dates don't actually do what you want, which appears to be time calculations outside of an actual need to pick times off a real world calendar.

You'd be much better off writing your own class, to avoid all the nasty special handling that Dates must do in order to keep up with the Gregorian Calendar. This special handling includes (but is not limited to) timezone awareness, daylight savings, declared "skipped days", leap seconds, leap years, etc.

public TimeOnly {

   private long timestamp;
   private int millis;
   private int seconds;
   ... etc ...

   public TimeOnly(int hours, int minutes, int seconds, int millis) {
     this.timestamp = millis + seconds * 1000L + minutes * 60000L + hours * 3600000L; 
     this.millis = millis;
     this.seconds = seconds;
     ... etc ...
   }

   private TimeOnly(long timestamp) {
     this.timestamp = timestamp;
     this.millis = timestamp % 1000;
     this.seconds = timestamp % 60000L - this.millis;
     ... etc ...
   }

   public long getTimestamp() {
     return timestamp;
   }

   public int getMillis() {
     return millis;
   }

   public int getSeconds() {
     return seconds;
   }

   ... etc ...
}

public TimeFormatter {

  public TimeFormatter() {

  }

  public String format(Time time) {
    StringBuilder builder = new StringBuilder();
    builder.append(String.valueOf(time.getHours()));
    builder.append(":");
    builder.append(String.valueOf(time.getMinutes()));
    builder.append(":");
    builder.append(String.valueOf(time.getSeconds()));
    builder.append(".");
    if (time.getMillis() < 10) {
      builder.append("00");
    } else if (time.getMillis() < 100) {
      builder.append("0");
    }
    builder.append(time.getMillis());
    return builder.toString();
}

This solution may seem like it is reinventing the wheel, but really it is avoiding the use of an octagon as a wheel. Date's behavior doesn't seem to be what you want, although you could possibly make Date work for some limited range of values.

If you want to get really fancy, you could make the above implement comparable, etc. However, I would advise against on thing. Don't provide update methods after construction, as this forces some pretty nasty recalculations and makes the code harder to maintain. Instead provide methods that return new TimeOnlys in response to the operations you wish to implement.

public TimeOnly addSeconds(int value) {
  int stamp = this.timestamp;
  stamp += value * 60000L;
  if (stamp < timestamp) {
    throw new Excepton("overflow");
  }
  return new TimeOnly(stamp);
}

Also, don't implement what you aren't going to use. Unused code tends to be fertile soil for bugs.

And of course, the stock answer for all "time" things, consider using JodaTime, which differentiates all the different types of time measurement. However, for just a small problem like this, it's akin to using a tank to kill an ant.

--- The pre-edit answer ---

Without a full specification of the time (year, month, day, hour, minute, second, milliseconds) your time value as formatted in the first step will have lots of fields that are not specified. What goes in those fields will likely be garbage.

Then getTime() acts on the entire Date object translating both the valid fields and the garbage into a value, where the garbage may even modify the valid values (96 sec = 1 minute and 36 seconds, as the fields interact).

The best way to go about this is to have all your "time only" Dates initialized to one known day, so when you do comparisons and math operations (is, 3 11 23 > 1 02 10 ?) you get consistent results (yes, 3 11 23 > 1 02 10, because it is actually 2013 02 10 00 03 11 23 > 2013 02 10 00 03 11 23 and not 2013 02 10 00 03 11 23 compared to 2000 02 10 00 03 11 23

When selecting the day to use, avoid days adjacent to Feb 29th, days that are near daylight savings shifts, etc.

like image 27
Edwin Buck Avatar answered Sep 27 '22 23:09

Edwin Buck