Parsing and formatting LocalDate with unnecessary time and timezone - java

Edit :
I opened a bug and it has been confirmed by Oracle. You can follow the resolution here : https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8216414
I'm interfacing with a LDAP repository which store the birthdate of a person with the time and timezone like this :
If the birthdate is "27-12-2018", then the LDAP string is "20181227000000+0000".
I cannot find a way to parse AND format the birthdate using the same pattern.
The following code works well for formatting but not for parsing :
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMdd'000000+0000'";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs correctly 20181227000000+0000
date.format(birthdateFormat);
// Throw a DatetimeParseException at index 0
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
And the following code works well for parsing but not for formatting
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMddkkmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Throws a UnsupportedTemporalTypeException for ClockHourOfDay not supported
// Anyway I would have an unwanted string with non zero hour, minute, second, timezone
date.format(birthdateFormat);
// Parse correctly the date to 27-12-2018
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
Which pattern could satisfy both parsing and formating ?
Am I forced to use 2 different patterns ?
I am asking because the pattern is configured in a property file. I want to configure 1 pattern only in this property file. I would like to externalize the pattern because the LDAP is not part of my project, it is a shared resource and I have no guarantee that the format cannot change.

Since your LDAP string has zoned format ...+0000, I would suggest using ZonedDateTime or OffsetDateTime.
This pattern yyyyMMddHHmmssZZZ would do the trick for both parsing and formatting.
LocalDate date = LocalDate.of(2018, 12, 27);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssZZZ");
Formatting
First convert your LocalDate to ZonedDateTime/OffsetDateTime:
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneOffset.UTC);
// or
OffsetDateTime offsetDateTime = date.atStartOfDay().atOffset(ZoneOffset.UTC);
Then format it:
// Both output correctly 20181227000000+0000
System.out.println(zonedDateTime.format(formatter));
// or
System.out.println(offsetDateTime.format(formatter));
Parsing
First parse the ZonedDateTime/OffsetDateTime:
// Both parse correctly
ZonedDateTime zonedDateTime = ZonedDateTime.parse("20181227000000+0000", formatter);
// or
OffsetDateTime offsetDateTime = OffsetDateTime.parse("20181227000000+0000", formatter);
Once you have ZonedDateTime/OffsetDateTime, you can simply retrieve the LocalDate like this:
LocalDate date = LocalDate.from(zonedDateTime);
// or
LocalDate date = LocalDate.from(offsetDateTime);
Update
Both parsing and formatting can be simplified to one-liners:
LocalDate date = LocalDate.from(formatter.parse(ldapString));
String ldapString = OffsetDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC).format(formatter);
In case you're still unsatisfied with the code above then you can extract the logic to utility methods:
public LocalDate parseLocalDate(String ldapString) {
return LocalDate.from(formatter.parse(ldapString));
}
public String formatLocalDate(LocalDate date) {
return OffsetDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC)
.format(formatter);
}

