فصل ۶: 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ها| Letter | Meaning | Sample input | Result | Notes |
| G یا g | General | 1.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 محدود میکند. |
| F | Fixed point | 2345.678, "F2"
2345.6, "F2" | 2345.68 2345.60 | F2 تا دو Decimal Place Round میکند. |
ادامهٔ جدول 6-2 — Standard Numeric Format Stringها| Letter | Meaning | Sample input | Result | Notes |
| N | Fixed point با Group Separator (Numeric) | 2345.678, "N2"
2345.6, "N2" | 2,345.68 2,345.60 | مانند بالا همراه Group Separator هزارگان؛ Detail از Format Provider گرفته میشود. |
| D | Pad با Leading Zero | 123, "D5"
123, "D1" | 00123 123 | فقط برای Integral Type. D5 سمت چپ را تا پنج Digit Pad میکند و Truncate نمیکند. |
| E یا e | اجبار Exponential Notation | 56789, "E"
56789, "e"
56789, "E2" | 5.678900E+004 5.678900e+004 5.68E+004 | Precision پیشفرض شش Digit. |
| C | Currency | 1.2, "C"
1.2, "C4" | $1.20 $1.2000 | C بدون Digit از Count پیشفرض Decimal Place در Format Provider استفاده میکند. |
| P | Percent | .503, "P"
.503, "P0" | 50.30% 50% | Symbol و Layout از Format Provider گرفته میشود. Decimal Place را میتوان Override کرد. |
| X یا x | Hexadecimal | 47, "X"
47, "x"
47, "X4" | 2F 2f 002F | X برای Hex Digit Uppercase و x برای Lowercase؛ فقط Integralها. |
| R یا G9/G17 | Round-trip | 1f / 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ها| Specifier | Meaning | Sample input | Result | Notes |
| # | Digit placeholder | 12.345, ".##"
12.345, ".####" | 12.35 12.345 | Digitهای بعد از Decimal Point را محدود میکند. |
| 0 | Zero placeholder | 12.345, ".00"
12.345, ".0000"
99, "000.00" | 12.35 12.3450 099.00 | مانند بالا، اما قبل و بعد از Decimal Point نیز با Zero Pad میکند. |
| . | Decimal point | | | Decimal Point را نشان میدهد؛ Symbol واقعی از NumberFormatInfo میآید. |
| , | Group separator | 1234, "#,###,###"
1234, "0,000,000" | 1,234 0,001,234 | Symbol از NumberFormatInfo میآید. |
| , (همان بالا) | Multiplier | 1000000, "#,"
1000000, "#,," | 1000 1 | اگر Comma در انتها یا پیش از Decimal Point باشد بهعنوان Multiplier عمل میکند و Result را بر 1,000، 1,000,000 و غیره تقسیم میکند. |
| % | Percent notation | 0.6, "00%" | 60% | ابتدا در 100 ضرب میکند و سپس Percent Symbol گرفتهشده از NumberFormatInfo را جایگزین میکند. |
| E0, e0, E+0, e+0, E-0, e-0 | Exponent notation | 1234, "0E0"
1234, "0E+0"
1234, "0.00E00"
1234, "0.00e00" | 1E3 1E+3 1.23E03 1.23e03 | |
| \ | Literal character quote | 50, @"\#0" | #50 | همراه Prefix با علامت @ روی String استفاده کنید، یا از \\ استفاده کنید. |
| 'xx' / "xx" | Literal string quote | 50, "0 '...'" | 50 ... | |
| ; | Section separator | 15, "#;(#);zero"
-5, "#;(#);zero"
0, "#;(#);zero" | 15 (5) zero | بهترتیب برای Positive، Negative و Zero. |
| هر Character دیگر | Literal | 35.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 آنها را نشان میدهد و سه مورد مفیدتر با تأکید نمایش داده شدهاند.
شکل 6-1 — ترکیب Compositeهای NumberStyles.
وقتی Parse را بدون مشخص کردن Flag فراخوانی میکنید، Defaultهای نشاندادهشده در Figure 6-2 اعمال میشوند.
شکل 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 string | Meaning | Sample output |
| d | Short date | 01/02/2000 |
| D | Long date | Sunday, 02 January 2000 |
| t | Short time | 17:18 |
| T | Long time | 17:18:19 |
| f | Long date + short time | Sunday, 02 January 2000 17:18 |
| F | Long date + long time | Sunday, 02 January 2000 17:18:19 |
| g | Short date + short time | 01/02/2000 17:18 |
| G (default) | Short date + long time | 01/02/2000 17:18:19 |
| m, M | Month and day | 02 January |
| y, Y | Year and month | January 2000 |
جدول 6-5 — Date/Time Format Stringهای مستقل از Culture| Format string | Meaning | Sample output | Notes |
| o | Round-trippable | 2000-01-02T17:18:19.0000000 | اگر DateTimeKind برابر Unspecified نباشد Time Zone Information را Append میکند. |
| r, R | RFC 1123 standard | Sun, 02 Jan 2000 17:18:19 GMT | باید با DateTime.ToUniversalTime بهصورت صریح به UTC تبدیل کنید. |
| s | Sortable; ISO 8601 | 2000-01-02T17:18:19 | با Text-based Sorting سازگار است. |
| u | Universal sortable | 2000-01-02 17:18:19Z | مشابه بالا؛ باید صریحاً به UTC تبدیل شود. |
| U | UTC | Sunday, 02 January 2000 17:18:19 | Long 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 استفاده میشود.