Source for gnu.inet.http.HTTPDateFormat

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