How to reject 3-digit time in request in Spring Boot? - java

I am a newbie to Java. I was getting date and time in the following format in json,
{
"appointmentDate":"2017-05-30",
"appointmentTime":"23:30:00"
}
In the request, I was doing this,
#NotNull(message = "appointmentDate is required")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date appointmentDate;
#NotNull(message = "appointmentTime is required")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss")
private String appointmentTime;
In the above request class, I was using Date class for getting date and considering time as String.
Then in service class, I am trying to convert string object into date object, then search in the table to find the list of appointments in the table.
// convert into string
String dateString = null;
SimpleDateFormat sdfr = new SimpleDateFormat("yyyy-MM-dd");
dateString = sdfr.format( appointmentDate );
String dt = dateString + " " + appointmenTime;
Date startDateAndTime = null;
try {
//startDateAndTime = yyyyMMddFormat.parse(yyyyMMddFormat.format(dt));
startDateAndTime = new SimpleDateFormat(Constants.DATEANDTIME).parse(dt);
} catch (ParseException e) {
throw new IllegalArgumentException("Illegal date format");
}
but the issue I am facing is, even when i entered the wrong date it giving me output. No parse exception is thrown on the error in the date.
{
"appointmentDate":"20171-05-30",
"appointmentTime":"231:30:00"
}
this is my constants
public static final String DATEANDTIME = "yyyy-MM-dd HH:mm:ss";
and this is my repository query,
#Query(value = "select COUNT(a.doctorId) from Appointment a WHERE a.doctorId = :doctorId AND a.scheduleFromTime = :appointmentDateTime")
Long findAppointmentExists(#Param("doctorId") Long doctorId,
#Param("appointmentDateTime") Date appointmentDateTime);

Allow me to suggest you use the modern classes LocalDate and LocalTime for your date and time. Then it’s straightforward to do for example
if (appointmentDate.isAfter(LocalDate.now().plusYears(5)) {
throw new IllegalArgumentException("Too far into the future");
}
This will catch the 5-digit year and many other errors in the year value. Please set an appropriate limit yourself. You may similarly forbid dates in the past, maybe with a stricter limit. For the time this will be built-in since LocalTime only accepts times up to 23:59:59.999999999.
To combine both into one object
LocalDateTime startDateAndTime = LocalDateTime.of(appointmentDate, appointmentTime);
I have entered the code snippets off the top of my head, there could be a typo or two. If you cannot fix, please revert.
I haven’t got recent experience with Spring Boot. There’s more on using the modern Java date and time classes with Spring Boot and Jackson in this question: JSON Java 8 LocalDateTime format in Spring Boot. And no doubt still more in other places.

The first date format in your code is pointless, it is just converting to a Date(which was failing when I tried) and then immediately converting back to String anyway.
I've taken out the unnecessary lines from your example and posted my "working" example below.
String dt = appointmentDate + " " + appointmentTime;
Date startDateAndTime = null;
try
{
startDateAndTime = new SimpleDateFormat(Constants.DATEANDTIME).parse(dt);
} catch (ParseException e)
{
throw new IllegalArgumentException("Illegal date format");
}
System.out.println(startDateAndTime);
This tends to happen a lot when something is not working and you keep throwing code at it until you get entirely stuck. It's good to step back sometimes and try to work out what you are actually trying to achieve and then take another crack at it.

Related

String to Date Conversion mm/dd/yy to YYYY-MM-DD in java [duplicate]

This question already has answers here:
Java Date Error
(8 answers)
Closed 4 years ago.
I want to convert String values in the format of mm/dd/yy to YYYY-MM-DD Date. how to do this conversion?
The input parameter is: 03/01/18
Code to convert String to Date is given below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
When tried to convert using this method it shows the following error
java.text.ParseException: Unparseable date: "03/01/18"
As you say the input is in a different format, first convert the String to a valid Date object. Once you have the Date object you can format it into different types , as you want, check.
To Convert as Date,
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
date = formatter.parse(dateVlaue);
To Print it out in the other format,
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date)
You are writing it the wrong way. In fact, for the date you want to convert, you need to write
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yy");
The format you are passing to SimpleDateFormat is ("yyyy-MM-dd") which expects date to be in form 2013-03-01 and hence the error.
You need to supply the correct format that you are passing your input as something like below
public static Date stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yy");
try {
date = formatter.parse(dateVlaue);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
The solution for the above problem
Convert the String date value in the Format of "dd/mm/yy" to Date.
By using the converted Date can able to frame the required date format.
The method has given below
public static String stringToDateLinen(String dateVlaue) {
Date date = null;
SimpleDateFormat formatter = new SimpleDateFormat("dd/mm/yy");
String dateString = null;
try {
// convert to Date Format From "dd/mm/yy" to Date
date = formatter.parse(dateVlaue);
// from the Converted date to the required format eg : "yyyy-MM-dd"
SimpleDateFormat formatter1 = new SimpleDateFormat("yyyy-MM-dd");
dateString = formatter1.format(date);
} catch (ParseException e) {
e.printStackTrace();
}
return dateString;
}
EDIT: Your question said “String values in the format of mm/dd/yy”, but I understand from your comments that you meant “my input format is dd/mm/yy as string”, so I have changed the format pattern string in the below code accordingly. Otherwise the code is the same in both cases.
public static Optional<LocalDate> stringToDateLinen(String dateValue) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yy");
try {
return Optional.of(LocalDate.parse(dateValue, dateFormatter));
} catch (DateTimeParseException dtpe) {
return Optional.empty();
}
}
Try it:
stringToDateLinen("03/01/18")
.ifPresentOrElse(System.out::println,
() -> System.out.println("Could not parse"));
Output:
2018-01-03
I recommend you stay away from SimpleDateFormat. It is long outdated and notoriously troublesome too. And Date is just as outdated. Instead use LocalDate and DateTimeFormatter from java.time, the modern Java date and time API. It is so much nicer to work with. A LocalDate is a date without time of day, so this suites your requirements much more nicely than a Date, which despite its name is a point in time. LocalDate.toString() produces exactly the format you said you desired (though the LocalDate doesn’t have a format in it).
My method interprets your 2-digit year as 2000-based, that is, from 2000 through 2099. Please think twice before deciding that this is what you want.
What would you want to happen if the string cannot be parsed into a valid date? I’m afraid that returning null is a NullPointerException waiting to happen and a subsequent debugging session to track down the root cause. You may consider letting the DateTimeParseException be thrown out of your method (just declare that in Javadoc) so the root cause is in the stack trace. Or even throw an AssertionError if the situation is not supposed to happen. In my code I am returning an Optional, which clearly signals to the caller that there may not be a result, which (I hope) prevents any NullPointerException. In the code calling the method I am using the ifPresentOrElse method introduced in Java 9. If not using Java 9 yet, use ifPresent and/or read more about using Optional elsewhere.
What went wrong in your code?
The other answers are correct: Your format pattern string used for parsing needs to match the input (not your output). The ParseException was thrown because the format pattern contained hyphens and the input slashes. It was good that you got the exception because another problem is that the order of year, month and day doesn’t match, neither does the number of digits in the year.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Android method for consistent DateTime formatting across application

My application queries a SQLite DateTime string and I'm trying to write a single method than I can use across my application so that DateTime timestamps are formatted consistently. So far I have,
public class DateTimeUtils {
public static String formatDueDate(String queryResponse) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss z", Locale.US);
Date result = new Date();
try {
result = sdf.parse(queryResponse);
} catch (ParseException e) {
e.printStackTrace();
}
return result.toString();
}
}
Which is used in a situations such as
taskViewHolder.mDue.setText(formatDueDate(task.getDue().toString()));
I'd like the output to look like June 27, 20015, 5:30PM
The raw datetime String takes the form: 2015-08-10T17:28:00.000-04:00
My problems are currently the resulting timestamp format is incorrect and instead looks like Sun Aug 02 17:29:03 EDT 2015 and instead of parsing the inputted timestamp, just returns the current datetime.
I believe this is because my formatting is actually throwing an exception and i'm just returning the current Date() object. What should I change so that the datetime string is parsed correctly?
Well, since you're creating the Date object outside the try block, you're right, if there is a parsing error, the current date will return. SimpleDateFormat does that work for you in the parse method. You could reduce the possibility of the current date returning by assigning the Date variable to null instead of instantiating a new object.
If you already get an correct Date class, maybe you can generator your special format by Calendar class and StringBuilder class.
Like below:
String[] monthString = new String[12];
if (monthString[0] == null) {
// Get month string by android locale
// The String will like Jule or May ...
Map<String, Integer> months = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, getResources().getConfiguration().locale);
for (int i = 0; i < 12; i++) {
for (String month : months.keySet()) {
if (i == months.get(month).intValue()) {
monthString[i] = month;
months.remove(month);
break;
}
}
}
}
Calendar calendar=Calendar.getInstance();
calendar.setTime(result);
StringBuilder sb=new StringBuilder();
sb.append(monthString[calendar.get(Calendar.MONTH)]);
//spec your format string...
Hope this can help you.

