Computing the time elapsed in Java - java

Is javax.swing.Timer timer; enough for computing the time elapsed? I'm doing a Tetris project that also calculates the time it took for the player to play the game. In the play method,
public void play() {
reset();
onPlay = true;
Thread thread = new Thread(new Game());
thread.start();
rightPanel.setPlayButtonEnable(false);
rightPanel.setPause(true);
rightPanel.setStop(true);
playItem.setEnabled(false);
pauseItem.setEnabled(true);
stopItem.setEnabled(true);
setBlockColorItem.setEnabled(false);
setBGColorItem.setEnabled(false);
rightPanel.requestFocus();
//time.start();
//timeElapsed = System.currentTimeMillis();
RightPanel.timeElapsedTextField.setText("");
timer.start();
startTime = System.currentTimeMillis();
}
In the stop method,
public void stop() {
isStop=true;
onPlay = false;
if (piece != null)
piece.isMoving = false;
playItem.setEnabled(true);
rightPanel.setPlayButtonEnable(true);
pauseItem.setEnabled(false);
rightPanel.setPauseButtonLabel(true);
stopItem.setEnabled(false);
resumeItem.setEnabled(false);
setBlockColorItem.setEnabled(true);
setBGColorItem.setEnabled(true);
rightPanel.setPause(false);
rightPanel.setStop(false);
reset();
//time.stop();
timer.stop();
long delta = (System.currentTimeMillis() - startTime)/10;
RightPanel.timeElapsedTextField.setText(Double.toString(delta/100.0) + " seconds");
}
Are those enough? Whenever try to display the time elapsed, the JTextField displays nothing. What can be wrong? Thank you very much!
public void run() {
int column = 4, style = Piece.SHAPES[(int) (Math.random() * 7)][(int) (Math.random() * 4)];
//timer.start();
//startTime = System.currentTimeMillis();
while (onPlay) {
if (piece != null) {
if (piece.isAlive()) {
try {
Thread.currentThread().sleep(100);
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
continue;
}
}
checkFullLine();
if (isGameOver()) {
playItem.setEnabled(true);
pauseItem.setEnabled(true);
resumeItem.setEnabled(false);
rightPanel.setPlayButtonEnable(true);
rightPanel.setPauseButtonLabel(true);
displayGameOver();
return;
}
piece = new Piece(style, -1, column, board);
piece.start();
//numDropped = numDropped+1;
//RightPanel.scoreTextField.setText(numDropped+"");
style = Piece.SHAPES[(int) (Math.random() * 7)][(int) (Math.random() * 4)];
rightPanel.setTipStyle(style);
numDropped = numDropped+1;
RightPanel.numDroppedTextField.setText(numDropped+"");
//RightPanel.numDroppedTextField = new JTextField(numDropped+"");
long startTime = System.currentTimeMillis();
}
long estimatedTime = ((System.currentTimeMillis() - startTime)/1000);
RightPanel.timeElapsedTextField.setText(estimatedTime+"");
}

You should use nanoTime() to calculate elapsed time.
long startTime = System.nanoTime();
// ... the code being measured ...
long estimatedTime = System.nanoTime() - startTime;
You have a Thread running for Game. I think you should calculate startTime in start of run() method and estimatedTime end of run() method. I can only speculate since I don't know Game class code.
Below is the simple illustration of how you can do it.
import java.util.concurrent.TimeUnit;
public class Demo implements Runnable {
private volatile boolean shouldRun = true;
#Override
public void run() {
long startTime = System.nanoTime();
while (shouldRun) {
// do nothing
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println(TimeUnit.NANOSECONDS.toSeconds(estimatedTime));//Print apprx 5 seconds since time may be required to actually enter in to run method after start
}
public void stop() {
shouldRun = false;
}
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
Thread thread = new Thread(demo);
thread.start();
Thread.sleep(5000);// 5 Seconds sleep
demo.stop();// Stop the thread
}
}

tl;dr
Duration
.between(
then ,
Instant.now()
)
.toSeconds()
Details
Timer does not measure elapsed time.
Your calls to System.currentTimeMillis measure elapsed time. But that method is obsolete with Java 8 and later. Instead, use java.time.Instant.
Instant represents a moment in UTC with a resolution of nanoseconds. However, the hardware clocks in current conventional computers do not track time that accurately, so Instant.now captures the current moment with a resolution of microseconds in Java 9 and later (and milliseconds in Java 8 only).
Instant start = Instant.now() ;
…
Instant stop = Instant.now() ;
To represent the time elapsed between those two moments, use Duration.
Duration d = Duration.between( start , stop ) ;
Generate text in standard ISO 8601 for that elapsed time.
String output = d.toString() ;
Or interrogate for a count of seconds or some such.
long secondsElapsed = d.toSeconds() ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Related

How to update Date() every second?

I am trying to make a program that updates currentTime every second so that in the console it will go 1s, 2s, 3s, 4s and so on.
public static void main(String[] args) throws InterruptedException{
OSpanel runner = new OSpanel();
runner.currentTime();
}
public static void currentTime() throws InterruptedException{
if(true) {
Date currentTime = new Date();
while(true) {
Thread.sleep(1000);
System.out.println(currentTime);
Thread.sleep(1000);
System.out.println(currentTime);
}
}
}
java.time
The java.util Date-Time API is outdated and error-prone. It is recommended to stop using it completely and switch to the modern Date-Time API*.
You can use Instant#now to get the current instant of time. In order to get it every second, you can use ScheduledExecutorService#scheduleWithFixedDelay e.g.
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
Executors.newScheduledThreadPool(1)
.scheduleWithFixedDelay(
() -> System.out.println(Instant.now()),
0,
1,
TimeUnit.SECONDS
);
}
}
Output from a sample run:
2021-10-03T13:53:42.462768Z
2021-10-03T13:53:43.469758Z
2021-10-03T13:53:44.470316Z
...
ONLINE DEMO
An Instant represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Note: If you want to print just the running second, replace the print statement with the following:
System.out.println(ZonedDateTime.now(ZoneOffset.UTC).getSecond() + "s")
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project. Android 8.0 Oreo already provides support for java.time.
You are updating currentTime outside of the while loop - you are outputting the date to the console every second but you are not updating the time.
Try this:
Main.java
public static void main(String[] args) throws InterruptedException{
OSpanel runner = new OSpanel();
runner.currentTime();
}
OSpanel.java
public void currentTime() throws InterruptedException{
int counter = 1;
while (true) {
System.out.println(counter + "s");
Thread.sleep(1000);
counter++;
}
}
}
Duration
Adding to the good Answer by Avinash, let me show the use of Duration to track elapsed time.
Record a starting moment.
Instant start = Instant.now() ; // Capture the current moment as seen in UTC.
At any other moment, calculate elapsed time on a scale of hours-minutes-seconds using the java.time.Duration class.
Duration d = Duration.between( start , Instant.now() ) ;
Generate text representing that elapsed time in standard ISO 8601 format: PnYnMnDTnHnMnS .
String output = Duration.toString() ;
PT21s
To generate text in a custom format, write a method that accesses the various parts. Call toHoursPart, toMinutesPart, etc. Put those parts together in whatever way you desire.
Pulling this all together, in the code of that other Answer, change this line:
() -> System.out.println( Instant.now() ) ,
… to this line:
() -> System.out.println( Duration.between( start , Instant.now() ).toString() ) ,
… or call your custom formatting method.
This will create a thread pool with one thread which will execute the lambda printing the number of seconds since the program started once every second.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SOAnswer {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
long start = System.currentTimeMillis();
executorService.scheduleAtFixedRate(() -> System.out.println(String.format("%ds", (System.currentTimeMillis() - start) / 1000)), 0, 1, TimeUnit.SECONDS);
}
}

