فصل ۶: Formatting، Parsing و Format Stringها در .NET

فصل ۶: Formatting، Parsing و Format Stringها در .NET

فصل ۶: Formatting، Parsing و Format Stringها در .NET

  • همیشه می‌توانید بین UTC و Local Time روی همان Computer به‌صورت قابل‌اعتماد Round-trip کنید، حتی هنگامی که Clockها به عقب برمی‌گردند.

Method با نام IsDaylightSavingTime مشخص می‌کند آیا یک DateTime Local داده‌شده تحت Daylight Saving Time قرار دارد یا نه. Timeهای UTC همیشه false برمی‌گردانند:

Console.Write (DateTime.Now.IsDaylightSavingTime());    // True or False
Console.Write (DateTime.UtcNow.IsDaylightSavingTime()); // Always False

اگر dto از نوع DateTimeOffset باشد، Expression زیر همان کار را انجام می‌دهد:

dto.LocalDateTime.IsDaylightSavingTime

پایان Daylight Saving Time برای Algorithmهایی که از Local Time استفاده می‌کنند پیچیدگی خاصی ایجاد می‌کند، زیرا وقتی Clockها به عقب می‌روند همان Hour، یا دقیق‌تر همان Delta، دوباره تکرار می‌شود.

Formatting و Parsing

Formatting یعنی تبدیل به String؛ Parsing یعنی تبدیل از String. در Programming، نیاز به Format یا Parse کردن در موقعیت‌های گوناگون بسیار زیاد پیش می‌آید. به همین دلیل .NET چند Mechanism ارائه می‌کند:

ToString و Parse
این Methodها Functionality پیش‌فرض را برای بسیاری از Typeها فراهم می‌کنند.
Format Providerها
این‌ها به شکل Methodهای اضافی ToString و Parse ظاهر می‌شوند که Format String و/یا Format Provider می‌پذیرند. Format Providerها بسیار انعطاف‌پذیر و Culture-aware هستند. .NET برای Typeهای Numeric و DateTime/DateTimeOffset Format Provider دارد.
XmlConvert
کلاسی Static با Methodهایی برای Formatting و Parsing در حالی که Standardهای XML را رعایت می‌کند. XmlConvert برای Conversion عمومی نیز مفید است، زمانی که Culture Independence لازم دارید یا می‌خواهید از Misparsing جلوگیری کنید. از Typeهای Numeric، bool، DateTime، DateTimeOffset، TimeSpan و Guid پشتیبانی می‌کند.
Type Converterها
این‌ها برای Designerها و XAML Parserها طراحی شده‌اند.

در این بخش دو Mechanism اول را بررسی می‌کنیم و به‌طور ویژه روی Format Providerها تمرکز داریم. سپس XmlConvert، Type Converterها و سایر Mechanismهای Conversion را توضیح می‌دهیم.

ToString و Parse

ساده‌ترین Mechanism برای Formatting، Method با نام ToString است. این Method روی همهٔ Value Typeهای ساده، شامل bool، DateTime، DateTimeOffset، TimeSpan، Guid و تمام Typeهای Numeric، Output معنادار می‌دهد. برای Operation معکوس، هر یک از این Typeها Method Static با نام Parse تعریف می‌کنند:

string s = true.ToString();         // s = "True"
bool b = bool.Parse (s);            // b = true

اگر Parsing شکست بخورد FormatException Throw می‌شود. بسیاری از Typeها Method با نام TryParse نیز دارند که در صورت شکست Conversion به‌جای Throw کردن Exception مقدار false برمی‌گرداند:

bool failure = int.TryParse ("qwerty", out int i1);
bool success = int.TryParse ("123", out int i2);

اگر Output برایتان مهم نیست و فقط می‌خواهید موفق بودن Parsing را Test کنید، می‌توانید از Discard استفاده کنید:

bool success = int.TryParse ("123", out int _);

اگر احتمال Error را می‌دهید، فراخوانی TryParse سریع‌تر و تمیزتر از Parse درون Block مربوط به Exception Handling است.

Methodهای Parse و TryParse روی DateTime(Offset) و Typeهای Numeric، Local Culture Settingها را رعایت می‌کنند؛ با مشخص کردن Object از نوع CultureInfo می‌توانید این رفتار را تغییر دهید. استفاده از Invariant Culture اغلب Idea خوبی است. برای مثال Parse کردن "1.234" به double در Germany مقدار 1234 می‌دهد:

