So Our database has a column BigInt REVISIONTS used as part of java Hibernate Envers
It initially contained timestamps from Java Date.
E.g) ts=1561637560383
I used to convert to Date using new Date(ts)
But since Date cannot contain timezones and we needed UTC date, we had to store UTC directly as BigInt and applied a fix suggested by hibernate. Because of this now our timestamps are like this
E.g) ts=20190827202449 now this is not a timestamp anymore but an actual UTC LocaleDateTime stored as bigint
Now querying this i get long and if I use new Date(ts) i am getting incorrect date of course since this is not a timestamp but Hibernate Date with Temporal.Timestamp stored the UTC as is.
I am thinking of converting Long to string and use formatting to convert back when retrieving.
Are there any other cleaner method of converting ?
UTC fix for Envers
How to save UTC (instead of local) timestamps for Hibernate Envers revision info?
You shouldn't be using Date at all. Never. Ever.
I think storing the time as a Unix Timestamp is pretty fine. They're always in UTC and represent a unique instant on the timeline.
Envers supports both Date and Long/long to be defined as revision timestamp. You should use Long.
Formatting it using a timezone or timezone offset can be easily done with the newer Java Date and Time API available in the java.time package.
With Instant.ofEpochSecond(yourTimestamp) you can create an Instant. With atOffset or atZone you can combine the bare timestamp with a certain timezone or timezone offset.
Your Question is quite unclear. But this might help.
Avoid legacy date-time classes
to convert to Date using new Date(ts)
Never use java.util.Date. That terrible class was supplanted years ago by the java.time classes, specifically by Instant.
Instant
E.g) ts=1561637560383
You are not clear about exactly what that value represents. I will guess it is a count of milliseconds since the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z.
long count = 1_561_637_560_383L ;
If that is a textual value, parse using Long class.
long count = Long.parseLong( "1561637560383" ) ;
Instant instant = Instant.ofEpochMilli( count ) ;
See this code run live at IdeOne.com.
instant.toString(): 2019-06-27T12:12:40.383Z
Tip: In your database, store date-time values using date-time data type.
If your database is too primitive to support date-time types, store as text in UTC using ISO 8601 format.
String output = instant.toString() ; // Ex: 2019-06-27T12:12:40.383Z
…and…
Instant instant = Instant.parse( "2019-06-27T12:12:40.383Z" ) ;
Get count of milliseconds since epoch reference.
long count = instant.toEpochMilli() ;
Convert
When you must use Date to interoperate with old code not yet updated to java.time, convert. Call new to…/from… methods added to the old classes.
java.util.Date d = Date.from( instant ) ;
Instant instant = d.toInstant() ;
Related
Part 1:
I have a file which contains date as string in the following format: 2006-02-16T21:36:32.000+0000
I need to write this value to Postgres in a column which is of type Timestamp. How do I go about doing this in Java?
Part 2:
I need to read the value saved in Part 1 from Postgres, and convert it to a string of the following format "2006-02-16T21:36:32.000+0000"
This is what I have tried:
Timestamp lastSyncDate = Timestamp.valueOf(2006-02-16T21:36:32.000+0000)
When I try to write it to postgres, it gives me the following error:
java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
tl;dr
myPreparedStatement.setObject(
… ,
OffsetDateTime.parse( "2006-02-16T21:36:32.000+0000" )
)
With and without zone/offset
You said your column is of type TIMESTAMP which is short for TIMESTAMP WITHOUT TIME ZONE. This type purposely lacks any concept of time zone or offset-from-UTC. Not usually appropriate for common business purposes.
You are using wrong type
That is the wrong type for your input. Your input has an offset-from-UTC of zero hours which means UTC itself. Having an offset or zone, your data should only be stored in a TIMESTAMP WITH TIME ZONE type column. In this …WITH… type in Postgres, any submitted offset/zone info is used to adjust into UTC for storage, and then discarded.
Storing a date-time value with an offset or zone in a column of type TIMESTAMP WITHOUT TIME ZONE is like storing a price/cost with a designated currency( USD, Euros, Rubles, etc.) in a column of numeric type. You are losing vital data, the currency. This renders your data worthless.
See the Postgres documentation page for these types.
Smart objects, not dumb strings
Whenever possible, use objects to exchange data with your database rather than passing meter strings. Let your JDBC driver do it’s job in marshaling the data back-and-forth.
Parse the input string as an OffsetDateTime object.
ISO 8601
Your input strings are in a format defined by the ISO 8601 standard. The java.time classes use ISO 8601 formats by default when parsing/generating strings. No need to specify a formatting pattern.
String input = "2006-02-16T21:36:32.000+0000" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;
Define your SQL as a prepared statement.
With JDBC 4.2 and later, you can directly exchange java.time objects with your database. No need to ever again use the troublesome legacy classes such as java.sql.Timestamp.
You could pass an OffsetDateTime. I like to extract an Instant to demonstrate to the reader that I understand how Postgres always stores a TIMESTAMP WITH TIME ZONE value in UTC.
Instant instant = odt.toInstant() ;
myPreparedStatement.setObject( … , instant ) ;
Retrieval.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
2006-02-16T21:36:32.000+0000 looks like a ISO-8601 timestamp.
postgres understands this format. just treat it like any other string.
don't try to convert it into a java timestamp
I need exactly that format in java which in C# is
DateTime.Now.ToString("o"). Sample returned date for DateTime.Now.ToString("o") is
2016-03-10T11:24:59.7862749+04:00
and then in sql it's inserted as
2016-03-10 11:24:59.786
I'm trying to insert same date format from java. I use that:
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());
and it returns this
2016-03-10T07:29+0000
Because of that format then it goes in error. How can I change format to be exactly which I want?
For Java 7 you can use:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(df.format(new Date()));
This uses the pattern symbol XXX which will print the colon inside the offset, too. However, for Java-6 this feature is not offered. And the precision is always constrained to milliseconds.
For Java-8, you can also use:
DateTimeFormatter dtf = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(OffsetDateTime.now().format(dtf)); // 2016-03-10T08:46:44.849+01:00
This enables nanosecond precision if such a clock is available (starting with Java-9).
For Java-6 either apply a hack based on SimpleDateFormat or use external libraries:
// Java-6 (SimpleDateFormat)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
String text = sdf.format(new Date());
text = text.substring(0, text.length() - 2) + ":" + text.substring(text.length() - 2);
System.out.println(text);
// Joda-Time
DateTime now = DateTime.now();
System.out.println(DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZ").print(now));
// Time4J
Moment now = SystemClock.currentMoment();
System.out.println(Iso8601Format.EXTENDED_DATE_TIME_OFFSET.withStdTimezone().format(now));
The Answer by Meno Hochschild is correct. I'll just add some more comments and some SQL-specific code.
Avoid Old Date-Time Classes
The old date-time classes, java.util.Date/.Calendar & java.text.SimpleDateFormat, are poorly designed, confusing, and troublesome. Avoid them.
The old clases have been supplanted by the java.time framework built into Java 8 and later.
For use before Java 8, check out the ThreeTen-Backport project.
Nanoseconds
The java.time classes have nanosecond resolution. So you will not have the problem of data loss where 2016-03-10T11:24:59.7862749+04:00 gets truncated to 2016-03-10 11:24:59.786 because of millisecond resolution used by the old classes.
Getting the current moment in Java 8 is limited to milliseconds, three digits of decimal fraction of second, due to legacy issue. Java 9 will get the current moment in nanoseconds, up to nine digits of decimal fraction (provided your computer’s hardware clock can provide such fine resolution).
ISO 8601
The ISO 8601 standard defines sensible text formats for date-time values. For example, 2016-03-09T23:24:33Z or 2016-03-09T22:24:33-01:00. The java.time classes use these by default, so no need to define parsing patterns.
Instant
An Instant is a moment on the timeline in UTC.
Instant instant = Instant.now();
Call Instant::toString to generate a string in standard format.
String output = instant.toString();
2016-03-09T23:24:33.123Z
OffsetDateTime
Apply a ZoneOffset to get an OffsetDateTime.
ZoneOffset zoneOffset = ZoneOffset.ofHoursMinutes( -5 , 30 );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );
ZonedDateTime
If you know the full time zone rather than just the offset-from-UTC, apply a ZoneId to get a ZonedDateTime.
Use proper time zone names.
ZoneId zoneId = ZoneId.of( "Asia/Kolkata" ); // "Europe/Paris", "America/Montreal", etc.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
java.sql.Timestamp
Hopefully the JDBC drivers will be updated to directly use the java.time types. Until then we convert to java.sql types for transferring data in/out of database.
As noted above, java.time can handle nanoseconds. So does java.sql.Timestamp. But your database may not. Some databases are limited to whole seconds, milliseconds, or microseconds. When data is passed via JDBC to the database, the database may truncate.
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );
…and going the other direction…
Instant instant = ts.toInstant();
Note that an Instant is always in UTC by definition. So no need to perform the kind of code attempted at the end of the Question.
Work Flow
You should minimize your use of strings when working with date-time. Maximize your use of helpful date-time classes/objects, namely java.time classes. Stop thinking of strings as date-time values -- they are a textual representation of a date-time value.
Do not insert/retrieve date-time values to/from your database as strings. Use the java.sql objects such as java.sql.Timestamp and java.sql.Date. Use PreparedStatement and the "set/get" methods such as setTimestamp/getTimestamp. And virtually always define your columns in database as TIMESTAMP WITH TIME ZONE rather than “without time zone”.
When getting data from database, use the java.sql types. But as soon as is possible, convert to java.time types. The java.sql types are a mess, a dirty hack, and should be used only for data transfer not business logic.
Generally best to use UTC in your business logic, data storage, data exchange, API calls, and so on. Adjust into a time zone only when expected by a user or required by a data sink.
use this format "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
Example:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
System.out.println(df.format(new Date()));
I am new to Java. I have a web application that runs on a godaddy server in the USA.
The problem is when we use the application anywhere we need to update the date with the respective timestamp in database. While updating it is storing as MST format date instead of IST format (when I tried from India). Let me know how can I solve this.
Thanks in advance.
My code is as follows.
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
f.setTimeZone(TimeZone.getTimeZone("IST"));
String date = f.format(new Date());
Date aptDate = formatter.parse(date);
System.Out.Print("The IST time is : "+ date);
Here I am getting the string format date, I want to convert into a date object and then store in the database.
When I apply the conversion it is giving earlier date. Let me what is the wrong going.
Your Question is confusing. Here are some guidelines to re-orient your thinking.
You are using old date-time classes now outmoded by the java.time framework built into Java 8 and later. Avoid the old classes as they are poorly designed, confusing, and troublesome.
Stop using the 3-4 letter codes such as IST. Do you mean India Standard Time or Irish? These codes are neither standardized nor unique. Erase these codes from both your thinking and your code. Use proper time zone names in the format of continent/region.
Do most of your work, your business logic, data exchange, data storage, and database in UTC. Apply a time zone only when expected by your user or data sink.
Use date-time types when defining columns in your database. Do not store date-time values as strings.
Use the java.sql types to transfer date-time values in and out of the database. Convert immediately to java.time types. Eventually JDBC drivers will be updated to use java.time types directly. Until that day, use new methods added to the old java.sql classes for convenient conversions.
An Instant is a moment on the timeline in UTC.
Instant now = Instant.now();
Convert to java.sql.Timestamp to write to the database.
java.sql.Timestamp ts = java.sql.Timestamp.from( now );
To the other direction, for retrieval, convert from java.sql to java.time.
Instant instant = myJavaSqlTimestamp.toInstant();
Notice that for database work we do not care about time zone as the values are all in UTC on all sides: Java, JDBC, SQL, database.
To view the wall-clock time for some locality, apply a time zone (ZoneId) to generate a ZonedDateTime object.
ZoneId zoneIdEdmonton = zoneId.of( "America/Edmonton" );
ZonedDateTime zdtEdmonton = ZonedDateTime.ofInstant( instant , zoneIdEdmonton );
You can apply yet another time zone. Note that java.time uses immutable objects. An new object arises from an old object rather than changing (“mutating”) the original. In this example, the zdtEdmonton object begets the zdtKolkata object.
ZoneId zoneIdKolkata = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdtKolkata = zdtEdmonton.withZoneSameInstant( zoneIdKolkata );
I'm querying a Postgres database and looking for the best way to store a Postgres "timestamp with timezone" information.
the format is "yyyy-MM-dd' 'HH:mm:ss.SSSzz" (i.e. "2014-04-22 05:39:49.916+03")
i'd like to use a timestamp oriented type/class to keep the info (not String)
the following throws Unparsable date for all the TIME_FORMATS i could think of:
final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSz";
final SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT);
final java.util.Date utilDate = sdf.parse("2014-04-22 05:39:49.916+03");
Your format is having ISO Timezone, so use X (not z).
final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSX";
More Info here
java.sql.Timestamp
Current JDBC drivers will create. java.sql.Timestamp object for you when retrieving a value from either of Postgres’ time stamp types, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITHOUT TIME ZONE.
java.sql.Timestamp ts = myResultSet.getTimestamp( 1 ) ;
No need for strings. Stick to using objects rather than strings for date-time values whenever possible.
java.time
In Java 8 and later you should be using the new java.time package. Avoid the old java.util.Date/.Calendar and so on.
When your JDBC driver is updated for JDBC 4.2, you will be able to retrieve your Postgres date-time value directly into the java.time types.
While waiting on such drivers, use the new conversion methods added to java.sql.Timestamp for going to and from an Instant.
Search StackOverflow for more info as this has been discussed many times already.
If you're using Java 8, you could store the data in a ZonedDateTime object, which stores a
date-time with a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00 Europe/Paris.
Simple call toInstant() on the java.sql.Timestamp value in the result and you're good to go (thanks #Basil Bourque for pointing this out).
Prior to Java 8, you could use your existing solution. Note however that if your timezone info is stored as +03, you should have your time format pattern end with X, which is a ISO 8601 time zone.
When you insert the following into a SQL database:
Date now = new Date(System.currentTimeMillis());
PreparedStatement audit = sqlConn.prepareStatement("INSERT INTO [Table](Date) VALUES (?)");
audit.setDate(1, now);
audit.execute();
This is the result:
2013-09-06 00:00:00.000
How do I also get the time information inserted into the database?
Edit: The column datatype is DateTime.
You may be confusing:
The java.util.Date class which represents a date and a time-of-day in UTC
The java.sql.Date class which represents a date-only, without time-of-day and without time zone.
Actually, java.sql.Date extends java.util.Date so it does in fact inherit a time-of-day but pretends to not have a time-of-day by setting it to 00:00:00.0 in UTC. The class doc tells you ignore the inheritance relationship (it's a hack). Confusing? Yes, these old date-time classes are a mess, poorly designed, confusing, and troublesome.
These old classes were supplanted by the java.time classes built into Java 8 and later. Much of the functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
If your database column is of a type similar to the SQL standard type TIMESTAMP WITH TIME ZONE, then you can use either:
InstantA moment on the timeline in UTC with a resolution up to nanoseconds.Instant instant = Instant.now();
ZonedDateTimeLike an Instant but adjusted into a particular time zone.ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );
If your JDBC driver complies with JDBC 4.2 spec, you can pass either of these via the setObject method on a PreparedStatement.
If your driver does not support the direct use of java.time types, fall back to using java.sql types. Find new methods added to the old classes for converting to/from java.time types. You are looking for the java.sql.Timestamp class in particular for a date-time value, which can convert to/from Instant. From a ZonedDateTime you can extract an Instant for this purpose of conversion.
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );
java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );
Going the other direction.
Instant instant = mySqlTimestamp.toInstant();
Some database only store up to the second precision for the date.
SQLserver's datetime supports fractional seconds, but it rounds to the nearest 3 milliseconds.
If you need more precision, store your date as a bigint type and pass the now.getTime() Java long value instead.