Cry about...
C# Development How To Notes

Localization in a nutshell


The purpose of these notes are to provide a guide on how to localize a C# application.

Contents:


Why Localize?

I am writing this article in English, and when I develop software I develop it using English - so all my forms, dialogs, messages etc are in English. This is great for a native English speaker/reader, but what if a need arises to release say a German version of my software?

It would be undesirable to have two versions of an application, one English and one German, a more maintainable approach would be to have one application which supported by English and German. That is what localization can do for you.

Localization can also provide a means to deal with minor variations between different dialects. For example, I mostly write using British-English but sometimes I have a need to support American-English. The differences are minor, but include things like "colour" (British-English) instead of "color" (American-English). Again localization provides a means to allow for these differences.

Localization covers more than just language translation, but also time zone, date format, currency, reading-order, etc. If done properly (which does require effort) a localized application should look as though it was developed for the local culture. Sometimes localization will also require a partial redesign - but that is outside the scope of this article.


Localization and Culture

Culture defines the language settings and how dates, numbers and currency should be formatted.

So whilst Localization is the process of making an application appear localized, the culture is what is used to specify much of how the localization should be done.

Cultures are usually specified by name. For example (and this is only a sample, it is not intended to be complete):

Culture name English name
(empty string) Invariant culture
en English
en-CA English (Canadian)
en-GB English (United Kingdom)
en-US English (United States)
fr French
fr-CA French (Canadian)
fr-FR French (France)

There is an MSDN article which gives a list of the pre-defined culture names.

This table does illustrate three important divisions within cultures:

  1. "invariant culture"
    The invariant culture (there is only one) is culture insensitive. It is generally associated with English but not with any country or region. Think of the invariant culture as being the default culture for an application.
  2. "neutral culture"
    A neutral culture is associated with a language but not with a country. For example "en" is a neutral culture for English, and "fr" is a neutral culture for "French".
  3. "specific culture"
    A specific culture is associated with both a language and a country (or region). For example "en-GB" is a specific culture for English, United Kingdom, and "en-US" is a specific culture for English, United States.

If you can, always opt for a specific culture as that will give better localization.


How to get the current culture?

Getting the current culture is fortunatly quite simple. Less so is understanding which culture you want, because the framework maintains not one but two culture settings: CurrentCuture and CurrentUICulture.

CurrentCulture reflects the user locale settings. This defaults to the regional options in Control Panel. This is used to control the presentation of date and time settings.

Where as CurrentUICulture reflects the user interface language. This defaults to the language used by Windows. This is the culture used by the resource manager when retrieving resources.

Obtaining the current culture is very simple (once you appreciate which of the above you need), and is either:

CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;

or

CultureInfo currentUICulture = Thread.CurrentThread.CurrentUICulture;

Both of these will return the appropriate culture used on the current thread, which (unless you have changed it in code) will be the culture in use when Windows open the application.

You will need to use/import the module System.Globalization to use CultureInfo. You can also access the culture information via System.Globalization.CultureInfo.CurrentCulture and System.Globalization.CultureInfo.CurrentUICulture.

The CultureInfo class has a number of useful properties, a few of which are worth highlighting:

string Name
Returns the name of the culture. This is in the code for the culture.
For example "en-GB".
string EnglishName
Returns the name of the culture in English.
For example "English (United Kingdom)"
string NativeName
Returns the name of the culture in the native language.
For example "English (United Kingdom)"

You can see the full list on the MSDN page.


How to set the culture in code

Thread.CurrentThread.CurrentCulture is a property, so it can be assigned to as well as read from. So to set the culture is simply:

Thread.CurrentThead.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");

ditto for Thread.CurrentThread.CurrentUICulture.

When localisaing an application the ability to set the culture is probably only going to be useful if as a developer you want to override the culture setting specified in control panel, or for web applications where the culture information should be specific to the user and not the server it is running on.


How to reset the culture

