Convert UTC date into milliseconds

27,093

Solution 1

EDIT: I'd missed the "ignoring the time of day" part. It's now present, but near the end...

The simplest approach is probably to use SimpleDateFormat, having set the time zone appropriately:

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));

Date date = format.parse(text);
long millis = date.getTime();

(Setting the time zone is the important bit here, as otherwise it will interpret the value to be in the local time zone.)

Alternatively, if you're doing anything less trivial than this, use Joda Time which is a much better date/time API. In particular, SimpleDateFormat isn't thread-safe whereas DateTimeFormatter is:

// This can be reused freely across threads after construction.
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
    .withLocale(Locale.US)
    .withZoneUTC();

// Option 1
DateTime datetime = formatter.parseDateTime(text);
long millis = dateTime.getMillis();

// Option 2, more direct, but harder to diagnose errors
long millis = formatter.parseMillis(text);

Now so far, we've parsed the whole whole caboodle. The easiest way of ignoring the date part is just to round it off - after all, Java doesn't observe leap seconds, so we can just truncate it:

long millisPerDay = 24L * 60L * 60L * 1000L; // Or use TimeUnit
long dayMillis = (millis / millisPerDay) * millisPerDay;

That will "round towards 1970" so if you have a date before 1970 it will round to the end of the day - but I suspect that's unlikely to be a problem.

With the Joda Time version you could just use this instead:

DateTime dateTime = formatter.parseDateTime(text);
long millis = dateTime.toLocalDate().getLocalMillis();

I would personally not go with the idea of just taking a substring. Even though you're not actually interested in preserving the hour/minute/second, I think it's appropriate to parse what you've been given and then throw away information. Aside from anything else, it makes your code fail appropriately with bad data, e.g.

"2012-06-100"

or

"2012-06-14 25:01:25"

indicate problems in whatever's supplying you data, and it's good to spot that rather than to continue blindly just because the first 10 characters are okay.

Solution 2

UPDATE: See the modern solution using java.time classes in the correct Answer by Ole V.V..

Simpler

The answer by Jon Skeet is correct. And he makes a good point about including, rather than truncating, the time-of-day info while parsing.

However, his code could be simplified. Especially so because Joda-Time gained an important new method in the latest versions: withTimeAtStartOfDay. This method supplants all the "midnight"-related classes and methods which are now deprecated.

Specifying a Locale is a good habit, as shown in his code. But in this particular case a Locale is not necessary.

His answer correctly suggests the Joda-Time library, far superior to using java.util.Date, .Calendar, and java.text.SimpleTextFormat. Those classes are notoriously troublesome, and should be avoided. Instead use either Joda-Time or the new java.time package built into Java 8 (inspired by Joda-Time, defined by JSR 310).

First Moment Of The Day

You cannot ignore time-of-day if what you want is a count of milliseconds-since-epoch. I suspect what you want is to change the time to first moment of the day. In UTC, this always means the time 00:00:00.000. But note that in local time zones, the first moment may be a different time because of Daylight Saving Time and possibly other anomalies.

ISO 8601

Your string is nearly in standard ISO 8601 format, but we need to swap a T for the SPACE in the middle. Then we can feed the resulting string directly to Joda-Time as Joda-Time has built-in formatters used by default for standard strings.

Example Code

The following example code assumes the intent of your question is to parse a string as a date-time value in UTC time zone, adjust the time to the first moment of the day, and then convert to number of milliseconds since Unix epoch (beginning of 1970 in UTC).

String inputRaw = "2012-06-14 05:01:25";
String input = inputRaw.replace( " ", "T" );  // Replace SPACE with a 'T'.
DateTime dateTime = new DateTime( input, DateTimeZone.UTC );  // Parse, assuming UTC.
DateTime dateTimeTopOfTheDay = dateTime.withTimeAtStartOfDay();  // Adjust to first moment of the day.
long millisecondsSinceUnixEpoch = dateTimeTopOfTheDay.getMillis();  // Convert to millis. Use a 'long', not an 'int'.

Solution 3

java.time and JDBC 4.2

I am providing the modern answer. These days (and for the last several years) you should use java.time, the modern Java date and time API, for your date and time work. And since JDBC 4.2 you can directly retrieve java.time objects from your database (and also store them into it). A modern JPA implementation (Hibernate at least since Hibernate 5) will be happy to do the same. So forget about SimpleDateFormat, Date and other old classes used in most of the old answers. The mentioned ones are poorly designed, and java.time is so much nicer to work with.

Retrieve proper date-time objects from your database

I also recommend that you don’t retrieve your UTC time as a string from the database. If the datatype in SQL is timestamp with time zone (recommended for UTC times), retrieve an OffsetDateTime. For example:

    PreparedStatement pStmt = yourDatabaseConnection
            .prepareStatement("select utc_time from your_table where id = 7;");
    ResultSet rs = pStmt.executeQuery();
    if (rs.next()) {
        OffsetDateTime utcDateTime = rs.getObject("utc_time", OffsetDateTime.class);
        long millisecondsSinceEpoch = utcDateTime.truncatedTo(ChronoUnit.DAYS)
                .toInstant()
                .toEpochMilli();
        System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);
    }

If the type in SQL is dateTime or timestamp without time zone, we probably need to retrieve a LocalDateTime instead (details depending on your JDBC driver and the time zone of your database session). It goes in the same manner. For converting your LocalDateTime to OffsetDateTime, see the conversion below.

If you need to convert from a string

If you cannot avoid getting your UTC time as a string as in the question, parse it into a LocalDateTime and convert from there. For example:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
    String utcTimeString = "2012-06-14 05:01:25";
    long millisecondsSinceEpoch = LocalDateTime.parse(utcTimeString, formatter)
            .atOffset(ZoneOffset.UTC)
            .toInstant()
            .toEpochMilli();
    System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);

Output:

Milliseconds since the epoch: 1339650085000

Link

Share:
27,093
Tyrick
Author by

Tyrick

Programmer

Updated on July 15, 2022

Comments

  • Tyrick
    Tyrick almost 2 years

    I am not interested in what the current UTC time is in milliseconds, nor do I need to mess with timezones. My original date is already stored as a UTC timestamp.

    I have a date stored in a database in UTC time, "2012-06-14 05:01:25". I am not interested in the datetime, but just the date portion of the it. So, after retrieving the date in Java, and excluding the hours, minutes, and seconds - I am left with "2012-06-14".

    How can I convert this into UTC milliseconds?

  • Vishy
    Vishy over 11 years
    Since OP only wants the date, I would substring(0,10) it first and parse just the date.
  • Jon Skeet
    Jon Skeet over 11 years
    @PeterLawrey: I've argued against the substring call at the end of the post.
  • zeeshan
    zeeshan over 7 years
    Somebody downvoted my answer without giving a reason. This has been an increasing trend on StackOverflow lately and should be discouraged.
  • Ole V.V.
    Ole V.V. about 4 years
    I didn’t downvote. Thinkable reasons for doing so: (1) Your code throws an IllegalArgumentException (when utcDateInString is the string from the question). (2) You are trying to use the Date(String) constructor that had been deprecated for much more than 10 years already when you posted your answer.