EMMA Coverage Report (generated Tue Oct 28 00:01:11 GMT 2008)
[all classes][org.joda.time]

COVERAGE SUMMARY FOR SOURCE FILE [DateTimeZone.java]

nameclass, %method, %block, %line, %
DateTimeZone.java67%  (2/3)90%  (43/48)90%  (998/1104)91%  (276/302)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DateTimeZone$10%   (0/1)0%   (0/5)0%   (0/13)0%   (0/5)
DateTimeZone$1 (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getZone (): DateTimeZone 0%   (0/1)0%   (0/2)0%   (0/1)
toString (): String 0%   (0/1)0%   (0/4)0%   (0/1)
withUTC (): Chronology 0%   (0/1)0%   (0/2)0%   (0/1)
withZone (DateTimeZone): Chronology 0%   (0/1)0%   (0/2)0%   (0/1)
     
class DateTimeZone100% (1/1)100% (39/39)91%  (978/1071)93%  (268/289)
<static initializer> 100% (1/1)33%  (10/30)46%  (6/13)
setNameProvider (NameProvider): void 100% (1/1)54%  (7/13)80%  (4/5)
setProvider (Provider): void 100% (1/1)54%  (7/13)80%  (4/5)
convertLocalToUTC (long, boolean): long 100% (1/1)67%  (60/90)86%  (12/14)
setDefault (DateTimeZone): void 100% (1/1)70%  (14/20)86%  (6/7)
getDefaultProvider (): Provider 100% (1/1)72%  (34/47)74%  (14/19)
parseOffset (String): int 100% (1/1)76%  (13/17)75%  (3/4)
convertUTCToLocal (long): long 100% (1/1)83%  (24/29)80%  (4/5)
isLocalDateTimeGap (LocalDateTime): boolean 100% (1/1)86%  (12/14)83%  (5/6)
getDefaultNameProvider (): NameProvider 100% (1/1)97%  (30/31)92%  (12/13)
DateTimeZone (String): void 100% (1/1)100% (13/13)100% (5/5)
fixedOffsetZone (String, int): DateTimeZone 100% (1/1)100% (43/43)100% (12/12)
forID (String): DateTimeZone 100% (1/1)100% (55/55)100% (14/14)
forOffsetHours (int): DateTimeZone 100% (1/1)100% (4/4)100% (1/1)
forOffsetHoursMinutes (int, int): DateTimeZone 100% (1/1)100% (55/55)100% (14/14)
forOffsetMillis (int): DateTimeZone 100% (1/1)100% (7/7)100% (2/2)
forTimeZone (TimeZone): DateTimeZone 100% (1/1)100% (80/80)100% (23/23)
getAvailableIDs (): Set 100% (1/1)100% (2/2)100% (1/1)
getConvertedId (String): String 100% (1/1)100% (175/175)100% (37/37)
getDefault (): DateTimeZone 100% (1/1)100% (2/2)100% (1/1)
getID (): String 100% (1/1)100% (3/3)100% (1/1)
getMillisKeepLocal (DateTimeZone, long): long 100% (1/1)100% (23/23)100% (6/6)
getName (long): String 100% (1/1)100% (5/5)100% (1/1)
getName (long, Locale): String 100% (1/1)100% (29/29)100% (9/9)
getNameProvider (): NameProvider 100% (1/1)100% (2/2)100% (1/1)
getOffset (ReadableInstant): int 100% (1/1)100% (11/11)100% (3/3)
getOffsetFromLocal (long): int 100% (1/1)100% (40/40)100% (9/9)
getProvider (): Provider 100% (1/1)100% (2/2)100% (1/1)
getShortName (long): String 100% (1/1)100% (5/5)100% (1/1)
getShortName (long, Locale): String 100% (1/1)100% (29/29)100% (9/9)
hashCode (): int 100% (1/1)100% (6/6)100% (1/1)
isStandardOffset (long): boolean 100% (1/1)100% (11/11)100% (1/1)
offsetFormatter (): DateTimeFormatter 100% (1/1)100% (14/14)100% (3/3)
printOffset (int): String 100% (1/1)100% (89/89)100% (23/23)
setNameProvider0 (NameProvider): void 100% (1/1)100% (7/7)100% (4/4)
setProvider0 (Provider): void 100% (1/1)100% (42/42)100% (12/12)
toString (): String 100% (1/1)100% (3/3)100% (1/1)
toTimeZone (): TimeZone 100% (1/1)100% (4/4)100% (1/1)
writeReplace (): Object 100% (1/1)100% (6/6)100% (1/1)
     
class DateTimeZone$Stub100% (1/1)100% (4/4)100% (20/20)100% (8/8)
DateTimeZone$Stub (String): void 100% (1/1)100% (6/6)100% (3/3)
readObject (ObjectInputStream): void 100% (1/1)100% (5/5)100% (2/2)
readResolve (): Object 100% (1/1)100% (4/4)100% (1/1)
writeObject (ObjectOutputStream): void 100% (1/1)100% (5/5)100% (2/2)

1/*
2 *  Copyright 2001-2006 Stephen Colebourne
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16package org.joda.time;
17 
18import java.io.IOException;
19import java.io.ObjectInputStream;
20import java.io.ObjectOutputStream;
21import java.io.ObjectStreamException;
22import java.io.Serializable;
23import java.lang.ref.Reference;
24import java.lang.ref.SoftReference;
25import java.util.HashMap;
26import java.util.Locale;
27import java.util.Map;
28import java.util.Set;
29import java.util.TimeZone;
30 
31import org.joda.time.chrono.BaseChronology;
32import org.joda.time.chrono.ISOChronology;
33import org.joda.time.field.FieldUtils;
34import org.joda.time.format.DateTimeFormat;
35import org.joda.time.format.DateTimeFormatter;
36import org.joda.time.format.DateTimeFormatterBuilder;
37import org.joda.time.format.FormatUtils;
38import org.joda.time.tz.DefaultNameProvider;
39import org.joda.time.tz.FixedDateTimeZone;
40import org.joda.time.tz.NameProvider;
41import org.joda.time.tz.Provider;
42import org.joda.time.tz.UTCProvider;
43import org.joda.time.tz.ZoneInfoProvider;
44 
45/**
46 * DateTimeZone represents a time zone.
47 * <p>
48 * A time zone is a system of rules to convert time from one geographic 
49 * location to another. For example, Paris, France is one hour ahead of
50 * London, England. Thus when it is 10:00 in London, it is 11:00 in Paris.
51 * <p>
52 * All time zone rules are expressed, for historical reasons, relative to
53 * Greenwich, London. Local time in Greenwich is referred to as Greenwich Mean
54 * Time (GMT).  This is similar, but not precisely identical, to Universal 
55 * Coordinated Time, or UTC. This library only uses the term UTC.
56 * <p>
57 * Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00
58 * in the summer. The offset -08:00 indicates that America/Los_Angeles time is
59 * obtained from UTC by adding -08:00, that is, by subtracting 8 hours.
60 * <p>
61 * The offset differs in the summer because of daylight saving time, or DST.
62 * The folowing definitions of time are generally used:
63 * <ul>
64 * <li>UTC - The reference time.
65 * <li>Standard Time - The local time without a daylight saving time offset.
66 * For example, in Paris, standard time is UTC+01:00.
67 * <li>Daylight Saving Time - The local time with a daylight saving time 
68 * offset. This offset is typically one hour, but not always. It is typically
69 * used in most countries away from the equator.  In Paris, daylight saving 
70 * time is UTC+02:00.
71 * <li>Wall Time - This is what a local clock on the wall reads. This will be
72 * either Standard Time or Daylight Saving Time depending on the time of year
73 * and whether the location uses Daylight Saving Time.
74 * </ul>
75 * <p>
76 * Unlike the Java TimeZone class, DateTimeZone is immutable. It also only
77 * supports long format time zone ids. Thus EST and ECT are not accepted.
78 * However, the factory that accepts a TimeZone will attempt to convert from
79 * the old short id to a suitable long id.
80 * <p>
81 * DateTimeZone is thread-safe and immutable, and all subclasses must be as
82 * well.
83 * 
84 * @author Brian S O'Neill
85 * @author Stephen Colebourne
86 * @since 1.0
87 */
88public abstract class DateTimeZone implements Serializable {
89    
90    /** Serialization version. */
91    private static final long serialVersionUID = 5546345482340108586L;
92 
93    /** The time zone for Universal Coordinated Time */
94    public static final DateTimeZone UTC = new FixedDateTimeZone("UTC", "UTC", 0, 0);
95 
96    /** The instance that is providing time zones. */
97    private static Provider cProvider;
98    /** The instance that is providing time zone names. */
99    private static NameProvider cNameProvider;
100    /** The set of ID strings. */
101    private static Set cAvailableIDs;
102    /** The default time zone. */
103    private static DateTimeZone cDefault;
104    /** A formatter for printing and parsing zones. */
105    private static DateTimeFormatter cOffsetFormatter;
106 
107    /** Cache that maps fixed offset strings to softly referenced DateTimeZones */
108    private static Map iFixedOffsetCache;
109 
110    /** Cache of old zone IDs to new zone IDs */
111    private static Map cZoneIdConversion;
112 
113    static {
114        setProvider0(null);
115        setNameProvider0(null);
116 
117        try {
118            try {
119                cDefault = forID(System.getProperty("user.timezone"));
120            } catch (RuntimeException ex) {
121                // ignored
122            }
123            if (cDefault == null) {
124                cDefault = forTimeZone(TimeZone.getDefault());
125            }
126        } catch (IllegalArgumentException ex) {
127            // ignored
128        }
129 
130        if (cDefault == null) {
131            cDefault = UTC;
132        }
133    }
134 
135    //-----------------------------------------------------------------------
136    /**
137     * Gets the default time zone.
138     * 
139     * @return the default datetime zone object
140     */
141    public static DateTimeZone getDefault() {
142        return cDefault;
143    }
144 
145    /**
146     * Sets the default time zone.
147     * 
148     * @param zone  the default datetime zone object, must not be null
149     * @throws IllegalArgumentException if the zone is null
150     * @throws SecurityException if the application has insufficient security rights
151     */
152    public static void setDefault(DateTimeZone zone) throws SecurityException {
153        SecurityManager sm = System.getSecurityManager();
154        if (sm != null) {
155            sm.checkPermission(new JodaTimePermission("DateTimeZone.setDefault"));
156        }
157        if (zone == null) {
158            throw new IllegalArgumentException("The datetime zone must not be null");
159        }
160        cDefault = zone;
161    }
162 
163    //-----------------------------------------------------------------------
164    /**
165     * Gets a time zone instance for the specified time zone id.
166     * <p>
167     * The time zone id may be one of those returned by getAvailableIDs.
168     * Short ids, as accepted by {@link java.util.TimeZone}, are not accepted.
169     * All IDs must be specified in the long format.
170     * The exception is UTC, which is an acceptable id.
171     * <p>
172     * Alternatively a locale independent, fixed offset, datetime zone can
173     * be specified. The form <code>[+-]hh:mm</code> can be used.
174     * 
175     * @param id  the ID of the datetime zone, null means default
176     * @return the DateTimeZone object for the ID
177     * @throws IllegalArgumentException if the ID is not recognised
178     */
179    public static DateTimeZone forID(String id) {
180        if (id == null) {
181            return getDefault();
182        }
183        if (id.equals("UTC")) {
184            return DateTimeZone.UTC;
185        }
186        DateTimeZone zone = cProvider.getZone(id);
187        if (zone != null) {
188            return zone;
189        }
190        if (id.startsWith("+") || id.startsWith("-")) {
191            int offset = parseOffset(id);
192            if (offset == 0L) {
193                return DateTimeZone.UTC;
194            } else {
195                id = printOffset(offset);
196                return fixedOffsetZone(id, offset);
197            }
198        }
199        throw new IllegalArgumentException("The datetime zone id is not recognised: " + id);
200    }
201 
202    /**
203     * Gets a time zone instance for the specified offset to UTC in hours.
204     * This method assumes standard length hours.
205     * <p>
206     * This factory is a convenient way of constructing zones with a fixed offset.
207     * 
208     * @param hoursOffset  the offset in hours from UTC
209     * @return the DateTimeZone object for the offset
210     * @throws IllegalArgumentException if the offset is too large or too small
211     */
212    public static DateTimeZone forOffsetHours(int hoursOffset) throws IllegalArgumentException {
213        return forOffsetHoursMinutes(hoursOffset, 0);
214    }
215 
216    /**
217     * Gets a time zone instance for the specified offset to UTC in hours and minutes.
218     * This method assumes 60 minutes in an hour, and standard length minutes.
219     * <p>
220     * This factory is a convenient way of constructing zones with a fixed offset.
221     * The minutes value is always positive and in the range 0 to 59.
222     * If constructed with the values (-2, 30), the resultiong zone is '-02:30'.
223     * 
224     * @param hoursOffset  the offset in hours from UTC
225     * @param minutesOffset  the offset in minutes from UTC, must be between 0 and 59 inclusive
226     * @return the DateTimeZone object for the offset
227     * @throws IllegalArgumentException if the offset or minute is too large or too small
228     */
229    public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
230        if (hoursOffset == 0 && minutesOffset == 0) {
231            return DateTimeZone.UTC;
232        }
233        if (minutesOffset < 0 || minutesOffset > 59) {
234            throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
235        }
236        int offset = 0;
237        try {
238            int hoursInMinutes = FieldUtils.safeMultiply(hoursOffset, 60);
239            if (hoursInMinutes < 0) {
240                minutesOffset = FieldUtils.safeAdd(hoursInMinutes, -minutesOffset);
241            } else {
242                minutesOffset = FieldUtils.safeAdd(hoursInMinutes, minutesOffset);
243            }
244            offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
245        } catch (ArithmeticException ex) {
246            throw new IllegalArgumentException("Offset is too large");
247        }
248        return forOffsetMillis(offset);
249    }
250 
251    /**
252     * Gets a time zone instance for the specified offset to UTC in milliseconds.
253     *
254     * @param millisOffset  the offset in millis from UTC
255     * @return the DateTimeZone object for the offset
256     */
257    public static DateTimeZone forOffsetMillis(int millisOffset) {
258        String id = printOffset(millisOffset);
259        return fixedOffsetZone(id, millisOffset);
260    }
261 
262    /**
263     * Gets a time zone instance for a JDK TimeZone.
264     * <p>
265     * DateTimeZone only accepts a subset of the IDs from TimeZone. The
266     * excluded IDs are the short three letter form (except UTC). This 
267     * method will attempt to convert between time zones created using the
268     * short IDs and the full version.
269     * <p>
270     * This method is not designed to parse time zones with rules created by
271     * applications using <code>SimpleTimeZone</code> directly.
272     * 
273     * @param zone  the zone to convert, null means default
274     * @return the DateTimeZone object for the zone
275     * @throws IllegalArgumentException if the zone is not recognised
276     */
277    public static DateTimeZone forTimeZone(TimeZone zone) {
278        if (zone == null) {
279            return getDefault();
280        }
281        final String id = zone.getID();
282        if (id.equals("UTC")) {
283            return DateTimeZone.UTC;
284        }
285 
286        // Convert from old alias before consulting provider since they may differ.
287        DateTimeZone dtz = null;
288        String convId = getConvertedId(id);
289        if (convId != null) {
290            dtz = cProvider.getZone(convId);
291        }
292        if (dtz == null) {
293            dtz = cProvider.getZone(id);
294        }
295        if (dtz != null) {
296            return dtz;
297        }
298 
299        // Support GMT+/-hh:mm formats
300        if (convId == null) {
301            convId = zone.getDisplayName();
302            if (convId.startsWith("GMT+") || convId.startsWith("GMT-")) {
303                convId = convId.substring(3);
304                int offset = parseOffset(convId);
305                if (offset == 0L) {
306                    return DateTimeZone.UTC;
307                } else {
308                    convId = printOffset(offset);
309                    return fixedOffsetZone(convId, offset);
310                }
311            }
312        }
313 
314        throw new IllegalArgumentException("The datetime zone id is not recognised: " + id);
315    }
316 
317    //-----------------------------------------------------------------------
318    /**
319     * Gets the zone using a fixed offset amount.
320     * 
321     * @param id  the zone id
322     * @param offset  the offset in millis
323     * @return the zone
324     */
325    private static synchronized DateTimeZone fixedOffsetZone(String id, int offset) {
326        if (offset == 0) {
327            return DateTimeZone.UTC;
328        }
329        if (iFixedOffsetCache == null) {
330            iFixedOffsetCache = new HashMap();
331        }
332        DateTimeZone zone;
333        Reference ref = (Reference) iFixedOffsetCache.get(id);
334        if (ref != null) {
335            zone = (DateTimeZone) ref.get();
336            if (zone != null) {
337                return zone;
338            }
339        }
340        zone = new FixedDateTimeZone(id, null, offset, offset);
341        iFixedOffsetCache.put(id, new SoftReference(zone));
342        return zone;
343    }
344 
345    /**
346     * Gets all the available IDs supported.
347     * 
348     * @return an unmodifiable Set of String IDs
349     */
350    public static Set getAvailableIDs() {
351        return cAvailableIDs;
352    }
353 
354    //-----------------------------------------------------------------------
355    /**
356     * Gets the zone provider factory.
357     * <p>
358     * The zone provider is a pluggable instance factory that supplies the
359     * actual instances of DateTimeZone.
360     * 
361     * @return the provider
362     */
363    public static Provider getProvider() {
364        return cProvider;
365    }
366 
367    /**
368     * Sets the zone provider factory.
369     * <p>
370     * The zone provider is a pluggable instance factory that supplies the
371     * actual instances of DateTimeZone.
372     * 
373     * @param provider  provider to use, or null for default
374     * @throws SecurityException if you do not have the permission DateTimeZone.setProvider
375     * @throws IllegalArgumentException if the provider is invalid
376     */
377    public static void setProvider(Provider provider) throws SecurityException {
378        SecurityManager sm = System.getSecurityManager();
379        if (sm != null) {
380            sm.checkPermission(new JodaTimePermission("DateTimeZone.setProvider"));
381        }
382        setProvider0(provider);
383    }
384 
385    /**
386     * Sets the zone provider factory without performing the security check.
387     * 
388     * @param provider  provider to use, or null for default
389     * @throws IllegalArgumentException if the provider is invalid
390     */
391    private static void setProvider0(Provider provider) {
392        if (provider == null) {
393            provider = getDefaultProvider();
394        }
395        Set ids = provider.getAvailableIDs();
396        if (ids == null || ids.size() == 0) {
397            throw new IllegalArgumentException
398                ("The provider doesn't have any available ids");
399        }
400        if (!ids.contains("UTC")) {
401            throw new IllegalArgumentException("The provider doesn't support UTC");
402        }
403        if (!UTC.equals(provider.getZone("UTC"))) {
404            throw new IllegalArgumentException("Invalid UTC zone provided");
405        }
406        cProvider = provider;
407        cAvailableIDs = ids;
408    }
409 
410    /**
411     * Gets the default zone provider.
412     * <p>
413     * Tries the system property <code>org.joda.time.DateTimeZone.Provider</code>.
414     * Then tries a <code>ZoneInfoProvider</code> using the data in <code>org/joda/time/tz/data</code>.
415     * Then uses <code>UTCProvider</code>.
416     * 
417     * @return the default name provider
418     */
419    private static Provider getDefaultProvider() {
420        Provider provider = null;
421 
422        try {
423            String providerClass =
424                System.getProperty("org.joda.time.DateTimeZone.Provider");
425            if (providerClass != null) {
426                try {
427                    provider = (Provider) Class.forName(providerClass).newInstance();
428                } catch (Exception ex) {
429                    Thread thread = Thread.currentThread();
430                    thread.getThreadGroup().uncaughtException(thread, ex);
431                }
432            }
433        } catch (SecurityException ex) {
434            // ignored
435        }
436 
437        if (provider == null) {
438            try {
439                provider = new ZoneInfoProvider("org/joda/time/tz/data");
440            } catch (Exception ex) {
441                Thread thread = Thread.currentThread();
442                thread.getThreadGroup().uncaughtException(thread, ex);
443            }
444        }
445 
446        if (provider == null) {
447            provider = new UTCProvider();
448        }
449 
450        return provider;
451    }
452 
453    //-----------------------------------------------------------------------
454    /**
455     * Gets the name provider factory.
456     * <p>
457     * The name provider is a pluggable instance factory that supplies the
458     * names of each DateTimeZone.
459     * 
460     * @return the provider
461     */
462    public static NameProvider getNameProvider() {
463        return cNameProvider;
464    }
465 
466    /**
467     * Sets the name provider factory.
468     * <p>
469     * The name provider is a pluggable instance factory that supplies the
470     * names of each DateTimeZone.
471     * 
472     * @param nameProvider  provider to use, or null for default
473     * @throws SecurityException if you do not have the permission DateTimeZone.setNameProvider
474     * @throws IllegalArgumentException if the provider is invalid
475     */
476    public static void setNameProvider(NameProvider nameProvider) throws SecurityException {
477        SecurityManager sm = System.getSecurityManager();
478        if (sm != null) {
479            sm.checkPermission(new JodaTimePermission("DateTimeZone.setNameProvider"));
480        }
481        setNameProvider0(nameProvider);
482    }
483 
484    /**
485     * Sets the name provider factory without performing the security check.
486     * 
487     * @param nameProvider  provider to use, or null for default
488     * @throws IllegalArgumentException if the provider is invalid
489     */
490    private static void setNameProvider0(NameProvider nameProvider) {
491        if (nameProvider == null) {
492            nameProvider = getDefaultNameProvider();
493        }
494        cNameProvider = nameProvider;
495    }
496 
497    /**
498     * Gets the default name provider.
499     * <p>
500     * Tries the system property <code>org.joda.time.DateTimeZone.NameProvider</code>.
501     * Then uses <code>DefaultNameProvider</code>.
502     * 
503     * @return the default name provider
504     */
505    private static NameProvider getDefaultNameProvider() {
506        NameProvider nameProvider = null;
507        try {
508            String providerClass = System.getProperty("org.joda.time.DateTimeZone.NameProvider");
509            if (providerClass != null) {
510                try {
511                    nameProvider = (NameProvider) Class.forName(providerClass).newInstance();
512                } catch (Exception ex) {
513                    Thread thread = Thread.currentThread();
514                    thread.getThreadGroup().uncaughtException(thread, ex);
515                }
516            }
517        } catch (SecurityException ex) {
518            // ignore
519        }
520 
521        if (nameProvider == null) {
522            nameProvider = new DefaultNameProvider();
523        }
524 
525        return nameProvider;
526    }
527 
528    //-----------------------------------------------------------------------
529    /**
530     * Converts an old style id to a new style id.
531     * 
532     * @param id  the old style id
533     * @return the new style id, null if not found
534     */
535    private static synchronized String getConvertedId(String id) {
536        Map map = cZoneIdConversion;
537        if (map == null) {
538            // Backwards compatibility with TimeZone.
539            map = new HashMap();
540            map.put("GMT", "UTC");
541            map.put("MIT", "Pacific/Apia");
542            map.put("HST", "Pacific/Honolulu");
543            map.put("AST", "America/Anchorage");
544            map.put("PST", "America/Los_Angeles");
545            map.put("MST", "America/Denver");
546            map.put("PNT", "America/Phoenix");
547            map.put("CST", "America/Chicago");
548            map.put("EST", "America/New_York");
549            map.put("IET", "America/Indianapolis");
550            map.put("PRT", "America/Puerto_Rico");
551            map.put("CNT", "America/St_Johns");
552            map.put("AGT", "America/Buenos_Aires");
553            map.put("BET", "America/Sao_Paulo");
554            map.put("WET", "Europe/London");
555            map.put("ECT", "Europe/Paris");
556            map.put("ART", "Africa/Cairo");
557            map.put("CAT", "Africa/Harare");
558            map.put("EET", "Europe/Bucharest");
559            map.put("EAT", "Africa/Addis_Ababa");
560            map.put("MET", "Asia/Tehran");
561            map.put("NET", "Asia/Yerevan");
562            map.put("PLT", "Asia/Karachi");
563            map.put("IST", "Asia/Calcutta");
564            map.put("BST", "Asia/Dhaka");
565            map.put("VST", "Asia/Saigon");
566            map.put("CTT", "Asia/Shanghai");
567            map.put("JST", "Asia/Tokyo");
568            map.put("ACT", "Australia/Darwin");
569            map.put("AET", "Australia/Sydney");
570            map.put("SST", "Pacific/Guadalcanal");
571            map.put("NST", "Pacific/Auckland");
572            cZoneIdConversion = map;
573        }
574        return (String) map.get(id);
575    }
576 
577    private static int parseOffset(String str) {
578        Chronology chrono;
579        if (cDefault != null) {
580            chrono = ISOChronology.getInstanceUTC();
581        } else {
582            // Can't use a real chronology if called during class
583            // initialization. Offset parser doesn't need it anyhow.
584            chrono = new BaseChronology() {
585                public DateTimeZone getZone() {
586                    return null;
587                }
588                public Chronology withUTC() {
589                    return this;
590                }
591                public Chronology withZone(DateTimeZone zone) {
592                    return this;
593                }
594                public String toString() {
595                    return getClass().getName();
596                }
597            };
598        }
599 
600        return -(int) offsetFormatter().withChronology(chrono).parseMillis(str);
601    }
602 
603    /**
604     * Formats a timezone offset string.
605     * <p>
606     * This method is kept separate from the formatting classes to speed and
607     * simplify startup and classloading.
608     * 
609     * @param offset  the offset in milliseconds
610     * @return the time zone string
611     */
612    private static String printOffset(int offset) {
613        StringBuffer buf = new StringBuffer();
614        if (offset >= 0) {
615            buf.append('+');
616        } else {
617            buf.append('-');
618            offset = -offset;
619        }
620 
621        int hours = offset / DateTimeConstants.MILLIS_PER_HOUR;
622        FormatUtils.appendPaddedInteger(buf, hours, 2);
623        offset -= hours * (int) DateTimeConstants.MILLIS_PER_HOUR;
624 
625        int minutes = offset / DateTimeConstants.MILLIS_PER_MINUTE;
626        buf.append(':');
627        FormatUtils.appendPaddedInteger(buf, minutes, 2);
628        offset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
629        if (offset == 0) {
630            return buf.toString();
631        }
632 
633        int seconds = offset / DateTimeConstants.MILLIS_PER_SECOND;
634        buf.append(':');
635        FormatUtils.appendPaddedInteger(buf, seconds, 2);
636        offset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
637        if (offset == 0) {
638            return buf.toString();
639        }
640 
641        buf.append('.');
642        FormatUtils.appendPaddedInteger(buf, offset, 3);
643        return buf.toString();
644    }
645 
646    /**
647     * Gets a printer/parser for managing the offset id formatting.
648     * 
649     * @return the formatter
650     */
651    private static synchronized DateTimeFormatter offsetFormatter() {
652        if (cOffsetFormatter == null) {
653            cOffsetFormatter = new DateTimeFormatterBuilder()
654                .appendTimeZoneOffset(null, true, 2, 4)
655                .toFormatter();
656        }
657        return cOffsetFormatter;
658    }
659 
660    // Instance fields and methods
661    //--------------------------------------------------------------------
662 
663    private final String iID;
664 
665    /**
666     * Constructor.
667     * 
668     * @param id  the id to use
669     * @throws IllegalArgumentException if the id is null
670     */
671    protected DateTimeZone(String id) {
672        if (id == null) {
673            throw new IllegalArgumentException("Id must not be null");
674        }
675        iID = id;
676    }
677 
678    // Principal methods
679    //--------------------------------------------------------------------
680 
681    /**
682     * Gets the ID of this datetime zone.
683     * 
684     * @return the ID of this datetime zone
685     */
686    public final String getID() {
687        return iID;
688    }
689 
690    /**
691     * Returns a non-localized name that is unique to this time zone. It can be
692     * combined with id to form a unique key for fetching localized names.
693     *
694     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
695     * @return name key or null if id should be used for names
696     */
697    public abstract String getNameKey(long instant);
698 
699    /**
700     * Gets the short name of this datetime zone suitable for display using
701     * the default locale.
702     * <p>
703     * If the name is not available for the locale, then this method returns a
704     * string in the format <code>[+-]hh:mm</code>.
705     * 
706     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
707     * @return the human-readable short name in the default locale
708     */
709    public final String getShortName(long instant) {
710        return getShortName(instant, null);
711    }
712 
713    /**
714     * Gets the short name of this datetime zone suitable for display using
715     * the specified locale.
716     * <p>
717     * If the name is not available for the locale, then this method returns a
718     * string in the format <code>[+-]hh:mm</code>.
719     * 
720     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
721     * @param locale  the locale to get the name for
722     * @return the human-readable short name in the specified locale
723     */
724    public String getShortName(long instant, Locale locale) {
725        if (locale == null) {
726            locale = Locale.getDefault();
727        }
728        String nameKey = getNameKey(instant);
729        if (nameKey == null) {
730            return iID;
731        }
732        String name = cNameProvider.getShortName(locale, iID, nameKey);
733        if (name != null) {
734            return name;
735        }
736        return printOffset(getOffset(instant));
737    }
738 
739    /**
740     * Gets the long name of this datetime zone suitable for display using
741     * the default locale.
742     * <p>
743     * If the name is not available for the locale, then this method returns a
744     * string in the format <code>[+-]hh:mm</code>.
745     * 
746     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
747     * @return the human-readable long name in the default locale
748     */
749    public final String getName(long instant) {
750        return getName(instant, null);
751    }
752 
753    /**
754     * Gets the long name of this datetime zone suitable for display using
755     * the specified locale.
756     * <p>
757     * If the name is not available for the locale, then this method returns a
758     * string in the format <code>[+-]hh:mm</code>.
759     * 
760     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
761     * @param locale  the locale to get the name for
762     * @return the human-readable long name in the specified locale
763     */
764    public String getName(long instant, Locale locale) {
765        if (locale == null) {
766            locale = Locale.getDefault();
767        }
768        String nameKey = getNameKey(instant);
769        if (nameKey == null) {
770            return iID;
771        }
772        String name = cNameProvider.getName(locale, iID, nameKey);
773        if (name != null) {
774            return name;
775        }
776        return printOffset(getOffset(instant));
777    }
778 
779    /**
780     * Gets the millisecond offset to add to UTC to get local time.
781     * 
782     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
783     * @return the millisecond offset to add to UTC to get local time
784     */
785    public abstract int getOffset(long instant);
786 
787    /**
788     * Gets the millisecond offset to add to UTC to get local time.
789     * 
790     * @param instant  instant to get the offset for, null means now
791     * @return the millisecond offset to add to UTC to get local time
792     */
793    public final int getOffset(ReadableInstant instant) {
794        if (instant == null) {
795            return getOffset(DateTimeUtils.currentTimeMillis());
796        }
797        return getOffset(instant.getMillis());
798    }
799 
800    /**
801     * Gets the standard millisecond offset to add to UTC to get local time,
802     * when standard time is in effect.
803     * 
804     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
805     * @return the millisecond offset to add to UTC to get local time
806     */
807    public abstract int getStandardOffset(long instant);
808 
809    /**
810     * Checks whether, at a particular instant, the offset is standard or not.
811     * <p>
812     * This method can be used to determine whether Summer Time (DST) applies.
813     * As a general rule, if the offset at the specified instant is standard,
814     * then either Winter time applies, or there is no Summer Time. If the
815     * instant is not standard, then Summer Time applies.
816     * <p>
817     * The implementation of the method is simply whether {@link #getOffset(long)}
818     * equals {@link #getStandardOffset(long)} at the specified instant.
819     * 
820     * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
821     * @return true if the offset at the given instant is the standard offset
822     * @since 1.5
823     */
824    public boolean isStandardOffset(long instant) {
825        return getOffset(instant) == getStandardOffset(instant);
826    }
827 
828    /**
829     * Gets the millisecond offset to subtract from local time to get UTC time.
830     * This offset can be used to undo adding the offset obtained by getOffset.
831     *
832     * <pre>
833     * millisLocal == millisUTC   + getOffset(millisUTC)
834     * millisUTC   == millisLocal - getOffsetFromLocal(millisLocal)
835     * </pre>
836     *
837     * NOTE: After calculating millisLocal, some error may be introduced. At
838     * offset transitions (due to DST or other historical changes), ranges of
839     * local times may map to different UTC times.
840     * <p>
841     * This method will return an offset suitable for calculating an instant
842     * after any DST gap. For example, consider a zone with a cutover
843     * from 01:00 to 01:59:<br />
844     * Input: 00:00  Output: 00:00<br />
845     * Input: 00:30  Output: 00:30<br />
846     * Input: 01:00  Output: 02:00<br />
847     * Input: 01:30  Output: 02:30<br />
848     * Input: 02:00  Output: 02:00<br />
849     * Input: 02:30  Output: 02:30<br />
850     * <p>
851     * NOTE: The behaviour of this method changed in v1.5, with the emphasis
852     * on returning a consistent result later along the time-line (shown above).
853     *
854     * @param instantLocal  the millisecond instant, relative to this time zone, to
855     * get the offset for
856     * @return the millisecond offset to subtract from local time to get UTC time
857     */
858    public int getOffsetFromLocal(long instantLocal) {
859        // get the offset at instantLocal (first estimate)
860        int offsetLocal = getOffset(instantLocal);
861        // adjust instantLocal using the estimate and recalc the offset
862        int offsetAdjusted = getOffset(instantLocal - offsetLocal);
863        // if the offsets differ, we must be near a DST boundary
864        if (offsetLocal != offsetAdjusted) {
865            // we need to ensure that time is always after the DST gap
866            // this happens naturally for positive offsets, but not for negative
867            if ((offsetLocal - offsetAdjusted) < 0) {
868                // if we just return offsetAdjusted then the time is pushed
869                // back before the transition, whereas it should be
870                // on or after the transition
871                long nextLocal = nextTransition(instantLocal - offsetLocal);
872                long nextAdjusted = nextTransition(instantLocal - offsetAdjusted);
873                if (nextLocal != nextAdjusted) {
874                    return offsetLocal;
875                }
876            }
877        }
878        return offsetAdjusted;
879    }
880 
881    /**
882     * Converts a standard UTC instant to a local instant with the same
883     * local time. This conversion is used before performing a calculation
884     * so that the calculation can be done using a simple local zone.
885     *
886     * @param instantUTC  the UTC instant to convert to local
887     * @return the local instant with the same local time
888     * @throws ArithmeticException if the result overflows a long
889     * @since 1.5
890     */
891    public long convertUTCToLocal(long instantUTC) {
892        int offset = getOffset(instantUTC);
893        long instantLocal = instantUTC + offset;
894        // If there is a sign change, but the two values have the same sign...
895        if ((instantUTC ^ instantLocal) < 0 && (instantUTC ^ offset) >= 0) {
896            throw new ArithmeticException("Adding time zone offset caused overflow");
897        }
898        return instantLocal;
899    }
900 
901    /**
902     * Converts a local instant to a standard UTC instant with the same
903     * local time. This conversion is used after performing a calculation
904     * where the calculation was done using a simple local zone.
905     *
906     * @param instantLocal  the local instant to convert to UTC
907     * @param strict  whether the conversion should reject non-existent local times
908     * @return the UTC instant with the same local time, 
909     * @throws ArithmeticException if the result overflows a long
910     * @throws IllegalArgumentException if the zone has no eqivalent local time
911     * @since 1.5
912     */
913    public long convertLocalToUTC(long instantLocal, boolean strict) {
914        // get the offset at instantLocal (first estimate)
915        int offsetLocal = getOffset(instantLocal);
916        // adjust instantLocal using the estimate and recalc the offset
917        int offset = getOffset(instantLocal - offsetLocal);
918        // if the offsets differ, we must be near a DST boundary
919        if (offsetLocal != offset) {
920            // if strict then always check if in DST gap
921            // otherwise only check if zone in Western hemisphere (as the
922            // value of offset is already correct for Eastern hemisphere)
923            if (strict || offsetLocal < 0) {
924                // determine if we are in the DST gap
925                long nextLocal = nextTransition(instantLocal - offsetLocal);
926                long nextAdjusted = nextTransition(instantLocal - offset);
927                if (nextLocal != nextAdjusted) {
928                    // yes we are in the DST gap
929                    if (strict) {
930                        // DST gap is not acceptable
931                        throw new IllegalArgumentException("Illegal instant due to time zone offset transition: " +
932                                DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").print(new Instant(instantLocal)) +
933                                " (" + getID() + ")");
934                    } else {
935                        // DST gap is acceptable, but for the Western hemisphere
936                        // the offset is wrong and will result in local times
937                        // before the cutover so use the offsetLocal instead
938                        offset = offsetLocal;
939                    }
940                }
941            }
942        }
943        // check for overflow
944        long instantUTC = instantLocal - offset;
945        // If there is a sign change, but the two values have different signs...
946        if ((instantLocal ^ instantUTC) < 0 && (instantLocal ^ offset) < 0) {
947            throw new ArithmeticException("Subtracting time zone offset caused overflow");
948        }
949        return instantUTC;
950    }
951 
952    /**
953     * Gets the millisecond instant in another zone keeping the same local time.
954     * <p>
955     * The conversion is performed by converting the specified UTC millis to local
956     * millis in this zone, then converting back to UTC millis in the new zone.
957     *
958     * @param newZone  the new zone, null means default
959     * @param oldInstant  the UTC millisecond instant to convert
960     * @return the UTC millisecond instant with the same local time in the new zone
961     */
962    public long getMillisKeepLocal(DateTimeZone newZone, long oldInstant) {
963        if (newZone == null) {
964            newZone = DateTimeZone.getDefault();
965        }
966        if (newZone == this) {
967            return oldInstant;
968        }
969        long instantLocal = oldInstant + getOffset(oldInstant);
970        return instantLocal - newZone.getOffsetFromLocal(instantLocal);
971    }
972 
973//    //-----------------------------------------------------------------------
974//    /**
975//     * Checks if the given {@link LocalDateTime} is within an overlap.
976//     * <p>
977//     * When switching from Daylight Savings Time to standard time there is
978//     * typically an overlap where the same clock hour occurs twice. This
979//     * method identifies whether the local datetime refers to such an overlap.
980//     * 
981//     * @param localDateTime  the time to check, not null
982//     * @return true if the given datetime refers to an overlap
983//     */
984//    public boolean isLocalDateTimeOverlap(LocalDateTime localDateTime) {
985//        if (isFixed()) {
986//            return false;
987//        }
988//        long instantLocal = localDateTime.toDateTime(DateTimeZone.UTC).getMillis();
989//        // get the offset at instantLocal (first estimate)
990//        int offsetLocal = getOffset(instantLocal);
991//        // adjust instantLocal using the estimate and recalc the offset
992//        int offset = getOffset(instantLocal - offsetLocal);
993//        // if the offsets differ, we must be near a DST boundary
994//        if (offsetLocal != offset) {
995//            long nextLocal = nextTransition(instantLocal - offsetLocal);
996//            long nextAdjusted = nextTransition(instantLocal - offset);
997//            if (nextLocal != nextAdjusted) {
998//                // in DST gap
999//                return false;
1000//            }
1001//            long diff = Math.abs(offset - offsetLocal);
1002//            DateTime dateTime = localDateTime.toDateTime(this);
1003//            DateTime adjusted = dateTime.plus(diff);
1004//            if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1005//                    dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1006//                    dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1007//                return true;
1008//            }
1009//            adjusted = dateTime.minus(diff);
1010//            if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1011//                    dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1012//                    dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1013//                return true;
1014//            }
1015//            return false;
1016//        }
1017//        return false;
1018//    }
1019//        
1020//        
1021//        DateTime dateTime = null;
1022//        try {
1023//            dateTime = localDateTime.toDateTime(this);
1024//        } catch (IllegalArgumentException ex) {
1025//            return false;  // it is a gap, not an overlap
1026//        }
1027//        long offset1 = Math.abs(getOffset(dateTime.getMillis() + 1) - getStandardOffset(dateTime.getMillis() + 1));
1028//        long offset2 = Math.abs(getOffset(dateTime.getMillis() - 1) - getStandardOffset(dateTime.getMillis() - 1));
1029//        long offset = Math.max(offset1, offset2);
1030//        if (offset == 0) {
1031//            return false;
1032//        }
1033//        DateTime adjusted = dateTime.plus(offset);
1034//        if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1035//                dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1036//                dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1037//            return true;
1038//        }
1039//        adjusted = dateTime.minus(offset);
1040//        if (dateTime.getHourOfDay() == adjusted.getHourOfDay() &&
1041//                dateTime.getMinuteOfHour() == adjusted.getMinuteOfHour() &&
1042//                dateTime.getSecondOfMinute() == adjusted.getSecondOfMinute()) {
1043//            return true;
1044//        }
1045//        return false;
1046        
1047//        long millis = dateTime.getMillis();
1048//        long nextTransition = nextTransition(millis);
1049//        long previousTransition = previousTransition(millis);
1050//        long deltaToPreviousTransition = millis - previousTransition;
1051//        long deltaToNextTransition = nextTransition - millis;
1052//        if (deltaToNextTransition < deltaToPreviousTransition) {
1053//            int offset = getOffset(nextTransition);
1054//            int standardOffset = getStandardOffset(nextTransition);
1055//            if (Math.abs(offset - standardOffset) >= deltaToNextTransition) {
1056//                return true;
1057//            }
1058//        } else  {
1059//            int offset = getOffset(previousTransition);
1060//            int standardOffset = getStandardOffset(previousTransition);
1061//            if (Math.abs(offset - standardOffset) >= deltaToPreviousTransition) {
1062//                return true;
1063//            }
1064//        }
1065//        return false;
1066//    }
1067 
1068    /**
1069     * Checks if the given {@link LocalDateTime} is within a gap.
1070     * <p>
1071     * When switching from standard time to Daylight Savings Time there is
1072     * typically a gap where a clock hour is missing. This method identifies
1073     * whether the local datetime refers to such a gap.
1074     * 
1075     * @param localDateTime  the time to check, not null
1076     * @return true if the given datetime refers to a gap
1077     * @since 1.6
1078     */
1079    public boolean isLocalDateTimeGap(LocalDateTime localDateTime) {
1080        if (isFixed()) {
1081            return false;
1082        }
1083        try {
1084            localDateTime.toDateTime(this);
1085            return false;
1086        } catch (IllegalArgumentException ex) {
1087            return true;
1088        }
1089    }
1090 
1091    //-----------------------------------------------------------------------
1092    /**
1093     * Returns true if this time zone has no transitions.
1094     *
1095     * @return true if no transitions
1096     */
1097    public abstract boolean isFixed();
1098 
1099    /**
1100     * Advances the given instant to where the time zone offset or name changes.
1101     * If the instant returned is exactly the same as passed in, then
1102     * no changes occur after the given instant.
1103     *
1104     * @param instant  milliseconds from 1970-01-01T00:00:00Z
1105     * @return milliseconds from 1970-01-01T00:00:00Z
1106     */
1107    public abstract long nextTransition(long instant);
1108 
1109    /**
1110     * Retreats the given instant to where the time zone offset or name changes.
1111     * If the instant returned is exactly the same as passed in, then
1112     * no changes occur before the given instant.
1113     *
1114     * @param instant  milliseconds from 1970-01-01T00:00:00Z
1115     * @return milliseconds from 1970-01-01T00:00:00Z
1116     */
1117    public abstract long previousTransition(long instant);
1118 
1119    // Basic methods
1120    //--------------------------------------------------------------------
1121 
1122    /**
1123     * Get the datetime zone as a {@link java.util.TimeZone}.
1124     * 
1125     * @return the closest matching TimeZone object
1126     */
1127    public java.util.TimeZone toTimeZone() {
1128        return java.util.TimeZone.getTimeZone(iID);
1129    }
1130 
1131    /**
1132     * Compare this datetime zone with another.
1133     * 
1134     * @param object the object to compare with
1135     * @return true if equal, based on the ID and all internal rules
1136     */
1137    public abstract boolean equals(Object object);
1138 
1139    /**
1140     * Gets a hash code compatable with equals.
1141     * 
1142     * @return suitable hashcode
1143     */
1144    public int hashCode() {
1145        return 57 + getID().hashCode();
1146    }
1147 
1148    /**
1149     * Gets the datetime zone as a string, which is simply its ID.
1150     * @return the id of the zone
1151     */
1152    public String toString() {
1153        return getID();
1154    }
1155 
1156    /**
1157     * By default, when DateTimeZones are serialized, only a "stub" object
1158     * referring to the id is written out. When the stub is read in, it
1159     * replaces itself with a DateTimeZone object.
1160     * @return a stub object to go in the stream
1161     */
1162    protected Object writeReplace() throws ObjectStreamException {
1163        return new Stub(iID);
1164    }
1165 
1166    /**
1167     * Used to serialize DateTimeZones by id.
1168     */
1169    private static final class Stub implements Serializable {
1170        /** Serialization lock. */
1171        private static final long serialVersionUID = -6471952376487863581L;
1172        /** The ID of the zone. */
1173        private transient String iID;
1174 
1175        /**
1176         * Constructor.
1177         * @param id  the id of the zone
1178         */
1179        Stub(String id) {
1180            iID = id;
1181        }
1182 
1183        private void writeObject(ObjectOutputStream out) throws IOException {
1184            out.writeUTF(iID);
1185        }
1186 
1187        private void readObject(ObjectInputStream in) throws IOException {
1188            iID = in.readUTF();
1189        }
1190 
1191        private Object readResolve() throws ObjectStreamException {
1192            return forID(iID);
1193        }
1194    }
1195}

[all classes][org.joda.time]
EMMA 2.0.5312 (C) Vladimir Roubtsov