5

I am very new to Java and coding in general - I have some code which returns a timestamp in the following format yyyy.MM.dd HH:mm:ss:ms which is shown below:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");

This returns:

2017.07.19 11:42:30:423

Is there a way to edit the "SimpleDateFormat formatter" code above to return the date/time as an epoch timestamp that includes milliseconds so that the value returned is formatted as per the below?

1500464550423

I'm hoping that I can amend the ("yyyy.MM.dd HH:mm:ss:sss") part of the SimpleDateFormat formatter code to do this.

halfer
  • 19,824
  • 17
  • 99
  • 186
Mark Smith
  • 757
  • 2
  • 16
  • 27
  • It’s not 100 % clear to me. Are you saying that you are receiving `2017.07.19 11:42:30:423` as a string and you want to convert it to number of milliseconds since the epoch? – Ole V.V. Jul 19 '17 at 12:16
  • 1
    Thats right - the timestamp value is being used later in the code and is used to make a string of variables - the part that returns the timestamp is "+formatter.format(time)" without the quotes - this is why I was hoping that I could amend the ("yyyy.MM.dd HH:mm:ss:sss") part of the code to return the epoc in milliseconds as I am not sure how to amend the code to use the other methods people have suggested here... – Mark Smith Jul 19 '17 at 12:21

6 Answers6

6

You have a simple error in the use of case in your format pattern string (these are case sensitive). And worse, you are using the old and troublesome SimpleDateFormat class. One of the many problems with it is it’s not telling you what the problem is.

So I recommend you use the modern Java date and time API instead (I am deliberately using your format pattern string verbatim):

    String receivedTimetamp = "2017.07.19 11:42:30:423";
    DateTimeFormatter parseFormatter
            = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:sss");
    LocalDateTime dateTime = LocalDateTime.parse(receivedTimetamp, parseFormatter);
    System.out.println(dateTime);

This code throws an IllegalArgumentException: Too many pattern letters: s. I hope this calls your awareness to the fact that you are using two s’s for seconds and three s’s for fraction of second. If it still isn’t clear, the documentation will tell you that lowercase s is correct for seconds, while you need uppercase S for the fraction. Let’s repair:

    DateTimeFormatter parseFormatter
            = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");

Now the code prints 2017-07-19T11:42:30.423, so we have managed to parse the string correctly.

To convert to milliseconds we are still missing a crucial piece of information: in what time zone should the timestamp be interpreted? I think the two obvious guesses are UTC and your local time zone (which I don’t know). Try UTC:

    System.out.println(dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli());

This produces 1500464550423, which is the number you asked for. I suppose we’re done.

If you wanted your JVM’s time zone setting instead, use .atZone(ZoneId.systemDefault()) instead of .atOffset(ZoneOffset.UTC), but beware that the setting may be altered by other software running in the same JVM, so this is fragile.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thank you for the great explanation - I am struggling to understand how I can integrate this into my code as I am very new to Java, however I don't doubt what you have suggested is correct - I will need to spend some time to work this out – Mark Smith Jul 19 '17 at 12:48
2

First of all, check the documentation of SimpleDateFormat. The pattern that corresponds to milliseconds is an uppercase S, while the lowercase s corresponds to seconds. The problem is that SimpleDateFormat usually doesn't complain and try to parse 423 as seconds, adding this amount to your end date (giving an incorrect result).

Anyway, SimpleDateFormat just parses a String to a java.util.Date or formats the Date to a String. If you want the epoch millis value, you must get it from the Date object:

// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
// parse to a date
Date date = formatter.parse(s);
// get epoch millis
long millis = date.getTime();
System.out.println(millis); // 1500475350423

The problem is that SimpleDateFormat uses the system's default timezone, so the final value above (1500475350423) will be equivalent to the specificed date and time in my system's timezone (which can be different from yours - just for the record, my system's default timezone is America/Sao_Paulo). If you want to specify in what timezone this date is, you need to set in the formatter (before calling parse):

// set a timezone to the formatter (using UTC as example)
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

With this, the result for millis will be 1500464550423 (the equivalent to the specificed date and time in UTC).

To do the opposite (create a date from the millis value), you must create a Date object and then pass it to the formatter (also taking care of setting a timezone to the formatter):

// create date from millis
Date date = new Date(1500464550423L);
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// format date
String formatted = formatter.format(date);

Java new date/time API

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.

If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.

If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).

