I'm having trouble to figure out what is this date format: 2019-02-28T12:17:46.279+0000. I have tried different date formats to get this result but nothing worked. Closest pattern was: yyyy-MM-dd'T'HH:mm:ss.SSSZ But with this pattern output was like this: 2019-02-28T12:17:46.279-0000 (- is after seconds instead of +)
I get this exception:
Caused by: java.lang.IllegalArgumentException: 2019-02-28T12:17:46.279+0000
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl$Parser.skip(XMLGregorianCalendarImpl.java:2932)
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl$Parser.parse(XMLGregorianCalendarImpl.java:2898)
at org.apache.xerces.jaxp.datatype.XMLGregorianCalendarImpl.<init>(XMLGregorianCalendarImpl.java:478)
at org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl.newXMLGregorianCalendar(DatatypeFactoryImpl.java:230)
at __redirected.__DatatypeFactory.newXMLGregorianCalendar(__DatatypeFactory.java:132)
at javax.xml.bind.DatatypeConverterImpl.parseDate(DatatypeConverterImpl.java:519)
at javax.xml.bind.DatatypeConverter.parseDate(DatatypeConverter.java:431)
at eu.europa.ec.my.custom.package.model.mapper.XsdDateTimeConverter.unmarshal(XsdDateTimeConverter.java:23)
My XsdDateTimeConverter class looks like this:
public class XsdDateTimeConverter {
public static Date unmarshal(String dateTime) {
return DatatypeConverter.parseDate(dateTime).getTime();
}
public static String marshalDate(Date date) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return DatatypeConverter.printDate(calendar);
}
public static String marshalDateTime(Date dateTime) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(dateTime);
return DatatypeConverter.printDateTime(calendar);
}
}
And parsed date in my postgres db looks like this:
move_timestamp timestamp(6) with time zone
2019-02-28 12:17:46.279+00
In my rest method I use ObjectMapper like this.
MyCustomResponseDto responseDto = customService.getCustomResponseDto(query);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
String strValue = mapper.writeValueAsString(responseDto);
return Response.ok(strValue).build();
I guess what I really wanted is what is the right pattern for this date. I can go in this page: http://www.sdfonlinetester.info/ and enter my pattern (e.g. yyyy-MM-dd'T'HH:mm:ss.SSSZ) and it gives you an actual date output for that pattern. I need the other way around. I want to enter my date and it will give me the right pattern for it.
tl;dr
OffsetDateTime.parse(
"2019-02-28T12:17:46.279+0000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" , Locale.ROOT )
)
java.time
You are using terrible Calendar class that was supplanted years ago by the java.time classes.
ISO 8601
Your input string is in standard ISO 8601 format, designed for human-readable machine-parseable textual representations of date-time values. That is a good thing.
The java.time classes use ISO 8601 formats by default when parsing/generating strings.
OffsetDateTime
You should be able to simply parse with OffsetDateTime.
OffsetDateTime.parse( "2019-02-28T12:17:46.279+0000" )
…but unfortunately the optional COLON character being omitted from the offset (+00:00) is a problem. The OffsetDateTime class has a minor bug where it refuses to parse without that character. The bug is discussed here and here.
The ISO 8601 standard permits the colon’s absence, but practically you should always include it. The OffsetDateTime class is not alone; I have seen other libraries that break when the colon or padding zeros are absent. I suggest asking the publisher of your data to use the full ISO 8601 format including the colon.
The workaround for the OffsetDateTime bug is to define a DateTimeFormatter explicitly.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ss.SSSX" , Locale.ROOT ) ;
Then parse.
String input = "2019-02-28T12:17:46.279+0000" ;
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
To generate text in full standard ISO 8601 format, simply call toString.
String output = odt.toString() ;
See this code run live at IdeOne.com.
output: 2019-02-28T12:17:46.279Z
The Z on the end means UTC, that is +0000 or +00:00. Pronounced “Zulu”. Very commonly used, more immediately readable than the numeric offset.
If you want same output format as your input, use the same DateTimeFormatter.
String output = odt.format( f ) ;
You may try below code
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH);
String lastmod = format.format(new Date());
Save yourself a mountain of trouble and save the epochtime in millis. Only parse and render dates in UIs. Very very few cases of scheduling for humans require a computer to know hours, day, week, month, year... But saving an instant in time is just a 'long'.
Related
I have a situation where I need to convert one Java date time format into another but have been having just a bear of a time doing so. Been searching for solutions a long time and have tried many things that have just not worked but I'm at my wits end :(.
I have to convert from
yyyy-MM-dd'T'HH:mm:ss
to
MM/dd/yyyy HH:mm:ss
This is the last thing I've tried, but alas it has no effect at all at transforming the pattern:
private Instant convertInstantFormat(Instant incomingDate) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AUTH_DATE_PATTERN)
.withZone(ZoneId.systemDefault());
return Instant.parse(formatter.format(incomingDate));
}
Where
AUTH_DATE_PATTERN = "MM/dd/yyyy HH:mm:ss";
incomingDate = 2021-10-22T06:39:13Z
and outgoing date = 2021-10-22T06:39:13Z
I'm sure this is probably just the most naive attempt.
I've tried standardizing the date format and then reformatting, but no go.
I'm just sort of out of steam.
As always, any and all help from this incredible community is tremendously appreciated!
UPDATE
I just wanted to point out that the input and output to this method are of type "Instant."
Apologies for not making this clear initially.
I have to convert from yyyy-MM-dd'T'HH:mm:ss to MM/dd/yyyy HH:mm:ss
Your incoming date time format is ISO_LOCAL_DATE_TIME.
String datetime = "2021-12-16T16:22:34";
LocalDateTime source = LocalDateTime.parse(datetime,DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// desired output format
String AUTH_DATE_PATTERN = "MM/dd/yyyy HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(AUTH_DATE_PATTERN);
String output = source.format(formatter);
System.out.println(output);
prints
12/16/2021 16:22:34
If your incoming date is 2021-10-22T06:39:13Z that is a zoned date time and can be parsed-from/formatted-to using
DateTimeFormatter.ISO_ZONED_DATE_TIME.
tl;dr
Instant // Represents a point on the time line.
.parse( "2021-10-22T06:39:13Z" ) // Returns an `Instant` object. By default, parses text in standard ISO 8601 for at where `Z` means offset of zero.
.atOffset( ZoneOffset.UTC ) // Returns an `OffsetDateTime` object.
.format(
DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" )
) // Returns a `String` object.
See this code run live at IdeOne.com.
10/22/2021 06:39:13
Details
You said:
incomingDate = 2021-10-22T06:39:13Z
Your formatting pattern fails to match your input string.
Your input string happens to comply with the ISO 8691 format used by default in Instant.parse. So no need to specify a formatting pattern.
Instant instant = Instant.parse( "2021-10-22T06:39:13Z" ) ;
The Z on the end means an offset of zero hours-minutes-seconds from the prime meridian of UTC.
You asked to generate text representing that moment in the format of MM/dd/yyyy HH:mm:ss. I recommend including an indicator of the offset or time zone. But it you insist on omitting that, read on.
Convert from the basic class Instant to the more flexible OffsetDateTime class.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
Specify your formatting pattern.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" ) ;
Generate your desired text.
String output = odt.format( f ) ;
To learn more, search Stack Overflow. These topics have already been addressed many times.
Append timezone 'z' info at the end of the format pattern otherwise parsing throws an exception.
Here's a working example.
Unit Test (Passing)
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class DateFormatConverterTest {
#Test
void convertDate() {
final String incomingDate = "2021-10-22T06:39:13Z";
final String expectedOutgoingDate = "2021/10/22T06:39:13Z";
String actualOutgoingDate = new DateFormatConverter().convertDate(incomingDate);
assertThat(actualOutgoingDate).isEqualTo(expectedOutgoingDate);
}
}
Implementation
import java.time.format.DateTimeFormatter;
public class DateFormatConverter {
private static final DateTimeFormatter INCOMING_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssz");
private static final DateTimeFormatter OUTGOING_DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy/MM/dd'T'HH:mm:ssz");
String convertDate(String incoming) {
return OUTGOING_DATE_TIME_FORMAT.format(INCOMING_DATE_TIME_FORMAT.parse(incoming));
}
}
In my spring boot application I have to convert ISO 8601 datetime to localdatetime without using JODA. Currently what I am doing is
String receivedDateTime = "2019-11-13T00:11:08+05:00";
ZonedDateTime zonedDateTime = ZonedDateTime.parse(receivedDateTime);
DateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
utcFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = new Date();
try {
date = utcFormat.parse(zonedDateTime.toString());
} catch (ParseException e) {
e.printStackTrace();
}
When I am using receivedDateTime with +00:00 like "2019-11-13T00:11:08+00:00" then it does not give any parsing error but not converting either. When I use +01:00 at the end then it also gives the parsing error.
UPDATE: 1
As per #Deadpool answer, I am using it like
String receivedDateTime = "2019-11-13T00:11:08+05:00";
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
.optionalStart().appendOffset("+HHMM", "0000").optionalEnd()
.toFormatter();
OffsetDateTime dt = OffsetDateTime.parse(receivedDateTime, formatter);
LocalDateTime ldt = dt.toLocalDateTime();
System.out.println(ldt);
and the the value of ldt it print is 2019-11-13T00:11:08.
UPDATE 2:
I tried using C# the same example and it gives me this date time {2019-11-12 11:11:08 AM}, which looks correct as the input time GMT +5 Hours and local time is EST America. So, when it converted it then it went back to 12th of Nov. Here is the code
var timeString = "2019-11-13T00:11:08+05:00";
DateTime d2 = DateTime.Parse(timeString, null, System.Globalization.DateTimeStyles.RoundtripKind);
Console.WriteLine("Hello World!" + d2);
UPDATE 3: So it boils down to following solution input String "2019-11-13T06:01:41+00:00" and output is local date "2019-11-13T00:01:41" Where system defauld ZoneId is "America/Chicago" which is -06:00 GMT
private LocalDateTime convertUtcStringToLocalDateTime(String UtcDateTime) {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
.optionalStart().appendOffset("+HHMM", "0000").optionalEnd()
.toFormatter();
OffsetDateTime dateTime = OffsetDateTime.parse(UtcDateTime, formatter);
return dateTime.atZoneSameInstant(ZoneId.of(ZoneId.systemDefault().getId())).toLocalDateTime();
}
Using java.time alone this is simpler than you seem to think:
String receivedDateTime = "2019-11-13T00:11:08+05:00";
OffsetDateTime parsedDateTime = OffsetDateTime.parse(receivedDateTime);
ZonedDateTime dateTimeInMyTimeZone
= parsedDateTime.atZoneSameInstant(ZoneId.systemDefault());
System.out.println(dateTimeInMyTimeZone);
When I ran this in America/Toronto time zone, the output was:
2019-11-12T14:11:08-05:00[America/Toronto]
Since your string contains an offset, +05:00, and no time zone, like Asia/Karachi, use an OffsetDateTime for parsing it. Then convert to your local time zone using the atZoneSameInstant method. Even though you asked for your local time, don’t be fooled into using LocalDateTime. That class represent a date and time without any time zone, which is not what you need (and seldom needed at all).
Fortunately it’s easy to avoid the old classes SimpleDateFormat, DateFormat, TimeZone and Date. They were always poorly designed, the first two in particular are notoriously troublesome. They are all long outdated now. Instead get all the functionality we dream of from java.time, the modern Java date and time API.
What happened in your code?
Don’t use 'Z' in a format pattern string (and I repeat, don’t use SimpleDateFormat).
No matter if you use ZonedDateTime or OffsetDateTime, when you use toString with offset zero (as parsed from +00:00), the offset is printed as Z, which matches the 'Z' in your format pattern string, so your second parsing works. Only parsing once, converting back to string and parsing again is needlessly complicated. Worse when the original offset was +01:00 or +05:00. These are rendered the same again from toString, so don’t match 'Z', which caused your ParseException. Never use 'Z' in a format pattern string. Z denotes an offset of zero and needs to be parsed as an offset for you to get the correct result.
By using DateTimeFormatter you can customize the date format with different offset format by making them optional
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
.optionalStart().appendOffset("+HHMM", "0000").optionalEnd()
.toFormatter();
And the use the OffsetDateTime to parse string representing with offset
A date-time with an offset from UTC/Greenwich in the ISO-8601 calendar system, such as 2007-12-03T10:15:30+01:00.
OffsetDateTime dateTime = OffsetDateTime.parse("2019-11-13T00:11:08+0000", formatter);
OffsetDateTime dateTime = OffsetDateTime.parse("2019-11-13T00:11:08+05:00", formatter);
If you want to convert it into local time zone time LocalDateTime then use atZoneWithSameInstant()
LocalDateTime local = dateTime.atZoneSameInstant(ZoneId.of("America/New_York")).toLocalDateTime()
Note : Don't use SimpleDateFormat and util.Date which are legacy old framework
I have a java component to format the date that I retrieve. Here is my code:
Format formatter = new SimpleDateFormat("yyyyMMdd");
String s = "2019-04-23 06:57:00.0";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss.S");
try
{
Date date = simpleDateFormat.parse(s);
System.out.println("Formatter: "+formatter.format(date));
}
catch (ParseException ex)
{
System.out.println("Exception "+ex);
}
The code works great as long as the String s has the format "2019-04-23 06:57:00.0";
My Question is, how to tweak this code so it will work for below scenarios ex,
my s string may have values like
String s = "2019-04-23 06:57:00.0";
or
String s = "2019-04-23 06:57:00";
Or
String s = "2019-04-23";
right now it fails if I don't pass the ms.. Thanks!
Different types
String s = "2019-04-23 06:57:00";
String s = "2019-04-23";
These are two different kinds of information. One is a date with time-of-day, the other is simply a date. So you should be parsing each as different types of objects.
LocalDateTime.parse
To comply with the ISO 8601 standard format used by default in the LocalDateTime class, replace the SPACE in the middle with a T. I suggest you educate the publisher of your data about using only ISO 8601 formats when exchanging date-time values as text.
LocalDateTime ldt1 = LocalDateTime.parse( "2019-04-23 06:57:00".replace( " " , "T" ) ) ;
The fractional second parses by default as well.
LocalDateTime ldt2 = LocalDateTime.parse( "2019-04-23 06:57:00.0".replace( " " , "T" ) ) ;
See this code run live at IdeOne.com.
ldt1.toString(): 2019-04-23T06:57
ldt2.toString(): 2019-04-23T06:57
LocalDate.parse
Your date-only input already complies with ISO 8601.
LocalDate ld = LocalDate.parse( "2019-04-23" ) ;
See this code run live at IdeOne.com.
ld.toString(): 2019-04-23
Date with time-of-day
You can strip out the time-of-day from the date.
LocalDate ld = ldt.toLocalDate() ;
And you can add it back in.
LocalTime lt = LocalTime.parse( "06:57:00" ) ;
LocalDateTime ldt = ld.with( lt ) ;
Moment
However, be aware that a LocalDateTime does not represent a moment, is not a point on the timeline. Lacking the context of a time zone or offset-from-UTC, a LocalDateTime cannot hold a moment, as explained in its class JavaDoc.
For a moment, use the ZonedDateTime, OffsetDateTime, or Instant classes. Teach the publisher of your data to include the offset, preferably in UTC.
Avoid legacy date-time classes
The old classes SimpleDateFormat, Date, and Calendar are terrible, riddled with poor design choices, written by people not skilled in date-time handling. These were supplanted years ago by the modern java.time classes defined in JSR 310.
In case of you have optional parts in pattern you can use [ and ].
For example
public static Instant toInstant(final String timeStr){
final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH[:mm[:ss[ SSSSSSSS]]]")
.withZone(ZoneId.of("UTC"));
try {
return Instant.from(formatter.parse(timeStr));
}catch (DateTimeException e){
final DateTimeFormatter formatter2 = DateTimeFormatter
.ofPattern("yyyy-MM-dd")
.withZone(ZoneId.of("UTC"));
return LocalDate.parse(timeStr, formatter2).atStartOfDay().atZone(ZoneId.of("UTC")).toInstant();
}
}
cover
yyyy-MM-dd
yyyy-MM-dd HH
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss SSSSSSSS
I'm trying to convert a String value (initially a LocalDateTime variable) that was stored in a database (as datetime) and parse it into a LocalDateTime variable. I've tried it with a formatter:
String dTP;
dTP=(rs.getString("arrivedate"));
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
LocalDateTime dateTimeParked = LocalDateTime.parse(dTP,formatter);
And without a formatter:
String dTP;
dTP=(rs.getString("arrivedate"));
LocalDateTime dateTimeParked = LocalDateTime.parse(dTP);
But I get the same error each time:
Exception in thread "AWT-EventQueue-0" java.time.format.DateTimeParseException: Text '2016-07-09 01:30:00.0' could not be parsed at index 10
My thinking is that index 10 is the space between date and time.
Could anyone help me with this? I've been at it for hours :(
There is a error in the format of the that causes the issue. Please refer https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html.The ISO date time is of the format '2011-12-03T10:15:30' . The following will give you the idea
public static void main(String[] args) throws Exception {
String isoDate = "2016-07-09T01:30:00.0";
// ISO Local Date and Time '2011-12-03T10:15:30'
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
LocalDateTime dateTimeParked = LocalDateTime.parse(isoDate, formatter);
System.out.println(dateTimeParked);
String date = "2016-07-09 01:30:00.0";
DateTimeFormatter formatterNew = DateTimeFormatter.ofPattern("yyyy-LL-dd HH:mm:ss.S");
LocalDateTime dateTimeParkedNew = LocalDateTime.parse(date, formatterNew);
System.out.println(dateTimeParkedNew);
}
This prints :
2016-07-09T01:30
2016-07-09T01:30
The other answers are correct, your string is in SQL format which differs from the canonical version of ISO 8601 format by using a space character in the middle rather than a T. So either replace the space with a T or define a formatting pattern for parsing.
Use smart objects, not dumb strings
But the bigger problem is that you are retrieving the date-time value from your database as a string. You should be retrieving date-time types of data as date-times types in Java.
For drivers compliant with JDBC 4.2 and later, you should be able to use setObject and getObject with java.time objects.
For SQL type of TIMESTAMP WITHOUT TIME ZONE use LocalDateTime. For TIMESTAMP WITH TIME ZONE, use Instant or perhaps ZonedDateTime depending on the database.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class );
Store in database.
myPreparedStatement.setObject( … , ldt ) ;
try this formatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S");
I'm not sure about the millisecond part though (In case it is more than 1 character long).
I need to add minutes into timestamp and return new value after that. Timestamp is taken from database and passed to method as String and has this pattern: yyyy-MM-dd hh:mm:ss.SSSSSS
public static String changeCreditTimeStampMin(String tmStmp, int minutesToAdd) {
tmStmp = tmStmp.substring(0, tmStmp.length() - 3);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
java.util.Date parsedDate = dateFormat.parse(tmStmp);
java.sql.Timestamp timestamp = new java.sql.Timestamp(parsedDate.getTime() + minutesToAdd*60*1000);
tmStmp = timestamp.toString();
} catch (ParseException e) {
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
return tmStmp;
}
As you can see, my logic uses SimpleDateFormat and in order to use that I have to use substring operation on timestamp. My question, is there any better way I could get the same result without cutting the timestamp string? Please note that my java version is 1.5 and I cannot use newer versions.
The issue for you is that you are using mm. You should use MM. MM is for month and mm is for minutes. Try with yyyy-MM-dd HH:mm
Other approach:
static final long ONE_MINUTE_IN_MILLIS=60000;//millisecs
Calendar date = Calendar.getInstance();
long t=date.getTimeInMillis();
Date afterAddingTenMins=new Date(t + (10 *ONE_MINUTE_IN_MILLIS));
A better way?
Yes, use java.time classes rather than the old legacy date-time classes.
Change your string input to standard ISO 8601 format by replacing the space in middle with a T. I assume this string was intended to represent a moment in UTC. So, append a Z, short for Zulu, meaning UTC.
The java.time classes use ISO 8601 formats by default when parsing/generating string representations. So feed directly to the parse method without bothering to define a formatting pattern. An Instant represents a moment on the timeline in UTC with a result ion of nanoseconds.
Instant instant = Instant.parse( input );
Add minutes. If you want this in UTC, so you don't care about handling time zones and anomalies like Daylight Saving Time (DST), use this Instant to create another. Otherwise apply w time zone to get a ZonedDateTime.
long minutes = 5L;
Instant later = instant.plus( minutes , ChronoUnit.MINUTES );
Tip: Pass these Instant objects around your app rather than strings.