XsdDateTime.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Xml / System / Xml / schema / XsdDateTime.cs / 1305376 / XsdDateTime.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
 
namespace System.Xml.Schema { 
    using System;
    using System.Xml; 
    using System.Diagnostics;
    using System.Text;

    ///  
    /// This enum specifies what format should be used when converting string to XsdDateTime
    ///  
    [Flags] 
    internal enum XsdDateTimeFlags {
        DateTime        = 0x01, 
        Time            = 0x02,
        Date            = 0x04,
        GYearMonth      = 0x08,
        GYear           = 0x10, 
        GMonthDay       = 0x20,
        GDay            = 0x40, 
        GMonth          = 0x80, 
#if !SILVERLIGHT // XDR is not supported in Silverlight
        XdrDateTimeNoTz = 0x100, 
        XdrDateTime     = 0x200,
        XdrTimeNoTz     = 0x400,  //XDRTime with tz is the same as xsd:time
#endif
        AllXsd          = 0xFF //All still does not include the XDR formats 
    }
 
    ///  
    /// This structure extends System.DateTime to support timeInTicks zone and Gregorian types scomponents of an Xsd Duration.  It is used internally to support Xsd durations without loss
    /// of fidelity.  XsdDuration structures are immutable once they've been created. 
    /// 
    internal struct XsdDateTime {
        // DateTime is being used as an internal representation only
        // Casting XsdDateTime to DateTime might return a different value 
        private DateTime dt;
 
        // Additional information that DateTime is not preserving 
        // Information is stored in the following format:
        // Bits     Info 
        // 31-24    DateTimeTypeCode
        // 23-16    XsdDateTimeKind
        // 15-8     Zone Hours
        // 7-0      Zone Minutes 
        private uint extra;
 
 
        // Subset of XML Schema types XsdDateTime represents
        enum DateTimeTypeCode { 
            DateTime,
            Time,
            Date,
            GYearMonth, 
            GYear,
            GMonthDay, 
            GDay, 
            GMonth,
#if !SILVERLIGHT // XDR is not supported in Silverlight 
            XdrDateTime,
#endif
        }
 
        // Internal representation of DateTimeKind
        enum XsdDateTimeKind { 
            Unspecified, 
            Zulu,
            LocalWestOfZulu,    // GMT-1..14, N..Y 
            LocalEastOfZulu     // GMT+1..14, A..M
        }

        // Masks and shifts used for packing and unpacking extra 
        private const uint TypeMask = 0xFF000000;
        private const uint KindMask = 0x00FF0000; 
        private const uint ZoneHourMask = 0x0000FF00; 
        private const uint ZoneMinuteMask = 0x000000FF;
        private const int TypeShift = 24; 
        private const int KindShift = 16;
        private const int ZoneHourShift = 8;

        // Maximum number of fraction digits; 
        private const short maxFractionDigits = 7;
 
        static readonly int Lzyyyy         = "yyyy".Length; 
        static readonly int Lzyyyy_        = "yyyy-".Length;
        static readonly int Lzyyyy_MM      = "yyyy-MM".Length; 
        static readonly int Lzyyyy_MM_     = "yyyy-MM-".Length;
        static readonly int Lzyyyy_MM_dd   = "yyyy-MM-dd".Length;
        static readonly int Lzyyyy_MM_ddT  = "yyyy-MM-ddT".Length;
        static readonly int LzHH           = "HH".Length; 
        static readonly int LzHH_          = "HH:".Length;
        static readonly int LzHH_mm        = "HH:mm".Length; 
        static readonly int LzHH_mm_       = "HH:mm:".Length; 
        static readonly int LzHH_mm_ss     = "HH:mm:ss".Length;
        static readonly int Lz_            = "-".Length; 
        static readonly int Lz_zz          = "-zz".Length;
        static readonly int Lz_zz_         = "-zz:".Length;
        static readonly int Lz_zz_zz       = "-zz:zz".Length;
        static readonly int Lz__           = "--".Length; 
        static readonly int Lz__mm         = "--MM".Length;
        static readonly int Lz__mm_        = "--MM-".Length; 
        static readonly int Lz__mm__       = "--MM--".Length; 
        static readonly int Lz__mm_dd      = "--MM-dd".Length;
        static readonly int Lz___          = "---".Length; 
        static readonly int Lz___dd        = "---dd".Length;


#if !SILVERLIGHT 
        /// 
        /// Constructs an XsdDateTime from a string trying all possible formats. 
        ///  
        public XsdDateTime(string text) : this(text, XsdDateTimeFlags.AllXsd) {
        } 
#endif

