2

I have a date string of 1/1/1970 8:00 AM

The correct millis should be 8 hours * 60 minutes per hour * 60000 milliseconds per minute = 28800000

However, using Date.parse(dateString) returns 50400000

What am I not understanding?

Edit

I originally tried using date.getTime();

Here's my original code:

SimpleDateFormat dateFmt = new SimpleDateFormat("MM/dd/yyyy h:mm a");
dateFmt.setTimeZone(TimeZone.getTimeZone("UTC"));
StringBuilder sb = new StringBuilder();                     
sb.append(month).append("/");
sb.append(day).append("/");
sb.append(year).append(" ");
sb.append(pad(hour)).append(":");
sb.append(pad(minute)).append(" ");;
sb.append(ampm);
Date date = new Date();

date = dateFmt.parse(sb.toString());

date.getTime()
Roy Hinkley
  • 10,111
  • 21
  • 80
  • 120

3 Answers3

7

This is almost certainly the problem:

If no time zone is specified, the local time zone is assumed.

My guess is that you're in a time zone which was at UTC-6 at the Unix epoch, so 8am local time was 2pm UTC.

Then there's the more fundamental problem of you using deprecated methods when there are better alternative (SimpleDateFormat, which allows you to set the time zone) available. Methods are deprecated for a reason. You shouldn't just use deprecated methods regardless, otherwise you'll keep running into things like this.

In fact, you'd be better off using Joda Time if you possibly can - but at least stay away from the deprecated methods in Date.

Sample code:

SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy h:mm aa", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
long millis = format.parse(text).getTime();

You may want to change dd/MM to MM/dd, depending on what format your dates are going to be in - we can't tell from "01/01". Note the explicit setting of both time zone and locale.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • SimpleDateFormat returns a Date object, I want it as a long. Please show me how this can be done using the method that you recommend, thank you! – Roy Hinkley Oct 25 '12 at 18:08
  • +1@Jon Skeet. But you could've tried to tell him where he lived rather than the tz info. 'How did you know?' :-) – mcalex Oct 25 '12 at 18:08
  • @AndroidAddict: `Date.getTime()` perhaps? (And really, it's not hard to read the documentation to get that...) – Jon Skeet Oct 25 '12 at 18:10
  • I have used that method too and still get the same result. Please show me an example. Also, please keep your comments constructive, because it may be you that is not understanding something. – Roy Hinkley Oct 25 '12 at 18:12
  • 1
    @Android Addict: Jon Skeet not understanding something? That's pretty funny. – GriffeyDog Oct 25 '12 at 18:16
  • @AndroidAddict: If you use `SimpleDateFormat` and set the time zone to UTC, then call `getTime()` on the result, you'll get the answer you expect. Honestly. – Jon Skeet Oct 25 '12 at 18:20
  • @John Skeet I understand what my issue was now. I was calculating top of day in milliseconds and adding that to a time value. Since both were corrected for time zone, the result was always off by the doubling of the time zone offset. Should I have added another adjusted time, it would have been off by another tzo offset. Thanks for helping, I wish I could have voted for both but the other answer actually gave me the key. – Roy Hinkley Oct 25 '12 at 21:30
  • @AndroidAddict: You should *never* be adding one date/time to another. The concept make no sense. It's like adding a point to a point. You should only ever add a "duration" (a number of milliseconds) to a date/time - this is like adding a *vector* to a point, which makes more sense. Joda Time separates these concepts more clearly. – Jon Skeet Oct 25 '12 at 21:34
  • @John Skeet essentially that is what I was doing. I always add longs, in this case the 8 hours past 0 (a duration) and the top on the day in milliseconds. Perhaps i did not say it eloquently. – Roy Hinkley Oct 25 '12 at 21:46
  • @AndroidAddict: But you've fetched that value by parsing a date/time string. You *really* shouldn't do that. – Jon Skeet Oct 25 '12 at 21:56
  • @John Skeet I was just trying to create some mock data for testing. Normally that value gets supplied differently. Thanks for pointing out best practices! :^) – Roy Hinkley Oct 25 '12 at 22:06
  • @AndroidAddict: In this case, the *far* better way of providing the data would be to use `TimeUnit`: `TimeUnit.HOURS.toMillis(8)`. – Jon Skeet Oct 25 '12 at 22:07
1

Its because of your local timezone. Use Simple date format with timezone as below to get your desired value against UTC timezone:

    DateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
    String dateS = "1/1/1970 8:00 AM";
    format.setTimeZone(TimeZone.getTimeZone("UTC"));  
    format.setLenient(true);
    Date date = format.parse(dateS);
    System.out.println(date.getTime()); //<-- prints 28800000

or more compact:

    DateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm a");
    format.setTimeZone(TimeZone.getTimeZone("UTC"));  
    Date date = format.parse("1/1/1970 8:00 AM");
    System.out.println(date.getTime());  //<-- prints 28800000
Yogendra Singh
  • 33,927
  • 6
  • 63
  • 73
0

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Also, quoted below is a notice from the home page of Joda-Time:

Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

Solution using java.time, the modern Date-Time API:

You do not need to form the string: You can use LocalDateTime#of to create an instance of LocalDateTime which can be converted into an Instant in order to get the number of milliseconds from the Unix epoch.

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

public class Main {
    public static void main(String[] args) {
        int year = 1970, month = 1, dayOfMonth = 1, hour = 8, minute = 0;
        LocalDateTime ldt = LocalDateTime.of(year, month, dayOfMonth, hour, minute);
        Instant instant = ldt.toInstant(ZoneOffset.UTC);
        System.out.println(instant.toEpochMilli());
    }
}

Output:

28800000

ONLINE DEMO

If you already have a date-time string in the given format:

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M/d/u h:m a", Locale.ENGLISH);
        String strDateTime = "1/1/1970 8:00 AM";
        LocalDateTime ldt = LocalDateTime.parse(strDateTime, dtf);

        Instant instant = ldt.toInstant(ZoneOffset.UTC);
        System.out.println(instant.toEpochMilli());
    }
}

Output:

28800000

ONLINE DEMO

An Instant represents an instantaneous point on the timeline in UTC.

Learn more about the modern Date-Time API from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110