1 /* 2 * Copyright (C) 2012 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 com.google.android.vending.expansion.downloader.impl; 18 19 import android.text.format.Time; 20 21 import java.util.Calendar; 22 import java.util.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 /** 26 * Helper for parsing an HTTP date. 27 */ 28 public final class HttpDateTime { 29 30 /* 31 * Regular expression for parsing HTTP-date. Wdy, DD Mon YYYY HH:MM:SS GMT 32 * RFC 822, updated by RFC 1123 Weekday, DD-Mon-YY HH:MM:SS GMT RFC 850, 33 * obsoleted by RFC 1036 Wdy Mon DD HH:MM:SS YYYY ANSI C's asctime() format 34 * with following variations Wdy, DD-Mon-YYYY HH:MM:SS GMT Wdy, (SP)D Mon 35 * YYYY HH:MM:SS GMT Wdy,DD Mon YYYY HH:MM:SS GMT Wdy, DD-Mon-YY HH:MM:SS 36 * GMT Wdy, DD Mon YYYY HH:MM:SS -HHMM Wdy, DD Mon YYYY HH:MM:SS Wdy Mon 37 * (SP)D HH:MM:SS YYYY Wdy Mon DD HH:MM:SS YYYY GMT HH can be H if the first 38 * digit is zero. Mon can be the full name of the month. 39 */ 40 private static final String HTTP_DATE_RFC_REGEXP = 41 "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" 42 + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; 43 44 private static final String HTTP_DATE_ANSIC_REGEXP = 45 "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" 46 + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; 47 48 /** 49 * The compiled version of the HTTP-date regular expressions. 50 */ 51 private static final Pattern HTTP_DATE_RFC_PATTERN = 52 Pattern.compile(HTTP_DATE_RFC_REGEXP); 53 private static final Pattern HTTP_DATE_ANSIC_PATTERN = 54 Pattern.compile(HTTP_DATE_ANSIC_REGEXP); 55 56 private static class TimeOfDay { TimeOfDay(int h, int m, int s)57 TimeOfDay(int h, int m, int s) { 58 this.hour = h; 59 this.minute = m; 60 this.second = s; 61 } 62 63 int hour; 64 int minute; 65 int second; 66 } 67 parse(String timeString)68 public static long parse(String timeString) 69 throws IllegalArgumentException { 70 71 int date = 1; 72 int month = Calendar.JANUARY; 73 int year = 1970; 74 TimeOfDay timeOfDay; 75 76 Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); 77 if (rfcMatcher.find()) { 78 date = getDate(rfcMatcher.group(1)); 79 month = getMonth(rfcMatcher.group(2)); 80 year = getYear(rfcMatcher.group(3)); 81 timeOfDay = getTime(rfcMatcher.group(4)); 82 } else { 83 Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); 84 if (ansicMatcher.find()) { 85 month = getMonth(ansicMatcher.group(1)); 86 date = getDate(ansicMatcher.group(2)); 87 timeOfDay = getTime(ansicMatcher.group(3)); 88 year = getYear(ansicMatcher.group(4)); 89 } else { 90 throw new IllegalArgumentException(); 91 } 92 } 93 94 // FIXME: Y2038 BUG! 95 if (year >= 2038) { 96 year = 2038; 97 month = Calendar.JANUARY; 98 date = 1; 99 } 100 101 Time time = new Time(Time.TIMEZONE_UTC); 102 time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date, 103 month, year); 104 return time.toMillis(false /* use isDst */); 105 } 106 getDate(String dateString)107 private static int getDate(String dateString) { 108 if (dateString.length() == 2) { 109 return (dateString.charAt(0) - '0') * 10 110 + (dateString.charAt(1) - '0'); 111 } else { 112 return (dateString.charAt(0) - '0'); 113 } 114 } 115 116 /* 117 * jan = 9 + 0 + 13 = 22 feb = 5 + 4 + 1 = 10 mar = 12 + 0 + 17 = 29 apr = 0 118 * + 15 + 17 = 32 may = 12 + 0 + 24 = 36 jun = 9 + 20 + 13 = 42 jul = 9 + 20 119 * + 11 = 40 aug = 0 + 20 + 6 = 26 sep = 18 + 4 + 15 = 37 oct = 14 + 2 + 19 120 * = 35 nov = 13 + 14 + 21 = 48 dec = 3 + 4 + 2 = 9 121 */ getMonth(String monthString)122 private static int getMonth(String monthString) { 123 int hash = Character.toLowerCase(monthString.charAt(0)) + 124 Character.toLowerCase(monthString.charAt(1)) + 125 Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; 126 switch (hash) { 127 case 22: 128 return Calendar.JANUARY; 129 case 10: 130 return Calendar.FEBRUARY; 131 case 29: 132 return Calendar.MARCH; 133 case 32: 134 return Calendar.APRIL; 135 case 36: 136 return Calendar.MAY; 137 case 42: 138 return Calendar.JUNE; 139 case 40: 140 return Calendar.JULY; 141 case 26: 142 return Calendar.AUGUST; 143 case 37: 144 return Calendar.SEPTEMBER; 145 case 35: 146 return Calendar.OCTOBER; 147 case 48: 148 return Calendar.NOVEMBER; 149 case 9: 150 return Calendar.DECEMBER; 151 default: 152 throw new IllegalArgumentException(); 153 } 154 } 155 getYear(String yearString)156 private static int getYear(String yearString) { 157 if (yearString.length() == 2) { 158 int year = (yearString.charAt(0) - '0') * 10 159 + (yearString.charAt(1) - '0'); 160 if (year >= 70) { 161 return year + 1900; 162 } else { 163 return year + 2000; 164 } 165 } else if (yearString.length() == 3) { 166 // According to RFC 2822, three digit years should be added to 1900. 167 int year = (yearString.charAt(0) - '0') * 100 168 + (yearString.charAt(1) - '0') * 10 169 + (yearString.charAt(2) - '0'); 170 return year + 1900; 171 } else if (yearString.length() == 4) { 172 return (yearString.charAt(0) - '0') * 1000 173 + (yearString.charAt(1) - '0') * 100 174 + (yearString.charAt(2) - '0') * 10 175 + (yearString.charAt(3) - '0'); 176 } else { 177 return 1970; 178 } 179 } 180 getTime(String timeString)181 private static TimeOfDay getTime(String timeString) { 182 // HH might be H 183 int i = 0; 184 int hour = timeString.charAt(i++) - '0'; 185 if (timeString.charAt(i) != ':') 186 hour = hour * 10 + (timeString.charAt(i++) - '0'); 187 // Skip ':' 188 i++; 189 190 int minute = (timeString.charAt(i++) - '0') * 10 191 + (timeString.charAt(i++) - '0'); 192 // Skip ':' 193 i++; 194 195 int second = (timeString.charAt(i++) - '0') * 10 196 + (timeString.charAt(i++) - '0'); 197 198 return new TimeOfDay(hour, minute, second); 199 } 200 } 201