        /// 
        /// Constructs an XsdDateTime from a string using specific format. 
        /// 
        public XsdDateTime(string text, XsdDateTimeFlags kinds) : this() { 
            Parser parser = new Parser(); 
            if (! parser.Parse(text, kinds)) {
                throw new FormatException(Res.GetString(Res.XmlConvert_BadFormat, text, kinds)); 
            }
            InitiateXsdDateTime(parser);
        }
 
#if !SILVERLIGHT
        private XsdDateTime(Parser parser) : this() { 
            InitiateXsdDateTime(parser); 
        }
#endif 

        private void InitiateXsdDateTime(Parser parser) {
            dt = new DateTime(parser.year, parser.month, parser.day, parser.hour, parser.minute, parser.second);
            if (parser.fraction != 0) { 
                dt = dt.AddTicks(parser.fraction);
            } 
            extra = (uint)(((int)parser.typeCode << TypeShift) | ((int)parser.kind << KindShift) | (parser.zoneHour << ZoneHourShift) | parser.zoneMinute); 
        }
 
#if !SILVERLIGHT
        internal static bool TryParse(string text, XsdDateTimeFlags kinds, out XsdDateTime result) {
            Parser parser = new Parser();
            if (! parser.Parse(text, kinds)) { 
                result = new XsdDateTime();
                return false; 
            } 
            result = new XsdDateTime(parser);
            return true; 
        }
#endif

        ///  
        /// Constructs an XsdDateTime from a DateTime.
        ///  
        public XsdDateTime(DateTime dateTime, XsdDateTimeFlags kinds) { 
            Debug.Assert(Bits.ExactlyOne((uint)kinds), "Only one DateTime type code can be set.");
            dt = dateTime; 

            DateTimeTypeCode code = (DateTimeTypeCode) (Bits.LeastPosition((uint) kinds) - 1);
            int zoneHour = 0;
            int zoneMinute = 0; 
            XsdDateTimeKind kind;
 
            switch (dateTime.Kind) { 
                case DateTimeKind.Unspecified: kind = XsdDateTimeKind.Unspecified; break;
                case DateTimeKind.Utc: kind = XsdDateTimeKind.Zulu; break; 

                default: {
                    Debug.Assert(dateTime.Kind == DateTimeKind.Local, "Unknown DateTimeKind: " + dateTime.Kind);
                    TimeSpan utcOffset = TimeZoneInfo.Local.GetUtcOffset(dateTime); 

                    if (utcOffset.Ticks < 0) { 
                        kind = XsdDateTimeKind.LocalWestOfZulu; 
                        zoneHour = -utcOffset.Hours;
                        zoneMinute = -utcOffset.Minutes; 
                    }
                    else {
                        kind = XsdDateTimeKind.LocalEastOfZulu;
                        zoneHour = utcOffset.Hours; 
                        zoneMinute = utcOffset.Minutes;
                    } 
                    break; 
                }
            } 

            extra = (uint)(((int)code << TypeShift) | ((int)kind << KindShift) | (zoneHour << ZoneHourShift) | zoneMinute);
        }
 
        // Constructs an XsdDateTime from a DateTimeOffset
        public XsdDateTime(DateTimeOffset dateTimeOffset) : this(dateTimeOffset, XsdDateTimeFlags.DateTime) { 
        } 

        public XsdDateTime(DateTimeOffset dateTimeOffset, XsdDateTimeFlags kinds) { 
            Debug.Assert(Bits.ExactlyOne((uint)kinds), "Only one DateTime type code can be set.");

            dt = dateTimeOffset.DateTime;
 
            TimeSpan zoneOffset = dateTimeOffset.Offset;
            DateTimeTypeCode code = (DateTimeTypeCode) (Bits.LeastPosition((uint) kinds) - 1); 
            XsdDateTimeKind kind; 
            if (zoneOffset.TotalMinutes < 0) {
                zoneOffset = zoneOffset.Negate(); 
                kind = XsdDateTimeKind.LocalWestOfZulu;
            }
            else if (zoneOffset.TotalMinutes > 0) {
                kind = XsdDateTimeKind.LocalEastOfZulu; 
            }
            else { 
                kind = XsdDateTimeKind.Zulu; 
            }
 
            extra = (uint)(((int)code << TypeShift) | ((int)kind << KindShift) | (zoneOffset.Hours << ZoneHourShift) | zoneOffset.Minutes);
        }

        ///  
        /// Returns auxiliary enumeration of XSD date type
        ///  
        private DateTimeTypeCode InternalTypeCode { 
            get { return (DateTimeTypeCode)((extra & TypeMask) >> TypeShift); }
        } 

