Total time of flight between two time zones? - java

If we leave Frankfurt at 14:05 and arrives in Los Angeles at 16:40. How long is the flight?
I tried the below :
ZoneId frank = ZoneId.of("Europe/Berlin");
ZoneId los = ZoneId.of("America/Los_Angeles");
LocalDateTime dateTime = LocalDateTime.of(2015, 02, 20, 14, 05);
LocalDateTime dateTime2 = LocalDateTime.of(2015, 02, 20, 16, 40);
ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, frank);
ZonedDateTime losDateTime2 = ZonedDateTime.of(dateTime2, los);
int offsetInSeconds = berlinDateTime.getOffset().getTotalSeconds();
int offsetInSeconds2 = losDateTime2.getOffset().getTotalSeconds();
Duration duration = Duration.ofSeconds(offsetInSeconds - offsetInSeconds2);
System.out.println(duration);
But I am not able to get the successful answer which is about 11hrs and 30min. Will some one please help me to figure out the problem above. Thanks you :)

getOffset is the wrong method. That gets the UTC offset for that zone at that point in time. It doesn't help determine the actual time of day.
One way is to explicitly get the Instant represented by each value, using toInstant. Then use Duration.between to calculate the amount of time elapsed.
Instant departingInstant = berlinDateTime.toInstant();
Instant arrivingInstant = losDateTime2.toInstant();
Duration duration = Duration.between(departingInstant, arrivingInstant);
Alternatively, since Duration.between works on Temporal objects, and both Instant and ZonedDateTime implement Temporal, you can just call Duration.between directly on the ZonedDateTime objects:
Duration duration = Duration.between(berlinDateTime, losDateTime2);
And lastly, there are shortcuts like the one atao mentioned that are fine if you want to get directly at a unit of measure such as total seconds. Any of these are acceptable.

Replace:
int offsetInSeconds = berlinDateTime.getOffset().getTotalSeconds();
int offsetInSeconds2 = losDateTime2.getOffset().getTotalSeconds();
Duration duration = Duration.ofSeconds(offsetInSeconds - offsetInSeconds2);
with:
long seconds = ChronoUnit.SECONDS.between(berlinDateTime, losDateTime2);
Duration duration = Duration.ofSeconds(seconds);
EDIT
I like the shorter (and shortest) answer given by Matt Johnson:
Duration duration = Duration.between(berlinDateTime, losDateTime2);

Related

Get hours, minutes and seconds correctly

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.

Converting UTC time string to UTC milliseconds

