I am trying to implement java.time to parse different date formats and return as java.sql.Date. However I am loosing time if I use java.sql.Date.valueOf(LocalDate date).
How I can achive the same way as java.sql.Date(java.util.Date date.getTime())?
Possible formats in my application are.
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ssZ");
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter datetimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
DateTimeFormatter datetimeFormatterMil = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
import java.sql.Date;
public Date oslcStringToDate(String str) throws ParseException
{
//remove colon in time zone
Date date = null;
if (!str.equalsIgnoreCase(NULL)) {
int colonPos = str.length() - 3;
str = str != null && str.length() > 3 && str.charAt(colonPos) == ':' ?
str.substring(0, colonPos) + str.substring(colonPos + 1)
: str.endsWith("Z") ?
str.substring(0, str.length() - 1) + "+0000"
: str;
date = str.indexOf("T") > 0 ?
str.indexOf(".") > 0 ?
java.sql.Date.valueOf(LocalDateTime.parse(str, datetimeFormatterMil).toLocalDate())
: java.sql.Date.valueOf(LocalDateTime.parse(str, datetimeFormatter).toLocalDate())
: java.sql.Date.valueOf(LocalDateTime.parse(str, dateFormatter).toLocalDate());
}
return date;
}
Do not mix legacy and modern classes
if I use java.sql.Date.valueOf(LocalDate date)
Never mix the terrible legacy classes with their replacement, the modern java.time classes defined in JSR 310.
When handed an object of a legacy class, immediately convert. You can convert to and fro by way of to…/from…/valueOf methods found on the old classes.
LocalDate ld = myJavaSqlDate.toLocalDate() ;
And the other direction.
java.sql.Date d = Date.valueOf( myLocalDate ) ;
Avoid legacy classes
With JDBC 4.2 and later, support for the java.time classes is required in your JDBC driver.
So use only the java.time classes. No need to ever use either Date, nor Calendar, SimpleDateFormat, Timestamp, etc.
Date-only values
The LocalDate class represents a date-only, without a time-of-day, without a time zone or offset-from-UTC. So a LocalDate is a year, month, and day — nothing more.
The java.sql.Date class pretends to represent a date-only value. But due to a tragically poor design decision, the class inherits from java.util.Date which does have a time-of-day. And even more confusing, the java.util.Date class is supposed to represent a moment as seen in UTC, but nevertheless contains a time zone used when generating text. These legacy classes are a master class in how to not do OOP.
You said:
However I am loosing time if I use java.sql.Date.valueOf(LocalDate date).
If you mean the resulting java.sql.Date has no time-of-day, yes, of course, that is a feature, not a bug. As mentioned above, objects of this class actually do have a time-of-day because of inheritance, but the class pretends to have a date-only (year-month-day).
You said:
How I can achive the same way as java.sql.Date(java.util.Date date.getTime())?
I cannot understand your goal here. So, after again advising against ever using these awful legacy classes, I will layout some of the possible types for use with SQL.
standard SQL
java.time ☑️
legacy ❌
DATE
LocalDate
java.sql.Date
TIME
LocalTime
java.sql.Time
TIMESTAMP WITHOUT TIME ZONE
LocalDateTIME
no support
TIMESTAMP WITH TIME ZONE
OffsetDateTime
java.sql.Timestamp
ISO 8601
Your Question is not clear, but apparently you are trying to parse text in the format of "yyyy-MM-dd'T'HH:mm:ss.SSSZ".
This format happens to comply with the ISO 8601 standard. The java.time classes use the standard formats by default when parsing/generating text. So no need to specify a formatting pattern.
Instant instant = Instant.parse( "2022-01-23T12:34:56.789Z" ) ;
To generate such text:
String output = instant.toString() ;
To record to a database, convert to the class mapped in JDBC to the standard SQL type of TIMESTAMP WITH TIME ZONE.
That SQL name is a misnomer, as the SQL standard was written with mere offset-from-UTC in mind, not real time zones. An offset is merely a number of hours-minutes-seconds ahead or behind the temporal prime meridian of UTC. A time zone, in contrast, is a named history of the past, present, and future changes to the offset used by the people of a particular region as decided by their politicians.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Note that LocalDateTime is exactly the wrong class to be using in this context. The LocalDateTime class purposely lacks the context of an offset or zone. With only a date and a time-of-day, a LocalDateTime object is inherently ambiguous with regard to the timeline.
If you want to store only the date portion of your ISO 8601 string, you will need to think about time zones. For any given moment, the date varies around the globe by time zone. A moment can be “tomorrow” in Tokyo Japan while simultaneously “yesterday” in Toledo Ohio US.
Call toLocalDate to extract the date portion of an OffsetDateTime or ZonedDateTime object.
LocalDate dateInTokyo = instant.atZone( ZoneId.of( "Asia/Tokyo" ) ).toLocalDate() ;
LocalDate dateInUtc = instant.atOffset ZoneOffset.UTC ).toLocalDate() ;
LocalDate dateInToledo = instant.atZone( ZoneId.of( "America/New_York" ) ).toLocalDate() ; // Time zone name for Toledo Ohio US is `America/New_York`.
Write to a database column of a type akin to the SQL standard type DATE.
myPreparedStatement.setObject( … , dateInTokyo ) ;
Retrieval.
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
All of these issues have been covered many many times already in Stack Overflow. Search to learn more.
Related
I am using ZonedDateTime to convert GMT timestamp to America/Phoenix timestamp,
Here is my implementation
public static ZonedDateTime convertGMTTimestampToLocalTime(Timestamp gmtTime, String timeZone) throws Exception
{
ZonedDateTime atZone =Instant
.ofEpochMilli( // Parse a count of milliseconds since 1970-01-01T00:00Z.
gmtTime.getTime()
) // Returns a `Instant` object.
.atZone(
ZoneId.of( timeZone )
); // Returns a `ZonedDateTime` object.
return atZone;
}
This gives proper result when I test in local but when I test it remotely (server with docker container) it gives wrong result.
If I do a transaction at 6-Jan-2021 8:00PM (America/Phoenix) it should give 6-Jan-2021 instead it display 7-Jan-2021. My server is in MST
What am I missing, isn't the server supposed to print the date according to the specified time zone. Any help would be appreciated. Thanks.
Avoid legacy date-time classes
Never use the terrible legacy classes Timestamp, Date, Calendar, etc. These were years ago supplanted by the modern java.time classes defined in JSR 310.
Use java.time
If handed an object of that class, immediately convert to Instant. The Instant class represents a moment as seen in in UTC, with an offset of zero hours-minutes-seconds.
Instant instant = myTimestamp.toInstant() ;
Adjust to your desired time zone.
ZoneId z = ZoneId.of( "America/Phoenix" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Extract the date portion.
LocalDate ld = zdt.toLocalDate() ;
Generate text representing the value of that LocalDate object.
String iso8601 = ld.toString() ;
Or automatically localize.
Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.SHORT ).
String localized = ld.format( f ) ;
You said:
My server is in MST
First, servers should generally be kept to a default time zone of UTC, that is no zone, just an offset of zero hours-minutes-seconds.
Secondly, as a Java programmer, write your code without depending on the default time zone. Specify explicitly the desired time zone by passing the otherwise optional time zone argument to the various date-time methods.
Thirdly, MST is not a real time zone.
I'm tring to convert this date string "2021-12-10T00:00:00" into a Date but when i deserialize it i got this.
Thu Dec 09 19:00:00 COT 2021.
it seems I'm losing one day.
Can anyone help me?
"startDate": "2021-12-10T00:00:00", and the result is this
2021-12-09T19:00:00.000-0500
tl;dr
java.util.Date.from(
LocalDateTime
.parse( "2021-12-10T00:00:00" )
.atZone(
ZoneId.of( "America/Bogota" )
)
.toInstant()
)
Details
I am guessing that you are using the terrible legacy date-time classes such as Date and Calendar. Don’t. Use only java.time class.
Your input string complies with the ISO 8601 standard for date-time formats. The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.
LocalDateTime ldt = LocalDateTime.parse( "2021-12-10T00:00:00" ) ;
You said:
I'm tring to convert this date string "2021-12-10T00:00:00" into a Date
That does not make sense.
I assume by “Date”, you meant a java.until.Date. That legacy class represents a moment, a point on the timeline as seen in UTC, that is, with an offset from UTC of zero hours-minutes-seconds.
But your input lacks an indicator of time zone or offset. For example, if that string was meant to represent a moment as seen in UTC, it should have had a Z appended.
I am guessing that you assume the input was meant to represent a moment as seen in Colombia.
ZoneId z = ZoneId.of( "America/Bogota" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;
Now we have determined a moment as seen through the wall-clock time used by the people of Colombia.
Generally best to avoid java.util.Date class. But if you must, to interoperate with legacy code not yet updated to java.time, you can convert.
java.util.Date d = Date.from( zdt.toInstant() ) ;
Your start date is 2021-12-10 00:00:00 GMT+0 and your result is 2021-12-09 19:00:00 GMT-5. These times are the same. You can pass a Locale to your SimpleDataFormat constructor to be able to configure the used time zone.
DateTimes in different formats is always a problem for me. I have a date with the datatype string like "2021-07-25"
I want to convert this date to the datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00. I have the get and set property like below
private LocalDateTime relationshipStatusDate;
public LocalDateTime getRelationshipStatusDate() {
return relationshipStatusDate;
}
public void setRelationshipStatusDate(LocalDateTime relationshipStatusDate) {
this.relationshipStatusDate = relationshipStatusDate;
}
public void setRelationshipStatusDate(String time) {
if (time != null) {
try {
long epochTime = Long.parseLong(time);
this.relationshipStatusDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochTime), ZoneOffset.UTC);
} catch (NumberFormatException e){
this.relationshipStatusDate = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
}
}
}
and I am trying to format like below and its failing with an error "Unknown pattern letter T"
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddT00:00:00.000-05:00");
LocalDateTime dateTime = LocalDateTime.parse(statusDate, formatter);
Your format won't be parsable as it doesn't support default values like T00:00:00.000-05:00. You could escape literals e.g. use 'T00:00...' but that would just make the parser ignore them.
Instead, if all you get is a date then only parse a date and add the default time after that, e.g. like this:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
LocalDate date = LocalDate.parse(statusDate, formatter );
LocalDateTime dateTime = date.atStartOfDay(); //"relative" representation as the absolute instant would require adding a timezone
ZonedDateTime zonedDT = date.atSTartOfDay(ZoneOffset.UTC); //"absolute" representation of instant
I want to convert this date to the datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00.
Note the potential misconception here: LocalDateTime does NOT have a format. It represents a date and time (from a local point of reference - not in absolute terms as the timezone is missing) and provides access to individual fields such as day of month, day of week etc. but it is not formatted. Formatting is applied when you convert that date object to a string.
tl;dr
LocalDate // Represent a date-only value, without a time-of-day and without a time zone or offset-from-UTC.
.parse( "2021-07-25" ) // Parse a string in standard ISO 8601 format to instantiate a `LocalDate` object.
.atStartOfDay( // Determine the first moment of the day on that date in that zone. NB: The day does *not* always begin at 00:00, so never assume that time.
ZoneId.of( "America/Bogota" ) // Real time zones have a name in Continent/Region format. Never use 2-4 letter pseudo-zones such as `CST` or `IST`.
) // Returns a `ZonedDateTime` object, a moment in the context of a time zone.
.toOffsetDateTime() // Strips away the time zone information, leaving only a date with time-of-day in a particular offset. Returns an `OffsetDateTime` object.
.toString() // Generate text in standard ISO 8601 format.
2021-07-25T00:00-05:00
Details
LocalDateTime is the wrong class
You said:
datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00
That is a contradiction. The -05:00 at the end of your string is an offset-from-UTC. A LocalDateTime object has no offset.
You seem to misunderstand the purpose of LocalDateTime. That class does not represent a moment as seen through the wall-clock time with an offset-from-UTC used by the people of a particular region. For that purpose, use OffsetDateTime, or preferably, ZonedDateTime.
Use documentation rather than intuition when programming with unfamiliar classes. To quote the Javadoc for LocalDateTime:
A date-time without a time-zone … such as 2007-12-03T10:15:30.
…
This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, … It cannot represent an instant on the time-line …
Here is my chart to summarize the types. Three classes represent a moment, while LocalDateTime does not.
You said:
I have a date with the datatype string like "2021-07-25"
So use LocalDate to represent that value.
By default, the java.time classes use standard ISO 8601 format when parsing/generating text. So no need to specify a formatting pattern here, as your input complies with that standard.
LocalDate ld = LocalDate.parse( "2021-07-25" ) ;
You said:
I want to convert this date to … the format 2021-07-25T00:00:00.000-05:00
Your example there uses only a mere offset rather than a time zone. I suggest you use a time zone whenever possible.
An offset is simply a number of hours-minutes-seconds, nothing more. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by people of a particular region, as decided by their politicians.
ZoneId
So specify your time zone using Continent/Region naming.
ZoneId z = ZoneId.of( "America/Cancun" );
ZonedDateTime
Let java.time determine the first moment of the day on that date as seen in that time zone. Be aware that the day does not always start at 00:00. So never hard-code that time-of-day; let java.time do the work here.
ZonedDateTime zdt = ld.atStartOfDay( z ); // Determine the first moment of the day as seen in that zone. Not always 00:00.
Generate text to represent the value inside our ZonedDateTime. The ZonedDateTime#toString method generates text in a format that wisely extends the ISO 8601 format by appending the name of the time zone in square brackets.
String output = zdt.toString();
2021-07-25T00:00-05:00[America/Cancun]
Pull all that code together.
LocalDate ld = LocalDate.parse( "2021-07-25" );
ZoneId z = ZoneId.of( "America/Cancun" );
ZonedDateTime zdt = ld.atStartOfDay( z ); // Determine the first moment of the day as seen in that zone. Not always 00:00.
String output = zdt.toString();
See this code run live at IdeOne.com.
2021-07-25T00:00-05:00[America/Cancun]
OffsetDateTime
If you insist on generating text in your stated format, while omitting the name of the time zone, use OffsetDateTime.
String output = zdt.toOffSetDateTime().toString() ;
All of these topics have been covered many times on Stack Overflow. Search to learn more.
I have several time zone strings in UTC format, such as "UTC+08:00", "UTC-05:00", the question is how can i convert these utc format strings to the java.util.TimeZone in Java?
I have tried to convert by ZoneId as follows, but it did not work:
ZoneId zoneId = ZoneId.of("UTC+08:00");
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
I know TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); would work, but I do not know the mapping between "UTC+08:00" and "Asia/Shanghai"
tl;dr
Do not use TimeZone class (now legacy).
Use ZoneOffset and ZoneId instead.
Example:
ZoneOffset.of( "+08:00" )
Use java.time.ZoneId, not TimeZone
The troublesome old date-time classes bundled with the earliest versions of Java are now legacy, supplanted by the java.time classes. Among these old legacy classes is TimeZone, now supplanted by ZoneId and ZoneOffset.
An offset-from-UTC is a number of hours and minutes adjustment ahead of, or behind, UTC. This is represented by the ZoneOffset class.
A time zone is a collection of offsets, the history of changes in the offset used by a particular region in determining their wall-clock time. This is represented by the ZoneId class.
Using a time zone is always preferable to an offset as a zone has the offset plus so much more information. But your examples are only mere offsets. So use the ZoneOffset to parse the strings after deleting the characters UTC.
String input = "UTC+08:00".replace( "UTC" , "" ) ;
ZoneOffset offset = ZoneOffset.of( input ) ;
Do not guess the time zone
You cannot assume that a particular offset implies a particular time zone. Many zones may have used a particular offset in the past, present, or future. So you should not guess the zone.
Take, for example, the offset of +08:00. That offset is currently used by several different time zones including Asia/Shangai, Asia/Macao, and Australia/Perth.
If you are certain a particular zone was intended for a date-time value, apply it to get a ZonedDateTime. But do not guess.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.now() ;
ZoneId z = ZoneId.of( "Asia/Shanghai" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
If you do not know for certain the intended time zone and have only an offset, use the offset to get an OffsetDateTime.
Instant instant = Instant.now() ;
ZoneOffset offset = ZoneOffset.of( "+08:00" ) ;
OffsetDateTime odt = instant.atOffset( offset ) ;
Convert
Best to avoid the old legacy class TimeZone. But if you must use that class to work with old code not yet updated for the java.time classes, you can convert to/from a ZoneId. Use the new conversion methods added to the old classes.
TimeZone myLegacyTimeZone = TimeZone.getTimeZone( myModernZoneId );
…and…
ZoneId z = myLegacyTimeZone.toZoneId() ;
Note that ZoneOffset is a subclass of ZoneId. Normally, we ignore that inheritance relationship. If you have only a mere offset such as +08:00, use ZoneOffset. If you have a full time zone such as Asia/Shanghai, use ZoneId. One exception to this rule is for this conversion to/from TimeZone where only the superclass ZoneId is recognized.
If you strip the UTC, you can parse it as a ZoneOffset, which extends ZoneId
ZoneId zoneId = ZoneOffset.of("+08:00")
Since you can use the modern classes in the java.time package, I recommend you stick with them and avoid the outdated classes like TimeZone, SimpleDateFormat and Date. I am mostly repeating what #Basil Bourque already said in his answer, but also wanted to demonstrate how nicely his suggestion fits into your context:
DateTimeFormatter format = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
ZonedDateTime dateTime = LocalDateTime.parse(dateTimeString, format).atZone(zoneId);
Instant i = dateTime.toInstant();
System.out.println(dateTime + " -> " + i);
I have also demonstrated that you may convert the ZonedDateTime to an Instant in case you need that. The snippet prints
2017-05-05T05:05+08:00[UTC+08:00] -> 2017-05-04T21:05:00Z
If you are sure your date-time string and your zone string belong together, there is no need to go through String.replace() for removing UTC from the beginning of the zone string.
I am parsing the string independently of the time zone and then combining it with the zone offset information afterward. I think it’s more natural than having to know the zone for parsing.
In case you need an oldfashioned Date, for example for a call to some legacy code, that’s easy enough:
Date d = Date.from(i);
The old classes are troublesome
Even though I know the old classes have a tendency to show unwanted behaviour without telling you that anyting is wrong, I was still negatively surprised to learn that the code in your question didn’t work. It gives a time zone of GMT! It’s documented that this is a possibility, though, in the documentation of TimeZone.getTimeZone(ZoneId):
Returns:
the specified TimeZone, or the GMT zone if the given ID cannot be understood.
One may stil wonder how a simple time zone like UTC+08:00 can be “not understood”.
This may be an extremely simple typo that I have in my code, but I just can't make it work no matter what I do.
I have the following code:
String date="2014-05-22";
String time="09:09:04.145"
String dateAndTime=date.concat(" "+ time);
DateFormat convertDate=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
Date dateOfMeasurement=convertDate.parse(dateAndTime);
I have tried using yyyy-MM-dd'T'HH:mm:ss:SSSZ and yyyy-MM-dd HH:mm:ss:SSS for the formatting, but nothing works.
What am I doing wrong?
Thank you very much.
java.time
You are using troublesome old legacy date-time classes now supplanted by the java.time classes.
ISO 8601
Your inputs happen to comply with ISO 8601 standard. The java.time classes use ISO 8601 formats by default when parsing/generating Strings that represent date-time values. So no need to specify a formatting pattern.
LocalDate ld = LocalDate.parse( "2014-05-22" );
LocalTime lt = LocalTime.parse( "09:09:04.145" );
LocalDateTime ldt = LocalDateTime.of( ld , lt );
Time zones
A LocalDateTime has no concept of time zone or offset-from-UTC. So it does not represent a moment on the timeline.
For an actual moment, assign a time zone. Perhaps you intended the inputs to represent a moment in UTC.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
Or perhaps you know from the context that another particular time zone was intended.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( z );
Strings
The format you were attempting to assemble is also defined by the ISO 8601 standard. Simply call toString to generate such a value.
An Instant is a basic building-block class in java.time, representing a moment on the timeline in UTC with a resolution up to nanoseconds.
String output = zdt.toInstant().toString(); // 2016-09-08T02:46:15Z
The ZonedDateTime extends standard format by wisely appending the name of the time zone in square brackets.
String output = zdt.toString(); // 2016-09-07T23:46:15-03:00[America/Montreal]
DateFormat convertDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date dateOfMeasurement=convertDate.parse(dateAndTime);