Will be a bug of SimpleDateFormat.parse - java

I have the following code:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone tz2 = TimeZone.getTimeZone("GMT");
dateFormat.setTimeZone(tz2);
String aDate = "2012-05-02 23:59:59";
for(int i=0 ; i<1000; i++){
dateFormat.setLenient(false);
ParsePosition p = new ParsePosition(0);
Date date = dateFormat.parse(aDate, p);
java.sql.Date sqlDate = null;
if (p.getIndex() != aDate.length())
throw new RuntimeException("just a test");
}
After testing plenty of times, it was very stranger. Basically, it never be finished completely, it ran into exception very randomly.
You see the code should be correct, but: it ran into exception when maybe i is 500 or i is 799 or i is 988(just take some examples here, means it was not getting happened when i = 0, it actually has finished some circles), the exception may get thrown in either line Date date = dateFormat.parse(aDate, p); or line throw new RuntimeException("just a test");;
Can everybody advice me whats wrong?

SimpleDateFormat.parse() uses an instance variable called calendar to build the date from the string. If two threads try to parse at the same time, the calendar variable will get clobbered and you'll get wrong results.
Making the variable not static won't necessarily help, since two threads could still be using the same controller. A better solution is to either create a new DateFormat object each time you parse a date, or use thread local storage. Better still, use JodaTime which has thread safe parsers.
Also consider the following points while using SDF.
Creating SimpleDateFormat is expensive. Don't use this unless it's done seldom.
OK if you can live with a bit of blocking. Use if formatDate() is not used much.
Fastest option IF you reuse threads (thread pool). Uses more memory than 2. and has higher startup overhead.
For applications both 2. and 3. are viable options. Which is best for your case depends on your use case. Beware of premature optimization. Only do it if you believe this is an issue.
For libraries that would be used by 3rd party I'd use option 3.

Your code just plain works, even after 20 reruns. So let's take a guess at what your real SSCCE might look like:
public static void main(String[] args) {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
dateFormat.setLenient(false);
final String aDate = "2012-05-02 23:59:59";
for (int i = 0; i < 2; i++)
new Thread() { public void run() {
for (int i = 0; i < 1000; i++) {
ParsePosition p = new ParsePosition(0);
dateFormat.parse(aDate, p);
if (p.getIndex() != aDate.length())
throw new RuntimeException("just a test");
}
System.out.println("Done");
}}.start();
}
This breaks, more or less the way you describe, and for obvious reasons. Listen to #Bhavik Ambani's advice, he's covering this for you.

p.getIndex() != aDate.length()
This statement is returning true, which means that when you parsed the date you did not consume the entirety of string aDate, mean aDate either has timezone or other information also

Related

Cannot find symbol for method format(DateTimeFormatter)?