Console.WriteLine (double.Parse ("1.234"));         // 1234   (In Germany)

دلیل این است که در Germany، Period نقش Thousands Separator دارد نه Decimal Point. تعیین Invariant Culture مشکل را برطرف می‌کند:

double x = double.Parse ("1.234", CultureInfo.InvariantCulture);

در فراخوانی ToString() نیز همین Rule صدق می‌کند:

string x = 1.234.ToString (CultureInfo.InvariantCulture);

Format Providerها

گاهی به کنترل بیشتری بر نحوهٔ Formatting و Parsing نیاز دارید. برای نمونه ده‌ها روش برای Format کردن DateTime(Offset) وجود دارد. Format Providerها کنترل گسترده‌ای بر Formatting و Parsing می‌دهند و برای Typeهای Numeric و Date/Time پشتیبانی می‌شوند.

Controlهای User Interface نیز از Format Providerها برای Formatting و Parsing استفاده می‌کنند.

Gateway استفاده از Format Provider، Interface با نام IFormattable است. تمام Typeهای Numeric و DateTime(Offset) این Interface را Implement می‌کنند:

public interface IFormattable
{
  string ToString (string format, IFormatProvider formatProvider);
}

Argument اول Format String و Argument دوم Format Provider است. Format String Instructionها را می‌دهد و Format Provider تعیین می‌کند این Instructionها چگونه Translate شوند. برای مثال:

NumberFormatInfo f = new NumberFormatInfo();
f.CurrencySymbol = "$$";
Console.WriteLine (3.ToString ("C", f));                // $$ 3.00

در اینجا "C" Format Stringای است که Currency را نشان می‌دهد و Object با نام NumberFormatInfo Format Providerای است که نحوهٔ Render شدن Currency و سایر Representationهای Numeric را تعیین می‌کند. این Mechanism امکان Globalization را فراهم می‌کند.

اگر Format String یا Provider را null قرار دهید، Default اعمال می‌شود. Format Provider پیش‌فرض CultureInfo.CurrentCulture است که اگر Reassign نشده باشد Settings Control Panel در Runtime روی Computer را منعکس می‌کند. برای مثال روی این Computer:

Console.WriteLine (10.3.ToString ("C", null));       // $10.30

برای راحتی، بیشتر Typeها ToString را Overload می‌کنند تا بتوانید Provider با مقدار null را حذف کنید:

Console.WriteLine (10.3.ToString ("C"));          // $10.30
Console.WriteLine (10.3.ToString ("F4"));         // 10.3000 (Fix to 4 D.P.)

فراخوانی ToString روی DateTime(Offset) یا Type Numeric بدون Argument، معادل استفاده از Format Provider پیش‌فرض همراه Format String خالی است.

.NET سه Format Provider تعریف می‌کند که همه IFormatProvider را Implement می‌کنند:

NumberFormatInfo
DateTimeFormatInfo
CultureInfo

Format Providerها و CultureInfo

در Context مربوط به Format Providerها، CultureInfo برای دو Format Provider دیگر نقش Mechanism غیرمستقیم را دارد و Object از نوع NumberFormatInfo یا DateTimeFormatInfo متناسب با Regional Settingهای Culture را برمی‌گرداند.

در Example زیر Culture مشخصی، یعنی زبان انگلیسی در Great Britain، درخواست می‌کنیم:

CultureInfo uk = CultureInfo.GetCultureInfo ("en-GB");
Console.WriteLine (3.ToString ("C", uk));      // £3.00

این کد با Object پیش‌فرض NumberFormatInfo مربوط به Culture با نام en-GB اجرا می‌شود.

Example بعدی یک DateTime را با Invariant Culture Format می‌کند. Invariant Culture مستقل از Settings Computer همیشه یکسان است:

DateTime dt = new DateTime (2000, 1, 2);
CultureInfo iv = CultureInfo.InvariantCulture;
Console.WriteLine (dt.ToString (iv));                  // 01/02/2000 00:00:00
Console.WriteLine (dt.ToString ("d", iv));             // 01/02/2000

استفاده از NumberFormatInfo یا DateTimeFormatInfo