Converting timestamp from parse.com in java

I'm getting my object's createdAt timestamp back from parse.com as 2014-08-01T01:17:56.751Z. I have a class that converts it to relative time.
public static String timeAgo(String time){
PrettyTime mPtime = new PrettyTime();
long timeAgo = timeStringtoMilis(time);
return mPtime.format( new Date( timeAgo ) );
}
public static long timeStringtoMilis(String time) {
long milis = 0;
try {
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sd.parse(time);
milis = date.getTime();
} catch (Exception e) {
e.printStackTrace();
}
return milis;
}
The problem is that this parses the date wrongly. Right now the result says 4 decades ago and this very wrong. What I'm I doing wrong?
Your current date format "yyyy-MM-dd HH:mm:ss" does not work for the given example 2014-08-01T01:17:56.751Z. The format is missing the characters T and Z and the milliseconds.
Change it to:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
to fix it.
Also check the examples in the JavaDoc of SimpleDateFormat, because it also shows the correct date format for your example: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html.
Expanding #Tom's answer:
The problem
When hardcoding 'Z', you assume that all dates were saved as UTC - which doesn't necessarily have to be the case.
The problem is that SimpleDateFormat does not recognize the literal 'Z'as an alias for UTC's '-0000' offset (For whatever reason, since it claims to be ISO-8601 compliant).
So you can't do
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
since this wrongly assumes all dates will always be written as in UTC, but you can't do
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
either, since this would not be able to parse the date when the literal 'Z' occurs.
Solution 1: Use javax.xml.bind.DatatypeConverter
This datatype converter actually is ISO8601 compliant and can be used as easy as
import javax.xml.bind.DatatypeConverter;
public Long isoToMillis(String dateString){
Calendar calendar = DatatypeConverter.parseDateTime(dateString);
return calendar.getTime().getTime();
}
If you use JAXB anyway, that would be the way to go.
Solution 2: Use conditional formats
final static String ZULUFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
final static String OFFSETFORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
/* This is a utility method, so you want the calling method
* to be informed that something is wrong with the input format
*/
public static Long isoToMillis(String dateString) throws ParseException{
/* It is the default, so we should use it by default */
String formatString = ZULUFORMAT;
if(! dateString.endsWith("Z") ) {
formatString = OFFSETFORMAT;
}
SimpleDateFormat sd = new SimpleDateFormat(formatString);
return sd.parse(dateString).getTime();
}
If you don't already use JAXB, you might want to put this method into a utility class.

