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