Source Code Cross Referenced for DateTimeFormat.java in  » Ajax » GWT » com » google » gwt » i18n » client » 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 » Ajax » GWT » com.google.gwt.i18n.client 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2007 Google Inc.
0003:         * 
0004:         * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005:         * use this file except in compliance with the License. You may obtain a copy of
0006:         * 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, WITHOUT
0012:         * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013:         * License for the specific language governing permissions and limitations under
0014:         * the License.
0015:         */
0016:
0017:        package com.google.gwt.i18n.client;
0018:
0019:        import com.google.gwt.core.client.GWT;
0020:        import com.google.gwt.i18n.client.constants.DateTimeConstants;
0021:
0022:        import java.util.ArrayList;
0023:        import java.util.Date;
0024:
0025:        /**
0026:         * Formats and parses dates and times using locale-sensitive patterns.
0027:         * 
0028:         * <h3>Patterns</h3>
0029:         * 
0030:         * <table>
0031:         * <tr>
0032:         * <th>Symbol</th>
0033:         * <th>Meaning</th>
0034:         * <th>Presentation</th>
0035:         * <th>Example</th>
0036:         * </tr>
0037:         * 
0038:         * <tr>
0039:         * <td><code>G</code></td>
0040:         * <td>era designator</td>
0041:         * <td>Text</td>
0042:         * <td><code>AD</code></td>
0043:         * </tr>
0044:         * 
0045:         * <tr>
0046:         * <td><code>y</code></td>
0047:         * <td>year</td>
0048:         * <td>Number</td>
0049:         * <td><code>1996</code></td>
0050:         * </tr>
0051:         * 
0052:         * <tr>
0053:         * <td><code>M</code></td>
0054:         * <td>month in year</td>
0055:         * <td>Text or Number</td>
0056:         * <td><code>July (or) 07</code></td>
0057:         * </tr>
0058:         * 
0059:         * <tr>
0060:         * <td><code>d</code></td>
0061:         * <td>day in month</td>
0062:         * <td>Number</td>
0063:         * <td><code>10</code></td>
0064:         * </tr>
0065:         * 
0066:         * <tr>
0067:         * <td><code>h</code></td>
0068:         * <td>hour in am/pm (1-12)</td>
0069:         * <td>Number</td>
0070:         * <td><code>12</code></td>
0071:         * </tr>
0072:         * 
0073:         * <tr>
0074:         * <td><code>H</code></td>
0075:         * <td>hour in day (0-23)</td>
0076:         * <td>Number</td>
0077:         * <td><code>0</code></td>
0078:         * </tr>
0079:         * 
0080:         * <tr>
0081:         * <td><code>m</code></td>
0082:         * <td>minute in hour</td>
0083:         * <td>Number</td>
0084:         * <td><code>30</code></td>
0085:         * </tr>
0086:         * 
0087:         * <tr>
0088:         * <td><code>s</code></td>
0089:         * <td>second in minute</td>
0090:         * <td>Number</td>
0091:         * <td><code>55</code></td>
0092:         * </tr>
0093:         * 
0094:         * <tr>
0095:         * <td><code>S</code></td>
0096:         * <td>fractional second</td>
0097:         * <td>Number</td>
0098:         * <td><code>978</code></td>
0099:         * </tr>
0100:         * 
0101:         * <tr>
0102:         * <td><code>E</code></td>
0103:         * <td>day of week</td>
0104:         * <td>Text</td>
0105:         * <td><code>Tuesday</code></td>
0106:         * </tr>
0107:         * 
0108:         * <tr>
0109:         * <td><code>a</code></td>
0110:         * <td>am/pm marker</td>
0111:         * <td>Text</td>
0112:         * <td><code>PM</code></td>
0113:         * </tr>
0114:         * 
0115:         * <tr>
0116:         * <td><code>k</code></td>
0117:         * <td>hour in day (1-24)</td>
0118:         * <td>Number</td>
0119:         * <td><code>24</code></td>
0120:         * </tr>
0121:         * 
0122:         * <tr>
0123:         * <td><code>K</code></td>
0124:         * <td>hour in am/pm (0-11)</td>
0125:         * <td>Number</td>
0126:         * <td><code>0</code></td>
0127:         * </tr>
0128:         * 
0129:         * <tr>
0130:         * <td><code>z</code></td>
0131:         * <td>time zone</td>
0132:         * <td>Text</td>
0133:         * <td><code>Pacific Standard Time</code></td>
0134:         * </tr>
0135:         * 
0136:         * <tr>
0137:         * <td><code>Z</code></td>
0138:         * <td>time zone (RFC 822)</td>
0139:         * <td>Number</td>
0140:         * <td><code>-0800</code></td>
0141:         * </tr>
0142:         * 
0143:         * <tr>
0144:         * <td><code>v</code></td>
0145:         * <td>time zone (generic)</td>
0146:         * <td>Text</td>
0147:         * <td><code>Pacific Time</code></td>
0148:         * </tr>
0149:         * 
0150:         * <tr>
0151:         * <td><code>'</code></td>
0152:         * <td>escape for text</td>
0153:         * <td>Delimiter</td>
0154:         * <td><code>'Date='</code></td>
0155:         * </tr>
0156:         * 
0157:         * <tr>
0158:         * <td><code>''</code></td>
0159:         * <td>single quote</td>
0160:         * <td>Literal</td>
0161:         * <td><code>'o''clock'</code></td>
0162:         * </tr>
0163:         * </table>
0164:         * 
0165:         * <p>
0166:         * The number of pattern letters influences the format, as follows:
0167:         * </p>
0168:         * 
0169:         * <dl>
0170:         * <dt>Text</dt>
0171:         * <dd>if 4 or more, then use the full form; if less than 4, use short or
0172:         * abbreviated form if it exists (e.g., <code>"EEEE"</code> produces
0173:         * <code>"Monday"</code>, <code>"EEE"</code> produces <code>"Mon"</code>)</dd>
0174:         * 
0175:         * <dt>Number</dt>
0176:         * <dd>the minimum number of digits. Shorter numbers are zero-padded to this
0177:         * amount (e.g. if <code>"m"</code> produces <code>"6"</code>,
0178:         * <code>"mm"</code> produces <code>"06"</code>). Year is handled
0179:         * specially; that is, if the count of 'y' is 2, the Year will be truncated to 2
0180:         * digits. (e.g., if <code>"yyyy"</code> produces <code>"1997"</code>,
0181:         * <code>"yy"</code> produces <code>"97"</code>.) Unlike other fields,
0182:         * fractional seconds are padded on the right with zero.</dd>
0183:         * 
0184:         * <dt>Text or Number</dt>
0185:         * <dd>3 or more, use text, otherwise use number. (e.g. <code>"M"</code>
0186:         * produces <code>"1"</code>, <code>"MM"</code> produces <code>"01"</code>,
0187:         * <code>"MMM"</code> produces <code>"Jan"</code>, and <code>"MMMM"</code>
0188:         * produces <code>"January"</code>.</dd>
0189:         * </dl>
0190:         * 
0191:         * <p>
0192:         * Any characters in the pattern that are not in the ranges of ['<code>a</code>'..'<code>z</code>']
0193:         * and ['<code>A</code>'..'<code>Z</code>'] will be treated as quoted
0194:         * text. For instance, characters like '<code>:</code>', '<code>.</code>', '<code> </code>'
0195:         * (space), '<code>#</code>' and '<code>@</code>' will appear in the
0196:         * resulting time text even they are not embraced within single quotes.
0197:         * </p>
0198:         * 
0199:         * <h3>Parsing Dates and Times</h3>
0200:         * <p>
0201:         * This implementation could parse partial date/time. Current date will be
0202:         * used to fill in the unavailable date part.  00:00:00 will be used to
0203:         * fill in the time part.
0204:         * </p>
0205:         * 
0206:         * <p>
0207:         * As with formatting (described above), the count of pattern letters determine
0208:         * the parsing behavior.
0209:         * </p>
0210:         * 
0211:         * <dl>
0212:         * <dt>Text</dt>
0213:         * <dd>4 or more pattern letters--use full form, less than 4--use short or
0214:         * abbreviated form if one exists. In parsing, we will always try long format,
0215:         * then short.</dd>
0216:         * 
0217:         * <dt>Number</dt>
0218:         * <dd>the minimum number of digits.</dd>
0219:         * 
0220:         * <dt>Text or Number</dt>
0221:         * <dd>3 or more characters means use text, otherwise use number</dd>
0222:         * </dl>
0223:         * 
0224:         * <p>
0225:         * Although the current pattern specification doesn't not specify behavior for
0226:         * all letters, it may in the future. It is strongly discouraged to used
0227:         * unspecified letters as literal text without being surrounded by quotes.
0228:         * </p>
0229:         * 
0230:         * <h3>Examples</h3>
0231:         * <table>
0232:         * <tr>
0233:         * <th>Pattern</th>
0234:         * <th>Formatted Text</th>
0235:         * </tr>
0236:         * 
0237:         * <tr>
0238:         * <td><code>"yyyy.MM.dd G 'at' HH:mm:ss vvvv"</code></td>
0239:         * <td><code>1996.07.10 AD at 15:08:56 Pacific Time</code></td>
0240:         * </tr>
0241:         * 
0242:         * <tr>
0243:         * <td><code>"EEE, MMM d, ''yy"</code></td>
0244:         * <td><code>Wed, July 10, '96</code></td>
0245:         * </tr>
0246:         * 
0247:         * <tr>
0248:         * <td><code>"h:mm a"</code></td>
0249:         * <td><code>12:08 PM</code></td>
0250:         * </tr>
0251:         * 
0252:         * <tr>
0253:         * <td><code>"hh 'o''clock' a, zzzz"</code></td>
0254:         * <td><code> 12 o'clock PM, Pacific Daylight Time</code></td>
0255:         * </tr>
0256:         * 
0257:         * <tr>
0258:         * <td><code>"K:mm a, vvv"</code></td>
0259:         * <td><code> 0:00 PM, PT</code></td>
0260:         * </tr>
0261:         * 
0262:         * <tr>
0263:         * <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code></td>
0264:         * <td><code>01996.July.10 AD 12:08 PM</code></td>
0265:         * </tr>
0266:         * </table>
0267:         * 
0268:         * <h3>Additional Parsing Considerations</h3>
0269:         * <p>
0270:         * When parsing a date string using the abbreviated year pattern (<code>"yy"</code>),
0271:         * the parser must interpret the abbreviated year relative to some century. It
0272:         * does this by adjusting dates to be within 80 years before and 20 years after
0273:         * the time the parser instance is created. For example, using a pattern of
0274:         * <code>"MM/dd/yy"</code> and a <code>DateTimeFormat</code> object created
0275:         * on Jan 1, 1997, the string <code>"01/11/12"</code> would be interpreted as
0276:         * Jan 11, 2012 while the string <code>"05/04/64"</code> would be interpreted
0277:         * as May 4, 1964. During parsing, only strings consisting of exactly two
0278:         * digits, as defined by {@link java.lang.Character#isDigit(char)}, will be
0279:         * parsed into the default century. If the year pattern does not have exactly
0280:         * two 'y' characters, the year is interpreted literally, regardless of the
0281:         * number of digits. For example, using the pattern <code>"MM/dd/yyyy"</code>,
0282:         * "01/11/12" parses to Jan 11, 12 A.D.
0283:         * </p>
0284:         * 
0285:         * <p>
0286:         * When numeric fields abut one another directly, with no intervening delimiter
0287:         * characters, they constitute a run of abutting numeric fields. Such runs are
0288:         * parsed specially. For example, the format "HHmmss" parses the input text
0289:         * "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and fails to
0290:         * parse "1234". In other words, the leftmost field of the run is flexible,
0291:         * while the others keep a fixed width. If the parse fails anywhere in the run,
0292:         * then the leftmost field is shortened by one character, and the entire run is
0293:         * parsed again. This is repeated until either the parse succeeds or the
0294:         * leftmost field is one character in length. If the parse still fails at that
0295:         * point, the parse of the run fails.
0296:         * </p>
0297:         * 
0298:         * <p>
0299:         * In the current implementation, timezone parsing only supports
0300:         * <code>GMT:hhmm</code>, <code>GMT:+hhmm</code>, and
0301:         * <code>GMT:-hhmm</code>.
0302:         * </p>
0303:         */
0304:        public class DateTimeFormat {
0305:            /**
0306:             * Class PatternPart holds a "compiled" pattern part.
0307:             */
0308:            private class PatternPart {
0309:                public String text;
0310:                public int count; // 0 has a special meaning, it stands for literal
0311:                public boolean abutStart;
0312:
0313:                public PatternPart(String txt, int cnt) {
0314:                    text = txt;
0315:                    count = cnt;
0316:                    abutStart = false;
0317:                }
0318:            }
0319:
0320:            private static final int FULL_DATE_FORMAT = 0;
0321:            private static final int LONG_DATE_FORMAT = 1;
0322:            private static final int MEDIUM_DATE_FORMAT = 2;
0323:            private static final int SHORT_DATE_FORMAT = 3;
0324:            private static final int FULL_TIME_FORMAT = 0;
0325:            private static final int LONG_TIME_FORMAT = 1;
0326:            private static final int MEDIUM_TIME_FORMAT = 2;
0327:
0328:            private static final int SHORT_TIME_FORMAT = 3;
0329:            private static final int NUMBER_BASE = 10;
0330:            private static final int JS_START_YEAR = 1900;
0331:
0332:            private static DateTimeFormat cachedFullDateFormat;
0333:            private static DateTimeFormat cachedLongDateFormat;
0334:            private static DateTimeFormat cachedMediumDateFormat;
0335:
0336:            private static DateTimeFormat cachedShortDateFormat;
0337:            private static DateTimeFormat cachedFullTimeFormat;
0338:            private static DateTimeFormat cachedLongTimeFormat;
0339:            private static DateTimeFormat cachedMediumTimeFormat;
0340:
0341:            private static DateTimeFormat cachedShortTimeFormat;
0342:            private static DateTimeFormat cachedFullDateTimeFormat;
0343:            private static DateTimeFormat cachedLongDateTimeFormat;
0344:            private static DateTimeFormat cachedMediumDateTimeFormat;
0345:            private static DateTimeFormat cachedShortDateTimeFormat;
0346:
0347:            private static final DateTimeConstants defaultDateTimeConstants = (DateTimeConstants) GWT
0348:                    .create(DateTimeConstants.class);
0349:
0350:            private static final String PATTERN_CHARS = "GyMdkHmsSEDahKzZv";
0351:
0352:            private static final String NUMERIC_FORMAT_CHARS = "MydhHmsSDkK";
0353:
0354:            private static final String WHITE_SPACE = " \t\r\n";
0355:
0356:            private static final String GMT = "GMT";
0357:
0358:            private static final int MINUTES_PER_HOUR = 60;
0359:
0360:            /**
0361:             * Returns a format object using the specified pattern and the date time
0362:             * constants for the default locale. If you need to format or parse repeatedly
0363:             * using the same pattern, it is highly recommended that you cache the
0364:             * returned <code>DateTimeFormat</code> object and reuse it rather than
0365:             * calling this method repeatedly.
0366:             * 
0367:             * @param pattern string to specify how the date should be formatted
0368:             * 
0369:             * @return a <code>DateTimeFormat</code> object that can be used for format
0370:             *         or parse date/time values matching the specified pattern
0371:             * 
0372:             * @throws IllegalArgumentException if the specified pattern could not be
0373:             *           parsed
0374:             */
0375:            public static DateTimeFormat getFormat(String pattern) {
0376:                return new DateTimeFormat(pattern, defaultDateTimeConstants);
0377:            }
0378:
0379:            public static DateTimeFormat getFullDateFormat() {
0380:                if (cachedFullDateFormat == null) {
0381:                    String pattern = defaultDateTimeConstants.dateFormats()[FULL_DATE_FORMAT];
0382:                    cachedFullDateFormat = new DateTimeFormat(pattern);
0383:                }
0384:                return cachedFullDateFormat;
0385:            }
0386:
0387:            public static DateTimeFormat getFullDateTimeFormat() {
0388:                if (cachedFullDateTimeFormat == null) {
0389:                    String pattern = defaultDateTimeConstants.dateFormats()[FULL_DATE_FORMAT]
0390:                            + " "
0391:                            + defaultDateTimeConstants.timeFormats()[FULL_TIME_FORMAT];
0392:                    cachedFullDateTimeFormat = new DateTimeFormat(pattern);
0393:                }
0394:                return cachedFullDateTimeFormat;
0395:            }
0396:
0397:            public static DateTimeFormat getFullTimeFormat() {
0398:                if (cachedFullTimeFormat == null) {
0399:                    String pattern = defaultDateTimeConstants.timeFormats()[FULL_TIME_FORMAT];
0400:                    cachedFullTimeFormat = new DateTimeFormat(pattern);
0401:                }
0402:                return cachedFullTimeFormat;
0403:            }
0404:
0405:            public static DateTimeFormat getLongDateFormat() {
0406:                if (cachedLongDateFormat == null) {
0407:                    String pattern = defaultDateTimeConstants.dateFormats()[LONG_DATE_FORMAT];
0408:                    cachedLongDateFormat = new DateTimeFormat(pattern);
0409:                }
0410:                return cachedLongDateFormat;
0411:            }
0412:
0413:            public static DateTimeFormat getLongDateTimeFormat() {
0414:                if (cachedLongDateTimeFormat == null) {
0415:                    String pattern = defaultDateTimeConstants.dateFormats()[LONG_DATE_FORMAT]
0416:                            + " "
0417:                            + defaultDateTimeConstants.timeFormats()[LONG_TIME_FORMAT];
0418:                    cachedLongDateTimeFormat = new DateTimeFormat(pattern);
0419:                }
0420:                return cachedLongDateTimeFormat;
0421:            }
0422:
0423:            public static DateTimeFormat getLongTimeFormat() {
0424:                if (cachedLongTimeFormat == null) {
0425:                    String pattern = defaultDateTimeConstants.timeFormats()[LONG_TIME_FORMAT];
0426:                    cachedLongTimeFormat = new DateTimeFormat(pattern);
0427:                }
0428:                return cachedLongTimeFormat;
0429:            }
0430:
0431:            public static DateTimeFormat getMediumDateFormat() {
0432:                if (cachedMediumDateFormat == null) {
0433:                    String pattern = defaultDateTimeConstants.dateFormats()[MEDIUM_DATE_FORMAT];
0434:                    cachedMediumDateFormat = new DateTimeFormat(pattern);
0435:                }
0436:                return cachedMediumDateFormat;
0437:            }
0438:
0439:            public static DateTimeFormat getMediumDateTimeFormat() {
0440:                if (cachedMediumDateTimeFormat == null) {
0441:                    String pattern = defaultDateTimeConstants.dateFormats()[MEDIUM_DATE_FORMAT]
0442:                            + " "
0443:                            + defaultDateTimeConstants.timeFormats()[MEDIUM_TIME_FORMAT];
0444:                    cachedMediumDateTimeFormat = new DateTimeFormat(pattern);
0445:                }
0446:                return cachedMediumDateTimeFormat;
0447:            }
0448:
0449:            public static DateTimeFormat getMediumTimeFormat() {
0450:                if (cachedMediumTimeFormat == null) {
0451:                    String pattern = defaultDateTimeConstants.timeFormats()[MEDIUM_TIME_FORMAT];
0452:                    cachedMediumTimeFormat = new DateTimeFormat(pattern);
0453:                }
0454:                return cachedMediumTimeFormat;
0455:            }
0456:
0457:            public static DateTimeFormat getShortDateFormat() {
0458:                if (cachedShortDateFormat == null) {
0459:                    String pattern = defaultDateTimeConstants.dateFormats()[SHORT_DATE_FORMAT];
0460:                    cachedShortDateFormat = new DateTimeFormat(pattern);
0461:                }
0462:                return cachedShortDateFormat;
0463:            }
0464:
0465:            public static DateTimeFormat getShortDateTimeFormat() {
0466:                if (cachedShortDateTimeFormat == null) {
0467:                    String pattern = defaultDateTimeConstants.dateFormats()[SHORT_DATE_FORMAT]
0468:                            + " "
0469:                            + defaultDateTimeConstants.timeFormats()[SHORT_TIME_FORMAT];
0470:                    cachedShortDateTimeFormat = new DateTimeFormat(pattern);
0471:                }
0472:                return cachedShortDateTimeFormat;
0473:            }
0474:
0475:            public static DateTimeFormat getShortTimeFormat() {
0476:                if (cachedShortTimeFormat == null) {
0477:                    String pattern = defaultDateTimeConstants.timeFormats()[SHORT_TIME_FORMAT];
0478:                    cachedShortTimeFormat = new DateTimeFormat(pattern);
0479:                }
0480:                return cachedShortTimeFormat;
0481:            }
0482:
0483:            private final ArrayList<PatternPart> patternParts = new ArrayList<PatternPart>();
0484:
0485:            private final DateTimeConstants dateTimeConstants;
0486:
0487:            private final String pattern;
0488:
0489:            /**
0490:             * Constructs a format object using the specified pattern and the date time
0491:             * constants for the default locale.
0492:             * 
0493:             * @param pattern string pattern specification
0494:             */
0495:            protected DateTimeFormat(String pattern) {
0496:                this (pattern, defaultDateTimeConstants);
0497:            }
0498:
0499:            /**
0500:             * Constructs a format object using the specified pattern and user-supplied
0501:             * date time constants.
0502:             * 
0503:             * @param pattern string pattern specification
0504:             * @param dateTimeConstants locale specific symbol collection
0505:             */
0506:            protected DateTimeFormat(String pattern,
0507:                    DateTimeConstants dateTimeConstants) {
0508:                this .pattern = pattern;
0509:                this .dateTimeConstants = dateTimeConstants;
0510:
0511:                /*
0512:                 * Even though the pattern is only compiled for use in parsing and parsing
0513:                 * is far less common than formatting, the pattern is still parsed eagerly
0514:                 * here to fail fast in case the pattern itself is malformed.
0515:                 */
0516:                parsePattern(pattern);
0517:            }
0518:
0519:            /**
0520:             * Format a date object.
0521:             * 
0522:             * @param date the date object being formatted
0523:             * 
0524:             * @return formatted date representation
0525:             */
0526:            public String format(Date date) {
0527:                StringBuffer toAppendTo = new StringBuffer(64);
0528:                int j, n = pattern.length();
0529:                for (int i = 0; i < n;) {
0530:                    char ch = pattern.charAt(i);
0531:                    if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
0532:                        // ch is a date-time pattern character to be interpreted by subFormat().
0533:                        // Count the number of times it is repeated.
0534:                        for (j = i + 1; j < n && pattern.charAt(j) == ch; ++j) {
0535:                        }
0536:                        subFormat(toAppendTo, ch, j - i, date);
0537:                        i = j;
0538:                    } else if (ch == '\'') {
0539:                        // Handle an entire quoted string, included embedded
0540:                        // doubled apostrophes (as in 'o''clock').
0541:
0542:                        // i points after '.
0543:                        ++i;
0544:
0545:                        // If start with '', just add ' and continue.
0546:                        if (i < n && pattern.charAt(i) == '\'') {
0547:                            toAppendTo.append('\'');
0548:                            ++i;
0549:                            continue;
0550:                        }
0551:
0552:                        // Otherwise add the quoted string.
0553:                        boolean trailQuote = false;
0554:                        while (!trailQuote) {
0555:                            // j points to next ' or EOS.
0556:                            j = i;
0557:                            while (j < n && pattern.charAt(j) != '\'') {
0558:                                ++j;
0559:                            }
0560:
0561:                            if (j >= n) {
0562:                                // Trailing ' (pathological).
0563:                                throw new IllegalArgumentException(
0564:                                        "Missing trailing \'");
0565:                            }
0566:
0567:                            // Look ahead to detect '' within quotes.
0568:                            if (j + 1 < n && pattern.charAt(j + 1) == '\'') {
0569:                                ++j;
0570:                            } else {
0571:                                trailQuote = true;
0572:                            }
0573:                            toAppendTo.append(pattern.substring(i, j));
0574:                            i = j + 1;
0575:                        }
0576:                    } else {
0577:                        // Append unquoted literal characters.
0578:                        toAppendTo.append(ch);
0579:                        ++i;
0580:                    }
0581:                }
0582:
0583:                return toAppendTo.toString();
0584:            }
0585:
0586:            public String getPattern() {
0587:                return pattern;
0588:            }
0589:
0590:            /**
0591:             * Parses text to produce a {@link Date} value. An
0592:             * {@link IllegalArgumentException} is thrown if either the text is empty or
0593:             * if the parse does not consume all characters of the text.
0594:             * 
0595:             * @param text the string being parsed
0596:             * @return a parsed date/time value
0597:             * @throws IllegalArgumentException if the entire text could not be converted
0598:             *           into a number
0599:             */
0600:            public Date parse(String text) {
0601:                Date curDate = new Date();
0602:                Date date = new Date(curDate.getYear(), curDate.getMonth(),
0603:                        curDate.getDate());
0604:                int charsConsumed = parse(text, 0, date);
0605:                if (charsConsumed == 0 || charsConsumed < text.length()) {
0606:                    throw new IllegalArgumentException(text);
0607:                }
0608:                return date;
0609:            }
0610:
0611:            /**
0612:             * This method parses the input string, fill its value into a {@link Date}.
0613:             * 
0614:             * @param text the string that need to be parsed
0615:             * @param start the character position in "text" where parsing should start
0616:             * @param date the date object that will hold parsed value
0617:             * 
0618:             * @return 0 if parsing failed, otherwise the number of characters advanced
0619:             */
0620:            public int parse(String text, int start, Date date) {
0621:                DateRecord cal = new DateRecord();
0622:                int[] parsePos = { start };
0623:
0624:                // For parsing abutting numeric fields. 'abutPat' is the
0625:                // offset into 'pattern' of the first of 2 or more abutting
0626:                // numeric fields. 'abutStart' is the offset into 'text'
0627:                // where parsing the fields begins. 'abutPass' starts off as 0
0628:                // and increments each time we try to parse the fields.
0629:                int abutPat = -1; // If >=0, we are in a run of abutting numeric fields.
0630:                int abutStart = 0;
0631:                int abutPass = 0;
0632:
0633:                for (int i = 0; i < patternParts.size(); ++i) {
0634:                    PatternPart part = patternParts.get(i);
0635:
0636:                    if (part.count > 0) {
0637:                        if (abutPat < 0 && part.abutStart) {
0638:                            abutPat = i;
0639:                            abutStart = start;
0640:                            abutPass = 0;
0641:                        }
0642:
0643:                        // Handle fields within a run of abutting numeric fields. Take
0644:                        // the pattern "HHmmss" as an example. We will try to parse
0645:                        // 2/2/2 characters of the input text, then if that fails,
0646:                        // 1/2/2. We only adjust the width of the leftmost field; the
0647:                        // others remain fixed. This allows "123456" => 12:34:56, but
0648:                        // "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we
0649:                        // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
0650:                        if (abutPat >= 0) {
0651:                            // If we are at the start of a run of abutting fields, then
0652:                            // shorten this field in each pass. If we can't shorten
0653:                            // this field any more, then the parse of this set of
0654:                            // abutting numeric fields has failed.
0655:                            int count = part.count;
0656:                            if (i == abutPat) {
0657:                                count -= abutPass++;
0658:                                if (count == 0) {
0659:                                    return 0;
0660:                                }
0661:                            }
0662:
0663:                            if (!subParse(text, parsePos, part, count, cal)) {
0664:                                // If the parse fails anywhere in the run, back up to the
0665:                                // start of the run and retry.
0666:                                i = abutPat - 1;
0667:                                parsePos[0] = abutStart;
0668:                                continue;
0669:                            }
0670:                        } else {
0671:                            // Handle non-numeric fields and non-abutting numeric fields.
0672:                            abutPat = -1;
0673:                            if (!subParse(text, parsePos, part, 0, cal)) {
0674:                                return 0;
0675:                            }
0676:                        }
0677:                    } else {
0678:                        // Handle literal pattern characters. These are any
0679:                        // quoted characters and non-alphabetic unquoted characters.
0680:                        abutPat = -1;
0681:                        // A run of white space in the pattern matches a run
0682:                        // of white space in the input text.
0683:                        if (part.text.charAt(0) == ' ') {
0684:                            // Advance over run in input text.
0685:                            int s = parsePos[0];
0686:                            skipSpace(text, parsePos);
0687:
0688:                            // Must see at least one white space char in input.
0689:                            if (parsePos[0] > s) {
0690:                                continue;
0691:                            }
0692:                        } else if (text.startsWith(part.text, parsePos[0])) {
0693:                            parsePos[0] += part.text.length();
0694:                            continue;
0695:                        }
0696:
0697:                        // We fall through to this point if the match fails.
0698:                        return 0;
0699:                    }
0700:                }
0701:
0702:                if (!cal.calcDate(date)) {
0703:                    return 0;
0704:                }
0705:
0706:                // Return progress.
0707:                return parsePos[0] - start;
0708:            }
0709:
0710:            /**
0711:             * Method append current content in buf as pattern part if there is any, and
0712:             * clear buf for next part.
0713:             * 
0714:             * @param buf pattern part text specification
0715:             * @param count pattern part repeat count
0716:             */
0717:            private void addPart(StringBuffer buf, int count) {
0718:                if (buf.length() > 0) {
0719:                    patternParts.add((new PatternPart(buf.toString(), count)));
0720:                    buf.setLength(0);
0721:                }
0722:            }
0723:
0724:            /**
0725:             * Generate GMT timezone string for given date.
0726:             * 
0727:             * @param buf where timezone string will be appended to
0728:             * @param date whose value being evaluated
0729:             */
0730:            private void appendGMT(StringBuffer buf, Date date) {
0731:                int value = -date.getTimezoneOffset();
0732:
0733:                if (value < 0) {
0734:                    buf.append("GMT-");
0735:                    value = -value; // suppress the '-' sign for text display.
0736:                } else {
0737:                    buf.append("GMT+");
0738:                }
0739:
0740:                zeroPaddingNumber(buf, value / MINUTES_PER_HOUR, 2);
0741:                buf.append(':');
0742:                zeroPaddingNumber(buf, value % MINUTES_PER_HOUR, 2);
0743:            }
0744:
0745:            /**
0746:             * Formats (0..11) Hours field according to pattern specified.
0747:             * 
0748:             * @param buf where formatted string will be appended to
0749:             * @param count number of time pattern char repeats; this controls how a field
0750:             *          should be formatted
0751:             * @param date hold the date object to be formatted
0752:             */
0753:            private void format0To11Hours(StringBuffer buf, int count, Date date) {
0754:                int value = date.getHours() % 12;
0755:                zeroPaddingNumber(buf, value, count);
0756:            }
0757:
0758:            /**
0759:             * Formats (0..23) Hours field according to pattern specified.
0760:             * 
0761:             * @param buf where formatted string will be appended to
0762:             * @param count number of time pattern char repeats; this controls how a field
0763:             *          should be formatted
0764:             * @param date hold the date object to be formatted
0765:             */
0766:            private void format0To23Hours(StringBuffer buf, int count, Date date) {
0767:                int value = date.getHours();
0768:                zeroPaddingNumber(buf, value, count);
0769:            }
0770:
0771:            /**
0772:             * Formats (1..12) Hours field according to pattern specified.
0773:             * 
0774:             * @param buf where formatted string will be appended to
0775:             * @param count number of time pattern char repeats; this controls how a field
0776:             *          should be formatted
0777:             * @param date hold the date object to be formatted
0778:             */
0779:            private void format1To12Hours(StringBuffer buf, int count, Date date) {
0780:                int value = date.getHours() % 12;
0781:                if (value == 0) {
0782:                    zeroPaddingNumber(buf, 12, count);
0783:                } else {
0784:                    zeroPaddingNumber(buf, value, count);
0785:                }
0786:            }
0787:
0788:            /**
0789:             * Formats (1..24) Hours field according to pattern specified.
0790:             * 
0791:             * @param buf where formatted string will be appended to
0792:             * @param count number of time pattern char repeats; this controls how a field
0793:             *          should be formatted
0794:             * @param date hold the date object to be formatted
0795:             */
0796:            private void format24Hours(StringBuffer buf, int count, Date date) {
0797:                int value = date.getHours();
0798:                if (value == 0) {
0799:                    zeroPaddingNumber(buf, 24, count);
0800:                } else {
0801:                    zeroPaddingNumber(buf, value, count);
0802:                }
0803:            }
0804:
0805:            /**
0806:             * Formats AM/PM field according to pattern specified.
0807:             * 
0808:             * @param buf where formatted string will be appended to
0809:             * @param count number of time pattern char repeats; this controls how a field
0810:             *          should be formatted
0811:             * @param date hold the date object to be formatted
0812:             */
0813:            private void formatAmPm(StringBuffer buf, int count, Date date) {
0814:                if (date.getHours() >= 12 && date.getHours() < 24) {
0815:                    buf.append(dateTimeConstants.ampms()[1]);
0816:                } else {
0817:                    buf.append(dateTimeConstants.ampms()[0]);
0818:                }
0819:            }
0820:
0821:            /**
0822:             * Formats Date field according to pattern specified.
0823:             * 
0824:             * @param buf where formatted string will be appended to
0825:             * @param count number of time pattern char repeats; this controls how a field
0826:             *          should be formatted
0827:             * @param date hold the date object to be formatted
0828:             */
0829:            private void formatDate(StringBuffer buf, int count, Date date) {
0830:                int value = date.getDate();
0831:                zeroPaddingNumber(buf, value, count);
0832:            }
0833:
0834:            /**
0835:             * Formats Day of week field according to pattern specified.
0836:             * 
0837:             * @param buf where formatted string will be appended to
0838:             * @param count number of time pattern char repeats; this controls how a field
0839:             *          should be formatted
0840:             * @param date hold the date object to be formatted
0841:             */
0842:            private void formatDayOfWeek(StringBuffer buf, int count, Date date) {
0843:                int value = date.getDay();
0844:                if (count >= 4) {
0845:                    buf.append(dateTimeConstants.weekdays()[value]);
0846:                } else {
0847:                    buf.append(dateTimeConstants.shortWeekdays()[value]);
0848:                }
0849:            }
0850:
0851:            /**
0852:             * Formats Era field according to pattern specified.
0853:             * 
0854:             * @param buf where formatted string will be appended to
0855:             * @param count number of time pattern char repeats; this controls how a field
0856:             *          should be formatted
0857:             * @param date hold the date object to be formatted
0858:             */
0859:            private void formatEra(StringBuffer buf, int count, Date date) {
0860:                int value = date.getYear() >= -JS_START_YEAR ? 1 : 0;
0861:                if (count >= 4) {
0862:                    buf.append(dateTimeConstants.eraNames()[value]);
0863:                } else {
0864:                    buf.append(dateTimeConstants.eras()[value]);
0865:                }
0866:            }
0867:
0868:            /**
0869:             * Formats Fractional seconds field according to pattern specified.
0870:             * 
0871:             * @param buf where formatted string will be appended to
0872:             * @param count number of time pattern char repeats; this controls how a field
0873:             *          should be formatted
0874:             * @param date hold the date object to be formatted
0875:             */
0876:            private void formatFractionalSeconds(StringBuffer buf, int count,
0877:                    Date date) {
0878:                // Fractional seconds should be left-justified, ie. zero must be padded
0879:                // from left. For example, if value in milliseconds is 5, and count is 3,
0880:                // the output need to be "005".
0881:                int value = (int) (date.getTime() % 1000);
0882:                if (count == 1) {
0883:                    value = (value + 50) / 100; // Round to 100ms.
0884:                    buf.append(Integer.toString(value));
0885:                } else if (count == 2) {
0886:                    value = (value + 5) / 10; // Round to 10ms.
0887:                    zeroPaddingNumber(buf, value, 2);
0888:                } else {
0889:                    zeroPaddingNumber(buf, value, 3);
0890:
0891:                    if (count > 3) {
0892:                        zeroPaddingNumber(buf, 0, count - 3);
0893:                    }
0894:                }
0895:            }
0896:
0897:            /**
0898:             * Formats Minutes field according to pattern specified.
0899:             * 
0900:             * @param buf where formatted string will be appended to
0901:             * @param count number of time pattern char repeats; this controls how a field
0902:             *          should be formatted
0903:             * @param date hold the date object to be formatted
0904:             */
0905:            private void formatMinutes(StringBuffer buf, int count, Date date) {
0906:                int value = date.getMinutes();
0907:                zeroPaddingNumber(buf, value, count);
0908:            }
0909:
0910:            /**
0911:             * Formats Month field according to pattern specified.
0912:             * 
0913:             * @param buf where formatted string will be appended to
0914:             * @param count number of time pattern char repeats; this controls how a field
0915:             *          should be formatted
0916:             * @param date hold the date object to be formatted
0917:             */
0918:            private void formatMonth(StringBuffer buf, int count, Date date) {
0919:                int value = date.getMonth();
0920:                switch (count) {
0921:                case 5:
0922:                    buf.append(dateTimeConstants.narrowMonths()[value]);
0923:                    break;
0924:                case 4:
0925:                    buf.append(dateTimeConstants.standaloneMonths()[value]);
0926:                    break;
0927:                case 3:
0928:                    buf.append(dateTimeConstants.shortMonths()[value]);
0929:                    break;
0930:                default:
0931:                    zeroPaddingNumber(buf, value + 1, count);
0932:                }
0933:            }
0934:
0935:            /**
0936:             * Formats Quarter field according to pattern specified.
0937:             * 
0938:             * @param buf where formatted string will be appended to
0939:             * @param count number of time pattern char repeats; this controls how a field
0940:             *          should be formatted
0941:             * @param date hold the date object to be formatted
0942:             */
0943:            private void formatQuarter(StringBuffer buf, int count, Date date) {
0944:                int value = date.getMonth() / 3;
0945:                if (count < 4) {
0946:                    buf.append(dateTimeConstants.shortQuarters()[value]);
0947:                } else {
0948:                    buf.append(dateTimeConstants.quarters()[value]);
0949:                }
0950:            }
0951:
0952:            /**
0953:             * Formats Seconds field according to pattern specified.
0954:             * 
0955:             * @param buf where formatted string will be appended to
0956:             * @param count number of time pattern char repeats; this controls how a field
0957:             *          should be formatted
0958:             * @param date hold the date object to be formatted
0959:             */
0960:            private void formatSeconds(StringBuffer buf, int count, Date date) {
0961:                int value = date.getSeconds();
0962:                zeroPaddingNumber(buf, value, count);
0963:            }
0964:
0965:            /**
0966:             * Formats Standalone weekday field according to pattern specified.
0967:             * 
0968:             * @param buf where formatted string will be appended to
0969:             * @param count number of time pattern char repeats; this controls how a field
0970:             *          should be formatted
0971:             * @param date hold the date object to be formatted
0972:             */
0973:            private void formatStandaloneDay(StringBuffer buf, int count,
0974:                    Date date) {
0975:                int value = date.getDay();
0976:                if (count == 5) {
0977:                    buf
0978:                            .append(dateTimeConstants
0979:                                    .standaloneNarrowWeekdays()[value]);
0980:                } else if (count == 4) {
0981:                    buf.append(dateTimeConstants.standaloneWeekdays()[value]);
0982:                } else if (count == 3) {
0983:                    buf
0984:                            .append(dateTimeConstants.standaloneShortWeekdays()[value]);
0985:                } else {
0986:                    zeroPaddingNumber(buf, value, 1);
0987:                }
0988:            }
0989:
0990:            /**
0991:             * Formats Standalone Month field according to pattern specified.
0992:             * 
0993:             * @param buf where formatted string will be appended to
0994:             * @param count number of time pattern char repeats; this controls how a field
0995:             *          should be formatted
0996:             * @param date hold the date object to be formatted
0997:             */
0998:            private void formatStandaloneMonth(StringBuffer buf, int count,
0999:                    Date date) {
1000:                int value = date.getMonth();
1001:                if (count == 5) {
1002:                    buf
1003:                            .append(dateTimeConstants.standaloneNarrowMonths()[value]);
1004:                } else if (count == 4) {
1005:                    buf.append(dateTimeConstants.standaloneMonths()[value]);
1006:                } else if (count == 3) {
1007:                    buf
1008:                            .append(dateTimeConstants.standaloneShortMonths()[value]);
1009:                } else {
1010:                    zeroPaddingNumber(buf, value + 1, count);
1011:                }
1012:            }
1013:
1014:            /**
1015:             * Formats Timezone field following RFC.
1016:             * 
1017:             * @param buf where formatted string will be appended to
1018:             * @param count number of time pattern char repeats; this controls how a field
1019:             *          should be formatted
1020:             * @param date hold the date object to be formatted
1021:             */
1022:            private void formatTimeZoneRFC(StringBuffer buf, int count,
1023:                    Date date) {
1024:                if (count < 4) {
1025:                    // 'short' (standard Java) form, must use ASCII digits
1026:                    int val = date.getTimezoneOffset();
1027:                    char sign = '-';
1028:                    if (val < 0) {
1029:                        val = -val;
1030:                        sign = '+';
1031:                    }
1032:
1033:                    val = (val / 3) * 5 + (val % MINUTES_PER_HOUR); // minutes => KKmm
1034:                    buf.append(sign);
1035:                    zeroPaddingNumber(buf, val, 4);
1036:                } else {
1037:                    appendGMT(buf, date);
1038:                }
1039:            }
1040:
1041:            /**
1042:             * Formats Year field according to pattern specified. Javascript Date object
1043:             * seems incapable handling 1BC and year before. It can show you year 0 which
1044:             * does not exists. following we just keep consistent with javascript's
1045:             * toString method. But keep in mind those things should be unsupported.
1046:             * 
1047:             * @param buf where formatted string will be appended to
1048:             * @param count number of time pattern char repeats; this controls how a field
1049:             *          should be formatted
1050:             * @param date hold the date object to be formatted
1051:             */
1052:            private void formatYear(StringBuffer buf, int count, Date date) {
1053:                int value = date.getYear() + JS_START_YEAR;
1054:                if (value < 0) {
1055:                    value = -value;
1056:                }
1057:                if (count == 2) {
1058:                    zeroPaddingNumber(buf, value % 100, 2);
1059:                } else {
1060:                    // count != 2
1061:                    buf.append(Integer.toString(value));
1062:                }
1063:            }
1064:
1065:            /**
1066:             * Method getNextCharCountInPattern calculate character repeat count in
1067:             * pattern.
1068:             * 
1069:             * @param pattern describe the format of date string that need to be parsed
1070:             * @param start the position of pattern character
1071:             * @return repeat count
1072:             */
1073:            private int getNextCharCountInPattern(String pattern, int start) {
1074:                char ch = pattern.charAt(start);
1075:                int next = start + 1;
1076:                while (next < pattern.length() && pattern.charAt(next) == ch) {
1077:                    ++next;
1078:                }
1079:                return next - start;
1080:            }
1081:
1082:            /**
1083:             * Method identifies the start of a run of abutting numeric fields. Take the
1084:             * pattern "HHmmss" as an example. We will try to parse 2/2/2 characters of
1085:             * the input text, then if that fails, 1/2/2. We only adjust the width of the
1086:             * leftmost field; the others remain fixed. This allows "123456" => 12:34:56,
1087:             * but "12345" => 1:23:45. Likewise, for the pattern "yyyyMMdd" we try 4/2/2,
1088:             * 3/2/2, 2/2/2, and finally 1/2/2. The first field of connected numeric
1089:             * fields will be marked as abutStart, its width can be reduced to accomodate
1090:             * others.
1091:             */
1092:            private void identifyAbutStart() {
1093:                // 'abut' parts are continuous numeric parts. abutStart is the switch
1094:                // point from non-abut to abut.
1095:                boolean abut = false;
1096:
1097:                int len = patternParts.size();
1098:                for (int i = 0; i < len; i++) {
1099:                    if (isNumeric(patternParts.get(i))) {
1100:                        // If next part is not following abut sequence, and isNumeric.
1101:                        if (!abut && i + 1 < len
1102:                                && isNumeric(patternParts.get(i + 1))) {
1103:                            abut = true;
1104:                            patternParts.get(i).abutStart = true;
1105:                        }
1106:                    } else {
1107:                        abut = false;
1108:                    }
1109:                }
1110:            }
1111:
1112:            /**
1113:             * Method checks if the pattern part is a numeric field.
1114:             * 
1115:             * @param part pattern part to be examined
1116:             * @return <code>true</code> if the pattern part is numberic field
1117:             */
1118:            private final boolean isNumeric(PatternPart part) {
1119:                if (part.count <= 0) {
1120:                    return false;
1121:                }
1122:                int i = NUMERIC_FORMAT_CHARS.indexOf(part.text.charAt(0));
1123:                return (i > 0 || (i == 0 && part.count < 3));
1124:            }
1125:
1126:            /**
1127:             * Method attempts to match the text at a given position against an array of
1128:             * strings. Since multiple strings in the array may match (for example, if the
1129:             * array contains "a", "ab", and "abc", all will match the input string
1130:             * "abcd") the longest match is returned.
1131:             * 
1132:             * @param text the time text being parsed
1133:             * @param start where to start parsing
1134:             * @param data the string array to parsed
1135:             * @param pos to receive where the match stopped
1136:             * @return the new start position if matching succeeded; a negative number
1137:             *         indicating matching failure
1138:             */
1139:            private int matchString(String text, int start, String[] data,
1140:                    int[] pos) {
1141:                int count = data.length;
1142:
1143:                // There may be multiple strings in the data[] array which begin with
1144:                // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1145:                // We keep track of the longest match, and return that. Note that this
1146:                // unfortunately requires us to test all array elements.
1147:                int bestMatchLength = 0, bestMatch = -1;
1148:                String textInLowerCase = text.substring(start).toLowerCase();
1149:                for (int i = 0; i < count; ++i) {
1150:                    int length = data[i].length();
1151:                    // Always compare if we have no match yet; otherwise only compare
1152:                    // against potentially better matches (longer strings).
1153:                    if (length > bestMatchLength
1154:                            && textInLowerCase
1155:                                    .startsWith(data[i].toLowerCase())) {
1156:                        bestMatch = i;
1157:                        bestMatchLength = length;
1158:                    }
1159:                }
1160:                if (bestMatch >= 0) {
1161:                    pos[0] = start + bestMatchLength;
1162:                }
1163:                return bestMatch;
1164:            }
1165:
1166:            /**
1167:             * Method parses a integer string and return integer value.
1168:             * 
1169:             * @param text string being parsed
1170:             * @param pos parse position
1171:             * 
1172:             * @return integer value
1173:             */
1174:            private int parseInt(String text, int[] pos) {
1175:                int ret = 0;
1176:                int ind = pos[0];
1177:                char ch = text.charAt(ind);
1178:                while (ch >= '0' && ch <= '9') {
1179:                    ret = ret * 10 + (ch - '0');
1180:                    ind++;
1181:                    if (ind >= text.length()) {
1182:                        break;
1183:                    }
1184:                    ch = text.charAt(ind);
1185:                }
1186:                if (ind > pos[0]) {
1187:                    pos[0] = ind;
1188:                } else {
1189:                    ret = -1;
1190:                }
1191:                return ret;
1192:            }
1193:
1194:            /**
1195:             * Method parses the input pattern string a generate a vector of pattern
1196:             * parts.
1197:             * 
1198:             * @param pattern describe the format of date string that need to be parsed
1199:             */
1200:            private void parsePattern(String pattern) {
1201:                StringBuffer buf = new StringBuffer(32);
1202:                boolean inQuote = false;
1203:
1204:                for (int i = 0; i < pattern.length(); i++) {
1205:                    char ch = pattern.charAt(i);
1206:
1207:                    // Handle space, add literal part (if exist), and add space part.
1208:                    if (ch == ' ') {
1209:                        addPart(buf, 0);
1210:                        buf.append(' ');
1211:                        addPart(buf, 0);
1212:                        while (i + 1 < pattern.length()
1213:                                && pattern.charAt(i + 1) == ' ') {
1214:                            i++;
1215:                        }
1216:                        continue;
1217:                    }
1218:
1219:                    // If inside quote, except two quote connected, just copy or exit.
1220:                    if (inQuote) {
1221:                        if (ch == '\'') {
1222:                            if (i + 1 < pattern.length()
1223:                                    && pattern.charAt(i + 1) == '\'') {
1224:                                // Quote appeared twice continuously, interpret as one quote.
1225:                                buf.append(ch);
1226:                                ++i;
1227:                            } else {
1228:                                inQuote = false;
1229:                            }
1230:                        } else {
1231:                            // Literal.
1232:                            buf.append(ch);
1233:                        }
1234:                        continue;
1235:                    }
1236:
1237:                    // Outside quote now.
1238:                    if (PATTERN_CHARS.indexOf(ch) > 0) {
1239:                        addPart(buf, 0);
1240:                        buf.append(ch);
1241:                        int count = getNextCharCountInPattern(pattern, i);
1242:                        addPart(buf, count);
1243:                        i += count - 1;
1244:                        continue;
1245:                    }
1246:
1247:                    // Two consecutive quotes is a quote literal, inside or outside of quotes.
1248:                    if (ch == '\'') {
1249:                        if (i + 1 < pattern.length()
1250:                                && pattern.charAt(i + 1) == '\'') {
1251:                            buf.append('\'');
1252:                            i++;
1253:                        } else {
1254:                            inQuote = true;
1255:                        }
1256:                    } else {
1257:                        buf.append(ch);
1258:                    }
1259:                }
1260:
1261:                addPart(buf, 0);
1262:
1263:                identifyAbutStart();
1264:            }
1265:
1266:            /**
1267:             * Method parses time zone offset.
1268:             * 
1269:             * @param text the time text to be parsed
1270:             * @param pos Parse position
1271:             * @param cal DateRecord object that holds parsed value
1272:             * 
1273:             * @return <code>true</code> if parsing successful, otherwise
1274:             *         <code>false</code>
1275:             */
1276:            private boolean parseTimeZoneOffset(String text, int[] pos,
1277:                    DateRecord cal) {
1278:                if (pos[0] >= text.length()) {
1279:                    cal.setTzOffset(0);
1280:                    return true;
1281:                }
1282:
1283:                int sign;
1284:                switch (text.charAt(pos[0])) {
1285:                case '+':
1286:                    sign = 1;
1287:                    break;
1288:                case '-':
1289:                    sign = -1;
1290:                    break;
1291:                default:
1292:                    cal.setTzOffset(0);
1293:                    return true;
1294:                }
1295:                ++(pos[0]);
1296:
1297:                // Look for hours:minutes or hhmm.
1298:                int st = pos[0];
1299:                int value = parseInt(text, pos);
1300:                if (value == 0 && pos[0] == st) {
1301:                    return false;
1302:                }
1303:
1304:                int offset;
1305:                if (pos[0] < text.length() && text.charAt(pos[0]) == ':') {
1306:                    // This is the hours:minutes case.
1307:                    offset = value * MINUTES_PER_HOUR;
1308:                    ++(pos[0]);
1309:                    st = pos[0];
1310:                    value = parseInt(text, pos);
1311:                    if (value == 0 && pos[0] == st) {
1312:                        return false;
1313:                    }
1314:                    offset += value;
1315:                } else {
1316:                    // This is the hhmm case.
1317:                    offset = value;
1318:                    // Assume "-23".."+23" refers to hours.
1319:                    if (offset < 24 && (pos[0] - st) <= 2) {
1320:                        offset *= MINUTES_PER_HOUR;
1321:                    } else {
1322:                        offset = offset % 100 + offset / 100 * MINUTES_PER_HOUR;
1323:                    }
1324:                }
1325:
1326:                offset *= sign;
1327:                cal.setTzOffset(-offset);
1328:                return true;
1329:            }
1330:
1331:            /**
1332:             * Method skips space in the string as pointed by pos.
1333:             * 
1334:             * @param text input string
1335:             * @param pos where skip start, and return back where skip stop
1336:             */
1337:            private void skipSpace(String text, int[] pos) {
1338:                while (pos[0] < text.length()
1339:                        && WHITE_SPACE.indexOf(text.charAt(pos[0])) >= 0) {
1340:                    ++(pos[0]);
1341:                }
1342:            }
1343:
1344:            /**
1345:             * Formats a single field according to pattern specified.
1346:             * 
1347:             * @param buf where formatted string will be appended to
1348:             * @param ch pattern for this field
1349:             * @param count number of time pattern char repeats; this controls how a field
1350:             *          should be formatted
1351:             * @param date hold the date object to be formatted
1352:             * 
1353:             * @return <code>true</code> if pattern valid, otherwise <code>false</code>
1354:             */
1355:            private boolean subFormat(StringBuffer buf, char ch, int count,
1356:                    Date date) {
1357:                switch (ch) {
1358:                case 'G':
1359:                    formatEra(buf, count, date);
1360:                    break;
1361:                case 'y':
1362:                    formatYear(buf, count, date);
1363:                    break;
1364:                case 'M':
1365:                    formatMonth(buf, count, date);
1366:                    break;
1367:                case 'k':
1368:                    format24Hours(buf, count, date);
1369:                    break;
1370:                case 'S':
1371:                    formatFractionalSeconds(buf, count, date);
1372:                    break;
1373:                case 'E':
1374:                    formatDayOfWeek(buf, count, date);
1375:                    break;
1376:                case 'a':
1377:                    formatAmPm(buf, count, date);
1378:                    break;
1379:                case 'h':
1380:                    format1To12Hours(buf, count, date);
1381:                    break;
1382:                case 'K':
1383:                    format0To11Hours(buf, count, date);
1384:                    break;
1385:                case 'H':
1386:                    format0To23Hours(buf, count, date);
1387:                    break;
1388:                case 'c':
1389:                    formatStandaloneDay(buf, count, date);
1390:                    break;
1391:                case 'L':
1392:                    formatStandaloneMonth(buf, count, date);
1393:                    break;
1394:                case 'Q':
1395:                    formatQuarter(buf, count, date);
1396:                    break;
1397:                case 'd':
1398:                    formatDate(buf, count, date);
1399:                    break;
1400:                case 'm':
1401:                    formatMinutes(buf, count, date);
1402:                    break;
1403:                case 's':
1404:                    formatSeconds(buf, count, date);
1405:                    break;
1406:                case 'z':
1407:                case 'v':
1408:                    appendGMT(buf, date);
1409:                    break;
1410:                case 'Z':
1411:                    formatTimeZoneRFC(buf, count, date);
1412:                    break;
1413:                default:
1414:                    return false;
1415:                }
1416:                return true;
1417:            }
1418:
1419:            /**
1420:             * Converts one field of the input string into a numeric field value. Returns
1421:             * <code>false</code> if failed.
1422:             * 
1423:             * @param text the time text to be parsed
1424:             * @param pos Parse position
1425:             * @param part the pattern part for this field
1426:             * @param digitCount when greater than 0, numeric parsing must obey the count
1427:             * @param cal DateRecord object that will hold parsed value
1428:             * 
1429:             * @return <code>true</code> if parsing successful
1430:             */
1431:            @SuppressWarnings("fallthrough")
1432:            private boolean subParse(String text, int[] pos, PatternPart part,
1433:                    int digitCount, DateRecord cal) {
1434:
1435:                skipSpace(text, pos);
1436:
1437:                int start = pos[0];
1438:                char ch = part.text.charAt(0);
1439:
1440:                // Parse integer value if it is a numeric field.
1441:                int value = -1; // initialize value to be -1,
1442:                if (isNumeric(part)) {
1443:                    if (digitCount > 0) {
1444:                        if ((start + digitCount) > text.length()) {
1445:                            return false;
1446:                        }
1447:                        value = parseInt(text.substring(0, start + digitCount),
1448:                                pos);
1449:                    } else {
1450:                        value = parseInt(text, pos);
1451:                    }
1452:                }
1453:
1454:                switch (ch) {
1455:                case 'G': // 'G' - ERA
1456:                    value = matchString(text, start, dateTimeConstants.eras(),
1457:                            pos);
1458:                    cal.setEra(value);
1459:                    return true;
1460:                case 'M': // 'M' - MONTH
1461:                    return subParseMonth(text, pos, cal, value, start);
1462:                case 'E':
1463:                    return subParseDayOfWeek(text, pos, start, cal);
1464:                case 'a': // 'a' - AM_PM
1465:                    value = matchString(text, start, dateTimeConstants.ampms(),
1466:                            pos);
1467:                    cal.setAmpm(value);
1468:                    return true;
1469:                case 'y': // 'y' - YEAR
1470:                    return subParseYear(text, pos, start, value, part, cal);
1471:                case 'd': // 'd' - DATE
1472:                    cal.setDayOfMonth(value);
1473:                    return true;
1474:                case 'S': // 'S' - FRACTIONAL_SECOND
1475:                    return subParseFractionalSeconds(value, start, pos[0], cal);
1476:                case 'h': // 'h' - HOUR (1..12)
1477:                    if (value == 12) {
1478:                        value = 0;
1479:                    }
1480:                    // fall through
1481:                case 'K': // 'K' - HOUR (0..11)
1482:                case 'H': // 'H' - HOUR_OF_DAY (0..23)
1483:                    cal.setHours(value);
1484:                    return true;
1485:                case 'k': // 'k' - HOUR_OF_DAY (1..24)
1486:                    cal.setHours(value);
1487:                    return true;
1488:                case 'm': // 'm' - MINUTE
1489:                    cal.setMinutes(value);
1490:                    return true;
1491:                case 's': // 's' - SECOND
1492:                    cal.setSeconds(value);
1493:                    return true;
1494:
1495:                case 'z': // 'z' - ZONE_OFFSET
1496:                case 'Z': // 'Z' - TIMEZONE_RFC
1497:                case 'v': // 'v' - TIMEZONE_GENERIC
1498:                    return subParseTimeZoneInGMT(text, start, pos, cal);
1499:                default:
1500:                    return false;
1501:                }
1502:            }
1503:
1504:            /**
1505:             * Method subParseDayOfWeek parses day of the week field.
1506:             * 
1507:             * @param text the time text to be parsed
1508:             * @param pos Parse position
1509:             * @param start from where parse start
1510:             * @param cal DateRecord object that holds parsed value
1511:             * 
1512:             * @return <code>true</code> if parsing successful, otherwise
1513:             *         <code>false</code>
1514:             */
1515:            private boolean subParseDayOfWeek(String text, int[] pos,
1516:                    int start, DateRecord cal) {
1517:                int value;
1518:                // 'E' - DAY_OF_WEEK
1519:                // Want to be able to parse both short and long forms.
1520:                // Try count == 4 (DDDD) first:
1521:                value = matchString(text, start, dateTimeConstants.weekdays(),
1522:                        pos);
1523:                if (value < 0) {
1524:                    value = matchString(text, start, dateTimeConstants
1525:                            .shortWeekdays(), pos);
1526:                }
1527:                if (value < 0) {
1528:                    return false;
1529:                }
1530:                cal.setDayOfWeek(value);
1531:                return true;
1532:            }
1533:
1534:            /**
1535:             * Method subParseFractionalSeconds parses fractional seconds field.
1536:             * 
1537:             * @param value parsed numberic value
1538:             * @param start
1539:             * @param end parse position
1540:             * @param cal DateRecord object that holds parsed value
1541:             * @return <code>true</code> if parsing successful, otherwise
1542:             *         <code>false</code>
1543:             */
1544:            private boolean subParseFractionalSeconds(int value, int start,
1545:                    int end, DateRecord cal) {
1546:                // Fractional seconds left-justify.
1547:                int i = end - start;
1548:                if (i < 3) {
1549:                    while (i < 3) {
1550:                        value *= 10;
1551:                        i++;
1552:                    }
1553:                } else {
1554:                    int a = 1;
1555:                    while (i > 3) {
1556:                        a *= 10;
1557:                        i--;
1558:                    }
1559:                    value = (value + (a >> 1)) / a;
1560:                }
1561:                cal.setMilliseconds(value);
1562:                return true;
1563:            }
1564:
1565:            /**
1566:             * Method subParseMonth parses Month field.
1567:             * 
1568:             * @param text the time text to be parsed
1569:             * @param pos Parse position
1570:             * @param cal DateRecord object that will hold parsed value
1571:             * @param value numeric value if this field is expressed using numberic
1572:             *          pattern
1573:             * @param start from where parse start
1574:             * 
1575:             * @return <code>true</code> if parsing successful
1576:             */
1577:            private boolean subParseMonth(String text, int[] pos,
1578:                    DateRecord cal, int value, int start) {
1579:                // When month is symbols, i.e., MMM or MMMM, value will be -1.
1580:                if (value < 0) {
1581:                    // Want to be able to parse both short and long forms.
1582:                    // Try count == 4 first:
1583:                    value = matchString(text, start,
1584:                            dateTimeConstants.months(), pos);
1585:                    if (value < 0) { // count == 4 failed, now try count == 3.
1586:                        value = matchString(text, start, dateTimeConstants
1587:                                .shortMonths(), pos);
1588:                    }
1589:                    if (value < 0) {
1590:                        return false;
1591:                    }
1592:                    cal.setMonth(value);
1593:                    return true;
1594:                } else {
1595:                    cal.setMonth(value - 1);
1596:                    return true;
1597:                }
1598:            }
1599:
1600:            /**
1601:             * Method parses GMT type timezone.
1602:             * 
1603:             * @param text the time text to be parsed
1604:             * @param start from where parse start
1605:             * @param pos Parse position
1606:             * @param cal DateRecord object that holds parsed value
1607:             * 
1608:             * @return <code>true</code> if parsing successful, otherwise
1609:             *         <code>false</code>
1610:             */
1611:            private boolean subParseTimeZoneInGMT(String text, int start,
1612:                    int[] pos, DateRecord cal) {
1613:                // First try to parse generic forms such as GMT-07:00. Do this first
1614:                // in case localized DateFormatZoneData contains the string "GMT"
1615:                // for a zone; in that case, we don't want to match the first three
1616:                // characters of GMT+/-HH:MM etc.
1617:
1618:                // For time zones that have no known names, look for strings
1619:                // of the form:
1620:                // GMT[+-]hours:minutes or
1621:                // GMT[+-]hhmm or
1622:                // GMT.
1623:                if (text.startsWith(GMT, start)) {
1624:                    pos[0] = start + GMT.length();
1625:                    return parseTimeZoneOffset(text, pos, cal);
1626:                }
1627:
1628:                // At this point, check for named time zones by looking through
1629:                // the locale data from the DateFormatZoneData strings.
1630:                // Want to be able to parse both short and long forms.
1631:                /*
1632:                 * i = subParseZoneString(text, start, cal); if (i != 0) return i;
1633:                 */
1634:
1635:                // As a last resort, look for numeric timezones of the form
1636:                // [+-]hhmm as specified by RFC 822. This code is actually
1637:                // a little more permissive than RFC 822. It will try to do
1638:                // its best with numbers that aren't strictly 4 digits long.
1639:                return parseTimeZoneOffset(text, pos, cal);
1640:            }
1641:
1642:            /**
1643:             * Method subParseYear parse year field. Year field is special because 1, two
1644:             * digit year need to be resolved. 2, we allow year to take a sign. 3, year
1645:             * field participate in abut processing. In my testing, negative year does not
1646:             * seem working due to JDK (or redpill implementation) limitation. It is not a
1647:             * big deal so we don't worry about it. But keep the logic here so that we
1648:             * might want to replace DateRecord with our a calendar class.
1649:             * 
1650:             * @param text the time text to be parsed
1651:             * @param pos parse position
1652:             * @param start where this field star
1653:             * @param value integer value of yea
1654:             * @param part the pattern part for this field
1655:             * @param cal DateRecord object that will hold parsed value
1656:             * 
1657:             * @return <code>true</code> if successful
1658:             */
1659:            private boolean subParseYear(String text, int[] pos, int start,
1660:                    int value, PatternPart part, DateRecord cal) {
1661:                char ch = ' ';
1662:                if (value < 0) {
1663:                    ch = text.charAt(pos[0]);
1664:                    // Check if it is a sign.
1665:                    if (ch != '+' && ch != '-') {
1666:                        return false;
1667:                    }
1668:                    ++(pos[0]);
1669:                    value = parseInt(text, pos);
1670:                    if (value < 0) {
1671:                        return false;
1672:                    }
1673:                    if (ch == '-') {
1674:                        value = -value;
1675:                    }
1676:                }
1677:
1678:                // no sign, only 2 digit was actually parsed, pattern say it has 2 digit.
1679:                if (ch == ' ' && (pos[0] - start) == 2 && part.count == 2) {
1680:                    // Assume for example that the defaultCenturyStart is 6/18/1903.
1681:                    // This means that two-digit years will be forced into the range
1682:                    // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02
1683:                    // correspond to 2000, 2001, and 2002. Years 04, 05, etc. correspond
1684:                    // to 1904, 1905, etc. If the year is 03, then it is 2003 if the
1685:                    // other fields specify a date before 6/18, or 1903 if they specify a
1686:                    // date afterwards. As a result, 03 is an ambiguous year. All other
1687:                    // two-digit years are unambiguous.
1688:                    Date date = new Date();
1689:                    int defaultCenturyStartYear = date.getYear() + 1900 - 80;
1690:                    int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
1691:                    cal.setAmbiguousYear(value == ambiguousTwoDigitYear);
1692:                    value += (defaultCenturyStartYear / 100) * 100
1693:                            + (value < ambiguousTwoDigitYear ? 100 : 0);
1694:                }
1695:                cal.setYear(value);
1696:                return true;
1697:            }
1698:
1699:            /**
1700:             * Formats a number with the specified minimum number of digits, using zero to
1701:             * fill the gap.
1702:             * 
1703:             * @param buf where zero padded string will be written to
1704:             * @param value the number value being formatted
1705:             * @param minWidth minimum width of the formatted string; zero will be padded
1706:             *          to reach this width
1707:             */
1708:            private void zeroPaddingNumber(StringBuffer buf, int value,
1709:                    int minWidth) {
1710:                int b = NUMBER_BASE;
1711:                for (int i = 0; i < minWidth - 1; i++) {
1712:                    if (value < b) {
1713:                        buf.append('0');
1714:                    }
1715:                    b *= NUMBER_BASE;
1716:                }
1717:                buf.append(Integer.toString(value));
1718:            }
1719:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.