001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer.geoimage; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.text.ParseException; 007import java.util.Objects; 008 009/** 010 * Timezone in hours.<p> 011 * TODO: should probably be replaced by {@link java.util.TimeZone}. 012 * @since 11914 (extracted from {@link CorrelateGpxWithImages}) 013 */ 014public final class Timezone { 015 016 static final Timezone ZERO = new Timezone(0.0); 017 private final double timezone; 018 019 Timezone(double hours) { 020 this.timezone = hours; 021 } 022 023 /** 024 * Returns the timezone in hours. 025 * @return the timezone in hours 026 */ 027 public double getHours() { 028 return timezone; 029 } 030 031 String formatTimezone() { 032 StringBuilder ret = new StringBuilder(); 033 034 double timezone = this.timezone; 035 if (timezone < 0) { 036 ret.append('-'); 037 timezone = -timezone; 038 } else { 039 ret.append('+'); 040 } 041 ret.append((long) timezone).append(':'); 042 int minutes = (int) ((timezone % 1) * 60); 043 if (minutes < 10) { 044 ret.append('0'); 045 } 046 ret.append(minutes); 047 048 return ret.toString(); 049 } 050 051 static Timezone parseTimezone(String timezone) throws ParseException { 052 053 if (timezone.isEmpty()) 054 return ZERO; 055 056 String error = tr("Error while parsing timezone.\nExpected format: {0}", "+H:MM"); 057 058 char sgnTimezone = '+'; 059 StringBuilder hTimezone = new StringBuilder(); 060 StringBuilder mTimezone = new StringBuilder(); 061 int state = 1; // 1=start/sign, 2=hours, 3=minutes. 062 for (int i = 0; i < timezone.length(); i++) { 063 char c = timezone.charAt(i); 064 switch (c) { 065 case ' ': 066 if (state != 2 || hTimezone.length() != 0) 067 throw new ParseException(error, i); 068 break; 069 case '+': 070 case '-': 071 if (state == 1) { 072 sgnTimezone = c; 073 state = 2; 074 } else 075 throw new ParseException(error, i); 076 break; 077 case ':': 078 case '.': 079 if (state == 2) { 080 state = 3; 081 } else 082 throw new ParseException(error, i); 083 break; 084 case '0': 085 case '1': 086 case '2': 087 case '3': 088 case '4': 089 case '5': 090 case '6': 091 case '7': 092 case '8': 093 case '9': 094 switch (state) { 095 case 1: 096 case 2: 097 state = 2; 098 hTimezone.append(c); 099 break; 100 case 3: 101 mTimezone.append(c); 102 break; 103 default: 104 throw new ParseException(error, i); 105 } 106 break; 107 default: 108 throw new ParseException(error, i); 109 } 110 } 111 112 int h = 0; 113 int m = 0; 114 try { 115 h = Integer.parseInt(hTimezone.toString()); 116 if (mTimezone.length() > 0) { 117 m = Integer.parseInt(mTimezone.toString()); 118 } 119 } catch (NumberFormatException nfe) { 120 // Invalid timezone 121 throw (ParseException) new ParseException(error, 0).initCause(nfe); 122 } 123 124 if (h > 12 || m > 59) 125 throw new ParseException(error, 0); 126 else 127 return new Timezone((h + m / 60.0) * (sgnTimezone == '-' ? -1 : 1)); 128 } 129 130 @Override 131 public boolean equals(Object o) { 132 if (this == o) return true; 133 if (!(o instanceof Timezone)) return false; 134 Timezone timezone1 = (Timezone) o; 135 return Double.compare(timezone1.timezone, timezone) == 0; 136 } 137 138 @Override 139 public int hashCode() { 140 return Objects.hash(timezone); 141 } 142}