I suggest:
LocalDate date = LocalDate.of(2018, Month.DECEMBER, 27);
String pattern = "yyyyMMddHHmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs 20181227000000+0000
String formatted = date.atStartOfDay(ZoneOffset.UTC).format(birthdateFormat);
System.out.println(formatted);
// Parses to 2018-12-27T00:00Z
OffsetDateTime odt = OffsetDateTime.parse("20181227000000+0000", birthdateFormat);
System.out.println(odt);
// Validate
if (! odt.toLocalTime().equals(LocalTime.MIN)) {
System.out.println("Unexpected time of day: " + odt);
}
if (! odt.getOffset().equals(ZoneOffset.UTC)) {
System.out.println("Unexpected time zone offset: " + odt);
}
// Converts to 2018-12-27
date = odt.toLocalDate();
System.out.println(date);
The LDAP string represents both date, time and UTC offset. The good solution is to respect that and generate all of those when formatting (setting time of day to 00:00 and offset to 0) and parsing all of them back (at best also validating them to catch if any surprises should arise). Conversion between LocalDate and OffsetDateTime is straightforward when you know how.
Edit 3: Allowing the pattern to be configured
… the pattern is configured in a property file… I want to configure 1
pattern only in this property file.
… I have no guarantee that the format cannot change.
To take the possibility into account that the pattern may some day not contain time of day and/or no UTC offset use this formatter in the above code:
DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendPattern(pattern)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter()
.withZone(ZoneOffset.UTC);
This defines a default time of day (midnight) and a default offset (0). As long as time and offset are defined in the string from LDAP, the defaults are not used.
If you think it is getting too complicated, using two configured formats, one for formatting and one for parsing, may be the best solution (the least annoying solution) for you.
Edit: Avoiding type conversions
I consider the above the nice solution. However, if you insist an avoiding the conversion from LocalDate to ZonedDateTime using atStartOfDay and from OffsetDateTime using toLocalDate, that is possible through the following hack:
DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
.appendValue(ChronoField.MONTH_OF_YEAR, 2, 2, SignStyle.NEVER)
.appendValue(ChronoField.DAY_OF_MONTH, 2, 2, SignStyle.NEVER)
.appendLiteral("000000+0000")
.toFormatter();
// Outputs 20181227000000+0000
String formatted = date.format(birthdateFormat);
System.out.println(formatted);
// Parses into 2018-12-27
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
System.out.println(date);
I am specifying the exact width of each field so that the formatter can know where to separate them in the string when parsing.
Edit 2: Is this a bug in parsing?
I would immediately have expected yyyyMMdd'000000+0000' to work for both formatting and parsing. You may try filing a bug with Oracle and seeing what they say, though I wouldn’t bee too optimistic.

Stupid simple solution:
String s1 = "20181227000000+0000";
DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate date = LocalDate.parse(s1.substring(0, 8), yyyyMMdd);
System.out.println("date = " + date);
String s2 = date.format(yyyyMMdd) + "000000+0000";
System.out.println("s2 = " + s2);
System.out.println(s1.equals(s2));

Related

Calculate Number of Days between Given time in ISO format and Current time

I have to find out number of days between a given Time and current time. Given time is in ISO format and one example is "2021-01-14 16:23:46.217-06:00".
I have tried it using "java.text.SimpleDateFormat" but it's not giving me accurate results.
In Below Given date, for today's time I am getting output as "633" Days which isn't correct. somehow after parsing it is taking date as "21 december 2020" which isn't correct
String TIMESTAMP_FORMAT = "YYYY-MM-DD hh:mm:ss.s-hh:mm" ;
int noOfDays = Utility.getTimeDifferenceInDays("2021-01-14 16:23:46.217-06:00", TIMESTAMP_FORMAT);
public static int getTimeDifferenceInDays(String timestamp, String TIMESTAMP_FORMAT) {
DateFormat df = new SimpleDateFormat(TIMESTAMP_FORMAT);
try {
Date date = df.parse(timestamp);
long timeDifference = (System.currentTimeMillis() - date.getTime());
return (int) (timeDifference / (1000*60*60*24));
} catch (ParseException e) {
e.printStackTrace();
}
return 0;
}
Looking for a better solution which gives me correct number of days. Thanks
Use java.time API
Classes Date and SimpleDateFormat are legacy.
Since Java 8 (which was released 10 years ago) we have a new Time API, represented by classes from the java.time package.
To parse and format the data, you can use DateTimeFormatter. An instance of DateTimeFormatter can be obtained via static method ofPattern(), or using DateTimeFormatterBuilder.
ofPattern():
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSXXX");
DateTimeFormatterBuilder:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd HH:mm:ss.") // main date-time part
.appendValue(ChronoField.MILLI_OF_SECOND, 3) // fraction part of second
.appendOffset("+HH:MM", "+00:00") // can be substituted with appendPattern("zzz") or appendPattern("XXX")
.toFormatter();
The string "2021-01-14 16:23:46.217-06:00", which you've provided as an example, contains date-time information and UTC offset. Such data can be represented by OffsetDateTime.
To get the number of days between two temporal objects, you can use ChronoUnit.between() as #MC Emperor has mentioned in the comments.
That's how the whole code might look like:
String toParse = "2021-01-14 16:23:46.217-06:00";
OffsetDateTime dateTime = OffsetDateTime.parse(toParse, formatter);
System.out.println("parsed date-time: " + dateTime);
Instant now = Instant.now();
long days = ChronoUnit.DAYS.between(dateTime.toInstant(), now);
System.out.println("days: " + days);
Output:
parsed date-time: 2021-01-14T16:23:46.217-06:00
days: 615
Note that since in this case you need only difference in days between the current date instead of OffsetDateTime you can use LocalDateTime, UTC offset would be ignored while parsing a string. If you decide to do so, then the second argument passed to ChronoUnit.between() should be also of type LocalDateTime.

