I have a DatePicker in my FXML and I need the Date to insert it into my SQL-Database. I want to format my Date but it doesn't work.
LocalDate localDate = purchased_at.getValue();
localDate.format(DateTimeFormatter.ofPattern("dd.mm.yyyy"));
This is the Error I get.
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: MinuteOfHour
I'm still kind of a beginner. I had Java for the past 3 or 4 months now. I'm trying my best to improve.
Don’t format your date for insertion into your SQL database. Assuming that your database column has datatype date and you are using at least Java 8 and at least JDBC 4.2, just pass the LocalDate to your PreparedStatement as it is:
PreparedStatement insertStmt = myConnection.prepareStatement(
"insert into my_table(purchase_date) values (?)");
insertStmt.setObject(1, purchaseDate);
Your JDBC driver will take care of the rest. If using JPA, your JPA implementation will take care of it too.
If your column has char type (for example varchar(10)) and you cannot change it, don’t invent your own format for it. Store the date in ISO 8601 format. LocalDate.toString() produces this format.
String formattedDate = purchaseDate.toString();
System.out.println(formattedDate);
In my case output was:
2017-11-29
As an aside, for presentation to your user you shouldn’t invent your own format either. Rather rely on the built-in formats in Java. For example:
Locale turkish = Locale.forLanguageTag("tr");
DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(turkish);
String formattedDate = purchaseDate.format(dateFormatter);
System.out.println(formattedDate);
Output:
29.11.2017
What went wrong in your code?
There are two things wrong:
You used lowercase mm. This means minute of hour, and since a LocalDate doesn’t have a time of day in it, it threw the exception you saw. The message you got is pretty precise:
Unsupported field: MinuteOfHour
Instead you may use uppercase MM for two-digit month.
You need to pick up the format in the String returned from the format method. The LocalDate is immutable and therefore not affected by the method call. Also it cannot have a format in it. It’s just a date in the calendar.
Links
Wikipedia article: ISO 8601
Insert & fetch java.time.LocalDate objects to/from an SQL database such as H2
How to format LocalDate object to MM/dd/yyyy and have format persist (TL;DR: you cannot)
I had to use a String converter for my Datepicker.
public String changeformat(DatePicker date) {
date.setConverter(new StringConverter<LocalDate>() {
String pattern = "MM.yyyy";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(pattern);
{
date.setPromptText(pattern.toLowerCase());
}
#Override
public String toString(LocalDate date) {
if (date != null) {
return dateFormatter.format(date);
} else {
return "";
}
}
#Override
public LocalDate fromString(String string) {
if (string != null && !string.isEmpty()) {
return LocalDate.parse(string, dateFormatter);
} else {
return null;
}
}
});
return null;
}
It worked perfectly fine. I had to use a parameter since I'm currently using 5 Datepickers.
Related
I'm given some datetime format string that user entered, and need to check into what java.time temporals I can parse data in that format (within reason, I indend to support only the simpler cases for now).
Something along the lines of this table:
Input Format
Expected Answer
yyyy-MM
java.time.YearMonth
MM/dd/yyyy
java.time.LocalDate
yyyy:DD
java.time.LocalDate (because of day-of-year data)
HH:mm:ss
java.time.LocalTime
dd MM yyyy hh:mm:ssV
java.time.ZonedDateTime
Keeping in mind, that both the date format and the date are entered by the user, so these input formats are just examples, and they obviously can contain literals (but not optional parts, that's a relief).
So far I've only been able to come up with this tornado of ifs as a solution:
private static final ZonedDateTime ZDT = ZonedDateTime.of(1990, 10, 26, 14, 40, 59, 123456, ZoneId.of("Europe/Oslo"));
...
String formatted = externalFormatter.format(ZDT);
Class<?> type;
TemporalAccessor parsed = externalFormatter.parse(formatted);
if (parsed.isSupported(YEAR)) {
if (parsed.isSupported(MONTH_OF_YEAR)) {
if (parsed.query(TemporalQueries.localDate()) != null) {
if (parsed.query(TemporalQueries.localTime()) != null) {
if (parsed.query(TemporalQueries.zone()) != null) {
type = ZonedDateTime.class;
}
else if (parsed.query(TemporalQueries.offset()) != null) {
type = OffsetDateTime.class;
}
else {
type = LocalDateTime.class;
}
}
else {
type = LocalDate.class;
}
}
else {
type = YearMonth.class;
}
}
else {
type = Year.class;
}
}
else if (parsed.query(TemporalQueries.localTime()) != null) {
if (parsed.query(TemporalQueries.offset()) != null) {
type = OffsetTime.class;
}
else {
type = LocalTime.class;
}
}
Surely, there must be some better way, at least marginally? I will not limit myself to just using java.time, I also have the Joda-Time library available to me (although it's technically on legacy status), and I will not turn down a simpler code that uses the SimpleDateFormat if there is such an option.
DateTimeFormatter::parseBest()
I am taking your word for it:
Keeping in mind, that both the date format and the date are entered by
the user, …
So I am assuming that I may use both the format pattern and the date string entered for seeing what I can make of it. DateTimeFormatter::parseBest() is the method we need for that.
public static TemporalAccessor parse(String formatPattern, String toBeParsed) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern, Locale.ROOT);
return formatter.parseBest(toBeParsed, ZonedDateTime::from,
LocalDate::from, LocalTime::from, YearMonth::from);
}
For demonstrating the power of the above method I am using the following auxiliary method:
public static void demo(String formatPattern, String toBeParsed) {
TemporalAccessor result = parse(formatPattern, toBeParsed);
System.out.format("%-21s %s%n", formatPattern, result.getClass().getName());
}
Let’s call it using your examples:
demo("yyyy-MM", "2017-11");
demo("MM/dd/yyyy", "10/21/2023");
demo("yyyy:DD", "2021:303");
demo("HH:mm:ss", "23:34:45");
demo("dd MM yyyy HH:mm:ssVV", "05 09 2023 14:01:55Europe/Oslo");
I have corrected a couple of errors in the last format pattern string. Output is:
yyyy-MM java.time.YearMonth
MM/dd/yyyy java.time.LocalDate
yyyy:DD java.time.LocalDate
HH:mm:ss java.time.LocalTime
dd MM yyyy HH:mm:ssVV java.time.ZonedDateTime
In the list of TemporalQuery arguments to parseBest() it’s essential to put the classes that hold the most information first. Or which classes you prefer, but I assumed you wanted as much information as you can have. parseBest() uses the first query that works. So put ZonedDateTime first and Year, Month and DayOfWeek last if you intend to support any of the last three.
Remember to decide which locale you want.
With only the format pattern string
If you are supposed to do the trick without the string to be parsed, it’s not badly more complicated. Start by formatting some ZonedDateTime using the formatter that you have constructed from the format pattern string. Then use parseBest() to parse the resulting string back. You must use ZonedDateTime because it’s the only class that holds about every field that could thinkably be in the format pattern string, so with some other class, formatting could break. On the other hand the information coming into the formatted string is limited by the pattern, so in most cases you won’t be able to parse a ZonedDateTime back. Which is exactly what we want: parseBest() will tell us which class we can parse into.
public static Class<? extends TemporalAccessor> getParseableType(String formatPattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern, Locale.ROOT);
String example = ZonedDateTime.now(ZoneId.systemDefault()).format(formatter);
TemporalAccessor parseableTemporalAccessor = formatter.parseBest(example, ZonedDateTime::from,
LocalDate::from, LocalTime::from, YearMonth::from);
return parseableTemporalAccessor.getClass();
}
Documentation link
parseBest(CharSequence text, TemporalQuery<?>... queries)
I'm querying database and getting date in this format "01-SEP-22"
I want to convert this date into this format "yyyy-MM-dd" in Java. Is there any way I can do this.
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work.
In order to parse the month abbreviation in all upper case (like SEP) we need to instruct it to apply case insensitive parsing.
We can use DateTimeFormatterBuilder to build a DateTimeFormatter with such an instruction.
private static final DateTimeFormatter oracleFormatter
= new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("dd-MMM-uu")
.toFormatter(Locale.ROOT);
The rest goes smoothly:
String stringFromOracle = "01-SEP-22";
LocalDate date = LocalDate.parse(stringFromOracle, oracleFormatter);
String formattedString = date.toString();
System.out.println(formattedString);
Output is:
2022-09-01
For generating the string I am exploiting the fact that LocalDate.toString() gives the format that you asked for, so I am not using any formatter explicitly. The format is known as ISO 8601 and as this name says, is an international standard.
Suggestions for improvement
Don’t retrieve your date as a String from Oracle. Retrieve a LocalDate directly and save the parsing.
Don’t convert a date from one string format to another. In your program keep the date as a LocalDate. If you need to take string input (which is not the case here), parse the string into a LocalDate immediately. Only when you need to give string output (to the user or in data exchange with another system, for example), format the LocalDate into a string in the required format.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Question: Insert & fetch java.time.LocalDate objects to/from an SQL database such as H2
You can simply use DateTimeFormatter:
public String convertDate(String dateStr) throws ParseException {
String[] split = dateStr.split("-");
split[1] = split[1].substring(0, 1).toUpperCase() + split[1].substring(1).toLowerCase();
dateStr = String.join("-", split);
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yy", Locale.ENGLISH);
LocalDate date = LocalDate.parse(dateStr, dateFormatter);
return date.toString();
}
#Test
public void test_convertDate() throws ParseException {
assertEquals("2022-09-01", convertDate("01-SEP-22"));
}
I have successfully imported date column from excel to the java code. However, I am unable to change the date format from 17-Dec-2018 to 2018-12-17. Kindly help.
public void saveToDatabase(Vector dataHolder) throws ParseException {
System.out.println(dataHolder);
for(Iterator iterator = dataHolder.iterator();iterator.hasNext();) {
List list = (List) iterator.next();
fullName = list.get(0).toString();
idNumberString = list.get(1).toString();
//idNumber = Integer.parseInt ( idNumberString );
committee = list.get(2).toString();
amountString = list.get(3).toString();
//amount = Integer.parseInt ( amountString );
bosaFosa = list.get(4).toString();
purpose = list.get(5).toString();
paymentsType = list.get(6).toString();
supportingDocuments = list.get(7).toString();
entryDate = list.get(8).toString();
}
The code now after fetching data from excel column the month is in text as "Dec" that is "17-Dec-2018"
I expect the final output in string as "2018-12-17" so that I can store in MYSQL database as DATE Type.
java.time
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("d-MMM-uuuu", Locale.ENGLISH);
String fullName = "Emmanuel Macron";
int idNumber = 42;
String entryDateString = "17-Dec-2018";
LocalDate entryDate = LocalDate.parse(entryDateString, dateFormatter);
PreparedStatement insertStatement = yourDbConnection.prepareStatement(
"insert into your_table (name, id, entry) values (?, ?, ?);");
insertStatement.setString(1, fullName);
insertStatement.setInt(2, idNumber);
insertStatement.setObject(3, entryDate);
int rowsInserted = insertStatement.executeUpdate();
The example is a bit simpler than yours, but should answer what you are asking about, so I trust the rest to you. I am using (and warmly recommending) java.time, the modern Java date and time API, for all date work in Java.
The steps involved for the date are:
Parse the date string into a LocalDate. LocalDate.parse(entryDateString, dateFormatter) does this. In the format pattern string, d-MMM-uuuu d means day of month in 1 or 2 digits, MMM means month abbreviation, and uuuu means 4 digit year. The month abbreviation is locale specific. Since I took Dec to be English, I have specified English locale for the formatter; please correct if your date strings are in some other language.
Pass the LocalDate to your PreparedStatement. insertStatement.setObject(3, entryDate); does this.
If you want to check that the first point worked as expected:
System.out.println("Entry date was parsed into " + entryDate);
Output:
Entry date was parsed into 2018-12-17
PS You may also want to check whether you can get the date from Excel in a different way. If you are using an older library such as Apache POI, I am afraid that the other option is an old-fashioned Date object, which you would then need to convert, so it’s a question whether it’s worth it.
Link: Oracle tutorial: Date Time explaining how to use java.time.
Your code is just simple. Here is code:
Parse string input.
String fromDate="17-Dec-2018";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
LocalDate localDate = LocalDate.parse(fromDate, dateFormatter);
Generate string output.
String convertedDate = localDate.toString();
System.out.println(convertedDate);
Here DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH) is the input date format and locale.
Here is the imports:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
EDIT:
Please follow the answer of #Ole V.V. given above as it is a better solution than mine.
You will first have to convert the String data into Date object.
Example code:
String sDate1="31/12/1998";
Date date1=new SimpleDateFormat("dd/MM/yyyy").parse(sDate1);
Then you can use SimpleDateFormat class to format date as you wish.
For more details on SimpleDateFormatter class, check this out
I am working on a REST API which supports Date as a query param. Since it is Query param it will be String. Now the Date can be sent in the following formats in the QueryParams:
yyyy-mm-dd[(T| )HH:MM:SS[.fff]][(+|-)NNNN]
It means following are valid dates:
2017-05-05 00:00:00.000+0000
2017-05-05 00:00:00.000
2017-05-05T00:00:00
2017-05-05+0000
2017-05-05
Now to parse all these different date-times i am using Java8 datetime api. The code is as shown below:
DateTimeFormatter formatter = new DateTimeFormatterBuilder().parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd[[ ][['T'][ ]HH:mm:ss[.SSS]][Z]"))
.toFormatter();
LocalDateTime localDateTime = null;
LocalDate localDate = null;
ZoneId zoneId = ZoneId.of(ZoneOffset.UTC.getId());
Date date = null;
try {
localDateTime = LocalDateTime.parse(datetime, formatter);
date = Date.from(localDateTime.atZone(zoneId).toInstant());
} catch (Exception exception) {
System.out.println("Inside Excpetion");
localDate = LocalDate.parse(datetime, formatter);
date = Date.from(localDate.atStartOfDay(zoneId).toInstant());
}
As can be seens from the code I am using DateTimeFormatter and appending a pattern. Now I am first trying to parse date as LocalDateTime in the try-block and if it throws an exception for cases like 2017-05-05 as no time is passed, I am using a LocalDate in the catch block.
The above approach is giving me the solution I am looking for but my questions are that is this the standard way to deal with date sent as String and is my approach is in line with those standards?
Also, If possible what is the other way I can parse the different kinds of date (shown as the Valid dates above) except some other straightforward solutions like using an Array list and putting all the possible formats and then using for-loop trying to parse the date?
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
// time is optional
.optionalStart()
.parseCaseInsensitive()
.appendPattern("[ ]['T']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.optionalEnd()
// offset is optional
.appendPattern("[xx]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
for (String queryParam : new String[] {
"2017-05-05 00:00:00.000+0000",
"2017-05-05 00:00:00.000",
"2017-05-05T00:00:00",
"2017-05-05+0000",
"2017-05-05",
"2017-05-05T11:20:30.643+0000",
"2017-05-05 16:25:09.897+0000",
"2017-05-05 22:13:55.996",
"2017-05-05t02:24:01"
}) {
Instant inst = OffsetDateTime.parse(queryParam, formatter).toInstant();
System.out.println(inst);
}
The output from this snippet is:
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T11:20:30.643Z
2017-05-05T16:25:09.897Z
2017-05-05T22:13:55.996Z
2017-05-05T02:24:01Z
The tricks I am using include:
Optional parts may be included in either optionalStart/optionalEnd or in [] in a pattern. I use both, each where I find it easier to read, and you may prefer differently.
There are already predefined formatters for date and time of day, so I reuse those. In particular I take advantage of the fact that DateTimeFormatter.ISO_LOCAL_TIME already handles optional seconds and fraction of second.
For parsing into an OffsetDateTime to work we need to supply default values for the parts that may be missing in the query parameter. parseDefaulting does this.
In your code you are converting to a Date. The java.util.Date class is long outdated and has a number of design problems, so avoid it if you can. Instant will do fine. If you do need a Date for a legacy API that you cannot change or don’t want to change just now, convert in the same way as you do in the question.
EDIT: Now defaulting HOUR_OF_DAY, not MILLI_OF_DAY. The latter caused a conflict when only the millis were missing, but it seems the formatter is happy with just default hour of day when the time is missing.
I usually use the DateUtils.parseDate which belongs to commons-lang.
This method looks like this:
public static Date parseDate(String str,
String... parsePatterns)
throws ParseException
Here is the description:
Parses a string representing a date by trying a variety of different parsers.
The parse will try each parse pattern in turn. A parse is only deemed successful if it parses the whole of the input string. If no parse patterns match, a ParseException is thrown.
The parser will be lenient toward the parsed date.
#Configuration
public class DateTimeConfig extends WebMvcConfigurationSupport {
/**
* https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#format-configuring-formatting-globaldatetimeformat
* #return
*/
#Bean
#Override
public FormattingConversionService mvcConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"));
dateTimeRegistrar.registerFormatters(conversionService);
// Register date conversion with a specific global format
DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd"));
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd'T'HH:mm:ss'Z'"));
dateRegistrar.registerFormatters(conversionService);
return conversionService;
}
}
I'm trying to put in some time stamps in a AnnotatedTimeLine (Google Chart), and its req. to be in the Datetime format.
When I reformat (to Timestamp format) the string that I receive from the class, it gives me this:
2013-06-28 10:08:35.0
What I want to do is to remove the .0 at the end. How can I do this?
The code looks like this:
public List<Survey> getAllSurvey(List<Status> statusList) {
String sqlValues = "SELECT * FROM status WHERE idCategory = (SELECT idCategory FROM category WHERE name = '"
+ statusList.get(0).getCategoryName() + "');";
List<Survey> survies = new ArrayList<Survey>();
try {
List<Map<String, Object>> rows = getJdbcTemplate().queryForList(
sqlValues);
for (Map row : rows) {
Survey survey = new Survey();
survey.setCategoryName(statusList.get(0).getCategoryName());
survey.setValue((String) (row.get("value")));
survey.setUnit((String) row.get("unit"));
survey.setTimestamp(row.get("timeStamp") + "");
survies.add(survey);
System.out.println(survey.toString());
}
} catch (BadSqlGrammarException e) {
e.printStackTrace();
} catch (Exception e) {
System.out.println(sqlValues);
e.printStackTrace();
}
return survies;
}
Thanks in advance!
You must convert the timestamp to the dateformat you want in your class. The default string representation of Timestamp is including the nanoseconds at the end. If you want to change that you can always do this:
Timestamp t = Your timestamp;
SimpleDateFormat df = new SimpleDateFormat("YYYY.MM.dd HH:mm:ss");
String s = df.format(t);
Your code contains the line row.get("timeStamp") + "", which will effectively call row.get("timeStamp").toString() + "". Assuming the row contains a java.sql.Timestamp object, it will return the timestamp formatted like yyyy-mm-dd hh:mm:ss.fffffffff.
If this is not desired, create a SimpleDateFormat object to represent the desired formatting, and then call SimpleDateFormat#format(row.get("timeStamp")) to have your value formatted properly.
The SimpleDateFormat class describes how to define your date and time patterns. Make sure to create the SimpleDateFormat object only once, since it's relatively expensive to create it. Many people define it as a private static final variable so it can be reused many times.
TL;DR
You seem to be mixing up two things that you should try to distinguish: the value of your timestamp and the format of it.
You should not wish to use the Timestamp class. It is poorly designed and long outdated. Instead use OffsetDateTime, Instant or LocalDatetime from java.time.
Also in your Survey class do not keep the timestamp as a String. Still use one of the java.time classes mentioned. Only when you need to give string output, format into a string in the desired format. For this purpose, depending on what you need the string for you can probably do better than just avoiding the .0 at the end.
Your problem line
The center of your problem is here:
survey.setTimestamp(row.get("timeStamp") + "");
You are concatenating a Timestamp from the query and a String. In order to do this Java converts the Timestamp to a String too by calling its toString method. Timestamp.toString() returns a string with at least one decimal on the seconds (and more, up to 9, if there are more non-zero decimals).
Solution: java.time
A timestamp means (a representation of) a point in time. The best class to use for a point in time is Instant from java.time. So in your Survey class define timestamp to be an Instant.
For a moment I assume that you are stuck with the List<Map<String,Object>> that Spring queryForList() returns, and that we cannot avoid the timestamp there being a java.sql.Timestamp. If so convert the Timestamp to an Instant as soon as you get hold of it:
survey.setTimestamp(((Timestamp) row.get("timeStamp")).toInstant());
With a more modern query tool you can get either an OffsetDateTime, an Instant or a LocalDateTIme directly from your database and avoid the Timestamp class completely.
How to format your Instant (or other java.time object) into a string for presentation to the user or for interchange with another system depends so much on the context and requirements. Here’s just one simple example:
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
String formattedTimestamp = survey.getTimetamp()
.atZone(ZoneId.systemDefault())
.format(formatter);
System.out.println(formattedTimestamp);
Example output:
Sep 13, 2020 2:26:40 PM
The output from the example code will depend not only on time zone, also on locale.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Questions and answers about using the java.time classes with your SQL database:
Getting the date from a ResultSet for use with java.time classes
Best way to Change java.sql.TimeStamp to UTC format in Java
Documentation of the JdbcTemplate.queryForList method that you used
try this one line solution
survey.setTimestamp(row.get("timeStamp").toString().replaceAll("\\.\\d+", ""));
if performance is critical then this solution is better
String ts = row.get("timeStamp").toString();
ts = ts.substring(0, str.indexOf('.'));