How to extract epoch from LocalDate and LocalDateTime?
Asked Answered
F

7

155

How do I extract the epoch value to Long from instances of LocalDateTime or LocalDate? I've tried the following, but it gives me other results:

LocalDateTime time = LocalDateTime.parse("04.02.2014  19:51:01", DateTimeFormatter.ofPattern("dd.MM.yyyy  HH:mm:ss"));
System.out.println(time.getLong(ChronoField.SECOND_OF_DAY)); // gives 71461
System.out.println(time.getLong(ChronoField.EPOCH_DAY)); // gives 16105

What I want is simply the value 1391539861 for the local datetime "04.02.2014 19:51:01". My timezone is Europe/Oslo UTC+1 with daylight saving time.

Frisket answered 10/4, 2014 at 13:52 Comment(3)
Please explain your expected number 1396468261. I get without timezone correction: 1391543461 (see edit in my answer). 57 days difference!Inerasable
@MenoHochschild I've updated my question with timezone info and corrected the actual value from GTM to localtime. Is there an easier way to get the epoch of some LocalDateTime other than manually calculating it?Frisket
Coming back from my pause, see my update.Inerasable
H
213

The classes LocalDate and LocalDateTime do not contain information about the timezone or time offset, and seconds since epoch would be ambigious without this information. However, the objects have several methods to convert them into date/time objects with timezones by passing a ZoneId instance.

LocalDate

LocalDate date = ...;
ZoneId zoneId = ZoneId.systemDefault(); // or: ZoneId.of("Europe/Oslo");
long epoch = date.atStartOfDay(zoneId).toEpochSecond();

LocalDateTime

LocalDateTime time = ...;
ZoneId zoneId = ZoneId.systemDefault(); // or: ZoneId.of("Europe/Oslo");
long epoch = time.atZone(zoneId).toEpochSecond();
Horrid answered 10/4, 2014 at 15:36 Comment(7)
Is it possible to avoid using ZoneId, or use with a customized, constant ZoneId instance (of +0 , meaning GMT) ? I ask this because I want all calculations be normalized to it. Also, how do I do the opposite: convert from epoch time to LocalDate/LocalDateTime (also without ZoneId, or with the GMT one) ?Monk
Never mind. Found it: ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))Monk
A simpler approach is just long epoch = time.toEpochSecond(ZoneOffset.UTC) for UTC cases, or where you already know the timezone, or long epoch = time.toEpochSecond(ZoneId.systemDefault()); if you want to go that route.Fikes
This might sound ridiculous but how about converting back from epoch to LocalDate, LocalTime & LocalDateTimeNigrify
Never mind I think I found it; Instant.ofEpochMilli(responseTime).atZone(ZoneId.systemDefault()).toLocalTime()Nigrify
I just want to note that ZonedDateTime also exists and is much more useful in my experience since it can parse the Z (or other time zones) at the end of strings into the correct time zoneEncumbrance
Do not use ZoneId.systemDefault ... be explicit about what you want, especially since the timezone is gone in the LocalDateTime.Houdon
M
40

'Millis since unix epoch' represents an instant, so you should use the Instant class:

private long toEpochMilli(LocalDateTime localDateTime)
{
  return localDateTime.atZone(ZoneId.systemDefault())
    .toInstant().toEpochMilli();
}
Mallard answered 21/11, 2016 at 11:20 Comment(4)
it is incorrect to use ZoneId.systemDefault() because unix epoch refers to UTCBrisesoleil
@Brisesoleil Are you still agree what you once thought? Did the API designer failed to define ZonedDateTime#toInstant?Roveover
@JinKwon I was merely refering to the fact that ZoneId.systemDefault() may not necessarily return the timezon the OP was expecting: My timezone is Europe/Oslo. So they should use ZoneId.of("Europe/Oslo") instead as suggested in other answers. As for ZonedDateTime.toInstant() - its javadoc clearly explains the implementation: This returns an Instant representing the same point on the time-line as this date-time. The calculation combines the local date-time and offset.Brisesoleil
ZoneId.systemDefault() seems to return UTC, so systemDefault should be fineButterfingers
T
14

The conversion you need requires the offset from UTC/Greewich, or a time-zone.

If you have an offset, there is a dedicated method on LocalDateTime for this task:

long epochSec = localDateTime.toEpochSecond(zoneOffset);

If you only have a ZoneId then you can obtain the ZoneOffset from the ZoneId:

ZoneOffset zoneOffset = ZoneId.of("Europe/Oslo").getRules().getOffset(ldt);

But you may find conversion via ZonedDateTime simpler:

long epochSec = ldt.atZone(zoneId).toEpochSecond();
Toul answered 11/4, 2014 at 14:38 Comment(1)
If you only care about UTC, there is long epochSec = localDateTime.toEpochSecond(ZoneOffset.UTC);Leclair
C
2

This is one way without using a time zone:

LocalDateTime now = LocalDateTime.now();
long epoch = (now.getLong(ChronoField.EPOCH_DAY) * 86400000) + now.getLong(ChronoField.MILLI_OF_DAY);
Cudlip answered 13/11, 2019 at 15:16 Comment(0)
P
1

Extracting two good answers found in comments on this post,

If you only care about UTC (Coordinated Universal Time), there is

final long epoch = localDateTime.toEpochSecond(ZoneOffset.UTC);

or where you already know the timezone

final long epoch = localDateTime.toEpochSecond(ZoneId.systemDefault());

More information from https://docs.oracle.com/javase/8/docs/api/java/time/chrono/ChronoLocalDateTime.html#toEpochSecond-java.time.ZoneOffset-

toEpochSecond

default long toEpochSecond(ZoneOffset offset)

Converts this date-time to the number of seconds from the epoch of 1970-01-01T00:00:00Z.

This combines this local date-time and the specified offset to calculate the epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z. Instants on the time-line after the epoch are positive, earlier are negative.

This default implementation calculates from the epoch-day of the date and the second-of-day of the time.

Parameters:

offset - the offset to use for the conversion, not null

Returns:

the number of seconds from the epoch of 1970-01-01T00:00:00Z

Puttyroot answered 30/11, 2022 at 17:59 Comment(0)
I
0

Look at this method to see which fields are supported. You will find for LocalDateTime:

•NANO_OF_SECOND 
•NANO_OF_DAY 
•MICRO_OF_SECOND 
•MICRO_OF_DAY 
•MILLI_OF_SECOND 
•MILLI_OF_DAY 
•SECOND_OF_MINUTE 
•SECOND_OF_DAY 
•MINUTE_OF_HOUR 
•MINUTE_OF_DAY 
•HOUR_OF_AMPM 
•CLOCK_HOUR_OF_AMPM 
•HOUR_OF_DAY 
•CLOCK_HOUR_OF_DAY 
•AMPM_OF_DAY 
•DAY_OF_WEEK 
•ALIGNED_DAY_OF_WEEK_IN_MONTH 
•ALIGNED_DAY_OF_WEEK_IN_YEAR 
•DAY_OF_MONTH 
•DAY_OF_YEAR 
•EPOCH_DAY 
•ALIGNED_WEEK_OF_MONTH 
•ALIGNED_WEEK_OF_YEAR 
•MONTH_OF_YEAR 
•PROLEPTIC_MONTH 
•YEAR_OF_ERA 
•YEAR 
•ERA 

The field INSTANT_SECONDS is - of course - not supported because a LocalDateTime cannot refer to any absolute (global) timestamp. But what is helpful is the field EPOCH_DAY which counts the elapsed days since 1970-01-01. Similar thoughts are valid for the type LocalDate (with even less supported fields).

If you intend to get the non-existing millis-since-unix-epoch field you also need the timezone for converting from a local to a global type. This conversion can be done much simpler, see other SO-posts.

Coming back to your question and the numbers in your code:

The result 1605 is correct
  => (2014 - 1970) * 365 + 11 (leap days) + 31 (in january 2014) + 3 (in february 2014)
The result 71461 is also correct => 19 * 3600 + 51 * 60 + 1

16105L * 86400 + 71461 = 1391543461 seconds since 1970-01-01T00:00:00 (attention, no timezone) Then you can subtract the timezone offset (watch out for possible multiplication by 1000 if in milliseconds).

UPDATE after given timezone info:

local time = 1391543461 secs
offset = 3600 secs (Europe/Oslo, winter time in february)
utc = 1391543461 - 3600 = 1391539861

As JSR-310-code with two equivalent approaches:

long secondsSinceUnixEpoch1 =
  LocalDateTime.of(2014, 2, 4, 19, 51, 1).atZone(ZoneId.of("Europe/Oslo")).toEpochSecond();

long secondsSinceUnixEpoch2 =
  LocalDate
    .of(2014, 2, 4)
    .atTime(19, 51, 1)
    .atZone(ZoneId.of("Europe/Oslo"))
    .toEpochSecond();
Inerasable answered 10/4, 2014 at 14:9 Comment(1)
Thanks for the explanation. Got the right result by using LocalDateTime time = LocalDateTime.parse("04.02.2014 19:51:01", DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")); and then Long epoch = time.atZone(ZoneId.of("Europe/Oslo")).toEpochSecond();.Frisket
K
0

Convert from human readable date to epoch:

long epoch = new java.text.SimpleDateFormat("MM/dd/yyyyHH:mm:ss").parse("01/01/1970 01:00:00").getTime() / 1000;

Convert from epoch to human readable date:

String date = new java.text.SimpleDateFormat("MM/dd/yyyyHH:mm:ss").format(new java.util.Date (epoch*1000));

For other language converter: https://www.epochconverter.com

Kepi answered 13/4, 2018 at 3:34 Comment(1)
This doesn't answer the question.Jaquith

© 2022 - 2024 — McMap. All rights reserved.