        /// 
        /// Returns geographical "position" of the value
        ///  
        private XsdDateTimeKind InternalKind {
            get { return (XsdDateTimeKind)((extra & KindMask) >> KindShift); } 
        } 

#if !SILVERLIGHT 
        /// 
        /// Returns XmlTypeCode of the value being stored
        /// 
        public XmlTypeCode TypeCode { 
            get { return typeCodes[(int)InternalTypeCode]; }
        } 
 
        /// 
        /// Returns whether object represent local, UTC or unspecified time 
        /// 
        public DateTimeKind Kind {
            get {
                switch (InternalKind) { 
                case XsdDateTimeKind.Unspecified:
                    return DateTimeKind.Unspecified; 
                case XsdDateTimeKind.Zulu: 
                    return DateTimeKind.Utc;
                default: 
                    // XsdDateTimeKind.LocalEastOfZulu:
                    // XsdDateTimeKind.LocalWestOfZulu:
                    return DateTimeKind.Local;
                } 
            }
        } 
#endif 

        ///  
        /// Returns the year part of XsdDateTime
        /// The returned value is integer between 1 and 9999
        /// 
        public int Year { 
            get { return dt.Year; }
        } 
 
        /// 
        /// Returns the month part of XsdDateTime 
        /// The returned value is integer between 1 and 12
        /// 
        public int Month {
            get { return dt.Month; } 
        }
 
        ///  
        /// Returns the day of the month part of XsdDateTime
        /// The returned value is integer between 1 and 31 
        /// 
        public int Day {
            get { return dt.Day; }
        } 

        ///  
        /// Returns the hour part of XsdDateTime 
        /// The returned value is integer between 0 and 23
        ///  
        public int Hour {
            get { return dt.Hour; }
        }
 
        /// 
        /// Returns the minute part of XsdDateTime 
        /// The returned value is integer between 0 and 60 
        /// 
        public int Minute { 
            get { return dt.Minute; }
        }

        ///  
        /// Returns the second part of XsdDateTime
        /// The returned value is integer between 0 and 60 
        ///  
        public int Second {
            get { return dt.Second; } 
        }

        /// 
        /// Returns number of ticks in the fraction of the second 
        /// The returned value is integer between 0 and 9999999
        ///  
        public int Fraction { 
            get { return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks); }
        } 

        /// 
        /// Returns the hour part of the time zone
        /// The returned value is integer between -13 and 13 
        /// 
        public int ZoneHour { 
            get { 
                uint result = (extra & ZoneHourMask) >> ZoneHourShift;
                return (int)result; 
            }
        }

        ///  
        /// Returns the minute part of the time zone
        /// The returned value is integer between 0 and 60 
        ///  
        public int ZoneMinute {
            get { 
                uint result = (extra & ZoneMinuteMask);
                return (int)result;
            }
        } 

#if !SILVERLIGHT 
        public DateTime ToZulu() { 
            switch (InternalKind) {
                case XsdDateTimeKind.Zulu: 
                    // set it to UTC
                    return new DateTime(dt.Ticks, DateTimeKind.Utc);
                case XsdDateTimeKind.LocalEastOfZulu:
                    // Adjust to UTC and then convert to local in the current time zone 
                    return new DateTime(dt.Subtract(new TimeSpan(ZoneHour, ZoneMinute, 0)).Ticks, DateTimeKind.Utc);
                case XsdDateTimeKind.LocalWestOfZulu: 
                    // Adjust to UTC and then convert to local in the current time zone 
                    return new DateTime(dt.Add(new TimeSpan(ZoneHour, ZoneMinute, 0)).Ticks, DateTimeKind.Utc);
                default: 
                    return dt;
            }
        }
#endif 

        ///  
        /// Cast to DateTime 
        /// The following table describes the behaviors of getting the default value
        /// when a certain year/month/day values are missing. 
        ///
        /// An "X" means that the value exists.  And "--" means that value is missing.
        ///
        /// Year    Month   Day =>  ResultYear  ResultMonth     ResultDay       Note 
        ///
        /// X       X       X       Parsed year Parsed month    Parsed day 
        /// X       X       --      Parsed Year Parsed month    First day       If we have year and month, assume the first day of that month. 
        /// X       --      X       Parsed year First month     Parsed day      If the month is missing, assume first month of that year.
        /// X       --      --      Parsed year First month     First day       If we have only the year, assume the first day of that year. 
        ///
        /// --      X       X       CurrentYear Parsed month    Parsed day      If the year is missing, assume the current year.
        /// --      X       --      CurrentYear Parsed month    First day       If we have only a month value, assume the current year and current day.
        /// --      --      X       CurrentYear First month     Parsed day      If we have only a day value, assume current year and first month. 
        /// --      --      --      CurrentYear Current month   Current day     So this means that if the date string only contains time, you will get current date.
        ///  
        public static implicit operator DateTime(XsdDateTime xdt) { 
            DateTime result;
            switch (xdt.InternalTypeCode) { 
                case DateTimeTypeCode.GMonth:
                case DateTimeTypeCode.GDay:
                    result = new DateTime(DateTime.Now.Year, xdt.Month, xdt.Day);
                    break; 
                case DateTimeTypeCode.Time:
                    //back to DateTime.Now 
                    DateTime currentDateTime = DateTime.Now; 
                    TimeSpan addDiff = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day) - new DateTime(xdt.Year, xdt.Month, xdt.Day);
                    result = xdt.dt.Add(addDiff); 
                    break;
                default:
                    result = xdt.dt;
                    break; 
            }
 
