Source Code Cross Referenced for DateTimeZone.java in  » Development » Joda-Time » org » joda » time » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Development » Joda Time » org.joda.time 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *  Copyright 2001-2006 Stephen Colebourne
0003:         *
0004:         *  Licensed under the Apache License, Version 2.0 (the "License");
0005:         *  you may not use this file except in compliance with the License.
0006:         *  You may obtain a copy of the License at
0007:         *
0008:         *      http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         *  Unless required by applicable law or agreed to in writing, software
0011:         *  distributed under the License is distributed on an "AS IS" BASIS,
0012:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         *  See the License for the specific language governing permissions and
0014:         *  limitations under the License.
0015:         */
0016:        package org.joda.time;
0017:
0018:        import java.io.IOException;
0019:        import java.io.ObjectInputStream;
0020:        import java.io.ObjectOutputStream;
0021:        import java.io.ObjectStreamException;
0022:        import java.io.Serializable;
0023:        import java.lang.ref.Reference;
0024:        import java.lang.ref.SoftReference;
0025:        import java.util.HashMap;
0026:        import java.util.Locale;
0027:        import java.util.Map;
0028:        import java.util.Set;
0029:        import java.util.TimeZone;
0030:
0031:        import org.joda.time.chrono.BaseChronology;
0032:        import org.joda.time.chrono.ISOChronology;
0033:        import org.joda.time.field.FieldUtils;
0034:        import org.joda.time.format.DateTimeFormat;
0035:        import org.joda.time.format.DateTimeFormatter;
0036:        import org.joda.time.format.DateTimeFormatterBuilder;
0037:        import org.joda.time.format.FormatUtils;
0038:        import org.joda.time.tz.DefaultNameProvider;
0039:        import org.joda.time.tz.FixedDateTimeZone;
0040:        import org.joda.time.tz.NameProvider;
0041:        import org.joda.time.tz.Provider;
0042:        import org.joda.time.tz.UTCProvider;
0043:        import org.joda.time.tz.ZoneInfoProvider;
0044:
0045:        /**
0046:         * DateTimeZone represents a time zone.
0047:         * <p>
0048:         * A time zone is a system of rules to convert time from one geographic 
0049:         * location to another. For example, Paris, France is one hour ahead of
0050:         * London, England. Thus when it is 10:00 in London, it is 11:00 in Paris.
0051:         * <p>
0052:         * All time zone rules are expressed, for historical reasons, relative to
0053:         * Greenwich, London. Local time in Greenwich is referred to as Greenwich Mean
0054:         * Time (GMT).  This is similar, but not precisely identical, to Universal 
0055:         * Coordinated Time, or UTC. This library only uses the term UTC.
0056:         * <p>
0057:         * Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00
0058:         * in the summer. The offset -08:00 indicates that America/Los_Angeles time is
0059:         * obtained from UTC by adding -08:00, that is, by subtracting 8 hours.
0060:         * <p>
0061:         * The offset differs in the summer because of daylight saving time, or DST.
0062:         * The folowing definitions of time are generally used:
0063:         * <ul>
0064:         * <li>UTC - The reference time.
0065:         * <li>Standard Time - The local time without a daylight saving time offset.
0066:         * For example, in Paris, standard time is UTC+01:00.
0067:         * <li>Daylight Saving Time - The local time with a daylight saving time 
0068:         * offset. This offset is typically one hour, but not always. It is typically
0069:         * used in most countries away from the equator.  In Paris, daylight saving 
0070:         * time is UTC+02:00.
0071:         * <li>Wall Time - This is what a local clock on the wall reads. This will be
0072:         * either Standard Time or Daylight Saving Time depending on the time of year
0073:         * and whether the location uses Daylight Saving Time.
0074:         * </ul>
0075:         * <p>
0076:         * Unlike the Java TimeZone class, DateTimeZone is immutable. It also only
0077:         * supports long format time zone ids. Thus EST and ECT are not accepted.
0078:         * However, the factory that accepts a TimeZone will attempt to convert from
0079:         * the old short id to a suitable long id.
0080:         * <p>
0081:         * DateTimeZone is thread-safe and immutable, and all subclasses must be as
0082:         * well.
0083:         * 
0084:         * @author Brian S O'Neill
0085:         * @author Stephen Colebourne
0086:         * @since 1.0
0087:         */
0088:        public abstract class DateTimeZone implements  Serializable {
0089:
0090:            /** Serialization version. */
0091:            private static final long serialVersionUID = 5546345482340108586L;
0092:
0093:            /** The time zone for Universal Coordinated Time */
0094:            public static final DateTimeZone UTC = new FixedDateTimeZone("UTC",
0095:                    "UTC", 0, 0);
0096:
0097:            /** The instance that is providing time zones. */
0098:            private static Provider cProvider;
0099:            /** The instance that is providing time zone names. */
0100:            private static NameProvider cNameProvider;
0101:            /** The set of ID strings. */
0102:            private static Set cAvailableIDs;
0103:            /** The default time zone. */
0104:            private static DateTimeZone cDefault;
0105:            /** A formatter for printing and parsing zones. */
0106:            private static DateTimeFormatter cOffsetFormatter;
0107:
0108:            /** Cache that maps fixed offset strings to softly referenced DateTimeZones */
0109:            private static Map iFixedOffsetCache;
0110:
0111:            /** Cache of old zone IDs to new zone IDs */
0112:            private static Map cZoneIdConversion;
0113:
0114:            static {
0115:                setProvider0(null);
0116:                setNameProvider0(null);
0117:
0118:                try {
0119:                    try {
0120:                        cDefault = forID(System.getProperty("user.timezone"));
0121:                    } catch (RuntimeException ex) {
0122:                        // ignored
0123:                    }
0124:                    if (cDefault == null) {
0125:                        cDefault = forTimeZone(TimeZone.getDefault());
0126:                    }
0127:                } catch (IllegalArgumentException ex) {
0128:                    // ignored
0129:                }
0130:
0131:                if (cDefault == null) {
0132:                    cDefault = UTC;
0133:                }
0134:            }
0135:
0136:            //-----------------------------------------------------------------------
0137:            /**
0138:             * Gets the default time zone.
0139:             * 
0140:             * @return the default datetime zone object
0141:             */
0142:            public static DateTimeZone getDefault() {
0143:                return cDefault;
0144:            }
0145:
0146:            /**
0147:             * Sets the default time zone.
0148:             * 
0149:             * @param zone  the default datetime zone object, must not be null
0150:             * @throws IllegalArgumentException if the zone is null
0151:             * @throws SecurityException if the application has insufficient security rights
0152:             */
0153:            public static void setDefault(DateTimeZone zone)
0154:                    throws SecurityException {
0155:                SecurityManager sm = System.getSecurityManager();
0156:                if (sm != null) {
0157:                    sm.checkPermission(new JodaTimePermission(
0158:                            "DateTimeZone.setDefault"));
0159:                }
0160:                if (zone == null) {
0161:                    throw new IllegalArgumentException(
0162:                            "The datetime zone must not be null");
0163:                }
0164:                cDefault = zone;
0165:            }
0166:
0167:            //-----------------------------------------------------------------------
0168:            /**
0169:             * Gets a time zone instance for the specified time zone id.
0170:             * <p>
0171:             * The time zone id may be one of those returned by getAvailableIDs.
0172:             * Short ids, as accepted by {@link java.util.TimeZone}, are not accepted.
0173:             * All IDs must be specified in the long format.
0174:             * The exception is UTC, which is an acceptable id.
0175:             * <p>
0176:             * Alternatively a locale independent, fixed offset, datetime zone can
0177:             * be specified. The form <code>[+-]hh:mm</code> can be used.
0178:             * 
0179:             * @param id  the ID of the datetime zone, null means default
0180:             * @return the DateTimeZone object for the ID
0181:             * @throws IllegalArgumentException if the ID is not recognised
0182:             */
0183:            public static DateTimeZone forID(String id) {
0184:                if (id == null) {
0185:                    return getDefault();
0186:                }
0187:                if (id.equals("UTC")) {
0188:                    return DateTimeZone.UTC;
0189:                }
0190:                DateTimeZone zone = cProvider.getZone(id);
0191:                if (zone != null) {
0192:                    return zone;
0193:                }
0194:                if (id.startsWith("+") || id.startsWith("-")) {
0195:                    int offset = parseOffset(id);
0196:                    if (offset == 0L) {
0197:                        return DateTimeZone.UTC;
0198:                    } else {
0199:                        id = printOffset(offset);
0200:                        return fixedOffsetZone(id, offset);
0201:                    }
0202:                }
0203:                throw new IllegalArgumentException(
0204:                        "The datetime zone id is not recognised: " + id);
0205:            }
0206:
0207:            /**
0208:             * Gets a time zone instance for the specified offset to UTC in hours.
0209:             * This method assumes standard length hours.
0210:             * <p>
0211:             * This factory is a convenient way of constructing zones with a fixed offset.
0212:             * 
0213:             * @param hoursOffset  the offset in hours from UTC
0214:             * @return the DateTimeZone object for the offset
0215:             * @throws IllegalArgumentException if the offset is too large or too small
0216:             */
0217:            public static DateTimeZone forOffsetHours(int hoursOffset)
0218:                    throws IllegalArgumentException {
0219:                return forOffsetHoursMinutes(hoursOffset, 0);
0220:            }
0221:
0222:            /**
0223:             * Gets a time zone instance for the specified offset to UTC in hours and minutes.
0224:             * This method assumes 60 minutes in an hour, and standard length minutes.
0225:             * <p>
0226:             * This factory is a convenient way of constructing zones with a fixed offset.
0227:             * The minutes value is always positive and in the range 0 to 59.
0228:             * If constructed with the values (-2, 30), the resultiong zone is '-02:30'.
0229:             * 
0230:             * @param hoursOffset  the offset in hours from UTC
0231:             * @param minutesOffset  the offset in minutes from UTC, must be between 0 and 59 inclusive
0232:             * @return the DateTimeZone object for the offset
0233:             * @throws IllegalArgumentException if the offset or minute is too large or too small
0234:             */
0235:            public static DateTimeZone forOffsetHoursMinutes(int hoursOffset,
0236:                    int minutesOffset) throws IllegalArgumentException {
0237:                if (hoursOffset == 0 && minutesOffset == 0) {
0238:                    return DateTimeZone.UTC;
0239:                }
0240:                if (minutesOffset < 0 || minutesOffset > 59) {
0241:                    throw new IllegalArgumentException("Minutes out of range: "
0242:                            + minutesOffset);
0243:                }
0244:                int offset = 0;
0245:                try {
0246:                    int hoursInMinutes = FieldUtils.safeMultiply(hoursOffset,
0247:                            60);
0248:                    if (hoursInMinutes < 0) {
0249:                        minutesOffset = FieldUtils.safeAdd(hoursInMinutes,
0250:                                -minutesOffset);
0251:                    } else {
0252:                        minutesOffset = FieldUtils.safeAdd(hoursInMinutes,
0253:                                minutesOffset);
0254:                    }
0255:                    offset = FieldUtils.safeMultiply(minutesOffset,
0256:                            DateTimeConstants.MILLIS_PER_MINUTE);
0257:                } catch (ArithmeticException ex) {
0258:                    throw new IllegalArgumentException("Offset is too large");
0259:                }
0260:                return forOffsetMillis(offset);
0261:            }
0262:
0263:            /**
0264:             * Gets a time zone instance for the specified offset to UTC in milliseconds.
0265:             *
0266:             * @param millisOffset  the offset in millis from UTC
0267:             * @return the DateTimeZone object for the offset
0268:             */
0269:            public static DateTimeZone forOffsetMillis(int millisOffset) {
0270:                String id = printOffset(millisOffset);
0271:                return fixedOffsetZone(id, millisOffset);
0272:            }
0273:
0274:            /**
0275:             * Gets a time zone instance for a JDK TimeZone.
0276:             * <p>
0277:             * DateTimeZone only accepts a subset of the IDs from TimeZone. The
0278:             * excluded IDs are the short three letter form (except UTC). This 
0279:             * method will attempt to convert between time zones created using the
0280:             * short IDs and the full version.
0281:             * <p>
0282:             * This method is not designed to parse time zones with rules created by
0283:             * applications using <code>SimpleTimeZone</code> directly.
0284:             * 
0285:             * @param zone  the zone to convert, null means default
0286:             * @return the DateTimeZone object for the zone
0287:             * @throws IllegalArgumentException if the zone is not recognised
0288:             */
0289:            public static DateTimeZone forTimeZone(TimeZone zone) {
0290:                if (zone == null) {
0291:                    return getDefault();
0292:                }
0293:                final String id = zone.getID();
0294:                if (id.equals("UTC")) {
0295:                    return DateTimeZone.UTC;
0296:                }
0297:
0298:                // Convert from old alias before consulting provider since they may differ.
0299:                DateTimeZone dtz = null;
0300:                String convId = getConvertedId(id);
0301:                if (convId != null) {
0302:                    dtz = cProvider.getZone(convId);
0303:                }
0304:                if (dtz == null) {
0305:                    dtz = cProvider.getZone(id);
0306:                }
0307:                if (dtz != null) {
0308:                    return dtz;
0309:                }
0310:
0311:                // Support GMT+/-hh:mm formats
0312:                if (convId == null) {
0313:                    convId = zone.getDisplayName();
0314:                    if (convId.startsWith("GMT+") || convId.startsWith("GMT-")) {
0315:                        convId = convId.substring(3);
0316:                        int offset = parseOffset(convId);
0317:                        if (offset == 0L) {
0318:                            return DateTimeZone.UTC;
0319:                        } else {
0320:                            convId = printOffset(offset);
0321:                            return fixedOffsetZone(convId, offset);
0322:                        }
0323:                    }
0324:                }
0325:
0326:                throw new IllegalArgumentException(
0327:                        "The datetime zone id is not recognised: " + id);
0328:            }
0329:
0330:            //-----------------------------------------------------------------------
0331:            /**
0332:             * Gets the zone using a fixed offset amount.
0333:             * 
0334:             * @param id  the zone id
0335:             * @param offset  the offset in millis
0336:             * @return the zone
0337:             */
0338:            private static synchronized DateTimeZone fixedOffsetZone(String id,
0339:                    int offset) {
0340:                if (offset == 0) {
0341:                    return DateTimeZone.UTC;
0342:                }
0343:                if (iFixedOffsetCache == null) {
0344:                    iFixedOffsetCache = new HashMap();
0345:                }
0346:                DateTimeZone zone;
0347:                Reference ref = (Reference) iFixedOffsetCache.get(id);
0348:                if (ref != null) {
0349:                    zone = (DateTimeZone) ref.get();
0350:                    if (zone != null) {
0351:                        return zone;
0352:                    }
0353:                }
0354:                zone = new FixedDateTimeZone(id, null, offset, offset);
0355:                iFixedOffsetCache.put(id, new SoftReference(zone));
0356:                return zone;
0357:            }
0358:
0359:            /**
0360:             * Gets all the available IDs supported.
0361:             * 
0362:             * @return an unmodifiable Set of String IDs
0363:             */
0364:            public static Set getAvailableIDs() {
0365:                return cAvailableIDs;
0366:            }
0367:
0368:            //-----------------------------------------------------------------------
0369:            /**
0370:             * Gets the zone provider factory.
0371:             * <p>
0372:             * The zone provider is a pluggable instance factory that supplies the
0373:             * actual instances of DateTimeZone.
0374:             * 
0375:             * @return the provider
0376:             */
0377:            public static Provider getProvider() {
0378:                return cProvider;
0379:            }
0380:
0381:            /**
0382:             * Sets the zone provider factory.
0383:             * <p>
0384:             * The zone provider is a pluggable instance factory that supplies the
0385:             * actual instances of DateTimeZone.
0386:             * 
0387:             * @param provider  provider to use, or null for default
0388:             * @throws SecurityException if you do not have the permission DateTimeZone.setProvider
0389:             * @throws IllegalArgumentException if the provider is invalid
0390:             */
0391:            public static void setProvider(Provider provider)
0392:                    throws SecurityException {
0393:                SecurityManager sm = System.getSecurityManager();
0394:                if (sm != null) {
0395:                    sm.checkPermission(new JodaTimePermission(
0396:                            "DateTimeZone.setProvider"));
0397:                }
0398:                setProvider0(provider);
0399:            }
0400:
0401:            /**
0402:             * Sets the zone provider factory without performing the security check.
0403:             * 
0404:             * @param provider  provider to use, or null for default
0405:             * @throws IllegalArgumentException if the provider is invalid
0406:             */
0407:            private static void setProvider0(Provider provider) {
0408:                if (provider == null) {
0409:                    provider = getDefaultProvider();
0410:                }
0411:                Set ids = provider.getAvailableIDs();
0412:                if (ids == null || ids.size() == 0) {
0413:                    throw new IllegalArgumentException(
0414:                            "The provider doesn't have any available ids");
0415:                }
0416:                if (!ids.contains("UTC")) {
0417:                    throw new IllegalArgumentException(
0418:                            "The provider doesn't support UTC");
0419:                }
0420:                if (!UTC.equals(provider.getZone("UTC"))) {
0421:                    throw new IllegalArgumentException(
0422:                            "Invalid UTC zone provided");
0423:                }
0424:                cProvider = provider;
0425:                cAvailableIDs = ids;
0426:            }
0427:
0428:            /**
0429:             * Gets the default zone provider.
0430:             * <p>
0431:             * Tries the system property <code>org.joda.time.DateTimeZone.Provider</code>.
0432:             * Then tries a <code>ZoneInfoProvider</code> using the data in <code>org/joda/time/tz/data</code>.
0433:             * Then uses <code>UTCProvider</code>.
0434:             * 
0435:             * @return the default name provider
0436:             */
0437:            private static Provider getDefaultProvider() {
0438:                Provider provider = null;
0439:
0440:                try {
0441:                    String providerClass = System
0442:                            .getProperty("org.joda.time.DateTimeZone.Provider");
0443:                    if (providerClass != null) {
0444:                        try {
0445:                            provider = (Provider) Class.forName(providerClass)
0446:                                    .newInstance();
0447:                        } catch (Exception ex) {
0448:                            Thread thread = Thread.currentThread();
0449:                            thread.getThreadGroup().uncaughtException(thread,
0450:                                    ex);
0451:                        }
0452:                    }
0453:                } catch (SecurityException ex) {
0454:                    // ignored
0455:                }
0456:
0457:                if (provider == null) {
0458:                    try {
0459:                        provider = new ZoneInfoProvider("org/joda/time/tz/data");
0460:                    } catch (Exception ex) {
0461:                        Thread thread = Thread.currentThread();
0462:                        thread.getThreadGroup().uncaughtException(thread, ex);
0463:                    }
0464:                }
0465:
0466:                if (provider == null) {
0467:                    provider = new UTCProvider();
0468:                }
0469:
0470:                return provider;
0471:            }
0472:
0473:            //-----------------------------------------------------------------------
0474:            /**
0475:             * Gets the name provider factory.
0476:             * <p>
0477:             * The name provider is a pluggable instance factory that supplies the
0478:             * names of each DateTimeZone.
0479:             * 
0480:             * @return the provider
0481:             */
0482:            public static NameProvider getNameProvider() {
0483:                return cNameProvider;
0484:            }
0485:
0486:            /**
0487:             * Sets the name provider factory.
0488:             * <p>
0489:             * The name provider is a pluggable instance factory that supplies the
0490:             * names of each DateTimeZone.
0491:             * 
0492:             * @param nameProvider  provider to use, or null for default
0493:             * @throws SecurityException if you do not have the permission DateTimeZone.setNameProvider
0494:             * @throws IllegalArgumentException if the provider is invalid
0495:             */
0496:            public static void setNameProvider(NameProvider nameProvider)
0497:                    throws SecurityException {
0498:                SecurityManager sm = System.getSecurityManager();
0499:                if (sm != null) {
0500:                    sm.checkPermission(new JodaTimePermission(
0501:                            "DateTimeZone.setNameProvider"));
0502:                }
0503:                setNameProvider0(nameProvider);
0504:            }
0505:
0506:            /**
0507:             * Sets the name provider factory without performing the security check.
0508:             * 
0509:             * @param nameProvider  provider to use, or null for default
0510:             * @throws IllegalArgumentException if the provider is invalid
0511:             */
0512:            private static void setNameProvider0(NameProvider nameProvider) {
0513:                if (nameProvider == null) {
0514:                    nameProvider = getDefaultNameProvider();
0515:                }
0516:                cNameProvider = nameProvider;
0517:            }
0518:
0519:            /**
0520:             * Gets the default name provider.
0521:             * <p>
0522:             * Tries the system property <code>org.joda.time.DateTimeZone.NameProvider</code>.
0523:             * Then uses <code>DefaultNameProvider</code>.
0524:             * 
0525:             * @return the default name provider
0526:             */
0527:            private static NameProvider getDefaultNameProvider() {
0528:                NameProvider nameProvider = null;
0529:                try {
0530:                    String providerClass = System
0531:                            .getProperty("org.joda.time.DateTimeZone.NameProvider");
0532:                    if (providerClass != null) {
0533:                        try {
0534:                            nameProvider = (NameProvider) Class.forName(
0535:                                    providerClass).newInstance();
0536:                        } catch (Exception ex) {
0537:                            Thread thread = Thread.currentThread();
0538:                            thread.getThreadGroup().uncaughtException(thread,
0539:                                    ex);
0540:                        }
0541:                    }
0542:                } catch (SecurityException ex) {
0543:                    // ignore
0544:                }
0545:
0546:                if (nameProvider == null) {
0547:                    nameProvider = new DefaultNameProvider();
0548:                }
0549:
0550:                return nameProvider;
0551:            }
0552:
0553:            //-----------------------------------------------------------------------
0554:            /**
0555:             * Converts an old style id to a new style id.
0556:             * 
0557:             * @param id  the old style id
0558:             * @return the new style id, null if not found
0559:             */
0560:            private static synchronized String getConvertedId(String id) {
0561:                Map map = cZoneIdConversion;
0562:                if (map == null) {
0563:                    // Backwards compatibility with TimeZone.
0564:                    map = new HashMap();
0565:                    map.put("GMT", "UTC");
0566:                    map.put("MIT", "Pacific/Apia");
0567:                    map.put("HST", "Pacific/Honolulu");
0568:                    map.put("AST", "America/Anchorage");
0569:                    map.put("PST", "America/Los_Angeles");
0570:                    map.put("MST", "America/Denver");
0571:                    map.put("PNT", "America/Phoenix");
0572:                    map.put("CST", "America/Chicago");
0573:                    map.put("EST", "America/New_York");
0574:                    map.put("IET", "America/Indianapolis");
0575:                    map.put("PRT", "America/Puerto_Rico");
0576:                    map.put("CNT", "America/St_Johns");
0577:                    map.put("AGT", "America/Buenos_Aires");
0578:                    map.put("BET", "America/Sao_Paulo");
0579:                    map.put("WET", "Europe/London");
0580:                    map.put("ECT", "Europe/Paris");
0581:                    map.put("ART", "Africa/Cairo");
0582:                    map.put("CAT", "Africa/Harare");
0583:                    map.put("EET", "Europe/Bucharest");
0584:                    map.put("EAT", "Africa/Addis_Ababa");
0585:                    map.put("MET", "Asia/Tehran");
0586:                    map.put("NET", "Asia/Yerevan");
0587:                    map.put("PLT", "Asia/Karachi");
0588:                    map.put("IST", "Asia/Calcutta");
0589:                    map.put("BST", "Asia/Dhaka");
0590:                    map.put("VST", "Asia/Saigon");
0591:                    map.put("CTT", "Asia/Shanghai");
0592:                    map.put("JST", "Asia/Tokyo");
0593:                    map.put("ACT", "Australia/Darwin");
0594:                    map.put("AET", "Australia/Sydney");
0595:                    map.put("SST", "Pacific/Guadalcanal");
0596:                    map.put("NST", "Pacific/Auckland");
0597:                    cZoneIdConversion = map;
0598:                }
0599:                return (String) map.get(id);
0600:            }
0601:
0602:            private static int parseOffset(String str) {
0603:                Chronology chrono;
0604:                if (cDefault != null) {
0605:                    chrono = ISOChronology.getInstanceUTC();
0606:                } else {
0607:                    // Can't use a real chronology if called during class
0608:                    // initialization. Offset parser doesn't need it anyhow.
0609:                    chrono = new BaseChronology() {
0610:                        public DateTimeZone getZone() {
0611:                            return null;
0612:                        }
0613:
0614:                        public Chronology withUTC() {
0615:                            return this ;
0616:                        }
0617:
0618:                        public Chronology withZone(DateTimeZone zone) {
0619:                            return this ;
0620:                        }
0621:
0622:                        public String toString() {
0623:                            return getClass().getName();
0624:                        }
0625:                    };
0626:                }
0627:
0628:                return -(int) offsetFormatter().withChronology(chrono)
0629:                        .parseMillis(str);
0630:            }
0631:
0632:            /**
0633:             * Formats a timezone offset string.
0634:             * <p>
0635:             * This method is kept separate from the formatting classes to speed and
0636:             * simplify startup and classloading.
0637:             * 
0638:             * @param offset  the offset in milliseconds
0639:             * @return the time zone string
0640:             */
0641:            private static String printOffset(int offset) {
0642:                StringBuffer buf = new StringBuffer();
0643:                if (offset >= 0) {
0644:                    buf.append('+');
0645:                } else {
0646:                    buf.append('-');
0647:                    offset = -offset;
0648:                }
0649:
0650:                int hours = offset / DateTimeConstants.MILLIS_PER_HOUR;
0651:                FormatUtils.appendPaddedInteger(buf, hours, 2);
0652:                offset -= hours * (int) DateTimeConstants.MILLIS_PER_HOUR;
0653:
0654:                int minutes = offset / DateTimeConstants.MILLIS_PER_MINUTE;
0655:                buf.append(':');
0656:                FormatUtils.appendPaddedInteger(buf, minutes, 2);
0657:                offset -= minutes * DateTimeConstants.MILLIS_PER_MINUTE;
0658:                if (offset == 0) {
0659:                    return buf.toString();
0660:                }
0661:
0662:                int seconds = offset / DateTimeConstants.MILLIS_PER_SECOND;
0663:                buf.append(':');
0664:                FormatUtils.appendPaddedInteger(buf, seconds, 2);
0665:                offset -= seconds * DateTimeConstants.MILLIS_PER_SECOND;
0666:                if (offset == 0) {
0667:                    return buf.toString();
0668:                }
0669:
0670:                buf.append('.');
0671:                FormatUtils.appendPaddedInteger(buf, offset, 3);
0672:                return buf.toString();
0673:            }
0674:
0675:            /**
0676:             * Gets a printer/parser for managing the offset id formatting.
0677:             * 
0678:             * @return the formatter
0679:             */
0680:            private static synchronized DateTimeFormatter offsetFormatter() {
0681:                if (cOffsetFormatter == null) {
0682:                    cOffsetFormatter = new DateTimeFormatterBuilder()
0683:                            .appendTimeZoneOffset(null, true, 2, 4)
0684:                            .toFormatter();
0685:                }
0686:                return cOffsetFormatter;
0687:            }
0688:
0689:            // Instance fields and methods
0690:            //--------------------------------------------------------------------
0691:
0692:            private final String iID;
0693:
0694:            /**
0695:             * Constructor.
0696:             * 
0697:             * @param id  the id to use
0698:             * @throws IllegalArgumentException if the id is null
0699:             */
0700:            protected DateTimeZone(String id) {
0701:                if (id == null) {
0702:                    throw new IllegalArgumentException("Id must not be null");
0703:                }
0704:                iID = id;
0705:            }
0706:
0707:            // Principal methods
0708:            //--------------------------------------------------------------------
0709:
0710:            /**
0711:             * Gets the ID of this datetime zone.
0712:             * 
0713:             * @return the ID of this datetime zone
0714:             */
0715:            public final String getID() {
0716:                return iID;
0717:            }
0718:
0719:            /**
0720:             * Returns a non-localized name that is unique to this time zone. It can be
0721:             * combined with id to form a unique key for fetching localized names.
0722:             *
0723:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
0724:             * @return name key or null if id should be used for names
0725:             */
0726:            public abstract String getNameKey(long instant);
0727:
0728:            /**
0729:             * Gets the short name of this datetime zone suitable for display using
0730:             * the default locale.
0731:             * <p>
0732:             * If the name is not available for the locale, then this method returns a
0733:             * string in the format <code>[+-]hh:mm</code>.
0734:             * 
0735:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
0736:             * @return the human-readable short name in the default locale
0737:             */
0738:            public final String getShortName(long instant) {
0739:                return getShortName(instant, null);
0740:            }
0741:
0742:            /**
0743:             * Gets the short name of this datetime zone suitable for display using
0744:             * the specified locale.
0745:             * <p>
0746:             * If the name is not available for the locale, then this method returns a
0747:             * string in the format <code>[+-]hh:mm</code>.
0748:             * 
0749:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
0750:             * @param locale  the locale to get the name for
0751:             * @return the human-readable short name in the specified locale
0752:             */
0753:            public String getShortName(long instant, Locale locale) {
0754:                if (locale == null) {
0755:                    locale = Locale.getDefault();
0756:                }
0757:                String nameKey = getNameKey(instant);
0758:                if (nameKey == null) {
0759:                    return iID;
0760:                }
0761:                String name = cNameProvider.getShortName(locale, iID, nameKey);
0762:                if (name != null) {
0763:                    return name;
0764:                }
0765:                return printOffset(getOffset(instant));
0766:            }
0767:
0768:            /**
0769:             * Gets the long name of this datetime zone suitable for display using
0770:             * the default locale.
0771:             * <p>
0772:             * If the name is not available for the locale, then this method returns a
0773:             * string in the format <code>[+-]hh:mm</code>.
0774:             * 
0775:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
0776:             * @return the human-readable long name in the default locale
0777:             */
0778:            public final String getName(long instant) {
0779:                return getName(instant, null);
0780:            }
0781:
0782:            /**
0783:             * Gets the long name of this datetime zone suitable for display using
0784:             * the specified locale.
0785:             * <p>
0786:             * If the name is not available for the locale, then this method returns a
0787:             * string in the format <code>[+-]hh:mm</code>.
0788:             * 
0789:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the name for
0790:             * @param locale  the locale to get the name for
0791:             * @return the human-readable long name in the specified locale
0792:             */
0793:            public String getName(long instant, Locale locale) {
0794:                if (locale == null) {
0795:                    locale = Locale.getDefault();
0796:                }
0797:                String nameKey = getNameKey(instant);
0798:                if (nameKey == null) {
0799:                    return iID;
0800:                }
0801:                String name = cNameProvider.getName(locale, iID, nameKey);
0802:                if (name != null) {
0803:                    return name;
0804:                }
0805:                return printOffset(getOffset(instant));
0806:            }
0807:
0808:            /**
0809:             * Gets the millisecond offset to add to UTC to get local time.
0810:             * 
0811:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
0812:             * @return the millisecond offset to add to UTC to get local time
0813:             */
0814:            public abstract int getOffset(long instant);
0815:
0816:            /**
0817:             * Gets the millisecond offset to add to UTC to get local time.
0818:             * 
0819:             * @param instant  instant to get the offset for, null means now
0820:             * @return the millisecond offset to add to UTC to get local time
0821:             */
0822:            public final int getOffset(ReadableInstant instant) {
0823:                if (instant == null) {
0824:                    return getOffset(DateTimeUtils.currentTimeMillis());
0825:                }
0826:                return getOffset(instant.getMillis());
0827:            }
0828:
0829:            /**
0830:             * Gets the standard millisecond offset to add to UTC to get local time,
0831:             * when standard time is in effect.
0832:             * 
0833:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
0834:             * @return the millisecond offset to add to UTC to get local time
0835:             */
0836:            public abstract int getStandardOffset(long instant);
0837:
0838:            /**
0839:             * Checks whether, at a particular instant, the offset is standard or not.
0840:             * <p>
0841:             * This method can be used to determine whether Summer Time (DST) applies.
0842:             * As a general rule, if the offset at the specified instant is standard,
0843:             * then either Winter time applies, or there is no Summer Time. If the
0844:             * instant is not standard, then Summer Time applies.
0845:             * <p>
0846:             * The implementation of the method is simply whether {@link #getOffset(long)}
0847:             * equals {@link #getStandardOffset(long)} at the specified instant.
0848:             * 
0849:             * @param instant  milliseconds from 1970-01-01T00:00:00Z to get the offset for
0850:             * @return true if the offset at the given instant is the standard offset
0851:             * @since 1.5
0852:             */
0853:            public boolean isStandardOffset(long instant) {
0854:                return getOffset(instant) == getStandardOffset(instant);
0855:            }
0856:
0857:            /**
0858:             * Gets the millisecond offset to subtract from local time to get UTC time.
0859:             * This offset can be used to undo adding the offset obtained by getOffset.
0860:             *
0861:             * <pre>
0862:             * millisLocal == millisUTC   + getOffset(millisUTC)
0863:             * millisUTC   == millisLocal - getOffsetFromLocal(millisLocal)
0864:             * </pre>
0865:             *
0866:             * NOTE: After calculating millisLocal, some error may be introduced. At
0867:             * offset transitions (due to DST or other historical changes), ranges of
0868:             * local times may map to different UTC times.
0869:             * <p>
0870:             * This method will return an offset suitable for calculating an instant
0871:             * after any DST gap. For example, consider a zone with a cutover
0872:             * from 01:00 to 01:59:<br />
0873:             * Input: 00:00  Output: 00:00<br />
0874:             * Input: 00:30  Output: 00:30<br />
0875:             * Input: 01:00  Output: 02:00<br />
0876:             * Input: 01:30  Output: 02:30<br />
0877:             * Input: 02:00  Output: 02:00<br />
0878:             * Input: 02:30  Output: 02:30<br />
0879:             * <p>
0880:             * NOTE: The behaviour of this method changed in v1.5, with the emphasis
0881:             * on returning a consistent result later along the time-line (shown above).
0882:             *
0883:             * @param instantLocal  the millisecond instant, relative to this time zone, to
0884:             * get the offset for
0885:             * @return the millisecond offset to subtract from local time to get UTC time
0886:             */
0887:            public int getOffsetFromLocal(long instantLocal) {
0888:                // get the offset at instantLocal (first estimate)
0889:                int offsetLocal = getOffset(instantLocal);
0890:                // adjust instantLocal using the estimate and recalc the offset
0891:                int offsetAdjusted = getOffset(instantLocal - offsetLocal);
0892:                // if the offsets differ, we must be near a DST boundary
0893:                if (offsetLocal != offsetAdjusted) {
0894:                    // we need to ensure that time is always after the DST gap
0895:                    // this happens naturally for positive offsets, but not for negative
0896:                    if ((offsetLocal - offsetAdjusted) < 0) {
0897:                        // if we just return offsetAdjusted then the time is pushed
0898:                        // back before the transition, whereas it should be
0899:                        // on or after the transition
0900:                        long nextLocal = nextTransition(instantLocal
0901:                                - offsetLocal);
0902:                        long nextAdjusted = nextTransition(instantLocal
0903:                                - offsetAdjusted);
0904:                        if (nextLocal != nextAdjusted) {
0905:                            return offsetLocal;
0906:                        }
0907:                    }
0908:                }
0909:                return offsetAdjusted;
0910:            }
0911:
0912:            /**
0913:             * Converts a standard UTC instant to a local instant with the same
0914:             * local time. This conversion is used before performing a calculation
0915:             * so that the calculation can be done using a simple local zone.
0916:             *
0917:             * @param instantUTC  the UTC instant to convert to local
0918:             * @return the local instant with the same local time
0919:             * @throws ArithmeticException if the result overflows a long
0920:             * @since 1.5
0921:             */
0922:            public long convertUTCToLocal(long instantUTC) {
0923:                int offset = getOffset(instantUTC);
0924:                long instantLocal = instantUTC + offset;
0925:                // If there is a sign change, but the two values have the same sign...
0926:                if ((instantUTC ^ instantLocal) < 0
0927:                        && (instantUTC ^ offset) >= 0) {
0928:                    throw new ArithmeticException(
0929:                            "Adding time zone offset caused overflow");
0930:                }
0931:                return instantLocal;
0932:            }
0933:
0934:            /**
0935:             * Converts a local instant to a standard UTC instant with the same
0936:             * local time. This conversion is used after performing a calculation
0937:             * where the calculation was done using a simple local zone.
0938:             *
0939:             * @param instantLocal  the local instant to convert to UTC
0940:             * @param strict  whether the conversion should reject non-existent local times
0941:             * @return the UTC instant with the same local time, 
0942:             * @throws ArithmeticException if the result overflows a long
0943:             * @throws IllegalArgumentException if the zone has no eqivalent local time
0944:             * @since 1.5
0945:             */
0946:            public long convertLocalToUTC(long instantLocal, boolean strict) {
0947:                // get the offset at instantLocal (first estimate)
0948:                int offsetLocal = getOffset(instantLocal);
0949:                // adjust instantLocal using the estimate and recalc the offset
0950:                int offset = getOffset(instantLocal - offsetLocal);
0951:                // if the offsets differ, we must be near a DST boundary
0952:                if (offsetLocal != offset) {
0953:                    // if strict then always check if in DST gap
0954:                    // otherwise only check if zone in Western hemisphere (as the
0955:                    // value of offset is already correct for Eastern hemisphere)
0956:                    if (strict || offsetLocal < 0) {
0957:                        // determine if we are in the DST gap
0958:                        long nextLocal = nextTransition(instantLocal
0959:                                - offsetLocal);
0960:                        long nextAdjusted = nextTransition(instantLocal
0961:                                - offset);
0962:                        if (nextLocal != nextAdjusted) {
0963:                            // yes we are in the DST gap
0964:                            if (strict) {
0965:                                // DST gap is not acceptable
0966:                                throw new IllegalArgumentException(
0967:                                        "Illegal instant due to time zone offset transition: "
0968:                                                + DateTimeFormat
0969:                                                        .forPattern(
0970:                                                                "yyyy-MM-dd'T'HH:mm:ss.SSS")
0971:                                                        .print(
0972:                                                                new Instant(
0973:                                                                        instantLocal))
0974:                                                + " (" + getID() + ")");
0975:                            } else {
0976:                                // DST gap is acceptable, but for the Western hemisphere
0977:                                // the offset is wrong and will result in local times
0978:                                // before the cutover so use the offsetLocal instead
0979:                                offset = offsetLocal;
0980:                            }
0981:                        }
0982:                    }
0983:                }
0984:                // check for overflow
0985:                long instantUTC = instantLocal - offset;
0986:                // If there is a sign change, but the two values have different signs...
0987:                if ((instantLocal ^ instantUTC) < 0
0988:                        && (instantLocal ^ offset) < 0) {
0989:                    throw new ArithmeticException(
0990:                            "Subtracting time zone offset caused overflow");
0991:                }
0992:                return instantUTC;
0993:            }
0994:
0995:            /**
0996:             * Gets the millisecond instant in another zone keeping the same local time.
0997:             * <p>
0998:             * The conversion is performed by converting the specified UTC millis to local
0999:             * millis in this zone, then converting back to UTC millis in the new zone.
1000:             *
1001:             * @param newZone  the new zone, null means default
1002:             * @param oldInstant  the UTC millisecond instant to convert
1003:             * @return the UTC millisecond instant with the same local time in the new zone
1004:             */
1005:            public long getMillisKeepLocal(DateTimeZone newZone, long oldInstant) {
1006:                if (newZone == null) {
1007:                    newZone = DateTimeZone.getDefault();
1008:                }
1009:                if (newZone == this ) {
1010:                    return oldInstant;
1011:                }
1012:                long instantLocal = oldInstant + getOffset(oldInstant);
1013:                return instantLocal - newZone.getOffsetFromLocal(instantLocal);
1014:            }
1015:
1016:            /**
1017:             * Returns true if this time zone has no transitions.
1018:             *
1019:             * @return true if no transitions
1020:             */
1021:            public abstract boolean isFixed();
1022:
1023:            /**
1024:             * Advances the given instant to where the time zone offset or name changes.
1025:             * If the instant returned is exactly the same as passed in, then
1026:             * no changes occur after the given instant.
1027:             *
1028:             * @param instant  milliseconds from 1970-01-01T00:00:00Z
1029:             * @return milliseconds from 1970-01-01T00:00:00Z
1030:             */
1031:            public abstract long nextTransition(long instant);
1032:
1033:            /**
1034:             * Retreats the given instant to where the time zone offset or name changes.
1035:             * If the instant returned is exactly the same as passed in, then
1036:             * no changes occur before the given instant.
1037:             *
1038:             * @param instant  milliseconds from 1970-01-01T00:00:00Z
1039:             * @return milliseconds from 1970-01-01T00:00:00Z
1040:             */
1041:            public abstract long previousTransition(long instant);
1042:
1043:            // Basic methods
1044:            //--------------------------------------------------------------------
1045:
1046:            /**
1047:             * Get the datetime zone as a {@link java.util.TimeZone}.
1048:             * 
1049:             * @return the closest matching TimeZone object
1050:             */
1051:            public java.util.TimeZone toTimeZone() {
1052:                return java.util.TimeZone.getTimeZone(iID);
1053:            }
1054:
1055:            /**
1056:             * Compare this datetime zone with another.
1057:             * 
1058:             * @param object the object to compare with
1059:             * @return true if equal, based on the ID and all internal rules
1060:             */
1061:            public abstract boolean equals(Object object);
1062:
1063:            /**
1064:             * Gets a hash code compatable with equals.
1065:             * 
1066:             * @return suitable hashcode
1067:             */
1068:            public int hashCode() {
1069:                return 57 + getID().hashCode();
1070:            }
1071:
1072:            /**
1073:             * Gets the datetime zone as a string, which is simply its ID.
1074:             * @return the id of the zone
1075:             */
1076:            public String toString() {
1077:                return getID();
1078:            }
1079:
1080:            /**
1081:             * By default, when DateTimeZones are serialized, only a "stub" object
1082:             * referring to the id is written out. When the stub is read in, it
1083:             * replaces itself with a DateTimeZone object.
1084:             * @return a stub object to go in the stream
1085:             */
1086:            protected Object writeReplace() throws ObjectStreamException {
1087:                return new Stub(iID);
1088:            }
1089:
1090:            /**
1091:             * Used to serialize DateTimeZones by id.
1092:             */
1093:            private static final class Stub implements  Serializable {
1094:                /** Serialization lock. */
1095:                private static final long serialVersionUID = -6471952376487863581L;
1096:                /** The ID of the zone. */
1097:                private transient String iID;
1098:
1099:                /**
1100:                 * Constructor.
1101:                 * @param id  the id of the zone
1102:                 */
1103:                Stub(String id) {
1104:                    iID = id;
1105:                }
1106:
1107:                private void writeObject(ObjectOutputStream out)
1108:                        throws IOException {
1109:                    out.writeUTF(iID);
1110:                }
1111:
1112:                private void readObject(ObjectInputStream in)
1113:                        throws IOException {
1114:                    iID = in.readUTF();
1115:                }
1116:
1117:                private Object readResolve() throws ObjectStreamException {
1118:                    return forID(iID);
1119:                }
1120:            }
1121:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.