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.
Time information is missing when converting LocalDate to java.util.Date
My input date is in the format "2019-08-30T19:47:22+00:00" (String). I have to convert it to java.util.Data. I would like to do it using java 8.
String input = "2019-08-30T19:47:22+00:00";
Date date = null;
LocalDate dateTime = LocalDate.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
date = Date.from(dateTime.atStartOfDay(ZoneId.systemDefault()).toInstant());
System.out.println("Parsed from LoacalDate: " + date);
System.out.println("From new Date(): " + new Date());
Output
Parsed from LoacalDate: Fri Aug 30 00:00:00 CDT 2019 From new
Date(): Fri Aug 30 17:16:23 CDT 2019
In the output time information is missing. How to get time information?
tl;dr
LocalDate stores only a date, so wrong class to use.
java.util.Date // Terrible class, now legacy. Avoid. Replaced by `java.time.Instant` in JSR 310. Use only where required, to interoperate with old code not yet updated for java.time.
.from( // Convert from modern `Instant` to legacy `Date`.
OffsetDateTime // The modern class to represent a moment in the context of an offset-from-UTC (a number of hours-minutes-seconds). Not to be confused with a time zone, which is a history of changes to the offset used by the people of a particular region.
.parse( "2019-08-30T19:47:22+00:00" ) // Parse text into an `OffsetDateTime` object.
.toInstant() // Extract an `Instant`, a more basic building-block class that represent a moment in UTC (an offset of zero).
)
.toString() // Generates text representing the value of the `Date`. But this method lies! It dynamically applies your JVM’s current default time zone while generating the text.
Note that java.util.Date::toString tells a lie! That method dynamically applies the JVM’s current default time zone while generating the text to represent the value that is actually in UTC. One of many reasons to never use this Date class.
Details
You are using the wrong classes.
LocalDate
LocalDate represents a date only, no time-of-day, no time zone or offset.
By parsing your input representing a moment as simply a date, you are lopping off the time-of-day and the offset-from-UTC.
OffsetDateTime
Your input "2019-08-30T19:47:22+00:00" represents a moment: a date, a time-of-day, an offset-from-UTC of zero hours-minutes-seconds. So parse that as a OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.parse( "2019-08-30T19:47:22+00:00" ) ;
Avoid legacy date-time classes
The java.util.Date class is terrible, and should no longer be used. It was supplanted years ago by the modern java.time classes as of the adoption of JSR 310.
However, if you must interoperate with old code not yet updated to java.time, you can convert back-and-forth. Look to new to…/from… methods added to the old classes.
The equivalent of java.util.Date is java.time.Instant, both representing a moment in UTC (though with a difference in resolution, milliseconds versus nanoseconds). So extract a basic Instant object from our more flexible OffsetDateTime.
Instant instant = odt.toInstant() ;
java.util.Date d = Date.from( instant ) ;
I was able to make it work using the following code snippet
LocalDateTime dateTime = LocalDateTime.parse(input,DateTimeFormatter.ISO_OFFSET_DATE_TIME);
date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("Parsed from LoacalDate:" + date);
System.out.println("From new Date():" + new Date());
I am trying to convert a Calendar instance to instant and i am doing that as follows
Calendar cal = new GregorianCalendar();
System.out.println(cal.getTime());
Instant inst = cal.toInstant();
System.out.println(inst.toString());
And, the output for it is as follows
Fri Oct 30 17:23:40 IST 2015
2015-10-30T11:53:40.037Z
So, my question is about the difference in the outputs from the Calendar and the instant. The output from the instant is in GMT instead of the local time.
I have not seen any documentation that says that Instant class only gives the date in GMT. So, i am not sure why this is happening.
Invoking toString on an Instant produces output like the following:
2013-05-30T23:38:23.085Z
This format follows the ISO-8601 standard for representing date and
time.
Have a look at:
Tutorial
Instant class documentation
The answer by Rawat is correct. An Instant is always in UTC by definition.
The format of the string representation generated by the toString method is standard, defined by ISO 8601.
The Instant class is part of the java.time framework built into Java 8 and later. The new classes in this framework supplant the old java.util.Date/.Calendar classes from the early days of Java.
Avoid the old classes as they are notoriously troublesome. Do not mix up the old and new. The conversion methods such as toInstant are intended only for use when interoperating with old code has not yet been updated for the new types.
To adjust an Instant into a time zone, specify the time zone via a ZoneId object to produce a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Do not be confused by the "Local" in LocalDateTime class. That class represents the vague idea of a date-time. It does not apply to any particular time zone and is therefore not tied to the timeline.
I am trying to parse a String into Date.
I am using
new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS").parse("20140923-14:32:34.456")
My output is: (Date object)
Tue Sep 23 14:32:34 EDT 2014
I dont understand why I do not get the milliseconds. I need the date along with milliseconds and not just the milliseconds.
Expected Output: (Date object)
Tue Sep 23 14:32:34.456 EDT 2014
thanks
PS: I dont want a string object in the end but a Date.
You're just printing out the result of calling Date.toString() which happens not to include the milliseconds. If you print out the result of calling getTime() on the date, you'll see that it ends in "456", showing that it has parsed the milliseconds.
Also, don't be fooled by the "EDT" part of the output of Date.toString(). It doesn't mean that this is a Date object "in" EDT... a Date doesn't have a time zone at all; it's just a number of milliseconds since the unix epoch.
I'd advise you to explicitly set the time zone of your SimpleDateFormat, however - even if you deliberately set it to the system default time zone. By doing it explicitly, you make it clear to the reader that you meant to use that time zone, rather than just that you failed to think about it. You should take the same approach to the locale, too - in this case I'd probably specify Locale.US, which is usually a good bet for formats which are designed more for machine-parsing than humans.
You need to use the format when printing the date you just read too:
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS")
Date date = sdf.parse("20140923-14:32:34.456");
System.out.println(date); // Tue Sep 23 14:32:34 EDT 2014
System.out.println(sdf.format(date)); // 20140923-14:32:34.456
Your Date object does already contain the correct milliseconds. It's just that, when you are writing the Date object back out again, the default String representation of a Date does not include milliseconds.
You presumably have:
DateFormat myFormat = new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS");
Date myDate = myFormat.parse("20140923-14:32:34.456");
To output the date with milliseconds, you should use:
System.out.println(myFormat.format(myDate));
Instead of reusing myFormat, you could use a different DateFormat that also includes milliseconds in its specification.
The answer by Jon Skeet is correct.
Joda-Time
I will add some example code using the Joda-Time library.
The java.util.Date, java.util.Calendar, and java.text.SimpleDateFormat classes bundled with Java are notoriously troublesome, flawed in both design and implementation. Avoid them. Use either Joda-Time or the java.time package in Java 8 (inspired by Joda-Time, defined by JSR 310).
In both Joda-Time and java.time, a date-time object knows its own assigned time zone, unlike in java.util.Date. Another difference is that both those good libraries use immutable objects where we instantiate fresh objects based on original object rather than alter ("mutate") the original.
DateTimeZone timeZone = DateTimeZone.UTC; // Or DateTimeZone.forID( "America/Montreal" )
DateTimeFormatter formatter = DateTimeFormat.forPattern( "yyyyMMdd-HH:mm:ss.SSS" ).withZone( timeZone );
DateTime dateTime = formatter.parseDateTime("20140923-14:32:34.456"); // Parsed *and* assigned the specified time zone.
String output = dateTime.toString();
You can convert to another time zone.
DateTime dateTimeKolkata = dateTime.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );
If you really need a java.util.Date, perhaps required by other classes, convert from Joda-Time.
java.util.Date date = dateTime.toDate();
I have a Timestamp value that comes from my application. The user can be in any given local TimeZone.
Since this date is used for a WebService that assumes the time given is always in GMT, I have a need to convert the user's parameter from say (EST) to (GMT). Here's the kicker: The user is oblivious to his TZ. He enters the creation date that he wants to send to the WS, so what I need is:
User enters: 5/1/2008 6:12 PM (EST)
The parameter to the WS needs to be: 5/1/2008 6:12 PM (GMT)
I know TimeStamps are always supposed to be in GMT by default, but when sending the parameter, even though I created my Calendar from the TS (which is supposed to be in GMT), the hours are always off unless the user is in GMT. What am I missing?
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
...
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) {
java.util.Calendar cal = java.util.Calendar.getInstance(
GMT_TIMEZONE, EN_US_LOCALE);
cal.setTimeInMillis(ts_.getTime());
return cal;
}
With the previous Code, this is what I get as a result (Short Format for easy reading):
[May 1, 2008 11:12 PM]
public static Calendar convertToGmt(Calendar cal) {
Date date = cal.getTime();
TimeZone tz = cal.getTimeZone();
log.debug("input calendar has date [" + date + "]");
//Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = date.getTime();
//gives you the current offset in ms from GMT at the current date
int offsetFromUTC = tz.getOffset(msFromEpochGmt);
log.debug("offset is " + offsetFromUTC);
//create a new calendar in GMT timezone, set to this date and add the offset
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.setTime(date);
gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);
log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]");
return gmtCal;
}
Here's the output if I pass the current time ("12:09:05 EDT" from Calendar.getInstance()) in:
DEBUG - input calendar has date [Thu Oct 23 12:09:05 EDT 2008]
DEBUG - offset is -14400000
DEBUG - Created GMT cal with date [Thu Oct 23 08:09:05 EDT 2008]
12:09:05 GMT is 8:09:05 EDT.
The confusing part here is that Calendar.getTime() returns you a Date in your current timezone, and also that there is no method to modify the timezone of a calendar and have the underlying date rolled also. Depending on what type of parameter your web service takes, your may just want to have the WS deal in terms of milliseconds from epoch.
Thank you all for responding. After a further investigation I got to the right answer. As mentioned by Skip Head, the TimeStamped I was getting from my application was being adjusted to the user's TimeZone. So if the User entered 6:12 PM (EST) I would get 2:12 PM (GMT). What I needed was a way to undo the conversion so that the time entered by the user is the time I sent to the WebServer request. Here's how I accomplished this:
// Get TimeZone of user
TimeZone currentTimeZone = sc_.getTimeZone();
Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE);
// Get the Offset from GMT taking DST into account
int gmtOffset = currentTimeZone.getOffset(
currentDt.get(Calendar.ERA),
currentDt.get(Calendar.YEAR),
currentDt.get(Calendar.MONTH),
currentDt.get(Calendar.DAY_OF_MONTH),
currentDt.get(Calendar.DAY_OF_WEEK),
currentDt.get(Calendar.MILLISECOND));
// convert to hours
gmtOffset = gmtOffset / (60*60*1000);
System.out.println("Current User's TimeZone: " + currentTimeZone.getID());
System.out.println("Current Offset from GMT (in hrs):" + gmtOffset);
// Get TS from User Input
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
System.out.println("TS from ACP: " + issuedDate);
// Set TS into Calendar
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
// Adjust for GMT (note the offset negation)
issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset);
System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: "
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
.format(issueDate.getTime()));
The code's output is: (User entered 5/1/2008 6:12PM (EST)
Current User's TimeZone: EST
Current Offset from GMT (in hrs):-4 (Normally -5, except is DST adjusted)
TS from ACP: 2008-05-01 14:12:00.0
Calendar Date converted from TS using GMT and US_EN Locale: 5/1/08 6:12 PM (GMT)
You say that the date is used in connection with web services, so I assume that is serialized into a string at some point.
If this is the case, you should take a look at the setTimeZone method of the DateFormat class. This dictates which time zone that will be used when printing the time stamp.
A simple example:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Calendar cal = Calendar.getInstance();
String timestamp = formatter.format(cal.getTime());
You can solve it with Joda Time:
Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false));
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));
Java 8:
LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30");
ZonedDateTime fromDateTime = localDateTime.atZone(
ZoneId.of("America/Toronto"));
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(
ZoneId.of("Canada/Newfoundland"));
It looks like your TimeStamp is being set to the timezone of the originating system.
This is deprecated, but it should work:
cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset());
The non-deprecated way is to use
Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)
but that would need to be done on the client side, since that system knows what timezone it is in.
Method for converting from one timeZone to other(probably it works :) ).
/**
* Adapt calendar to client time zone.
* #param calendar - adapting calendar
* #param timeZone - client time zone
* #return adapt calendar to client time zone
*/
public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) {
Calendar ret = new GregorianCalendar(timeZone);
ret.setTimeInMillis(calendar.getTimeInMillis() +
timeZone.getOffset(calendar.getTimeInMillis()) -
TimeZone.getDefault().getOffset(calendar.getTimeInMillis()));
ret.getTime();
return ret;
}
Date and Timestamp objects are timezone-oblivious: they represent a certain number of seconds since the epoch, without committing to a particular interpretation of that instant as hours and days.
Timezones enter the picture only in GregorianCalendar (not directly needed for this task) and SimpleDateFormat, which need a timezone offset to convert between separate fields and Date (or long) values.
The OP's problem is right at the beginning of his processing: the user inputs hours, which are ambiguous, and they are interpreted in the local, non-GMT timezone; at this point the value is "6:12 EST", which can be easily printed as "11.12 GMT" or any other timezone but is never going to change to "6.12 GMT".
There is no way to make the SimpleDateFormat that parses "06:12" as "HH:MM" (defaulting to the local time zone) default to UTC instead; SimpleDateFormat is a bit too smart for its own good.
However, you can convince any SimpleDateFormat instance to use the right time zone if you put it explicitly in the input: just append a fixed string to the received (and adequately validated) "06:12" to parse "06:12 GMT" as "HH:MM z".
There is no need of explicit setting of GregorianCalendar fields or of retrieving and using timezone and daylight saving time offsets.
The real problem is segregating inputs that default to the local timezone, inputs that default to UTC, and inputs that really require an explicit timezone indication.
Something that has worked for me in the past was to determine the offset (in milliseconds) between the user's timezone and GMT. Once you have the offset, you can simply add/subtract (depending on which way the conversion is going) to get the appropriate time in either timezone. I would usually accomplish this by setting the milliseconds field of a Calendar object, but I'm sure you could easily apply it to a timestamp object. Here's the code I use to get the offset
int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();
timezoneId is the id of the user's timezone (such as EST).
java.time
The modern approach uses the java.time classes that supplanted the troublesome legacy date-time classes bundled with the earliest versions of Java.
The java.sql.Timestamp class is one of those legacy classes. No longer needed. Instead use Instant or other java.time classes directly with your database using JDBC 4.2 and later.
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 ) ;
If you must interoperate with an existing Timestamp, convert immediately into java.time via the new conversion methods added to the old classes.
Instant instant = myTimestamp.toInstant() ;
To adjust into another time zone, specify the time zone as a ZoneId object. Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter pseudo-zones such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
Apply to the Instant to produce a ZonedDateTime object.
ZonedDateTime zdt = instant.atZone( z ) ;
To generate a string for display to the user, search Stack Overflow for DateTimeFormatter to find many discussions and examples.
Your Question is really about going the other direction, from user data-entry to the date-time objects. Generally best to break your data-entry into two parts, a date and a time-of-day.
LocalDate ld = LocalDate.parse( dateInput , DateTimeFormatter.ofPattern( "M/d/uuuu" , Locale.US ) ) ;
LocalTime lt = LocalTime.parse( timeInput , DateTimeFormatter.ofPattern( "H:m a" , Locale.US ) ) ;
Your Question is not clear. Do you want to interpret the date and the time entered by the user to be in UTC? Or in another time zone?
If you meant UTC, create a OffsetDateTime with an offset using the constant for UTC, ZoneOffset.UTC.
OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;
If you meant another time zone, combine along with a time zone object, a ZoneId. But which time zone? You might detect a default time zone. Or, if critical, you must confirm with the user to be certain of their intention.
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
To get a simpler object that is always in UTC by definition, extract an Instant.
Instant instant = odt.toInstant() ;
…or…
Instant instant = zdt.toInstant() ;
Send to your database.
myPreparedStatement.setObject( … , instant ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
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.