I have a code of which is used to create a 24 hour countdown.
This code checks if a "date" file exists and if it doesn't it creates one, which contains the date and time in 24 hourse/a day. It then gets the current time and compares the two, to create a countdown from the current date, to the date in the document.
This makes it possible to save the timer and check how far it has come even though the code is "turned off". The only issue is the fact that sometimes the timer turns negative. Like if I run the code from the start with no "date" file created on Monday, right before midnight, lets say Monday at half past eleven at night. Then if I stop the code and run it again when the current date has passed midnight, so it is actually Tuesday, but there is still missing up to 23 hours before it hits the actual goal timer. If this is the case, the time left in the countdown is negative. Like it would show "-1day 23hours 60minutes and 60seconds remaining". But if as an example it is run from scratch past midnight on Tuesday and then relaunch after 30 minutes the same day, there is no issue.
I hope you can understand what the issue is, it is slightly hard to express through text. But I have attached the whole code of mine, which is the exact one I am using and of which is having that issue. The code has comments for every actions happening, so it should be rather easy to understand.
static File dFileD = new File("date.txt");
static String date = "";
public static void main(String[] args) throws ParseException {
Timer tickTock = new Timer();
TimerTask tickTockTask = new TimerTask(){
public void run(){
try {
timFunRun(); //Timer calls method to start the countdown
} catch (ParseException e) {
e.printStackTrace();
}
}
};
tickTock.schedule(tickTockTask, 1000, 1000);
}
static void timFunRun() throws ParseException {
if (!dFileD.exists()){ //if it doesn't exist, first part
//Get current date and time
Calendar startDat = Calendar.getInstance();
System.out.println("Current date: " + startDat.getTime());
//Get that current date and time and then add 1 day
Calendar todAdd = Calendar.getInstance();
todAdd.add(Calendar.DATE, 1);
System.out.println("Date in 1 day: " + todAdd.getTime());
//Create a format for sending date to text file
SimpleDateFormat formDat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
String formatted = formDat.format(todAdd.getTime());
System.out.println("Formatted: " + formatted);
try{
PrintWriter dW = new PrintWriter("date.txt");
dW.println(formatted);
dW.close();
} catch (IOException e) {
}
System.out.println(formDat.parse(formatted));
} else { //if it does exist, second part
//Get current date and time
Calendar currentDeT = Calendar.getInstance();
System.out.println("Current date: " + currentDeT.getTime());
SimpleDateFormat formDat2 = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
Date dateFroText = null; //Get the "goal" date
try {
Scanner dateRead = new Scanner(dFileD);
while (dateRead.hasNextLine()) {
date = dateRead.nextLine();
dateFroText = formDat2.parse(date);
System.out.println("Date from text new format: " + dateFroText);
}
dateRead.close();
} catch (Exception e) {
System.out.println("Error!");
}
if (dateFroText != null){ //method to compare the current date and the goal date
Calendar dateFromTxtCal = Calendar.getInstance();
dateFromTxtCal.setTime(dateFroText);
int yearDiff = dateFromTxtCal.get(Calendar.YEAR) - currentDeT.get(Calendar.YEAR);
int dayDiff = ((yearDiff*365) + dateFromTxtCal.get(Calendar.DAY_OF_YEAR)) - currentDeT.get(Calendar.DAY_OF_YEAR);
dayDiff--;
int hourDiffer = dateFromTxtCal.get(Calendar.HOUR_OF_DAY)+23 - currentDeT.get(Calendar.HOUR_OF_DAY);
int minuDiff = dateFromTxtCal.get(Calendar.MINUTE)+60 - currentDeT.get(Calendar.MINUTE);
int secoDiff = dateFromTxtCal.get(Calendar.SECOND)+60 - currentDeT.get(Calendar.SECOND);
System.out.println(dayDiff + " days " + hourDiffer + " hours " + minuDiff +" minutes " + secoDiff + "seconds remaining");
}
}
}
You are working too hard. And you are using troublesome old date-time classes now obsoleted and supplanted by the java.time classes.
Also, if you only care about the next 24 hours, then no need for time zones. Just use UTC.
Instant
The Instant
class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instantNow = Instant.now();
Duration
A span of time unattached to the timeline is represented by the Duration
or Period
classes.
Duration duration = Duration.ofHours( 24 );
You can perform date-time math, adding a Duration
to an Instant
. The java.time classes use immutable objects, so the result of such manipulations is a fresh object with values based on the original.
Instant instantLater = instantNow.plus( duration );
To serialize date-time values such as those to text, use the standard ISO 8601 formats. The java.time classes use ISO 8601 by default when generating and parsing strings.
String output = instantNow.toString();
2017-04-03T03:18:48.183Z
To get the remaining time, let java.time do the math.
Duration remaining = Duration.between( Instant.now() , instantLater );
To report that in standard ISO 8601 format, call toString
. The format for durations is PnYnMnDTnHnMnS
. The P
marks the beginning (for Period
), and the T
separates any years-months-dates from hours-minutes-seconds.
String outputRemaining = remaining.toString();
PT23H34M22S
To generate a longer string, in Java 9 call the to…Part
method for each unit (hours, minutes, seconds). Oddly those methods were omitted from the original java.time.Duration
class in Java 8. You could look to the source code of Java 9 to write similar code.
Or more simply, manipulate the standard string. Delete the PT
. Replace the H
with hours
and so on. Do S
first to avoid the plural s
in the other two words. Admittedly this is kind of a hack, but it works thanks to some good luck in the occurrence of letters is the English spelling of hours-minutes-seconds.
String output = remaining.toString()
.replace( "PT" , "" )
.replace( "S" , " seconds " )
.replace( "H" , " hours " )
.replace( "M" , " minutes " ) ;
23 hours 34 minutes 22 seconds
ZonedDateTime
If you want to display the target date-time in the user's desired/expected time zone, apply a ZoneId
to get a ZonedDateTime
object.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
To generate a string in standard format, call toString
. Actually the ZonedDateTime
class extends the standard by appending the name of the time zone in square brackets.
To generate strings in other formats, search Stack Overflow for many examples of DateTimeFormatter
class.
java.util.Date
The Timer
class has not yet been updated to work with the java.time types. So convert back to a Date
object via new methods added to the old classes, in this case from
.
java.util.Date date = java.util.Date.from( instantLater );
Or use a count of milliseconds from the Duration
object.
long milliseconds = duration.toMillis() ;
ScheduledExecutorService
FYI, the Timer
and TimeTask
classes have been supplanted by the Executors framework. Specifically for your purpose, the ScheduledExecutorService
. Search Stack Overflow for many examples and discussions.
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
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
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.