I'm having a problem and I absolutely can't wrap my head around it. I am inexperienced in Java (or any language, for that matter), so excuse me if this is a stupid question.
From what I can tell, the format() method from java.time.format.DateTimeFormatter is not working how I thought it did. I am consistently getting the following error message (CLI):
Watch.java:609: error: cannot find symbol
String dateTimeDisplay = dateTime.format(dateTimeFormat);
^
symbol: method format(DateTimeFormatter)
location: variable dateTime of type Object
1 error
And here are the bits of code I think are relevant:
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class Watch {
protected int hour, hourTens, hourOnes, minute, minuteTens, minuteOnes, amPM;
public void watchMethod(String userInput) {
Object dateTime; // Create reference to object called dateTime
switch (userInput) {
case "1":
ZonedDateTime dateTimeHere = ZonedDateTime.now();
hour = dateTimeHere.getHour();
minute = dateTimeHere.getMinute();
// amPM is used exclusively to display AM/PM or a heart (see lines 580 to 567).
amPM = dateTimeHere.getHour();
dateTime = dateTimeHere;
break;
case "2":
ZonedDateTime dateTimeThere = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
hour = dateTimeThere.getHour();
minute = dateTimeThere.getMinute();
// amPM is used exclusively to display AM/PM or a heart (see lines 560 to 567).
amPM = dateTimeThere.getHour();
dateTime = dateTimeThere;
break;
case "3":
hour = -1;
minute = -1;
break;
}
// There is some code that prints things to console depending on the hour, minute, and amPM variables.
// I don't think this is relevant, but that's what's here.
// The following code prints out the actual date and time from a ZonedDateTime object.
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("hh:mm a 'on' EEEE, MMMM dd, yyyy");
String dateTimeDisplay = dateTime.format(dateTimeFormat); // ERROR
System.out.print("\nIt is currently " + dateTimeDisplay + "\n");
}
}
This is all one class.
I have checked that my imports are correct and that there are no misspellings anywhere! This is what other questions related to my problem have led me to believe was the issue, but it doesn't seem to be the case.
I'm also not very familiar with using Object as a reference type, so if that could be what's causing the problem, I'm not surprised. It's all I could figure out to break dateTimeHere/dateTimeThere out of the switch block.
Anyway, I'd greatly appreciate any help I receive.
QUICK EDIT: I also figured that if there were no suitable dateTime object (i.e. case "3"), this would also cause an error. I briefly added an if/else statement to remedy this, with some code in case "3" indicating that dateTime was null, but this did absolutely nothing. Let me know if I should re-add this, though.
EDIT: Thanks to a comment from #MarsAtomic, I see that this post could have used another read-through. My problem is that at the end of my code, I want to print out the ZonedDateTime data the switch statement retrieved, whether that is from the user's computer location or "Paris/Europe". Eventually, that "Paris/Europe" string will (ideally) have some user input from a subclass (as is the case with userInput, but that particular string was just for choosing one of the 3 cases already shown).
You probably meant to declare dateTime as a ZonedDateTime, as Object's definition does not include a format method.
Revised code:
ZonedDateTime dateTime = null; // Create reference to object called dateTime
//...
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("hh:mm a 'on' EEEE, MMMM dd, yyyy");
if (dateTime != null) {
String dateTimeDisplay = dateTime.format(dateTimeFormat);
System.out.print("\nIt is currently " + dateTimeDisplay + "\n");
}

Static method in multithreading

