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

4
我想将DateTime输出为字符串,在操作系统首选项中指定的格式。我不想提供显式的格式模式,而是要使用用户在区域面板中定义的内容。
例如,在我的Windows 10系统上,格式如下所示:控制面板 > 区域 > 格式
- 短日期:"yyyy-MM-dd" - 短时间:"HH:mm"
当在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));
    }
}

我假设我需要以某种方式访问在操作系统区域面板中定义的文化信息,并使用它来格式化字符串,但这就是我迷失的地方。
CurrentCulture似乎正在使用"M / d / yyyy"的ShortDate和"h:mm tt"的ShortTime,这不是我在区域面板中设置的内容。
如何按照用户在Windows上存储的区域面板中指定的格式将DateTime输出为字符串,而不事先知道该模式是什么?
最终,我希望字符串的格式与Windows资源管理器使用的格式相同,类似于"ShortDate ShortTime"。

2
以下内容是从我的PC上运行的.NET应用程序(非Unity)调用的。在我的区域面板中,现在使用非标准的“ddd d-MMM-yy”日期格式和标准的“hh:mm:ss tt”时间格式,它们在Windows资源管理器和屏幕左下角的时钟小部件中使用,并且CurrentUICulture和CurrentCulture也使用它们,DateTime.Now.ToString(CultureInfo.CurrentCulture)返回“Tue 28-Jul-20 7:13:20 AM”。调用CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePatternCultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern也会返回它们的正确值。 - Martheen
2
由于Fiddle不会意识到您的PC区域设置,因此问题似乎出在Unity方面。他们的论坛间歇性地提到了与区域格式有关的问题,这些问题应该在以后的版本中得到解决。如果您能够可靠地重现CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern忽略您在区域面板上设置的自定义格式,请报告一个错误。 - Martheen
啊,好的。我错误地认为.NET Fiddle会神奇地使用我的系统设置,但它不具备访问那些信息的能力是有道理的。这似乎很可能是一个Unity的问题。谢谢! - Michael Ryan
1
很有趣,这个问题仍然没有被解决... 这是一个非常旧的已知问题,并且存在一个解决方法 - derHugo
@derHugo 我之前尝试了那个解决方法,但结果与Unity相同,即短日期和时间是“en-US”(“M/d/yyyy”和“h:mm tt”)的默认值,而不是我在区域设置中设置的值(“yyyy-MM-dd”和“HH:mm”)。当然,kernel32.dll方法调用指引了我正确的方向,因为DLL中还包含另一种方法,可以给我我想要的东西。 - Michael Ryan
2个回答

2
正如Martheen在评论中所述,Unity编辑器似乎忽略或覆盖了操作系统提供的CurrentCulture。
更新:
我找到了一个解决方案,可以返回我要查找的数据,下面将包括该解决方案。首先,我会更详细地描述问题。
在我的Windows 10系统上,我的区域设置如下所示:
格式:英语(美国)
短日期:“yyyy-MM-dd”
短时间:“HH:mm”
长时间:“HH:mm:ss”
这些属性的默认“en-US”设置为:
格式:英语(美国)
短日期:“M/d/yyyy”
短时间:“h:mm tt”
长时间:“h:mm:ss tt”
我没有自定义“长日期”格式或任何其他设置。
在Unity内部,当访问Thread.CurrentThread.CurrentCulture时,它似乎返回正确的文化,但是处于其默认状态。好像当Unity加载时,它看到正在使用“en-US”,但然后用默认值覆盖了文化。类似于这样:
var culture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture.Name);

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

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

通过调用包含在kernel32.dll中的GetLocaleInfoEx()方法并传递特定参数,我可以访问当前系统中实际的区域设置。以下方法可以返回短日期、短时间和长时间的格式模式。而GetDateTimeFormat()为我返回当前文化的组合短日期和长时间:"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);
}

1
这似乎只是 Windows 的解决方案。 - krock
是的,这个解决方案只适用于Windows。我还没有测试Unity在Mac上如何处理CurrentCulture,以及它是否像在Windows下那样被重置。所以我不确定我的原始问题甚至是否在Mac上可以再现。 - Michael Ryan

0

看起来 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"));
    }

谢谢。我最初是在寻找一个解决方案,能够获取操作系统中由用户定义的设置,并且我已经采用了像你提供的字符串字面量这样的方式,直到我发现了一种能够获取我所需的实际值的方法。 - Michael Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接