I am getting a time string that is the sunrise/sunset times in UTC in format HH:mm
example:
09:35
Currently I am doing this to convert the given time to the current date UTC using the java.time library
val utcZoneId = ZoneId.of("UTC")
val now = Instant.now()
val dateTimeFormater:DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(utcZoneId)
val date = dateTimeFormater.format(now)
val fullDateSunrise = "${date}T${data[0].sunrise}:00"
val local = LocalDateTime.parse(fullDateSunrise, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
val final = local.atZone(utcZoneId)
val utcSunrise = final.toInstant().toEpochMilli()
val fullDateSunset = "${date}T${data[0].sunset}:00"
val local2 = LocalDateTime.parse(fullDateSunset, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
val final2 = local2.atZone(utcZoneId)
val utcSunset = final2.toInstant().toEpochMilli()
I then pass the UTC milliseconds back the the client once I have them
It works how I need it to but I can help but feel there must be an easier way than getting a formatted UTC date string and combining that with the given time and then converting that to an actual DateTime object.
So the question is, is there an easier way to do this?
Sure, you definitely don't need to parse back and forth to strings. I assume an input of 09:35 means: At 09:35, local time, the sun will rise. Note that you're confusing things; UTC is a zone, an input like 09:35 is zoneless. I doubt this stamp represent 'UTC'; it would mean that the correct value for the sunrise today for Tokyo is -5:25, as it'll be 19:25, the previous day, in the UTC timezone when the sun rose in tokyo today.
Once you stop using the UTC zone it becomes muuuch simpler:
DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
LocalDateTime sunriseToday = LocalDateTime.now().with(LocalTime.parse("04:35", TIME_FORMAT));
ZonedDateTime inTokyo = sunriseToday.atZone(ZoneId.of("Asia/Tokyo"));
return inTokyo.toInstant().toEpochMilli();
Note that this would return the exact moment in time when the sun rises in tokyo. Printing it as an ISO stamp, that'd be 2020-06-09T19:35Z.
If you really want the epoch-millis that match 2020-06-10T04:35Z - which to be clear makes no sense, that is NOT when the sun rose in tokyo at all today! - then...
DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
LocalDateTime sunriseToday = LocalDateTime.now().with(LocalTime.parse("04:35", TIME_FORMAT));
ZonedDateTime inTokyo = sunriseToday.atZone(ZoneId.of("Asia/Tokyo"));
ZoneDateTime thisMakesNoSense = inTokyo.withZoneSameLocal(ZoneOffset.UTC);
return thisMakesNoSense.toInstant().toEpochMilli();
You don't have to convert Strings, use a ZonedDateTime instead and provide a desired zone.
Use some fun like this one:
fun convertToEpochMillis(time: String, zoneId: ZoneId): Long {
// parse the time to a LocalTime
val localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("HH:mm"))
// create a ZonedDateTime from the current date, the parsed time the given time zone
val zonedDateTime = ZonedDateTime.of(LocalDate.now(), localTime, zoneId)
// then return the representation of the instant in epoch millis
return zonedDateTime.toInstant().toEpochMilli()
}
and use it in a fun main() as follows
fun main() {
// define / receive a time
val time = "09:35"
// and a zone identifier
var zone = "UTC"
// use the method
val utcSunriseMillis = convertToEpochMillis(time, ZoneId.of(zone))
// and print a result statement
println("Sunrise time ${time} in ${zone} is ${utcSunriseMillis}")
// change the zone and do the same again with a different zone, just to see what happens...
zone = "America/Los_Angeles"
val laSunriseMillis = convertToEpochMillis(time, ZoneId.of(zone))
println("Sunrise time ${time} in ${zone} is ${laSunriseMillis}")
}
which then prints today (=> 2020-06-10)
Sunrise time 09:35 in UTC is 1591781700000
Sunrise time 09:35 in America/Los_Angeles is 1591806900000

Find the difference between a Posted UTC timestamp in minutes from the current time

What I'm trying to do: Check for the minutes until an event. (I'm in central time which is UTC -5 hours).
The object I get is a JSON Element that looks like this when I take the string:
/Date(1502964420000-0500)/
I should be able to:
//take the departure time and subtract it from the current time. Divide by 60
timeStamp = timeStamp.substring(6,16);
This gives me 1502964420 which I can use a time converter to get: Thursday, August 17, 2017 5:07:00 AM
Problem is.. How do I get the current time in the same format to subtract it?
(or if there's a better way to do this I'd gladly take that advice as well).
I would recommend looking at the datatype ZonedDateTime.
With this you can easily perform calculasions and conversions like this:
ZonedDateTime startTime = ZonedDateTime.now();
Instant timestamp = startTime.toInstant(); // You can also convert to timestamp
ZonedDateTime endTime = startTime.plusSeconds(30);
Duration duration = Duration.between(startTime, endTime);
if(duration.isNegative()){
// The end is before the start
}
long secondsBetween = duration.toMillis(); // duration between to seconds
Since you don't know about ZonedDateTime here is a quick overview how to convert string to ZonedDateTime:
Note: The String has to be in the ISO8601 format!
String example = "2017-08-17T09:14+02:00";
OffsetDateTime offset = OffsetDateTime.parse(example);
ZonedDateTime result = offset.atZoneSameInstant( ZoneId.systemDefault() );
You can either use Date currentDate = new Date() and then currentDate.getTime() to get the current Unix time in milliseconds or use the Calendar-class: Calendar currentDate = Calendar.getInstance() and currentDate.getTime().getTime() to get the current Unix time in milliseconds.
You can do the same with the date parsed from the json and then calculate the difference between the two values. To get the difference in minutes, just divide it then by (60*1000)

