Easy way to convert Java 8's LocalDateTime to Joda's LocalDateTime - java

Is there any easy way to convert Java 8's LocalDateTime to Joda's LocalDateTime?
One of the ways is to convert it to String and then create Joda's LocalDateTime from that String.

Convert through epoch millis (essentially a java.util.Date()):
java.time.LocalDateTime java8LocalDateTime = java.time.LocalDateTime.now();
// Separate steps, showing intermediate types
java.time.ZonedDateTime java8ZonedDateTime = java8LocalDateTime.atZone(ZoneId.systemDefault());
java.time.Instant java8Instant = java8ZonedDateTime.toInstant();
long millis = java8Instant.toEpochMilli();
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime(millis);
// Chained
org.joda.time.LocalDateTime jodaLocalDateTime =
new org.joda.time.LocalDateTime(
java8LocalDateTime.atZone(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
);
// One-liner
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime(java8LocalDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
Single line, but long, so "easy"? It's all relative.

Both localDate types consist of (year, month, date), so just copy those values:
public static org.joda.time.LocalDate toJoda(java.time.LocalDate input) {
return new org.joda.time.LocalDate(input.getYear(),
input.getMonthValue(),
input.getDayOfMonth());
}

A slightly shorter method:
If you can get away with using a ZoneOffset instead of a ZoneId then the code may be shortened slightly.
java.time.LocalDateTime java8LDT = java.time.LocalDateTime.now();
org.joda.time.LocalDateTime jodaLDT = new org.joda.time.LocalDateTime(
java8LDT.toInstant(ZoneOffset.UTC).toEpochMilli()
);
The call to the atZone() method can be dropped by providing the timezone offset to the toInstant() method.
More detailed explanation
Just to clarify, the step I have removed is with creating the intermediate ZonedDateTime object. This part of the sequence is still with the Java 8 API before anything to do with Joda.
The process of conversion first involves converting the Java 8 LocalDateTime to the number of millis since the epoch. This can be achieved in a couple of different ways. For example in Andreas' answer:
LocalDateTime ldt = LocalDateTime.now();
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Instant instant = zdt.toInstant();
long millis = instant.toEpochMilli();
Or my alternative:
LocalDateTime ldt = LocalDateTime.now();
Instant instant = ldt.toInstant(ZoneOffset.UTC);
long millis = instant.toEpochMilli();
The difference is that I am skipping creating a Java 8 ZonedDateTime and instead passing the timezone offset to the toInstant() method.
From this point on the two answers are the same.
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime(millis);
Caveats
This works well when converting using a consistent offset where no daylight savings changes apply. For example UTC is always +0:00. If you need to convert to a local timezone where the offset can change then you'll need the slightly longer answer using atZone()
A LocalDateTime with both API's (Java 8 and Joda) is a DateTime without a timezone. However, if you have the number of millis since the epoch then you need an offset to derive a date and time. The Java 8 API requires either an offset or timezone to be explicitly passed in, whereas the Joda API will use the system default if none is passed.
A more precise way to construct the Joda LocalDateTime would be:
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime(millis, DateTimeZone.UTC);
This will only use the timezone during construction to get the correct date and time. Once the constructor has completed the timezone will not form part of the object since LocalDateTime has no timezone.

Related

How to parse JSR-310 date to Instant?

I'm trying to parse lets say "2020-01-12+01:00" with JSR-310 time.
I read it via DateTimeFormatter.ofPattern("yyyy-MM-ddVV"), however now if I want to transform that into a Instant via Instant.from(DateTimeFormatter.ofPattern("yyyy-MM-ddVV").parse("..."), it throws where it complains that time is null.
Which granted it is, but, I'd like to get Instant from that, i.e. epochMillis, so I can serialize the long into a database.
Is there a way around it? Basically I'd like to extend the "2020-01-12+01:00" to "2020-01-12T00:00.000+01:00" and parse that to Instant as usual
You need to use DateTimeFormatterBuilder, specifying ISO_DATE format and a default time-of-day (midnight1):
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_DATE)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter();
Instant instant = Instant.from(formatter.parse("2020-01-12+01:00"));
System.out.println(instant);
1) The ChronoField can be any time-of-day field, i.e. HOUR_OF_DAY, CLOCK_HOUR_OF_DAY, MINUTE_OF_DAY, SECOND_OF_DAY, MILLI_OF_DAY, MICRO_OF_DAY, or NANO_OF_DAY.
Output
2020-01-11T23:00:00Z
If you want to retain the time zone offset, you need to use OffsetDateTime (or ZonedDateTime) instead of Instant:
OffsetDateTime dateTime = OffsetDateTime.parse("2020-01-12+01:00", formatter);
System.out.println(dateTime);
System.out.println(dateTime.format(DateTimeFormatter.ISO_DATE));
Output (from both OffsetDateTime and ZonedDateTime)
2020-01-12T00:00+01:00
2020-01-12+01:00
You can use LocalDate.parse(dateString, formatter) using the Formatter you've made above to give you a LocalDate instance.
LocalDate can then give you a LocalDateTime at any time in that day, but (for example) you can get the start of day from it.
LocalDateTime has a toInstant method to give you an Instant.
Instant has a toEpochMilli method to get your long.
It’s easy enough when you know how. The formatter we need is built in. There’s a complication in the fact that there isn’t a type to parse the string into, no OffsetDate. I present two options for tackling this.
String s = "2020-01-12+01:00";
TemporalAccessor parsed = DateTimeFormatter.ISO_OFFSET_DATE.parse(s);
LocalDate date = LocalDate.from(parsed);
ZoneOffset offset = ZoneOffset.from(parsed);
Instant result = date.atStartOfDay(offset).toInstant();
System.out.println(result);
Output from this snippet is:
2020-01-11T23:00:00Z
We seldom need to use the TemporalAccessor interface directly, and it’s considered low-level. It also isn’t the only way to go here. The other good option is to define a default time of day so we can parse directly into an Instant:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_OFFSET_DATE)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter();
Instant result = formatter.parse(s, Instant::from);
The result is the same as before.

