Source for gnu.java.net.protocol.http.HTTPDateFormat

   1: /* HTTPDateFormat.java --
   2:    Copyright (C) 2004 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.net.protocol.http;
  40: 
  41: import java.text.DateFormat;
  42: import java.text.DecimalFormat;
  43: import java.text.FieldPosition;
  44: import java.text.NumberFormat;
  45: import java.text.ParsePosition;
  46: import java.util.Calendar;
  47: import java.util.Date;
  48: import java.util.GregorianCalendar;
  49: import java.util.TimeZone;
  50: 
  51: /**
  52:  * HTTP date formatter and parser.
  53:  * Formats dates according to RFC 822 (updated by RFC 1123).
  54:  * Parses dates according to the above, <i>or</i> RFC 1036, <i>or</i> the
  55:  * ANSI C <code>asctime()</code> format.
  56:  *
  57:  * @author Chris Burdess (dog@gnu.org)
  58:  */
  59: public class HTTPDateFormat
  60:   extends DateFormat
  61: {
  62: 
  63:   static final String[] DAYS_OF_WEEK = {
  64:     null, "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  65:   };
  66: 
  67:   static final String[] MONTHS = {
  68:     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  69:     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  70:   };
  71: 
  72:   public HTTPDateFormat()
  73:   {
  74:     calendar = new GregorianCalendar(TimeZone.getTimeZone ("GMT"));
  75:     numberFormat = new DecimalFormat();
  76:   }
  77: 
  78:   /**
  79:    * Appends the textual value for the specified field to the given string
  80:    * buffer. This method should be avoided, use <code>format(Date)</code>
  81:    * instead.
  82:    * @param date the Date object
  83:    * @param buf the buffer to append to
  84:    * @param field the current field position
  85:    * @return the modified buffer
  86:    */
  87:   public StringBuffer format(Date date, StringBuffer buf,
  88:                              FieldPosition field)
  89:   {
  90:     calendar.clear();
  91:     calendar.setTime(date);
  92:     buf.setLength(0);
  93: 
  94:     // Day of week
  95:     buf.append(DAYS_OF_WEEK[calendar.get(Calendar.DAY_OF_WEEK)]);
  96:     buf.append(',');
  97:     buf.append(' ');
  98: 
  99:     // Day of month
 100:     int day = calendar.get(Calendar.DAY_OF_MONTH);
 101:     buf.append(Character.forDigit(day / 10, 10));
 102:     buf.append(Character.forDigit(day % 10, 10));
 103:     buf.append(' ');
 104: 
 105:     // Month
 106:     buf.append(MONTHS[calendar.get(Calendar.MONTH)]);
 107:     buf.append(' ');
 108: 
 109:     // Year
 110:     int year = calendar.get(Calendar.YEAR);
 111:     if (year < 1000)
 112:       {
 113:         buf.append('0');
 114:         if (year < 100)
 115:           {
 116:             buf.append('0');
 117:             if (year < 10)
 118:               {
 119:                 buf.append('0');
 120:               }
 121:           }
 122:       }
 123:     buf.append(Integer.toString(year));
 124:     buf.append(' ');
 125: 
 126:     // Hour
 127:     int hour = calendar.get(Calendar.HOUR_OF_DAY);
 128:     buf.append(Character.forDigit(hour / 10, 10));
 129:     buf.append(Character.forDigit(hour % 10, 10));
 130:     buf.append(':');
 131: 
 132:     // Minute
 133:     int minute = calendar.get(Calendar.MINUTE);
 134:     buf.append(Character.forDigit(minute / 10, 10));
 135:     buf.append(Character.forDigit(minute % 10, 10));
 136:     buf.append(':');
 137: 
 138:     // Second
 139:     int second = calendar.get(Calendar.SECOND);
 140:     buf.append(Character.forDigit(second / 10, 10));
 141:     buf.append(Character.forDigit(second % 10, 10));
 142:     buf.append(' ');
 143: 
 144:     // Timezone
 145:     // Get time offset in minutes
 146:     int zoneOffset =(calendar.get(Calendar.ZONE_OFFSET) +
 147:                      calendar.get(Calendar.DST_OFFSET)) / 60000;
 148: 
 149:     // Apply + or - appropriately
 150:     if (zoneOffset < 0)
 151:       {
 152:         zoneOffset = -zoneOffset;
 153:         buf.append('-');
 154:       }
 155:     else
 156:       {
 157:         buf.append('+');
 158:       }
 159: 
 160:     // Set the 2 2-char fields as specified above
 161:     int tzhours = zoneOffset / 60;
 162:     buf.append(Character.forDigit(tzhours / 10, 10));
 163:     buf.append(Character.forDigit(tzhours % 10, 10));
 164:     int tzminutes = zoneOffset % 60;
 165:     buf.append(Character.forDigit(tzminutes / 10, 10));
 166:     buf.append(Character.forDigit(tzminutes % 10, 10));
 167: 
 168:     field.setBeginIndex(0);
 169:     field.setEndIndex(buf.length());
 170:     return buf;
 171:   }
 172: 
 173:   /**
 174:    * Parses the given date in the current TimeZone.
 175:    * @param text the formatted date to be parsed
 176:    * @param pos the current parse position
 177:    */
 178:   public Date parse(String text, ParsePosition pos)
 179:   {
 180:     int date, month, year, hour, minute, second;
 181:     String monthText;
 182:     int start = 0, end = -1;
 183:     int len = text.length();
 184:     calendar.clear();
 185:     pos.setIndex(start);
 186:     try
 187:       {
 188:         // Advance to date
 189:         if (Character.isLetter(text.charAt(start)))
 190:           {
 191:             start = skipNonWhitespace(text, start);
 192:           }
 193:         // Determine mode
 194:         switch(start)
 195:           {
 196:           case 3:
 197:             // asctime
 198:             start = skipWhitespace(text, start);
 199:             pos.setIndex(start);
 200:             end = skipNonWhitespace(text, start + 1);
 201:             monthText = text.substring(start, end);
 202:             month = -1;
 203:             for (int i = 0; i < 12; i++)
 204:               {
 205:                 if (MONTHS[i].equals(monthText))
 206:                   {
 207:                     month = i;
 208:                     break;
 209:                   }
 210:               }
 211:             if (month == -1)
 212:               {
 213:                 pos.setErrorIndex(end);
 214:                 return null;
 215:               }
 216:             // Advance to date
 217:             start = skipWhitespace(text, end + 1);
 218:             pos.setIndex(start);
 219:             end = skipNonWhitespace(text, start + 1);
 220:             date = Integer.parseInt(text.substring(start, end));
 221:             // Advance to hour
 222:             start = skipWhitespace(text, end + 1);
 223:             pos.setIndex(start);
 224:             end = skipTo(text, start + 1, ':');
 225:             hour = Integer.parseInt(text.substring(start, end));
 226:             // Advance to minute
 227:             start = end + 1;
 228:             pos.setIndex(start);
 229:             end = skipTo(text, start + 1, ':');
 230:             minute = Integer.parseInt(text.substring(start, end));
 231:             // Advance to second
 232:             start = end + 1;
 233:             pos.setIndex(start);
 234:             end = skipNonWhitespace(text, start + 1);
 235:             second = Integer.parseInt(text.substring(start, end));
 236:             // Advance to year
 237:             start = skipWhitespace(text, end + 1);
 238:             pos.setIndex(start);
 239:             end = skipNonWhitespace(text, start + 1);
 240:             year = Integer.parseInt(text.substring(start, end));
 241:             break;
 242:           case 0:
 243:           case 4:
 244:             // rfc822
 245:             start = skipWhitespace(text, start);
 246:             pos.setIndex(start);
 247:             end = skipNonWhitespace(text, start + 1);
 248:             date = Integer.parseInt(text.substring(start, end));
 249:             // Advance to month
 250:             start = skipWhitespace(text, end + 1);
 251:             pos.setIndex(start);
 252:             end = skipNonWhitespace(text, start + 1);
 253:             monthText = text.substring(start, end);
 254:             month = -1;
 255:             for (int i = 0; i < 12; i++)
 256:               {
 257:                 if (MONTHS[i].equals(monthText))
 258:                   {
 259:                     month = i;
 260:                     break;
 261:                   }
 262:               }
 263:             if (month == -1)
 264:               {
 265:                 pos.setErrorIndex(end);
 266:                 return null;
 267:               }
 268:             // Advance to year
 269:             start = skipWhitespace(text, end + 1);
 270:             pos.setIndex(start);
 271:             end = skipNonWhitespace(text, start + 1);
 272:             year = Integer.parseInt(text.substring(start, end));
 273:             // Advance to hour
 274:             start = skipWhitespace(text, end + 1);
 275:             pos.setIndex(start);
 276:             end = skipTo(text, start + 1, ':');
 277:             hour = Integer.parseInt(text.substring(start, end));
 278:             // Advance to minute
 279:             start = end + 1;
 280:             pos.setIndex(start);
 281:             end = skipTo(text, start + 1, ':');
 282:             minute = Integer.parseInt(text.substring(start, end));
 283:             // Advance to second
 284:             start = end + 1;
 285:             pos.setIndex(start);
 286:             end = start + 1;
 287:             while (end < len && !Character.isWhitespace(text.charAt(end)))
 288:               {
 289:                 end++;
 290:               }
 291:             second = Integer.parseInt(text.substring(start, end));
 292:             break;
 293:           default:
 294:             // rfc850(obsolete)
 295:             start = skipWhitespace(text, start);
 296:             pos.setIndex(start);
 297:             end = skipTo(text, start + 1, '-');
 298:             date = Integer.parseInt(text.substring(start, end));
 299:             // Advance to month
 300:             start = end + 1;
 301:             pos.setIndex(start);
 302:             end = skipTo(text, start + 1, '-');
 303:             monthText = text.substring(start, end);
 304:             month = -1;
 305:             for (int i = 0; i < 12; i++)
 306:               {
 307:                 if (MONTHS[i].equals(monthText))
 308:                   {
 309:                     month = i;
 310:                     break;
 311:                   }
 312:               }
 313:             if (month == -1)
 314:               {
 315:                 pos.setErrorIndex(end);
 316:                 return null;
 317:               }
 318:             // Advance to year
 319:             start = end + 1;
 320:             pos.setIndex(start);
 321:             end = skipNonWhitespace(text, start + 1);
 322:             year = 1900 + Integer.parseInt(text.substring(start, end));
 323:             // Advance to hour
 324:             start = skipWhitespace(text, end + 1);
 325:             pos.setIndex(start);
 326:             end = skipTo(text, start + 1, ':');
 327:             hour = Integer.parseInt(text.substring(start, end));
 328:             // Advance to minute
 329:             start = end + 1;
 330:             pos.setIndex(start);
 331:             end = skipTo(text, start + 1, ':');
 332:             minute = Integer.parseInt(text.substring(start, end));
 333:             // Advance to second
 334:             start = end + 1;
 335:             pos.setIndex(start);
 336:             end = start + 1;
 337:             while (end < len && !Character.isWhitespace(text.charAt(end)))
 338:               {
 339:                 end++;
 340:               }
 341:             second = Integer.parseInt(text.substring(start, end));
 342:           }
 343: 
 344:         calendar.set(Calendar.YEAR, year);
 345:         calendar.set(Calendar.MONTH, month);
 346:         calendar.set(Calendar.DAY_OF_MONTH, date);
 347:         calendar.set(Calendar.HOUR, hour);
 348:         calendar.set(Calendar.MINUTE, minute);
 349:         calendar.set(Calendar.SECOND, second);
 350: 
 351:         if (end != len)
 352:           {
 353:             // Timezone
 354:             start = skipWhitespace(text, end + 1);
 355:             end = start + 1;
 356:             while (end < len && !Character.isWhitespace(text.charAt(end)))
 357:               {
 358:                 end++;
 359:               }
 360:             char pm = text.charAt(start);
 361:             if (Character.isLetter(pm))
 362:               {
 363:                 TimeZone tz =
 364:                   TimeZone.getTimeZone(text.substring(start, end));
 365:                 calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset());
 366:               }
 367:             else
 368:               {
 369:                 int zoneOffset = 0;
 370:                 zoneOffset += 600 * Character.digit(text.charAt(++start), 10);
 371:                 zoneOffset += 60 * Character.digit(text.charAt(++start), 10);
 372:                 zoneOffset += 10 * Character.digit(text.charAt(++start), 10);
 373:                 zoneOffset += Character.digit(text.charAt(++start), 10);
 374:                 zoneOffset *= 60000; // minutes -> ms
 375:                 if ('-' == pm)
 376:                   {
 377:                     zoneOffset = -zoneOffset;
 378:                   }
 379:                 calendar.set(Calendar.ZONE_OFFSET, zoneOffset);
 380:               }
 381:           }
 382:         pos.setIndex(end);
 383: 
 384:         return calendar.getTime();
 385:       }
 386:     catch (NumberFormatException e)
 387:       {
 388:         pos.setErrorIndex(Math.max(start, end));
 389:       }
 390:     catch (StringIndexOutOfBoundsException e)
 391:       {
 392:         pos.setErrorIndex(Math.max(start, end));
 393:       }
 394:     return null;
 395:   }
 396: 
 397:   private int skipWhitespace(String text, int pos)
 398:   {
 399:     while(Character.isWhitespace(text.charAt(pos)))
 400:       {
 401:         pos++;
 402:       }
 403:     return pos;
 404:   }
 405: 
 406:   private int skipNonWhitespace(String text, int pos)
 407:   {
 408:     while(!Character.isWhitespace(text.charAt(pos)))
 409:       {
 410:         pos++;
 411:       }
 412:     return pos;
 413:   }
 414: 
 415:   private int skipTo(String text, int pos, char c)
 416:   {
 417:     while(text.charAt(pos) != c)
 418:       {
 419:         pos++;
 420:       }
 421:     return pos;
 422:   }
 423: 
 424:   /**
 425:    * Don't allow setting the calendar.
 426:    */
 427:   public void setCalendar(Calendar newCalendar)
 428:   {
 429:     throw new UnsupportedOperationException();
 430:   }
 431: 
 432:   /**
 433:    * Don't allow setting the NumberFormat.
 434:    */
 435:   public void setNumberFormat(NumberFormat newNumberFormat)
 436:   {
 437:     throw new UnsupportedOperationException();
 438:   }
 439: 
 440: }