            switch (xdt.InternalKind) { 
                case XsdDateTimeKind.Zulu:
                    // set it to UTC 
                    result = new DateTime(result.Ticks, DateTimeKind.Utc);
                    break;
                case XsdDateTimeKind.LocalEastOfZulu:
                    // Adjust to UTC and then convert to local in the current time zone 
                    try {
                        result = result.Subtract(new TimeSpan(xdt.ZoneHour, xdt.ZoneMinute, 0)); //NOTE new TimeSpan() will not throw ArgumentOutOfRange as the zone values have already been checked to be less than 99 
                    } 
                    catch(ArgumentOutOfRangeException) {
                        return new DateTime(DateTime.MinValue.Ticks, DateTimeKind.Local); 
                    }
                    result = new DateTime(result.Ticks, DateTimeKind.Utc).ToLocalTime();
                    break;
                case XsdDateTimeKind.LocalWestOfZulu: 
                    // Adjust to UTC and then convert to local in the current time zone
                    try { 
                        result = result.Add(new TimeSpan(xdt.ZoneHour, xdt.ZoneMinute, 0)); //NOTE new TimeSpan() will not throw ArgumentOutOfRange as the zone values have already been checked to be less than 99 
                    }
                    catch(ArgumentOutOfRangeException) { 
                        return new DateTime(DateTime.MaxValue.Ticks, DateTimeKind.Local);
                    }
                    result = new DateTime(result.Ticks, DateTimeKind.Utc).ToLocalTime();
                    break; 
                default:
                    break; 
            } 
            return result;
        } 

        public static implicit operator DateTimeOffset(XsdDateTime xdt) {
            DateTime dt;
 
            switch (xdt.InternalTypeCode) {
                case DateTimeTypeCode.GMonth: 
                case DateTimeTypeCode.GDay: 
                    dt = new DateTime( DateTime.Now.Year, xdt.Month, xdt.Day );
                    break; 
                case DateTimeTypeCode.Time:
                    //back to DateTime.Now
                    DateTime currentDateTime = DateTime.Now;
                    TimeSpan addDiff = new DateTime(currentDateTime.Year, currentDateTime.Month, currentDateTime.Day) - new DateTime(xdt.Year, xdt.Month, xdt.Day); 
                    dt = xdt.dt.Add( addDiff );
                    break; 
                default: 
                    dt = xdt.dt;
                    break; 
            }

            DateTimeOffset result;
            switch (xdt.InternalKind) { 
                case XsdDateTimeKind.LocalEastOfZulu:
                    result = new DateTimeOffset(dt, new TimeSpan(xdt.ZoneHour, xdt.ZoneMinute, 0)); 
                    break; 
                case XsdDateTimeKind.LocalWestOfZulu:
                    result = new DateTimeOffset(dt, new TimeSpan(-xdt.ZoneHour, -xdt.ZoneMinute, 0)); 
                    break;
                case XsdDateTimeKind.Zulu:
                    result = new DateTimeOffset(dt, new TimeSpan( 0 ) );
                    break; 
                case XsdDateTimeKind.Unspecified:
                default: 
                    result = new DateTimeOffset(dt, TimeZoneInfo.Local.GetUtcOffset(dt)); 
                    break;
            } 

            return result;
        }
 
#if !SILVERLIGHT
        ///  
        /// Compares two DateTime values, returning an integer that indicates 
        /// their relationship.
        ///  
        public static int Compare(XsdDateTime left, XsdDateTime right) {
            if (left.extra == right.extra) {
                return DateTime.Compare(left.dt, right.dt);
            } 
            else {
                // Xsd types should be the same for it to be comparable 
                if (left.InternalTypeCode != right.InternalTypeCode) { 
                    throw new ArgumentException(Res.GetString(Res.Sch_XsdDateTimeCompare, left.TypeCode, right.TypeCode));
                } 
                // Convert both to UTC
                return DateTime.Compare(left.GetZuluDateTime(), right.GetZuluDateTime());

            } 
        }
 
        // Compares this DateTime to a given object. This method provides an 
        // implementation of the IComparable interface. The object
        // argument must be another DateTime, or otherwise an exception 
        // occurs.  Null is considered less than any instance.
        //
        // Returns a value less than zero if this  object
        ///  
        public int CompareTo(Object value) {
            if (value == null) return 1; 
            return Compare(this, (XsdDateTime)value); 
        }
