Why doesn't DateTime.ToShortTimeString() respect the Short Time format in "Regional and Language Settings"?
Asked Answered
S

3

10

I have run into an issue that is probably due to my mis-understanding of how the DateTime.ToShortTimeString() method works. When formatting time strings with this function, I was assuming that it would respect the "Short Time" setting in Windows 7's Format settings

Control Panel -> Clock, Language and Region -> Region and Language -> Formats Tab.

However .NET seems to select a short time format not based upon this setting but based upon the current culture:

Region and Language -> Location -> Current Location

I did some testing on Windows 7 RC:

Culture: en-GB, 6AM: 06:00, 6PM: 18:00 // HH:mm (United Kingdom)
Culture: en-GB, 6AM: 06:00, 6PM: 18:00 // hh:mm (United Kingdom)
Culture: en-US, 6AM: 6:00 AM, 6PM: 6:00 PM // HH:mm (United States)
Culture: en-US, 6AM: 6:00 AM, 6PM: 6:00 PM // hh:mm (United States)
Culture: el-GR, 6AM: 6:00 πμ, 6PM: 6:00 μμ // HH:mm (Greece)
Culture: el-GR, 6AM: 6:00 πμ, 6PM: 6:00 μμ // hh:mm (Greece)

I used el-GR as that was the culture that the user that reported the problem with, he also tested this on Vista SP2 and Win 7 RC with the same result.

The question is two-fold really: 1) What is my misunderstanding of .NET and Windows Formats? 2) What is the best solution to create a short format time string (HH:mm or hh:mm tt) based upon the operating system, ideally this should work in Mono so I would prefer to avoid reading from the registry or P/Invoke.

Method used to generate the above, for future reference and testing.

[STAThread]
static void Main(string[] args)
{
    CultureInfo culture = CultureInfo.CurrentCulture;

    DateTime sixAm = new DateTime(2009, 07, 05, 6, 0, 0); // 6AM 
    DateTime sixPm = new DateTime(2009, 07, 05, 18, 0, 0); // 6PM

    string sixAmString = sixAm.ToShortTimeString();
    string sixPmString = sixPm.ToShortTimeString();

    string format = "Culture: {0}, 6AM: {1}, 6PM: {2}";

    string output = String.Format(format, culture, sixAmString, sixPmString);
    Console.WriteLine(output);
    Clipboard.Clear();
    Clipboard.SetText(output);

    Console.ReadKey();
}

Update: Based upon Mike's comments below I adapted the above method with the following changes:

The following two lines

string sixAmString = sixAm.ToShortTimeString();
string sixPmString = sixPm.ToShortTimeString();

Changed to

string sixAmString = sixAm.ToString("t", culture);
string sixPmString = sixPm.ToString("t", culture);

I also changed the culture variable to use CultureInfo.CurrentUICulture.

This unfortunatly didn't work as well as I had hoped, the output regardless of the configuration of Short Time in Windows 7's Formats tab was:

Culture: en-US, 6AM: 6:00 AM, 6PM: 6:00 PM

It seems the CultureInfo.CurrentUICulture is always en-US.

Sweat answered 18/8, 2009 at 7:2 Comment(0)
S
1

In answer to each of my questions:

1) What is my misunderstanding of .NET and Windows Formats?

The short answer is, there is no link between the "Short Time" setting in "Regional and Language" settings and .NET's ShortTimePattern property. However the LongTimePattern property is dictated by the "Long Time" setting.

I adapted the above method replacing the two formatting lines to:

string sixAmString = sixAm.ToString("T", culture.DateTimeFormat);
string sixPmString = sixPm.ToString("T", culture.DateTimeFormat);

Here is the output:

Culture: en-GB, 6AM: 06:00:00, 6PM: 18:00:00 // HH:mm:ss
Culture: en-GB, 6AM: 06:00:00 AM, 6PM: 06:00:00 PM //hh:mm:ss tt

The bottom of this article explained the problem to me.

2) What is the best solution to create a short format time string (HH:mm or hh:mm tt) based upon the operating system setting?

I don't know about the best solution, but I created the following function that converts the LongTimeFormat to a ShortTimeFormat thus allowing an application to follow the users option if they change the "Long Time" (albeit it won't track the "Short Time" setting).

static string GetShortTimeString(DateTime ShortTimeString)
{
    DateTimeFormatInfo dateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat;
    string ShortTimePattern = dateTimeFormat.LongTimePattern.Replace(":ss", String.Empty);
    ShortTimePattern = ShortTimePattern.Replace(":s", String.Empty);

    return ShortTimeString.ToString(ShortTimePattern);
}

The output after making the above changes:

Culture: en-GB, 6AM: 06:00, 6PM: 18:00
Culture: en-GB, 6AM: 06:00 AM, 6PM: 06:00 PM