date.getTime() is shorter than System.getCurrentTimeMillis()

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

Getting today's UNIX timestamp with Calendar

I'm trying to get today's UNIX timestamp (seconds elapsed since 01/01/1970) at 00:00:00 with Java's Calendar:
public long getTodayTimestamp() {
Calendar c = Calendar.getInstance(Locale.getDefault());
c.setTimeInMillis(System.currentTimeMillis());
c.set( c.get(Calendar.YEAR),
c.get(Calendar.MONTH),
c.get(Calendar.DAY_OF_MONTH),
0, 0, 0
);
return c.getTimeInMillis()/1000;
}
The problem is it returns 1409608800 for today (September 1st), but when I try to convert it with some converter (for example http://www.onlineconversion.com/unix_time.htm) I get Mon, 01 Sep 2014 22:00:00 GMT which is midnight September 2nd my local time. Basically the method is returning the timestamp 24 hours ahead of what I need.
Where did I screw up?
The online converter returns the date UTC+0
Your are in TimeZone +2.
That means: 22:00:00 (UTC+0) is 00:00:00 (UTC+2) in your timezone
so, when i am not wrong (i hope so), everything seems to be correct.
EDIT:
The problem in your case:
the method getTodayTimestamp() returns the timestamp in UTC+0
(getCalendar.getTimeInMillis returns in UTC+0)
That means 00:00:00 in your locale (UTC+2) is 22:00:00 in UTC+0
this is what the converter is showing!
see here:
http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html#getTimeInMillis()
you can get the unix time the hard way :-)
at least you won't have to struggle with locales and etc
public class GetUnixTime {
public static void main(String[] args) throws IOException,
InterruptedException {
ProcessBuilder ps = new ProcessBuilder("date");
ps.redirectErrorStream(true);
Process pr = ps.start();
StringWriter sw = new StringWriter();
BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
sw.write(line);
}
pr.waitFor();
in.close();
sw.flush();
sw.close();
System.out.println(">"+sw.toString()+"<"); //parse this guy
}
}
tl;dr
java.time.Instant // Represent a moment in UTC.
.now() // Capture current moment in UTC.
.getEpochSecond() // Get the whole seconds component within an `Instant`, ignoring any fractional second.
Details
The answer by Ben appears to be correct.
Avoid j.u.Calendar
Date-time work is much easier with a decent library instead of the notoriously troublesome java.util.Date and .Calendar classes. Either use Joda-Time or the new java.time package in Java 8. Both of these libraries handle time zones explicitly and clearly.
java.time
Capture the current moment as seen in UTC using Instant. An Instant is always in UTC by definition.
Instant instant = Instant.now() ; // Capture current moment in UTC.
Apparently you want a count of whole seconds since the epoch references of first moment of 1970 in UTC.
long secondsSinceEpoch = instant.getEpochSecond() ; // Get the whole seconds component within an `Instant`, ignoring any fractional second.
Joda-Time
Example in Joda-Time 2.4.
DateTime nowParis = DateTime.now( DateTimeZone.forID( "Europe/Paris" ) );
DateTime nowUtc = nowParis.withZone( DateTimeZone.UTC );
long millisecondsSinceUnixEpoch = nowParis.getMillis(); // Some result when called on either nowParis or nowUtc.
long secondsSinceUnixEpoch = ( millisecondsSinceUnixEpoch / 1000L );
Call toString on those DateTime objects to verify their value.
Or in a single line.
long secondsSinceUnixEpoch = ( DateTime.now().getMillis() / 1000L );
Since we want only seconds since epoch of the current moment, the time zone does not matter.

Categories