در Example بعدی یک NumberFormatInfo می‌سازیم و Group Separator را از Comma به Space تغییر می‌دهیم. سپس با آن یک Number را تا سه Decimal Place Format می‌کنیم:

NumberFormatInfo f = new NumberFormatInfo ();
f.NumberGroupSeparator = " ";
Console.WriteLine (12345.6789.ToString ("N3", f));          // 12 345.679

Settingهای اولیهٔ NumberFormatInfo یا DateTimeFormatInfo بر Invariant Culture بنا شده‌اند. گاهی بهتر است Starting Point دیگری انتخاب کنید. برای این کار می‌توانید Format Provider موجود را Clone کنید:

NumberFormatInfo f = (NumberFormatInfo)
                     CultureInfo.CurrentCulture.NumberFormat.Clone();

Format Provider Clone‌شده همیشه Writable است، حتی اگر Original Read-only باشد.

Composite Formatting

Composite Format Stringها اجازه می‌دهند Variable Substitution را با Format String ترکیب کنید. Method Static با نام string.Format یک Composite Format String می‌پذیرد؛ این موضوع در بخش «String.Format and composite format strings» در صفحهٔ 296 نشان داده شد:

string composite = "Credit={0:C}";
Console.WriteLine (string.Format (composite, 500));      // Credit=$500.00

خود کلاس Console Methodهای Write و WriteLine را برای پذیرش Composite Format String Overload می‌کند و اجازه می‌دهد Example کمی کوتاه‌تر شود:

Console.WriteLine ("Credit={0:C}", 500);     // Credit=$500.00

همچنین می‌توانید Composite Format String را از طریق AppendFormat به StringBuilder و برای I/O به TextWriter Append کنید؛ فصل ۱۵ را ببینید.

string.Format Format Provider اختیاری می‌پذیرد. کاربرد سادهٔ آن فراخوانی ToString روی Object دلخواه همراه با Format Provider است:

string s = string.Format (CultureInfo.InvariantCulture, "{0}", someObject);

این کد معادل مورد زیر است:

string s;
if (someObject is IFormattable)
  s = ((IFormattable)someObject).ToString (null,
                                           CultureInfo.InvariantCulture);
else if (someObject == null)
  s = "";
else
  s = someObject.ToString();

Parsing با Format Providerها

Interface استانداردی برای Parsing از طریق Format Provider وجود ندارد. در عوض هر Type مشارکت‌کننده، Method Static با نام Parse و TryParse را Overload می‌کند تا Format Provider و به‌صورت اختیاری Enum از نوع NumberStyles یا DateTimeStyles بپذیرد.

NumberStyles و DateTimeStyles نحوهٔ Parsing را کنترل می‌کنند؛ برای مثال اجازه می‌دهند مشخص کنید Parenthesis یا Currency Symbol می‌تواند در Input String وجود داشته باشد یا نه. به‌صورت پیش‌فرض Answer هر دو مورد «نه» است. برای مثال:

int error = int.Parse ("(2)");     // Exception thrown
int minusTwo = int.Parse ("(2)", NumberStyles.Integer |
                                 NumberStyles.AllowParentheses);        // OK
decimal fivePointTwo = decimal.Parse ("£5.20", NumberStyles.Currency,
                       CultureInfo.GetCultureInfo ("en-GB"));

بخش بعدی تمام Memberهای NumberStyles و DateTimeStyles و Ruleهای پیش‌فرض Parsing برای هر Type را List می‌کند.

IFormatProvider و ICustomFormatter

تمام Format Providerها IFormatProvider را Implement می‌کنند:

public interface IFormatProvider { object GetFormat (Type formatType); }

هدف این Method فراهم کردن Indirection است؛ همین Mechanism به CultureInfo اجازه می‌دهد کار را به Object مناسب از نوع NumberFormatInfo یا DateTimeFormatInfo بسپارد.

با Implement کردن IFormatProvider همراه با ICustomFormatter می‌توانید Format Provider سفارشی بنویسید که در کنار Typeهای موجود کار کند. ICustomFormatter یک Method تعریف می‌کند:

string Format (string format, object arg, IFormatProvider formatProvider);

Format Provider سفارشی زیر Numberها را به Word تبدیل می‌کند:

public class WordyFormatProvider : IFormatProvider, ICustomFormatter
{
  static readonly string[] _numberWords =
   "zero one two three four five six seven eight nine minus point".Split();