Wait/sleep until a specific time (e.g. Thursday at 10:59) in java

I’m currently using selenium in a web bot to purchase items on a website. When I search for the item I want to buy and it cannot be found I use driver.navigate().refresh() to refresh the page to see if it is there now, it will keep doing this until it finds the product when it is released on the page. However, I wish to start my bot a few hours before the release of the product which currently doesn’t work as after roughly 30 seconds of refreshing the page I get banned from the page due to the anti-ddos software they use. One option is to increase the delay between refreshing, however I need to catch the release of this product as soon as possible so I’m trying to find a way that my program can wait/sleep until 30 seconds before the release however I’m struggling to find a way to do this.
Just call Thread.sleep with the appropriate amount of milliseconds:
public static void main(String[] args) throws InterruptedException {
long currentTime = System.currentTimeMillis();
long releaseTime = currentTime + 1000 * 60 * 60 * 24 * 3; // 3 days
Thread.sleep(releaseTime - currentTime);
}
Another way would be to use java.time classes:
public static void main(String[] args) throws InterruptedException {
LocalDateTime now = LocalDateTime.now();
LocalDateTime release = LocalDateTime.of(2019, 10, 30, 13, 30);
long sleepDuration = Duration.between(now, release).toMillis();
TimeUnit.MILLISECONDS.sleep(sleepDuration);
}
Java 9 introduces new methods to the Duration class like toSeconds(), toMinutes() and so on.
You could also consider using a ScheduledExecutorService to schedule your tasks. This is especially useful if you have multiple tasks to schedule and don't want having multiple threads being blocked for that:
private static final ScheduledExecutorService service = new ScheduledThreadPoolExecutor(2);
private static ScheduledFuture<?> scheduleTask(Runnable task, LocalDateTime releaseTime) {
Duration duration = Duration.between(LocalDateTime.now(), releaseTime);
return service.schedule(task, duration.toSeconds(), TimeUnit.SECONDS);
}
In general, to sleep until the next Thursday at 10:59 you could use the following code:
LocalDateTime release = LocalDateTime.now()
.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY))
.withHour(10)
.withMinute(59);
Duration duration = Duration.between(LocalDateTime.now(), release);
TimeUnit.MILLISECONDS.sleep(duration.toMillis());
I think rather than sleeping you should take a look at scheduled tasks with cron expressions in Spring... that way you don't have a blocked thread just sitting there.
Scheduled Tasks with Spring
Cron Expressions