Why java SimpleDateFormat can parse formatted text with extra characters?

I am parsing date strings from user input with MM-dd-yyyy HH:mm:ss format, and I found 12-20-2012 10:10:10 abcdexxxx could be pasred as well. How can this happen? Here is my code:
SimpleDateFormat df = new SimpleDateFormat( "MM-dd-yyyy HH:mm:ss" );
String currColValue = "12-20-2012 10:10:10 abcdexxxx";
try{
d=df.parse( currColValue );
}catch( ParseException e ){
System.out.println("Error parsing date: "+e.getMessage());
}
But there is no exception, the String value is parsed to be a Date. Why?
Per the Javadoc of the parse method:
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
(emphases mine).
Contrary to the implication of comments above, this has nothing to do with lenient parsing; rather, it's just that this method is not intended to consume the whole string. If you wish to validate that it consumed the whole string, I suppose you could set up a ParsePosition object and use the two-arg overload, and then examine the ParsePosition afterward to see if it parsed to the end of the string.
java.time
I should like to contribute the modern answer. This question was asked just the month before java.time, the modern Java date and time API, came out, which we all should be using now.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss");
String currColValue = "12-20-2012 10:10:10 abcdexxxx";
try {
LocalDateTime ldt = LocalDateTime.parse(currColValue, formatter);
// Do something with ldt
} catch (DateTimeParseException e) {
System.out.println("Error parsing date and time: " + e.getMessage());
}
Output:
Error parsing date and time: Text '12-20-2012 10:10:10 abcdexxxx' could not be parsed, unparsed text found at index 19
Contrary to the old SimpleDateFormat class the parse methods of the modern classes do insist on parsing the entire string (there is a way to parse only part of the string if that is what you require). Also please note the precision and clarity of the exception message. By the way, SimpleDateFormat is not only long outdated, it is also notoriously troublesome. You found just one of many surprising problems it has. I recommend that you no longer use SimpleDateFormat and Date.
Link: Oracle tutorial: Date Time explaining how to use java.time.
Check SimpleDateFormat.parse(String) doc. It clearly says it.
Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.
http://docs.oracle.com/javase/7/docs/api/java/text/DateFormat.html#parse(java.lang.String)
I want to contibute to the above correct answers with examples, using the method overload
public Date parse(String text, ParsePosition pos);
To parse the exact whole string just create a new ParsePosition object (with index 0, indicating that parse needs to start from begin), pass it to the method, and inspect its index property after parse.
Index is where the parse did end. If matches with string length then the string matches exactly form start to end.
Here is a unit test demonstrating it
public class DateParseUnitTest {
#Test
public void testParse(){
Date goodDate = parseExact("2019-11-05");
Date badDate1 = parseExact("foo 2019-11-05");
Date badDate2 = parseExact("2019-11-05 foo");
assert(goodDate != null);
assert(badDate1 == null);
assert(badDate2 == null);
}
#Nullable
private Date parseExact(#NonNull String text){
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
ParsePosition pos = new ParsePosition(0);
Date date = formatter.parse(text, pos);
if (pos.getIndex() != text.length())
return null;
return date;
}
}