  IFormatProvider _parent;           // Allows consumers to chain format providers

  public WordyFormatProvider () : this (CultureInfo.CurrentCulture) { }
  public WordyFormatProvider (IFormatProvider parent) => _parent = parent;

  public object GetFormat (Type formatType)
  {
    if (formatType == typeof (ICustomFormatter)) return this;
    return null;
  }

  public string Format (string format, object arg, IFormatProvider prov)
  {
    // If it's not our format string, defer to the parent provider:
    if (arg == null || format != "W")
      return string.Format (_parent, "{0:" + format + "}", arg);

    StringBuilder result = new StringBuilder();
    string digitList = string.Format (CultureInfo.InvariantCulture,
                                       "{0}", arg);
    foreach (char digit in digitList)
    {
      int i = "0123456789-.".IndexOf (digit,
                                       StringComparison.InvariantCulture);
      if (i == -1) continue;
      if (result.Length > 0) result.Append (' ');
      result.Append (_numberWords[i]);
    }
    return result.ToString();
  }
}

توجه کنید که در Method با نام Format برای تبدیل Input Number به String از string.Format همراه InvariantCulture استفاده کردیم. ساده‌تر بود فقط ToString() را روی arg فراخوانی کنیم، اما در آن حالت CurrentCulture استفاده می‌شد. دلیل نیاز به Invariant Culture چند Line بعد روشن می‌شود:

int i = "0123456789-.".IndexOf (digit, StringComparison.InvariantCulture);

در اینجا حیاتی است که Number String فقط Characterهای 0123456789-. را داشته باشد و نه Versionهای Internationalized آن‌ها.

Example استفاده از WordyFormatProvider:

double n = -123.45;
IFormatProvider fp = new WordyFormatProvider();
Console.WriteLine (string.Format (fp, "{0:C} in words is {0:W}", n));
// -$123.45 in words is minus one two three point four five

Custom Format Providerها فقط در Composite Format Stringها قابل استفاده‌اند.

Standard Format Stringها و Parsing Flagها

Standard Format Stringها نحوهٔ تبدیل Type Numeric یا DateTime/DateTimeOffset به String را کنترل می‌کنند. دو نوع Format String وجود دارد:

Standard Format String
در این حالت Guidance عمومی می‌دهید. Standard Format String از یک Letter و به‌صورت اختیاری یک Digit تشکیل می‌شود که Meaning آن به Letter بستگی دارد. Example: "C" یا "F2".
Custom Format String
در این حالت هر Character را با Template دقیق کنترل می‌کنید. Example: "0:#.000E+00".

Custom Format Stringها ارتباطی با Custom Format Providerها ندارند.

Numeric Format Stringها

Table 6-2 تمام Standard Numeric Format Stringها را List می‌کند.

جدول 6-2 — Standard Numeric Format Stringها
LetterMeaningSample inputResultNotes
G یا gGeneral1.2345, "G"
0.00001, "G"
0.00001, "g"
1.2345, "G3"
12345, "G3"
1.2345
1E-05
1e-05
1.23
1.23E04
برای Numberهای کوچک یا بزرگ به Exponential Notation می‌رود. G3 Precision را در مجموع، قبل و بعد از Point، به سه Digit محدود می‌کند.
FFixed point2345.678, "F2"
2345.6, "F2"
2345.68
2345.60
F2 تا دو Decimal Place Round می‌کند.
ادامهٔ جدول 6-2 — Standard Numeric Format Stringها
LetterMeaningSample inputResultNotes
NFixed point با Group Separator (Numeric)2345.678, "N2"
2345.6, "N2"
2,345.68
2,345.60
مانند بالا همراه Group Separator هزارگان؛ Detail از Format Provider گرفته می‌شود.
DPad با Leading Zero123, "D5"
123, "D1"
00123
123
فقط برای Integral Type. D5 سمت چپ را تا پنج Digit Pad می‌کند و Truncate نمی‌کند.
E یا eاجبار Exponential Notation56789, "E"
56789, "e"
56789, "E2"
5.678900E+004
5.678900e+004
5.68E+004
Precision پیش‌فرض شش Digit.
CCurrency1.2, "C"
1.2, "C4"
$1.20
$1.2000
C بدون Digit از Count پیش‌فرض Decimal Place در Format Provider استفاده می‌کند.
PPercent.503, "P"
.503, "P0"
50.30%
50%
Symbol و Layout از Format Provider گرفته می‌شود. Decimal Place را می‌توان Override کرد.
X یا xHexadecimal47, "X"
47, "x"
47, "X4"
2F
2f
002F
X برای Hex Digit Uppercase و x برای Lowercase؛ فقط Integralها.
R یا G9/G17Round-trip1f / 3f, "R"0.333333343برای BigInteger از R، برای double از G17 و برای float از G9 استفاده کنید.

