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 HTTPDateFormat()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 */ format(Date date, StringBuffer buf, FieldPosition field)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 */ parse(String text, ParsePosition pos)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 skipWhitespace(String text, int pos)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 skipNonWhitespace(String text, int pos)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 skipTo(String text, int pos, char c)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 */ setCalendar(Calendar newCalendar)427 public void setCalendar(Calendar newCalendar) 428 { 429 throw new UnsupportedOperationException(); 430 } 431 432 /** 433 * Don't allow setting the NumberFormat. 434 */ setNumberFormat(NumberFormat newNumberFormat)435 public void setNumberFormat(NumberFormat newNumberFormat) 436 { 437 throw new UnsupportedOperationException(); 438 } 439 440 } 441