【问题标题】:Is this c# code enough to handle globalization?这个 c# 代码是否足以处理全球化?
【发布时间】:2015-03-28 11:57:36
【问题描述】:

我的 C# winforms 程序将在以下国家/地区使用

United Kingdom  : date format day-month-year , currency separator is '.'
United States   : date format month-day-year , currency separator is '.'
Denmark         : date format day-month-year , currency separator is ','

无论用户计算机上的区域设置如何,我都想让程序正常运行。我主要关心的是处理日期格式和货币字段 (语言翻译不是问题,因为程序只会显示英文文本)

为此,我决定将数据库中的所有日期以yyyy-mm-dd 格式保存,所有小数字段将以. 作为分隔符保存。
我使用 Danish_Norwegian_CI_AS collat​​ion 创建了数据库。 所以我假设数据将以上述datetime 格式和十进制格式保存,而不需要我做任何特别的事情。

我已将以下代码放入我的程序中

var cult = new CultureInfo("en-GB");
Thread.CurrentThread.CurrentCulture = cult;
Thread.CurrentThread.CurrentUICulture = cult;

CultureInfo.DefaultThreadCurrentCulture = cult;
CultureInfo.DefaultThreadCurrentUICulture = cult;

请根据您的经验告诉我上述代码是否足以使我的程序安全

【问题讨论】:

  • 我希望您的数据库不会真正将小数和日期存储为字符串列。
  • Henk - 不,所有日期都存储在 datetime 列中,小数和货币值存储在小数列中
  • 那么数据库中的日期将以yyyy-mm-dd格式保存不适用。
  • 查询取决于您的 SQL 引擎愿意理解的内容(唯一的标准化格式是美国 m/d/y)。它不依赖于客户端的语言环境。
  • @HenkHolterman - 不,这取决于数据库服务器的语言环境。在英国 m/d/y 将被解释为 d/m/y。唯一的标准是 yyyy-mm-dd:我从来没有见过这种误解。

标签: c# .net sql-server datetime


【解决方案1】:

文化只在以下两种情况下应用

  • 将值的字符串表示形式转换为本机格式,这称为“解析”。

  • 将值的本机格式转换为字符串表示,这称为“格式化”。

当您将datetimedecimal 或其他类型存储到数据库中时,它以native 格式存储。在 SQL 数据库中,这通常是一些您永远不会直接使用的紧凑二进制值。

考虑以下 SQL:

declare @dt datetime
set @dt = '01/02/2015 12:34:56'
select @dt

在第一行,我们声明了一个datetime 类型的变量。不是字符串,是占用8字节内存或磁盘的特定数据类型。

在第二行中,我们为变量分配了一个字符串值。 SQL 解析字符串,将其转换为datetime,以便将其存储在@dt 变量中。实际存储的值有一个十六进制表示形式0x0000A41400CF5940

当它进行解析时,应用了代码运行环境的当前文化。因为我在美国,所以它把日期解释为 1 月 2 日。如果我在欧洲,它会将日期解释为 2 月 1 日(将内部值更改为 0x0000A43200CF5940)。

使用yyyy-mm-dd 格式的日期可以避免误解,但这并不意味着实际值存储为该格式的字符串。只是格式是明确的,因此无论文化如何,都会以相同的方式解析

在上面的第三行代码中,我们选择变量以将其包含在结果集中。尽管我们在没有任何转换的情况下以其原生形式选择它,但我们最终以字符串表示形式看到它。如果您在 SQL Server Management Studio 等工具中运行查询,则输出窗口会将本机值格式化为字符串,以便您读取它们。这样做时,将再次应用当前区域性。 SQL 的默认设置是以yyyy-mm-dd 格式显示日期,而不是特定于文化的格式。但其他值(例如小数)将使用当前区域性的分隔符。

如果您实际上不是在 SSMS 中运行它,而是通过 SqlDataReader 中的 自己的 代码检索结果(例如),那么格式化永远不会发生。阅读器使用the mappings shown here 将SQL 的二进制本机值直接映射到适当的.NET 本机类型。 SQL datetime 本地映射到 .NET DateTime

DateTime dt = (DateTime) reader["dt"];

现在,你经常看到有人做这样的傻事:

DateTime dt = Convert.ToDateTime(reader["dt"].ToString());

这很浪费,因为值已经DateTime,并且这段代码会使用当前的文化来格式化字符串,然后再次使用它来解析字符串。这是无缘无故的大量字符串操作。

最终,在您的 .NET 代码中,您最终将使用该 DateTime 值并将其转换为某处的字符串以供输出。当你这样做时,那就是你应用​​当前文化的时候。

同样,当您收到来自用户的输入字符串时(例如在填写表单时),您再次使用当前文化解析值到DateTime

本机数据类型不是字符串 - 因此不受文化影响。

注意事项:

  • 如果您想查看任何 SQL 数据类型的本机二进制形式的十六进制表示,您可以使用类似:select convert(varbinary, @dt)

  • 请注意您工作的任何地方的原生格式。如果您正在写入 http 流、文本文件或文档数据库等,则字符串表示确实确实很重要,因为字符串 本机格式在这些情况下。

为了证明这适用于多个日期,请考虑:

select 123, 123.45, convert(varbinary, 123), convert(varbinary, 123.45)

--results:    123    123.45    0x0000007B    0x0502000139300000

【讨论】:

  • 马特,你提到了SSMS you actually retrieved the results through your code in a SqlDataReader。只是想知道,在线信息在哪里可用?
  • @Love - 抱歉,我不明白你在问什么。
  • 我的意思是,如果您在SSMS 中运行查询,引擎盖下的代码实际上使用SqlDataReader 来检索结果?换句话说,如果我们通过红门反射或IL Spy 检查SSMS 的dll。它包含SqlDataReader。这是你的意思吗?
  • 我不知道 SSMS 在幕后做了什么。我正在比较查看 SSMS 中的结果,它将显示格式化的字符串,与从自定义 .net 应用程序查询 SQL。不知道您如何将其解释为深入 SSMS 内部。
  • 别担心,我误会了。
猜你喜欢
  • 1970-01-01
  • 2013-03-13
  • 1970-01-01
  • 1970-01-01
  • 2022-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多