ندادن Numeric Format String، یا دادن String با مقدار null یا خالی، معادل استفاده از Standard Format String با مقدار "G" بدون Digit است. این رفتار را دارد:

  • Numberهای کوچک‌تر از 10-4 یا بزرگ‌تر از Precision Type با Exponential یا Scientific Notation نمایش داده می‌شوند.
  • دو Decimal Place در Limit مربوط به Precision نوع float یا double Round می‌شوند تا Inaccuracy ناشی از Conversion به Decimal از Representation باینری زیربنایی پنهان شود.

Table 6-3، Custom Numeric Format Stringها را List می‌کند.

جدول 6-3 — Custom Numeric Format Stringها
SpecifierMeaningSample inputResultNotes
#Digit placeholder12.345, ".##"
12.345, ".####"
12.35
12.345
Digitهای بعد از Decimal Point را محدود می‌کند.
0Zero placeholder12.345, ".00"
12.345, ".0000"
99, "000.00"
12.35
12.3450
099.00
مانند بالا، اما قبل و بعد از Decimal Point نیز با Zero Pad می‌کند.
.Decimal pointDecimal Point را نشان می‌دهد؛ Symbol واقعی از NumberFormatInfo می‌آید.
,Group separator1234, "#,###,###"
1234, "0,000,000"
1,234
0,001,234
Symbol از NumberFormatInfo می‌آید.
, (همان بالا)Multiplier1000000, "#,"
1000000, "#,,"
1000
1
اگر Comma در انتها یا پیش از Decimal Point باشد به‌عنوان Multiplier عمل می‌کند و Result را بر 1,000، 1,000,000 و غیره تقسیم می‌کند.
%Percent notation0.6, "00%"60%ابتدا در 100 ضرب می‌کند و سپس Percent Symbol گرفته‌شده از NumberFormatInfo را جایگزین می‌کند.
E0, e0, E+0, e+0, E-0, e-0Exponent notation1234, "0E0"
1234, "0E+0"
1234, "0.00E00"
1234, "0.00e00"
1E3
1E+3
1.23E03
1.23e03
\Literal character quote50, @"\#0"#50همراه Prefix با علامت @ روی String استفاده کنید، یا از \\ استفاده کنید.
'xx' / "xx"Literal string quote50, "0 '...'"50 ...
;Section separator15, "#;(#);zero"
-5, "#;(#);zero"
0, "#;(#);zero"
15
(5)
zero
به‌ترتیب برای Positive، Negative و Zero.
هر Character دیگرLiteral35.2, "$0 . 00c"$35 . 20c

NumberStyles

هر Numeric Type یک Method Static با نام Parse تعریف می‌کند که Argument از نوع NumberStyles می‌پذیرد. NumberStyles یک Flags Enum است که اجازه می‌دهد تعیین کنید String هنگام Conversion به Numeric Type چگونه خوانده شود. Memberهای قابل‌ترکیب آن عبارت‌اند از:

AllowLeadingWhite         AllowTrailingWhite
AllowLeadingSign          AllowTrailingSign
AllowParentheses          AllowDecimalPoint
AllowThousands            AllowExponent
AllowCurrencySymbol       AllowHexSpecifier

NumberStyles همچنین Memberهای Composite زیر را تعریف می‌کند:

None   Integer    Float    Number HexNumber    Currency   Any

به‌جز None، تمام Composite Valueها شامل AllowLeadingWhite و AllowTrailingWhite هستند. Figure 6-1 بقیهٔ Composition آن‌ها را نشان می‌دهد و سه مورد مفیدتر با تأکید نمایش داده شده‌اند.

