/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package okhttp3.internal.http;

import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import static okhttp3.internal.Util.UTC;

Best-effort parser for HTTP dates.
/** * Best-effort parser for HTTP dates. */
public final class HttpDate {
The last four-digit year: "Fri, 31 Dec 9999 23:59:59 GMT".
/** The last four-digit year: "Fri, 31 Dec 9999 23:59:59 GMT". */
public static final long MAX_DATE = 253402300799999L;
Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such cookies are on the fast path.
/** * Most websites serve cookies in the blessed format. Eagerly create the parser to ensure such * cookies are on the fast path. */
private static final ThreadLocal<DateFormat> STANDARD_DATE_FORMAT = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { // Date format specified by RFC 7231 section 7.1.1.1. DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US); rfc1123.setLenient(false); rfc1123.setTimeZone(UTC); return rfc1123; } };
If we fail to parse a date in a non-standard format, try each of these formats in sequence.
/** If we fail to parse a date in a non-standard format, try each of these formats in sequence. */
private static final String[] BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS = new String[] { // HTTP formats required by RFC2616 but with any timezone. "EEE, dd MMM yyyy HH:mm:ss zzz", // RFC 822, updated by RFC 1123 with any TZ "EEEE, dd-MMM-yy HH:mm:ss zzz", // RFC 850, obsoleted by RFC 1036 with any TZ. "EEE MMM d HH:mm:ss yyyy", // ANSI C's asctime() format // Alternative formats. "EEE, dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MMM-yyyy HH-mm-ss z", "EEE, dd MMM yy HH:mm:ss z", "EEE dd-MMM-yyyy HH:mm:ss z", "EEE dd MMM yyyy HH:mm:ss z", "EEE dd-MMM-yyyy HH-mm-ss z", "EEE dd-MMM-yy HH:mm:ss z", "EEE dd MMM yy HH:mm:ss z", "EEE,dd-MMM-yy HH:mm:ss z", "EEE,dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MM-yyyy HH:mm:ss z", /* RI bug 6641315 claims a cookie of this format was once served by www.yahoo.com */ "EEE MMM d yyyy HH:mm:ss z", }; private static final DateFormat[] BROWSER_COMPATIBLE_DATE_FORMATS = new DateFormat[BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length];
Returns the date for value. Returns null if the value couldn't be parsed.
/** Returns the date for {@code value}. Returns null if the value couldn't be parsed. */
public static Date parse(String value) { if (value.length() == 0) { return null; } ParsePosition position = new ParsePosition(0); Date result = STANDARD_DATE_FORMAT.get().parse(value, position); if (position.getIndex() == value.length()) { // STANDARD_DATE_FORMAT must match exactly; all text must be consumed, e.g. no ignored // non-standard trailing "+01:00". Those cases are covered below. return result; } synchronized (BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS) { for (int i = 0, count = BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length; i < count; i++) { DateFormat format = BROWSER_COMPATIBLE_DATE_FORMATS[i]; if (format == null) { format = new SimpleDateFormat(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS[i], Locale.US); // Set the timezone to use when interpreting formats that don't have a timezone. GMT is // specified by RFC 7231. format.setTimeZone(UTC); BROWSER_COMPATIBLE_DATE_FORMATS[i] = format; } position.setIndex(0); result = format.parse(value, position); if (position.getIndex() != 0) { // Something was parsed. It's possible the entire string was not consumed but we ignore // that. If any of the BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS ended in "'GMT'" we'd have // to also check that position.getIndex() == value.length() otherwise parsing might have // terminated early, ignoring things like "+01:00". Leaving this as != 0 means that any // trailing junk is ignored. return result; } } } return null; }
Returns the string for value.
/** Returns the string for {@code value}. */
public static String format(Date value) { return STANDARD_DATE_FORMAT.get().format(value); } private HttpDate() { } }