Java: unparseable date exception

While trying to transform the date format I get an exception:unparseable date and don't know how to fix this problem.
I am receiving a string which represents an event date and would like to display this date in different format in GUI.
What I was trying to do is the following:
private String modifyDateLayout(String inputDate){
try {
//inputDate = "2010-01-04 01:32:27 UTC";
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").parse(inputDate);
return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date);
} catch (ParseException e) {
e.printStackTrace();
return "15.01.2010";
}
}
Anyway the line
String modifiedDateString = originalDate.toString();
is dummy. I would like to get a date string in the following format:
dd.MM.yyyy HH:mm:ss
and the input String example is the following:
2010-01-04 01:32:27 UTC
Does anyone know how to convert the example date (String) above into a String format dd.MM.yyyy HH:mm:ss?
Thank you!
Edit: I fixed the wrong input date format but still it doesn't work. Above is the pasted method and below is the screen image from debugging session.
alt text http://img683.imageshack.us/img683/193/dateproblem.png
#Update
I ran
String[] timezones = TimeZone.getAvailableIDs();
and there is UTC String in the array. It's a strange problem.
I did a dirty hack that works:
private String modifyDateLayout(String inputDate){
try {
inputDate = inputDate.replace(" UTC", "");
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(inputDate);
return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date);
} catch (ParseException e) {
e.printStackTrace();
return "15.01.2010";
}
}
But still I would prefer to transform the original input without cutting timezone away.
This code is written for Android phone using JDK 1.6.
What you're basically doing here is relying on Date#toString() which already has a fixed pattern. To convert a Java Date object into another human readable String pattern, you need SimpleDateFormat#format().
private String modifyDateLayout(String inputDate) throws ParseException{
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").parse(inputDate);
return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(date);
}
By the way, the "unparseable date" exception can here only be thrown by SimpleDateFormat#parse(). This means that the inputDate isn't in the expected pattern "yyyy-MM-dd HH:mm:ss z". You'll probably need to modify the pattern to match the inputDate's actual pattern.
Update: Okay, I did a test:
public static void main(String[] args) throws Exception {
String inputDate = "2010-01-04 01:32:27 UTC";
String newDate = new Test().modifyDateLayout(inputDate);
System.out.println(newDate);
}
This correctly prints:
03.01.2010 21:32:27
(I'm on GMT-4)
Update 2: as per your edit, you really got a ParseException on that. The most suspicious part would then be the timezone of UTC. Is this actually known at your Java environment? What Java version and what OS version are you using? Check TimeZone.getAvailableIDs(). There must be a UTC in between.
I encountered this error working in Talend. I was able to store S3 CSV files created from Redshift without a problem. The error occurred when I was trying to load the same S3 CSV files into an Amazon RDS MySQL database. I tried the default timestamp Talend timestamp formats but they were throwing exception:unparseable date when loading into MySQL.
This from the accepted answer helped me solve this problem:
By the way, the "unparseable date" exception can here only be thrown by SimpleDateFormat#parse(). This means that the inputDate isn't in the expected pattern "yyyy-MM-dd HH:mm:ss z". You'll probably need to modify the pattern to match the inputDate's actual pattern
The key to my solution was changing the Talend schema. Talend set the timestamp field to "date" so I changed it to "timestamp" then I inserted "yyyy-MM-dd HH:mm:ss z" into the format string column view a screenshot here talend schema
I had other issues with 12 hour and 24 hour timestamp translations until I added the "z" at the end of the timestamp string.
From Oracle docs, Date.toString() method convert Date object to a String of the specific form - do not use toString method on Date object. Try to use:
String stringDate = new SimpleDateFormat(YOUR_STRING_PATTERN).format(yourDateObject);
Next step is parse stringDate to Date:
Date date = new SimpleDateFormat(OUTPUT_PATTERN).parse(stringDate);
Note that, parse method throws ParseException

Categories