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 * Construcs 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}