#endif 

        /// 
        /// Serialization to a string
        ///  
        public override string ToString() {
            StringBuilder sb = new StringBuilder(64); 
            char[] text; 
            switch (InternalTypeCode) {
            case DateTimeTypeCode.DateTime: 
                PrintDate(sb);
                sb.Append('T');
                PrintTime(sb);
                break; 
            case DateTimeTypeCode.Time:
                PrintTime(sb); 
                break; 
            case DateTimeTypeCode.Date:
                PrintDate(sb); 
                break;
            case DateTimeTypeCode.GYearMonth:
                text = new char[Lzyyyy_MM];
                IntToCharArray(text, 0, Year, 4); 
                text[Lzyyyy] = '-';
                ShortToCharArray(text, Lzyyyy_, Month); 
                sb.Append(text); 
                break;
            case DateTimeTypeCode.GYear: 
                text = new char[Lzyyyy];
                IntToCharArray(text, 0, Year, 4);
                sb.Append(text);
                break; 
            case DateTimeTypeCode.GMonthDay:
                text = new char[Lz__mm_dd]; 
                text[0] = '-'; 
                text[Lz_] = '-';
                ShortToCharArray(text, Lz__, Month); 
                text[Lz__mm] = '-';
                ShortToCharArray(text, Lz__mm_, Day);
                sb.Append(text);
                break; 
            case DateTimeTypeCode.GDay:
                text = new char[Lz___dd]; 
                text[0] = '-'; 
                text[Lz_] = '-';
                text[Lz__] = '-'; 
                ShortToCharArray(text, Lz___, Day);
                sb.Append(text);
                break;
            case DateTimeTypeCode.GMonth: 
                text = new char[Lz__mm__];
                text[0] = '-'; 
                text[Lz_] = '-'; 
                ShortToCharArray(text, Lz__, Month);
                text[Lz__mm] = '-'; 
                text[Lz__mm_] = '-';
                sb.Append(text);
                break;
            } 
            PrintZone(sb);
            return sb.ToString(); 
        } 

        // Serialize year, month and day 
        private void PrintDate(StringBuilder sb) {
            char[] text = new char[Lzyyyy_MM_dd];
            IntToCharArray(text, 0, Year, 4);
            text[Lzyyyy] = '-'; 
            ShortToCharArray(text, Lzyyyy_, Month);
            text[Lzyyyy_MM] = '-'; 
            ShortToCharArray(text, Lzyyyy_MM_, Day); 
            sb.Append(text);
        } 

        // Serialize hour, minute, second and fraction
        private void PrintTime(StringBuilder sb) {
            char[] text = new char[LzHH_mm_ss]; 
            ShortToCharArray(text, 0, Hour);
            text[LzHH] = ':'; 
            ShortToCharArray(text, LzHH_, Minute); 
            text[LzHH_mm] = ':';
            ShortToCharArray(text, LzHH_mm_, Second); 
            sb.Append(text);
            int fraction = Fraction;
            if (fraction != 0) {
                int fractionDigits = maxFractionDigits; 
                while (fraction % 10 == 0) {
                    fractionDigits --; 
                    fraction /= 10; 
                }
                text = new char[fractionDigits + 1]; 
                text[0] = '.';
                IntToCharArray(text, 1, fraction, fractionDigits);
                sb.Append(text);
            } 
        }
 
        // Serialize time zone 
        private void PrintZone(StringBuilder sb) {
            char[] text; 
            switch (InternalKind) {
            case XsdDateTimeKind.Zulu:
                sb.Append('Z');
                break; 
            case XsdDateTimeKind.LocalWestOfZulu:
                text = new char[Lz_zz_zz]; 
                text[0] = '-'; 
                ShortToCharArray(text, Lz_, ZoneHour);
                text[Lz_zz] = ':'; 
                ShortToCharArray(text, Lz_zz_, ZoneMinute);
                sb.Append(text);
                break;
            case XsdDateTimeKind.LocalEastOfZulu: 
                text = new char[Lz_zz_zz];
                text[0] = '+'; 
                ShortToCharArray(text, Lz_, ZoneHour); 
                text[Lz_zz] = ':';
                ShortToCharArray(text, Lz_zz_, ZoneMinute); 
                sb.Append(text);
                break;
            default:
                // do nothing 
                break;
            } 
        } 

        // Serialize integer into character array starting with index [start]. 
        // Number of digits is set by [digits]
        private void IntToCharArray(char[] text, int start, int value, int digits) {
            while(digits -- != 0) {
                text[start + digits] = (char)(value%10 + '0'); 
                value /= 10;
            } 
        } 

        // Serialize two digit integer into character array starting with index [start]. 
        private void ShortToCharArray(char[] text, int start, int value) {
            text[start] = (char)(value/10 + '0');
            text[start + 1] = (char)(value%10 + '0');
        } 