The code below works for both. The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.

As the input String has no timezone information (only date and time), first I parsed it to a LocalDateTime (a class that represents a date and time without timezone). Then I convert this date/time to a specific timezone and get the millis value from it:

// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// as the input string has no timezone information, parse it to a LocalDateTime
LocalDateTime dt = LocalDateTime.parse(s, formatter);

// convert the LocalDateTime to a timezone
ZonedDateTime zdt = dt.atZone(ZoneId.of("Europe/London"));
// get the millis value
long millis = zdt.toInstant().toEpochMilli(); // 1500460950423

The value is now 1500460950423, equivalent to the specified date and time in London timezone.

Note that the API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin). Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.

You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().

You can also use ZoneOffset.UTC constant if you want to use UTC.

To do the opposite, you can get the millis value to create an Instant, convert it to a timezone and pass it to the formatter:

// create Instant from millis value 
Instant instant = Instant.ofEpochMilli(1500460950423L);
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// convert to timezone
ZonedDateTime z = instant.atZone(ZoneId.of("Europe/London"));
// format
String formatted = z.format(formatter);
Community
  • 1
  • 1
1

If you have a java.util.Date then invoking getTime() will return the number of millis since the epoch. For example:

SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");

Date dateToBeFormatted = new Date();

// this will print a datetime literal on the above format
System.out.println(formatter.format(dateToBeFormatted));

// this will print the number of millis since the Java epoch
System.out.println(dateToBeFormatted.getTime());

The key point here is that in order to get the number of millis since the epoch you do not need a SimpleDateFormatter because the number of millis since the epoch is a property of the Date.

glytching
  • 44,936
  • 9
  • 114
  • 120
  • Thanks for your comment - the code I reference above is part of a block of code and I am not sure how I can integrate what you have suggested - ideally I need to understand if/how I can use the "SimpleDateFormat" function to return an epoch timestamp as this would mean that I wont need to re-jig any of the other code as my understanding is limited – Mark Smith Jul 19 '17 at 12:05
  • I've updated the answer to include an example, hope that helps. – glytching Jul 19 '17 at 12:11
  • Thank you glitch - this does help with my understanding! – Mark Smith Jul 19 '17 at 12:14
1

First advice is to move to java8 java.time API instead of learning the broken java.date API

then do:

Instant i = Instant.now();
System.out.println(i.toEpochMilli());

in your case you can do:

LocalDateTime myldt = LocalDateTime.parse("2017-06-14 14:29:04",
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(myldt.toInstant(ZoneOffset.UTC).toEpochMilli());

note that as soon as you play more with the api you will find more ways to achieve the same thing, at the end you will end invoking toEpochMilli

ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • Thanks for your comment - the code I reference above is part of a block of code and I am not sure how I can integrate what you have suggested - ideally I need to understand if/how I can use the "SimpleDateFormat" function to return an epoch timestamp as this would mean that I wont need to re-jig any of the other code as my understanding is limited – Mark Smith Jul 19 '17 at 12:05
  • 1
    yes. instead of using ***SimpleDateFormat*** use the ***DateTimeFormatter*** – ΦXocę 웃 Пepeúpa ツ Jul 19 '17 at 12:06
  • Thank you, I will have a play and see if I can literally exchange "SimpleDateFormat " for "DateTimeFormatter" in the code and use "SSS" as a formatter (?) - I am very new to java so please forgive my ignorance! – Mark Smith Jul 19 '17 at 12:11
  • 1
    you will need java 8 to use that class – ΦXocę 웃 Пepeúpa ツ Jul 19 '17 at 12:13
1
    String strDate = "Jun 13 2003 23:11:52.454 UTC";
    DateTimeFormatter dtf  = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
    ZonedDateTime     zdt  = ZonedDateTime.parse(strDate,dtf);        
    System.out.println(zdt.toInstant().toEpochMilli());  // 1055545912454  
Vilius
  • 21
  • 3
  • 2
    Nice code, but lacking explanation. Code-only answers are seldom helpful, so please explain how you are solving the asker’s issue. – Ole V.V. Jul 19 '17 at 12:09
  • 2
    This thread could be helpful https://stackoverflow.com/questions/6687433/convert-a-date-format-in-epoch – Vilius Jul 19 '17 at 12:12
1

You can try

long time = System.currentTimeMillis();
assembler
  • 3,098
  • 12
  • 43
  • 84