DateTimes, DateTimeFormatInfos, and Calendars


The relationship between the DateTime structure and DateTimeFormatInfo and Calendar classes warrants some explanation. The DateTime structure holds date/time information as a culture-agnostic point in time in the form of ticks (a long type). Regardless of how a DateTime structure is created, the point in time is not dependent upon any given culture, so January 1, 2000, refers to a fixed point in time, regardless of the calendar system used to create it or represent it. The DateTime structure accepts calendar objects in its constructor for the sole purpose of interpreting the year, month, and day passed to the constructor. (Recall from the previous section that the year 2000, month 1, and day 1 is open to interpretation depending upon the calendar system in use.) After the meaning of the year, month, and day has been established, the DateTime is not directly related to any given calendar.

The DateTimeFormatInfo class contains date-/time-formatting information. It has a read/write Calendar property from which it can draw information about how any given point in time is represented in a particular calendar. In the following code example, CultureInfo objects are created for three cultures: "en-US" (English (United States)), "ar-SA" (Arabic (Saudi Arabia)), and "ja-JP" (Japanese (Japan)). The default calendar for all three cultures is the GregorianCalendar, but the calendar is changed for the "ar-SA" culture to the HijriCalendar and for the "ja-JP" culture to the JapaneseCalendar. (Remember, the CultureInfo.Calendar property is the default Calendar and is read-only, so the only way to change a culture's calendar is through its DateTimeFormat property.)

 CultureInfo englishCultureInfo = new CultureInfo("en-US"); CultureInfo arabicCultureInfo = new CultureInfo("ar-SA"); arabicCultureInfo.DateTimeFormat.Calendar = new HijriCalendar(); CultureInfo japaneseCultureInfo = new CultureInfo("ja-JP"); japaneseCultureInfo.DateTimeFormat.Calendar = new JapaneseCalendar(); DateTime firstJan2000 =     new DateTime(2000, 1, 1, new GregorianCalendar()); listBox1.Items.Add(firstJan2000.ToString(englishCultureInfo)); listBox1.Items.Add(firstJan2000.ToString(arabicCultureInfo)); listBox1.Items.Add(firstJan2000.ToString(japaneseCultureInfo)); 


A single variable, firstJan2000, is shown three times, once for each calendar, giving the following results:

 1/1/2000 12:00:00 AM 25/09/20 12:00:00   12/1/1 0:00:00 


Although the representations of January 1, 2000, show different days, months, and years (and different AM/PM notation), it is the same point in time, as should be obvious by the fact that the variable does not change its value. The calendars are simply used to provide a human point of reference to the underlying ticks. You could draw an analogy with temperature in which the freezing point of water represents a single absolute temperature value (like a single point in time), and Fahrenheit, Celsius, and Kelvin are simply different ways of representing the same valueso Fahrenheit, Celsius, and Kelvin act like the calendars of the temperature world.

The DateTimeFormatInfo class has several methods that provide access to the localized names of days, months, and eras (see Table 6.13).

Table 6.13. DateTimeFormatInfo Localized Name Methods

Method

Description

English (United States) Example

GetAbbreviatedDayName

Gets the abbreviated day name from a DayOfWeek value

"Sun"

GetAbbreviatedEraName

Gets the abbreviated era name from a 0-based era number

"AD"

GetAbbreviatedMonthName

Gets the abbreviated month name from a 1-based month number

"Jan"

GetdayName

Gets the day name from a DayOfWeek value

"Sunday"

GetEraName

Gets the era name from a 0-based era number

"A.D."

GetMonthName

Gets the month name from a 1-based month number

"January"


It should go without saying that you should always use these methods to iterate through day and month names instead of hard-coding them, but the following code should give you even more reason to use the GetMonthName method:

 CultureInfo cultureInfo = new CultureInfo("he-IL"); DateTimeFormatInfo dtfi = cultureInfo.DateTimeFormat; dtfi.Calendar = new HebrewCalendar(); for(int monthNumber = 1; monthNumber <=     dtfi.Calendar.GetMonthsInYear(5345); monthNumber++) {     listBox1.Items.Add(dtfi.GetMonthName(monthNumber)); } 


In this example, we use the "he-IL" (Hebrew (Israel)) culture and assign to its DateTimeFormatInfo.Calendar a new HebrewCalendar object. The Hebrew calendar has 13 months in leap years, and the Hebrew year 5345 is one such year.

We use the Calendar.GetMonthsInYear method to get the number of months in the given year (13, in this example), and then use the DateTimeFormatInfo.GetMonthName to get the name of each month.

In most cases, the System.Windows.Forms.MonthCalendar and System.Windows.Forms.DateTimePicker controls respect the calendar of the CurrentUICulture. The exception is the HijriCalendar, which is the default calendar for the Arabic (Saudi Arabia) and Divehi (Maldives) cultures. In this case, MonthCalendar and DateTimePicker simply display a partially localized GregorianCalendar (day names are in Arabic/Divehi, and month names are in English).


DateTime.ToString, DateTime Parsing, and DateTimeFormatInfo

The DateTime.ToString method provides myriad formatting options for formatting date and times. You are at liberty to devise your own date/time formatting by assembling the basic building blocks of format patterns (d, M, y, g, h, H, m, s, f, t, z for the day, month, year, era, 12 hour, 24 hour, minute, second, fractions of a second, am/pm designator, and time zone offset, respectively), such as this:

 new DateTime(2005, 1, 2).ToString("MM/dd/yy"); 


It should be obvious that these format patterns are not localizable strings and should not be placed in resources. If they are placed in a resource, you run the risk of a translator translating them. So in French, the same string might be accidentally translated to "MM/jj/aa" (year is année, Month is Mois, and day is jour), resulting in a runtime exception.


The problem with this approach, however, is that it is almost certainly culturally biased. Whether the resulting string ("01/02/05") means January 2, 2005, or February 1, 2005, or February 5, 2001, depends on whether you come from the U.S., the U.K., or the People's Republic of China. The locale-aware solution is to let the .NET Framework worry about the format and use format characters instead of constructing your own format patterns. Format characters are single letters that indicate a completed pattern without forcing a specific implementation of that pattern. So, for example, the following conversion to the short date time character "d":

 new DateTime(2005, 1, 2).ToString("d"); 


results in these strings in English (United States), English (United Kingdom), and Chinese (People's Republic Of China), respectively:

 1/2/2005 02/01/2005 2005-1-2 


The full list of format characters, their equivalent methods, and associated Date-TimeFormatInfo pattern properties is shown in Table 6.14. There is no functional difference between the format character and its associated method, so the following two lines are functionally identical:

 new DateTime(2000, 1, 1).ToString("d"); new DateTime(2000, 1, 1).ToShortDateString(); 


Table 6.14. DateTime Format Character and Methods, and DateTimeFormatInfo Patterns

Format Character

Equivalent Method

Associated DateTimeFormatInfo Pattern Property

d

ToShortDateString

ShortDatePattern

D

ToLongDateString

LongDatePattern

f

  

F

 

FullDateTimePattern

g

  

G

  

m, M

 

MonthDayPattern

r, R

 

RFC1123Pattern

s

 

SortableDateTimePattern

t

ToShortTimeString

ShortTimePattern

T

ToLongTimeString

LongTimePattern

u

 

UniversalSortableDateTimePattern

U

  

y, Y

 

YearMonthPattern


However, the former accepts an optional IFormatProvider parameter (see the next section), whereas the latter does not, so it could be considered more versatile but equally, by FxCop standards (also see next section), more ambiguous.

So you should conclude from this that when displaying a date/time to the user, you should always use the format character or its associated method, and should avoid building your own formats. If you want to enforce this approach in your code, take a look at the "DateTime.ToString() should not use a culture-specific format" rule in Chapter 13.

The DateTime structure in the.NET Framework 1.1 supports two methods for parsing date/time strings into dates/times: Parse and ParseExact. The .NET Framework 2.0 adds two new methods: tryParse and tryParseExact. The Parse method is very forgiving in its parsing of date/time strings and works hard to attempt to recognize numerous variations on string formats. So "12/31/01", "December, 31 01", and "31 December 01" are all parsed (using English (United States)) to mean December 31, 2001. This flexibility can be very handy but has a necessary performance hit and can be fooled. The ParseExact method, however, demands the date/time format string that should be used to parse the string. There are no gray areas; if the string doesn't match the format, an exception is thrown. The tryParse and tryParseExact .NET Framework 2.0 methods simply try the same operations but do not throw an exception if the parse attempt fails.

Genitive Date Support

If ever you needed any more reasons to let the .NET Framework do your globalization for you, then there is the issue of genitive dates. The good news is that the .NET Framework understands this problem and deals with it accurately. This is especially good news because it is unlikely that most English-only developers will know what a genitive date is or what the problem is. The issue is that, in some languages, when a month can be seen to "own" or "possess" a day, the month name changes. Table 6.15 shows English month names, Polish month names, and their genitive forms.

Table 6.15. Polish Month Names and Their Genitive Forms

English Month Name

Polish Month Name

Polish Genitive Month Example

January

stycze

1 stycznia 2000

February

luty

1 lutego 2000

March

marzec

1 marca 2000

April

kwiecie

1 kwietnia 2000

May

maj

1 maja 2000

June

czerwiec

1 czerwca 2000

July

lipiec

1 lipca 2000

August

sierpie

1 sierpnia 2000

September

wrzesie

1 wrzenia 2000

October

padziernik

1 padziernika 2000

November

listopad

1 listopada 2000

December

grudzie

1 grudnia 2000


You can see that the month name is different when it is used to "possess" a day. In the list of cultures that the .NET Framework supports, Czech, Greek, Latvian, Lithuanian, Mongolian, Polish, and Slovak all use genitive dates. The moral of the story, as usual, is to let the framework build date strings instead of taking the problem into your own hands. In both the .NET Framework 1.1 and 2.0, the DateTimeFormatInfo class understands how to use genitive dates, so the DateTime.ToString method always returns correct strings. In the .NET Framework 2.0, you can gain programmatic access to the genitive month names using the AbbreviatedMonthGenitiveNames and MonthGenitiveNames array properties.

DateTime.ToString and IFormatProvider

The DateTime.ToString method has four overloads, which ultimately all boil down to a single signature:

 public string ToString(string format, IFormatProvider provider); 


This method simply returns the following value:

 DateTimeFormat.Format(     this, format, DateTimeFormatInfo.GetInstance(provider)); 


If the provider parameter is null, DateTimeFormatInfo.GetInstance uses CultureInfo.CurrentCulture.

The IFormatProvider interface has a single method:

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


There are just three classes in the .NET Framework that support the IFormatProvider interface:

CultureInfo

DateTimeFormatInfo

NumberFormatInfo

The GetFormat method accepts a Type and returns an object of that Type. So if the CultureInfo.GetFormat method is called with the DateTimeFormatInfo Type, then it returns the value of its CultureInfo.DateTimeFormat property. As the name implies, the IFormatProvider implementation provides formatting information. In this example, a German CultureInfo object provides the IFormatProvider interface:

 DateTime firstJan2000 = new DateTime(2000, 1, 1); CultureInfo cultureInfo = new CultureInfo("de-DE"); listBox1.Items.Add(firstJan2000.ToString("D", cultureInfo)); 


The following string is added to the list box:

 Samstag, 1. Januar 2000 


The CultureInfo object provides the DateTimeFormatInfo, which includes the date-/time-formatting patterns and also the Calendar required to represent the date. The FxCop "Specify IFormatProvider" rule (see Chapter 13) enforces that the IFormatProvider parameter is always passed to DateTime.ToString. The idea behind this rule is to ensure that there is no ambiguity in the way the date is represented and that the developer has been forced to consider the globalization issues of the code.




.NET Internationalization(c) The Developer's Guide to Building Global Windows and Web Applications
.NET Internationalization: The Developers Guide to Building Global Windows and Web Applications
ISBN: 0321341384
EAN: 2147483647
Year: 2006
Pages: 213

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net