The P/Invoke option is to use GetTimeFormat passing the TIME_NOSECONDS using DateTime.ToString(Format) as above. I have not tested this as I would prefer to avoid using P/Invoke.

Sweat answered 18/8, 2009 at 17:44 Comment(0)
A
5

Answer to your second question is

DateTimeFormat.Format(DateTime.Now, "t", CultureInfo.CurrentUICulture);

or

DateTime.Now.ToString("t", CultureInfo.CurrentUICulture);

It’s actually always better to use explicit methods that accept CultureInfo. There is no consistency how .Net chooses what to use by default either CurrentCulture or CurrentUICulture or InvarinatCulture.

To make answer full. Also I will outline differences between cultures.

So CurrentCulture is "Control Panel -> Clock, Language and Region -> Region and Language -> Formats Tab." This is culture that you expect your calculations to be. For example you can do your accounting in US, so you have this to be configured in US.

CurrentUICulture is "Region and Language -> Display language", means that when you are emigrant from Ukraine, you want your application to be localized in UA (but all calculations is still in US).

And InvariantCulture is so called culture agnostic locale. You should use this for storing information and so on. Effectively it En-US.

Note: I could be wrong where each setting is located in windows. But you probably got an idea.

Atherosclerosis answered 18/8, 2009 at 7:12 Comment(5)
Thanks, I will do some testing with that and get in implemented. What else is affected by culture other than Date, Time, Currency, Decimal and Thousand separators?Sweat
Month, weekday names. LTR/RTL directions. Sorting of strings. Upper/Lower case operations with strings. Serialization.Atherosclerosis
CultureInfo.CurrentUICulture seems to be en-US, where else might this be configured? I have tried reboting after checking everything was set to United Kingdom. I also noticed that sometimes after running the program it dosn't pickup the culture change immediatly. However running it a second time always picks it up.Sweat
CurrentUICulture is the language version of windows you have installed. not the chosen display language. Try Console.WriteLine( CultureInfo.CurrentUICulture ) while changing display language, it will stay at en-US if you have english windows installed. Changing CurrentUICulture includes reinstalling the operating system. It is not suggested to use this for any date, time, number, list, currency or anything- formatting related. It is probably good to use it for localization of language.Tamar
@Tamar did you reloaded your profile (reboot computer or logout/login) after you changed display name? I do not have easy way to check your observation right now, but I am pretty sure you are missing something. One of my previous applications depended on this property and it was worked the way I explained in post.Atherosclerosis
T
2

I'm quite sure the fact that the short time format string isn't used in DateTime.ToShortTimeString() or DateTime.ToString("t") is a bug, because it was fixed in .NET framework 4.0.

Tamar answered 3/4, 2013 at 17:45 Comment(1)
this should be the accepted answer as this explains why the ShortTimePattern does not use the current region settings. Quick to verify with a unit test in a 3.5 project and one in a 4.0 project and you will see that the 3.5 ignores whatever is set in the region and language dialogTranscendentalistic
S
1

In answer to each of my questions:

1) What is my misunderstanding of .NET and Windows Formats?

The short answer is, there is no link between the "Short Time" setting in "Regional and Language" settings and .NET's ShortTimePattern property. However the LongTimePattern property is dictated by the "Long Time" setting.

I adapted the above method replacing the two formatting lines to:

string sixAmString = sixAm.ToString("T", culture.DateTimeFormat);
string sixPmString = sixPm.ToString("T", culture.DateTimeFormat);

Here is the output:

Culture: en-GB, 6AM: 06:00:00, 6PM: 18:00:00 // HH:mm:ss
Culture: en-GB, 6AM: 06:00:00 AM, 6PM: 06:00:00 PM //hh:mm:ss tt

The bottom of this article explained the problem to me.

2) What is the best solution to create a short format time string (HH:mm or hh:mm tt) based upon the operating system setting?

I don't know about the best solution, but I created the following function that converts the LongTimeFormat to a ShortTimeFormat thus allowing an application to follow the users option if they change the "Long Time" (albeit it won't track the "Short Time" setting).

static string GetShortTimeString(DateTime ShortTimeString)
{
    DateTimeFormatInfo dateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat;
    string ShortTimePattern = dateTimeFormat.LongTimePattern.Replace(":ss", String.Empty);
    ShortTimePattern = ShortTimePattern.Replace(":s", String.Empty);

    return ShortTimeString.ToString(ShortTimePattern);
}

The output after making the above changes:

Culture: en-GB, 6AM: 06:00, 6PM: 18:00
Culture: en-GB, 6AM: 06:00 AM, 6PM: 06:00 PM

The P/Invoke option is to use GetTimeFormat passing the TIME_NOSECONDS using DateTime.ToString(Format) as above. I have not tested this as I would prefer to avoid using P/Invoke.

Sweat answered 18/8, 2009 at 17:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.