_HTTPDateParse.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Net / System / Net / _HTTPDateParse.cs / 1 / _HTTPDateParse.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

using System.Globalization; 
 
namespace System.Net {
    internal static class HttpDateParse { 
        private const int BASE_DEC  = 10; // base 10

        //
        // Date indicies used to figure out what each entry is. 
        //
 
 
        private const int DATE_INDEX_DAY_OF_WEEK     = 0;
 
        private const int DATE_1123_INDEX_DAY        = 1;
        private const int DATE_1123_INDEX_MONTH      = 2;
        private const int DATE_1123_INDEX_YEAR       = 3;
        private const int DATE_1123_INDEX_HRS        = 4; 
        private const int DATE_1123_INDEX_MINS       = 5;
        private const int DATE_1123_INDEX_SECS       = 6; 
 
        private const int DATE_ANSI_INDEX_MONTH      = 1;
        private const int DATE_ANSI_INDEX_DAY        = 2; 
        private const int DATE_ANSI_INDEX_HRS        = 3;
        private const int DATE_ANSI_INDEX_MINS       = 4;
        private const int DATE_ANSI_INDEX_SECS       = 5;
        private const int DATE_ANSI_INDEX_YEAR       = 6; 

        private const int DATE_INDEX_TZ              = 7; 
 
        private const int DATE_INDEX_LAST            = DATE_INDEX_TZ;
        private const int MAX_FIELD_DATE_ENTRIES           = (DATE_INDEX_LAST+1); 

        //
        // DATE_TOKEN's DWORD values used to determine what day/month we're on
        // 

        private const int DATE_TOKEN_JANUARY      = 1; 
        private const int DATE_TOKEN_FEBRUARY     = 2; 
        private const int DATE_TOKEN_MARCH        = 3;
        private const int DATE_TOKEN_APRIL        = 4; 
        private const int DATE_TOKEN_MAY          = 5;
        private const int DATE_TOKEN_JUNE         = 6;
        private const int DATE_TOKEN_JULY         = 7;
        private const int DATE_TOKEN_AUGUST       = 8; 
        private const int DATE_TOKEN_SEPTEMBER    = 9;
        private const int DATE_TOKEN_OCTOBER      = 10; 
        private const int DATE_TOKEN_NOVEMBER     = 11; 
        private const int DATE_TOKEN_DECEMBER     = 12;
 
        private const int DATE_TOKEN_LAST_MONTH   = (DATE_TOKEN_DECEMBER+1);

        private const int DATE_TOKEN_SUNDAY       = 0;
        private const int DATE_TOKEN_MONDAY       = 1; 
        private const int DATE_TOKEN_TUESDAY      = 2;
        private const int DATE_TOKEN_WEDNESDAY    = 3; 
        private const int DATE_TOKEN_THURSDAY     = 4; 
        private const int DATE_TOKEN_FRIDAY       = 5;
        private const int DATE_TOKEN_SATURDAY     = 6; 

        private const int DATE_TOKEN_LAST_DAY     = (DATE_TOKEN_SATURDAY+1);

        private const int DATE_TOKEN_GMT          = -1000; 

        private const int DATE_TOKEN_LAST         = DATE_TOKEN_GMT; 
 
        private const int DATE_TOKEN_ERROR        = (DATE_TOKEN_LAST+1);
 

        //
        // MAKE_UPPER - takes an assumed lower character and bit manipulates into a upper.
        //              (make sure the character is Lower case alpha char to begin, 
        //               otherwise it corrupts)
        // 
 
        private
        static 
        char
        MAKE_UPPER(char c) {
            return(Char.ToUpper(c, CultureInfo.InvariantCulture));
        } 

        /*++ 
 
        Routine Description:
 
            Looks at the first three bytes of string to determine if we're looking
                at a Day of the Week, or Month, or "GMT" string.  Is inlined so that
                the compiler can optimize this code into the caller FInternalParseHttpDate.
 
        Arguments:
 
            lpszDay - a string ptr to the first byte of the string in question. 

        Return Value: 

            DWORD
            Success - The Correct date token, 0-6 for day of the week, 1-14 for month, etc
 
            Failure - DATE_TOKEN_ERROR
 
        --*/ 

        private 
        static
        int
        MapDayMonthToDword(
                          char [] lpszDay, 
                          int index
                          ) { 
            switch (MAKE_UPPER(lpszDay[index])) { // make uppercase 
                case 'A':
                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'P':
                            return DATE_TOKEN_APRIL;
                        case 'U':
                            return DATE_TOKEN_AUGUST; 

                    } 
                    return DATE_TOKEN_ERROR; 

                case 'D': 
                    return DATE_TOKEN_DECEMBER;

                case 'F':
                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'R':
                            return DATE_TOKEN_FRIDAY; 
                        case 'E': 
                            return DATE_TOKEN_FEBRUARY;
                    } 

