如何使用区域短日期将 DateTime 转换为字符串

How to convert DateTime to string using Region Short Date

我想以操作系统首选项中指定的格式将 DateTime 输出为字符串。我不想提供明确的格式模式,而是想使用用户在区域面板中定义的任何内容。

例如在我的 Windows 10 系统上,格式定义在 控制面板 > 区域 > 格式如下:

当文件和文件夹在 Windows 资源管理器中显示时,它们的显示格式为:“yyyy-MM-dd HH:mm”。

我应该指出,我使用的是 Unity 2019.4.4。——据我所知,覆盖CurrentCulture 或类似的东西。我已经用“unity3d”标签标记了这个问题,但它可能与 Unity 没有特别相关,除非 Unity 正在对文化做一些奇怪的事情。

我尝试了以下 .NET Fiddle,并看到了与在 Unity 中相同的结果:

using System;
using System.Globalization;

public class Program
{
    public static void Main()
    {
        long dateTimeTicks = 637314588165245627;
        DateTime dateTime = new DateTime(dateTimeTicks);

        // I thought the following methods might work, since the Region panel allow configuring the
        // short and long formats for date and time.

        Console.WriteLine(dateTime.ToShortDateString());  // 7/27/2020
        Console.WriteLine(dateTime.ToLongDateString());   // Monday, July 27, 2020
        Console.WriteLine(dateTime.ToShortTimeString());  // 3:00 PM
        Console.WriteLine(dateTime.ToLongTimeString());   // 3:00:16 PM

        // None of the standard date and time format strings work. The "o", "s", and "u" are close to
        // what I'm looking for, but I don't want to force this format, I want to use whatever the
        // user has specified in the Region preferences.

        Console.WriteLine(dateTime.ToString());                                // 7/27/2020 3:00:16 PM
        Console.WriteLine(dateTime.ToString(CultureInfo.CurrentCulture));      // 7/27/2020 3:00:16 PM
        Console.WriteLine(dateTime.ToString(CultureInfo.InvariantCulture));    // 07/27/2020 15:00:16
        Console.WriteLine(dateTime.ToString(CultureInfo.InstalledUICulture));  // 7/27/2020 3:00:16 PM

        Console.WriteLine(dateTime.ToString("d"));  // 7/27/2020
        Console.WriteLine(dateTime.ToString("D"));  // Monday, July 27, 2020
        Console.WriteLine(dateTime.ToString("f"));  // Monday, July 27, 2020 3:00 PM
        Console.WriteLine(dateTime.ToString("F"));  // Monday, July 27, 2020 3:00:16 PM
        Console.WriteLine(dateTime.ToString("g"));  // 7/27/2020 3:00 PM
        Console.WriteLine(dateTime.ToString("G"));  // 7/27/2020 3:00:16 PM
        Console.WriteLine(dateTime.ToString("m"));  // July 27
        Console.WriteLine(dateTime.ToString("o"));  // 2020-07-27T15:00:16.5245627
        Console.WriteLine(dateTime.ToString("r"));  // Mon, 27 Jul 2020 15:00:16 GMT
        Console.WriteLine(dateTime.ToString("s"));  // 2020-07-27T15:00:16
        Console.WriteLine(dateTime.ToString("t"));  // 3:00 PM
        Console.WriteLine(dateTime.ToString("T"));  // 3:00:16 PM
        Console.WriteLine(dateTime.ToString("u"));  // 2020-07-27 15:00:16Z
        Console.WriteLine(dateTime.ToString("U"));  // Monday, July 27, 2020 3:00:16 PM
        Console.WriteLine(dateTime.ToString("y"));  // July 2020

        // I considered using CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern and
        // CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern together, but they also
        // don't return what is specified in Region preferences.

        // 7/27/2020
        Console.WriteLine(dateTime.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern));
    }
}

我假设我需要以某种方式访问​​ OS 区域面板中定义的文化信息并使用它来格式化字符串,但这就是我迷路的地方。

CurrentCulture 似乎使用了“M/d/yyyy”的 ShortDate 和“h:mm tt”的 ShortTime,这不是我在“区域”面板中设置的。

如何以用户指定的格式将 DateTime 输出为字符串并存储在 Windows 的“区域”面板中,而无需提前知道该模式是什么?

最终,我希望字符串的格式与 Windows Explorer 使用的格式相同,类似于“ShortDate ShortTime”。

正如 Martheen 在评论中所述,Unity 编辑器似乎忽略或覆盖了 OS 提供的 CurrentCulture。


更新:

我找到了一个解决方案,可以 return 我正在寻找的数据,我将在下面包含这些数据。首先,我将更详细地描述问题。

在我的Windows10系统上,我的Region设置自定义如下:

  • 格式:英语(美国)
  • 短日期:“yyyy-MM-dd”
  • 短时间:“HH:mm”
  • 长时间:“HH:mm:ss”