I have the following code in my class
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
public static String getTimeStampAsString(final long time) {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
return SDF_ISO_DATE.format(
new Date(time)) + " " + SDF_ISO_TIME.format(new Date(time)
);
}
In my multi threaded application the following method returns date in future, even for the current date, is the static method or variable is responsible for this?
edit:
I had the following code to reproduce and prove what are mentioned in the answers,but still not able to.Can some one help me for the same.
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> task = new Callable<String>(){
public String call() throws Exception {
return DateUtil.getTimeStampAsString(1524567870569L);
}
};
//pool with 50 threads
ExecutorService exec = Executors.newFixedThreadPool(50);
List<Future<String>> results = new ArrayList<Future<String>>();
//perform 10 date conversions
for(int i = 0 ; i < 50 ; i++){
results.add(exec.submit(task));
}
exec.shutdown();
//look at the results
for(Future<String> result : results){
System.out.println(result.get());
}
}
is the static method or variable is responsible for this?
Static variables. SimpleDateFormat isn't thread-safe, which should be obvious since you're modifying its internal state by calling setTimeZone(). It means that several threads could be doing that at the same time, which should feel like producing unpredictable results.
You need to build your formats locally rather than reuse some defined statically. Or better yet, drop Java's old time-managing classes and use java.time.* instead.
As an answer to your edit: how to reproduce the problem with thread-unsafety (not sure whether that really ought to be a separate question). Formatting the same date in two or more threads using the same SimpleDateFormat seems to go well (at least most often, no guarantee that it always will). Try formatting different date-times, and it will be very easy to get wrong results. I changed your task like this:
AtomicLong time = new AtomicLong(1_524_567_870_569L);
Callable<String> task = new Callable<String>(){
#Override
public String call() {
return DateUtil.getTimeStampAsString(time.getAndAdd(2_768_461_000L));
}
};
It’s easiest to see that the results are wrong when I also sort them in the output, so I have done that. I am only quoting the first few results from one run since this is enough to demonstrate the problem:
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-11 13:06:32
2018-07-29 14:07:33
2018-08-08 15:08:34
2018-10-01 16:09:35
…
The expected result was (obtained by declaring getTimeStampAsString() synchronized; also sorted afterward):
2018-04-24 11:04:30
2018-05-26 12:05:31
2018-06-27 13:06:32
2018-07-29 14:07:33
2018-08-30 15:08:34
2018-10-01 16:09:35
…
Already the fifth printed result has the day-of-month all wrong, 08 instead of 30, and there are many more errors in the full list. You may try it yourself. As you probably know, exact results are not reproducible, but you should get results that are wrong somehow.
PS Here’s my code for printing the results in sorted order in case you want to try it:
//look at the results
SortedSet<String> sorted = new TreeSet<>();
for (Future<String> result : results){
sorted.add(result.get());
}
sorted.forEach(System.out::println);
tl;dr
To capture the current moment and generate a string in your desired format (which is a modified form of standard ISO 8601 format), use the java.time classes. These classes are much simpler and vastly better designed. They are also thread-safe.
Instant.now().toString().replace( "T" , " " )
Current moment
Your method is named getCurrentTimeStamp(final Date date) yet you are passing an existing Date object set to a specific moment rather than capturing the current moment.
Nowhere in your code do I see you capturing the current moment. If you want the current moment, call Instant.now() as shown below.
Avoid legacy date-time classes
The legacy date-time classes such as Date & SimpleDateFormat are not thread-safe. One of many reasons to avoid these troublesome classes. They were supplanted years ago by the java.time classes.
java.time
As a moment in UTC, the java.util.Date class is replaced by the Instant class. Same idea, but Instant has a resolution in nanoseconds rather than milliseconds. And Instant::toString does not inject a time zone dynamically as Date::toString does.
To capture the current moment in UTC, call the static Instant.now() method.
Instant instant = Instant.now() ; // Capture current moment in UTC.
Parse your input number as a count of milliseconds since the epoch reference of first moment of 1970 in UTC.
Instant instant = Instant.ofEpochMilli( 1_524_567_870_569L ) ;
instant.toString(): 2018-04-24T11:04:30.569Z
No need for must of your code. No need for your DateUtil, as seen in code above. No need for custom formatting patterns, as your desired format happens to comply with the ISO 8601 standard used by default in the java.time classes. If the T in the middle bothers you or your users, replace with a SPACE.
String output = instant.toString().replace( "T" , " " ) ;
2018-04-24T11:04:30.569Z
ExecutorService blocking
You seem to misunderstand ExecutorService::shutdown. That method does not block to wait for tasks to complete. As your code is written, some tasks may not yet be done running until after you report results (partially-completed results).
Add a call to ExecutorService::awaitTermination, as seen in code below. Set a time-out long enough that if exceeded it must mean some problem occurred. To quote the doc:
Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
See example code below. For more discussion see this Question, ExecutorService - How to wait for completition of all tasks in non-blocking style
Threads
The java.time classes are thread-safe by design. They use the immutable objects pattern, returning fresh object based on existing values rather than changing (“mutating”) the original.
Example code. Your Question is confused about whether you want a hard-coded moment or the current moment. Switch to either by enabling the commented-out line in this example.
Callable < String > task = new Callable < String >() {
public String call () throws Exception {
long threadId = Thread.currentThread().getId();
// String moment = Instant.ofEpochMilli( 1524567870569L ).toString().replace( "T" , " " );
String moment = Instant.now().toString().replace( "T" , " " );
String output = ( moment + " | " + threadId );
return output;
}
};
// Pool with 5 threads
ExecutorService exec = Executors.newFixedThreadPool( 5 );
List < Future < String > > results = new ArrayList < Future < String > >();
// Perform a certain number of tasks.
int countAssignedTasks = 500;
for ( int i = 0 ; i < countAssignedTasks ; i++ ) {
results.add( exec.submit( task ) );
}
// Wait for tasks to complete.
Boolean completedBeforeTimeOut = null;
try {
exec.shutdown();
completedBeforeTimeOut = exec.awaitTermination( 5 , TimeUnit.SECONDS ); // Block until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Report results.
System.out.println( "completedBeforeTimeOut: " + completedBeforeTimeOut );
for ( Future < String > result : results ) {
try {
System.out.println( result.get() );
} catch ( InterruptedException e ) {
e.printStackTrace();
} catch ( ExecutionException e ) {
e.printStackTrace();
}
}
System.out.println( "BASIL - done." );
When run.
Note that the times are not chronological. In multi-threaded code, you cannot predict which tasks will be executed when.
2018-04-24 20:24:06.991225Z | 13
2018-04-24 20:24:06.991246Z | 14
2018-04-24 20:24:06.991236Z | 15
2018-04-24 20:24:06.991232Z | 16
2018-04-24 20:24:06.991222Z | 17
2018-04-24 20:24:07.067002Z | 16
2018-04-24 20:24:07.067009Z | 17
tz is effectively constant and the setters don't do anything after the first invocation of either method. Use a static initialiser to set the timezone right away to make the methods thread-safe.
private static final SimpleDateFormat SDF_ISO_DATE = new SimpleDateFormat("yyyy-MM-dd");
private static final SimpleDateFormat SDF_ISO_TIME = new SimpleDateFormat("HH:mm:ss");
static {
TimeZone tz = TimeZone.getTimeZone("UTC");
SDF_ISO_DATE.setTimeZone(tz);
SDF_ISO_TIME.setTimeZone(tz);
}
public static String getCurrentTimeStamp(final Date date) {
return SDF_ISO_DATE.format(date) + " " + SDF_ISO_TIME.format(date);
}
public static String getTimeStampAsString(final long time) {
return getCurrentTimeStamp(new Date(time));
}

CalendarArray get dates betweeen two Dates and skip a few

i am trying to create an Array of Calendar Objects where i get all dates between start and end, while skipping the date "skip". So let's imagine i have the dates:
start = 04.03.2016
skip = 21.07.2016
end = 02.03.2017
i tried something like this, but it didn't work that well:
public static Calendar[] getRange(Calendar start, Calendar skip, Calendar end){
Calendar[] daysRange = new Calendar[100];
Calendar placeholder = Calendar.getInstance();
while(start.before(end)){
if(placeholder.getTime() == skip.getTime()){
continue;
} placeholder.add(Calendar.Date, 1);
}
//(pseudocode)
//add placeholder to daysRange
return daysRange;
}
I would appreciate any tips on how i could continue.
Thanks!
First:
while (start.before(end)) {
You never modify start or end, so the loop is endless.
Second: you use one placeholder object, that means that your whole array points to that one object, create a new Calendar for every step en the loop.
Third: don't use an array, use an ArrayList.

Most efficient way of checking if Date object and Calendar object are in the same month

I am working on a project that will run many thousands of comparisons between dates to see if they are in the same month, and I am wondering what the most efficient way of doing it would be.
This isn't exactly what my code looks like, but here's the gist:
List<Date> dates = getABunchOfDates();
Calendar month = Calendar.getInstance();
for(int i = 0; i < numMonths; i++)
{
for(Date date : dates)
{
if(sameMonth(month, date)
.. doSomething
}
month.add(Calendar.MONTH, -1);
}
Creating a new Calendar object for every date seems like a pretty hefty overhead when this comparison will happen thousands of times, soI kind of want to cheat a bit and use the deprecated method Date.getMonth() and Date.getYear()
public static boolean sameMonth(Calendar month, Date date)
{
return month.get(Calendar.YEAR) == date.getYear() && month.get(Calendar.MONTH) == date.getMonth();
}
I'm pretty close to just using this method, since it seems to be the fastest, but is there a faster way? And is this a foolish way, since the Date methods are deprecated? Note: This project will always run with Java 7
I can't comment on whether to use the deprecated methods, but if you choose not to there's no need to instantiate a new Calendar for every Date you check. Just use one other Calendar and call setTime(date) before the check (or one Calendar for every thread if you parallelize it).
As a side note, I do have to agree with ChristopheD's comment that this is something worthy of a database.
I think you can define a static DateFormat to extract the month and year from Date and use both objects as date only.
public static DateFormat formatter= new SimpleDateForm("MMyyyy");
public static boolean sameMonth(Date date1, Date date2)
{
return formatter.format(date1).equals(formatter.format(date2));
}

Grouping objects by date: am I an idiot?

I have a list of objects called Activity:
class Activity {
public Date activityDate;
public double amount;
}
I want to iterate through List, group them by date and return a new list . Here's what I currently do:
private List<Activity> groupToList(List<Activity> activityList) {
SimpleDateFormatter sdf = new SimpleDateFormatter("YYYY-MM-DD");
Map<String,Activity> groupMap = new HashMap<String,Activity>();
for (Activity a in activityList) {
String key = sdf.format(a.getActivityDate());
Activity group = groupMap.get(key);
if (group == null) {
group = new Activity();
groupMap.add(key, group);
}
group.setAmount(group.getAmount() + a.getAmount());
}
return new ArrayList<Activity>(groupMap.values());
}
Is it a WTF to use the DateFormatter in this way?
I'm using the DateFormatter because each activityDate could have time information.
I would just use the date object itself as the key. If it it bothers you because the date object is mutable, then use its toString() value. No reason to go making formats.
If the issue is that you want to normalize the date by removing the time component, it would be much better to do that withing the Activity object and remove the time component. If the issue is still further that there are potential time zone issues, I would use JodaTime, but there is no object in the JDK currently that represents a pure date without time, so going with a string isn't outrageous, but it should be hidden behind a method in the Activity object and the fact that it is a date formatted string without a time component should be an implementation detail.
java.util.Date is a quite poor abstraction for your need; it is IMO fair to stick to strings if nothing better is around, HOWEVER Joda-time provides a good datatype for you: DateMidnight or alternatively LocalDate if Activity is strictly timezome-independant.
other than that, the code looks good to me, you might be able to shorten it a bit using an implementation of Multimap, to avoid messy null-checking code. to be honest, it doesn't get much shorter than your solution:
public List<Activity> groupedByDate(List<Activity> input) {
//group by day
final Multimap<DateMidnight, Activity> activityByDay
= Multimaps.index(input, new Function<Activity, DateMidnight>() {
#Override
public DateMidnight apply(Activity from) {
return new DateMidnight(from.activityDate);
}
});
//for each day, sum up amount
List<Activity> ret = Lists.newArrayList();
for (DateMidnight day : activityByDay.keySet()) {
Activity ins = new Activity();
ins.activityDate = day.toDate();
for (Activity activity : activityByDay.get(day)) {
ins.amount+=activity.amount;
}
}
return ret;
}
Why not simply create a HashMap<Date, Activity>() instead of the roundabout way with Strings?
Sorry, I didn't answer the question. The answer is: yes, unless I am an idiot ;)
You could do this using the Date as the key if you used a TreeMap and provided a Comparator that only compared the year, month and day and not the time.
As already mentioned the best solution is to represent your date with day precission. If this is not possible joda is nice library.
If you can ignore daylight saving time then grouping by date can be accomplished much easier. A unix time day is 86 400 s long. The timestamp does ignore leap seconds. (Your timer stops for one second or the leap second is distributed in some way.) All date values were day is equal are the same day:
int msPerDay = 86400 * 1000;
long day = new Date().getTime() / msPerDay
One minor point is to adjust the timezone. For my timezone CET (UTC/GMT +1 hour) the GMT day starts one our later:
new GregorianCalendar(2009, 10, 1, 1, 0).getTime().getTime() / msPerDay) ==
new GregorianCalendar(2009, 10, 2, 0, 59).getTime().getTime() / msPerDay) ==
new Date().getTime() / msPerDay
If the daylight saving time is significant the best way is to use joda. The rules are just to complicated and locale specific to implement.

Categories

Resources