diff options
Diffstat (limited to 'src/mscorlib/src/System/Globalization/DateTimeFormat.cs')
-rw-r--r-- | src/mscorlib/src/System/Globalization/DateTimeFormat.cs | 1130 |
1 files changed, 0 insertions, 1130 deletions
diff --git a/src/mscorlib/src/System/Globalization/DateTimeFormat.cs b/src/mscorlib/src/System/Globalization/DateTimeFormat.cs deleted file mode 100644 index 1ace207952..0000000000 --- a/src/mscorlib/src/System/Globalization/DateTimeFormat.cs +++ /dev/null @@ -1,1130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System { - using System.Text; - using System.Threading; - using System.Globalization; - using System.Collections.Generic; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Runtime.Versioning; - using System.Security; - using System.Diagnostics; - using System.Diagnostics.Contracts; - - /* - Customized format patterns: - P.S. Format in the table below is the internal number format used to display the pattern. - - Patterns Format Description Example - ========= ========== ===================================== ======== - "h" "0" hour (12-hour clock)w/o leading zero 3 - "hh" "00" hour (12-hour clock)with leading zero 03 - "hh*" "00" hour (12-hour clock)with leading zero 03 - - "H" "0" hour (24-hour clock)w/o leading zero 8 - "HH" "00" hour (24-hour clock)with leading zero 08 - "HH*" "00" hour (24-hour clock) 08 - - "m" "0" minute w/o leading zero - "mm" "00" minute with leading zero - "mm*" "00" minute with leading zero - - "s" "0" second w/o leading zero - "ss" "00" second with leading zero - "ss*" "00" second with leading zero - - "f" "0" second fraction (1 digit) - "ff" "00" second fraction (2 digit) - "fff" "000" second fraction (3 digit) - "ffff" "0000" second fraction (4 digit) - "fffff" "00000" second fraction (5 digit) - "ffffff" "000000" second fraction (6 digit) - "fffffff" "0000000" second fraction (7 digit) - - "F" "0" second fraction (up to 1 digit) - "FF" "00" second fraction (up to 2 digit) - "FFF" "000" second fraction (up to 3 digit) - "FFFF" "0000" second fraction (up to 4 digit) - "FFFFF" "00000" second fraction (up to 5 digit) - "FFFFFF" "000000" second fraction (up to 6 digit) - "FFFFFFF" "0000000" second fraction (up to 7 digit) - - "t" first character of AM/PM designator A - "tt" AM/PM designator AM - "tt*" AM/PM designator PM - - "d" "0" day w/o leading zero 1 - "dd" "00" day with leading zero 01 - "ddd" short weekday name (abbreviation) Mon - "dddd" full weekday name Monday - "dddd*" full weekday name Monday - - - "M" "0" month w/o leading zero 2 - "MM" "00" month with leading zero 02 - "MMM" short month name (abbreviation) Feb - "MMMM" full month name Febuary - "MMMM*" full month name Febuary - - "y" "0" two digit year (year % 100) w/o leading zero 0 - "yy" "00" two digit year (year % 100) with leading zero 00 - "yyy" "D3" year 2000 - "yyyy" "D4" year 2000 - "yyyyy" "D5" year 2000 - ... - - "z" "+0;-0" timezone offset w/o leading zero -8 - "zz" "+00;-00" timezone offset with leading zero -08 - "zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -07:30 - "zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00 - - "K" -Local "zzz", e.g. -08:00 - -Utc "'Z'", representing UTC - -Unspecified "" - -DateTimeOffset "zzzzz" e.g -07:30:15 - - "g*" the current era name A.D. - - ":" time separator : -- DEPRECATED - Insert separator directly into pattern (eg: "H.mm.ss") - "/" date separator /-- DEPRECATED - Insert separator directly into pattern (eg: "M-dd-yyyy") - "'" quoted string 'ABC' will insert ABC into the formatted string. - '"' quoted string "ABC" will insert ABC into the formatted string. - "%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year. - "\" escaped character E.g. '\d' insert the character 'd' into the format string. - other characters insert the character into the format string. - - Pre-defined format characters: - (U) to indicate Universal time is used. - (G) to indicate Gregorian calendar is used. - - Format Description Real format Example - ========= ================================= ====================== ======================= - "d" short date culture-specific 10/31/1999 - "D" long data culture-specific Sunday, October 31, 1999 - "f" full date (long date + short time) culture-specific Sunday, October 31, 1999 2:00 AM - "F" full date (long date + long time) culture-specific Sunday, October 31, 1999 2:00:00 AM - "g" general date (short date + short time) culture-specific 10/31/1999 2:00 AM - "G" general date (short date + long time) culture-specific 10/31/1999 2:00:00 AM - "m"/"M" Month/Day date culture-specific October 31 -(G) "o"/"O" Round Trip XML "yyyy-MM-ddTHH:mm:ss.fffffffK" 1999-10-31 02:00:00.0000000Z -(G) "r"/"R" RFC 1123 date, "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" Sun, 31 Oct 1999 10:00:00 GMT -(G) "s" Sortable format, based on ISO 8601. "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00 - ('T' for local time) - "t" short time culture-specific 2:00 AM - "T" long time culture-specific 2:00:00 AM -(G) "u" Universal time with sortable format, "yyyy'-'MM'-'dd HH':'mm':'ss'Z'" 1999-10-31 10:00:00Z - based on ISO 8601. -(U) "U" Universal time with full culture-specific Sunday, October 31, 1999 10:00:00 AM - (long date + long time) format - "y"/"Y" Year/Month day culture-specific October, 1999 - - */ - - //This class contains only static members and does not require the serializable attribute. - internal static - class DateTimeFormat { - - internal const int MaxSecondsFractionDigits = 7; - internal static readonly TimeSpan NullOffset = TimeSpan.MinValue; - - internal static char[] allStandardFormats = - { - 'd', 'D', 'f', 'F', 'g', 'G', - 'm', 'M', 'o', 'O', 'r', 'R', - 's', 't', 'T', 'u', 'U', 'y', 'Y', - }; - - internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK"; - internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz"; - - private const int DEFAULT_ALL_DATETIMES_SIZE = 132; - - internal static readonly DateTimeFormatInfo InvariantFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; - internal static readonly string[] InvariantAbbreviatedMonthNames = InvariantFormatInfo.AbbreviatedMonthNames; - internal static readonly string[] InvariantAbbreviatedDayNames = InvariantFormatInfo.AbbreviatedDayNames; - internal const string Gmt = "GMT"; - - internal static String[] fixedNumberFormats = new String[] { - "0", - "00", - "000", - "0000", - "00000", - "000000", - "0000000", - }; - - //////////////////////////////////////////////////////////////////////////// - // - // Format the positive integer value to a string and perfix with assigned - // length of leading zero. - // - // Parameters: - // value: The value to format - // len: The maximum length for leading zero. - // If the digits of the value is greater than len, no leading zero is added. - // - // Notes: - // The function can format to Int32.MaxValue. - // - //////////////////////////////////////////////////////////////////////////// - internal static void FormatDigits(StringBuilder outputBuffer, int value, int len) { - Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); - FormatDigits(outputBuffer, value, len, false); - } - - internal unsafe static void FormatDigits(StringBuilder outputBuffer, int value, int len, bool overrideLengthLimit) { - Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0"); - - // Limit the use of this function to be two-digits, so that we have the same behavior - // as RTM bits. - if (!overrideLengthLimit && len > 2) - { - len = 2; - } - - char* buffer = stackalloc char[16]; - char* p = buffer + 16; - int n = value; - do { - *--p = (char)(n % 10 + '0'); - n /= 10; - } while ((n != 0)&&(p > buffer)); - - int digits = (int) (buffer + 16 - p); - - //If the repeat count is greater than 0, we're trying - //to emulate the "00" format, so we have to prepend - //a zero if the string only has one character. - while ((digits < len) && (p > buffer)) { - *--p='0'; - digits++; - } - outputBuffer.Append(p, digits); - } - - private static void HebrewFormatDigits(StringBuilder outputBuffer, int digits) { - outputBuffer.Append(HebrewNumber.ToString(digits)); - } - - internal static int ParseRepeatPattern(String format, int pos, char patternChar) - { - int len = format.Length; - int index = pos + 1; - while ((index < len) && (format[index] == patternChar)) - { - index++; - } - return (index - pos); - } - - private static String FormatDayOfWeek(int dayOfWeek, int repeat, DateTimeFormatInfo dtfi) - { - Debug.Assert(dayOfWeek >= 0 && dayOfWeek <= 6, "dayOfWeek >= 0 && dayOfWeek <= 6"); - if (repeat == 3) - { - return (dtfi.GetAbbreviatedDayName((DayOfWeek)dayOfWeek)); - } - // Call dtfi.GetDayName() here, instead of accessing DayNames property, because we don't - // want a clone of DayNames, which will hurt perf. - return (dtfi.GetDayName((DayOfWeek)dayOfWeek)); - } - - private static String FormatMonth(int month, int repeatCount, DateTimeFormatInfo dtfi) - { - Debug.Assert(month >=1 && month <= 12, "month >=1 && month <= 12"); - if (repeatCount == 3) - { - return (dtfi.GetAbbreviatedMonthName(month)); - } - // Call GetMonthName() here, instead of accessing MonthNames property, because we don't - // want a clone of MonthNames, which will hurt perf. - return (dtfi.GetMonthName(month)); - } - - // - // FormatHebrewMonthName - // - // Action: Return the Hebrew month name for the specified DateTime. - // Returns: The month name string for the specified DateTime. - // Arguments: - // time the time to format - // month The month is the value of HebrewCalendar.GetMonth(time). - // repeat Return abbreviated month name if repeat=3, or full month name if repeat=4 - // dtfi The DateTimeFormatInfo which uses the Hebrew calendars as its calendar. - // Exceptions: None. - // - - /* Note: - If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this: - 1 Hebrew 1st Month - 2 Hebrew 2nd Month - .. ... - 6 Hebrew 6th Month - 7 Hebrew 6th Month II (used only in a leap year) - 8 Hebrew 7th Month - 9 Hebrew 8th Month - 10 Hebrew 9th Month - 11 Hebrew 10th Month - 12 Hebrew 11th Month - 13 Hebrew 12th Month - - Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7. - */ - private static String FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi) - { - Debug.Assert(repeatCount != 3 || repeatCount != 4, "repeateCount should be 3 or 4"); - if (dtfi.Calendar.IsLeapYear(dtfi.Calendar.GetYear(time))) { - // This month is in a leap year - return (dtfi.internalGetMonthName(month, MonthNameStyles.LeapYear, (repeatCount == 3))); - } - // This is in a regular year. - if (month >= 7) { - month++; - } - if (repeatCount == 3) { - return (dtfi.GetAbbreviatedMonthName(month)); - } - return (dtfi.GetMonthName(month)); - } - - // - // The pos should point to a quote character. This method will - // append to the result StringBuilder the string encloed by the quote character. - // - internal static int ParseQuoteString(String format, int pos, StringBuilder result) - { - // - // NOTE : pos will be the index of the quote character in the 'format' string. - // - int formatLen = format.Length; - int beginPos = pos; - char quoteChar = format[pos++]; // Get the character used to quote the following string. - - bool foundQuote = false; - while (pos < formatLen) - { - char ch = format[pos++]; - if (ch == quoteChar) - { - foundQuote = true; - break; - } - else if (ch == '\\') { - // The following are used to support escaped character. - // Escaped character is also supported in the quoted string. - // Therefore, someone can use a format like "'minute:' mm\"" to display: - // minute: 45" - // because the second double quote is escaped. - if (pos < formatLen) { - result.Append(format[pos++]); - } else { - // - // This means that '\' is at the end of the formatting string. - // - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - } else { - result.Append(ch); - } - } - - if (!foundQuote) - { - // Here we can't find the matching quote. - throw new FormatException( - String.Format( - CultureInfo.CurrentCulture, - Environment.GetResourceString("Format_BadQuote"), quoteChar)); - } - - // - // Return the character count including the begin/end quote characters and enclosed string. - // - return (pos - beginPos); - } - - // - // Get the next character at the index of 'pos' in the 'format' string. - // Return value of -1 means 'pos' is already at the end of the 'format' string. - // Otherwise, return value is the int value of the next character. - // - internal static int ParseNextChar(String format, int pos) - { - if (pos >= format.Length - 1) - { - return (-1); - } - return ((int)format[pos+1]); - } - - // - // IsUseGenitiveForm - // - // Actions: Check the format to see if we should use genitive month in the formatting. - // Starting at the position (index) in the (format) string, look back and look ahead to - // see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use - // genitive form. Genitive form is not used if there is more than two "d". - // Arguments: - // format The format string to be scanned. - // index Where we should start the scanning. This is generally where "M" starts. - // tokenLen The len of the current pattern character. This indicates how many "M" that we have. - // patternToMatch The pattern that we want to search. This generally uses "d" - // - private static bool IsUseGenitiveForm(String format, int index, int tokenLen, char patternToMatch) { - int i; - int repeat = 0; - // - // Look back to see if we can find "d" or "ddd" - // - - // Find first "d". - for (i = index - 1; i >= 0 && format[i] != patternToMatch; i--) { /*Do nothing here */ }; - - if (i >= 0) { - // Find a "d", so look back to see how many "d" that we can find. - while (--i >= 0 && format[i] == patternToMatch) { - repeat++; - } - // - // repeat == 0 means that we have one (patternToMatch) - // repeat == 1 means that we have two (patternToMatch) - // - if (repeat <= 1) { - return (true); - } - // Note that we can't just stop here. We may find "ddd" while looking back, and we have to look - // ahead to see if there is "d" or "dd". - } - - // - // If we can't find "d" or "dd" by looking back, try look ahead. - // - - // Find first "d" - for (i = index + tokenLen; i < format.Length && format[i] != patternToMatch; i++) { /* Do nothing here */ }; - - if (i < format.Length) { - repeat = 0; - // Find a "d", so contine the walk to see how may "d" that we can find. - while (++i < format.Length && format[i] == patternToMatch) { - repeat++; - } - // - // repeat == 0 means that we have one (patternToMatch) - // repeat == 1 means that we have two (patternToMatch) - // - if (repeat <= 1) { - return (true); - } - } - return (false); - } - - - // - // FormatCustomized - // - // Actions: Format the DateTime instance using the specified format. - // - private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) { - - - Calendar cal = dtfi.Calendar; - StringBuilder result = StringBuilderCache.Acquire(); - // This is a flag to indicate if we are format the dates using Hebrew calendar. - - bool isHebrewCalendar = (cal.ID == Calendar.CAL_HEBREW); - // This is a flag to indicate if we are formating hour/minute/second only. - bool bTimeOnly = true; - - int i = 0; - int tokenLen, hour12; - - while (i < format.Length) { - char ch = format[i]; - int nextChar; - switch (ch) { - case 'g': - tokenLen = ParseRepeatPattern(format, i, ch); - result.Append(dtfi.GetEraName(cal.GetEra(dateTime))); - break; - case 'h': - tokenLen = ParseRepeatPattern(format, i, ch); - hour12 = dateTime.Hour % 12; - if (hour12 == 0) - { - hour12 = 12; - } - FormatDigits(result, hour12, tokenLen); - break; - case 'H': - tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(result, dateTime.Hour, tokenLen); - break; - case 'm': - tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(result, dateTime.Minute, tokenLen); - break; - case 's': - tokenLen = ParseRepeatPattern(format, i, ch); - FormatDigits(result, dateTime.Second, tokenLen); - break; - case 'f': - case 'F': - tokenLen = ParseRepeatPattern(format, i, ch); - if (tokenLen <= MaxSecondsFractionDigits) { - long fraction = (dateTime.Ticks % Calendar.TicksPerSecond); - fraction = fraction / (long)Math.Pow(10, 7 - tokenLen); - if (ch == 'f') { - result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture)); - } - else { - int effectiveDigits = tokenLen; - while (effectiveDigits > 0) { - if (fraction % 10 == 0) { - fraction = fraction / 10; - effectiveDigits--; - } - else { - break; - } - } - if (effectiveDigits > 0) { - result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture)); - } - else { - // No fraction to emit, so see if we should remove decimal also. - if (result.Length > 0 && result[result.Length - 1] == '.') { - result.Remove(result.Length - 1, 1); - } - } - } - } else { - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - break; - case 't': - tokenLen = ParseRepeatPattern(format, i, ch); - if (tokenLen == 1) - { - if (dateTime.Hour < 12) - { - if (dtfi.AMDesignator.Length >= 1) - { - result.Append(dtfi.AMDesignator[0]); - } - } - else - { - if (dtfi.PMDesignator.Length >= 1) - { - result.Append(dtfi.PMDesignator[0]); - } - } - - } - else - { - result.Append((dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator)); - } - break; - case 'd': - // - // tokenLen == 1 : Day of month as digits with no leading zero. - // tokenLen == 2 : Day of month as digits with leading zero for single-digit months. - // tokenLen == 3 : Day of week as a three-leter abbreviation. - // tokenLen >= 4 : Day of week as its full name. - // - tokenLen = ParseRepeatPattern(format, i, ch); - if (tokenLen <= 2) - { - int day = cal.GetDayOfMonth(dateTime); - if (isHebrewCalendar) { - // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. - HebrewFormatDigits(result, day); - } else { - FormatDigits(result, day, tokenLen); - } - } - else - { - int dayOfWeek = (int)cal.GetDayOfWeek(dateTime); - result.Append(FormatDayOfWeek(dayOfWeek, tokenLen, dtfi)); - } - bTimeOnly = false; - break; - case 'M': - // - // tokenLen == 1 : Month as digits with no leading zero. - // tokenLen == 2 : Month as digits with leading zero for single-digit months. - // tokenLen == 3 : Month as a three-letter abbreviation. - // tokenLen >= 4 : Month as its full name. - // - tokenLen = ParseRepeatPattern(format, i, ch); - int month = cal.GetMonth(dateTime); - if (tokenLen <= 2) - { - if (isHebrewCalendar) { - // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values. - HebrewFormatDigits(result, month); - } else { - FormatDigits(result, month, tokenLen); - } - } - else { - if (isHebrewCalendar) { - result.Append(FormatHebrewMonthName(dateTime, month, tokenLen, dtfi)); - } else { - if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && tokenLen >= 4) { - result.Append( - dtfi.internalGetMonthName( - month, - IsUseGenitiveForm(format, i, tokenLen, 'd')? MonthNameStyles.Genitive : MonthNameStyles.Regular, - false)); - } else { - result.Append(FormatMonth(month, tokenLen, dtfi)); - } - } - } - bTimeOnly = false; - break; - case 'y': - // Notes about OS behavior: - // y: Always print (year % 100). No leading zero. - // yy: Always print (year % 100) with leading zero. - // yyy/yyyy/yyyyy/... : Print year value. No leading zero. - - int year = cal.GetYear(dateTime); - tokenLen = ParseRepeatPattern(format, i, ch); - if (dtfi.HasForceTwoDigitYears) { - FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2); - } - else if (cal.ID == Calendar.CAL_HEBREW) { - HebrewFormatDigits(result, year); - } - else { - if (tokenLen <= 2) { - FormatDigits(result, year % 100, tokenLen); - } - else { - String fmtPattern = "D" + tokenLen.ToString(); - result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture)); - } - } - bTimeOnly = false; - break; - case 'z': - tokenLen = ParseRepeatPattern(format, i, ch); - FormatCustomizedTimeZone(dateTime, offset, format, tokenLen, bTimeOnly, result); - break; - case 'K': - tokenLen = 1; - FormatCustomizedRoundripTimeZone(dateTime, offset, result); - break; - case ':': - result.Append(dtfi.TimeSeparator); - tokenLen = 1; - break; - case '/': - result.Append(dtfi.DateSeparator); - tokenLen = 1; - break; - case '\'': - case '\"': - tokenLen = ParseQuoteString(format, i, result); - break; - case '%': - // Optional format character. - // For example, format string "%d" will print day of month - // without leading zero. Most of the cases, "%" can be ignored. - nextChar = ParseNextChar(format, i); - // nextChar will be -1 if we already reach the end of the format string. - // Besides, we will not allow "%%" appear in the pattern. - if (nextChar >= 0 && nextChar != (int)'%') { - result.Append(FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi, offset)); - tokenLen = 2; - } - else - { - // - // This means that '%' is at the end of the format string or - // "%%" appears in the format string. - // - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - break; - case '\\': - // Escaped character. Can be used to insert character into the format string. - // For exmple, "\d" will insert the character 'd' into the string. - // - // NOTENOTE : we can remove this format character if we enforce the enforced quote - // character rule. - // That is, we ask everyone to use single quote or double quote to insert characters, - // then we can remove this character. - // - nextChar = ParseNextChar(format, i); - if (nextChar >= 0) - { - result.Append(((char)nextChar)); - tokenLen = 2; - } - else - { - // - // This means that '\' is at the end of the formatting string. - // - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - break; - default: - // NOTENOTE : we can remove this rule if we enforce the enforced quote - // character rule. - // That is, if we ask everyone to use single quote or double quote to insert characters, - // then we can remove this default block. - result.Append(ch); - tokenLen = 1; - break; - } - i += tokenLen; - } - return StringBuilderCache.GetStringAndRelease(result); - - } - - - // output the 'z' famliy of formats, which output a the offset from UTC, e.g. "-07:30" - private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, String format, Int32 tokenLen, Boolean timeOnly, StringBuilder result) { - // See if the instance already has an offset - Boolean dateTimeFormat = (offset == NullOffset); - if (dateTimeFormat) { - // No offset. The instance is a DateTime and the output should be the local time zone - - if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay) { - // For time only format and a time only input, the time offset on 0001/01/01 is less - // accurate than the system's current offset because of daylight saving time. - offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime); - } else if (dateTime.Kind == DateTimeKind.Utc) { - offset = TimeSpan.Zero; - } else { - offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); - } - } - if (offset >= TimeSpan.Zero) { - result.Append('+'); - } - else { - result.Append('-'); - // get a positive offset, so that you don't need a separate code path for the negative numbers. - offset = offset.Negate(); - } - - if (tokenLen <= 1) { - // 'z' format e.g "-7" - result.AppendFormat(CultureInfo.InvariantCulture, "{0:0}", offset.Hours); - } - else { - // 'zz' or longer format e.g "-07" - result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}", offset.Hours); - if (tokenLen >= 3) { - // 'zzz*' or longer format e.g "-07:30" - result.AppendFormat(CultureInfo.InvariantCulture, ":{0:00}", offset.Minutes); - } - } - } - - // output the 'K' format, which is for round-tripping the data - private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result) { - - // The objective of this format is to round trip the data in the type - // For DateTime it should round-trip the Kind value and preserve the time zone. - // DateTimeOffset instance, it should do so by using the internal time zone. - - if (offset == NullOffset) { - // source is a date time, so behavior depends on the kind. - switch (dateTime.Kind) { - case DateTimeKind.Local: - // This should output the local offset, e.g. "-07:30" - offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime); - // fall through to shared time zone output code - break; - case DateTimeKind.Utc: - // The 'Z' constant is a marker for a UTC date - result.Append("Z"); - return; - default: - // If the kind is unspecified, we output nothing here - return; - } - } - if (offset >= TimeSpan.Zero) { - result.Append('+'); - } - else { - result.Append('-'); - // get a positive offset, so that you don't need a separate code path for the negative numbers. - offset = offset.Negate(); - } - - AppendNumber(result, offset.Hours, 2); - result.Append(':'); - AppendNumber(result, offset.Minutes, 2); - } - - - internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) - { - String realFormat = null; - - switch (format[0]) - { - case 'd': // Short Date - realFormat = dtfi.ShortDatePattern; - break; - case 'D': // Long Date - realFormat = dtfi.LongDatePattern; - break; - case 'f': // Full (long date + short time) - realFormat = dtfi.LongDatePattern + " " + dtfi.ShortTimePattern; - break; - case 'F': // Full (long date + long time) - realFormat = dtfi.FullDateTimePattern; - break; - case 'g': // General (short date + short time) - realFormat = dtfi.GeneralShortTimePattern; - break; - case 'G': // General (short date + long time) - realFormat = dtfi.GeneralLongTimePattern; - break; - case 'm': - case 'M': // Month/Day Date - realFormat = dtfi.MonthDayPattern; - break; - case 'o': - case 'O': - realFormat = RoundtripFormat; - break; - case 'r': - case 'R': // RFC 1123 Standard - realFormat = dtfi.RFC1123Pattern; - break; - case 's': // Sortable without Time Zone Info - realFormat = dtfi.SortableDateTimePattern; - break; - case 't': // Short Time - realFormat = dtfi.ShortTimePattern; - break; - case 'T': // Long Time - realFormat = dtfi.LongTimePattern; - break; - case 'u': // Universal with Sortable format - realFormat = dtfi.UniversalSortableDateTimePattern; - break; - case 'U': // Universal with Full (long date + long time) format - realFormat = dtfi.FullDateTimePattern; - break; - case 'y': - case 'Y': // Year/Month Date - realFormat = dtfi.YearMonthPattern; - break; - default: - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - return (realFormat); - } - - - // Expand a pre-defined format string (like "D" for long date) to the real format that - // we are going to use in the date time parsing. - // This method also convert the dateTime if necessary (e.g. when the format is in Universal time), - // and change dtfi if necessary (e.g. when the format should use invariant culture). - // - private static String ExpandPredefinedFormat(String format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) { - switch (format[0]) { - case 'o': - case 'O': // Round trip format - dtfi = DateTimeFormatInfo.InvariantInfo; - break; - case 'r': - case 'R': // RFC 1123 Standard - if (offset != NullOffset) { - // Convert to UTC invariants mean this will be in range - dateTime = dateTime - offset; - } - else if (dateTime.Kind == DateTimeKind.Local) { - InvalidFormatForLocal(format, dateTime); - } - dtfi = DateTimeFormatInfo.InvariantInfo; - break; - case 's': // Sortable without Time Zone Info - dtfi = DateTimeFormatInfo.InvariantInfo; - break; - case 'u': // Universal time in sortable format. - if (offset != NullOffset) { - // Convert to UTC invariants mean this will be in range - dateTime = dateTime - offset; - } - else if (dateTime.Kind == DateTimeKind.Local) { - - InvalidFormatForLocal(format, dateTime); - } - dtfi = DateTimeFormatInfo.InvariantInfo; - break; - case 'U': // Universal time in culture dependent format. - if (offset != NullOffset) { - // This format is not supported by DateTimeOffset - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - } - // Universal time is always in Greogrian calendar. - // - // Change the Calendar to be Gregorian Calendar. - // - dtfi = (DateTimeFormatInfo)dtfi.Clone(); - if (dtfi.Calendar.GetType() != typeof(GregorianCalendar)) { - dtfi.Calendar = GregorianCalendar.GetDefaultInstance(); - } - dateTime = dateTime.ToUniversalTime(); - break; - } - format = GetRealFormat(format, dtfi); - return (format); - } - - internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi) { - return Format(dateTime, format, dtfi, NullOffset); - } - - - internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset) - { - Contract.Requires(dtfi != null); - if (format==null || format.Length==0) { - Boolean timeOnlySpecialCase = false; - if (dateTime.Ticks < Calendar.TicksPerDay) { - // If the time is less than 1 day, consider it as time of day. - // Just print out the short time format. - // - // This is a workaround for VB, since they use ticks less then one day to be - // time of day. In cultures which use calendar other than Gregorian calendar, these - // alternative calendar may not support ticks less than a day. - // For example, Japanese calendar only supports date after 1868/9/8. - // This will pose a problem when people in VB get the time of day, and use it - // to call ToString(), which will use the general format (short date + long time). - // Since Japanese calendar does not support Gregorian year 0001, an exception will be - // thrown when we try to get the Japanese year for Gregorian year 0001. - // Therefore, the workaround allows them to call ToString() for time of day from a DateTime by - // formatting as ISO 8601 format. - switch (dtfi.Calendar.ID) { - case Calendar.CAL_JAPAN: - case Calendar.CAL_TAIWAN: - case Calendar.CAL_HIJRI: - case Calendar.CAL_HEBREW: - case Calendar.CAL_JULIAN: - case Calendar.CAL_UMALQURA: - case Calendar.CAL_PERSIAN: - timeOnlySpecialCase = true; - dtfi = DateTimeFormatInfo.InvariantInfo; - break; - } - } - if (offset == NullOffset) { - // Default DateTime.ToString case. - if (timeOnlySpecialCase) { - format = "s"; - } - else { - format = "G"; - } - } - else { - // Default DateTimeOffset.ToString case. - if (timeOnlySpecialCase) { - format = RoundtripDateTimeUnfixed; - } - else { - format = dtfi.DateTimeOffsetPattern; - } - - } - - } - - if (format.Length == 1) { - switch (format[0]) - { - case 'O': - case 'o': - return FastFormatRoundtrip(dateTime, offset); - case 'R': - case 'r': - return FastFormatRfc1123(dateTime, offset, dtfi); - } - - format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); - } - - return (FormatCustomized(dateTime, format, dtfi, offset)); - } - - internal static string FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi) - { - // ddd, dd MMM yyyy HH:mm:ss GMT - const int Rfc1123FormatLength = 29; - StringBuilder result = StringBuilderCache.Acquire(Rfc1123FormatLength); - - if (offset != NullOffset) - { - // Convert to UTC invariants - dateTime = dateTime - offset; - } - - result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]); - result.Append(','); - result.Append(' '); - AppendNumber(result, dateTime.Day, 2); - result.Append(' '); - result.Append(InvariantAbbreviatedMonthNames[dateTime.Month - 1]); - result.Append(' '); - AppendNumber(result, dateTime.Year, 4); - result.Append(' '); - AppendHHmmssTimeOfDay(result, dateTime); - result.Append(' '); - result.Append(Gmt); - - return StringBuilderCache.GetStringAndRelease(result); - } - - internal static string FastFormatRoundtrip(DateTime dateTime, TimeSpan offset) - { - // yyyy-MM-ddTHH:mm:ss.fffffffK - const int roundTripFormatLength = 28; - StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength); - - AppendNumber(result, dateTime.Year, 4); - result.Append('-'); - AppendNumber(result, dateTime.Month, 2); - result.Append('-'); - AppendNumber(result, dateTime.Day, 2); - result.Append('T'); - AppendHHmmssTimeOfDay(result, dateTime); - result.Append('.'); - - long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond; - AppendNumber(result, fraction, 7); - - FormatCustomizedRoundripTimeZone(dateTime, offset, result); - - return StringBuilderCache.GetStringAndRelease(result); - } - - private static void AppendHHmmssTimeOfDay(StringBuilder result, DateTime dateTime) - { - // HH:mm:ss - AppendNumber(result, dateTime.Hour, 2); - result.Append(':'); - AppendNumber(result, dateTime.Minute, 2); - result.Append(':'); - AppendNumber(result, dateTime.Second, 2); - } - - internal static void AppendNumber(StringBuilder builder, long val, int digits) - { - for (int i = 0; i < digits; i++) - { - builder.Append('0'); - } - - int index = 1; - while (val > 0 && index <= digits) - { - builder[builder.Length - index] = (char)('0' + (val % 10)); - val = val / 10; - index++; - } - - Debug.Assert(val == 0, "DateTimeFormat.AppendNumber(): digits less than size of val"); - } - - internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi) - { - Contract.Requires(dtfi != null); - String [] allFormats = null; - String [] results = null; - - switch (format) - { - case 'd': - case 'D': - case 'f': - case 'F': - case 'g': - case 'G': - case 'm': - case 'M': - case 't': - case 'T': - case 'y': - case 'Y': - allFormats = dtfi.GetAllDateTimePatterns(format); - results = new String[allFormats.Length]; - for (int i = 0; i < allFormats.Length; i++) - { - results[i] = Format(dateTime, allFormats[i], dtfi); - } - break; - case 'U': - DateTime universalTime = dateTime.ToUniversalTime(); - allFormats = dtfi.GetAllDateTimePatterns(format); - results = new String[allFormats.Length]; - for (int i = 0; i < allFormats.Length; i++) - { - results[i] = Format(universalTime, allFormats[i], dtfi); - } - break; - // - // The following ones are special cases because these patterns are read-only in - // DateTimeFormatInfo. - // - case 'r': - case 'R': - case 'o': - case 'O': - case 's': - case 'u': - results = new String[] {Format(dateTime, new String(format, 1), dtfi)}; - break; - default: - throw new FormatException(Environment.GetResourceString("Format_InvalidString")); - - } - return (results); - } - - internal static String[] GetAllDateTimes(DateTime dateTime, DateTimeFormatInfo dtfi) - { - List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE); - - for (int i = 0; i < allStandardFormats.Length; i++) - { - String[] strings = GetAllDateTimes(dateTime, allStandardFormats[i], dtfi); - for (int j = 0; j < strings.Length; j++) - { - results.Add(strings[j]); - } - } - String[] value = new String[results.Count]; - results.CopyTo(0, value, 0, results.Count); - return (value); - } - - // This is a placeholder for an MDA to detect when the user is using a - // local DateTime with a format that will be interpreted as UTC. - internal static void InvalidFormatForLocal(String format, DateTime dateTime) { - } - - - } -} |