summaryrefslogtreecommitdiffstats
path: root/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java')
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java642
1 files changed, 642 insertions, 0 deletions
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java
new file mode 100644
index 0000000..7efea9c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java
@@ -0,0 +1,642 @@
+/*
+ * DateParser.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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 org.simpleframework.http.parse;
+
+import static java.util.Calendar.DAY_OF_MONTH;
+import static java.util.Calendar.DAY_OF_WEEK;
+import static java.util.Calendar.HOUR_OF_DAY;
+import static java.util.Calendar.MILLISECOND;
+import static java.util.Calendar.MINUTE;
+import static java.util.Calendar.MONTH;
+import static java.util.Calendar.SECOND;
+import static java.util.Calendar.YEAR;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import org.simpleframework.common.parse.Parser;
+
+/**
+ * This is used to create a <code>Parser</code> for the HTTP date format.
+ * This will parse the 3 formats that are acceptable for the HTTP/1.1 date.
+ * The three formats that are acceptable for the HTTP-date are
+ * <pre>
+ * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ * </pre>
+ * <p>
+ * This can also parse the date in ms as retrived from the <code>System</code>'s
+ * <code>System.currentTimeMillis</code> method. This has a parse method for a
+ * <code>long</code> which will do the same as the <code>parse(String)</code>.
+ * Once the date has been parsed there are two methods that allow the date
+ * to be represented, the <code>toLong</code> method converts the date to a
+ * <code>long</code> and the <code>toString</code> method will convert the date
+ * into a <code>String</code>.
+ * <p>
+ * This produces the same string as the <code>SimpleDateFormat.format</code>
+ * using the pattern <code>"EEE, dd MMM yyyy hh:mm:ss 'GMT'"</code>. This will
+ * however do the job faster as it does not take arbitrary inputs.
+ *
+ * @author Niall Gallagher
+ */
+public class DateParser extends Parser {
+
+ /**
+ * Ensure that the time zone for dates if set to GMT.
+ */
+ private static final TimeZone ZONE = TimeZone.getTimeZone("GMT");
+
+ /**
+ * Contains the possible days of the week for RFC 1123.
+ */
+ private static final String WKDAYS[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+
+ /**
+ * Contains the possible days of the week for RFC 850.
+ */
+ private static final String WEEKDAYS[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
+
+ /**
+ * Contains the possible months in the year for HTTP-date.
+ */
+ private static final String MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ /**
+ * Used as an index into the months array to get the month.
+ */
+ private int month;
+
+ /**
+ * Represents the decimal value of the date such as 1977.
+ */
+ private int year;
+
+ /**
+ * Represents the decimal value of the date such as 18.
+ */
+ private int day;
+
+ /**
+ * Used as an index into the weekdays array to get the weekday.
+ */
+ private int weekday;
+
+ /**
+ * Represents the decimal value of the hour such as 24.
+ */
+ private int hour;
+
+ /**
+ * Represents the decimal value of the minute.
+ */
+ private int mins;
+
+ /**
+ * Represents the decimal value for the second.
+ */
+ private int secs;
+
+ /**
+ * The default constructor will create a parser that can parse
+ * <code>String</code>s that contain dates in the form of RFC 1123,
+ * RFC 850 or asctime. If the dates that are to be parsed are not in
+ * the form of one of these date encodings the results of this
+ * parser will be random.
+ */
+ public DateParser(){
+ this.init();
+ }
+
+ /**
+ * This constructor will conveniently parse the <code>long</code> argument
+ * in the constructor. This can also be done by first calling the no-arg
+ * constructor and then using the parse method.
+ * <p>
+ * This will then set this object to one that uses the RFC 1123 format
+ * for a date.
+ *
+ * @param date the date to be parsed
+ */
+ public DateParser(long date){
+ this();
+ parse(date);
+ }
+
+ /** This constructor will conveniently parse the <code>String</code>
+ * argument in the constructor. This can also be done by first calling
+ * the no-arg constructor and then using the parse method.
+ * <p>
+ * This will then set this object to one that uses the RFC 1123 format
+ * for a date.
+ *
+ * @param date the date to be parsed
+ */
+ public DateParser(String date) {
+ this();
+ parse(date);
+ }
+
+ /**
+ * This is used to extract the date from a <code>long</code>. If this
+ * method is given the value of the date as a <code>long</code> it will
+ * construct the RFC 1123 date as required by RFC 2616 sec 3.3.
+ * <p>
+ * This saves time on parsing a <code>String</code> that is encoded in
+ * the HTTP-date format. The date given must be positive, if the date
+ * given is not a positive '<code>long</code>' then the results
+ * of this method is random/unknown.
+ *
+ * @param date the date to be parsed
+ */
+ public void parse(long date){
+ Calendar calendar = Calendar.getInstance(ZONE);
+ calendar.setTimeInMillis(date);
+
+ weekday = calendar.get(DAY_OF_WEEK);
+ year = calendar.get(YEAR);
+ month = calendar.get(MONTH);
+ day = calendar.get(DAY_OF_MONTH);
+ hour = calendar.get(HOUR_OF_DAY);
+ mins = calendar.get(MINUTE);
+ secs = calendar.get(SECOND);
+ month = month > 11 ? 11: month;
+ weekday = (weekday+5) % 7;
+ }
+
+ /**
+ * Convenience method used to convert the specified HTTP date in to a
+ * long representing the time. This is used when a single method is
+ * required to convert a HTTP date format to a usable long value for
+ * use in creating <code>Date</code> objects.
+ *
+ * @param date the date specified in on of the HTTP date formats
+ *
+ * @return the date value as a long value in milliseconds
+ */
+ public long convert(String date) {
+ parse(date);
+ return toLong();
+
+ }
+
+ /**
+ * Convenience method used to convert the specified long date in to a
+ * HTTP date format. This is used when a single method is required to
+ * convert a long data value in milliseconds to a HTTP date value.
+ *
+ * @param date the date specified as a long of milliseconds
+ *
+ * @return the date represented in the HTTP date format RFC 1123
+ */
+ public String convert(long date) {
+ parse(date);
+ return toString();
+ }
+
+ /**
+ * This is used to reset the date and the buffer variables
+ * for this <code>DateParser</code>. Every in is set to the
+ * value of 0.
+ */
+ protected void init() {
+ month = year = day =
+ weekday = hour = mins =
+ secs = off = 0;
+ }
+
+ /**
+ * This is used to parse the contents of the <code>buf</code>. This
+ * checks the fourth char of the buffer to see what it contains. Invariably
+ * a date format belonging to RFC 1123 will have a ',' character in position 4,
+ * a date format belonging to asctime will have a ' ' character in position 4
+ * and if neither of these characters are found at position 4 then it is
+ * assumed that the date is in the RFC 850 fromat, however it may not be.
+ */
+ protected void parse(){
+ if(buf.length<4)return;
+ if(buf[3]==','){
+ rfc1123();
+ }else if(buf[3]==' '){
+ asctime();
+ }else{
+ rfc850();
+ }
+ }
+
+ /**
+ * This will parse a date that is in the form of an RFC 1123 date. This
+ * date format is the date format that is to be used with all applications
+ * that are HTTP/1.1 compliant. The RFC 1123 date format is
+ * <pre>
+ * rfc1123 = 'wkday "," SP date1 SP time SP GMT'.
+ * date1 = '2DIGIT SP month SP 4DIGIT' and finally
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void rfc1123(){
+ wkday();
+ off+=2;
+ date1();
+ off++;
+ time();
+ }
+
+ /**
+ * This will parse a date that is in the form of an RFC 850 date. This date
+ * format is the date format that is to be used with some applications that
+ * are HTTP/1.0 compliant. The RFC 1123 date format is
+ * <pre>
+ * rfc850 = 'weekday "," SP date2 SP time SP GMT'.
+ * date2 = '2DIGIT "-" month "-" 2DIGIT' and finally
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void rfc850() {
+ weekday();
+ off+=2;
+ date2();
+ off++;
+ time();
+ }
+
+ /**
+ * This will parse a date that is in the form of an asctime date. This date
+ * format is the date format that is to be used with some applications that
+ * are HTTP/1.0 compliant. The RFC 1123 date format is
+ * <pre>
+ * asctime = 'weekday SP date3 SP time SP 4DIGIT'.
+ * date3 = 'month SP (2DIGIT | (SP 1DIGIT))' and
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void asctime(){
+ wkday();
+ off++;
+ date3();
+ off++;
+ time();
+ off++;
+ year4();
+ }
+
+ /**
+ * This is the date1 format of a date that is used by the RFC 1123
+ * date format. This date is
+ * <pre>
+ * date1 = '2DIGIT SP month SP 4DIGIT'.
+ * example '02 Jun 1982'.
+ * </pre>
+ */
+ private void date1(){
+ day();
+ off++;
+ month();
+ off++;
+ year4();
+ }
+
+ /**
+ * This is the date2 format of a date that is used by the RFC 850
+ * date format. This date is
+ * <pre>
+ * date2 = '2DIGIT "-" month "-" 2DIGIT'
+ * example '02-Jun-82'.
+ * </pre>
+ */
+ private void date2(){
+ day();
+ off++;
+ month();
+ off++;
+ year2();
+ }
+
+ /**
+ * This is the date3 format of a date that is used by the asctime
+ * date format. This date is
+ * <pre>
+ * date3 = 'month SP (2DIGIT | (SP 1DIGIT))'
+ * example 'Jun 2'.
+ * <pre>
+ */
+ private void date3(){
+ month();
+ off++;
+ day();
+ }
+
+ /**
+ * This is used to parse a consecutive set of digit characters to create
+ * the day of the week. This will tolerate a space on front of the digits
+ * thiswill allow all date formats including asctime to use this to get
+ * the day. This may parse more than 2 digits, however if there are more
+ * than 2 digits the date format is incorrect anyway.
+ */
+ private void day(){
+ if(space(buf[off])){
+ off++;
+ }
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ day *= 10;
+ day += buf[off];
+ day -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to get the year from a set of digit characters. This is
+ * used to parse years that are of the form of 2 digits (e.g 82) however
+ * this will assume that any dates that are in 2 digit format are dates
+ * for the 2000 th milleneum so 01 will be 2001.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2 digits
+ * in a row then the date format is incorrect anyway.
+ */
+ private void year2(){
+ int mill = 2000; /* milleneum */
+ int cent = 0; /* century */
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ cent *= 10;
+ cent += buf[off];
+ cent -= '0';
+ off++;
+ }
+ year= mill+cent; /* result 4 digits*/
+ }
+
+ /**
+ * This is used to get the year from a set of digit characters. This
+ * is used to parse years that are of the form of 4 digits (e.g 1982).
+ * <p>
+ * This may parse more than 4 digits but if there are more than 2
+ * digits in a row then the date format is incorrect anyway.
+ */
+ private void year4() {
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ year *= 10;
+ year += buf[off];
+ year -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to parse the time for a HTTP-date. The time for a
+ * HTTP-date is in the form <code>00:00:00</code> that is
+ * <pre>
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT' so this will
+ * read only a time of that form, although this will
+ * parse time = '2DIGIT CHAR 2DIGIT CHAR 2DIGIT'.
+ * </pre>
+ */
+ private void time(){
+ hours();
+ off++;
+ mins();
+ off++;
+ secs();
+ }
+
+ /**
+ * This is used to initialize the hour. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal number
+ * to represent the hour that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect.
+ */
+ private void hours(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ hour *= 10;
+ hour += buf[off];
+ hour -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to initialize the mins. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal number
+ * to represent the mins that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect.
+ */
+ private void mins(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ mins *= 10;
+ mins += buf[off];
+ mins -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to initialize the secs. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal
+ * number to represent the secs that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect
+ */
+ private void secs(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ secs *= 10;
+ secs += buf[off];
+ secs -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to read the week day of HTTP-date. The shorthand day
+ * (e.g Mon for Monday) is used by the RFC 1123 and asctime date formats.
+ * This will simply try to read each day from the buffer, when the day
+ * is read successfully then the index of that day is saved.
+ */
+ private void wkday(){
+ for(int i =0; i < WKDAYS.length;i++){
+ if(skip(WKDAYS[i])){
+ weekday = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to read the week day of HTTP-date. This format is used
+ * by the RFC 850 date format. This will simply try to read each day from
+ * the buffer, when the day is read successfully then the index of that
+ * day is saved.
+ */
+ private void weekday(){
+ for(int i =0; i < WKDAYS.length;i++){
+ if(skip(WEEKDAYS[i])){
+ weekday = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to read the month of HTTP-date. This will simply
+ * try to read each month from the buffer, when the month is read
+ * successfully then the index of that month is saved.
+ */
+ private void month(){
+ for(int i =0; i < MONTHS.length;i++){
+ if(skip(MONTHS[i])){
+ month = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to append the date in RFC 1123 format to the given
+ * string builder. This will append the date and a trailing space
+ * character to the buffer. Dates like the following are appended.
+ * <pre>
+ * Tue, 02 Jun 1982
+ * </pre>.
+ * For performance reasons a string builder is used. This avoids
+ * an unneeded synchronization caused by the string buffers.
+ *
+ * @param builder this is the builder to append the date to
+ */
+ private void date(StringBuilder builder) {
+ builder.append(WKDAYS[weekday]);
+ builder.append(", ");
+
+ if(day <= 9) {
+ builder.append('0');
+ }
+ builder.append(day);
+ builder.append(' ');
+ builder.append(MONTHS[month]);
+ builder.append(' ');
+ builder.append(year);
+ builder.append(' ');
+ }
+
+ /**
+ * This is used to append the time in RFC 1123 format to the given
+ * string builder. This will append the time and a trailing space
+ * character to the buffer. Times like the following are appended.
+ * <pre>
+ * 23:59:59
+ * </pre>.
+ * For performance reasons a string builder is used. This avoids
+ * an unneeded synchronization caused by the string buffers.
+ *
+ * @param builder this is the builder to write the time to
+ */
+ private void time(StringBuilder builder) {
+ if(hour <= 9) {
+ builder.append('0');
+ }
+ builder.append(hour);
+ builder.append(':');
+
+ if(mins <= 9) {
+ builder.append('0');
+ }
+ builder.append(mins);
+ builder.append(':');
+
+ if(secs <= 9) {
+ builder.append('0');
+ }
+ builder.append(secs);
+ builder.append(' ');
+ }
+
+ /**
+ * This is used to append the time zone to the provided appender.
+ * For HTTP the dates should always be in GMT format. So this will
+ * simply append the "GMT" string to the end of the builder.
+ *
+ * @param builder this builder to append the time zone to
+ */
+ private void zone(StringBuilder builder) {
+ builder.append("GMT");
+ }
+
+ /**
+ * This returns the date in as a <code>long</code>, given the exact
+ * time this will use the <code>java.util.Date</code> to parse this date
+ * into a <code>long</code>. The <code>GregorianCalendar</code> uses
+ * the method <code>getTime</code> which produces the <code>Date</code>
+ * object from this the <code>getTime</code> returns the <code>long</code>
+ *
+ * @return the date parsed as a <code>long</code>
+ */
+ public long toLong() {
+ Calendar calendar = Calendar.getInstance(ZONE); /* GMT*/
+ calendar.set(year,month, day, hour, mins, secs);
+ calendar.set(MILLISECOND, 0);
+
+ return calendar.getTime().getTime();
+ }
+
+ /**
+ * This prints the date in the format of a RFC 1123 date. Example
+ * <pre>
+ * Tue, 02 Jun 1982 23:59:59 GMT
+ * </pre>.
+ * This uses a <code>StringBuffer</code> to accumulate the various
+ * <code>String</code>s/<code>int</code>s to form the resulting date
+ * value. The resulting date value is the one required by RFC 2616.
+ * <p>
+ * The HTTP date must be in the form of RFC 1123. The hours, minutes
+ * and seconds are appended with the 0 character if they are less than
+ * 9 i.e. if they do not have two digits.
+ *
+ * @return the date in RFC 1123 format
+ */
+ public String toString(){
+ StringBuilder builder = new StringBuilder(30);
+
+ date(builder);
+ time(builder);
+ zone(builder);
+
+ return builder.toString();
+ }
+}