Composite NumberStylesجدول برداری بازسازی‌شده از شکل 6-1 که Flagهای تشکیل‌دهندهٔ Integer، Float، Number، HexNumber، Currency و Any را نشان می‌دهد.AllowLeadingSignAllowTrailingSignAllowParenthesesAllowDecimalPointAllowThousandsAllowExponentAllowCurrencySymbolAllowHexSpecifierIntegerFloatNumberHexNumberCurrencyAny
شکل 6-1 — ترکیب Compositeهای NumberStyles.

وقتی Parse را بدون مشخص کردن Flag فراخوانی می‌کنید، Defaultهای نشان‌داده‌شده در Figure 6-2 اعمال می‌شوند.

Default parsing flags for numeric typesجدول برداری بازسازی‌شده از شکل 6-2 که Flagهای پیش‌فرض Parsing برای Integral types، double/float و decimal را نشان می‌دهد.Default parsing flagsAllowLeadingSignAllowTrailingSignAllowParenthesesAllowDecimalPointAllowThousandsAllowExponentAllowCurrencySymbolAllowHexSpecifierIntegral typesIntegerdouble and floatFloat |AllowThousandsdecimalNumber
شکل 6-2 — Flagهای پیش‌فرض Parsing برای Numeric Typeها.

اگر Defaultهای Figure 6-2 را نمی‌خواهید، باید NumberStyles را به‌صورت صریح مشخص کنید:

int thousand = int.Parse ("3E8", NumberStyles.HexNumber);
int minusTwo = int.Parse ("(2)", NumberStyles.Integer |
                                 NumberStyles.AllowParentheses);
double aMillion = double.Parse ("1,000,000", NumberStyles.Any);
decimal threeMillion = decimal.Parse ("3e6", NumberStyles.Any);
decimal fivePointTwo = decimal.Parse ("$5.20", NumberStyles.Currency);

چون Format Provider مشخص نکردیم، این Example با Local Currency Symbol، Group Separator، Decimal Point و سایر Settingهای شما کار می‌کند. Example بعدی به‌صورت Hardcode برای Euro Sign و Group Separator خالی در Currencyها تنظیم شده است:

NumberFormatInfo ni = new NumberFormatInfo();
ni.CurrencySymbol = "€";
ni.CurrencyGroupSeparator = " ";
double million = double.Parse ("€1 000 000", NumberStyles.Currency, ni);

Date/Time Format Stringها

Format Stringهای DateTime/DateTimeOffset بر اساس اینکه Culture و Format Provider Settingها را رعایت کنند یا نه به دو گروه تقسیم می‌شوند. Table 6-4 موارد Culture-sensitive و Table 6-5 موارد Culture-insensitive را List می‌کند. Sample Output با Format کردن DateTime زیر تولید شده است؛ برای Table 6-4 از Invariant Culture استفاده شده:

new DateTime (2000, 1, 2, 17, 18, 19);
جدول 6-4 — Date/Time Format Stringهای حساس به Culture
Format stringMeaningSample output
dShort date01/02/2000
DLong dateSunday, 02 January 2000
tShort time17:18
TLong time17:18:19
fLong date + short timeSunday, 02 January 2000 17:18
FLong date + long timeSunday, 02 January 2000 17:18:19
gShort date + short time01/02/2000 17:18
G (default)Short date + long time01/02/2000 17:18:19
m, MMonth and day02 January
y, YYear and monthJanuary 2000
جدول 6-5 — Date/Time Format Stringهای مستقل از Culture
Format stringMeaningSample outputNotes
oRound-trippable2000-01-02T17:18:19.0000000اگر DateTimeKind برابر Unspecified نباشد Time Zone Information را Append می‌کند.
r, RRFC 1123 standardSun, 02 Jan 2000 17:18:19 GMTباید با DateTime.ToUniversalTime به‌صورت صریح به UTC تبدیل کنید.
sSortable; ISO 86012000-01-02T17:18:19با Text-based Sorting سازگار است.
uUniversal sortable2000-01-02 17:18:19Zمشابه بالا؛ باید صریحاً به UTC تبدیل شود.
UUTCSunday, 02 January 2000 17:18:19Long date + short time، با Conversion به UTC.