How to convert from string format of date (dd/mm/yyyy) to string format of date (yyyy-mm-dd'T'HH:mm:ss.SSS)

I have to convert
"fromTime" : "04-10-2021"`
to
"fromTime" : "2021-10-04T00:00:00.000"
and
"toTime" : "06-10-2021"
to
"toTime" : "2021-10-06T23:59:59.000"
in java . Help please!
Generally speaking, you really want to avoid representing date/time as String where ever possible.
Displaying date/time values to the user should be done in styles that respect their localisation and transmitting date/time values using JSON or XML should use common/standardised formats. This reduces a LOT of issues (don't even get me started)
For example, had I the choice, I would prefer to use ISO_LOCAL_DATE (uuuu-MM-dd) and ISO_LOCAL_DATE_TIME (uuuu-MM-dd'T'HH:mm:ss). I'm already worried about the lack of time zone information 😱
A "simple" approach might be to do something like...
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MM-uuuu");
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("dd-MM-uuuu.SSS");
LocalDateTime locatDateTime = LocalDate
.parse("04-10-2021", dateFormatter)
.atTime(0, 0, 0, 0);
String dateAndTime = locatDateTime.format(timeFormatter);
System.out.println(dateAndTime);
locatDateTime = LocalDate
.parse("04-10-2021", dateFormatter)
.atTime(23, 59, 59, 0);
dateAndTime = locatDateTime.format(timeFormatter);
System.out.println(dateAndTime);
Basically...
Parse the date value through a DateTimeFormatter using a matching format patten
Convert the LocalDate to a LocalDateTime by passing in the values we want to use
Format the LocalDateTime through a DateTimeFormatter to the desired format
which prints...
04-10-2021T00:00:00.000
06-10-2021T23:59:59.000
See:
The date/time trail
Introduction to the Java 8 Date/Time API
for more details
Now, if all you really want to do, is make it "start of" and "end of" day, you might be able to use...
LocalDateTime startOfDay = LocalDate
.parse("04-10-2021", dateFormatter)
.atStartOfDay();
LocalDateTime endOfDay = LocalDate
.parse("06-10-2021", dateFormatter)
.plusDays(1)
.atStartOfDay()
.minusSeconds(1);
Right about now, I'd be starting a utility class of some kind which had a method which could take a date String (and possibly an optional format) and generate a LocalDate and then another method which could take LocalDate which could convert it to a LocalDateTime at either the startOfDay or endOfDay
You have to do these steps:
create a DateTimeFormatter for dd-MM-yyyy
parse the date with LocalDate.parse with atTime
create a DateTimeFormatter for yyyy-MM-dd'T'HH:mm:ss.SSS
format the date in order to have the final result
public String changeFormatDate(String date, int hour, int minute, int second, int nanoOfSecond) {
DateTimeFormatter formatterFrom = DateTimeFormatter.ofPattern("dd-MM-yyyy"); // 1
LocalDateTime localDateTimeFrom = LocalDate.parse(date, formatterFrom).atTime(hour, minute, second, nanoOfSecond); // 2
DateTimeFormatter formatterTo = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); // 3
return localDateTimeFrom.format(formatterTo); //4
}
Call example:
String fromTime = changeFormatDate("04-10-2021", 0, 0, 0, 0);
String toTime = changeFormatDate("06-10-2021", 23, 59, 59, 999999999);
Here's an example using java.time:
public static void main(String[] args) {
// input dates / days
String fromDate = "04-10-2021";
String toDate = "06-10-2021";
// prepare the times of day
LocalTime startOfDay = LocalTime.MIN; // 00:00:00.000
LocalTime endOfDay = LocalTime.of(23, 59, 59); // 23:59:59.000
// create an object that takes care of parsing the expected format
DateTimeFormatter customDateParser = DateTimeFormatter.ofPattern("dd-MM-uuuu");
// then parse the dates using the formatter / parser
LocalDate fromDay = LocalDate.parse(fromDate, customDateParser);
LocalDate toDay = LocalDate.parse(toDate, customDateParser);
// then concatenate the parsed dates with the prepared times of day
LocalDateTime fromTime = LocalDateTime.of(fromDay, startOfDay);
LocalDateTime toTime = LocalDateTime.of(toDay, endOfDay);
// finally define a formatter for the String output of the LocalDateTimes
DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS");
// and print their toString() methods
System.out.println("from " + fromTime.format(customFormatter)
+ " to " + toTime.format(customFormatter));
}
from 2021-10-04T00:00:00.000 to 2021-10-06T23:59:59.000

DateTime parser format in java 8

I am trying to parse a datetimeStamp as below but I am not able to find the correct parser for it. Can someone advise how can I parse such dates:
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
public class HelloWorld{
public static void main(String []args){
int DAYS = 30;
String date ="2021-04-23T12:09:56.123-07:00";
DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
.ofPattern("YYYY-MM-DD'T'hh:mm:ss.SSS±hh:mm");
LocalDateTime localDateTime = LocalDateTime.parse(date, DATE_TIME_FORMATTER);
System.out.println("Local Date" + localDateTime);
LocalDateTime now = LocalDateTime.now();
if (localDateTime.isBefore(now) && localDateTime.isAfter(now.plusDays(DAYS)))
{
System.out.println("Date is incorrect");
}else{
System.out.println("Success");
}
}
}
Below is also a bit of additional detail:
Must be expressed in ISO 8601 extended format as one of the following - YYYY-MM-DDThh:mm:ss[ .sss ]Z, YYYY-MM-DDThh:mm:ss[ .sss ]±hh:mm . Where [ .sss ] is optional and can be 1 to 3 digits. Must be a value no more than 30 days in the future. Mastercard recommends using a value of (Current Time + 30 minutes). Max length - 29. Type - String.
example: "2015-07-04T12:09:56.123-07:00"
Please help me with the correct formatter for the day specified.
You have 3 separate problems here:
±hh:mm - as per the docs of DateTimeFormatter (remember, reading is fundamental! Always read the javadoc before asking SO questions!), ± isn't a thing. hh and mm are things, but not 'offset hours'. The 'pattern' that represents tz offsets is a single letter; you're most likely looking for Z. But that doesn't ordinarily include the :.
LocalDateTime cannot represent offsets and doesn't know about timezones. If you want that, you're looking for either ZonedDateTime or most likely OffsetDateTime.
Your pattern doesn't include a locale which means the output of it, and what it can parse, depends on the locale of the computer you run this code on, which sounds bad. Always use .ofPattern("someString", Locale.ENGLISH) (or some other locale) unless you are really really sure you want the app to depend on the configuration of the server you run it on, which should be very rare.
Get rid of this wrong pattern (and the DateTimeFormatter) at all and use an OffsetDateTime instead of a LocalDateTime which isn't able to store information about an offset.
This is a conversion of your code (slightly adjusted and commented):
public static void main(String[] args) {
String date = "2021-04-23T12:09:56.123-07:00";
/*
* no DateTimeFormatter needed due to default format of the String
*/
// use an OffsetDateTime to parse a String with an offset
OffsetDateTime odtParsed = OffsetDateTime.parse(date);
// print it
System.out.println("Parsed OffsetDateTime is " + odtParsed);
// take the current instant as an OffsetDateTime at an offset of -7 hours
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.ofHours(-7));
// run your check and get a success
if (odtParsed.isBefore(now) && odtParsed.isAfter(now.plusDays(DAYS))) {
System.out.println("Date is incorrect");
} else {
System.out.println("Success");
}
}
It prints
Parsed OffsetDateTime is 2021-04-23T12:09:56.123-07:00
Success
Hint: This will fail for differently formatted Strings. If the formattings only differ a little, you might be able to create a DateTimeFormatter (via a DateTimeFormatterBuilder) that can handle optional parts and so on.
Edit
If only the millis of day are optional and the format of the offset always consists of a plus or minus followed by two digits for hours, a colon and two digits for minutes, the following example might be showing a sufficient way of making it generic:
public static void main(String[] args) {
String date = "2021-04-23T12:09:56.123-07:00";
String date1 = "2021-04-23T12:09:56.123+02:00";
String date2 = "2021-04-23T12:09:56.123+00:00";
String date3 = "2021-04-23T12:09:56-07:00";
String date4 = "2021-04-23T12:09:56+02:00";
String date5 = "2021-04-23T12:09:56+00:00";
// DateTimeFormatter using optional millis of second
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[.SSS]xxx");
// use an OffsetDateTime to parse a String with an offset
OffsetDateTime odtParsed = OffsetDateTime.parse(date, dtf);
OffsetDateTime odtParsed1 = OffsetDateTime.parse(date1, dtf);
OffsetDateTime odtParsed2 = OffsetDateTime.parse(date2, dtf);
OffsetDateTime odtParsed3 = OffsetDateTime.parse(date3, dtf);
OffsetDateTime odtParsed4 = OffsetDateTime.parse(date4, dtf);
OffsetDateTime odtParsed5 = OffsetDateTime.parse(date5, dtf);
// print it
System.out.println("Parsed OffsetDateTime is " + odtParsed);
System.out.println("Parsed OffsetDateTime is " + odtParsed1);
System.out.println("Parsed OffsetDateTime is " + odtParsed2);
System.out.println("Parsed OffsetDateTime is " + odtParsed3);
System.out.println("Parsed OffsetDateTime is " + odtParsed4);
System.out.println("Parsed OffsetDateTime is " + odtParsed5);
}
Output:
Parsed OffsetDateTime is 2021-04-23T12:09:56.123-07:00
Parsed OffsetDateTime is 2021-04-23T12:09:56.123+02:00
Parsed OffsetDateTime is 2021-04-23T12:09:56.123Z
Parsed OffsetDateTime is 2021-04-23T12:09:56-07:00
Parsed OffsetDateTime is 2021-04-23T12:09:56+02:00
Parsed OffsetDateTime is 2021-04-23T12:09:56Z
and if you don't want an offset of zero hours and zero minutes to be output as a Z, then use the DateTimeFormatter for output like this:
OffsetDateTime odtParsed5 = OffsetDateTime.parse(date5, dtf);
which makes the output look like this:
Parsed OffsetDateTime is 2021-04-23T12:09:56.000+00:00
Where the millis of day are printed even if they are all zero.
Another Edit:
To check if the date is in the range 30 days in the past to 30 days in the future it might be good to use the date parts only:
public static void main(String[] args) {
// the first part is already known from the examples above...
String date = "2021-04-23T12:09:56.123-07:00";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[.SSS]xxx");
OffsetDateTime odtParsed = OffsetDateTime.parse(date, dtf);
// get the current instant as an OffsetDateTime (in UTC == +00:00)
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
/*
* might be smart to keep time of day out of the calculation, so...
*/
// get the date only out of the OffsetDateTime
LocalDate today = now.toLocalDate();
// create one for 30 days before
LocalDate thirtyDaysBeforeToday = today.minusDays(30);
// and one 30 days in the future
LocalDate thirtyDaysInTheFuture = today.plusDays(30);
// finally extract the date part from the parsed date time
LocalDate d = odtParsed.toLocalDate();
// make your checks using the LocalDates, maybe separate the two cases of an invalid date
if (d.isBefore(thirtyDaysBeforeToday)) {
System.err.println("Invalid date: too far in the past");
} else if (d.isAfter(thirtyDaysInTheFuture)) {
System.err.println("Invalid date: too far in the future");
} else {
System.out.println("Valid date");
}
}
Try some different valid and invalid dates...
Your documentation uses ± to mean either + or -, and its placeholders aren't the Java ones. Just use DateTimeFormatter.ISO_OFFSET_DATE_TIME.

Including wildcard in a SimpleDateFormat

I want a DateFormatter in java so that i can specify some special character as well as digits in a date expression. For ex :
String dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS zzz";
Here dd is used to specify the day of month which is numeric.
But i have a requirement to create a date as below :
String stringDate = "2017-12-??T00:00Z";
SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);
formatter.parse(stringDate);
I get an unparseable exception as the DAY specified here is ?? . Is there any workaround for this or shall i have to write a new parser ?
Thanks
Try escaping the additional literals using single quote
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-'??T'HH:mm:ss.SSS zzz");
Also the value and the format given should match(Can edit the string date as required), in your case following syntax will work.
String stringDate = "2017-12-??T00:00Z";
Date date = (new SimpleDateFormat("yyyy-MM-'??T'HH:mmZ")).parse(stringDate.replaceAll("Z$", "+0000"));
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'??T'HH:mmZ")).format(date));
Please note that 'Z' indicates that the timezone conforms to the RFC 822 time zone standard as well.
Edit: Consider a scheduler. Your comment may sound like what you need is a scheduler, for example Quartz scheduler. I include a link at the bottom. Then convert user input not to a YearMonth, OffsetDateTime or any other date-time object (because they don’t fit), but into a syntax that your scheduler can accept.
Original answer
I am giving you a couple of suggestions. It’s with reservation though: I don’t understand why you want this, not even exactly what you want, so these suggestions may not be the right ones for you.
One suggestion I am pretty sure of, though: do use java.time, the modern java date and time API, for your date and time work. It is so much nicer to work with than the old, poorly designed and long outdated date-time classes that include the notoriously troublesome SimpleDateFormat class.
Parsing year and month: If you just want the year and the month from a string that has question marks instead of the day of month, parse into a YearMonth:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-'??T'HH:mmX");
String stringDate = "2017-12-??T00:00Z";
YearMonth ym = YearMonth.parse(stringDate, formatter);
System.out.println("Year and month are " + ym);
Output from this snippet is:
Year and month are 2017-12
Parsing all information from the string: If you need time of day and offset from the same string too, just parse the string once and get the various information from the parse result:
TemporalAccessor parsed = formatter.parse(stringDate);
YearMonth ym = YearMonth.from(parsed);
System.out.println("Year and month are " + ym);
LocalTime time = LocalTime.from(parsed);
System.out.println("Time of day is " + time);
ZoneOffset offset = ZoneOffset.from(parsed);
System.out.println("UTC offset is " + offset);
Year and month are 2017-12
Time of day is 00:00
UTC offset is Z
Using a default day of month: If you know what day of month you want instead of the question marks, specify it as a default value:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-'??T'HH:mmX")
.parseDefaulting(ChronoField.DAY_OF_MONTH, 23)
.toFormatter();
String stringDate = "2017-12-??T00:00Z";
OffsetDateTime dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
Date and time is 2017-12-23T00:00Z
Accepting both numbers and question marks: If the date can be given as either numeric or question marks, use optional parts in the format pattern strings. Such are enclosed in square brackets:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu-MM-[??][dd]'T'HH:mmX")
.parseDefaulting(ChronoField.DAY_OF_MONTH, 23)
.toFormatter();
String stringDate = "2017-12-??T00:00Z";
OffsetDateTime dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
stringDate = "2018-02-16T00:00Z";
dateTime = OffsetDateTime.parse(stringDate, formatter);
System.out.println("Date and time is " + dateTime);
Date and time is 2017-12-23T00:00Z
Date and time is 2018-02-16T00:00Z
Tutorial links
Cron Trigger Tutorial from the Quartz Scheduler documentation.
Oracle tutorial: Date Time explaining how to use java.time.

Java String to Date object of the format “MM/dd/yyyy HH:mm:ss a”

I need to convert a String containing a date into a date object.
The String will be in the format "yyyy-mm-dd HH:mm:ss" and I want the "MM/dd/yyyy HH:mm:ss a " format as result.
String dateString = "2018-03-20 09:31:31";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss a",
Locale.ENGLISH);
LocalDate date = LocalDate.parse(dateString , formatter);
The code above is throwing an exception.
You have to use two Formatter, one to covert String to LocalDateTime and the other to format this date as you want :
From String to LocalDateTime :
String dateString = "2018-03-20 09:31:31";
LocalDateTime date = LocalDateTime.parse(
dateString,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
);
Now From LocalDateTime to String :
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"MM/dd/yyyy HH:mm:ss a", Locale.ENGLISH
);
String newDate = date.format(formatter);
System.out.println(newDate);// 03/20/2018 09:31:31 AM
Note : You have to use LocalDateTime instead of just LocalDate, your format contain both date and time, not just date, else you will get an error :
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
That's a common error, based on the misconception that dates have formats - but they actually don't.
Date/time objects have only values, and those values - usually numerical - represent the concept of a date (a specific point in the calendar) and a time (a specific moment of the day).
If you have a String, then you don't actually have a date. You have a text (a sequence of characters) that represents a date. Note that all of the strings below are different (they have a different sequence of characters), but all represent the same date (the same values, the same point in the calendar):
2018-03-20 09:31:31
03/20/2018 9:31:31 AM (using USA's format: month/day/year)
Tuesday, March 20th 2018, 09:31:31 am
and many others...
What you want to do is to get one format (one String, one text representing a date) and transform it to another format (anoter String, another different sequence of characters that represents the same date).
In Java (and in many other languages - if not all - btw) you must do it in 2 steps:
convert the String to a date/time object (convert the text to the numerical values) - that's what the parse method does
convert the date/time object to another format (convert the numerical values to another text)
That said, when you call the parse method, you're trying to transform a String (a text, a sequence of characters) into a date/time object. This means that the DateTimeFormatter must have a pattern that matches the input.
The input is 2018-03-20 09:31:31, which is year-month-day hour:minute:second. And the formatter you used to parse it has the pattern MM/dd/yyyy HH:mm:ss a (month/day/year hour:minute:second am/pm).
You used the output pattern (the one that should be used in step 2) to parse the input. That's why you've got an exception: the formatter tried to parse a month with 2 digits followed by a / when the input actually contains a year with 4 digits followed by a -.
You must use a different DateTimeFormatter for each step, using the correct pattern for each case. YCF_L's answer has the code that does the job, I'd just like to add one little detail. The formatter used for the output (step 2) is:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"MM/dd/yyyy HH:mm:ss a", Locale.ENGLISH
);
Note that HH is used for the hours. Take a look at the javadoc and you'll see that uppercase HH represents the hour-of-day fields (values from 0 to 23 - so 1 AM is printed as 01 and 1 PM is printed as 13).
But you're also printing the AM/PM field (the a in the pattern), so maybe what you need is actually the lowercase hh, which is the clock-hour-of-am-pm (values from 1 to 12) or even KK (hour-of-am-pm (values from 0 to 11)).
String dateString = "2018-03-20 09:31:31";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date = formatter.parse(dateInString);
DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
String reportDate = df.format(date );
} catch (ParseException e) {
e.printStackTrace();
}
You need to do a 2 steps conversion:
from your String date time in the wrong format to a (tempoary) LocalDateTime object.
if you still want to only extract the date (Year-Month-day) do a LocalDateTime.toLocalDate()
From this LocalDateTime object into the your String object in the right format
String dateString = "2018-03-20 09:31:31";
DateTimeFormatter formatterForWrongFormat = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(" ")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
//1- from String(wrong format) into datetime object
LocalDateTime dateTime = LocalDateTime.parse(dateString , formatterForWrongFormat);
// 1.1 extract date object (Optional)
LocalDate myDate = dateTime.toLocalDate();
// 2- now from your LocalDateTime to the String in the RIGHT format
DateTimeFormatter formatterForRightFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm:ss a",
Locale.ENGLISH);
System.out.println("right format: "+dateTime.format(formatterForRightFormat));
you can test this code here
You can use the SimpleDateFormatter which is easier to implement and permit you to change the format of your date easily.
More here : What are the date formats available in SimpleDateFormat class?
Hope this will help you !

Categories