Given a day and time value, how can I find the next class in my timetable?

I have an array of cell[] containing all classes for a student ID, each cell has two variables relevant to this context:
int day and int startTime.
I also have two given values for the currentDay and currentTime, both in the same format as cell's day and startTime
I want to make a loop that finds the cell element that contains the next class.
I've tried looping through the cell array and selecting the closest day that contains at least one class, with some success, and I imagine that I would need to make another array that contains all the classes for that day then do the same logic to them. I just can't figure out how.
public Cell getNextClass(int studentID, Context context){
DBHelper dbHelper = new DBHelper(context);
Cell[] cell = dbHelper.queryCellData(studentID);
Date currentTime = Calendar.getInstance().getTime();
int currentDay = getCurrentDay(currentTime.toString());
Log.d(TAG, "currentDay: " + currentDay);
DateFormat dateFormat = new SimpleDateFormat("hh a");
String formattedDate= dateFormat.format(currentTime);
int currentHour = getCurrentHour(formattedDate);
//TODO Find next class given currentDay and currentHour
Cell nextClass = cell[0];
for (int i = 0; i < cell.length; i++) {
if (cell[i].getDay() >= currentDay && cell[i].getDay() <= nextClass.getDay()){
//?????
}
}
return nextClass;
}
}
In this example, cell[0] has one class on hour 12, and is displayed by default because the loop doesn't change anything. However at time of posting in NZ the values would be:
currentDay = 2
currentHour = 21
As shown in this screenshot of my timetable: i.imgur.com/X6HzR6y.png
The next class will be tomorrow, day 3, hour 8.
I recommend that you use java.time classes for the fields of your Cell class, not int:
public class Cell {
DayOfWeek day;
LocalTime startTime;
// Constructor, getters, toString, etc.
}
Also if you can, fit your class with a method that given a day of week and time calculates how long it is until the class occurs next time:
public Duration getTimeUntilNext(DayOfWeek currentDay, LocalTime currentTime) {
int daysUntil = day.getValue() - currentDay.getValue();
Duration time = Duration.ofDays(daysUntil).plus(Duration.between(currentTime, startTime));
if (time.isNegative()) { // over for this week
// Calculate time until next week’s occurrence
time = time.plusDays(7);
assert ! time.isNegative() : time;
}
return time;
}
If you cannot add this method to the Cell class, you may declare it a static method somewhere else and just add the cell as an extra parameter.
Now the rest is not so bad:
Cell[] classes = new Cell[] {
new Cell(DayOfWeek.THURSDAY, LocalTime.of(12, 0)),
new Cell(DayOfWeek.MONDAY, LocalTime.of(14, 0)),
new Cell(DayOfWeek.THURSDAY, LocalTime.of(10, 0)),
new Cell(DayOfWeek.FRIDAY, LocalTime.of(9, 0)),
new Cell(DayOfWeek.THURSDAY, LocalTime.of(6, 0))
};
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Pacific/Auckland"));
final DayOfWeek currentDay = now.getDayOfWeek();
final LocalTime currentTime = now.toLocalTime();
Comparator<Cell> comparatorByTimeUntilNext = new Comparator<Cell>() {
#Override
public int compare(Cell c1, Cell c2) {
return c1.getTimeUntilNext(currentDay, currentTime)
.compareTo(c2.getTimeUntilNext(currentDay, currentTime));
}
};
Cell nextClass = Collections.min(Arrays.asList(classes), comparatorByTimeUntilNext);
System.out.println("Next class from " + now + " is " + nextClass);
When I ran this just now, it output:
Next class from 2019-06-20T06:49:23.188+12:00[Pacific/Auckland] is THURSDAY at 10:00
Even if you stick with int fields in your class, you should still be able to use the idea. Then getTimeUntilNext may either still return a Duration or an int denoting the number of hours. I recommend the former, of course.
It seems to me that your code was formatting the current date and time into two different strings and parsing each of them back to get the day and time. That’s certainly the detour. Also as I already said in a comment, I recommend you avoid Date, Calendar, DateFormat and SimpleDateFormat since they are all poorly designed and long outdated.
Question: Can I use java.time on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Here is a good entry point for you: https://docs.oracle.com/javase/tutorial/datetime/iso/index.html
I do understand that you don't want to change your code. But since your application deals with dates and time it should use the build in api's the java language gives you. Just try to introduce it a controlled and testable way. The String class is also massive, but I bet you use that one, and not your own implementation.
Thank you for your reference, #Jocke however, I found my own way of doing it (poorly), so for posterity and the sake of any poor student who coded themselves into the same situation as me, I just added a bunch of messy edge cases for how time works in looped week scenarios like timetables. I will be using default libraries when possible in the future. Don't you worry. Here's the final method that I used.
public Cell getNextClass(int studentID, Context context){
DBHelper dbHelper = new DBHelper(context);
Cell[] cell = dbHelper.queryCellData(studentID);
Date currentTime = Calendar.getInstance().getTime();
int currentDay = getCurrentDay(currentTime.toString());
Log.d(TAG, "currentDay: " + currentDay);
DateFormat dateFormat = new SimpleDateFormat("hh a");
String formattedDate= dateFormat.format(currentTime);
int currentHour = getCurrentHour(formattedDate);
ArrayList<Cell> classesInDay = new ArrayList<>();
if (currentHour >= 17){
currentDay++; //If the current hour is past the end of the day, add 1 to the day so that tomorrow is scanned.
}
if (currentDay > 4){
currentDay = 0; //If the current day is greater than 4 (the weekend) set day to monday.
}
for (int i = 0; i < cell.length; i++) {
if (cell[i].getDay() == currentDay){
Log.d(TAG, "getNextClass: cell " + i +" has a class today");
classesInDay.add(cell[i]);
}
}
Cell nextClass = classesInDay.get(0);
Log.d(TAG, "getNextClass: "+classesInDay.size());
//If today's classes are over, then search tomorrow.
if (classesInDay.size() == 1 && classesInDay.get(0).getStartTime() < currentHour){
classesInDay.clear();
currentDay++;
for (int i = 0; i < cell.length; i++) {
if (currentDay > 4){
currentDay = 0; //If the current day is greater than 4 (the weekend) set day to monday.
}
if (cell[i].getDay() == currentDay){
Log.d(TAG, "getNextClass: cell " + i +" has a class today");
classesInDay.add(cell[i]);
}
}
nextClass = classesInDay.get(0); //ReApply default class for new day
}
for (int i = 1; i < (classesInDay.size()) ; i++) {
int diff1 = classesInDay.get(i).getStartTime() - currentHour-1; //Minus one to ensure that the next class if found, not the current one.
int diff2 = nextClass.getStartTime() - currentHour-1;
Log.d(TAG, "diff1: "+diff1+" diff2: "+diff2);
if (diff1 < 0){ //This means that the Test cell is before the current hour
}else if(diff2 < 0){ //This means that the current choice is before the current hour
nextClass = classesInDay.get(i);
}else if (diff1 < diff2){ //This means that the test cell is closer to the current hour than the current choice
nextClass = classesInDay.get(i);
}else if (diff2 < diff1){ //This means that the current choice is closer to the current hour than the test cell
}
}
return nextClass;
}

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));
}

