I'm trying to create a simple Alarm Clock, but I stumbled upon a problem that I can't seem to fix. I'm trying to parse a string to a date so I can get the difference between the current time and the time to set off the alarm.
Here's my code to parse the time:
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
sdf.setTimeZone(getTimezone());
Date date = sdf.parse(args[0]);
Here's my getTimezone() method:
public static TimeZone getTimezone() {
Calendar cal = Calendar.getInstance();
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
String [] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
if (tz.getRawOffset() == milliDiff) {
// Found a match.
name = id;
break;
}
}
return TimeZone.getTimeZone(name);
}
And here's my code for figuring out the difference:
long diff = date.getTime() - System.currentTimeMillis();
So my problem is that the date.getTime() returns 79680000, while System.currentTimeMillis() returns 1473538047978 (This is of course different every time, but for some odd reason, date.getTime() is not).
Which means that I get a negative number when trying to figure out the difference, and therefore I cannot use it.
EDIT: After a little bit of debugging, I realised that it has to do with the year, month and day not being set, however I do not know how to get those.
You did notice that date.getTime() returns 79680000 which is 22 hours and 20 minutes after 1 January 1970. The problem is (as you noticed) that you did not parse year, month and day.
You can do it by:
SimpleDateFormat sdf = new SimpleDateFormat("DD/MM/YYYY hh:mm:ss");
Example input 20/04/2016 20:20:0 returns time as Mon Jan 04 20:20:00 CET 2016 (don't look at the timezone). It is 1451935200000 miliseconds after 1 January 1970.
Note: change string to match your format requirements (the syntax is self-explanatory).
The accepted answer by Ronin is correct. You are trying to put a time-of-day value into a date-time type.
java.time
Also, you are using troublesome old legacy date-time classes. Now supplanted by the java.time classes.
For a time-of-day value without a date and without a time zone, use LocalTime.
LocalTime alarmTime = LocalTime.parse( "12:34" );
Getting current time-of-day requires a time zone.
ZoneId z = ZoneId.of( "America/Montreal" );
LocalTime now = LocalTime.now( z );
But since we are setting an alarm, we care about the date too.
ZonedDateTime now = ZonedDateTime.now( z );
ZonedDateTime alarm = null;
if ( now.toLocalTime().isBefore( alarmTime ) ) {
alarm = ZonedDateTime.of( now.toLocalDate() , alarmTime , z );
} else {. // Else too late for today, so set alarm for tomorrow.
alarm = ZonedDateTime.of( now.toLocalDate().plusDays( 1 ) , alarmTime , z );
}
To calculate the elapsed time until the alarm, use the Duration class.
Duration untilAlarm = Duration.between( now , alarm );
You can interrogate the duration for a total number of milliseconds. But know that java.time classes are capable of handling nanoseconds.
long millis = untilAlarm.toMillis();
Updated.
You are using only time without a date with you date object in code (parses only time). If you add there date to you time, your date should be comparable to your System.getCurrentTimeMillis() call. And if you subtracting current millis from date in the past, you will have negative numbers. I prefer this convertion (date2 is after date1):
long diffInMillies = date2.getTime() - date1.getTime();
return TimeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS);
Related
I had previously created a question about this but I am still having problems.
I want to create a cooldown for player challenges. Currently there are two types of cooldowns, DAILY and WEEKLY.
The Daily cooldown lasts from the time the challenge is completed until 23:59 of the day, that is if you complete it at 14:00 you would have to wait 9 hours for the challenge to be enabled again.
The weekly cooldown is the same but the challenge is enabled until Thursday night, then the remaining time would be until Thursday night
So I want to get the hours, minutes and seconds until the cooldown ends but I can't get it right, I get giant numbers or it just doesn't work. I have never worked with dates and times, this would be my first time.
When a challenge is completed I store the date and time it was completed, something like this: 10-12-2021 and 20:33:58. Then I get them in the code to calculate the remaining time but when I get the String I get "-1" something like: 7d 10623m 637385s or 1d 2753m 1935s
My code for Daily:
public String getDailyCountdown(UUID uuid, Quest quest) {
QuestProperties questProperties = plugin.getQuestsCache().get(uuid, quest);
String countdownTimePlaceholder = "";
String[] completionDate = questProperties.getCompletionDate().split("-"), completionTime = questProperties.getCompletionTime().split(":");
int completionDay = Integer.parseInt(completionDate[0]), completionMonth = Integer.parseInt(completionDate[1]), completionYear = Integer.parseInt(completionDate[2]);
int completionHour = Integer.parseInt(completionTime[0]), completionMinutes = Integer.parseInt(completionTime[1]), completionSeconds = Integer.parseInt(completionTime[2]);
LocalDateTime fromDateTime = LocalDateTime.of(completionYear, completionMonth, completionDay, completionHour, completionMinutes, completionSeconds);
LocalDateTime toDateTime = LocalDateTime.of(2021, 11, 25, 23, 59, 0);
long current = System.currentTimeMillis();
long durationMillis = Duration.between(fromDateTime, toDateTime).toMillis();
Duration duration = Duration.between(fromDateTime, toDateTime);
long s = duration.getSeconds();
long s2 = s * 1000L;
long waitingTime = current - durationMillis;
long div = waitingTime / 1000L;
long waitingSeconds = s - div, waitingMinutes = waitingSeconds / 60L, waitingTimeHours = waitingMinutes / 60L;
if (durationMillis + s2 > current && durationMillis != 0L) {
if (waitingMinutes > 59L) waitingMinutes -= 60L * waitingTimeHours;
if (waitingMinutes > 0L) countdownTimePlaceholder = waitingMinutes + "m" + countdownTimePlaceholder;
if (waitingTimeHours > 0L) countdownTimePlaceholder = waitingTimeHours + "h" + countdownTimePlaceholder;
return countdownTimePlaceholder;
}
return "-1";
}
My code for Weekly:
QuestProperties questProperties = plugin.getQuestsCache().get(uuid, quest);
String countdownTimePlaceholder;
String[] completionDate = questProperties.getCompletionDate().split("-"), completionTime = questProperties.getCompletionTime().split(":");
int completionDay = Integer.parseInt(completionDate[0]), completionMonth = Integer.parseInt(completionDate[1]), completionYear = Integer.parseInt(completionDate[2]);
int completionHour = Integer.parseInt(completionTime[0]), completionMinutes = Integer.parseInt(completionTime[1]), completionSeconds = Integer.parseInt(completionTime[2]);
LocalDateTime fromDateTime = LocalDateTime.of(completionYear, completionMonth, completionDay, completionHour, completionMinutes, completionSeconds);
LocalDateTime toDateTime = LocalDateTime.now().plusDays(7);
LocalDateTime localDateTime = LocalDateTime.from(fromDateTime);
long days = localDateTime.until(toDateTime, ChronoUnit.DAYS);
long minutes = localDateTime.until(toDateTime, ChronoUnit.MINUTES);
long seconds = localDateTime.until(toDateTime, ChronoUnit.SECONDS);
countdownTimePlaceholder = days + "d" + minutes + "m" + seconds + "s";
return countdownTimePlaceholder;
How could I do it better or make it get the remaining time correctly?
Regards.
Date-time handling is tricky
You said:
I have never worked with dates and times, this would be my first time.
Date-time handling is surprisingly difficult and confusing. Be patient, study well, and run experiments.
Search Stack Overflow, as your issues have already been addressed many times. I’ll try to be brief here. Search to learn more and see more code examples.
Store date-time objects
As commented by Boris The Spider, use java.time to store your date-time values rather than repeatedly parsing their textual representation.
Or is QuestProperties outside your control?
If outside your control, at least simplify your parsing code. Change this:
int completionDay = Integer.parseInt(completionDate[0]), completionMonth = Integer.parseInt(completionDate[1]), completionYear = Integer.parseInt(completionDate[2]);
int completionHour = Integer.parseInt(completionTime[0]), completionMinutes = Integer.parseInt(completionTime[1]), completionSeconds = Integer.parseInt(completionTime[2]);
… to this:
LocalDate ld = LocalDate.of( Integer.parseInt(completionDate[2]) , Integer.parseInt(completionDate[1]) , Integer.parseInt(completionDate[0]) ) ; // year, month, day.
LocalTime lt = LocalTime.of( Integer.parseInt(completionTime[0]) , Integer.parseInt(completionTime[1]) , Integer.parseInt(completionTime[2]) ) ; // hour, minute, second.
Half-Open
Spans of time are usually best handled using Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive. So a day starts at the first moment of the day and runs up to, but does not include, the first moment of the following day.
Your code LocalDateTime.of(2021, 11, 25, 23, 59, 0) fails to account for the last full minute of the day, the minute between 23:59:00 and 00:00:00.
Change to:
LocalDateTime toDateTime = fromDateTime.toLocalDate().plusDays( 1 ).atStartOfDay() ;
Time zone
Your biggest problem is that you are ignoring the crucial issue of time zone. Getting the date, and determining the first moment of the day time, requires a time zone.
A day ends much earlier in the east than in the west. For example, at some moments, the date can be “tomorrow” in Tokyo Japan 🇯🇵 while simultaneously still “yesterday” in Edmonton Canada 🇨🇦.
So if you want to run your cooldowns to the end of the day as seen by the user, then you must account for the user’s time zone. You can query for their current default time zone. But ultimately best to confirm with the user.
ZoneId z = ZoneId.of( "America/Edmonton" ) ;
Capture the current moment as seen in that zone.
ZonedDateTime zdtNow = ZonedDateTime.now( z );
The LocalDateTime is the wrong class for your needs. That class represents a date and a time-of-day but lacks the context of a time zone or offset-from-UTC. We used ZonedDateTime to represent the current moment with a date, a time-of-day, and a time zone.
LocalDateTime = 🗓 + 🕛
ZonedDateTime = 🗓 + 🕛 + 🗺
Get the first moment of the next day. Notice how we ask java.time to determine when the day starts on that date in that zone. Do not assume a day starts at 00:00:00. Days may start at other times.
ZonedDateTime zdtStartOfTomorrow = zdtNow.toLocalDate().plusDays( 1 ).atStartOfDay( z ) ;
Time to elapse
If needed, calculate time to elapse.
Duration d = Duration.between( zdtNow , zdtStartOfTomorrow ) ;
Compare later moments to see if the deadline has passed.
if( ZonedDateTime.now( z ).isAfter( zdtStartOfTomorrow ) ) { … cooldown ended … }
Retrieving moment in UTC
In your case we do not want the current moment. We want to retrieve the user’s moment of last activity from your Quest system.
We parsed need date and the time above, in earlier part of this Answer. But your code did not account for retrieving the time zone. So I will assume the retrieved date and time are as seen « in UTC », with an offset of zero hours-minutes-seconds.
OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;
Adjust that to the user’s desired time zone.
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
Capturing the current moment
You asked:
When a challenge is completed I store the date and time it was completed, something like this: 10-12-2021 and 20:33:58.
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
To generate text representing that moment for data exchange, call toString. For text to present to user, let DateTimeFormatter class automatically localize. Both cases have been covered many times already on Stack Overflow.
This question already has answers here:
Getting specific date with timezone in Java
(1 answer)
how to get dateTime based on hour and minute
(4 answers)
Closed 1 year ago.
I am running a script in Jenkins. One piece of the script need to run at 07:00:00 Eastern Time.
The job is already scheduled on specific days and time. The job starts at 6am, but need to wait until 7am to run the next step
Using java, I can get current time/date using:
Date currentDate = new Date()
I think I need to compare currentDate with today's date at 7am so I can know how many seconds there is until 7am and put my build to sleep for that time.
My question is, how can I generate today's date at 7am?
Avoid using the terrible Date class. The legacy date-time classes were years ago supplanted by the modern java.time classes defined in JSR 310.
Capture the current moment as seen in your particular time zone.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Define your target.
LocalTime targetTime = LocalTime.of( 7 , 0 ) ;
Adjust to that time.
ZonedDateTime zdtTarget = zdt.with( targetTime ) ;
If that moment has passed, move to next day.
if( ! zdt.isBefore( zdtTarget ) ) {
zdtTarget = zdt.toLocalDate().plusDays( 1 ).atStartOfDay( z ).with( targetTime ) ;
}
Determine time to elapse.
Duration d = Duration.between( zdt.toInstant() , zdtTarget.toInstant() ) ;
Interrogate the duration object for whatever you need. For example, milliseconds:
long millis = d.toMillis() ;
Thread.sleep( millis ) ;
If you must use a java.until.Date for code not yet updated to java.time, you can convert.
java.until.Date date = Date.from( zdtTarget.toInstant() ) ;
Great question. It's easier to first create a Calendar object to set up the time, date and other custom inputs, then convert that to a Date object using the <CALENDAR_OBJECT>.getTime() member function.
EX: to get a Date object of 7 am today(10/25/2021) do the following:
Calendar cal = Calendar.getInstance(); // Calendar constructor
cal.set(Calendar.YEAR, 2021);
cal.set(Calendar.MONTH, 10);
cal.set(Calendar.DAY_OF_MONTH, 25);
cal.set(Calendar.HOUR_OF_DAY,7);
cal.set(Calendar.MINUTE,00);
cal.set(Calendar.SECOND,0);
cal.set(Calendar.MILLISECOND,0);
Date d = cal.getTime(); // convert to Date obj
For more info on the Calendar obj look at some documentation by Oracle (https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html)
I must convert a linux timestamp to android date.
i get this number from server
1386889262
I have written a small code snippet.
Date d = new Date(jsonProductData.getLong(MTIME));
SimpleDateFormat f = new SimpleDateFormat("dd.MM.yyyy");
.setTimeZone(TimeZone.getTimeZone("GMT"));
formatTime = f.format(d);
but it doesen't convert right, this is my result
17.01.1970
EDIT:
Normally i must get this here
12.12.2013
Is there an another method to get the right date???
if your UNIX time stamp is of 10 digit then it does not include milliseconds so do this first 1386889262*1000
and if its 13 digit then it includes milliseconds also then you do not have to multiply unix timestamp with 1000.
In Kotlin we can use this function:
val unix=1386889262*1000 /*if time stamp is of 10 digit*/
val dateFormat = SimpleDateFormat("dd-MM-yy HH:mm:ss");
val dt = Date(unix);
textview.settext(dateFormat.format(dt))
UNIX timestamp should be in milliseconds so multiply the Long value by 1000. So your value 1386889262 would be 1386889262000:
tl;dr
Instant.ofEpochSecond( 1386889262L )
.atZone( ZoneId.of( "Pacific/Auckland" ) )
.toLocalDate()
.toString()
java.time
You appear to have a count of whole seconds from the epoch reference date of first moment of 1970 in UTC, 1970-01-01T00:00:00Z.
The modern approach uses the java.time classes that supplant the troublesome old date-time classes bundled with the earliest versions of Java. For older Android see the ThreeTen-Backport and ThreeTenABP projects.
An Instant represents a point on the timeline in UTC with a resolution of nanoseconds (up to nine digits of decimal fraction).
Instant instant = Instant.ofEpochSecond( 1386889262L ) ;
To generate a String representing this moment, call toString.
String output = instant.toString() ;
Determining a date requires a time zone. For any given moment, the date varies around the globe by zone. Assign a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Africa/Casablanca" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Extract a date-only value for your purposes.
LocalDate ld = zdt.toLocalDate() ;
Generate a String.
String output = ld.toString() ;
For other formats in your String, search Stack Overflow for DateTimeFormatter.
Your timestamp or epoch time seems in sec "1386889262". You have to do something like this:
long date1 = 1386889262*1000;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yy HH:mm");
Date dt = new Date(date1);
datedisplay.setText(dateFormat.format(dt));
You can also get timestamp in java via
new Date().getTime() ;
It returns a long value.
EDIT: I have edited my question to include more information, I have tried many ways to do this already, asking a question on StackOverflow is usually my last resort. Any help is greatly appreciated.
I have a date (which is a Timestamp object) in a format of YYYYMMDDHHMMSS (e.g. 20140430193247). This is sent from my services to the front end which displays it in the format: date:'dd/MM/yyyy' using AngularJS.
How can I convert this into Epoch/Unix time?
I have tried the duplicate question that is linked to this, what I get returned is a different date.
I have also tried the following:
A:
//_time == 20140430193247
return _time.getTime()/1000; // returns 20140430193 - INCORRECT
B:
return _time.getTime(); // returns 20140430193247 (Frontend: 23/03/2608) - INCORRECT
C:
Date date = new Date();
//_time = 20140501143245 (i.e. 05/01/2014 14:32:45)
String str = _time.toString();
System.out.println("Time string is " + str);
//Prints: Time string is 2608-03-24 15:39:03.245 meaning _time.toString() cannot be used
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
date = df.parse(str);
} catch (ParseException e) {
}
return date; // returns 20140501143245 - INCORRECT
D:
date = new java.sql.Date(_time.getTime());
return date; // returns 2608-03-24 - INCORRECT
The following shows the todays date correctly:
Date date = new Date();
return date; // returns 1398939384523 (Frontend: 01/05/2014)
Thanks
I got the answer after quite a while of trying different ways. The solution was pretty simple - to parse the time to a string as toString() didn't work.
Date date;
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
try {
date = df.parse(String.valueOf(_time.getTime()));
} catch (ParseException e) {
throw new RuntimeException("Failed to parse date: ", e);
}
return date.getTime();
tl;dr
LocalDateTime
.parse(
"20140430193247" ,
DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" )
)
.atOffset(
ZoneOffset.UTC
)
.toEpochSecond()
java.time
Parse your input string as a LocalDateTime as it lacks an indicator of offset-from-UTC or time zone.
String input = "20140430193247" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
Now we have a date with time-of-day, around half-past 7 PM on April 30 of 2014. But we lack the context of offset/zone. So we do not know if this was 7 PM in Tokyo Japan or 7 PM in Toledo Ohio US, two different moments that happened several hours apart.
To determine a moment, you must know the intended offset/zone.
If you know for certain that an offset of zero, or UTC itself, was intended, apply the constant ZoneOffset.UTC to get an OffsetDateTime.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
How can I convert this into Epoch/Unix time?
Do you mean a count of seconds or milliseconds since the first moment of 1970 in UTC?
For a count of whole seconds since 1970-01-01T00:00Z, interrogate the OffsetDateTime object.
long secondsSinceEpoch = odt.toEpochSecond() ;
For milliseconds, extract a Instant object. An Instant represents a moment in UTC, and is the basic building-block class of java.time. Then interrogate for the count.
Instant instant = odt.toInstant() ;
long millisSinceEpoch = instant.toEpochMilli() ;
I am saving date's in a file in the following format as a string.
Sat Jul 21 23:31:55 EDT 2012
How can I check if 24 hours have passed? I am a beginner so please explain it a little bit =)
I am not sure if I completely understood the question - do you have two dates for comparison or do you wish to keep checking periodically if 24 hours have elapsed?
If comparing two date/times, I would suggest looking at joda or perhaps date4j. Using joda, one could look into using interval between two dates:
Interval interval = new Interval(previousTime, new Instant());
where previous time would be the time you mentioned
You can do something like this:
try {
// reading text...
Scanner scan = new Scanner( new FileInputStream( new File( "path to your file here..." ) ) );
String dateString = scan.nextLine();
// creating a formatter.
// to understand the format, take a look here: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
// EEE: Day name of week with 3 chars
// MMM: Month name of the year with 3 chars
// dd: day of month with 2 chars
// HH: hour of the day (0 to 23) with 2 chars
// mm: minute of the hour with 2 chars
// ss: second of the minute with 2 chars
// zzz: Timezone with 3 chars
// yyyy: year with 4 chars
DateFormat df = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy", Locale.US );
// parsing the date (using the format above, that matches with your date string)
Date date = df.parse( dateString );
// now!
Date now = new Date();
// gets the differente between the parsed date and the now date in milliseconds
long diffInMilliseconds = now.getTime() - date.getTime();
if ( diffInMilliseconds < 0 ) {
System.out.println( "the date that was read is in the future!" );
} else {
// calculating the difference in hours
// one hour have: 60 minutes or 3600 seconds or 3600000 milliseconds
double diffInHours = diffInMilliseconds / 3600000D;
System.out.printf( "%.2f hours have passed!", diffInHours );
}
} catch ( FileNotFoundException | ParseException exc ) {
exc.printStackTrace();
}
I would suggest storing your information as a java.util.Calendar which has a compareTo ()function.
If you want to compare now to current time, you can use System.getCurrentTimeMillis() to get the current time.
Define A Day
Do you really mean one day or 24-hours? Because of Daylight Saving Time nonsense, a day can vary in length such as 23 or 25 hours in the United States.
Avoid 3-Letter Time Zone Codes
That String format is a terrible representation of a date-time. It is difficult to parse. It uses a 3-letter time zone code, and such codes are neither standardized nor unique. If possible, choose another format. The obvious choice is ISO 8601, for example: 2014-07-08T04:17:01Z.
Use proper time zone names.
Avoid j.u.Date & .Calendar
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them.
Instead use either the venerable Joda-Time library or the new java.time package bundled in Java 8 (and inspired on Joda-Time).
Joda-Time
Here is some example code in Joda-Time.
Get the current moment.
DateTime now = DateTime.now();
Parse the input string.
String input = "Sat Jul 21 23:31:55 EDT 2012";
DateTime formatter = DateTimeFormat.forPattern( "EEE MMM dd HH:mm:ss zzz yyyy" ).with Locale( java.util.Locale.ENGLISH );
DateTime target = formatter.parseDateTime( input );
Calculate 24 hours (or next day).
DateTime twentyFourHoursLater = target.plusHours( 24 );
Test if current moment happened after.
boolean expired = now.isAfter( twentyFourHoursLater );
Or, if you want next day rather than 24-hours, use plusDays rather than plusHours. If necessary, adjust to desired time zone. Time zone is crucial as it defines the day/date and applies rules for anomalies such as Daylight Saving Time.
DateTime targetAdjusted = target.withZone( DateTimeZone.forID( "Europe/Paris" ) );
…
DateTime aDayLater = targetAdjusted.plusDays( 1 ); // Go to next day, accounting for DST etc.
boolean expired = now.isAfter( aDayLater ); // Test if current moment happened after.