Once you have changed the culture there is no simple way to reset it back! So if you want to temporarily change the culture then it might be best to either:

  • Keep a reference to the original culture object and set back again.
  • Keep a reference to the original culture object at application startup, so it is always available.
  • Use a new culture object directly without changing the thead's culture.

Whenever a new thread is started it will use the default culture. So you can take advantage of this as a means to get the default. The following class illustrates how to read the default culture at run-time, it uses the fact that a new thread thread picks up the default culture:

internal static class DefaultCulture
{
  private static CultureInfo defaultCulture = null;
  private static CultureInfo defaultUICulture = null;

  private static void EnsureCultureLoaded()
  {
    if (defaultCulture == null)
    {
      CultureInfo.CurrentCulture.ClearCachedData();
      CultureInfo.CurrentUICulture.ClearCachedData();
      Thread readCulture = new Thread(() =>
      {
        defaultCulture = Thread.CurrentThread.CurrentCulture;
        defaultUICulture = Thread.CurrentThread.CurrentUICulture;
      });
      readCulture.Start();
      readCulture.Join();
    }
  }
  /// <summary>
  /// Returns the default culture.
  /// </summary>
  public static CultureInfo GetCulture()
  {
    EnsureCultureLoaded();
    return defaultCulture;
  }
  /// <summary>
  /// Returns the default UI culture.
  /// </summary>
  public static CultureInfo GetUICulture()
  {
    EnsureCultureLoaded();
    return defaultUICulture;
  }
}


How to create and use localized strings (resource files)

Resource files provide a simple but effective means of defining constants (typically strings), where the string value used varies automatically according to the current culture.

Whether you are working on a desktop application or a web application the way to create a new resource file is very similar.

  1. In Visual Studio, select Project > Add New Item ...
  2. Select "Resources File"
    Note: You must add a resource file, you cannot use the application's resource file for this purpose.
  3. Give the file a suitable name. The name of the file will be used each time you refer to one of the resources in the file, so remember that when you name it.
    For example: "LocalizedText.resx".
  4. Add one or more text strings to this resource file.
    For example: Name=Welcome, Value=Welcome
    Remember that these are in English, or rather they specify the default (invariant-culture) values that your application will use.
  5. In your code you can now reference any resource simply by using the name of the resource file and the name of the string.
    For example: LocalizedText.Welcome
  6. To create a localized version of the resource file simply add another resource file with the same name as the original but with the language code appended.
    For example: "LocalizedText.fr.resx"

The only other thing to be aware of before using this is that the values pulled back will depend on the CurentUICulture.

The following example shows the effect of this. This assumes that I have defined two resource files "LocalizedText" and "LocalizedText.fr" both of which define a value for "Welcome":

Console.WriteLine(
    "Welcome in " +
    Thread.CurrentThread.CurrentUICulture.EnglishName +
    " is " +
    LocalizedText.Welcome);
Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture("fr");
Console.WriteLine(
    "Welcome in " +
    Thread.CurrentThread.CurrentUICulture.EnglishName +
    " is " +
    LocalizedText.Welcome);

and this produces:

Welcome in English (United Kingdom) is Welcome

Welcome in French (France) is Bienvenue

Whilst my UICulture is en-GB, I don't have a resource file defined for en-GB, nore do I have one for en, so the application pulls back the text from my culture-invariant one (i.e. LocalizedText.resx).

You can use resource files in this way with images and even files.


The easy way to localize dates

The easiest way to localize dates is to use the built in functions ToShortDateString() or ToLongDateString(). For example:

Console.WriteLine(
    "Short date now: " + DateTime.Now.ToShortDateString());
Console.WriteLine(
    "Long date now: " + DateTime.Now.ToLongDateString());

Alternatively you can extract the date string from the current culture and use that to format the date, so the following is longer but equivalent to the above:

Console.WriteLine(
    "Short date now: " + DateTime.Now.ToString(
    Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern));
Console.WriteLine(
    "Long date now: " + DateTime.Now.ToString(
    Thread.CurrentThread.CurrentCulture.DateTimeFormat.LongDatePattern));

To illustrate the effect of changing the culture consider the following example:

Console.WriteLine(
    "Culture: " +
    Thread.CurrentThread.CurrentCulture.EnglishName);
Console.WriteLine(
    "Short date now: " + DateTime.Now.ToShortDateString());
Console.WriteLine(
    "Long date now: " + DateTime.Now.ToLongDateString());
Thread.CurrentThread.CurrentCulture =
    CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(
    "Culture: " +
    Thread.CurrentThread.CurrentCulture.EnglishName);
Console.WriteLine(
    "Short date now: " + DateTime.Now.ToShortDateString());
Console.WriteLine(
    "Long date now: " + DateTime.Now.ToLongDateString());

which produces:

Culture: English (United Kingdom)

Short date now: 12/06/2013

Long date now: 13 June 2013

Culture: English (United States)

Short date now: 6/12/2013

Long date now: Thursday, June 13, 2013

The local culture on my PC is English UK. The above does show one of the common causes of misunderstanding between British English speakers and American English speakers - that British English express dates as day/month/year whereas American's use month/day/year. Which all goes to illustrate the need for localization even across different regions which notionally speak the same language.

If you require a custom date format, yet one which is still localizable, then the other option is to define your own date formats in the resource files and to use those when formatting dates. 


Localized currency format

Currency is a good example of where a number needs to be displayed for different cultures.

To display a currency amount using the current culture simply use the number format string "C", i.e.:

Console.WriteLine(money.ToString("C"));

or

Console.WriteLine("Money: {0:C}",money);

For more information on the "C" format string for currency see The Currency ("C") Format Specifier on the Microsoft website.

For example:

double money = 12345.67;
Console.WriteLine(
    "Culture: " +
    Thread.CurrentThread.CurrentCulture.EnglishName);
Console.WriteLine(String.Format("Money: {0:C}", money));
Thread.CurrentThread.CurrentCulture =
    CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(
    "Culture: " +
    Thread.CurrentThread.CurrentCulture.EnglishName);
Console.WriteLine(String.Format("Money: {0:C}", money));

produces:

Culture: English (United Kingdom)

Money: £12,345.67

Culture: English (United States)

Money: $12,345.67

If you need to display multiple currencies then it probably makes more sense to use:

money.ToString("C",culture)

where "money" is your variable holding the amount and culture is the culture you want to use.

It is important to recognise that whilst this will allow you to display the amount in the correct format for the culture, it does not do currency convesions (say Pounds-Sterling to US-Dollars). Currency conversions are down to you. 


Web apps: Detecting browser language

On a website (as with a desktop application) the default culture is defined by the settings in Control Panel. If a website is aimed at a particular country then that may be sufficient, but if a website is aimed at people from different countries then the need to support different cultures quickly arises.

One way round this is to have a different website for each country or region that is being targetted. There are pros and cons of that. A simpler approach is to have one website for which you can switch cultures appropriatly for each visitor.

Of course the obvious question is which culture to use for a new visitor? You may well want to allow the visitor to change the language the website appears in, but what should be the initial choice? You can try to work it out from the browser. One of the messages the browser sends to the server when it requests a webpage is "Accept-Language" which notionally indicates the browsers preference for which language to use. However, it is important to be aware that this header might be missing or might be miss-configured at the client end. (Consider how many Windows PCs are set up with the default locale being US-English even though they are not located in the US?) However, other than trying to determine the location of a visitor by their IP address, the Accept-Language header is probably the best there is.

There are two ways to use the Accept-Language header, either (i.) do the work yourself or (ii.) let ASP.NET try to work it out for you.

1. Interpreting Accept-Language for yourself

Request.UserLanguages returns an array of strings (or possibly just null). Each of these represents a preferred language. You can work through this array and pick the language which more closely matches to what you have developed for or simply take the first and use that as a hint for which culture to use.

I'm not going to include any code samples for this, because there is a much simpler way - let ASP.NET try to work it out for you.

2. Let ASP.NET try to work it out

The simpler way is to let ASP.NET try to work out for itself what culture to use. To do this simply add:

<globalization culture="auto" uiCulture="auto"/>

to the web.config file. It goes inside the <system.web> section (which in turn is inside the <configuration> section).

Just remember that this is a starting point, and that it is probably best to allow the user the option to override it and select their own.


Time Zones

What time is it? It's a simple question, but depending on where you are in the world you will give a different answer. As I'm writing this it is 4:38PM, but someone in Paris would say no its 5:38PM and someone in San Francisco would say its 8:38AM.

So normally when we ask what time is it we mean what is the local time here?

Particularly when developing applications for the web it is important to know where "here" is, because a visitor might very easily be in a different time zome from where you web server is located. So your users's time zone may be different from your server's time zone (just like the server and user's culture may be different).

There are some things to ask yourself when considering the time-zone:

  1. Does it matter?

    For example lets say that a user records that a meeting took place at 10am in London. If looking at that information from Singapore (7 hours ahead), then should a reader in Singapore read that the meeting took place at 10am or at 5pm? (London 10am BST = 9am UTC = 5pm Singapore [UTC+8])

  2. What time (and date) are you going to store at the server?

    In the example above it might be appropriate to store "10am", but for others (say when planning a video-conference) it would be more appropriate to store times in UTC and then convert to each user's local time zone when displaying it.

  3. What is the client's time-zone and what is the server's time-zone?

    If you date stamp events at the server then consider whether it is appropriate to store the date stamp as per the client's timezone, the server's timezone or UTC. Which is the most appropriate for your application?

UTC : Coordinated Universal Time - A standard for time

Coordinated Universal Time (abbreviated UTC) is a global standard for time. It is based on GMT, which represents the time at latitude 0 (Greenwich, UK) without regard for any local daylight saving. Local times around the world are represented as an offset to UTC.

C# Routines for handling UTC time

C# makes it very easy to work with UTC, because DateTime provides the handy functions ToUniversalTime and ToLocalTime:

DateTime utcTime = now.ToUniversalTime();

DateTime meetingDate = meetingDateUTC.ToLocalTime();

One obvious thing to be aware of is that these functions use the time zone of the local computer. So if you are writing a desktop application then these two functions are all you need to know. If however you are writing web based application then you may also need to use the client's time zone instead of the server's time zone. So if you want to provide a timezone then you need to use a different set of functions and the TimeZoneInfo class.

To convert a time to UTC from a given time zone use TimeZoneInfo.ConvertTimeToUtc, eg:

DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(userEnteredTime,userTimeZone);

and to convert from UTC to a given time zone use TimeZone.ConvertTimeFromUtc, eg:

DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime,userTimeZone);

but where does the time zone information ("userTimeZone" in the above examples) come from? There are two ways to define the time zone to use, either TimeZoneInfo.FindSystemTimeZoneById(string id) or TimeZoneInfo.CreateCustomTimeZone(string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName). Actually there are a few other ways, but those two are the ones that I've found most useful.

TimeZoneInfo.FindSystemTimeZoneById(string id)

The function TimeZoneInfo.FindSystemTimeZoneById(string id) returns a TimeZoneInfo object from the registry on the local computer. It will throw an exception if it cannot find a matching id.

Unfortunatly there doesn't seem to be a list of pre-defined time zone id's. You can query the computer for a list using TimeZoneInfo.GetSystemTimeZones which returns a list of available time zones. However if you want to know what time zones are available then this isn't much help.

The following lists all the time zones available on my computer, remember that each of the IDs is a string:

ID Display name Standard name
Dateline Standard Time (UTC-12:00) International Date Line West Dateline Standard Time
UTC-11 (UTC-11:00) Co-ordinated Universal Time-11 UTC-11
Hawaiian Standard Time (UTC-10:00) Hawaii Hawaiian Standard Time
Alaskan Standard Time (UTC-09:00) Alaska Alaskan Standard Time
Pacific Standard Time (Mexico) (UTC-08:00) Baja California Pacific Standard Time (Mexico)
Pacific Standard Time (UTC-08:00) Pacific Time (US & Canada) Pacific Standard Time
US Mountain Standard Time (UTC-07:00) Arizona US Mountain Standard Time
Mountain Standard Time (Mexico) (UTC-07:00) Chihuahua, La Paz, Mazatlan Mountain Standard Time (Mexico)
Mountain Standard Time (UTC-07:00) Mountain Time (US & Canada) Mountain Standard Time
Central America Standard Time (UTC-06:00) Central America Central America Standard Time
Central Standard Time (UTC-06:00) Central Time (US & Canada) Central Standard Time
Central Standard Time (Mexico) (UTC-06:00) Guadalajara, Mexico City, Monterrey Central Standard Time (Mexico)
Canada Central Standard Time (UTC-06:00) Saskatchewan Canada Central Standard Time
SA Pacific Standard Time (UTC-05:00) Bogota, Lima, Quito SA Pacific Standard Time
Eastern Standard Time (UTC-05:00) Eastern Time (US & Canada) Eastern Standard Time
US Eastern Standard Time (UTC-05:00) Indiana (East) US Eastern Standard Time
Venezuela Standard Time (UTC-04:30) Caracas Venezuela Standard Time
Paraguay Standard Time (UTC-04:00) Asuncion Paraguay Standard Time
Atlantic Standard Time (UTC-04:00) Atlantic Time (Canada) Atlantic Standard Time
Central Brazilian Standard Time (UTC-04:00) Cuiaba Central Brazilian Standard Time
SA Western Standard Time (UTC-04:00) Georgetown, La Paz, Manaus, San Juan SA Western Standard Time
Pacific SA Standard Time (UTC-04:00) Santiago Pacific SA Standard Time
Newfoundland Standard Time (UTC-03:30) Newfoundland Newfoundland Standard Time
E. South America Standard Time (UTC-03:00) Brasilia E. South America Standard Time
Argentina Standard Time (UTC-03:00) Buenos Aires Argentina Standard Time
SA Eastern Standard Time (UTC-03:00) Cayenne, Fortaleza SA Eastern Standard Time
Greenland Standard Time (UTC-03:00) Greenland GreenlandStandard Time
Montevideo Standard Time (UTC-03:00) Montevideo Montevideo Standard Time
Bahia Standard Time (UTC-03:00) Salvador Bahia Standard Time
UTC-02 (UTC-02:00) Co-ordinated Universal Time-02 UTC-02
Mid-Atlantic Standard Time (UTC-02:00) Mid-Atlantic Mid-Atlantic Standard Time
Azores Standard Time (UTC-01:00) Azores Azores Standard Time
Cape Verde Standard Time (UTC-01:00) Cape Verde Is. Cape Verde Standard Time
Morocco Standard Time (UTC) Casablanca Morocco Standard Time
UTC (UTC) Co-ordinated Universal Time Co-ordinated Universal Time
GMT Standard Time (UTC) Dublin, Edinburgh, Lisbon, London GMT Standard Time
Greenwich Standard Time (UTC) Monrovia, Reykjavik Greenwich Standard Time
W. Europe Standard Time (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna W. Europe Standard Time
Central Europe Standard Time (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague Central Europe Standard Time
Romance Standard Time (UTC+01:00) Brussels, Copenhagen, Madrid, Paris Romance Standard Time
Central European Standard Time (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb Central European Standard Time
Libya Standard Time (UTC+01:00) Tripoli Libya Standard Time
W. Central Africa Standard Time (UTC+01:00) West Central Africa W. Central Africa Standard Time
Namibia Standard Time (UTC+01:00) Windhoek Namibia Standard Time
GTB Standard Time (UTC+02:00) Athens, Bucharest GTB Standard Time
Middle East Standard Time (UTC+02:00) Beirut Middle East Standard Time
Egypt Standard Time (UTC+02:00) Cairo Egypt Standard Time
Syria Standard Time (UTC+02:00) Damascus Syria StandardTime
E. Europe Standard Time (UTC+02:00) E. Europe E. EuropeStandard Time
South Africa Standard Time (UTC+02:00) Harare, Pretoria South Africa Standard Time
FLE Standard Time (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius FLE Standard Time
Turkey Standard Time (UTC+02:00) Istanbul Turkey Standard Time
Israel Standard Time (UTC+02:00) Jerusalem Jerusalem Standard Time
Jordan Standard Time (UTC+03:00) Amman Jordan Standard Time
Arabic Standard Time (UTC+03:00) Baghdad Arabic Standard Time
Kaliningrad Standard Time (UTC+03:00) Kaliningrad, Minsk Kaliningrad Standard Time
Arab Standard Time (UTC+03:00) Kuwait, Riyadh Arab Standard Time
E. Africa Standard Time (UTC+03:00) Nairobi E. Africa Standard Time
Iran Standard Time (UTC+03:30) Tehran Iran Standard Time
Arabian Standard Time (UTC+04:00) Abu Dhabi, Muscat Arabian Standard Time
Azerbaijan Standard Time (UTC+04:00) Baku Azerbaijan Standard Time
Russian Standard Time (UTC+04:00) Moscow, St. Petersburg, Volgograd Russian Standard Time
Mauritius Standard Time (UTC+04:00) Port Louis Mauritius Standard Time
Georgian Standard Time (UTC+04:00) Tbilisi Georgian Standard Time
Caucasus Standard Time (UTC+04:00) Yerevan Caucasus Standard Time
Afghanistan Standard Time (UTC+04:30) Kabul AfghanistanStandard Time
Pakistan Standard Time (UTC+05:00) Islamabad, Karachi Pakistan Standard Time
West Asia Standard Time (UTC+05:00) Tashkent West Asia Standard Time
India Standard Time (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi India Standard Time
Sri Lanka Standard Time (UTC+05:30) Sri Jayawardenepura Sri Lanka Standard Time
Nepal Standard Time (UTC+05:45) Kathmandu Nepal Standard Time
Central Asia Standard Time (UTC+06:00) Astana Central Asia Standard Time
Bangladesh Standard Time (UTC+06:00) Dhaka Bangladesh Standard Time
Ekaterinburg Standard Time (UTC+06:00) Ekaterinburg Ekaterinburg Standard Time
Myanmar Standard Time (UTC+06:30) Yangon (Rangoon) Myanmar Standard Time
SE Asia Standard Time (UTC+07:00) Bangkok, Hanoi, Jakarta SE Asia Standard Time
N. Central Asia Standard Time (UTC+07:00) Novosibirsk N. Central Asia Standard Time
China Standard Time (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi China Standard Time
North Asia Standard Time (UTC+08:00) Krasnoyarsk North Asia Standard Time
Singapore Standard Time (UTC+08:00) Kuala Lumpur, Singapore Malay Peninsula Standard Time
W. Australia Standard Time (UTC+08:00) Perth W. Australia Standard Time
Taipei Standard Time (UTC+08:00) Taipei Taipei StandardTime
Ulaanbaatar Standard Time (UTC+08:00) Ulaanbaatar Ulaanbaatar Standard Time
North Asia East Standard Time (UTC+09:00) Irkutsk NorthAsia East Standard Time
Tokyo Standard Time (UTC+09:00) Osaka, Sapporo, Tokyo Tokyo Standard Time
Korea Standard Time (UTC+09:00) Seoul Korea Standard Time
Cen. Australia Standard Time (UTC+09:30) Adelaide Cen. Australia Standard Time
AUS Central Standard Time (UTC+09:30) Darwin AUS Central Standard Time
E. Australia Standard Time (UTC+10:00) Brisbane E. Australia Standard Time
AUS Eastern Standard Time (UTC+10:00) Canberra, Melbourne, Sydney AUS Eastern Standard Time
West Pacific Standard Time (UTC+10:00) Guam, Port Moresby West Pacific Standard Time
Tasmania Standard Time (UTC+10:00) Hobart Tasmania Standard Time
Yakutsk Standard Time (UTC+10:00) Yakutsk Yakutsk Standard Time
Central Pacific Standard Time (UTC+11:00) Solomon Is., New Caledonia Central Pacific Standard Time
Vladivostok Standard Time (UTC+11:00) Vladivostok Vladivostok Standard Time
New Zealand Standard Time (UTC+12:00) Auckland, Wellington New Zealand Standard Time
UTC+12 (UTC+12:00) Co-ordinated Universal Time+12 UTC+12
Fiji Standard Time (UTC+12:00) Fiji Fiji Standard Time
Magadan Standard Time (UTC+12:00) Magadan Magadan Standard Time
Kamchatka Standard Time (UTC+12:00) Petropavlovsk-Kamchatsky - Old Kamchatka Standard Time
Tonga Standard Time (UTC+13:00) Nuku'alofa Tonga Standard Time
Samoa Standard Time (UTC+13:00) Samoa Samoa Standard Time

For example, to show the current time in Singapore:

DateTime utcTimeNow = DateTime.Now.ToUniversalTime();
TimeZoneInfo singaporeZone = TimeZoneInfo.FindSystemTimeZoneById(
    "Singapore Standard Time");
Console.WriteLine(
    "Time in Singapore: " +
    TimeZoneInfo.ConvertTimeFromUtc(
        utcTimeNow,singaporeTime).ToString());

TimeZoneInfo.CreateCustomTimeZone(string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName)

If you do not know the time zone id, or have a need to create your own timezone then you can do so using TimeZoneInfo.CreateCustomTimeZone. This takes four arguments:

string id
This is the id to associate with the time zone. (This doesn't create a new system time zone.)
TimeSpan baseUtcOffset
The time difference between this time zone and UTC. This must evaluate to a whole number of minutes.
string displayName
The display name of the new time zone.
string standardDisplayName
The name of the new time zone's standard time.

For example, to show the current time in Singapore which I know to be UTC+8:

DateTime utcTimeNow = DateTime.Now.ToUniversalTime();
TimeZoneInfo singaporeZone = TimeZoneInfo.CreateCustomTimeZone(
    "UTC+8",TimeSpan.FromHours(8),
    "Singapore time",
    "Singapore time");
Console.WriteLine(
    "Time in Singapore: " +
    TimeZoneInfo.ConvertTimeFromUtc(
        utcTimeNow,singaporeTime).ToString());


Daylight saving (eg British Summer Time)

If you use a named time zone (i.e. one loaded using TimeZoneInfo.FindSystemTimeZoneById) then any daylight saving should be automatically handled for you. This isn't the case if you create your own custom time zone. If you do need to create your own custom time zone and daylight saving is likely to be needed then use one of the following functions:

TimeZoneInfo.CreateCustomTimeZone(string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule[] adjustmentRules);

or

TimeZoneInfo.CreateCustomTimeZone(string id, TimeSpan baseUtcOffset, string displayName, string standardDisplayName, string daylightDisplayName, TimeZoneInfo.AdjustmentRule[] adjustmentRules, bool disableDaylightSavingTime);

I have yet to need to use either of these so for guidance on their use please refer to the msdn TimeZoneInfo.CreateCustomTimeZone method documentation.


Further Reading

http://www.csharp-examples.net/culture-names/
C# example showing how to list all the culture names in Windows.
http://support.microsoft.com/kb/306162
Microsoft article on how to set the current culture programtically in an ASP.NET Application.
http://www.dragon-tech.org/en/projects/selflocate/setculture/
Setting the culture for a Windows Forms application.
http://msdn.microsoft.com/en-us/library/fw69ke6f%28v=vs.100%29.aspx
Walkthrough: Using Resources for Localization with ASP.NET.
http://www.csharpdeveloping.net/Snippet/how_to_get_number_format_information_for_culture
How to get the number format for a culture in C#.


About the author: is a dedicated software developer and webmaster. For his day job he develops websites and desktop applications as well as providing IT services. He moonlights as a technical author and consultant.