Understanding the condition for age validation logic

I am trying to calculate the age of the person based on the date of birth and doing some logic if its over 18 years. I had my code written and it was working fine, but I stumbled upon a code I found online and I am not getting one condition in that. The code is:
public class AgeValidation {
public static void main(String[] args) {
getAge("29-12-1999");
}
private static void getAge(String dob1) {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
Date dob;
try {
dob = format.parse(dob1);
Calendar dob2 = Calendar.getInstance();
dob2.setTime(dob);
Calendar today = Calendar.getInstance();
int age = today.get(Calendar.YEAR) - dob2.get(Calendar.YEAR);
if(dob2.after(today)) {
System.out.println("Future date not allowed");
System.exit(0);
}
if (today.get(Calendar.MONTH) < dob2.get(Calendar.MONTH)) {
System.out.println("First if condition");
age--;
} else if (today.get(Calendar.MONTH) == dob2.get(Calendar.MONTH)
&& today.get(Calendar.DAY_OF_MONTH) < dob2.get(Calendar.DAY_OF_MONTH)) {
System.out.println("else if condition");
age--;
}
if (age < 18) {
System.out.println(age);
System.out.println("Underage");
} else {
System.out.println(age);
System.out.println("18 years");
//Some logic
}
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Need addressing on below points:
I have added a condition if DOB year is after Current year it should not proceed.
if(dob2.after(today)) {
System.out.println("Future date not allowed");
System.exit(0);
}
Is it correct to use System.exit(0); or is there some better approach to stop further execution.
In the code that I found online I saw a condition as
` if (today.get(Calendar.MONTH) < dob2.get(Calendar.MONTH))`
I am not getting in which scenario will this be executed.
Is there any use case in which this code will not work (I cannot think of any)
java.time
You should not (as in not) want to use the long outdated classes SimpleDateFormat, Date and Calendar. Especially the first is notoriously troublesome, but we have better replacements for all of them in java.time, the modern Java date and time API also known as JSR-310.
And even more so because the modern API has a method for counting years between two dates built-in. Not only is it easier to write the code, more importantly it is easier to read and understand, and you can be more sure of the correctness.
private static final DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("dd-MM-uuuu");
private static void getAge(String dob1) {
LocalDate dob = LocalDate.parse(dob1, dateFormatter);
LocalDate today = LocalDate.now(ZoneId.of("Asia/Dushanbe"));
if (dob.isAfter(today)) {
System.out.println("Future date not allowed");
} else {
int age = (int) ChronoUnit.YEARS.between(dob, today);
if (age < 18) {
System.out.println(age);
System.out.println("Underage");
} else {
System.out.println(age);
System.out.println("18 years");
//Some logic
}
}
}
With your example date of "29-12-1999" the above method prints:
17
Underage
Since it is never the same date in all time zones, please substitute your desired time zone instead of Asia/Dushanbe.
between() returns a long. In this case we can safely cast it to an int because LocalDate only handles years in the range -999 999 999 through 999 999 999, so the difference in years will never exceed the capacity of int.
Your questions
Use of System.exit(0); is generally questionable, though at times necessary. In my code I have avoided it using an if-else construct. Another option would be return;. I guess this would more give you what you wanted in case there were two calls to getAge() after each other. Yet another option is throwing an IllegalArgumentException, that would leave for the caller to decide to catch it or not.
The line you are quoting will not be executed when running your code here in December. Imagine running your code next January, then today’s month will be January and dob2’s month will still be December, so since January is before December, the code will be executed. Which will also be necessary for your method to calculate the correct age.
The code seems convoluted, as Jim Garrison said, but appears to be correct (not even with the outdated API needed it be this complex). I have not spotted a case that would not be handled correctly.
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310).
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and there’s a thorough explanation in this question: How to use ThreeTenABP in Android Project.
For learning to use java.time, see the Oracle tutorial or find other resoureces on the net.
I am giving you a solution which doesn't take care of leap year logic. But you can build upon this approach.
public static void main(String[] args) throws Exception{
String date ="11_10_1991";
SimpleDateFormat sdf = new SimpleDateFormat("dd_MM_yyyy");
Date birthDate = sdf.parse(date);
long ageInMillis = System.currentTimeMillis() - birthDate.getTime();
long years = ageInMillis /(365 * 24*60*60*1000l);
long leftover = ageInMillis %(365 * 24*60*60*1000l);
long days = leftover/(24*60*60*1000l);
System.out.println(years);
System.out.println(days);
}

Categories

Resources