这些属性的默认“en-US”设置是:

  • 格式:英语(美国)
  • 短日期:“M/d/yyyy”
  • 短时间:“h:mm tt”
  • 长时间:“h:mm:ss tt”

我没有自定义“长日期”格式或任何其他设置。

在 Unity 内部,当访问 Thread.CurrentThread.CurrentCulture 时,它似乎 return 正确的文化,但处于默认状态。就好像当 Unity 加载时,它看到正在使用“en-US”,但它随后用默认值覆盖了文化。像这样:

var culture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture.Name);

稍后,当尝试获取短日期和时间格式时,它们 return 默认值而不是我在区域设置中指定的值。

var culture = Thread.CurrentThread.CurrentCulture;
string shortDatePattern = culture.DateTimeFormat.ShortDatePattern; // "M/d/yyyy"
string shortTimePattern = culture.DateTimeFormat.ShortTimePattern; // "h:mm tt"

通过调用 kernel32.dll 中包含的 GetLocaleInfoEx() 方法并传递某些参数,我可以访问系统上活动的实际区域设置。以下方法可以return 短日期、短时间、长时间的格式模式。 GetDateTimeFormat() return 对我来说是当前文化的短日期和长时间的组合:“yyyy-MM-dd HH:mm:ss”。

public class LocaleFunctions
{
   public enum LCTYPE : uint
   {
      // There are 150+ values than can be passed to GetLocaleInfoEx(),
      // but most were excluded for brevity.
      //
      // See the following header file for defines with the "LOCALE_" prefix:
      //    https://github.com/wine-mirror/wine/blob/master/include/winnls.h
      //
      LOCALE_SSHORTDATE = 0x001F,
      LOCALE_STIMEFORMAT = 0x1003,
      LOCALE_SSHORTTIME = 0x0079
   }

   public static string GetDateTimeFormat()
   {
      var locale = Thread.CurrentThread.CurrentCulture.Name;
      return GetShortDateFormat(locale) + " " + GetLongTimeFormat(locale);
   }

   public static string GetLongTimeFormat(string locale, int maxLength = 32)
   {
      var data = new StringBuilder(maxLength);
      GetLocaleInfoEx(locale, LCTYPE.LOCALE_STIMEFORMAT, data, maxLength);
      return data.ToString();
   }

   public static string GetShortDateFormat(string locale, int maxLength = 32)
   {
      var data = new StringBuilder(maxLength);
      GetLocaleInfoEx(locale, LCTYPE.LOCALE_SSHORTDATE, data, maxLength);
      return data.ToString();
   }

   public static string GetShortTimeFormat(string locale, int maxLength = 32)
   {
      var data = new StringBuilder(maxLength);
      GetLocaleInfoEx(locale, LCTYPE.LOCALE_SSHORTTIME, data, maxLength);
      return data.ToString();
   }

   // pInvoke declarations
   [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
   private static extern int GetLocaleInfoEx(
      string lpLocaleName,
      LCTYPE lcType,
      StringBuilder lpLCData,
      int cchData);
}

似乎 Unity 无法直接从操作系统获取值,但您仍然可以使用所有系统选项格式化 DateTime,如下所示:

    System.DateTime currentTime = System.DateTime.Now;

    void Start()
    {
        //Short date
        Debug.Log(currentTime.ToString("M/d/yyyy"));
        Debug.Log(currentTime.ToString("M/d/yy"));
        Debug.Log(currentTime.ToString("MM/dd/yy"));
        Debug.Log(currentTime.ToString("MM/dd/yyyy"));
        Debug.Log(currentTime.ToString("yy/MM/dd"));
        Debug.Log(currentTime.ToString("yyyy-MM-dd"));
        Debug.Log(currentTime.ToString("dd-MMM-yy"));

        //Long date
        Debug.Log(currentTime.ToString("dddd, MMMM d, yyyy"));
        Debug.Log(currentTime.ToString("MMMM d, yyyy"));
        Debug.Log(currentTime.ToString("dddd, d MMMM, yyyy"));
        Debug.Log(currentTime.ToString("d MMMM, yyyy"));

        //Short time
        Debug.Log(currentTime.ToString("h:mm tt"));
        Debug.Log(currentTime.ToString("hh:mm tt"));
        Debug.Log(currentTime.ToString("H:mm"));
        Debug.Log(currentTime.ToString("HH:mm"));

        //Long time
        Debug.Log(currentTime.ToString("h:mm:ss tt"));
        Debug.Log(currentTime.ToString("hh:mm:ss t"));
        Debug.Log(currentTime.ToString("H:mm:ss"));
        Debug.Log(currentTime.ToString("HH:mm:ss"));
    }