001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.gpx;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.text.ParseException;
007import java.util.Objects;
008import java.util.regex.Matcher;
009import java.util.regex.Pattern;
010
011/**
012 * Timezone in hours.<p>
013 * TODO: should probably be replaced by {@link java.util.TimeZone}.
014 * @since 14205 (extracted from {@code CorrelateGpxWithImages})
015 */
016public final class GpxTimezone {
017
018    /**
019     * The timezone 0.
020     */
021    public static final GpxTimezone ZERO = new GpxTimezone(0.0);
022    private final double timezone;
023
024    /**
025     * Constructs a new {@code GpxTimezone}.
026     * @param hours timezone in hours
027     */
028    public GpxTimezone(double hours) {
029        this.timezone = hours;
030    }
031
032    /**
033     * Returns the timezone in hours.
034     * @return the timezone in hours
035     */
036    public double getHours() {
037        return timezone;
038    }
039
040    /**
041     * Formats time zone.
042     * @return formatted time zone. Format: ±HH:MM
043     */
044    public String formatTimezone() {
045        StringBuilder ret = new StringBuilder();
046
047        double tz = this.timezone;
048        if (tz < 0) {
049            ret.append('-');
050            tz = -tz;
051        } else {
052            ret.append('+');
053        }
054        ret.append((long) tz).append(':');
055        int minutes = (int) ((tz % 1) * 60);
056        if (minutes < 10) {
057            ret.append('0');
058        }
059        ret.append(minutes);
060
061        return ret.toString();
062    }
063
064    /**
065     * Parses timezone.
066     * @param timezone timezone. Expected format: ±HH:MM
067     * @return timezone
068     * @throws ParseException if timezone can't be parsed
069     */
070    public static GpxTimezone parseTimezone(String timezone) throws ParseException {
071        if (timezone.isEmpty())
072            return ZERO;
073
074        Matcher m = Pattern.compile("^([\\+\\-]?)(\\d{1,2})(?:\\:([0-5]\\d))?$").matcher(timezone);
075
076        ParseException pe = new ParseException(tr("Error while parsing timezone.\nExpected format: {0}", "±HH:MM"), 0);
077        try {
078            if (m.find()) {
079                int sign = "-".equals(m.group(1)) ? -1 : 1;
080                int hour = Integer.parseInt(m.group(2));
081                int min = m.group(3) == null ? 0 : Integer.parseInt(m.group(3));
082                return new GpxTimezone(sign * (hour + min / 60.0));
083            }
084        } catch (IndexOutOfBoundsException | NumberFormatException ex) {
085            pe.initCause(ex);
086        }
087        throw pe;
088    }
089
090    @Override
091    public boolean equals(Object o) {
092        if (this == o) return true;
093        if (!(o instanceof GpxTimezone)) return false;
094        GpxTimezone timezone1 = (GpxTimezone) o;
095        return Double.compare(timezone1.timezone, timezone) == 0;
096    }
097
098    @Override
099    public int hashCode() {
100        return Objects.hash(timezone);
101    }
102}