I have a timestamp encoded as a String—for example, "2012-02-12T09:08:13.123456-0400", coming from an Oracle database.
The only way that I can think of reading this timestamp, is by using Timestamp.valueOf(), and that requires a format of yyyy-[m]m-[d]d hh:mm:ss[.f...]
I am convinced that this is the only way to read time without losing precision because other ways do not support nanosecond precision included in the example above (".123456").
With that in mind, I can simply trim the needed values, to fit the required format. Hence, the original string would be transformed:
Before: "2012-02-12T09:08:13.123456-0400"
After: "2012-02-12 09:08:13.123456"
If I do this, I remove the "-0400" timezone offset. This comes as a red flag to me, until I saw this post. One of the proposed answers states,
I think the correct answer should be java.sql.Timestamp is NOT timezone specific. Timestamp is a composite of java.util.Date and a separate nanoseconds value. There is no timezone information in this class. Thus just as Date this class simply holds the number of milliseconds since January 1, 1970, 00:00:00 GMT + nanos.
To prove to myself that the offset is not needed, I wrote a simple integration test.
Insert this timestamp into the database: "2015-09-08 11:11:12.123457". Read the database using Java, and print out the details. I get "2015-09-08 11:11:12.123457", which is the same value. This happens to be ok, since my JVM and the Oracle DB are running on the same machine.
Is it a fact that a timezone is not factor in java.sql.Timestamp?
Is there a better way to read that entire timestamp, without losing any precision in Java 7?
tl;dr
org.threeten.bp.OffsetDateTime odt =
OffsetDateTime.parse(
"2012-02-12T09:08:13.123456-0400",
org.threeten.bp.format.DateTimeFormatter.ofPattern( "yyyy-MM-dd'T'HH:mm:ssZ" ) // Specify pattern as workaround for Java 8 bug in failing to parse if optional colon is not present.
)
;
Using java.time
Rather than receiving a String from your database, you should retrieve an object, a date-time object, specifically a java.time object.
The java.time classes supplant the troublesome old date-time classes including java.sql.Timestamp. If your JDBC driver supports JDBC 4.2 and later, you can pass and receive java.time objects directly.
Instant
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction). So this is equivalent to java.sql.Timestamp including support for the six digits of microseconds of your input data, so no precision lost per the requirements of your Question.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
instant.toString(): 2012-02-12T13:08:13.123456Z
ZonedDateTime
If you want to see that same moment through the lens of a particular region's wall-clock time, apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/St_Thomas" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2012-02-12T09:08:13.123456-04:00[America/St_Thomas]
OffsetDateTime
As for your direct Question of how to make sense of the string 2012-02-12T09:08:13.123456-0400 as a date-time value, parse as an OffsetDateTime.
A time zone has a name in the format of continent/region, and represents a history of past, present, and future changes to a region’s offset caused by anomalies such as Daylight Saving Time (DST). We have clue as to the time zone with your string, so we use OffsetDateTime rather than ZonedDateTime.
OffsetDateTime odt = OffsetDateTime.parse( "2012-02-12T09:08:13.123456-0400" ) ;
Well, that line of code above should have worked, but in Java 8 there is a small bug in parsing the offset lacking the optional COLON character between the hours and minutes. So -04:00 in Java 8 will parse but not -0400. Bug fixed in Java 9. Your String is indeed compliant with the ISO 8601 standard for date-time formats used by default in the java.time classes. Tip: Generally best to always format your offsets with the colon, and both hours/minutes and with a padding zero – I've seen other protocols and libraries expect only such full format.
Until you move to Java 9, specify the formatting pattern explicitly rather than rely on the implicit default pattern, as a workaround for this bug.
OffsetDateTime odt =
OffsetDateTime.parse(
"2012-02-12T09:08:13.123456-0400",
DateTimeFormatter.ofPattern( "yyyy-MM-dd'T'HH:mm:ssZ" ) // Specify pattern as workaround for Java 8 bug in failing to parse if optional colon is not present.
)
;
Converting
If your JDBC driver is not yet compliant with JDBC 4.2, retrieve a java.sql.Timestamp object, for use only briefly. Immediately convert to java.time using new methods added to the old date-time classes.
java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
Instant instant = ts.toInstant();
Proceed to do your business logic in java.time classes. To send a date-time back to the database convert from Instant to java.sql.Timestamp.
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
Java 6 & 7
In Java 6 & 7, the above concepts still apply, but java.time is not built-in. Use the ThreeTen-Backport library instead. To obtain, see bullets below.
In Java 7, you cannot use JDBC 4.2 features. So we cannot directly access java.time objects from the database through the JDBC driver. As seen above, we must convert briefly into java.sql.Timestamp from Instant. Call the utility methods DateTimeUtils.toInstant(Timestamp sqlTimestamp) & DateTimeUtils.toSqlTimestamp(Instant instant).
java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
Instant instant = DateTimeUtils.toInstant( ts ) ;
…and…
java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( instant ) ;
myPreparedStatement.setTimestamp( … , ts ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
java.sql.Timestamp.valueOf(String) parses the provided time in the current timezone. You can check this by looking at the implementation, which in the end simply calls Timestamp(int year, int month, int date, int hour, int minute, int second, int nano) which calls public Date(int year, int month, int date, int hrs, int min, int sec), which says (emphasis mine):
Allocates a Date object and initializes it so that it represents the instant at the start of the second specified by the year, month, date, hrs, min, and sec arguments, in the local time zone.
It is true that Timestamp doesn't have time zone information (it is just a wrapper with number of seconds since the GMT epoch + nanoseconds), but when loading or storing a Timestamp, JDBC will use the local (default) timezone, unless explicitly declared otherwise.
This means that a Timestamp at 10:00 in your local timezone will end up in the database as an SQL TIMESTAMP at 10:00, and not at - for example - 08:00 if your timezone is 2 hours ahead of GMT; and the reverse when loading.
You are correct that java.sql.Timestamp has no timezone information. Under the covers it is just an instant that is milliseconds since the epoch and the epoch itself is defined with essentially a 0 offset, January 1, 1970, 00:00:00 GMT.
That being said, when working with times, offset always matters and this will lead you into the hell, er, "dynamic world" that is ISO-8601 in Java 8 java.time. The offset matters a lot, unless you are standing in Greenwich, England. If you try to just ignore it you may sometimes guess the day wrong; it is Monday in Greenwich 8 hours earlier than it is Monday in Seattle, for example. I don't think Java8 has a built in DateTimeFormatter for the -0000 variants of ISO-8601, but if you are sure your format is "stable", this would work.
public void java8TimeWTF() {
OffsetDateTime odt = OffsetDateTime.parse(
"2012-02-12T09:08:13.123456-0400",
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.nZ"));
Instant i = odt.toInstant();
System.out.printf("odt: %s, i: %s\n", odt, i);
}
Outputs odt: 2012-02-12T09:08:13.000123456-04:00, i: 2012-02-12T13:08:13.000123456Z
Here's what we had to do to deal with all the variant's of ISO-8601 coming out of our clients
import org.testng.annotations.Test;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import static org.testng.Assert.assertEquals;
public class Java8Time8601 {
private final long EXPECTED_MILLIS = 1493397412000L;
public Instant iso8601ToInstant(String s) {
DateTimeFormatter[] dateTimeFormatters = {
DateTimeFormatter.ISO_INSTANT,
DateTimeFormatter.ISO_OFFSET_DATE_TIME,
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")
};
for (DateTimeFormatter dtf : dateTimeFormatters) {
try {
OffsetDateTime odt = OffsetDateTime.parse(s, dtf);
Instant i = odt.toInstant();
return i;
} catch (DateTimeParseException dtpe) {
;
}
}
throw new IllegalArgumentException(String.format("failed to parse %s", s));
}
#Test
public void testInstantParse8601_Z() throws Exception {
String[] candidates = {
"2017-04-28T16:36:52.000Z",
"2017-04-28T16:36:52.00Z",
"2017-04-28T16:36:52.0Z",
"2017-04-28T16:36:52Z",
"2017-04-28T16:36:52+00:00",
"2017-04-28T16:36:52-00:00",
"2017-04-28T09:36:52-07:00",
"2017-04-28T09:36:52-0700",
};
for (String candidate : candidates) {
Instant i = iso8601ToInstant(candidate);
assertEquals(i.toEpochMilli(), EXPECTED_MILLIS, String.format("failed candidate %s", candidate));
System.out.println(i.toString());
}
}
}
There has got to be a better way.
Related
I have received a string in format "yyyy-MM-dd'T'HH:mm:ssXXX"
e.g. "2020-06-01T11:04:02+02:00"
I want to convert it into "yyyy-MM-dd'T'HH:mm:ss:SSSZ"
e.g "2020-06-01T11:04:02.000+0200"
I don't know the time zone actually. It should take from that last part of string as it is.
I have tried but it is taking my local time and time zone when I convert string to date(i.e IST).
SimpleDateFormat sd1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
//sd1.setTimeZone(TimeZone.getTimeZone("PST"));
Date dt = sd1.parse("2020-06-01T11:04:02+02:00");
SimpleDateFormat sd2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
System.out.println(sd2.format(dt));
Output:
2020-06-01T14:34:02:000+0530
Only date is right, time and timezone has changed.
I know I am doing it wrong, it will be really helpful if someone can tell me how can I do this.
Thanks for the help.
OffsetDateTime
You said:
I have received a string in format "yyyy-MM-dd'T'HH:mm:ssXXX"
e.g. "2020-06-01T11:04:02+02:00"
No need to define a formatting pattern. Your input complies with the ISO 8601 standard.
These standard formats are used by default in the java.time classes when parsing/generating strings.
Your input should be parsed as a OffsetDateTime.
String input = "2020-06-01T11:04:02+02:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;
odt.toString(): 2020-06-01T11:04:02+02:00
Offset-from-UTC versus time zone
You said:
I don't know the time zone actually.
That +02:00 on the end is not a time zone. That text represents a mere offset-from-UTC. An offset is just a number of hours-minutes-seconds, positive or negative. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. A time zone has a name in the format of Continent/Region, such as Europe/Brussels or Africa/Cairo.
You can adjust from a mere offset to a specific time zone. Apply a ZoneId to get a ZonedDateTime.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
zdt.toString(): 2020-06-01T14:34:02+05:30[Asia/Kolkata]
You said:
It should take from that last part of string as it is.
I am not sure what you meant by that. If you parse your input as an OffsetDateTime, that object knows its offset, accessible as a ZoneOffset.
ZoneOffset offset = odt.getOffset() ;
See the code shown in this Answer run live at IdeOne.com.
offset.toString(): +02:00
Formatting strings
You said:
I want to convert it into "yyyy-MM-dd'T'HH:mm:ss:SSSZ"
e.g "2020-06-01T11:04:02.000+0200"
Not sure what you mean here. Do you mean to force the display of milliseconds even if the value is zero? Firstly, you should know that java.time objects have a resolution of nanoseconds for up to nine decimal digits, much finer that the milliseconds shown in 3 digits of a decimal fraction. Secondly, forcing display of fractional second has been covered on Stack Overflow, such as here. Always search Stack Overflow before posting.
Or do you mean displaying the offset without a COLON character as a delimiter between minutes and seconds?
I advise against this. While dropping the COLON is technically allowed by the ISO 8601 standard, I have seen more than one software library or system fail to handle an offset without that delimiter. Ditto for using an offset of hours without the minutes. I advise always using the hours, the minutes, and the delimiter.
If you insist, use DateTimeFormatter with a formatting pattern. Study the Javadoc, keeping mind that the formatting codes are (a) case-sensitive, and (b) sensitive to repeating the character 0, 1, or more times. Here we use xx to get the hours and minutes of an offset without the COLON character delimiting. (Again, I do not recommend that format.)
Code shown in that same IdeOne.com page.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSxx" ) ;
String output = odt.format( f ) ;
output: 2020-06-01T11:04:02.000+0200
Date::toString injects time zone
You said:
I have tried but it is taking my local time and time zone when I convert string to date(i.e IST).
The java.util.Date::toString method tells a lie. While well-intentioned, that method unfortunately applies the JVM’s current default time zone to the Date value as it generates the text. The Date class actually represents a moment in UTC. This is one of many reasons to never use Date. That class nowadays is replaced by java.time.Instant.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The first suggestion I would make to you is to switch from using the Date object to LocalDateTime (java 8+)
Using the new API would work in this way
String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
LocalDateTime dateTime = LocalDateTime.parse(input_date, formatter);
//Then you can set your timezone in this way - remember to replace the values with the proper timezone you want.
ZonedDateTime zonedUTC = dateTime.atZone(ZoneId.of("UTC"));
ZonedDateTime zonedIST = zonedUTC.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
let me know if that works for you
To elaborate the comment and picking up on #Daniel Vilas-Boas answer, you should go for Java8 and I think what you want is something like:
public static void main(String[] args) {
String YOUR_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssXXX";
String YOUR_NEW_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss:SSSZ";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(YOUR_DATE_TIME_PATTERN);
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern(YOUR_NEW_DATE_TIME_PATTERN);
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-06-01T11:04:02+02:00", formatter);
ZoneId from = ZoneId.from(zonedDateTime);
System.out.println(from);
ZonedDateTime zonedIST = zonedDateTime.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
System.out.println(zonedDateTime.format(newFormatter));
System.out.println(zonedIST.format(newFormatter));
}
The prints should print:
2020-06-01T11:04:02:000+0000
2020-06-01T16:34:02:000+0530
EDIT
Included ZoneId to allow handling different timezones.
just a question what i am doing wrong. I have this code:
public static int berechneSekundenwert(String datum, String zeit) throws ParseException {
Date dt = new Date();
SimpleDateFormat df = new SimpleDateFormat( "dd.MM.yyyy HH:mm:ss" );
dt = df.parse( datum+" "+ zeit);
int gesamtzeit = (int)dt.getTime();
return gesamtzeit;
}
Now my import format is:
09.11.2019 01:30:17
What i want to do is calculate the time passed for these dates, so i
can later sort them by time. But i get negative values?!
Example output (passed time, date, daytime):
-2120215336 30.09.2019 12:03:35
1757321960 25.09.2019 16:06:25
-2111322336 30.09.2019 14:31:48
-1281127040 21.08.2019 12:05:36
-1280681040 21.08.2019 12:13:02
377782960 09.09.2019 16:54:06
1301386664 09.11.2019 01:30:17
710621960 13.09.2019 13:21:25
712564960 13.09.2019 13:53:48
Shouldn't they all be positive, since java states, that the getTime function measures the time since 01.01.1970
Anyone knows what i did wrong?
Computers use something called a timestamp to represent dates. In Java, Date::getTime() returns the milliseconds passed since 1970-01-01T00:00:00.000Z up to the date in question as long (64-bit integer).
In the code presented, this value is narrowed down to an int (32-bit integer). By narrowing the long to an int, the highest 32 bits get cut of. The largest value representable by an int is 2^31 - 1. A quick calculation shows that:
(2^31 - 1) (milliseconds)
/ 1000 (milliseconds per second)
/ 60 (seconds per minute)#
/ 60 (minutes per hour)
/ 24 (hours per day)
= 24.8551348032 (days)
This means that after roughly 25 days, the int will overflow (as it is defined in the Two's compliment). Not to mention that a later point in time could have a lower value than an earlier point in time, thus the negative values.
To fix this issue1, I would suggest to define gesamtzeit as long.
Two remarks on your code:
java.util.Date is regarded as outdated. I would suggest to use java.time.Instant instead.
I would suggest to use English in the source code, only exception being you use domain-specific words that cannot (well) be translated to English.
1 This is only a temporary fix. All representation with a fixed number of bits will eventually overflow. In fact, all representation with any memory constraint at all will overflow eventually. I leave it up to the reader to find out when a 64-bit integer will overflow
tl;dr
See correct Answer by Turing85 about 32-bit versus 64-bit integers.
Use only modern java.time classes, never Date/SimpleDateFormat.
Consider the crucial issue of time zone or offset-from-UTC.
Educate the publisher of your data about the importance of (a) including zone/offset info, and (b) using ISO 8601 standard formats.
Code:
LocalDateTime.parse(
"09.11.2019 01:30:17" ,
DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" )
)
.atOffset(
ZoneOffset.UTC
)
.toInstant()
.toEpochMilli()
See this code run live at IdeOne.com.
1573263017000
Details
The correct Answer by Turing85 addresses your specific question as to why the invalid negative numbers. But you have other problems.
ISO 8601
Now my import format is: 09.11.2019 01:30:17
I suggest you educate the publisher of this data about the ISO 8601 standard defining formats to use when communicating date-time values as text.
Legacy date-time classes
You are use terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310. Never use Date or SimpleDateFormat.
Moment
Apparently you want to get a count of milliseconds since the epoch reference of first moment of 1970 in UTC. But doing that requires a moment, a specific point on the timeline.
Your input does not meet this requirement. Your input is a date and a time-of-day but lacks the context of an offset-from-UTC or a time zone.
So, take your example of 09.11.2019 01:30:17. We cannot know if this is 1:30 in the afternoon of Tokyo Japan, or 1:30 PM in Paris France, or 1:30 in Toledo Ohio US — which are all very different moments, several hours apart on the timeline.
So we must first parse your input as a LocalDateTime. This class represent a date and time without any concept of offset or zone.
String input = "09.11.2019 01:30:17" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu HH:mm:ss" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
Perhaps you know for certain the offset or zone intended by the publisher of this data. If so:
Suggest to the publisher of this data that they include the zone/offset info within their data.
Apply a ZoneOffset to get an OffsetDateTime, or a ZoneId to get a ZonedDateTime.
Perhaps you know for certain this input was intended for UTC, that is, an offset of zero hours-minutes-seconds.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
To get a count of milliseconds since 1970-01-01T00:00Z convert to the basic building-block class Instant.
Instant instant = odt.toInstant() ;
Interrogate for a count of milliseconds since epoch.
long millisSinceEpoch = instant.toEpochMilli() ;
Understand that your original code ignored the crucial issue of time zone & offset-from-UTC. So your code implicitly applies the JVM's current default time zone. This means your results will vary at runtime, and means you likely have incorrect results too.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
why you downcast the return value ofgetTime()?
just make you method return long instead of int
and replace this line
int gesamtzeit = (int)dt.getTime();
with
long gesamtzeit = dt.getTime();
I am very new to OffsetDateTime usage and I am trying to compare OffsetDateTime strings with OffsetDateTime.now() in java this way,
import java.time.OffsetDateTime;
public class OffsetDateTimeDemo {
public static void main(String[] args) {
OffsetDateTime one = OffsetDateTime.parse("2017-02-03T12:30:30+01:00");
System.out.println("First ::" + OffsetDateTime.now().compareTo(one));
OffsetDateTime date1 = OffsetDateTime.parse("2019-02-14T00:00:00");
System.out.println("Second ::" + OffsetDateTime.now().compareTo(date1));
OffsetDateTime date3 = OffsetDateTime.parse("Mon Jun 18 00:00:00 IST 2012");
System.out.println(" Third :: " +OffsetDateTime.now().compareTo(date3));
}
}
But I am getting java.time.format.DateTimeParseException in all the 3 cases.
However if i compare 2 OffsetDateTime Strings with CompareTo method its working fine.
Can someone shed some light to me in this regard and kindly guide me through my mistake.
Thanks in Advance.
Your compareTo coding is a distraction. Your exception is about parsing the string inputs into objects.
Another problem: You are using wrong classes on the 2nd and 3rd inputs.
Another problem: You are relying implicitly on your JVM’s current default time zone when calling now(). Poor practice as any programmer reading will not know if you intended the default or if you were unaware of the issue as are so many programmers. Furthermore, the current default can be changed at any moment during runtime by any code in any thread of any app within the JVM. So better to always specify explicitly your desired/expected zone or offset.
OffsetDateTime.now(
ZoneOffset.UTC
)
Or better yet, use a ZonedDateTime to capture more information than a OffsetDateTime.
ZonedDateTime.now(
ZoneId.of( "Pacific/Auckland" )
)
First: OffsetDateTime works
Your first string input is proper, and parses successfully.
OffsetDateTime.parse( "2017-02-03T12:30:30+01:00" )
Full line of code:
OffsetDateTime odt = OffsetDateTime.parse( "2017-02-03T12:30:30+01:00" ) ;
See this code run live at IdeOne.com.
odt.toString(): 2017-02-03T12:30:30+01:00
To compare, extract an Instant. Doing so effectively adjusts your moment from some offset to an offset of zero, or UTC itself. An Instant is always in UTC, by definition.
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
boolean odtIsPast = odt.toInstant().isBefore( instant ) ;
Second: LocalDateTime
Your second string input lacks any indicator of offset-from-UTC or time zone. So an OffsetDateTime is the wrong class to use. Instead use LocalDateTime which lacks any concept of offset or zone.
This means a LocalDateTime cannot represent a moment. For example, noon on the 23rd of January this year could mean noon on Asia/Tokyo which would be hours earlier than noon in Europe/Paris, or it could mean noon in America/Montreal which would be a moment even more hours later. Without the context of a zone or offset, a LocalDateTime has no real meaning. So comparing a LocalDateTime to the current moment is senseless.
LocalDateTime.parse( "2019-02-14T00:00:00" )
See this code run live at IdeOne.com.
ldt.toString(): 2019-02-14T00:00
To compare, you can’t — illogical as discussed above. You must assign a time zone (or offset) to determine a moment on the timeline. If you know for certain this date and time were meant for a specific time zone, assign ZoneId to get a ZonedDateTime. Then extract a Instant to compare.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ; // India time.
ZonedDateTime zdt = ldt.atZone( z ) ;
Instant instant = Instant.now() ; // Capture the current moment as seen in UTC.
boolean zdtIsPast = zdt.toInstant().isBefore( instant ) ; // Compare.
By the way, I noticed the time-of-day is zero. If your goal was to represent the date only, without any time-of-day and without any zone, use LocalDate class.
Third: Don’t bother, ambiguous input
Your third string input carries a time zone indicator. So it should be parsed as a ZonedDateTime.
Unfortunately, you’ve chosen a terrible string format to parse. Never use the 2-4 character pseudo-zones like IST. They are not standardized. And they are not unique! Your IST could mean Ireland Standard Time or India Standard Time or others.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
See this code run live at IdeOne.com.
zdt.toString(): 2019-02-20T22:34:26.833+01:00[Africa/Tunis]
You could try to parse this. ZonedDateTime will make a guess as to which zone was meant by IST. But it would be just a guess, and so is unreliable given the inherently ambiguous input. Personally, I would refuse to code that, rejecting this input data back to its source.
If you insist on making this unreliable parse attempt, see the correct Answer to a similar Question you asked recently.
Educate your source about always using standard ISO 8601 formats to exchange date-time values as human-readable text.
The java.time classes use these ISO 8601 formats by default when parsing/generating strings. The ZonedDateTime class wisely extends the standard to append the standard name of the time zone in square brackets.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I have a database in which I have users with their info etc. One field is named "maeindat" and there is stored the date of the entry (creation) of that entitiy ( user )
Now I want to compare if current time is "smaller" than input date and if it is set current date into the field, but if date of entry is bigger than current date set date of entry into the field
current date < date of entry --> set current date into the field
current date > date of entry --> set date of entry in field
Bellow is the code I'm trying out...
String maeindat = rs.getString("MAEINDAT");
LocalDateTime currTime = LocalDateTime.now();
if(currTime.isBefore(maeindat)) {
currTime = maeindat;
}
else if(currTime.isAfter(maeindat)) {
maeindat = maeindat;
}
UPDATE:
String maeindat = rs.getString("MAEINDAT");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYYMMDDHH24MI");
LocalDateTime maeindatDate = LocalDateTime.parse(maeindat, formatter);
LocalDateTime currTime = LocalDateTime.now();
if(currTime.isBefore(maeindatDate)) {
currTime = maeindatDate;
}
else if (currTime.isAfter(maeindatDate)) {
maeindatDate = maeindatDate;
}
tl;dr
Comparing a LocalDateTime with current moment makes no sense logically.
myResultSet.getObject(
… ,
Instant.class // Retrieve from database column of type similar to SQL-standard `TIMESTAMP WITH TIME ZONE`.
).isBefore( Instant.now() ) // Or `isAfter` or `equals` or combine with `!` (meaning NOT before/after).
Apples & Oranges
You cannot compare strings to date-time objects. Parse your strings into date-time objects, and then you may compare.
LocalDateTime
The LocalDateTime class lacks any concept of time zone or offset-from-UTC. Use this class only if using a column in your database of a type similar to SQL-standard TIMESTAMP WITHOUT TIME ZONE.
This type is not intended to represent actual moments, specific points on the timeline. Instead this type is only a rough idea of potential moments spread over a range of about 26-27 hours.
If we say "Santa delivers the toys just after midnight on December 25th", do we mean just after midnight in Auckland, New Zealand or do we mean midnight in Kolkata India which occurs hours later? Or Paris France even more hours later? "Midnight" has no real meaning until you specify Auckland, Kolkata, or Paris.
Comparing a LocalDateTime to the current moment makes no sense! The LocalDateTime has no real meaning without the context of a time zone or offset. If you know for certain of an appropriate time zone for that value, apply a ZoneId to get a ZonedDateTime. At that point, you have an actual moment, a point on the timeline.
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = myLocalDateTime.atZone( z ) ; // Converting vague idea about potential moments into an actual moment, a specific point on the timeline.
Instant
If you intend to represent actual moments, use SQL-standard type TIMESTAMP WITH TIME ZONE and Java type Instant (UTC) or possibly ZonedDateTime.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Capture the current moment in UTC.
Instant instantNow = Instant.now() ; // Current moment in UTC.
Compare using isBefore, isAfter, equals.
boolean targetPassed = instant.isAfter( instantNow ) ;
Smart objects, not dumb strings.
With a JDBC driver complying with JDBC 4.2 and later, you may directly exchange java.time objects with your database. No need for converting to/from strings.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ; // For database column of type like `TIMESTAMP WITHOUT TIME ZONE`.
Or…
Instant instant = myResultSet.getObject( … , Instant.class ) ; // For database column of type like `TIMESTAMP WITH TIME ZONE`.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I have an epoch second and a zoneId (see method1 below).
It can be convert to LocalDateTime with system default zoneId, but I don't find the way to convert epoch second to LocalDateTime (see method2 below), because there is no ZoneOffset.systemDefault. I think it's obscure.
import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}
val epochSecond = System.currentTimeMillis() / 1000
// method1
LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault())
// method2
LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.MAX)
NOTE
The source code presented above is Scala.
Here is how you can get ZoneOffset from ZoneId:
Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);
NB: ZoneId can have different offset depending on point in time and the history of the particular place. So choosing different Instants would result in different offsets.
NB2: ZoneId.of() can return a ZoneOffset instead of ZoneId if UTC+3/GMT+2/etc is passed as opposed to a time zone like Africa/Cairo. So if UTC/GMT offsets are passed then historical/geographical/daylight-saving information of the Instant won't be taken into account - you'll simply work with the specified offset.
tl;dr
ZonedDateTime.now(
ZoneId.of( "America/Montreal" )
)
…of current default time zone…
ZonedDateTime.now(
ZoneId.systemDefault()
)
Details
The Answer by Stanislav Bshkyrtsev correctly and directly answers your Question.
But, there are larger issues involved, as suggested in the Answer by Jon Skeet.
LocalDateTime
I don't find the way to convert epoch second to LocalDateTime
LocalDateTime purposely has no concept of time zone or offset-from-UTC. Not likely what you want. The Local… means any locality, not any one particular locality. This class does not represent a moment, only potential moments along a range of about 26-27 hours (the range of time zones around the globe).
Instant
No need to start with epoch seconds if you are trying to get current time. Get the current Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now();
Inside of that Instant is a count of nanoseconds-from-epoch. But we do not really care.
See also, What's the difference between Instant and LocalDateTime?
ZonedDateTime
If you want to see that moment through the lens of a particular region’s wall-clock time, apply a ZoneId to get a ZonedDateTime.
ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = instant.atZone( z );
As a shortcut, you can do directly to the ZonedDateTime.
ZonedDateTime zdt = ZonedDateTime.now( z );
A ZonedDateTime has an Instant within it. Call zdt.toInstant() to get the same moment in time as a basic value in UTC. Same number of nanoseconds-since-epoch either way, as a ZonedDateTime or as a Instant.
Seconds-since-epoch given
If you are given a count of seconds-since-epoch, and the epoch is the first moment of 1970 in UTC (1970-01-01T00:00:00Z), then feed that number to Instant.
long secondsSinceEpoch = 1_484_063_246L ;
Instant instant = Instant.ofEpochSecond( secondsSinceEpoch ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
There is no one-to-one mapping. A ZoneId defines a geographic extent in which a set of different ZoneOffsets is used over time. If the timezone uses daylight saving time, its ZoneOffset will be different between summer and winter.
Furthermore, the daylight saving time rules may have changed over time, so the ZoneOffset could be different for e.g. 13/10/2015 compared to 13/10/1980.
So you can only find the ZoneOffset for a ZoneId on a particular Instant.
See also https://en.wikipedia.org/wiki/Tz_database
As the documentation says, "This is primarily intended for low-level conversions rather than general application usage."
Going via Instant makes perfect sense to me - your epoch second is effectively a different representation of an Instant, so convert to an Instant and then convert that into a particular time zone.
I hope the first two lines of my solution below are helpful. My problem was I had a LocalDateTime and the name of a time zone, and I needed an instant so I could build a java.util.Date, because that's what MongoDB wanted. My code is Scala, but it's so close to Java here I think there should be no problem understanding it:
val zid = ZoneId.of(tzName) // "America/Los_Angeles"
val zo: ZoneOffset = zid.getRules.getOffset(localDateTime) // ⇒ -07:00
// 2017-03-16T18:03
val odt = OffsetDateTime.of(localDateTime, zo) // ⇒ 2017-03-16T18:03:00-07:00
val instant = odt.toInstant // ⇒ 2017-03-17T01:03:00Z
val issued = Date.from(instant)
The following returns the amount of time in milliseconds to add to UTC to get standard time in this time zone:
TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")).getRawOffset()
I have an epoch second and a zoneId. Is there any way to convert ZoneId to ZoneOffset in java 8?
Get ZonedDateTime from epoch second and Zone Id
Get ZoneOffset from ZonedDateTime
Demo:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
// Get ZonedDateTime from epoch second and Zone Id
ZonedDateTime zdt = Instant.ofEpochSecond(1597615462L).atZone(ZoneId.of("Europe/London"));
// Get ZoneOffset from ZonedDateTime
ZoneOffset offset = zdt.getOffset();
System.out.println(offset);
}
}
Output:
+01:00
Since you are looking for the default zone offset
ZonedDateTime.now().getOffset()
This does it without creating an Instant or such objects to pull it out.
public static ZoneOffset offset() {
return offset(ZoneId.systemDefault()); // Default system zone id
}
public static ZoneOffset offset(ZoneId id) {
return ZoneOffset.ofTotalSeconds((int)
TimeUnit.MILLISECONDS.toSeconds(
TimeZone.getTimeZone(id).getRawOffset() // Returns offset in milliseconds
)
);
}
Here is what we use. First convert zone id to timezone, then get the offset in millis, then convert to seconds, then create a ZoneOffset.
public static final ZoneOffset toOffset(ZoneId zoneId) {
return Optional.ofNullable(zoneId)
.map(TimeZone::getTimeZone)
.map(TimeZone::getRawOffset)
.map(Duration::ofMillis)
.map(Duration::getSeconds)
.map(Number::intValue)
.map(ZoneOffset::ofTotalSeconds)
.orElse(null);
}