How to convert LocalDateTime to com.google.protobuf.Timestamp?

I have an instance of LocalDateTime, which I get from the repository layer, and I need to convert it to a Timestamp (Protocol Buffer) instance.
I have used to following approach for the conversion:
LocalDateTime localDateTime = LocalDateTime.now();//this can be any date
Instant instant = localDateTime.toInstant(ZoneOffset.UTC);
Timestamp timestamp = Timestamp.newBuilder()
.setSeconds(instant.getEpochSecond())
.setNanos(instant.getNano())
.build();
Is the ZoneOffset used here, to convert localDateTime to an instance of Instant, correct?
I have used the UTC offset because the comment on the "seconds" attribute in the Timestamp class says the following:
Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive
Have I used the correct ZoneOffset and is my conversion approach correct?
In general, no, your approach is not correct. The reason is that a LocalDateTime does not have an associated timezone, so it is ambiguous by nature. To convert it to an actual timestamp (an absolute point in time, independent of timezones), you need to know what timezone it was measured in.
By calling localDateTime.toInstant(ZoneOffset.UTC), you are assuming that your localDateTime was actually measured in the UTC timezone. Instead, you should be using the timezone that the LocalDateTime is stored in. If you don't know, then your input data is inherently ambiguous and you'll need to fix that first.
Note that this has nothing to do with the fact that the Unix epoch is usually specified in UTC. We might as well say that the Unix epoch is 1970-01-01T08:00:00+08:00, and it would be the same instant in time.
The rest of it seems correct to me.
Here's a routine that pulls together the comments from the question and actual answer to this question:
protected Timestamp convertLocalDateTimeToGoogleTimestamp(LocalDateTime localDateTime) {
Instant instant = localDateTime.toInstant(ZoneOffset.UTC);
Timestamp result = Timestamp.newBuilder()
.setSeconds(instant.getEpochSecond())
.setNanos(instant.getNano())
.build();
return result;
}
LocalDateTime localDateTime = LocalDateTime.now();//this can be any date
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Timestamp timestamp = Timestamp.newBuilder()
.setSeconds(instant.getEpochSecond())
.setNanos(instant.getNano())
.build();

Java - SimpleDateFormat formatter to return epoch time with milliseconds [duplicate]

This question already has answers here:
Convert a date format in epoch
(6 answers)
Closed 5 years ago.
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.
Any help or advice is much appreciated.
Thanks
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.
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);
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
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
You can try
long time = System.currentTimeMillis();
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.

java code to convert UTC time to local datetime with an offset compare

