This question already has answers here:
Closed 2 years ago.
I want to convert 2018-02-21 15:47:35
UTC to epoch UTC form. How do we do it? I am currently in PST.
SimpleDateFormat df = new SimpleDateFormat("YYYY-MM-DD HH:MM:SS");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
date = df.parse(dateString).getTime();
The code above should return the number of milliseconds since January 1, 1970, 00:00:00 GMT
, but I'm getting an incorrect value.
The only problem with your code is DateFormat
please check.
https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
String dateString = "2018-02-21 15:47:35";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = df.parse(dateString);
long time = date.getTime();
System.out.println(time);
System.out.println(new Date(time));
System.out.println(date);
I'm in PKT so output would differ...
1519228055000
Wed Feb 21 20:47:35 PKT 2018
Wed Feb 21 20:47:35 PKT 2018
Expected: 2018-02-21 15:47:35 UTC is equivalent to 1 519 228 055 000 milliseconds since the epoch of January 1, 1970 at 0:00 UTC.
Observed: Your code in the question gives 1 514 818 800 035. So it’s 4 409 254 965 milliseconds off, a little over 51 days.
The solution:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
date = LocalDateTime.parse("2018-02-21 15:47:35", dtf)
.atOffset(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
This gives the correct 1 519 228 055 000.
What went wrong?
One of the many troublesome traits of SimpleDateFormat
is that with its default settings, if you specify an incorrect format pattern string, it will very often give you an incorrect result and pretend all is well. The modern Java date and time API that I am using in my snippet, is trying somewhat harder to figure out when the pattern doesn’t make sense and tell you it’s wrong somehow. As an example, let’s try your format pattern with the modern DateTimeFormatter
:
final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("YYYY-MM-DD HH:MM:SS");
LocalDateTime.parse(dateString, dtf);
This will throw a java.time.format.DateTimeParseException: Text '2018-02-21 15:47:35' could not be parsed at index 14
. Index 14 is where 47 is in the string, it was supposed to be the minutes. Apparently 47 doesn’t match MM
in the format. If you haven’t figured out yet, check the documentation. It says that uppercase M
is for “month-of-year”. So what the formatter is trying to tell you is there are not 47 months in a year. In the documentation you will also find lowercase m
for “minute-of-hour”. As you correct the case of the letters in the format pattern string, you will receive other exceptions until you end up with either yyyy-MM-dd HH:mm:ss
or uuuu-MM-dd HH:mm:ss
(lowercase yyyy
is year or era while uuuu
is a signed year, both work for years after year 0).
Links
- Oracle tutorial: Date Time explaining how to use
java.time
.
DateTimeFormatter
documentation spelling out the uppercase and lowercase letters of format pattern strings.
Also can be done via java8 time library:
String dateString = "2018-02-21 15:47:35";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
dateTimeFormatter.withZone(ZoneId.of("UTC"));
LocalDateTime parsedDateTime = LocalDateTime.from(dateTimeFormatter.parse(dateString));
ZonedDateTime timeAtYourZone = parsedDateTime.atZone(ZoneId.systemDefault());
System.out.println(timeAtYourZone.toInstant().toEpochMilli());
System.out.println(timeAtYourZone);
Your pattern must be yyyy-MM-dd HH:mm:ss
, as the other answers told you:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
I just want to add some more details.
First of all, take a look at the patterns description in the javadoc: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
Note that a lowercase y is not the same as the uppercase Y (lowercase is the year, while uppercase is the week year - 2 different fields with completely different definitions)
Also note that uppercase D is the day of the year, while the day of the month (which is what you want) is the lowercase d. And uppercase M is the month, while lowercase m is the minute of hour.
And uppercase S is the milliseconds field, while the seconds are represented by lowercase s.
And SimpleDateFormat
's design doesn't help: the class simply tries to parse the string, even if the month field (MM) appears twice in your pattern, while the minutes field doesn't appear (and it's set to a default value of zero - all behind the scenes, without any warning, no indication of error at all).
Conclusion: always read the docs :-)
For Java 8 or higher, consider using the new date API, which is much better because it doesn't have all these behind-the-scenes stuff:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneOffset.UTC);
long epochMilli = Instant.from(fmt.parse("2018-02-21 15:47:35")).toEpochMilli();
This API will also throw an exception if you use a pattern like YYYY-MM-DD HH:MM:SS
, because it will try to parse the minutes value 47 as a month (because uppercase MM will be in the respective position), and 47 is not a valid month.