001package ezvcard.property; 002 003import java.time.DateTimeException; 004import java.time.OffsetDateTime; 005import java.time.ZoneId; 006import java.time.ZoneOffset; 007import java.util.LinkedHashMap; 008import java.util.List; 009import java.util.Map; 010import java.util.Objects; 011 012import ezvcard.VCard; 013import ezvcard.VCardVersion; 014import ezvcard.ValidationWarning; 015import ezvcard.parameter.Pid; 016 017/* 018 Copyright (c) 2012-2026, Michael Angstadt 019 All rights reserved. 020 021 Redistribution and use in source and binary forms, with or without 022 modification, are permitted provided that the following conditions are met: 023 024 1. Redistributions of source code must retain the above copyright notice, this 025 list of conditions and the following disclaimer. 026 2. Redistributions in binary form must reproduce the above copyright notice, 027 this list of conditions and the following disclaimer in the documentation 028 and/or other materials provided with the distribution. 029 030 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 031 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 032 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 033 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 034 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 035 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 036 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 037 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 038 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 039 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 040 041 The views and conclusions contained in the software and documentation are those 042 of the authors and should not be interpreted as representing official policies, 043 either expressed or implied, of the FreeBSD Project. 044 */ 045 046/** 047 * <p> 048 * Defines the timezone that the person lives/works in. 049 * </p> 050 * 051 * <p> 052 * <b>Code sample</b> 053 * </p> 054 * 055 * <pre class="brush:java"> 056 * VCard vcard = new VCard(); 057 * 058 * Timezone tz = new Timezone(ZoneOffset.ofHours(-5), "America/New_York"); 059 * vcard.addTimezone(tz); 060 * 061 * //using a Java "ZoneId" object 062 * ZoneId zoneId = ZoneId.of("America/New_York"); 063 * tz = new Timezone(zoneId); 064 * vcard.addTimezone(tz); 065 * </pre> 066 * 067 * <p> 068 * <b>Property name:</b> {@code TZ} 069 * </p> 070 * <p> 071 * <b>Supported versions:</b> {@code 2.1, 3.0, 4.0} 072 * </p> 073 * 074 * @author Michael Angstadt 075 * @see <a href="http://tools.ietf.org/html/rfc6350#page-22">RFC 6350 p.22</a> 076 * @see <a href="http://tools.ietf.org/html/rfc2426#page-16">RFC 2426 p.16</a> 077 * @see <a href="http://www.imc.org/pdi/vcard-21.doc">vCard 2.1 p.16</a> 078 */ 079public class Timezone extends VCardProperty implements HasAltId { 080 private ZoneOffset offset; 081 private String text; 082 083 /** 084 * Creates a timezone property. 085 * @param text a free-form string representing the timezone, preferably a 086 * timezone ID from the <a 087 * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson 088 * Database</a> (e.g. "America/New_York") 089 */ 090 public Timezone(String text) { 091 this(null, text); 092 } 093 094 /** 095 * Creates a timezone property. 096 * @param offset the UTC offset 097 */ 098 public Timezone(ZoneOffset offset) { 099 this(offset, null); 100 } 101 102 /** 103 * Creates a timezone property. 104 * @param offset the UTC offset 105 * @param text a free-form string representing the timezone, preferably a 106 * timezone ID from the <a 107 * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson 108 * Database</a> (e.g. "America/New_York") 109 */ 110 public Timezone(ZoneOffset offset, String text) { 111 setOffset(offset); 112 setText(text); 113 } 114 115 /** 116 * Creates a timezone property. 117 * @param timezone the timezone 118 */ 119 public Timezone(ZoneId timezone) { 120 this(OffsetDateTime.now(timezone).getOffset(), timezone.getId()); 121 } 122 123 /** 124 * Copy constructor. 125 * @param original the property to make a copy of 126 */ 127 public Timezone(Timezone original) { 128 super(original); 129 offset = original.offset; 130 text = original.text; 131 } 132 133 /** 134 * Gets the UTC offset. 135 * @return the UTC offset or null if not set 136 */ 137 public ZoneOffset getOffset() { 138 return offset; 139 } 140 141 /** 142 * Sets the UTC offset. 143 * @param offset the UTC offset 144 */ 145 public void setOffset(ZoneOffset offset) { 146 this.offset = offset; 147 } 148 149 /** 150 * Gets the text portion of the timezone. 151 * @return the free-form string representing the timezone, such as a 152 * timezone ID from the <a 153 * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson 154 * Database</a> (e.g. "America/New_York") 155 */ 156 public String getText() { 157 return text; 158 } 159 160 /** 161 * Sets the text portion of the timezone. 162 * @param text a free-form string representing the timezone, preferably a 163 * timezone ID from the <a 164 * href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Olson 165 * Database</a> (e.g. "America/New_York") 166 */ 167 public void setText(String text) { 168 this.text = text; 169 } 170 171 /** 172 * Creates a {@link ZoneId} representation of this class. 173 * @return a {@link ZoneId} object or null if this object contains no 174 * offset data 175 */ 176 public ZoneId toTimeZone() { 177 if (text != null) { 178 try { 179 return ZoneId.of(text); 180 } catch (DateTimeException ignore) { 181 //not a recognized timezone 182 } 183 } 184 185 if (offset != null) { 186 return offset; 187 } 188 189 return null; 190 } 191 192 /** 193 * Gets the TYPE parameter. 194 * <p> 195 * <b>Supported versions:</b> {@code 4.0} 196 * </p> 197 * @return the TYPE value (typically, this will be either "work" or "home") 198 * or null if it doesn't exist 199 */ 200 public String getType() { 201 return parameters.getType(); 202 } 203 204 /** 205 * Sets the TYPE parameter. 206 * <p> 207 * <b>Supported versions:</b> {@code 4.0} 208 * </p> 209 * @param type the TYPE value (this should be either "work" or "home") or 210 * null to remove 211 */ 212 public void setType(String type) { 213 parameters.setType(type); 214 } 215 216 /** 217 * Gets the MEDIATYPE parameter. 218 * <p> 219 * <b>Supported versions:</b> {@code 4.0} 220 * </p> 221 * @return the media type or null if not set 222 */ 223 public String getMediaType() { 224 return parameters.getMediaType(); 225 } 226 227 /** 228 * Sets the MEDIATYPE parameter. 229 * <p> 230 * <b>Supported versions:</b> {@code 4.0} 231 * </p> 232 * @param mediaType the media type or null to remove 233 */ 234 public void setMediaType(String mediaType) { 235 parameters.setMediaType(mediaType); 236 } 237 238 @Override 239 public List<Pid> getPids() { 240 return super.getPids(); 241 } 242 243 @Override 244 public Integer getPref() { 245 return super.getPref(); 246 } 247 248 @Override 249 public void setPref(Integer pref) { 250 super.setPref(pref); 251 } 252 253 //@Override 254 public String getAltId() { 255 return parameters.getAltId(); 256 } 257 258 //@Override 259 public void setAltId(String altId) { 260 parameters.setAltId(altId); 261 } 262 263 @Override 264 protected void _validate(List<ValidationWarning> warnings, VCardVersion version, VCard vcard) { 265 if (offset == null && text == null) { 266 warnings.add(new ValidationWarning(8)); 267 } 268 if (offset == null && version == VCardVersion.V2_1) { 269 warnings.add(new ValidationWarning(20)); 270 } 271 } 272 273 @Override 274 protected Map<String, Object> toStringValues() { 275 Map<String, Object> values = new LinkedHashMap<>(); 276 values.put("offset", offset); 277 values.put("text", text); 278 return values; 279 } 280 281 @Override 282 public Timezone copy() { 283 return new Timezone(this); 284 } 285 286 @Override 287 public int hashCode() { 288 final int prime = 31; 289 int result = super.hashCode(); 290 result = prime * result + Objects.hash(offset, text); 291 return result; 292 } 293 294 @Override 295 public boolean equals(Object obj) { 296 if (this == obj) return true; 297 if (!super.equals(obj)) return false; 298 if (getClass() != obj.getClass()) return false; 299 Timezone other = (Timezone) obj; 300 return Objects.equals(offset, other.offset) && Objects.equals(text, other.text); 301 } 302}