Format Stringهای "r"، "R" و "u" Suffixای Emit می‌کنند که UTC را القا می‌کند، اما Local DateTime را به‌صورت خودکار به UTC تبدیل نمی‌کنند؛ بنابراین خودتان باید Conversion را انجام دهید. به‌شکل کنایه‌آمیزی "U" به‌طور خودکار به UTC تبدیل می‌کند اما Suffix مربوط به Time Zone را نمی‌نویسد! در واقع "o" تنها Format Specifier این گروه است که بدون Intervention می‌تواند یک DateTime بدون Ambiguity بنویسد.

DateTimeFormatInfo از Custom Format Stringها نیز پشتیبانی می‌کند که مشابه Custom Numeric Format Stringها هستند. List بسیار طولانی است و در Documentation آنلاین Microsoft موجود است. Example:

yyyy-MM-dd HH:mm:ss

Parsing و Misparsing مربوط به DateTime

Stringهایی که Month یا Day را اول می‌آورند Ambiguous هستند و به‌سادگی ممکن است Misparse شوند، به‌خصوص اگر Customerهای Global داشته باشید. در Controlهای UI این مشکل وجود ندارد، زیرا هنگام Parsing همان Settingهایی فعال‌اند که هنگام Formatting فعال بوده‌اند. اما هنگام نوشتن در File، Misparsing مربوط به Day/Month می‌تواند Problem واقعی باشد. دو Solution وجود دارد:

  • هنگام Formatting و Parsing همیشه Culture صریح یکسانی را مشخص کنید، مانند Invariant Culture.
  • DateTime و DateTimeOffset را به روشی مستقل از Culture Format کنید.

Approach دوم Robustتر است، به‌خصوص اگر Formatی انتخاب کنید که Year چهاررقمی را ابتدا قرار دهد؛ چنین Stringهایی برای Party دیگر بسیار سخت‌تر Misparse می‌شوند. همچنین Stringهایی که با Format استاندارد و Year-first مانند "o" Format شده‌اند می‌توانند در کنار Stringهای Local نیز صحیح Parse شوند، شبیه «Universal Donor». Dateهای Formatشده با "s" یا "u" مزیت Sortable بودن را هم دارند.

برای Illustration فرض کنید String مستقل از Culture با نام s را چنین تولید کنیم:

string s = DateTime.Now.ToString ("o");

این String را به دو روش می‌توانیم دوباره Parse کنیم. ParseExact تطابق Strict با Format String مشخص‌شده می‌خواهد:

DateTime dt1 = DateTime.ParseExact (s, "o", null);

Result مشابهی را با Methodهای ToString و ToDateTime مربوط به XmlConvert نیز می‌توانید به دست آورید.

اما Parse به‌طور ضمنی هم Format با مقدار "o" و هم Format مربوط به CurrentCulture را می‌پذیرد:

DateTime dt2 = DateTime.Parse (s);

این روش هم برای DateTime و هم DateTimeOffset کار می‌کند.

DateTimeStyles

DateTimeStyles یک Flags Enum است که هنگام فراخوانی Parse روی DateTime(Offset) Instructionهای اضافی می‌دهد. Memberهای آن:

None,
AllowLeadingWhite, AllowTrailingWhite, AllowInnerWhite,
AssumeLocal, AssumeUniversal, AdjustToUniversal,
NoCurrentDateDefault, RoundTripKind

Member Composite با نام AllowWhiteSpaces نیز وجود دارد:

AllowWhiteSpaces = AllowLeadingWhite | AllowTrailingWhite | AllowInnerWhite

Default برابر None است. یعنی Whitespace اضافی به‌صورت معمول ممنوع است؛ Whitespaceای که بخشی از Pattern استاندارد DateTime باشد استثناست.

AssumeLocal و AssumeUniversal زمانی اعمال می‌شوند که String Suffix مربوط به Time Zone مانند Z یا +9:00 نداشته باشد. AdjustToUniversal همچنان Suffix مربوط به Time Zone را رعایت می‌کند، اما سپس با Current Regional Settingها به UTC Convert می‌کند.

اگر String شامل Time باشد اما Date نداشته باشد، به‌صورت پیش‌فرض Date امروز اعمال می‌شود. اگر Flag با نام NoCurrentDateDefault را اعمال کنید، در عوض 1 January 0001 استفاده می‌شود.

منبع: C# 12 in a Nutshell: The Definitive Reference — Joseph Albahari — O’Reilly Media, Inc. — Copyright © 2024 Joseph Albahari — ISBN 978-1-098-14744-0

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620