#if !SILVERLIGHT 
        // Auxiliary for compare. 
        // Returns UTC DateTime
        private DateTime GetZuluDateTime() { 
            switch (InternalKind) {
            case XsdDateTimeKind.Zulu:
                return dt;
            case XsdDateTimeKind.LocalEastOfZulu: 
                return dt.Subtract(new TimeSpan(ZoneHour, ZoneMinute, 0));
            case XsdDateTimeKind.LocalWestOfZulu: 
                return dt.Add(new TimeSpan(ZoneHour, ZoneMinute, 0)); 
            default:
                return dt.ToUniversalTime(); 
            }
        }
#endif
 
        private static readonly XmlTypeCode[] typeCodes = {
            XmlTypeCode.DateTime, 
            XmlTypeCode.Time, 
            XmlTypeCode.Date,
            XmlTypeCode.GYearMonth, 
            XmlTypeCode.GYear,
            XmlTypeCode.GMonthDay,
            XmlTypeCode.GDay,
            XmlTypeCode.GMonth 
        };
 
 
        // Parsing string according to XML schema spec
        struct Parser { 
            private const int leapYear = 1904;
            private const int firstMonth = 1;
            private const int firstDay = 1;
 
            public DateTimeTypeCode typeCode;
            public int year; 
            public int month; 
            public int day;
            public int hour; 
            public int minute;
            public int second;
            public int fraction;
            public XsdDateTimeKind kind; 
            public int zoneHour;
            public int zoneMinute; 
 
            private string text;
            private int length; 

            public bool Parse(string text, XsdDateTimeFlags kinds) {
                this.text = text;
                this.length = text.Length; 

                // Skip leading withitespace 
                int start = 0; 
                while(start < length && char.IsWhiteSpace(text[start])) {
                    start ++; 
                }
                // Choose format starting from the most common and trying not to reparse the same thing too many times

#if !SILVERLIGHT // XDR is not supported in Silverlight 
                if (Test(kinds, XsdDateTimeFlags.DateTime | XsdDateTimeFlags.Date | XsdDateTimeFlags.XdrDateTime | XsdDateTimeFlags.XdrDateTimeNoTz)) {
#else 
                if (Test(kinds, XsdDateTimeFlags.DateTime | XsdDateTimeFlags.Date)) { 
#endif
                    if (ParseDate(start)) { 
                        if (Test(kinds, XsdDateTimeFlags.DateTime)) {
                            if (ParseChar(start + Lzyyyy_MM_dd, 'T') && ParseTimeAndZoneAndWhitespace(start + Lzyyyy_MM_ddT)) {
                                typeCode = DateTimeTypeCode.DateTime;
                                return true; 
                            }
                        } 
                        if (Test(kinds, XsdDateTimeFlags.Date)) { 
                            if (ParseZoneAndWhitespace(start + Lzyyyy_MM_dd)) {
                                typeCode = DateTimeTypeCode.Date; 
                                return true;
                            }
                        }
#if !SILVERLIGHT // XDR is not supported in Silverlight 
                        if (Test(kinds, XsdDateTimeFlags.XdrDateTime)) {
                            if (ParseZoneAndWhitespace(start + Lzyyyy_MM_dd) || (ParseChar(start + Lzyyyy_MM_dd, 'T') && ParseTimeAndZoneAndWhitespace(start + Lzyyyy_MM_ddT)) ) { 
                                typeCode = DateTimeTypeCode.XdrDateTime; 
                                return true;
                            } 
                        }
                        if (Test(kinds, XsdDateTimeFlags.XdrDateTimeNoTz)) {
                            if (ParseChar(start + Lzyyyy_MM_dd, 'T')) {
                                if (ParseTimeAndWhitespace(start + Lzyyyy_MM_ddT)) { 
                                    typeCode = DateTimeTypeCode.XdrDateTime;
                                    return true; 
                                } 
                            }
                            else { 
                                typeCode = DateTimeTypeCode.XdrDateTime;
                                return true;
                            }
                        } 
#endif
                    } 
                } 

                if (Test(kinds, XsdDateTimeFlags.Time)) { 
                    if (ParseTimeAndZoneAndWhitespace(start)) { //Equivalent to NoCurrentDateDefault on DateTimeStyles while parsing xs:time
                        year = leapYear;
                        month = firstMonth;
                        day = firstDay; 
                        typeCode = DateTimeTypeCode.Time;
                        return true; 
                    } 
                }
 
#if !SILVERLIGHT // XDR is not supported in Silverlight
                if (Test(kinds, XsdDateTimeFlags.XdrTimeNoTz)) {
                    if (ParseTimeAndWhitespace(start)) { //Equivalent to NoCurrentDateDefault on DateTimeStyles while parsing xs:time
                        year = leapYear; 
                        month = firstMonth;
                        day = firstDay; 
                        typeCode = DateTimeTypeCode.Time; 
                        return true;
                    } 
                }
#endif

                if (Test(kinds, XsdDateTimeFlags.GYearMonth | XsdDateTimeFlags.GYear)) { 
                    if (Parse4Dig(start ,         ref year) && 1 <= year) {
                        if (Test(kinds, XsdDateTimeFlags.GYearMonth)) { 
                            if ( 
                                ParseChar(start + Lzyyyy,     '-') &&
                                Parse2Dig(start + Lzyyyy_,    ref month) && 1 <= month && month <= 12 && 
                                ParseZoneAndWhitespace(start + Lzyyyy_MM)
                            ) {
                                day = firstDay;
                                typeCode = DateTimeTypeCode.GYearMonth; 
                                return true;
                            } 
                        } 
                        if (Test(kinds, XsdDateTimeFlags.GYear)) {
                            if (ParseZoneAndWhitespace(start + Lzyyyy)) { 
                                month = firstMonth;
                                day = firstDay;
                                typeCode = DateTimeTypeCode.GYear;
                                return true; 
                            }
                        } 
                    } 
                }
                if (Test(kinds, XsdDateTimeFlags.GMonthDay | XsdDateTimeFlags.GMonth)) { 
                    if (
                        ParseChar(start ,         '-') &&
                        ParseChar(start + Lz_,    '-') &&
                        Parse2Dig(start + Lz__,   ref month) && 1 <= month && month <= 12 
                    ) {
                        if (Test(kinds, XsdDateTimeFlags.GMonthDay) && ParseChar(start + Lz__mm, '-')) { 
                            if ( 
                                Parse2Dig(start + Lz__mm_,     ref day) && 1 <= day && day <= DateTime.DaysInMonth(leapYear, month) &&
                                ParseZoneAndWhitespace(start + Lz__mm_dd) 
                            ) {
                                year = leapYear;
                                typeCode = DateTimeTypeCode.GMonthDay;
                                return true; 
                            }
                        } 
                        if (Test(kinds, XsdDateTimeFlags.GMonth)) { 
                            if (ParseZoneAndWhitespace(start + Lz__mm) || (ParseChar(start + Lz__mm, '-') && ParseChar(start + Lz__mm_, '-') && ParseZoneAndWhitespace(start + Lz__mm__)) ) {
                                year = leapYear; 
                                day = firstDay;
                                typeCode = DateTimeTypeCode.GMonth;
                                return true;
                            } 
                        }
                    } 
 
                }
                if (Test(kinds, XsdDateTimeFlags.GDay)) { 
                    if (
                        ParseChar(start ,            '-') &&
                        ParseChar(start + Lz_,       '-') &&
                        ParseChar(start + Lz__,      '-') && 
                        Parse2Dig(start + Lz___,     ref day) && 1 <= day && day <= DateTime.DaysInMonth(leapYear, firstMonth) &&
                        ParseZoneAndWhitespace(start + Lz___dd) 
 
                    ) {
                        year = leapYear; 
                        month = firstMonth;
                        typeCode = DateTimeTypeCode.GDay;
                        return true;
                    } 
                }
                return false; 
            } 

 
            private bool ParseDate(int start) {
                return
                    Parse4Dig(start ,         ref year) && 1 <= year &&
                    ParseChar(start + Lzyyyy,     '-') && 
                    Parse2Dig(start + Lzyyyy_,    ref month) && 1 <= month && month <= 12 &&
                    ParseChar(start + Lzyyyy_MM,  '-') && 
                    Parse2Dig(start + Lzyyyy_MM_, ref day) && 1 <= day && day <= DateTime.DaysInMonth(year, month); 
            }
 
            private bool ParseTimeAndZoneAndWhitespace(int start) {
                if (ParseTime(ref start)) {
                    if (ParseZoneAndWhitespace(start)) {
                        return true; 
                    }
                } 
                return false; 
            }
 
#if !SILVERLIGHT // XDR is not supported in Silverlight
            private bool ParseTimeAndWhitespace(int start) {
                if (ParseTime(ref start)) {
                    while(start < length ) {//&& char.IsWhiteSpace(text[start])) { 
                        start ++;
                    } 
                    return start == length; 
                }
                return false; 
            }
#endif

            static int[] Power10 = new int[maxFractionDigits] {-1, 10, 100, 1000, 10000, 100000, 1000000}; 
            private bool ParseTime(ref int start) {
                if ( 
                    Parse2Dig(start ,       ref hour) && hour < 24 && 
                    ParseChar(start + LzHH,     ':') &&
                    Parse2Dig(start + LzHH_,    ref minute) && minute < 60 && 
                    ParseChar(start + LzHH_mm,  ':') &&
                    Parse2Dig(start + LzHH_mm_, ref second) && second < 60
                ) {
                    start += LzHH_mm_ss; 
                    if (ParseChar(start, '.')) {
                        // Parse factional part of seconds 
                        // We allow any number of digits, but keep only first 7 
                        this.fraction = 0;
                        int fractionDigits = 0; 
                        int round = 0;
                        while (++start < length) {
                            int d = text[start] - '0';
                            if (9u < (uint) d) { // d < 0 || 9 < d 
                                break;
                            } 
                            if (fractionDigits < maxFractionDigits) { 
                                this.fraction = (this.fraction * 10) + d;
                            } else if (fractionDigits == maxFractionDigits) { 
                                if (5 < d) {
                                    round = 1;
                                } else if (d == 5) {
                                    round = -1; 
                                }
                            } else if (round < 0 && d != 0) { 
                                round = 1; 
                            }
                            fractionDigits ++; 
                        }
                        if (fractionDigits < maxFractionDigits) {
                            if (fractionDigits == 0) {
                                return false; // cannot end with . 
                            }
                            fraction *= Power10[maxFractionDigits - fractionDigits]; 
                        } else { 
                            if (round < 0) {
                                round = fraction & 1; 
                            }
                            fraction += round;
                        }
                    } 
                    return true;
                } 
                // cleanup - conflict with gYear 
                hour = 0;
                return false; 
            }

            private bool ParseZoneAndWhitespace(int start) {
                if (start < length) { 
                    char ch = text[start];
                    if (ch == 'Z' || ch == 'z') { 
                        kind = XsdDateTimeKind.Zulu; 
                        start ++;
                    } 
                    else if (start + 5 < length) {
                        if (
                            Parse2Dig(start + Lz_,       ref zoneHour) && zoneHour <= 99 &&
                            ParseChar(start + Lz_zz,     ':') && 
                            Parse2Dig(start + Lz_zz_,    ref zoneMinute) && zoneMinute <= 99
                        ) { 
                            if (ch == '-') { 
                                kind = XsdDateTimeKind.LocalWestOfZulu;
                                start += Lz_zz_zz; 
                            }
                            else if (ch == '+') {
                                kind = XsdDateTimeKind.LocalEastOfZulu;
                                start += Lz_zz_zz; 
                            }
                        } 
                    } 
                }
                while(start < length && char.IsWhiteSpace(text[start])) { 
                    start ++;
                }
                return start == length;
            } 

 
            private bool Parse4Dig(int start, ref int num) { 
                if (start + 3 < length) {
                    int d4 = text[start]     - '0'; 
                    int d3 = text[start + 1] - '0';
                    int d2 = text[start + 2] - '0';
                    int d1 = text[start + 3] - '0';
                    if (0 <= d4 && d4 < 10 && 
                        0 <= d3 && d3 < 10 &&
                        0 <= d2 && d2 < 10 && 
                        0 <= d1 && d1 < 10 
                    ) {
                        num = ((d4 * 10 + d3) * 10 + d2) * 10 + d1; 
                        return true;
                    }
                }
                return false; 
            }
 
            private bool Parse2Dig(int start, ref int num) { 
                if (start + 1 < length) {
                    int d2 = text[start]     - '0'; 
                    int d1 = text[start + 1] - '0';
                    if (0 <= d2 && d2 < 10 &&
                        0 <= d1 && d1 < 10
                        ) { 
                        num =  d2 * 10 + d1;
                        return true; 
                    } 
                }
                return false; 
            }

            private bool ParseChar(int start, char ch) {
                return start < length && text[start] == ch; 
            }
 
            private static bool Test(XsdDateTimeFlags left, XsdDateTimeFlags right) { 
                return (left & right) != 0;
            } 

        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

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