Thursday 26 June 2008

Overide the default calendar culture (C#)

Useful links
http://msdn.microsoft.com/en-us/library/system.globalization.thaibuddhistcalendar.aspx
http://forums.asp.net/t/1029796.aspx
http://www.codeproject.com/KB/webforms/MultiCultureCalendar.aspx

Basically I have created a configuration setting called OverrideCalendarWith which by default is set to “None”. To override the i.e. ThaiBuddhistCalendar set the configuration entry to “GregorianCalendar:en-GB”.

The code will now override the calendar to be a GregorianCalendar with an en-GB culture. The reason you need the culture string is because you could have a ChineseLunisolarCalendar with either one of the following cultures:
  • zh-HK
  • zh-MO
  • zh-CN
  • zh-SG

All the code is placed in the Global.asax.cs file within the
Application_PreRequestHandlerExecute event:

Application_PreRequestHandlerExecute
void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
// Check if configuration setting exists to override the current calendar culture
string configCalendar = ConfigurationSettings.GetAppSetting("OverrideCalendarWith");
if (configCalendar.ToLower() != "none")
{
OverrideCalendarCulture(configCalendar);
}

}

OverrideCalendarCulture
///


/// Overrides the current threads' calendar with the calendar:culture specified in the parameter. For example if you wish
/// to override the calendar as a ThaiBuddhistCalendar with a culture of th-TH the parameter would look like this:
/// ThaiBuddhistCalendar:th-TH.
///

/// A two-part string representing the calendar:culture i.e. GregorianCalendar:en-GB.
private void OverrideCalendarCulture(string overrideCalendar)
{
// Get the configuration calendar and culture
// You can for example have a ChineseLunisolarCalendar with either Chinese (Singapore) or Chinese (Taiwan) culture settings
string[] parts = overrideCalendar.Split(':');
string calendar = parts[0];
string culture = parts[1];
// Get the culture of the calendar to use in overriding
System.Globalization.CultureInfo overrideCulture = new System.Globalization.CultureInfo(culture);
System.Globalization.DateTimeFormatInfo overrideDateTimeInfo = GetCultureDateTimeFormatInfo(overrideCulture);
if (overrideDateTimeInfo != null)
{
// Only override if the cultures are different
if (overrideCulture.DisplayName != System.Globalization.CultureInfo.CurrentCulture.DisplayName)
{
if (logger.IsDebugEnabled)
{
logger.Debug(string.Format("Overwritting {0} calendar with {1} ({2}) calendar.",
System.Globalization.CultureInfo.CurrentCulture.DisplayName,
overrideCulture.DisplayName,
overrideCalendar));
}
OverrideSupportedCalendar(overrideDateTimeInfo, calendar);
}
}
}


OverrideSupportedCalendar, GetCultureDateTimeFormatInfo, and SetCalendarCulture
///


/// This method overrides the calendar for the current thread for all the supported calendars. If a calendar is not
/// supported then an entry is logged into the log file.
///

/// The DateTimeFormatInfo object used in overriding.
/// The calendar to override with. For example GregorianCalendar or ChineseLunisolarCalendar.
private void OverrideSupportedCalendar(System.Globalization.DateTimeFormatInfo overrideDateTimeInfo, string calendar)
{
// Supported calendars
switch (calendar)
{
case "ChineseLunisolarCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.ChineseLunisolarCalendar());
break;
case "GregorianCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.GregorianCalendar());
break;
case "HebrewCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.HebrewCalendar());
break;
case "HijriCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.HijriCalendar());
break;
case "JapaneseCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.JapaneseCalendar());
break;
case "JapaneseLunisolarCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.JapaneseLunisolarCalendar());
break;
case "JulianCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.JulianCalendar());
break;
case "KoreanCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.KoreanCalendar());
break;
case "KoreanLunisolarCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.KoreanLunisolarCalendar());
break;
case "PersianCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.PersianCalendar());
break;
case "TaiwanCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.TaiwanCalendar());
break;
case "TaiwanLunisolarCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.TaiwanLunisolarCalendar());
break;
case "ThaiBuddhistCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.ThaiBuddhistCalendar());
break;
case "UmAlQuraCalendar":
SetCalendarCulture(overrideDateTimeInfo, new System.Globalization.UmAlQuraCalendar());
break;
default:
if (logger.IsDebugEnabled)
{
logger.Debug("Calendar in config is not supported in the code.");
}
break; // Do nothing
}
}
///
/// Gets the supported DateTimeFormatInfo for the culture passed in. Neutral cultures such as zh-Hans can't be used as
/// a DateTimeFormatInfo object and so NULL will be returned.
///

/// The culture to use in obtaining the DateTimeFormatInfo object.
/// A DateTimeFormatInfo object if successful or null if not supported.
private System.Globalization.DateTimeFormatInfo GetCultureDateTimeFormatInfo(System.Globalization.CultureInfo culture)
{
// Only log unsupported exceptions otherwise throw errors
// An unsupported culture is something like zh-Hans (Chinese - Simplified) which is a neutral culture and can't be set as a thread culture
try
{
return culture.DateTimeFormat;
}
catch (NotSupportedException notSupportedException)
{
if (logger.IsErrorEnabled)
{
logger.Error(string.Format("Culture {0} is not supported as a thread culture.",
culture.DisplayName), notSupportedException);
}
return null;
}
}
///
/// This method uses reflection to set the DateTimeFormat of the CurrentCulture and CurrentUICulture because
/// they are normally read-only.
///

/// The DateTimeFormatInfo to use in overriding the current threads DateTimeFormat.
/// The type of calendar to override with. For example System.Globalization.HebrewCalendar.
private void SetCalendarCulture(System.Globalization.DateTimeFormatInfo overrideInfo, object overrideCalendar)
{
// Use refelction to set the date time info
typeof(System.Globalization.DateTimeFormatInfo).GetField("calendar",
System.Reflection.BindingFlags.Public
System.Reflection.BindingFlags.Instance
System.Reflection.BindingFlags.NonPublic).SetValue(overrideInfo, overrideCalendar);
object obj = typeof(System.Globalization.DateTimeFormatInfo).GetField("m_cultureTableRecord",
System.Reflection.BindingFlags.Public
System.Reflection.BindingFlags.Instance
System.Reflection.BindingFlags.NonPublic).GetValue(overrideInfo);
obj.GetType().GetMethod("UseCurrentCalendar",
System.Reflection.BindingFlags.NonPublic
System.Reflection.BindingFlags.Instance).Invoke(obj,
new object[] { overrideCalendar.GetType().GetProperty("ID",
System.Reflection.BindingFlags.Instance
System.Reflection.BindingFlags.NonPublic).GetValue(overrideCalendar, null) });
typeof(System.Globalization.CultureInfo).GetField("calendar",
System.Reflection.BindingFlags.Public
System.Reflection.BindingFlags.Instance
System.Reflection.BindingFlags.NonPublic).SetValue(System.Globalization.CultureInfo.CurrentCulture, overrideCalendar);
// If the override errors just log and continue
try
{
System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat = overrideInfo;
System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat = overrideInfo;
}
catch (InvalidOperationException invalidException)
{
if (logger.IsErrorEnabled)
{
logger.Error(string.Format("Unable to override {0} calendar with {1} calendar.",
System.Globalization.CultureInfo.CurrentCulture.DisplayName,
overrideCalendar), invalidException);
}
}
}

Wednesday 4 June 2008

CATSEARCH and special characters like the quotation mark

This is a good example of how to make use of PRINTJOINS to escape special characters:

http://forums.oracle.com/forums/thread.jspa?messageID=1369280