package seng302.Model; import com.github.bfsmith.geotimezone.TimeZoneLookup; import com.github.bfsmith.geotimezone.TimeZoneResult; import javafx.animation.AnimationTimer; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import seng302.GPSCoordinate; import java.time.Duration; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.Date; /** * Created by Gondr on 19/04/2017. */ public class RaceClock implements Runnable { private long lastTime; private final ZoneId zoneId; private ZonedDateTime time; private ZonedDateTime startingTime; private final StringProperty timeString; private StringProperty duration; public RaceClock(ZonedDateTime zonedDateTime) { this.zoneId = zonedDateTime.getZone(); this.timeString = new SimpleStringProperty(); this.duration = new SimpleStringProperty(); this.time = zonedDateTime; setTime(time); } public static ZonedDateTime getCurrentZonedDateTime(GPSCoordinate gpsCoordinate) { TimeZoneLookup timeZoneLookup = new TimeZoneLookup(); TimeZoneResult timeZoneResult = timeZoneLookup.getTimeZone(gpsCoordinate.getLatitude(), gpsCoordinate.getLongitude()); ZoneId zone = ZoneId.of(timeZoneResult.getResult()); return LocalDateTime.now(zone).atZone(zone); } public void run() { new AnimationTimer() { @Override public void handle(long now) { updateTime(); } }.start(); } /** * Sets time to arbitrary zoned time. * * @param time arbitrary time with timezone. */ private void setTime(ZonedDateTime time) { this.time = time; this.timeString.set(DateTimeFormatter.ofPattern("HH:mm:ss dd/MM/YYYY Z").format(time)); this.lastTime = System.currentTimeMillis(); if(startingTime != null) { long seconds = Duration.between(startingTime.toLocalDateTime(), time.toLocalDateTime()).getSeconds(); if(seconds < 0) duration.set(String.format("Starting in: %02d:%02d:%02d", -seconds/3600, -(seconds%3600)/60, -seconds%60)); else duration.set(String.format("Time: %02d:%02d:%02d", seconds/3600, (seconds%3600)/60, seconds%60)); } } /** * Sets time to given UTC time in seconds from Unix epoch, preserving timezone. * @param time UTC time */ public void setUTCTime(long time) { Date utcTime = new Date(time); setTime(utcTime.toInstant().atZone(this.zoneId)); } public ZonedDateTime getStartingTime() { return startingTime; } public void setStartingTime(ZonedDateTime startingTime) { this.startingTime = startingTime; } /** * Get ZonedDateTime corresponding to local time zone and given UTC time. * @param time time in mills * @return local date time */ public ZonedDateTime getLocalTime(long time) { Date utcTime = new Date(time); return utcTime.toInstant().atZone(this.zoneId); } /** * Updates time by duration elapsed since last update. */ private void updateTime() { this.time = this.time.plus(Duration.of(System.currentTimeMillis() - this.lastTime, ChronoUnit.MILLIS)); this.lastTime = System.currentTimeMillis(); setTime(time); } public String getDuration() { return duration.get(); } public StringProperty durationProperty() { return duration; } public String getTimeZone() { return zoneId.toString(); } public ZonedDateTime getTime() { return time; } public StringProperty timeStringProperty() { return timeString; } }