1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.http; 18 19 import java.util.Calendar; 20 import java.util.GregorianCalendar; 21 import java.util.TimeZone; 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 /** 26 * Helper for parsing an HTTP date. 27 */ 28 final class LegacyHttpDateTime { 29 30 /* 31 * Regular expression for parsing HTTP-date. 32 * 33 * Wdy, DD Mon YYYY HH:MM:SS GMT 34 * RFC 822, updated by RFC 1123 35 * 36 * Weekday, DD-Mon-YY HH:MM:SS GMT 37 * RFC 850, obsoleted by RFC 1036 38 * 39 * Wdy Mon DD HH:MM:SS YYYY 40 * ANSI C's asctime() format 41 * 42 * with following variations 43 * 44 * Wdy, DD-Mon-YYYY HH:MM:SS GMT 45 * Wdy, (SP)D Mon YYYY HH:MM:SS GMT 46 * Wdy,DD Mon YYYY HH:MM:SS GMT 47 * Wdy, DD-Mon-YY HH:MM:SS GMT 48 * Wdy, DD Mon YYYY HH:MM:SS -HHMM 49 * Wdy, DD Mon YYYY HH:MM:SS 50 * Wdy Mon (SP)D HH:MM:SS YYYY 51 * Wdy Mon DD HH:MM:SS YYYY GMT 52 * 53 * HH can be H if the first digit is zero. 54 * 55 * Mon can be the full name of the month. 56 */ 57 private static final String HTTP_DATE_RFC_REGEXP = 58 "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" 59 + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; 60 61 private static final String HTTP_DATE_ANSIC_REGEXP = 62 "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" 63 + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; 64 65 /** 66 * The compiled version of the HTTP-date regular expressions. 67 */ 68 private static final Pattern HTTP_DATE_RFC_PATTERN = 69 Pattern.compile(HTTP_DATE_RFC_REGEXP); 70 private static final Pattern HTTP_DATE_ANSIC_PATTERN = 71 Pattern.compile(HTTP_DATE_ANSIC_REGEXP); 72 73 private static class TimeOfDay { TimeOfDay(int h, int m, int s)74 TimeOfDay(int h, int m, int s) { 75 this.hour = h; 76 this.minute = m; 77 this.second = s; 78 } 79 80 int hour; 81 int minute; 82 int second; 83 } 84 parse(String timeString)85 public static long parse(String timeString) 86 throws IllegalArgumentException { 87 88 int date = 1; 89 int month = Calendar.JANUARY; 90 int year = 1970; 91 TimeOfDay timeOfDay; 92 93 Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); 94 if (rfcMatcher.find()) { 95 date = getDate(rfcMatcher.group(1)); 96 month = getMonth(rfcMatcher.group(2)); 97 year = getYear(rfcMatcher.group(3)); 98 timeOfDay = getTime(rfcMatcher.group(4)); 99 } else { 100 Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); 101 if (ansicMatcher.find()) { 102 month = getMonth(ansicMatcher.group(1)); 103 date = getDate(ansicMatcher.group(2)); 104 timeOfDay = getTime(ansicMatcher.group(3)); 105 year = getYear(ansicMatcher.group(4)); 106 } else { 107 throw new IllegalArgumentException(); 108 } 109 } 110 111 TimeZone utc = TimeZone.getTimeZone("UTC"); 112 GregorianCalendar calendar = new GregorianCalendar(utc); 113 calendar.set(Calendar.YEAR, year); 114 calendar.set(Calendar.MONTH, month); 115 calendar.set(Calendar.DAY_OF_MONTH, date); 116 calendar.set(Calendar.HOUR_OF_DAY, timeOfDay.hour); 117 calendar.set(Calendar.MINUTE, timeOfDay.minute); 118 calendar.set(Calendar.SECOND, timeOfDay.second); 119 calendar.set(Calendar.MILLISECOND, 0); 120 return calendar.getTimeInMillis(); 121 } 122 getDate(String dateString)123 private static int getDate(String dateString) { 124 if (dateString.length() == 2) { 125 return (dateString.charAt(0) - '0') * 10 126 + (dateString.charAt(1) - '0'); 127 } else { 128 return (dateString.charAt(0) - '0'); 129 } 130 } 131 132 /* 133 * jan = 9 + 0 + 13 = 22 134 * feb = 5 + 4 + 1 = 10 135 * mar = 12 + 0 + 17 = 29 136 * apr = 0 + 15 + 17 = 32 137 * may = 12 + 0 + 24 = 36 138 * jun = 9 + 20 + 13 = 42 139 * jul = 9 + 20 + 11 = 40 140 * aug = 0 + 20 + 6 = 26 141 * sep = 18 + 4 + 15 = 37 142 * oct = 14 + 2 + 19 = 35 143 * nov = 13 + 14 + 21 = 48 144 * dec = 3 + 4 + 2 = 9 145 */ getMonth(String monthString)146 private static int getMonth(String monthString) { 147 int hash = Character.toLowerCase(monthString.charAt(0)) + 148 Character.toLowerCase(monthString.charAt(1)) + 149 Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; 150 switch (hash) { 151 case 22: 152 return Calendar.JANUARY; 153 case 10: 154 return Calendar.FEBRUARY; 155 case 29: 156 return Calendar.MARCH; 157 case 32: 158 return Calendar.APRIL; 159 case 36: 160 return Calendar.MAY; 161 case 42: 162 return Calendar.JUNE; 163 case 40: 164 return Calendar.JULY; 165 case 26: 166 return Calendar.AUGUST; 167 case 37: 168 return Calendar.SEPTEMBER; 169 case 35: 170 return Calendar.OCTOBER; 171 case 48: 172 return Calendar.NOVEMBER; 173 case 9: 174 return Calendar.DECEMBER; 175 default: 176 throw new IllegalArgumentException(); 177 } 178 } 179 getYear(String yearString)180 private static int getYear(String yearString) { 181 if (yearString.length() == 2) { 182 int year = (yearString.charAt(0) - '0') * 10 183 + (yearString.charAt(1) - '0'); 184 if (year >= 70) { 185 return year + 1900; 186 } else { 187 return year + 2000; 188 } 189 } else if (yearString.length() == 3) { 190 // According to RFC 2822, three digit years should be added to 1900. 191 int year = (yearString.charAt(0) - '0') * 100 192 + (yearString.charAt(1) - '0') * 10 193 + (yearString.charAt(2) - '0'); 194 return year + 1900; 195 } else if (yearString.length() == 4) { 196 return (yearString.charAt(0) - '0') * 1000 197 + (yearString.charAt(1) - '0') * 100 198 + (yearString.charAt(2) - '0') * 10 199 + (yearString.charAt(3) - '0'); 200 } else { 201 return 1970; 202 } 203 } 204 getTime(String timeString)205 private static TimeOfDay getTime(String timeString) { 206 // HH might be H 207 int i = 0; 208 int hour = timeString.charAt(i++) - '0'; 209 if (timeString.charAt(i) != ':') 210 hour = hour * 10 + (timeString.charAt(i++) - '0'); 211 // Skip ':' 212 i++; 213 214 int minute = (timeString.charAt(i++) - '0') * 10 215 + (timeString.charAt(i++) - '0'); 216 // Skip ':' 217 i++; 218 219 int second = (timeString.charAt(i++) - '0') * 10 220 + (timeString.charAt(i++) - '0'); 221 222 return new TimeOfDay(hour, minute, second); 223 } 224 } 225