Is there any way to convert ZoneId to ZoneOffset in Java 8? - java

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);
}

Related

OffsetDateTime String Comparision with OffsetDateTime.now() in java

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.

Comparing time in java between epoch milliseconds and LocalDateTime

I have a timestamp in epoch milliseconds and I want to check if it is between two LocalDateTime stamps. What's the best way to do this in java?
One way to do it is to convert the milliseconds to LocalDateTime
LocalDateTime date = Instant.ofEpochMilli(milliseconds)
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
LocalDateTime start = LocalDateTime.now().minusMinutes(1);
LocalDateTime end = LocalDateTime.now().plusMinutes(1);
if (date.isAfter(start) && date.isBefore(end)) {
// date is between start and end
}
tl;dr
You cannot compare a LocalDateTime to a moment until assigning a time zone (or offset-from-UTC).
org.threeten.extra.Interval // Represents a span-of-time attached to the timeline, as a pair of `Instant` objects, a pair of moments in UTC.
.of (
myLocalDateTimeStart
.atZone( ZoneId.of( "Pacific/Auckland" ) ) // Determine a moment by assigning an time zone to a `LocalDateTime` to produce a `ZonedDateTime`, from which we extract an `Instant` to adjust into UTC.
.toInstant() ,
myLocalDateTimeStop
.atZone( ZoneId.of( "Pacific/Auckland" ) ) // Returns a `ZonedDateTime` object.
.toInstant() // From the `ZonedDateTime`, extract a `Instant` object.
) // Returns `Interval` object.
.contains(
Instant.ofEpochMilli( 1_532_463_173_752L ) // Parse a count of milliseconds since 1970-01-01T00:00:00Z as a moment in UTC, a `Instant` object.
) // Returns a boolean.
Details
Comparing time in java between epoch milliseconds and LocalDateTime
You cannot. That comparison is illogical.
A LocalDateTime does not represent a moment, is not a point on the timeline. A LocalDateTime represents potential moments along a range of about 26-27 hours, the range of time zones around the world.
As such it has no real meaning until you place it in the context of a time zone. If that particular date and time were invalid in that zone, such as during a Daylight Saving Time (DST) cut-over, or during some other such anomaly, the ZonedDateTime class adjusts.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = myLocalDateTime.atZone( z ) ;
For comparisons, we will adjust into UTC by extracting an Instant object from your start and stop ZonedDateTime objects.
Instant start = zdtStart.toInstant() ;
Instant stop = zdtStop.toInstant() ;
Now parse your count of milliseconds since the epoch reference of first moment of 1970 as a Instant. Instant has an even finer resolution, nanoseconds.
Instant instant = Instant.ofEpochMilli( 1_532_463_173_752L ) ;
Compare to see if your epoch-milliseconds represent a moment in between our stop and start Instant objects. Usually in date-time work, the Half-Open approach is best, where the beginning is inclusive while the ending is exclusive.
Tip: A shorter way of saying “is equal to or is after” is to say “is not before”.
boolean inRange = ( ! instant.isBefore( start ) ) && instant.isBefore( stop ) ;
To make this work easier, add the ThreeTen-Extra library to your project. Use the Interval class.
Interval interval = Interval.of( start , stop ) ;
boolean inRange = interval.contains( instant ) ; // Uses Half-Open approach to comparisons.
Tip: If you had intended to be tracking moments, you should not have been using LocalDateTime class at all. Instead, use the Instant, OffsetDateTime, and ZonedDateTime classes.
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.
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, 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 (<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.
When comparing an Instant (time-since-Epoch) with a LocalDateTime, you always need to consider the timezone of the local times. Here's an example:
Instant now = Instant.now();
LocalDateTime start = LocalDateTime.of(2018, 7, 24, 0, 0);
LocalDateTime end = LocalDateTime.of(2018, 7, 24, 23, 59);
final ZoneId myLocalZone = ZoneId.of("Europe/Paris");
if (now.isAfter(start.atZone(myLocalZone).toInstant())
&& now.isBefore(end.atZone(myLocalZone).toInstant())) {
// the instant is between the local date-times
}

Comparing current time with a time of data enty in database

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.

Does the timezone matter when parsing a Timestamp?

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.

Date to timestring conversion one hour wrong

I have a Date, for example 2000-01-01T10:00:00Z . This stands for openinghours of a shop, so this means the shop opens at 10 o'clock. The other information is useless, it is a random date.
I only need to represent this as a string so: 10:00.
For this conversion i used a simple method:
public String dateToString(Date date){
SimpleDateFormat ft = new SimpleDateFormat ("HH:mm");
String time= ft.format(date);
return time;
}
I thought this should work, the capital HH for 24 hour representation of the hours. But when i run this code the return value is 11:00 !
Why is this, and how to prevent it? Does the format function take a look at my time zone and is this set wrong in the Date (i think my phone settings are mgt+1, cause i live in Holland)? And how to ignore this?
oke, found it!
Android converses a Date to the timezone of the device. I had to overrule that:
This works:
public String dateToString(Date date){
SimpleDateFormat ft = new SimpleDateFormat ("HH:mm");
ft.setTimeZone(TimeZone.getTimeZone("UTC"));
String time= ft.format(date);
return time;
}
The value 2000-01-01T10:00:00Z is not not at all “useless”. This describes an exact moment on the timeline, with a date, a time-of-day, and an offset-from-UTC which is UTC itself in this case.
ISO 8601
That string format is defined by the ISO 8601 standard. The Z on the end is short for Zulu and means UTC.
Instant
You can parse this string directly as a Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds. That means up to nine (9) digits of a decimal fraction.
Instant instant = Instant.parse( "2000-01-01T10:00:00Z" );
The Instant class is only the basic building-block class in Java. For more flexibility we should either:
Assign the instant an offset-from-UTC (ZoneOffset) to get an OffsetDateTime.
Assign the instant a time zone (ZoneId) to get a ZonedDateTime.
What is the difference? A time zone is an offset plus a set of rules for handling anomalies such as Daylight Saving Time (DST).
OffsetDateTime
So if you truly wanted the time-of-day for this moment as seen in UTC, then assign an offset of UTC (ZoneOffset.UTC) to get an OffsetDateTime, and then extract a LocalTime object. LocalTime represents a time-of-day without a date and without a time zone.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC );
LocalTime lt = odt.toLocalTime(); // 10:00:00
ZonedDateTime
If you want to see the same moment through the lens of another time zone, to see that particular region’s wall-clock time, specify a ZoneId. For example, that same moment in Québec would be 5 hours behind UTC.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z ); // 2000-01-01T05:00:00-05:00[America/Montreal]
LocalTime lt = zdt.toLocalTime(); // 05:00:00
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.

Categories