We receive datetime elements relative to the UTC time like 2004-04-12T13:20:00Z.
And we would like to output the datetime in the local datetime, that is expressed with an offset relative to the UTC time like 2004-04-12T12:20:00-01:00.
With the Java 8 date and time classes this is straightforward. Only catch is, we need to go through ZoneDateTime if we want to pick om the computer’s default time zone, and then on to OffsetDateTime to get the output format you requested (another option would be formatting the date and time using a specific DateTimeFormatter; now I am relying on OffsetDateTime.toString()).
String utcTime = "2004-04-12T13:20:00Z";
OffsetDateTime dateTimeWithOffset = Instant.parse(utcTime).atZone(ZoneId.systemDefault()).toOffsetDateTime();
System.out.println(dateTimeWithOffset);
On my computer the above prints
2004-04-12T14:20+01:00
In the answer and the code I have on purpose avoided the term “local date-time” that you used in the question. This is to avoid confusion with the class LocalDateTime, which is used for a date and time without any time zone or offset information.
Use ZonedDateTime from the java 8 API.
ZonedDateTime.ofInstant(Instant.parse("2004-04-12T13:20:00Z"), ZoneId.of("CET"))
it will give you 2004-04-12T15:20+02:00[CET]
Hope the below solution works.
String date_s = "2004-04-12T13:20:00Z";
SimpleDateFormat dt = new SimpleDateFormat("yyyyy-MM-dd'T'HH:mm:ss'Z'");
Date date = dt.parse(date_s);
Calendar cal = Calendar.getInstance(); // creates calendar
cal.setTime(date); // sets calendar time/date
cal.add(Calendar.HOUR, 1); // adds one hour (do any offset that you want here)
String out = dt.format(cal.getTime());
System.out.println(out.substring(1));
Using the Java 8 JDK, you can use the java.time.Instant class to refer to UTC time. and the folloowing classes for the offset times :
java.time.LocalDate
java.time.LocalTime
java.time.LocalDateTime
Example :
Instant instant = Instant.now();
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.ofOffset("MyZoneId", ZoneOffset.ofHours(3)));

How to create Java time instant from pattern?

Consider a code:
TemporalAccessor date = DateTimeFormatter.ofPattern("yyyy-MM-dd").parse("9999-12-31");
Instant.from(date);
The last line throws an exception:
Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 9999-12-31 of type java.time.format.Parsed
How to create Instant from yyyy-MM-dd pattern?
The string "9999-12-31" only contains information about a date. It does not contain any information about the time-of-day or offset. As such, there is insufficient information to create an Instant. (Other date and time libraries are more lenient, but java.time avoids defaulting these values)
Your first choice is to use a LocalDate instead of an `Instant:
LocalDate date = LocalDate.parse("9999-12-31");
Your second choice is to post process the date to convert it to an instant, which requires a time-zone, here chosen to be Paris:
LocalDate date = LocalDate.parse("9999-12-31");
Instant instant = date.atStartOfDay(ZoneId.of("Europe/Paris")).toInstant();
Your third choice is to add the time-zone to the formatter, and default the time-of-day:
static final DateTimeFormatter FMT = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd")
.parseDefaulting(ChronoField.NANO_OF_DAY, 0)
.toFormatter()
.withZone(ZoneId.of("Europe/Paris"));
Instant instant = FMT.parse("9999-31-12", Instant::from);
(If this doesn't work, ensure you have the latest JDK 8 release as a bug was fixed in this area).
It is worth noting that none of these possibilities use TemporalAccessor directly, because that type is a low-level framework interface, not one for most application developers to use.
The problem isn't the fact that you are using the year 9999. The Instant.MAX field evaluates to the timestamp 1000000000-12-31T23:59:59.999999999Z, so 9999 as a year is fine.
Dealing with TemporalAccessors instead of the more semantically rich types like LocalDateTime or ZonedDateTime is like using a Map to model an object and its properties instead of writing a class -- you have to assure that the value has the fields (like seconds, nanoseconds, etc) that are expected by something receiving it, rather than depending on formally declared operations in a higher level class to prevent dependencies from going unmet.
In your case it is likely that the temporal accessor contained the parsed date fields it was given, but didn't have a "seconds" field that the Instant needed. It is best to use the more semantically rich types like LocalDateTime in most instances.
Since you only have date fields, you should parse it as a date, then add the time fields before converting it to an Instant. Here is one way, using LocalDate to parse the date:
LocalDate localDate = LocalDate.parse("2016-04-17");
LocalDateTime localDateTime = localDate.atStartOfDay();
Instant instant = localDateTime.toInstant(ZoneOffset.UTC);
Either you are only interested in the date itself (31st of December 9999), in which case the appropriate type would be a LocalDate:
LocalDate date = LocalDate.parse("9999-12-31");
Or you do want an Instant, in which case you need to set a time and time zone, for example, 00:00 in Tokyo:
Instant instant = date.atStartOfDay(ZoneId.of("Asia/Tokyo")).toInstant();
public static void main(String[] args) throws ParseException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("2016-12-31").toInstant());
}
the above code gives the following output:
2016-12-31T00:00:00Z
i have answered this question using features('toInstant' method) of java 8. hope this answers your question...

Categories

Resources