                    return DATE_TOKEN_ERROR;

                case 'G': 
                    return DATE_TOKEN_GMT;
 
                case 'M': 

                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'O':
                            return DATE_TOKEN_MONDAY;
                        case 'A':
                            switch (MAKE_UPPER(lpszDay[index+2])) { 
                                case 'R':
                                    return DATE_TOKEN_MARCH; 
                                case 'Y': 
                                    return DATE_TOKEN_MAY;
                            } 

                            // fall through to error
                            break;
                    } 

                    return DATE_TOKEN_ERROR; 
 
                case 'N':
                    return DATE_TOKEN_NOVEMBER; 

                case 'J':

                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'A':
                            return DATE_TOKEN_JANUARY; 
 
                        case 'U':
                            switch (MAKE_UPPER(lpszDay[index+2])) { 
                                case 'N':
                                    return DATE_TOKEN_JUNE;
                                case 'L':
                                    return DATE_TOKEN_JULY; 
                            }
 
                            // fall through to error 
                            break;
                    } 

                    return DATE_TOKEN_ERROR;

                case 'O': 
                    return DATE_TOKEN_OCTOBER;
 
                case 'S': 

                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'A':
                            return DATE_TOKEN_SATURDAY;
                        case 'U':
                            return DATE_TOKEN_SUNDAY; 
                        case 'E':
                            return DATE_TOKEN_SEPTEMBER; 
                    } 

                    return DATE_TOKEN_ERROR; 


                case 'T':
                    switch (MAKE_UPPER(lpszDay[index+1])) { 
                        case 'U':
                            return DATE_TOKEN_TUESDAY; 
                        case 'H': 
                            return DATE_TOKEN_THURSDAY;
                    } 

                    return DATE_TOKEN_ERROR;

                case 'U': 
                    return DATE_TOKEN_GMT;
 
                case 'W': 
                    return DATE_TOKEN_WEDNESDAY;
 
            }

            return DATE_TOKEN_ERROR;
        } 

        /*++ 
 
        Routine Description:
 
            Parses through a ANSI, RFC850, or RFC1123 date format and covents it into
             a FILETIME/SYSTEMTIME time format.

            Important this a time-critical function and should only be changed 
             with the intention of optimizing or a critical need work item.
 
        Arguments: 

            lpft - Ptr to FILETIME structure.  Used to store converted result. 
                    Must be NULL if not intended to be used !!!

            lpSysTime - Ptr to SYSTEMTIME struture. Used to return Systime if needed.
 
            lpcszDateStr - Const Date string to parse.
 
        Return Value: 

            BOOL 
            Success - TRUE

            Failure - FALSE
 
        --*/
        public 
        static 
        bool
        ParseHttpDate( 
                     String DateString,
                     out DateTime dtOut
                     ) {
            int index = 0; 
            int i = 0, iLastLettered = -1;
            bool fIsANSIDateFormat = false; 
            int [] rgdwDateParseResults = new int[MAX_FIELD_DATE_ENTRIES]; 
            bool fRet = true;
            char [] lpInputBuffer = DateString.ToCharArray(); 

            dtOut = new DateTime();

            // 
            // Date Parsing v2 (1 more to go), and here is how it works...
            //  We take a date string and churn through it once, converting 
            //  integers to integers, Month,Day, and GMT strings into integers, 
            //  and all is then placed IN order in a temp array.
            // 
            // At the completetion of the parse stage, we simple look at
            //  the data, and then map the results into the correct
            //  places in the SYSTIME structure.  Simple, No allocations, and
            //  No dirting the data. 
            //
            // The end of the function does something munging and pretting 
            //  up of the results to handle the year 2000, and TZ offsets 
            //  Note: do we need to fully handle TZs anymore?
            // 

            while (index < DateString.Length && i < MAX_FIELD_DATE_ENTRIES) {
                if (lpInputBuffer[index] >= '0' && lpInputBuffer[index] <= '9') {
                    // 
                    // we have a numerical entry, scan through it and convent to DWORD
                    // 
 
                    rgdwDateParseResults[i] = 0;
 
                    do {
                        rgdwDateParseResults[i] *= BASE_DEC;
                        rgdwDateParseResults[i] += (lpInputBuffer[index] - '0');
                        index++; 
                    } while (index < DateString.Length &&
                             lpInputBuffer[index] >= '0' && 
                             lpInputBuffer[index] <= '9'); 

                    i++; // next token 
                }
                else if ((lpInputBuffer[index] >= 'A' && lpInputBuffer[index] <= 'Z') ||
                         (lpInputBuffer[index] >= 'a' && lpInputBuffer[index] <= 'z')) {
                    // 
                    // we have a string, should be a day, month, or GMT
                    //   lets skim to the end of the string 
                    // 

                    rgdwDateParseResults[i] = 
                    MapDayMonthToDword(lpInputBuffer, index);

                    iLastLettered = i;
 
                    // We want to ignore the possibility of a time zone such as PST or EST in a non-standard
                    // date format such as "Thu Dec 17 16:01:28 PST 1998" (Notice that the year is _after_ the time zone 
                    if ((rgdwDateParseResults[i] == DATE_TOKEN_ERROR) 
                        &&
                        !(fIsANSIDateFormat && (i==DATE_ANSI_INDEX_YEAR))) { 
                        fRet = false;
                        goto quit;
                    }
 
                    //
                    // At this point if we have a vaild string 
                    //  at this index, we know for sure that we're 
                    //  looking at a ANSI type DATE format.
                    // 

                    if (i == DATE_ANSI_INDEX_MONTH) {
                        fIsANSIDateFormat = true;
                    } 

                    // 
                    // Read past the end of the current set of alpha characters, 
                    //  as MapDayMonthToDword only peeks at a few characters
                    // 

                    do {
                        index++;
                    } while (index < DateString.Length && 
                             ( (lpInputBuffer[index] >= 'A' && lpInputBuffer[index] <= 'Z') ||
                               (lpInputBuffer[index] >= 'a' && lpInputBuffer[index] <= 'z') )); 
 
                    i++; // next token
                } 
                else {
                    //
                    // For the generic case its either a space, comma, semi-colon, etc.
                    //  the point is we really don't care, nor do we need to waste time 
                    //  worring about it (the orginal code did).   The point is we
                    //  care about the actual date information, So we just advance to the 
                    //  next lexume. 
                    //
 
                    index++;
                }
            }
 
            //
            // We're finished parsing the string, now take the parsed tokens 
            //  and turn them to the actual structured information we care about. 
            //  So we build lpSysTime from the Array, using a local if none is passed in.
            // 

            int year;
            int month;
            int day; 
            int hour;
            int minute; 
            int second; 
            int millisecond;
 
            millisecond =  0;

            if (fIsANSIDateFormat) {
                day    = rgdwDateParseResults[DATE_ANSI_INDEX_DAY]; 
                month  = rgdwDateParseResults[DATE_ANSI_INDEX_MONTH];
                hour   = rgdwDateParseResults[DATE_ANSI_INDEX_HRS]; 
                minute = rgdwDateParseResults[DATE_ANSI_INDEX_MINS]; 
                second = rgdwDateParseResults[DATE_ANSI_INDEX_SECS];
                if (iLastLettered != DATE_ANSI_INDEX_YEAR) { 
                    year   = rgdwDateParseResults[DATE_ANSI_INDEX_YEAR];
                }
                else {
                    // This is a fix to get around toString/toGMTstring (where the timezone is 
                    // appended at the end. (See above)
                    year   = rgdwDateParseResults[DATE_INDEX_TZ]; 
                } 
            }
            else { 
                day    = rgdwDateParseResults[DATE_1123_INDEX_DAY];
                month  = rgdwDateParseResults[DATE_1123_INDEX_MONTH];
                year   = rgdwDateParseResults[DATE_1123_INDEX_YEAR];
                hour   = rgdwDateParseResults[DATE_1123_INDEX_HRS]; 
                minute = rgdwDateParseResults[DATE_1123_INDEX_MINS];
                second = rgdwDateParseResults[DATE_1123_INDEX_SECS]; 
            } 

            // 
            // Normalize the year, 90 == 1990, handle the year 2000, 02 == 2002
            //  This is Year 2000 handling folks!!!  We get this wrong and
            //  we all look bad.
            // 

            if (year < 100) { 
                year += ((year < 80) ? 2000 : 1900); 
            }
 
            //
            // if we got misformed time, then plug in the current time
            // !lpszHrs || !lpszMins || !lpszSec
            // 

            if ((i < 4) 
                || (day > 31) 
                || (hour > 23)
                || (minute > 59) 
                || (second > 59)) {
                fRet = false;
                goto quit;
            } 

            // 
            // Now do the DateTime conversion 
            //
 
            dtOut = new DateTime (year, month, day, hour, minute, second, millisecond);

            //
            // we want the system time to be accurate. This is _suhlow_ 
            // The time passed in is in the local time zone; we have to convert this into GMT.
            // 
 
            if (iLastLettered==DATE_ANSI_INDEX_YEAR) {
                // this should be an unusual case. 
                dtOut = dtOut.ToUniversalTime();
            }

            // 
            // If we have an Offset to another Time Zone
            //   then convert to appropriate GMT time 
            // 

            if ((i > DATE_INDEX_TZ && 
                 rgdwDateParseResults[DATE_INDEX_TZ] != DATE_TOKEN_GMT)) {

                //
                // if we received +/-nnnn as offset (hhmm), modify the output FILETIME 
                //
 
                double offset; 

                offset = (double) rgdwDateParseResults[DATE_INDEX_TZ]; 
                dtOut.AddHours(offset);
            }

            // In the end, we leave it all in LocalTime 

            dtOut = dtOut.ToLocalTime(); 
 
            quit:
 
            return fRet;
        }

 
        public static  bool
        ParseCookieDate(string dateString, out DateTime dtOut) { 
            // 
            // The format variants
            // 
            // 1) .NET HttpCookie   = "dd-MMM-yyyy HH:mm:ss GMT'"
            // 2) Version0          = "dd-MMM-yy HH:mm:ss GMT"
            // 3) Some funky form   = "dd MMM yyyy HH:mm:ss GMT"
            // 
            // In all above cases we also accept single digit dd,hh,mm,ss
            // That's said what IE does. 
 
            dtOut = DateTime.MinValue;
            char[] buffer = dateString.ToCharArray(); 
            char ch;

            if (buffer.Length < 18) { //cover all before "ss" in the longest case
                return false; 
            }
 
            int idx = 0; 
            // Take the date
            int  day=0; 
            if (!Char.IsDigit(ch = buffer[idx++]))  {return false;}
            else                                    {day = ch-'0';}
            if (!Char.IsDigit(ch = buffer[idx++]))  {--idx;}                //one digit was used for a date
            else                                    {day = day*10 +(ch-'0');} 

 
            if (day > 31)  {return false;} 

            ++idx;  //ignore delimiter and position on Month 

            // Take the Month
            int month = MapDayMonthToDword(buffer, idx);
            if (month == DATE_TOKEN_ERROR)                   {return false;} 

            idx+=4; //position after Month and ignore delimiter 
 
            // Take the year
            int year=0; 
            int i;
            for (i=0; i < 4; ++i) {
                if (!Char.IsDigit(ch = buffer[i+idx])) {
                    // YY case 
                    if (i != 2)             {return false;}
                    else                    {break;} 
                } 
                year = year*10 + (ch-'0');
            } 

            //check for two digits
            if (i == 2) {
                year += ((year < 80) ? 2000 : 1900); 
            }
 
            i += idx;       //from now on 'i' is used as an index 
            if (buffer[i++] != ' ')             {return false;}
 
            //Take the hour
            int  hour=0;
            if (!Char.IsDigit(ch = buffer[i++])) {return false;}
            else                                 {hour = ch-'0';} 
            if (!Char.IsDigit(ch = buffer[i++])) {--i;}                     //accept single digit
            else                                 {hour = hour*10 +(ch-'0');} 
 
            if (hour > 24 || buffer[i++] != ':') {return false;}
 
            //Take the min
            int  min=0;
            if (!Char.IsDigit(ch = buffer[i++])) {return false;}
            else                                 {min = ch-'0';} 
            if (!Char.IsDigit(ch = buffer[i++])) {--i;}                     //accept single digit
            else                                 {min = min*10 +(ch-'0');} 
 
            if (min > 60 || buffer[i++] != ':')  {return false;}
 
            //Check that the rest will fit the buffer size "[s]s GMT"
            if ((buffer.Length - i) < 5)       {return false;}

            //Take the sec 
            int  sec=0;
            if (!Char.IsDigit(ch = buffer[i++])) {return false;} 
            else                                 {sec = ch-'0';} 
            if (!Char.IsDigit(ch = buffer[i++])) {--i;}                     //accept single digit
            else                                 {sec = sec*10 +(ch-'0');} 

            if (sec > 60 || buffer[i++] != ' ')  {return false;}

            //Test GMT 
            if((buffer.Length - i) < 3 || buffer[i++] != 'G' || buffer[i++] != 'M' || buffer[i++] != 'T') {
                return false; 
            } 

            dtOut = new DateTime (year, month, day, hour, min, sec, 0).ToLocalTime(); 
            return true;
        }
    }
 
} // namespace System.Net
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK