You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
8.3 KiB

package shared.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.jetbrains.annotations.Nullable;
import visualiser.Controllers.RaceStartController;
import visualiser.model.ResizableRaceCanvas;
import java.time.Duration;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
* This class is used to implement a clock which keeps track of and
* displays times relevant to a race. This is displayed on the
* {@link ResizableRaceCanvas} via the
* {@link visualiser.Controllers.RaceViewController} and the
* {@link RaceStartController}.
*/
public class RaceClock {
/**
* The time zone of the race.
*/
private final ZoneId zoneId;
/**
* The start time of the race.
*/
private ZonedDateTime startingTime;
/**
* The current time of the race.
*/
private final StringProperty startingTimeProperty = new SimpleStringProperty();
/**
* The current time of the race.
*/
@Nullable
private ZonedDateTime currentTime;
/**
* The current time of the race.
*/
private final StringProperty currentTimeProperty = new SimpleStringProperty();
/**
* The time until the race starts, or elapsed time in the race after it has started.
*/
private StringProperty durationProperty = new SimpleStringProperty();
//Format strings.
/**
* Format string used for starting time.
*/
private String startingTimeFormat = "'Starting time:' HH:mm dd/MM/YYYY";
/**
* Format string used for current time.
*/
private String currentTimeFormat = "'Current time:' HH:mm dd/MM/YYYY";
/**
* Format string used for duration before it has started.
*/
private String durationBeforeStartFormat = "%02d:%02d:%02d";
/**
* Format string used for duration once the race has started.
*/
private String durationAfterStartFormat = "%02d:%02d:%02d";
/**
* Constructs a RaceClock using a specified starting ZonedDateTime.
* @param startingTime The ZonedDateTime that the race starts at.
*/
public RaceClock(ZonedDateTime startingTime) {
this.zoneId = startingTime.getZone();
//Set start time.
setStartingTime(startingTime);
}
/**
* 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);
setCurrentTime(utcTime.toInstant().atZone(this.zoneId));
}
/**
* 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);
}
/**
* Returns the starting time of the race.
* @return The starting time of the race.
*/
public ZonedDateTime getStartingTime() {
return startingTime;
}
/**
* Returns the race start time, expressed as the number of milliseconds since the unix epoch.
* @return Start time expressed as milliseconds since unix epoch.
*/
public long getStartingTimeMilli() {
return startingTime.toInstant().toEpochMilli();
}
/**
* Sets the starting time of the race.
* @param startingTime The starting time of the race.
*/
public void setStartingTime(ZonedDateTime startingTime) {
this.startingTime = startingTime;
//Convert time into string.
String startingTimeString = DateTimeFormatter.ofPattern(this.startingTimeFormat).format(startingTime);
//Use it.
setStartingTimeString(startingTimeString);
}
/**
* Returns the starting time of the race, as a string.
* @return The starting time of the race, as a string.
*/
public String getStartingTimeString() {
return startingTimeProperty.get();
}
/**
* Sets the starting time string of the race.
* This should only be called by {@link #setStartingTime(ZonedDateTime)}.
* @param startingTime The new value for the starting time string.
*/
private void setStartingTimeString(String startingTime) {
this.startingTimeProperty.setValue(startingTime);
}
/**
* Returns the starting time property.
* @return The starting time property.
*/
public StringProperty startingTimeProperty() {
return startingTimeProperty;
}
/**
* Returns the race duration, in milliseconds.
* A negative value means that the race has not started.
* @return Race duration in milliseconds.
*/
public long getDurationMilli() {
return getCurrentTimeMilli() - getStartingTimeMilli();
}
/**
* Returns the race duration, as a string.
* @return Duration as a string.
*/
public String getDurationString() {
return durationProperty.get();
}
/**
* Sets the duration time string of the race.
* @param duration The new value for the duration time string.
*/
private void setDurationString(String duration) {
this.durationProperty.setValue(duration);
}
/**
* Returns the duration property.
* @return The duration property.
*/
public StringProperty durationProperty() {
return durationProperty;
}
/**
* Returns the current time of the race.
* @return The current time of the race.
*/
@Nullable
public ZonedDateTime getCurrentTime() {
return currentTime;
}
/**
* Returns the race current time, expressed as the number of milliseconds since the unix epoch.
* @return Current time expressed as milliseconds since unix epoch.
*/
public long getCurrentTimeMilli() {
return currentTime.toInstant().toEpochMilli();
}
/**
* Sets the current time of the race.
* @param currentTime The current time of the race.
*/
private void setCurrentTime(ZonedDateTime currentTime) {
this.currentTime = currentTime;
//Convert time into string.
String currentTimeString = DateTimeFormatter.ofPattern(this.currentTimeFormat).format(currentTime);
//Use it.
setCurrentTimeString(currentTimeString);
//Update the duration string.
updateDurationString();
}
/**
* Updates the duration string based on the start time and current time.
* This requires {@link #currentTime} to be non-null.
*/
private void updateDurationString() {
//Calculates the duration in seconds.
long seconds = Duration.between(startingTime.toLocalDateTime(), currentTime.toLocalDateTime()).getSeconds();
//Check if the race has already started or not. This determines the format string used.
String formatString;
if (seconds < 0) {
//Race hasn't started.
formatString = this.durationBeforeStartFormat;
//The seconds value is negative, so we make it positive.
seconds = seconds * -1;
} else {
//Race has started.
formatString = this.durationAfterStartFormat;
}
//Format the seconds value.
//Hours : minutes : seconds.
String formattedDuration = String.format(formatString, seconds / 3600, (seconds % 3600) / 60, seconds % 60);
//Use it.
setDurationString(formattedDuration);
}
/**
* Returns the current time of the race, as a string.
* @return The current time of the race, as a string.
*/
public String getCurrentTimeString() {
return currentTimeProperty.get();
}
/**
* Sets the current time string of the race.
* @param currentTime The new value for the current time string.
*/
private void setCurrentTimeString(String currentTime) {
this.currentTimeProperty.setValue(currentTime);
}
/**
* Returns the current time property.
* @return The current time property.
*/
public StringProperty currentTimeProperty() {
return currentTimeProperty;
}
/**
* Returns the time zone of the race, as a string.
* @return The race time zone.
*/
public String getTimeZone() {
return zoneId.toString();
}
}