I have stored in a String type variable my_date a value retrieved from an XML file.
The my_date is in GMT and has a timezone offset that needs to be considered for the UTC conersion.
I would like to convert it (another String) but in UTC format without the timezone - for example:
String my_date = "2020-02-16T20:40:55.000+01:00"
//Convertion
String my_date_utc = "2020-02-16 21:40:55.000"
Parse as a OffsetDateTime object. Adjust the offset to UTC, producing a second OffsetDateTime object.
OffsetDateTime
.parse
(
"2020-02-16T20:40:55.000+01:00"
)
.withOffsetSameInstant
(
ZoneOffset.UTC
)
Understand that date-time objects are not String objects. They parse and generate strings as inputs and outputs.
Search Stack Overflow to learn about producing strings in various formats using DateTimeFormatter. That has been covered many hundreds of times already.
Related
I'm trying to parse "1901-01-01T00:20:40.000+02:20:40" date but I'm getting the following error:
Cannot deserialize value of type java.util.Date from String "1901-01-01T00:20:40.000+02:20:40": not a valid representation (error: Failed to parse Date value '1901-01-01T00:20:40.000+02:20:40': Cannot parse date "1901-01-01T00:20:40.000+02:20:40":
I read it is a problem with java date format which is not able the pare old dates, is there something that I can do in order to fix this issue?
---- edit ----
until now, i deserialized lots of different dates from the last 20 years, and they all worked fine.
It is a very simple object:
public class Record extends Docs {
public Date published;
}
and
results =
given().
config(config).
log().ifValidationFails().
when().
get(lastUrlComponent).
then().
log().ifValidationFails().
statusCode(HttpStatus.SC_OK).
contentType(ContentType.JSON).
assertThat().body(matchesJsonSchemaInClasspath(jsonSchemaInClasspath)).
extract().
response().as(resultsClass);
}
java.time
Never use the terrible legacy date-time classes such as Date & Calendar. Use only their successors, the modern java.time classes defined in JSR 310.
Offset-from-UTC
Your input is text in standard ISO 8601 format. That text contains a date, a time-of-day, and a number of hours-minutes-seconds offset from the temporal prime meridian of UTC.
OffsetDateTime
The appropriate class for such an input is OffsetDateTime. That class can directly parse inputs in ISO 8601 format. So no need to define a formatting pattern.
String input = "1901-01-01T00:20:40.000+02:20:40" ;
OffsetDateTime odt = OffsetDateTime.parse( input ) ;
Adjust to an offset of zero.
Instant instant = odt.toInstant() ;
See this code run live at Ideone.com.
odt.toString(): 1901-01-01T00:20:40+02:20:40
instant.toString(): 1900-12-31T22:00:00Z
It should follow steps bellow:
Read it as String (It can work)
private String published
List item
Define the date format
Parse it to date by Java
I think it easier to make your application that can work
"1901-01-01T00:20:40.000+02:20:40" is not a valid offset - 2 hours, 20 minutes, and 40 seconds from UTC
I'm getting below exception while converting date string from ISO to UTC format in java, what I'm doing wrong here? Please find my code below:
Test.java
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class Test {
public static void main(String[] args) {
String date="2020-11-02T07:00:00.114";
String result = LocalDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")).atOffset(ZoneOffset.UTC).toString();
System.out.println("result: "+result);
}
}
Exception:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2020-11-02T07:00:00.114' could not be parsed, unparsed text found at index 19
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2049)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
at test.service.Test.main(Test.java:11)
tl;dr
LocalDateTime
.parse( "2020-11-02T07:00:00.114" )
.atOffset( ZoneOffset.UTC )
.toString()
2020-11-02T07:00:00.114Z
Details
You said:
converting date string from ISO to UTC format
That does not exactly make sense.
There is an ISO 8601 standard defining sensible formats for exchanging date-time values textually. These standard formats are used by default when parsing/generating text.
There is no such thing as "UTC format". UTC is the temporal prime meridian, against which all time zones are defined. The rules for a time zone specify an offset-from-UTC in effect as a number of hours-minutes-seconds ahead or behind UTC. But this has nothing to with "formats" of text.
As for parsing your input string as a LocalDateTime object, there is no need to bother with defining a formatting pattern. Your string complies with ISO 8601, so it can be parsed directly.
LocalDateTime ldt = LocalDateTime.parse( "2020-11-02T07:00:00.114" ) ;
Your string, and a LocalDateTime object, represent a date with a time-of-day. But both lack the context of a time zone or offset-from-UTC.
Apparently you know that the publisher of your input string intended that to be a moment as seen from an offset of zero hours-minutes-seconds from UTC. So we can assign a ZoneOffset to get OffsetDateTime object.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
To generate text representing that moment in ISO 8601 format, simply call toString.
String output = odt.toString() ;
See this code run live at Ideone.com.
2020-11-02T07:00:00.114Z
Notice the Z on the end, pronounced “Zulu”. That is an abbreviation for an offset of zero, +00:00.
Your pattern ends in ss, in other words: In seconds. Your input then has still more stuff.
DateTimeFormatter doesn't take a wild stab at it - it either matches properly or it does not parse. It looks like those are intended to be milliseconds; toss .SSS at the end of your pattern.
You are giving the format of:
yyyy-MM-dd'T'HH:mm:ss
However are specifying a string with fractions of a second 2020-11-02T07:00:00.114.
That is parsing whole seconds, but there is still more in the time string.
You need format of:
yyyy-MM-dd'T'HH:mm:ss.SSS
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() ;
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 want to use DateTimeFormatter.ISO_LOCAL_DATE to print and parse dates. This is what I'm doing for printing:
Date date;
String text = DateTimeFormatter.ISO_LOCAL_DATE.format(
date.toInstant()
);
This is what I'm getting:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: Year
at java.time.Instant.getLong(Instant.java:603)
at java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1744)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1718)
This happens because the Instant class represents a point in the timeline: the number of nanoseconds since unix epoch (1970-01-01T00:00Z), without any concept of timezone - so it doesn't have a specific date/time (day/month/year, hours/minutes/seconds), as it can represent a different date and time in different timezones.
Setting a specific zone in the formatter, like you did, converts the Instant to that zone (so the count of nanoseconds since epoch can be translated to a specific date and time), making it possible to be formatted.
For this specific case, you want only the date part (day, month and year) in ISO8601 format, so one alternative is to convert the Instant to a LocalDate and call the toString() method. As you set UTC in the formatter, I'm using the same to convert it:
String text = date.toInstant()
// convert to UTC
.atZone(ZoneOffset.UTC)
// get the date part
.toLocalDate()
// toString() returns the date in ISO8601 format
.toString();
This return the same thing as your formatter. Of course for another formats, you should use the formatter, but specifically for ISO8601, you can use the toString() method.
You could also convert the Instant to the timezone you want (in this case, to UTC) and pass it directly to the formatter:
String text = DateTimeFormatter.ISO_LOCAL_DATE.format(
date.toInstant().atZone(ZoneOffset.UTC)
);
The only difference is that, when you set the zone in the formatter, the date is converted to that zone when formatting (when you don't set it, the date is not converted).
This is how it works:
String text = DateTimeFormatter.ISO_LOCAL_DATE
.withZone(ZoneId.of("UTC"))
.format(date.toInstant());