public value class DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable
public value class DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable, System::Runtime::Serialization::ISerializable
public value class DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, ISpanFormattable, System::Runtime::Serialization::ISerializable
public value class DateTime : IComparable, IConvertible, IFormattable
public value class DateTime : IComparable, IComparable<DateTime>, IEquatable<DateTime>, IFormattable
public struct DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable
public struct DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable, System.Runtime.Serialization.ISerializable
public struct DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, ISpanFormattable, System.Runtime.Serialization.ISerializable
[System.Serializable]
public struct DateTime : IComparable, IConvertible, IFormattable
[System.Serializable]
public struct DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable, System.Runtime.Serialization.ISerializable
public struct DateTime : IComparable, IComparable<DateTime>, IEquatable<DateTime>, IFormattable
type DateTime = struct
    interface IConvertible
    interface IFormattable
type DateTime = struct
    interface IConvertible
    interface IFormattable
    interface ISerializable
type DateTime = struct
    interface IConvertible
    interface ISpanFormattable
    interface IFormattable
    interface ISerializable
[<System.Serializable>]
type DateTime = struct
    interface IFormattable
    interface IConvertible
[<System.Serializable>]
type DateTime = struct
    interface IFormattable
    interface IConvertible
    interface ISerializable
type DateTime = struct
    interface IFormattable
Public Structure DateTime
Implements IComparable, IComparable(Of DateTime), IConvertible, IEquatable(Of DateTime), IFormattable
Public Structure DateTime
Implements IComparable, IComparable(Of DateTime), IConvertible, IEquatable(Of DateTime), IFormattable, ISerializable
Public Structure DateTime
Implements IComparable, IComparable(Of DateTime), IConvertible, IEquatable(Of DateTime), ISerializable, ISpanFormattable
Public Structure DateTime
Implements IComparable, IConvertible, IFormattable
Public Structure DateTime
Implements IComparable, IComparable(Of DateTime), IEquatable(Of DateTime), IFormattable
Object
DateTime

日本历法中的年号是根据天皇统治来命名的,因此预计会发生变化。 例如,2019 年 5 月 1 日在 JapaneseCalendar JapaneseLunisolarCalendar 中标志着令和年号的开始。 这种年号的变化会影响使用这些日历的所有应用程序。 有关详细信息以及如何确定应用程序是否受影响,请参阅在 .net 中的日式日历中处理新时代 。 若要了解如何在 Windows 系统上测试应用程序以确保其应用程序更改的就绪性,请参阅 准备应用程序以进行日本时代更改 。 对于 .NET 中支持多个纪元的日历的功能,以及在处理支持多个纪元的日历时的最佳做法,请参阅使用 纪元

本文中的一些 C# 示例运行在 Try.NET 内联代码运行程序和演练环境中。 选择“运行”按钮以在交互窗口中运行示例。 执行代码后,可通过再次选择“运行”来修改它并运行已修改的代码。 已修改的代码要么在交互窗口中运行,要么编译失败时,交互窗口将显示所有 C# 编译器错误消息。

Try.NET 内联代码运行程序和演练环境的 本地时区 是协调世界时 (UTC)。 这可能会影响用于说明 DateTime DateTimeOffset TimeZoneInfo 类型及其成员的示例的行为和输出。

本文包含几个使用 类型 DateTime 的示例:

初始化示例

  • 调用构造函数
  • 调用隐式无参数构造函数
  • 返回值中的赋值
  • 分析表示日期和时间的字符串
  • Visual Basic初始化日期和时间的语法
  • DateTime 对象格式化为字符串

  • 使用默认日期时间格式
  • 使用特定区域性设置日期和时间的格式
  • 使用标准或自定义格式字符串设置日期时间的格式
  • 同时指定格式字符串和特定区域性
  • 使用 Web 服务的 ISO 8601 标准设置日期时间的格式
  • 将字符串分析为 DateTime 对象

  • 使用 Parse TryParse 将字符串转换为日期和时间
  • 使用 ParseExact TryParseExact 以已知格式转换字符串
  • 从 ISO 8601 字符串表示形式转换为日期和时间
  • DateTime 分辨率

  • 探索日期和时间值的解析
  • 比较容错内的相等性
  • 区域性和日历

  • 使用区域性特定日历显示日期和时间值
  • 根据区域性特定日历分析字符串
  • 从特定区域性的日历初始化日期和时间
  • 使用特定区域性的日历访问日期和时间属性
  • 使用区域性特定日历检索一年中的星期
  • 将日期和时间值保留为本地时区中的字符串
  • 以区域性和时间固定格式将日期和时间值保留为字符串
  • 将日期和时间值持久保存为整数
  • 使用 持久保存日期和时间值 XmlSerializer
  • 使用 持久保存日期和时间值 BinaryFormatter
  • 使用时区数据持久保存日期和时间值
  • 本部分包含有关结构的许多常见 DateTime 用途的主题:

  • 初始化 DateTime 对象
  • DateTime 值及其字符串表示形式
  • 分析字符串中的 DateTime 值
  • DateTime 值
  • DateTime 操作
  • DateTime 解析
  • DateTime 值和日历
  • 保留 DateTime 值
  • DateTime 与 TimeSpan
  • 在容错范围内比较相等性
  • COM 互操作注意事项
  • 值类型表示日期和时间,其值范围为 0001 年 1 月 1 日午夜 DateTime ) 00:00:00 ( (Anno Domini (Common Era) 到 9999 年 12 月 31 日下午 11:59:59。 晚上 11:59:59。

    时间值以 100 纳秒为单位(称为时钟周期)进行度量。 特定日期是自 0001 年 1 月 1 日午夜 12:00 以来的滴答数。 (日历) C.E.) 。 GregorianCalendar 该数字不包括将按闰秒添加的刻度。 例如,时钟周期值 31241376000000000L 表示星期五,0100 年 1 月 1 日午夜 12:00:00。 DateTime 值始终在显式或默认日历的上下文中表示。

    如果要使用要转换为其他时间间隔(如分钟或秒)的刻度值,则应该使用 、 或 常量 TimeSpan.TicksPerDay TimeSpan.TicksPerHour TimeSpan.TicksPerMinute TimeSpan.TicksPerSecond TimeSpan.TicksPerMillisecond 来执行转换。 例如,若要将指定刻度数表示的秒数添加到值的 分量中, Second DateTime 可以使用表达式 dateValue.Second + nTicks/Timespan.TicksPerSecond

    可以在 GitHub 上的文档存储库中的 Visual Basic 或 C# 中查看本文中整个示例集的GitHub。

    用于处理 DateTime 特定时区中的日期和时间值的结构的替代方法是 DateTimeOffset 结构。 结构将日期和时间信息存储在私有字段中,以及该日期和时间在私有字段中不同于 DateTimeOffset DateTime UTC 的 Int16 分钟数。 这使值能够反映特定时区的时间,而值可以明确反映 UTC 和本地 DateTimeOffset DateTime 时区的时间。 有关使用日期和时间值时何时使用 结构的讨论,请参阅 DateTime DateTimeOffset DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间选择 。

    初始化 DateTime 对象

    可以通过许多不同的方式将初始 DateTime 值分配给新值:

  • 调用构造函数(在构造函数中指定值的参数)或使用隐式无参数构造函数。
  • DateTime 分配给属性或方法的返回值。
  • 从值的 DateTime 字符串表示形式分析值。
  • 使用Visual Basic特定语言功能实例化 DateTime
  • 以下代码片段显示了每个代码片段的示例:

    调用构造函数

    调用构造函数的任何重载,这些重载指定日期和时间值元素 (如年、月、日或日期的刻度 DateTime 数) 。 以下代码使用构造函数创建指定年、月、 DateTime 日、小时、分钟和秒的特定日期。

    Dim date1 As New Date(2008, 5, 1, 8, 30, 52) var date1 = new DateTime(2008, 5, 1, 8, 30, 52); Console.WriteLine(date1);

    若要将 初始化为其默认值,请调用结构的隐式无 DateTime DateTime 参数构造函数。 (有关值类型的隐式无参数构造函数的详细信息,请参阅值类型 .) 某些编译器还支持声明值,而无需显式为其赋值 DateTime 。 创建不带显式初始化的值也会导致默认值。 以下示例演示 C# 和 Visual Basic 中的隐式无参数构造函数,以及未在 Visual Basic DateTime DateTime 中赋值的声明。

    Dim dat1 As DateTime ' The following method call displays 1/1/0001 12:00:00 AM. Console.WriteLine(dat1.ToString(System.Globalization.CultureInfo.InvariantCulture)) ' The following method call displays True. Console.WriteLine(dat1.Equals(Date.MinValue)) Dim dat2 As New DateTime() ' The following method call displays 1/1/0001 12:00:00 AM. Console.WriteLine(dat2.ToString(System.Globalization.CultureInfo.InvariantCulture)) ' The following method call displays True. Console.WriteLine(dat2.Equals(Date.MinValue)) var dat1 = new DateTime(); // The following method call displays 1/1/0001 12:00:00 AM. Console.WriteLine(dat1.ToString(System.Globalization.CultureInfo.InvariantCulture)); // The following method call displays True. Console.WriteLine(dat1.Equals(DateTime.MinValue));
    分配计算值

    你可以为 DateTime 对象分配属性或方法返回的日期和时间值。 以下示例将当前日期和时间、当前 协调世界时 (UTC) 日期和时间以及当前日期分配给三个新 DateTime 变量。

    Dim date1 As Date = Date.Now Dim date2 As Date = Date.UtcNow Dim date3 As Date = Date.Today DateTime date1 = DateTime.Now; DateTime date2 = DateTime.UtcNow; DateTime date3 = DateTime.Today;
    分析表示 DateTime 的字符串

    、、 Parse ParseExact TryParse TryParseExact 方法均将字符串转换为其等效的日期和时间值。 以下示例使用 和 Parse ParseExact 方法分析字符串并将其转换为 DateTime 值。 第二种格式使用 ISO 8601 标准支持的表单来表示字符串格式的日期和时间。 此标准表示形式通常用于在 Web 服务中传输日期信息。

    Dim dateString As String = "5/1/2008 8:30:52 AM" Dim date1 As Date = Date.Parse(dateString, System.Globalization.CultureInfo.InvariantCulture) Dim iso8601String As String = "20080501T08:30:52Z" Dim dateISO8602 As Date = DateTime.ParseExact(iso8601String, "yyyyMMddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture) Console.WriteLine(dateISO8602) var dateString = "5/1/2008 8:30:52 AM"; DateTime date1 = DateTime.Parse(dateString, System.Globalization.CultureInfo.InvariantCulture); var iso8601String = "20080501T08:30:52Z"; DateTime dateISO8602 = DateTime.ParseExact(iso8601String, "yyyyMMddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture);

    TryParse TryParseExact 方法指示字符串是否是值的有效表示形式,如果是, DateTime 则执行转换。

    特定于语言的语法Visual Basic

    下面的 Visual Basic 语句初始化新 DateTime 值。

    Dim date1 As Date = #5/1/2008 8:30:52AM#

    DateTime 值及其字符串表示形式

    在内部,所有值都表示为自 DateTime 20001 年 1 月 1 (日午夜 12:00:00 以来) 100 纳秒间隔数的刻度数。 实际 DateTime 值与该值在显示时显示的方式无关。 值的外观是格式设置操作的结果,该操作将 DateTime 值转换为其字符串表示形式。

    日期和时间值的外观取决于区域性、国际标准、应用程序要求和个人偏好。 结构 DateTime 通过 重载灵活设置日期和时间值的格式 ToString 。 默认方法使用当前区域性的短日期和时间模式返回日期和时间值的 DateTime.ToString() 字符串表示形式。 下面的示例使用默认 DateTime.ToString() 方法。 它使用当前区域性的短日期和时间模式显示日期和时间。 en-US 区域性是运行示例的计算机上的当前区域性。

    var date1 = new DateTime(2008, 3, 1, 7, 0, 0); Console.WriteLine(date1.ToString()); // For en-US culture, displays 3/1/2008 7:00:00 AM Dim date1 As Date = #3/1/2008 7:00AM# Console.WriteLine(date1.ToString()) ' For en-US culture, displays 3/1/2008 7:00:00 AM

    可能需要设置特定区域性的日期格式,以支持 Web 方案,其中服务器可能与客户端采用不同的区域性。 使用 方法指定区域性,以创建特定区域性中的短日期 DateTime.ToString(IFormatProvider) 和长时间表示形式。 下面的示例使用 DateTime.ToString(IFormatProvider) 方法来显示使用 fr-fr 区域性的短日期和长时间模式的日期和时间。

    var date1 = new DateTime(2008, 3, 1, 7, 0, 0); Console.WriteLine(date1.ToString(System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR"))); // Displays 01/03/2008 07:00:00 Dim date1 As Date = #3/1/2008 7:00AM# Console.WriteLine(date1.ToString(System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR"))) ' Displays 01/03/2008 07:00:00

    其他应用程序可能需要日期的不同字符串表示形式。 DateTime.ToString(String) 方法使用当前区域性的格式设置约定返回由标准或自定义格式说明符定义的字符串表示形式。 下面的示例使用 DateTime.ToString(String) 方法来显示 en-us 区域性(运行该示例的计算机上的当前区域性)的完整日期和时间模式。

    var date1 = new DateTime(2008, 3, 1, 7, 0, 0); Console.WriteLine(date1.ToString("F")); // Displays Saturday, March 01, 2008 7:00:00 AM Dim date1 As Date = #3/1/2008 7:00AM# Console.WriteLine(date1.ToString("F")) ' Displays Saturday, March 01, 2008 7:00:00 AM

    最后,可以使用方法指定区域性和格式 DateTime.ToString(String, IFormatProvider) 。 下面的示例使用 DateTime.ToString(String, IFormatProvider) 方法显示 fr-fr 区域性的完整日期和时间模式。

    var date1 = new DateTime(2008, 3, 1, 7, 0, 0); Console.WriteLine(date1.ToString("F", new System.Globalization.CultureInfo("fr-FR"))); // Displays samedi 1 mars 2008 07:00:00 Dim date1 As Date = #3/1/2008 7:00AM# Console.WriteLine(date1.ToString("F", New System.Globalization.CultureInfo("fr-FR"))) ' Displays samedi 1 mars 2008 07:00:00

    DateTime.ToString(String) 重载还可与自定义格式字符串一起用于指定其他格式。 下面的示例演示如何使用通常用于 web 服务的 ISO 8601 标准格式来设置字符串的格式。 Iso 8601 格式没有对应的标准格式字符串。

    var date1 = new DateTime(2008, 3, 1, 7, 0, 0); Console.WriteLine(date1.ToString("yyyyMMddTHH:mm:ssZ")); // Displays 20080301T07:00:00Z Dim date1 As Date = #3/1/2008 7:00AM# Console.WriteLine(date1.ToString("yyyyMMddTHH:mm:ssZ")) ' Displays 20080301T07:00:00Z

    有关设置值格式的详细信息 DateTime ,请参阅 标准日期和时间格式字符串 自定义日期和时间格式字符串

    分析字符串中的日期时间值

    分析将日期和时间的字符串表示形式转换为 DateTime 值。 通常,在应用程序中,日期和时间字符串具有两种不同的用法:

  • 日期和时间采用各种形式,并反映了当前区域性或特定区域性的约定。 例如,应用程序允许其当前区域性为 en-us 的用户将日期值输入为 "12/15/2013" 或 "12 月15日,2013"。 它允许当前区域性为 en 的用户将日期值输入为 "15/12/2013" 或 "15 12 月12日 2013"。

  • 日期和时间以预定义的格式表示。 例如,应用程序将日期序列化为 "20130103",而不考虑应用运行的区域性。 应用程序可能需要以当前区域性的短日期格式输入日期。

    使用 Parse 或方法可以 TryParse 将字符串从区域性使用的一个通用日期和时间格式转换为一个 DateTime 值。 下面的示例演示如何使用将 TryParse 不同区域性特定格式的日期字符串转换为 DateTime 值。 它将当前区域性更改为英语 (英国) ,并调用 GetDateTimeFormats() 方法来生成日期和时间字符串的数组。 然后,它将数组中的每个元素传递给 TryParse 方法。 该示例的输出显示,分析方法能够成功转换每个特定于区域性的日期和时间字符串。

    System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-GB"); var date1 = new DateTime(2013, 6, 1, 12, 32, 30); var badFormats = new List<String>(); Console.WriteLine($"{"Date String",-37} {"Date",-19}\n"); foreach (var dateString in date1.GetDateTimeFormats()) DateTime parsedDate; if (DateTime.TryParse(dateString, out parsedDate)) Console.WriteLine($"{dateString,-37} {DateTime.Parse(dateString),-19}"); badFormats.Add(dateString); // Display strings that could not be parsed. if (badFormats.Count > 0) Console.WriteLine("\nStrings that could not be parsed: "); foreach (var badFormat in badFormats) Console.WriteLine($" {badFormat}"); // Press "Run" to see the output. Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB") Dim date1 As New DateTime(2013, 6, 1, 12, 32, 30) Dim badFormats As New List(Of String) Console.WriteLine($"{"Date String",-37} {"Date",-19}") Console.WriteLine() For Each dateString As String In date1.GetDateTimeFormats() Dim parsedDate As DateTime If DateTime.TryParse(dateString, parsedDate) Then Console.WriteLine($"{dateString,-37} {DateTime.Parse(dateString),-19:g}") badFormats.Add(dateString) End If ' Display strings that could not be parsed. If badFormats.Count > 0 Then Console.WriteLine() Console.WriteLine("Strings that could not be parsed: ") For Each badFormat In badFormats Console.WriteLine($" {badFormat}") End If ' The example displays the following output: ' Date String Date ' 01/06/2013 01/06/2013 00:00:00 ' 01/06/13 01/06/2013 00:00:00 ' 1/6/13 01/06/2013 00:00:00 ' 1.6.13 01/06/2013 00:00:00 ' 2013-06-01 01/06/2013 00:00:00 ' 01 June 2013 01/06/2013 00:00:00 ' 1 June 2013 01/06/2013 00:00:00 ' 01 June 2013 12:32 01/06/2013 12:32:00 ' 01 June 2013 12:32 01/06/2013 12:32:00 ' 01 June 2013 12:32 PM 01/06/2013 12:32:00 ' 01 June 2013 12:32 PM 01/06/2013 12:32:00 ' 1 June 2013 12:32 01/06/2013 12:32:00 ' 1 June 2013 12:32 01/06/2013 12:32:00 ' 1 June 2013 12:32 PM 01/06/2013 12:32:00 ' 1 June 2013 12:32 PM 01/06/2013 12:32:00 ' 01 June 2013 12:32:30 01/06/2013 12:32:30 ' 01 June 2013 12:32:30 01/06/2013 12:32:30 ' 01 June 2013 12:32:30 PM 01/06/2013 12:32:30 ' 01 June 2013 12:32:30 PM 01/06/2013 12:32:30 ' 1 June 2013 12:32:30 01/06/2013 12:32:30 ' 1 June 2013 12:32:30 01/06/2013 12:32:30 ' 1 June 2013 12:32:30 PM 01/06/2013 12:32:30 ' 1 June 2013 12:32:30 PM 01/06/2013 12:32:30 ' 01/06/2013 12:32 01/06/2013 12:32:00 ' 01/06/2013 12:32 01/06/2013 12:32:00 ' 01/06/2013 12:32 PM 01/06/2013 12:32:00 ' 01/06/2013 12:32 PM 01/06/2013 12:32:00 ' 01/06/13 12:32 01/06/2013 12:32:00 ' 01/06/13 12:32 01/06/2013 12:32:00 ' 01/06/13 12:32 PM 01/06/2013 12:32:00 ' 01/06/13 12:32 PM 01/06/2013 12:32:00 ' 1/6/13 12:32 01/06/2013 12:32:00 ' 1/6/13 12:32 01/06/2013 12:32:00 ' 1/6/13 12:32 PM 01/06/2013 12:32:00 ' 1/6/13 12:32 PM 01/06/2013 12:32:00 ' 1.6.13 12:32 01/06/2013 12:32:00 ' 1.6.13 12:32 01/06/2013 12:32:00 ' 1.6.13 12:32 PM 01/06/2013 12:32:00 ' 1.6.13 12:32 PM 01/06/2013 12:32:00 ' 2013-06-01 12:32 01/06/2013 12:32:00 ' 2013-06-01 12:32 01/06/2013 12:32:00 ' 2013-06-01 12:32 PM 01/06/2013 12:32:00 ' 2013-06-01 12:32 PM 01/06/2013 12:32:00 ' 01/06/2013 12:32:30 01/06/2013 12:32:30 ' 01/06/2013 12:32:30 01/06/2013 12:32:30 ' 01/06/2013 12:32:30 PM 01/06/2013 12:32:30 ' 01/06/2013 12:32:30 PM 01/06/2013 12:32:30 ' 01/06/13 12:32:30 01/06/2013 12:32:30 ' 01/06/13 12:32:30 01/06/2013 12:32:30 ' 01/06/13 12:32:30 PM 01/06/2013 12:32:30 ' 01/06/13 12:32:30 PM 01/06/2013 12:32:30 ' 1/6/13 12:32:30 01/06/2013 12:32:30 ' 1/6/13 12:32:30 01/06/2013 12:32:30 ' 1/6/13 12:32:30 PM 01/06/2013 12:32:30 ' 1/6/13 12:32:30 PM 01/06/2013 12:32:30 ' 1.6.13 12:32:30 01/06/2013 12:32:30 ' 1.6.13 12:32:30 01/06/2013 12:32:30 ' 1.6.13 12:32:30 PM 01/06/2013 12:32:30 ' 1.6.13 12:32:30 PM 01/06/2013 12:32:30 ' 2013-06-01 12:32:30 01/06/2013 12:32:30 ' 2013-06-01 12:32:30 01/06/2013 12:32:30 ' 2013-06-01 12:32:30 PM 01/06/2013 12:32:30 ' 2013-06-01 12:32:30 PM 01/06/2013 12:32:30 ' 01 June 01/06/2013 00:00:00 ' 01 June 01/06/2013 00:00:00 ' 2013-06-01T12:32:30.0000000 01/06/2013 12:32:30 ' 2013-06-01T12:32:30.0000000 01/06/2013 12:32:30 ' Sat, 01 Jun 2013 12:32:30 GMT 01/06/2013 05:32:30 ' Sat, 01 Jun 2013 12:32:30 GMT 01/06/2013 05:32:30 ' 2013-06-01T12:32:30 01/06/2013 12:32:30 ' 12:32 22/04/2013 12:32:00 ' 12:32 22/04/2013 12:32:00 ' 12:32 PM 22/04/2013 12:32:00 ' 12:32 PM 22/04/2013 12:32:00 ' 12:32:30 22/04/2013 12:32:30 ' 12:32:30 22/04/2013 12:32:30 ' 12:32:30 PM 22/04/2013 12:32:30 ' 12:32:30 PM 22/04/2013 12:32:30 ' 2013-06-01 12:32:30Z 01/06/2013 05:32:30 ' 01 June 2013 19:32:30 01/06/2013 19:32:30 ' 01 June 2013 19:32:30 01/06/2013 19:32:30 ' 01 June 2013 07:32:30 PM 01/06/2013 19:32:30 ' 01 June 2013 7:32:30 PM 01/06/2013 19:32:30 ' 1 June 2013 19:32:30 01/06/2013 19:32:30 ' 1 June 2013 19:32:30 01/06/2013 19:32:30 ' 1 June 2013 07:32:30 PM 01/06/2013 19:32:30 ' 1 June 2013 7:32:30 PM 01/06/2013 19:32:30 ' June 2013 01/06/2013 00:00:00 ' June 2013 01/06/2013 00:00:00

    使用 ParseExact TryParseExact 方法可将必须与特定格式或格式匹配的字符串转换为 DateTime 值。 将一个或多个日期和时间格式字符串指定为分析方法的参数。 下面的示例使用 TryParseExact(String, String[], IFormatProvider, DateTimeStyles, DateTime) 方法将必须以 "yyyyMMdd" 格式或 "HHmmss" 格式的字符串转换为 DateTime 值。

    string[] formats = { "yyyyMMdd", "HHmmss" }; string[] dateStrings = { "20130816", "20131608", " 20130816 ", "115216", "521116", " 115216 " }; DateTime parsedDate; foreach (var dateString in dateStrings) if (DateTime.TryParseExact(dateString, formats, null, System.Globalization.DateTimeStyles.AllowWhiteSpaces | System.Globalization.DateTimeStyles.AdjustToUniversal, out parsedDate)) Console.WriteLine($"{dateString} --> {parsedDate:g}"); Console.WriteLine($"Cannot convert {dateString}"); // The example displays the following output: // 20130816 --> 8/16/2013 12:00 AM // Cannot convert 20131608 // 20130816 --> 8/16/2013 12:00 AM // 115216 --> 4/22/2013 11:52 AM // Cannot convert 521116 // 115216 --> 4/22/2013 11:52 AM Dim formats() As String = {"yyyyMMdd", "HHmmss"} Dim dateStrings() As String = {"20130816", "20131608", " 20130816 ", "115216", "521116", " 115216 "} Dim parsedDate As DateTime For Each dateString As String In dateStrings If DateTime.TryParseExact(dateString, formats, Nothing, DateTimeStyles.AllowWhiteSpaces Or DateTimeStyles.AdjustToUniversal, parsedDate) Then Console.WriteLine($"{dateString} --> {parsedDate:g}") Console.WriteLine($"Cannot convert {dateString}") End If ' The example displays the following output: ' 20130816 --> 8/16/2013 12:00 AM ' Cannot convert 20131608 ' 20130816 --> 8/16/2013 12:00 AM ' 115216 --> 4/22/2013 11:52 AM ' Cannot convert 521116 ' 115216 --> 4/22/2013 11:52 AM

    的一个常见用途 ParseExact 是转换 web 服务的字符串表示形式,通常采用 ISO 8601 标准格式。 下面的代码显示要使用的正确格式字符串:

    var iso8601String = "20080501T08:30:52Z"; DateTime dateISO8602 = DateTime.ParseExact(iso8601String, "yyyyMMddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture); Console.WriteLine($"{iso8601String} --> {dateISO8602:g}"); Dim iso8601String As String = "20080501T08:30:52Z" Dim dateISO8602 As DateTime = DateTime.ParseExact(iso8601String, "yyyyMMddTHH:mm:ssZ", CultureInfo.InvariantCulture) Console.WriteLine($"{iso8601String} --> {dateISO8602:g}")

    如果无法分析字符串,则 Parse 和方法会 ParseExact 引发异常。 TryParse TryParseExact 方法返回一个 Boolean 值,该值指示转换是成功还是失败。 TryParse TryParseExact 在性能非常重要的情况下,应使用或方法。 日期和时间字符串的分析操作往往会产生较高的故障率,并且异常处理开销较高。 如果用户输入字符串或来自未知源,请使用这些方法。

    有关分析日期和时间值的详细信息,请参阅 分析日期和时间字符串

    DateTime 值

    类型中时间值的说明 DateTime 通常使用协调世界时 (UTC) 标准来表示。 协调世界时是格林尼治标准时间 (GMT) 的国际公认的名称。 协调世界时是指以零度经度(UTC 原点)度量的时间。 夏令时不适用于 UTC。

    本地时间相对于特定时区。 时区与时区偏移量关联。 时区偏移量是从 UTC 原点开始,以小时为单位的时间范围的位移。 此外,还可以根据夏令时(添加或减去时间间隔调整)来影响本地时间。 本地时间是通过将时区偏移量添加到 UTC 并调整夏令时(如有必要)来计算的。 UTC 原点的时区偏移量为零。

    UTC 时间适用于计算、比较以及在文件中存储日期和时间。 本地时间适用于桌面应用程序的用户界面。 时区感知应用程序 (例如许多 Web 应用程序,) 还需要使用一些其他时区。

    如果 Kind 对象的属性 DateTime 为,则 DateTimeKind.Unspecified 指示表示的时间是本地时间、UTC 时间还是其他某个时区中的时间。

    日期时间解析

    作为对值执行日期和时间算法 DateTime 以测量运行时间的替代方法,可以使用 Stopwatch 类。

    Ticks 属性表示日期和时间值,单位为 1 10-秒秒。 Millisecond 属性返回日期和时间值的秒数的分之几秒。 使用对属性的重复调用 DateTime.Now 来度量经过的时间取决于系统时钟。 Windows 7 和 Windows 8 系统上的系统时钟的分辨率约为15毫秒。 此分辨率会影响小于100毫秒的小时间间隔。

    下面的示例演示了当前日期和时间值对系统时钟的分辨率的依赖关系。 在此示例中,外部循环重复了20次,而内层循环用于延迟外部循环。 如果外部循环计数器的值为10,则调用 Thread.Sleep 方法会引入5毫秒的延迟。 下面的示例演示了在对的调用后,属性更改返回的毫秒数 DateTime.Now.Milliseconds Thread.Sleep

    string output = ""; for (int ctr = 0; ctr <= 20; ctr++) output += String.Format($"{DateTime.Now.Millisecond}\n"); // Introduce a delay loop. for (int delay = 0; delay <= 1000; delay++) if (ctr == 10) output += "Thread.Sleep called...\n"; System.Threading.Thread.Sleep(5); Console.WriteLine(output); // Press "Run" to see the output. Dim output As String = "" For ctr As Integer = 0 To 20 output += Date.Now.Millisecond.ToString() + vbCrLf ' Introduce a delay loop. For delay As Integer = 0 To 1000 If ctr = 10 Then output += "Thread.Sleep called..." + vbCrLf Thread.Sleep(5) End If Console.WriteLine(output) ' The example displays output like the following: ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' 111 ' Thread.Sleep called... ' 143 ' 143 ' 143 ' 143 ' 143 ' 143 ' 143 ' 143 ' 143 ' 143

    DateTime 操作

    使用 DateTime 结构(如或)的计算 Add Subtract 不会修改结构的值。 相反,计算返回一个新的 DateTime 结构,其值为计算结果。

    时区之间的转换操作 (如 UTC 和本地时间之间,或在一个时区与另一个) 之间进行转换,则需要夏令时,但算术和比较运算不会考虑到这一点。

    DateTime 结构本身为从一个时区转换到另一个时区提供有限的支持。 您可以使用 ToLocalTime 方法将 UTC 转换为本地时间,也可以使用 ToUniversalTime 方法将本地时间转换为 UTC。 但是,类中提供了一组完整的时区转换方法 TimeZoneInfo 。 您可以使用这些方法将世界上任一时区中的时间转换为任何其他时区中的时间。

    DateTime 仅当对象表示相同时区中的时间时,对象的计算和比较才有意义。 可以使用 TimeZoneInfo 对象来表示 DateTime 值的时区,尽管这两个值是松散耦合的。 DateTime 对象没有返回对象的属性,该对象表示日期和时间值的时区。 Kind 属性指示 DateTime 表示 UTC、本地时间还是未指定。 在时区感知应用程序中,必须依赖于某些外部机制来确定 DateTime 创建对象的时区。 可以使用一个封装 DateTime 值和 TimeZoneInfo 表示 DateTime 值的时区的对象的结构。 有关在计算中使用 UTC 和值的比较的详细信息 DateTime ,请参阅使用 日期和时间执行算术运算

    每个 DateTime 成员都隐式使用公历来执行其操作。 异常是隐式指定日历的方法。 其中包括用于指定日历的构造函数,以及使用派生自的参数的方法 IFormatProvider ,例如 System.Globalization.DateTimeFormatInfo

    类型成员的操作将考虑 DateTime 闰年和一个月中的天数等详细信息。

    DateTime 值和日历

    .NET Framework 类库包含多个 calendar 类,它们都派生自 Calendar 类。 它们是:

  • ChineseLunisolarCalendar 类。
  • EastAsianLunisolarCalendar 类。
  • GregorianCalendar 类。
  • HebrewCalendar 类。
  • HijriCalendar 类。
  • JapaneseCalendar 类。
  • JapaneseLunisolarCalendar 类。
  • JulianCalendar 类。
  • KoreanCalendar 类。
  • KoreanLunisolarCalendar 类。
  • PersianCalendar 类。
  • TaiwanCalendar 类。
  • TaiwanLunisolarCalendar 类。
  • ThaiBuddhistCalendar 类。
  • UmAlQuraCalendar 类。
  • 日本历法中的年号是根据天皇统治来命名的,因此预计会发生变化。 例如,2019 年 5 月 1 日在 JapaneseCalendar JapaneseLunisolarCalendar 中标志着令和年号的开始。 这种年号的变化会影响使用这些日历的所有应用程序。 有关详细信息以及如何确定应用程序是否受影响,请参阅在 .net 中的日式日历中处理新时代 。 若要了解如何在 Windows 系统上测试应用程序以确保其应用程序更改的就绪性,请参阅 准备应用程序以进行日本时代更改 。 对于 .NET 中支持多个纪元的日历的功能,以及在处理支持多个纪元的日历时的最佳做法,请参阅使用 纪元

    每个区域性都使用由其只读属性定义的默认日历 CultureInfo.Calendar 。 每个区域性都可以支持由其只读属性定义的一个或多个日历 CultureInfo.OptionalCalendars 。 特定对象当前使用的 CultureInfo 日历由其 属性 DateTimeFormatInfo.Calendar 定义。 它必须是数组中的日历之 CultureInfo.OptionalCalendars 一。

    区域性的当前日历用于该区域性的所有格式设置操作。 例如,泰语区域性的默认日历是泰语纪元日历,它由 类 ThaiBuddhistCalendar 表示。 在日期和时间格式设置操作中使用表示泰语区域性的 对象时,默认情况下使用 CultureInfo 泰语纪元日历。 只有在更改区域性的 属性时,才使用公历, DateTimeFormatInfo.Calendar 如以下示例所示:

    var thTH = new System.Globalization.CultureInfo("th-TH"); var value = new DateTime(2016, 5, 28); Console.WriteLine(value.ToString(thTH)); thTH.DateTimeFormat.Calendar = new System.Globalization.GregorianCalendar(); Console.WriteLine(value.ToString(thTH)); // The example displays the following output: // 28/5/2559 0:00:00 // 28/5/2016 0:00:00 Dim thTH As New CultureInfo("th-TH") Dim value As New DateTime(2016, 5, 28) Console.WriteLine(value.ToString(thTH)) thTH.DateTimeFormat.Calendar = New GregorianCalendar() Console.WriteLine(value.ToString(thTH)) ' The example displays the following output: ' 28/5/2559 0:00:00 ' 28/5/2016 0:00:00

    区域性的当前日历也用于该区域性的所有分析操作,如以下示例所示。

    var thTH = new System.Globalization.CultureInfo("th-TH"); var value = DateTime.Parse("28/05/2559", thTH); Console.WriteLine(value.ToString(thTH)); thTH.DateTimeFormat.Calendar = new System.Globalization.GregorianCalendar(); Console.WriteLine(value.ToString(thTH)); // The example displays the following output: // 28/5/2559 0:00:00 // 28/5/2016 0:00:00 Private Sub ThaiBuddhistEraParse() Dim thTH As New CultureInfo("th-TH") Dim value As DateTime = DateTime.Parse("28/5/2559", thTH) Console.WriteLine(value.ToString(thTH)) thTH.DateTimeFormat.Calendar = New GregorianCalendar() Console.WriteLine(value.ToString(thTH)) ' The example displays the following output: ' 28/5/2559 0:00:00 ' 28/5/2016 0:00:00 End Sub

    通过使用日期和时间元素实例化值 (特定日历的年、月、日) 数,可以调用包含 参数的 DateTime DateTime 构造函数,并传递给它一个表示该日历的对象。 calendar Calendar 以下示例使用日历中的日期和时间 ThaiBuddhistCalendar 元素。

    var thTH = new System.Globalization.CultureInfo("th-TH"); var dat = new DateTime(2559, 5, 28, thTH.DateTimeFormat.Calendar); Console.WriteLine($"Thai Buddhist era date: {dat.ToString("d", thTH)}"); Console.WriteLine($"Gregorian date: {dat:d}"); // The example displays the following output: // Thai Buddhist Era Date: 28/5/2559 // Gregorian Date: 28/05/2016 Dim thTH As New CultureInfo("th-TH") Dim dat As New DateTime(2559, 5, 28, thTH.DateTimeFormat.Calendar) Console.WriteLine($"Thai Buddhist Era date: {dat.ToString("d", thTH)}") Console.WriteLine($"Gregorian date: {dat:d}") ' The example displays the following output: ' Thai Buddhist Era Date: 28/5/2559 ' Gregorian Date: 28/05/2016

    DateTime 不包含 参数的构造函数假定日期和时间元素以公历中的单位 calendar 表示。

    所有其他 DateTime 属性和方法都使用公历。 例如, 属性返回公历中的年份,并且 方法假定 参数是公历中的 DateTime.Year DateTime.IsLeapYear(Int32) year 年份。 使用 DateTime 公历的每个成员都有使用特定日历 Calendar 的 类的相应成员。 例如, 方法返回特定日历中的年份,并且 方法将 参数解释为特定日历 Calendar.GetYear Calendar.IsLeapYear year 中的年份号。 下面的示例同时使用 和 DateTime 类的相应 ThaiBuddhistCalendar 成员。

    var thTH = new System.Globalization.CultureInfo("th-TH"); var cal = thTH.DateTimeFormat.Calendar; var dat = new DateTime(2559, 5, 28, cal); Console.WriteLine("Using the Thai Buddhist Era calendar:"); Console.WriteLine($"Date: {dat.ToString("d", thTH)}"); Console.WriteLine($"Year: {cal.GetYear(dat)}"); Console.WriteLine($"Leap year: {cal.IsLeapYear(cal.GetYear(dat))}\n"); Console.WriteLine("Using the Gregorian calendar:"); Console.WriteLine($"Date: {dat:d}"); Console.WriteLine($"Year: {dat.Year}"); Console.WriteLine($"Leap year: {DateTime.IsLeapYear(dat.Year)}"); // The example displays the following output: // Using the Thai Buddhist Era calendar // Date : 28/5/2559 // Year: 2559 // Leap year : True // Using the Gregorian calendar // Date : 28/05/2016 // Year: 2016 // Leap year : True Dim thTH As New CultureInfo("th-TH") Dim cal As Calendar = thTH.DateTimeFormat.Calendar Dim dat As New DateTime(2559, 5, 28, cal) Console.WriteLine("Using the Thai Buddhist Era calendar:") Console.WriteLine($"Date: {dat.ToString("d", thTH)}") Console.WriteLine($"Year: {cal.GetYear(dat)}") Console.WriteLine($"Leap year: {cal.IsLeapYear(cal.GetYear(dat))}") Console.WriteLine() Console.WriteLine("Using the Gregorian calendar:") Console.WriteLine($"Date: {dat:d}") Console.WriteLine($"Year: {dat.Year}") Console.WriteLine($"Leap year: {DateTime.IsLeapYear(dat.Year)}") ' The example displays the following output: ' Using the Thai Buddhist Era calendar ' Date : 28/5/2559 ' Year: 2559 ' Leap year : True ' Using the Gregorian calendar ' Date : 28/05/2016 ' Year: 2016 ' Leap year : True

    DateTime 结构包括 DayOfWeek 一个 属性,该属性返回公历中星期日期。 它不包括允许检索一年中的周数的成员。 若要检索一年中的星期,请调用单个日历的 Calendar.GetWeekOfYear 方法。 下面的示例进行了这方面的演示。

    var thTH = new System.Globalization.CultureInfo("th-TH"); var thCalendar = thTH.DateTimeFormat.Calendar; var dat = new DateTime(1395, 8, 18, thCalendar); Console.WriteLine("Using the Thai Buddhist Era calendar:"); Console.WriteLine($"Date: {dat.ToString("d", thTH)}"); Console.WriteLine($"Day of Week: {thCalendar.GetDayOfWeek(dat)}"); Console.WriteLine($"Week of year: {thCalendar.GetWeekOfYear(dat, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday)}\n"); var greg = new System.Globalization.GregorianCalendar(); Console.WriteLine("Using the Gregorian calendar:"); Console.WriteLine($"Date: {dat:d}"); Console.WriteLine($"Day of Week: {dat.DayOfWeek}"); Console.WriteLine($"Week of year: {greg.GetWeekOfYear(dat, System.Globalization.CalendarWeekRule.FirstDay,DayOfWeek.Sunday)}"); // The example displays the following output: // Using the Thai Buddhist Era calendar // Date : 18/8/1395 // Day of Week: Sunday // Week of year: 34 // Using the Gregorian calendar // Date : 18/08/0852 // Day of Week: Sunday // Week of year: 34 Dim thTH As New CultureInfo("th-TH") Dim thCalendar As Calendar = thTH.DateTimeFormat.Calendar Dim dat As New DateTime(1395, 8, 18, thCalendar) Console.WriteLine("Using the Thai Buddhist Era calendar:") Console.WriteLine($"Date: {dat.ToString("d", thTH)}") Console.WriteLine($"Day of Week: {thCalendar.GetDayOfWeek(dat)}") Console.WriteLine($"Week of year: {thCalendar.GetWeekOfYear(dat, CalendarWeekRule.FirstDay, DayOfWeek.Sunday)}") Console.WriteLine() Dim greg As Calendar = New GregorianCalendar() Console.WriteLine("Using the Gregorian calendar:") Console.WriteLine($"Date: {dat:d}") Console.WriteLine($"Day of Week: {dat.DayOfWeek}") Console.WriteLine($"Week of year: {greg.GetWeekOfYear(dat, CalendarWeekRule.FirstDay, DayOfWeek.Sunday)}") ' The example displays the following output: ' Using the Thai Buddhist Era calendar ' Date : 18/8/1395 ' Day of Week: Sunday ' Week of year: 34 ' Using the Gregorian calendar ' Date : 18/08/0852 ' Day of Week: Sunday ' Week of year: 34

    有关日期和日历详细信息,请参阅 使用日历

    保留 DateTime 值

    可以通过四 DateTime 种方式保留值:

  • 将它们 转换为字符串并 保留字符串。
  • 将它们 转换为 64 位 整数值 (属性值转换为) Ticks 并保留整数。
  • 序列 化 DateTime 值
  • DateTime 值与时区信息 一起序列化
  • 无论选择哪种技术,都必须确保还原值的例程不会丢失数据或引发 DateTime 异常。 DateTime 值应往返。 也就是说,原始值和还原的值应相同。 如果原始 DateTime 值表示单个时刻,则它应标识还原时相同的时刻。

    将值保留为字符串

    若要成功 DateTime 还原保留为字符串的值,请遵循以下规则:

  • 还原字符串时,对特定于区域性的格式设置做出与保留字符串时相同的假设。 若要确保可以在当前区域性不同于保存字符串的系统区域性的系统中还原字符串,请调用 重载,以使用固定区域性的约定保存字符串。 ToString 调用 Parse(String, IFormatProvider, DateTimeStyles) TryParse(String, IFormatProvider, DateTimeStyles, DateTime) 重载,以使用固定区域性的约定还原字符串。 切勿使用 ToString() Parse(String) 、 或 TryParse(String, DateTime) 重载,它们使用当前区域性的约定。

  • 如果日期表示单个时刻,请确保它表示还原时相同的时刻,即使在不同的时区中也表示。 保存值 DateTime 之前,协调世界时 (UTC) 值。 还可以序列化值以及时区信息。 有关此方法的信息,请参阅 序列化日期/时间和时区数据

    将值保留为字符串时,最常见的错误是依赖于默认或当前 DateTime 区域性的格式设置约定。 如果保存和还原字符串时当前区域性不同,则会出现问题。 下面的示例阐释了这些问题。 它使用当前区域性的格式设置约定保存五个日期,本例中为英语 (美国) 。 它使用不同区域性的格式设置约定还原日期,本例中为英语 (英国) 。 由于两个区域性的格式设置约定不同,因此无法还原其中两个日期,并且其余三个日期解释不正确。 此外,如果原始日期和时间值表示单个时刻,则还原时间不正确,因为时区信息丢失。

    public static void PersistAsLocalStrings() SaveLocalDatesAsString(); RestoreLocalDatesFromString(); private static void SaveLocalDatesAsString() DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0), new DateTime(2014, 7, 10, 23, 49, 0), new DateTime(2015, 1, 10, 1, 16, 0), new DateTime(2014, 12, 20, 21, 45, 0), new DateTime(2014, 6, 2, 15, 14, 0) }; string output = null; Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); for (int ctr = 0; ctr < dates.Length; ctr++) Console.WriteLine(dates[ctr].ToString("f")); output += dates[ctr].ToString() + (ctr != dates.Length - 1 ? "|" : ""); var sw = new StreamWriter(filenameTxt); sw.Write(output); sw.Close(); Console.WriteLine("Saved dates..."); private static void RestoreLocalDatesFromString() TimeZoneInfo.ClearCachedData(); Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); StreamReader sr = new StreamReader(filenameTxt); string[] inputValues = sr.ReadToEnd().Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); sr.Close(); Console.WriteLine("The dates on an {0} system:", Thread.CurrentThread.CurrentCulture.Name); foreach (var inputValue in inputValues) DateTime dateValue; if (DateTime.TryParse(inputValue, out dateValue)) Console.WriteLine($"'{inputValue}' --> {dateValue:f}"); Console.WriteLine("Cannot parse '{inputValue}'"); Console.WriteLine("Restored dates..."); // When saved on an en-US system, the example displays the following output: // Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) // The dates on an en-US system: // Saturday, June 14, 2014 6:32 AM // Thursday, July 10, 2014 11:49 PM // Saturday, January 10, 2015 1:16 AM // Saturday, December 20, 2014 9:45 PM // Monday, June 02, 2014 3:14 PM // Saved dates... // When restored on an en-GB system, the example displays the following output: // Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London // The dates on an en-GB system: // Cannot parse //6/14/2014 6:32:00 AM// // //7/10/2014 11:49:00 PM// --> 07 October 2014 23:49 // //1/10/2015 1:16:00 AM// --> 01 October 2015 01:16 // Cannot parse //12/20/2014 9:45:00 PM// // //6/2/2014 3:14:00 PM// --> 06 February 2014 15:14 // Restored dates... Public Sub PersistAsLocalStrings() SaveDatesAsStrings() RestoreDatesAsStrings() End Sub Private Sub SaveDatesAsStrings() Dim dates As Date() = {#6/14/2014 6:32AM#, #7/10/2014 11:49PM#, #1/10/2015 1:16AM#, #12/20/2014 9:45PM#, #6/2/2014 3:14PM#} Dim output As String = Nothing Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For ctr As Integer = 0 To dates.Length - 1 Console.WriteLine(dates(ctr).ToString("f")) output += dates(ctr).ToString() + If(ctr <> dates.Length - 1, "|", "") Dim sw As New StreamWriter(filenameTxt) sw.Write(output) sw.Close() Console.WriteLine("Saved dates...") End Sub Private Sub RestoreDatesAsStrings() TimeZoneInfo.ClearCachedData() Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB") Dim sr As New StreamReader(filenameTxt) Dim inputValues As String() = sr.ReadToEnd().Split({"|"c}, StringSplitOptions.RemoveEmptyEntries) sr.Close() Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For Each inputValue In inputValues Dim dateValue As Date If DateTime.TryParse(inputValue, dateValue) Then Console.WriteLine($"'{inputValue}' --> {dateValue:f}") Console.WriteLine($"Cannot parse '{inputValue}'") End If Console.WriteLine("Restored dates...") End Sub ' When saved on an en-US system, the example displays the following output: ' Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) ' The dates on an en-US system: ' Saturday, June 14, 2014 6:32 AM ' Thursday, July 10, 2014 11:49 PM ' Saturday, January 10, 2015 1:16 AM ' Saturday, December 20, 2014 9:45 PM ' Monday, June 02, 2014 3:14 PM ' Saved dates... ' When restored on an en-GB system, the example displays the following output: ' Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London ' The dates on an en-GB system: ' Cannot parse '6/14/2014 6:32:00 AM' ' '7/10/2014 11:49:00 PM' --> 07 October 2014 23:49 ' '1/10/2015 1:16:00 AM' --> 01 October 2015 01:16 ' Cannot parse '12/20/2014 9:45:00 PM' ' '6/2/2014 3:14:00 PM' --> 06 February 2014 15:14 ' Restored dates...

    若要成功往返 DateTime 值,请执行以下步骤:

  • 如果值表示单个时刻,则通过调用 方法将它们从本地时间转换为 ToUniversalTime UTC。
  • 通过调用 或 重载,将日期转换为其 ToString(String, IFormatProvider) 字符串 String.Format(IFormatProvider, String, Object[]) 表示形式。 通过指定 作为参数,使用固定区域性 CultureInfo.InvariantCulture 的格式 provider 设置约定。 使用"O"或"R"标准格式字符串指定值应往返。
  • 若要还原持久化 DateTime 值而不丢失数据,请执行以下步骤:

  • 通过调用 或 重载来 ParseExact 分析 TryParseExact 数据。 指定 CultureInfo.InvariantCulture 作为 provider 参数,并使用在转换过程中用于参数的相同 format 标准格式字符串。 在 DateTimeStyles.RoundtripKind 参数中包括 styles 值。
  • 如果 DateTime 值表示单个时刻,请调用 方法,将分析日期 ToLocalTime 从 UTC 转换为本地时间。
  • 下面的示例使用固定区域性和"O"标准格式字符串来确保保存和还原的值表示相同的时刻,而不考虑源和目标系统的系统、区域性或 DateTime 时区。

    public static void PersistAsInvariantStrings() SaveDatesAsInvariantStrings(); RestoreDatesAsInvariantStrings(); private static void SaveDatesAsInvariantStrings() DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0), new DateTime(2014, 7, 10, 23, 49, 0), new DateTime(2015, 1, 10, 1, 16, 0), new DateTime(2014, 12, 20, 21, 45, 0), new DateTime(2014, 6, 2, 15, 14, 0) }; string output = null; Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); for (int ctr = 0; ctr < dates.Length; ctr++) Console.WriteLine(dates[ctr].ToString("f")); output += dates[ctr].ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) + (ctr != dates.Length - 1 ? "|" : ""); var sw = new StreamWriter(filenameTxt); sw.Write(output); sw.Close(); Console.WriteLine("Saved dates..."); private static void RestoreDatesAsInvariantStrings() TimeZoneInfo.ClearCachedData(); Console.WriteLine("Current Time Zone: {0}", TimeZoneInfo.Local.DisplayName); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); StreamReader sr = new StreamReader(filenameTxt); string[] inputValues = sr.ReadToEnd().Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); sr.Close(); Console.WriteLine("The dates on an {0} system:", Thread.CurrentThread.CurrentCulture.Name); foreach (var inputValue in inputValues) DateTime dateValue; if (DateTime.TryParseExact(inputValue, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateValue)) Console.WriteLine($"'{inputValue}' --> {dateValue.ToLocalTime():f}"); Console.WriteLine("Cannot parse '{0}'", inputValue); Console.WriteLine("Restored dates..."); // When saved on an en-US system, the example displays the following output: // Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) // The dates on an en-US system: // Saturday, June 14, 2014 6:32 AM // Thursday, July 10, 2014 11:49 PM // Saturday, January 10, 2015 1:16 AM // Saturday, December 20, 2014 9:45 PM // Monday, June 02, 2014 3:14 PM // Saved dates... // When restored on an en-GB system, the example displays the following output: // Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London // The dates on an en-GB system: // '2014-06-14T13:32:00.0000000Z' --> 14 June 2014 14:32 // '2014-07-11T06:49:00.0000000Z' --> 11 July 2014 07:49 // '2015-01-10T09:16:00.0000000Z' --> 10 January 2015 09:16 // '2014-12-21T05:45:00.0000000Z' --> 21 December 2014 05:45 // '2014-06-02T22:14:00.0000000Z' --> 02 June 2014 23:14 // Restored dates... Public Sub PersistAsInvariantStrings() SaveDatesAsInvariantStrings() RestoreDatesAsInvariantStrings() End Sub Private Sub SaveDatesAsInvariantStrings() Dim dates As Date() = {#6/14/2014 6:32AM#, #7/10/2014 11:49PM#, #1/10/2015 1:16AM#, #12/20/2014 9:45PM#, #6/2/2014 3:14PM#} Dim output As String = Nothing Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For ctr As Integer = 0 To dates.Length - 1 Console.WriteLine(dates(ctr).ToString("f")) output += dates(ctr).ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) + If(ctr <> dates.Length - 1, "|", "") Dim sw As New StreamWriter(filenameTxt) sw.Write(output) sw.Close() Console.WriteLine("Saved dates...") End Sub Private Sub RestoreDatesAsInvariantStrings() TimeZoneInfo.ClearCachedData() Console.WriteLine("Current Time Zone: {0}", TimeZoneInfo.Local.DisplayName) Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB") Dim sr As New StreamReader(filenameTxt) Dim inputValues As String() = sr.ReadToEnd().Split({"|"c}, StringSplitOptions.RemoveEmptyEntries) sr.Close() Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For Each inputValue In inputValues Dim dateValue As Date If DateTime.TryParseExact(inputValue, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, dateValue) Then Console.WriteLine($"'{inputValue}' --> {dateValue.ToLocalTime():f}") Console.WriteLine($"Cannot parse '{inputValue}'") End If Console.WriteLine("Restored dates...") End Sub ' When saved on an en-US system, the example displays the following output: ' Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) ' The dates on an en-US system: ' Saturday, June 14, 2014 6:32 AM ' Thursday, July 10, 2014 11:49 PM ' Saturday, January 10, 2015 1:16 AM ' Saturday, December 20, 2014 9:45 PM ' Monday, June 02, 2014 3:14 PM ' Saved dates... ' When restored on an en-GB system, the example displays the following output: ' Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London ' The dates on an en-GB system: ' '2014-06-14T13:32:00.0000000Z' --> 14 June 2014 14:32 ' '2014-07-11T06:49:00.0000000Z' --> 11 July 2014 07:49 ' '2015-01-10T09:16:00.0000000Z' --> 10 January 2015 09:16 ' '2014-12-21T05:45:00.0000000Z' --> 21 December 2014 05:45 ' '2014-06-02T22:14:00.0000000Z' --> 02 June 2014 23:14 ' Restored dates...
    将值保留为整数

    可以将日期和时间保留为 Int64 表示刻度数的值。 在这种情况下,不必考虑值持久保存和还原的系统 DateTime 区域性。

    将值 DateTime 保留为整数:

  • 如果值 DateTime 表示一个时刻,请通过调用 方法将其转换为 ToUniversalTime UTC。
  • 从其 属性中检索值 DateTime 表示的刻度 Ticks 数。
  • 还原 DateTime 已保留为整数的值:

  • 通过向构造函数 DateTime 传递值 Int64 来实例化新 DateTime(Int64) 对象。
  • 如果 DateTime 值表示一个时刻,请通过调用 方法将其从 UTC 转换为本地 ToLocalTime 时间。
  • 以下示例将值数组保留为美国太平洋时区 DateTime 中的系统上的整数。 它会在 UTC 区域中的系统上还原它。 包含整数的文件包含一个值,该值指示紧随它 Int32 Int64 之后的值的总数。

    public static void PersistAsIntegers() SaveDatesAsInts(); RestoreDatesAsInts(); private static void SaveDatesAsInts() DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0), new DateTime(2014, 7, 10, 23, 49, 0), new DateTime(2015, 1, 10, 1, 16, 0), new DateTime(2014, 12, 20, 21, 45, 0), new DateTime(2014, 6, 2, 15, 14, 0) }; Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); var ticks = new long[dates.Length]; for (int ctr = 0; ctr < dates.Length; ctr++) Console.WriteLine(dates[ctr].ToString("f")); ticks[ctr] = dates[ctr].ToUniversalTime().Ticks; var fs = new FileStream(filenameInts, FileMode.Create); var bw = new BinaryWriter(fs); bw.Write(ticks.Length); foreach (var tick in ticks) bw.Write(tick); bw.Close(); Console.WriteLine("Saved dates..."); private static void RestoreDatesAsInts() TimeZoneInfo.ClearCachedData(); Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); FileStream fs = new FileStream(filenameInts, FileMode.Open); BinaryReader br = new BinaryReader(fs); int items; DateTime[] dates; items = br.ReadInt32(); dates = new DateTime[items]; for (int ctr = 0; ctr < items; ctr++) long ticks = br.ReadInt64(); dates[ctr] = new DateTime(ticks).ToLocalTime(); catch (EndOfStreamException) Console.WriteLine("File corruption detected. Unable to restore data..."); return; catch (IOException) Console.WriteLine("Unspecified I/O error. Unable to restore data..."); return; // Thrown during array initialization. catch (OutOfMemoryException) Console.WriteLine("File corruption detected. Unable to restore data..."); return; finally br.Close(); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); foreach (var value in dates) Console.WriteLine(value.ToString("f")); Console.WriteLine("Restored dates..."); // When saved on an en-US system, the example displays the following output: // Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) // The dates on an en-US system: // Saturday, June 14, 2014 6:32 AM // Thursday, July 10, 2014 11:49 PM // Saturday, January 10, 2015 1:16 AM // Saturday, December 20, 2014 9:45 PM // Monday, June 02, 2014 3:14 PM // Saved dates... // When restored on an en-GB system, the example displays the following output: // Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London // The dates on an en-GB system: // 14 June 2014 14:32 // 11 July 2014 07:49 // 10 January 2015 09:16 // 21 December 2014 05:45 // 02 June 2014 23:14 // Restored dates... Public Sub PersistAsIntegers() SaveDatesAsIntegers() RestoreDatesAsIntegers() End Sub Private Sub SaveDatesAsIntegers() Dim dates As Date() = {#6/14/2014 6:32AM#, #7/10/2014 11:49PM#, #1/10/2015 1:16AM#, #12/20/2014 9:45PM#, #6/2/2014 3:14PM#} Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") Dim ticks(dates.Length - 1) As Long For ctr As Integer = 0 To dates.Length - 1 Console.WriteLine(dates(ctr).ToString("f")) ticks(ctr) = dates(ctr).ToUniversalTime().Ticks Dim fs As New FileStream(filenameInts, FileMode.Create) Dim bw As New BinaryWriter(fs) bw.Write(ticks.Length) For Each tick In ticks bw.Write(tick) bw.Close() Console.WriteLine("Saved dates...") End Sub Private Sub RestoreDatesAsIntegers() TimeZoneInfo.ClearCachedData() Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB") Dim fs As New FileStream(filenameInts, FileMode.Open) Dim br As New BinaryReader(fs) Dim items As Integer Dim dates As DateTime() items = br.ReadInt32() ReDim dates(items - 1) For ctr As Integer = 0 To items - 1 Dim ticks As Long = br.ReadInt64() dates(ctr) = New DateTime(ticks).ToLocalTime() Catch e As EndOfStreamException Console.WriteLine("File corruption detected. Unable to restore data...") Exit Sub Catch e As IOException Console.WriteLine("Unspecified I/O error. Unable to restore data...") Exit Sub Catch e As OutOfMemoryException 'Thrown in array initialization. Console.WriteLine("File corruption detected. Unable to restore data...") Exit Sub Finally br.Close() End Try Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For Each value In dates Console.WriteLine(value.ToString("f")) Console.WriteLine("Restored dates...") End Sub ' When saved on an en-US system, the example displays the following output: ' Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) ' The dates on an en-US system: ' Saturday, June 14, 2014 6:32 AM ' Thursday, July 10, 2014 11:49 PM ' Saturday, January 10, 2015 1:16 AM ' Saturday, December 20, 2014 9:45 PM ' Monday, June 02, 2014 3:14 PM ' Saved dates... ' When restored on an en-GB system, the example displays the following output: ' Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London ' The dates on an en-GB system: ' 14 June 2014 14:32 ' 11 July 2014 07:49 ' 10 January 2015 09:16 ' 21 December 2014 05:45 ' 02 June 2014 23:14 ' Restored dates...
    序列化 DateTime 值

    可以通过序列 DateTime 化将值持久化到流或文件,然后通过反序列化还原这些值。 DateTime 数据以某种指定的对象格式进行序列化。 反反化对象时,将还原这些对象。 格式化程序或序列化程序(如 XmlSerializer BinaryFormatter )处理序列化和反序列化的过程。 有关序列化和序列化支持的类型.NET Framework, 请参阅序列化

    下面的示例使用 类 XmlSerializer 来序列化和反序列化 DateTime 值。 这些值表示二十一世纪的所有闰年天数。 如果示例在当前区域性为英语的系统中运行,则输出 (英国) 。 由于已反化对象本身,因此代码不必处理日期和时间 DateTime 格式的文化差异。

    public static void PersistAsXML() // Serialize the data. var leapYears = new List<DateTime>(); for (int year = 2000; year <= 2100; year += 4) if (DateTime.IsLeapYear(year)) leapYears.Add(new DateTime(year, 2, 29)); DateTime[] dateArray = leapYears.ToArray(); var serializer = new XmlSerializer(dateArray.GetType()); TextWriter sw = new StreamWriter(filenameXml); serializer.Serialize(sw, dateArray); catch (InvalidOperationException e) Console.WriteLine(e.InnerException.Message); finally if (sw != null) sw.Close(); // Deserialize the data. DateTime[] deserializedDates; using (var fs = new FileStream(filenameXml, FileMode.Open)) deserializedDates = (DateTime[])serializer.Deserialize(fs); // Display the dates. Console.WriteLine($"Leap year days from 2000-2100 on an {Thread.CurrentThread.CurrentCulture.Name} system:"); int nItems = 0; foreach (var dat in deserializedDates) Console.Write($" {dat:d} "); nItems++; if (nItems % 5 == 0) Console.WriteLine(); // The example displays the following output: // Leap year days from 2000-2100 on an en-GB system: // 29/02/2000 29/02/2004 29/02/2008 29/02/2012 29/02/2016 // 29/02/2020 29/02/2024 29/02/2028 29/02/2032 29/02/2036 // 29/02/2040 29/02/2044 29/02/2048 29/02/2052 29/02/2056 // 29/02/2060 29/02/2064 29/02/2068 29/02/2072 29/02/2076 // 29/02/2080 29/02/2084 29/02/2088 29/02/2092 29/02/2096 Public Sub PersistAsXml() ' Serialize the data. Dim leapYears As New List(Of DateTime)() For year As Integer = 2000 To 2100 Step 4 If Date.IsLeapYear(year) Then leapYears.Add(New Date(year, 2, 29)) End If Dim dateArray As DateTime() = leapYears.ToArray() Dim serializer As New XmlSerializer(dateArray.GetType()) Dim sw As TextWriter = New StreamWriter(filenameXml) serializer.Serialize(sw, dateArray) Catch e As InvalidOperationException Console.WriteLine(e.InnerException.Message) Finally If sw IsNot Nothing Then sw.Close() End Try ' Deserialize the data. Dim deserializedDates As Date() Using fs As New FileStream(filenameXml, FileMode.Open) deserializedDates = CType(serializer.Deserialize(fs), Date()) End Using ' Display the dates. Console.WriteLine($"Leap year days from 2000-2100 on an {Thread.CurrentThread.CurrentCulture.Name} system:") Dim nItems As Integer For Each dat In deserializedDates Console.Write($" {dat:d} ") nItems += 1 If nItems Mod 5 = 0 Then Console.WriteLine() End Sub ' The example displays the following output: ' Leap year days from 2000-2100 on an en-GB system: ' 29/02/2000 29/02/2004 29/02/2008 29/02/2012 29/02/2016 ' 29/02/2020 29/02/2024 29/02/2028 29/02/2032 29/02/2036 ' 29/02/2040 29/02/2044 29/02/2048 29/02/2052 29/02/2056 ' 29/02/2060 29/02/2064 29/02/2068 29/02/2072 29/02/2076 ' 29/02/2080 29/02/2084 29/02/2088 29/02/2092 29/02/2096

    前面的示例不包括时间信息。 如果值表示一个时刻,并且表示为本地时间,则先将其从本地时间转换为 DateTime UTC,然后再通过调用 方法序列 ToUniversalTime 化它。 反初始化后,通过调用 方法将其从 UTC 转换为本地 ToLocalTime 时间。 以下示例使用 类在美国太平洋标准时区中的系统上序列化数据,并在美国中部标准区域中的系统上反序列 BinaryFormatter DateTime 化该数据。

    public static void PersistBinary() SaveDatesBinary(); RestoreDatesBinary(); private static void SaveDatesBinary() DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0), new DateTime(2014, 7, 10, 23, 49, 0), new DateTime(2015, 1, 10, 1, 16, 0), new DateTime(2014, 12, 20, 21, 45, 0), new DateTime(2014, 6, 2, 15, 14, 0) }; var fs = new FileStream(filenameBin, FileMode.Create); var bin = new BinaryFormatter(); Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); for (int ctr = 0; ctr < dates.Length; ctr++) Console.WriteLine(dates[ctr].ToString("f")); dates[ctr] = dates[ctr].ToUniversalTime(); bin.Serialize(fs, dates); fs.Close(); Console.WriteLine("Saved dates..."); private static void RestoreDatesBinary() TimeZoneInfo.ClearCachedData(); Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}"); Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB"); FileStream fs = new FileStream(filenameBin, FileMode.Open); BinaryFormatter bin = new BinaryFormatter(); var dates = (DateTime[])bin.Deserialize(fs); fs.Close(); Console.WriteLine($"The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:"); foreach (var value in dates) Console.WriteLine(value.ToLocalTime().ToString("f")); Console.WriteLine("Restored dates..."); // When saved on an en-US system, the example displays the following output: // Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) // The dates on an en-US system: // Saturday, June 14, 2014 6:32 AM // Thursday, July 10, 2014 11:49 PM // Saturday, January 10, 2015 1:16 AM // Saturday, December 20, 2014 9:45 PM // Monday, June 02, 2014 3:14 PM // Saved dates... // When restored on an en-GB system, the example displays the following output: // Current Time Zone: (UTC-6:00) Central Time (US & Canada) // The dates on an en-GB system: // 14 June 2014 08:32 // 11 July 2014 01:49 // 10 January 2015 03:16 // 20 December 2014 23:45 // 02 June 2014 17:14 // Restored dates... Public Sub PersistBinary() SaveDatesBinary() RestoreDatesBinary() End Sub Private Sub SaveDatesBinary() Dim dates As Date() = {#6/14/2014 6:32AM#, #7/10/2014 11:49PM#, #1/10/2015 1:16AM#, #12/20/2014 9:45PM#, #6/2/2014 3:14PM#} Dim fs As New FileStream(filenameBin, FileMode.Create) Dim bin As New BinaryFormatter() Console.WriteLine($"Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Console.WriteLine("The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For ctr As Integer = 0 To dates.Length - 1 Console.WriteLine(dates(ctr).ToString("f")) dates(ctr) = dates(ctr).ToUniversalTime() bin.Serialize(fs, dates) fs.Close() Console.WriteLine("Saved dates...") End Sub Private Sub RestoreDatesBinary() TimeZoneInfo.ClearCachedData() Console.WriteLine("Current Time Zone: {TimeZoneInfo.Local.DisplayName}") Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB") Dim fs As New FileStream(filenameBin, FileMode.Open) Dim bin As New BinaryFormatter() Dim dates As DateTime() = DirectCast(bin.Deserialize(fs), Date()) fs.Close() Console.WriteLine("The dates on an {Thread.CurrentThread.CurrentCulture.Name} system:") For Each value In dates Console.WriteLine(value.ToLocalTime().ToString("f")) Console.WriteLine("Restored dates...") End Sub ' When saved on an en-US system, the example displays the following output: ' Current Time Zone: (UTC-08:00) Pacific Time (US & Canada) ' The dates on an en-US system: ' Saturday, June 14, 2014 6:32 AM ' Thursday, July 10, 2014 11:49 PM ' Saturday, January 10, 2015 1:16 AM ' Saturday, December 20, 2014 9:45 PM ' Monday, June 02, 2014 3:14 PM ' Saved dates... ' When restored on an en-GB system, the example displays the following output: ' Current Time Zone: (UTC-6:00) Central Time (US & Canada) ' The dates on an en-GB system: ' 14 June 2014 08:32 ' 11 July 2014 01:49 ' 10 January 2015 03:16 ' 20 December 2014 11:45 ' 02 June 2014 17:14 ' Restored dates...
    序列化 DateTime 和时区数据

    前面的示例都假定 DateTime 值表示为本地时间。 代码转换了 UTC 和本地时间之间的值,因此它们反映了源系统和目标系统上相同的时刻。 DateTime 值还可以反映时区中除本地和 UTC 外的时间时刻。 由于 DateTime 结构不是时区感知型,因此必须序列化值和 DateTime TimeZoneInfo 表示其时区的对象。 创建其字段同时包含 DateTime 值及其时区的类型。 下面的示例定义 一个 DateWithTimeZone 结构。

    using System; namespace DateTimeExtensions [Serializable] public struct DateWithTimeZone private TimeZoneInfo tz; private DateTime dt; public DateWithTimeZone(DateTime dateValue, TimeZoneInfo timeZone) dt = dateValue; tz = timeZone ?? TimeZoneInfo.Local; public TimeZoneInfo TimeZone get { return (tz); } set { tz = value; } public DateTime DateTime get { return (dt); } set { dt = value; } Namespace DateTimeExtensions <Serializable> Public Structure DateWithTimeZone Private tz As TimeZoneInfo Private dt As DateTime Public Sub New(dateValue As DateTime, timeZone As TimeZoneInfo) dt = dateValue tz = If(timeZone, TimeZoneInfo.Local) End Sub Public Property TimeZone As TimeZoneInfo Return tz End Get tz = Value End Set End Property Public Property DateTime As Date Return dt End Get dt = Value End Set End Property End Structure End Namespace

    DateWithTimeZone 结构用于接下来的两个示例,这些示例序列化并反序列化 对象 DateWithTimeZone 数组。 可以在 Visual Basic 上的文档存储库中查看本文中整个示例集的Visual Basic C# GitHub。

    然后,可以使用 结构 DateWithTimeZone 保留日期和时间以及时区信息。 下面的示例使用 类 BinaryFormatter 序列化 对象的 DateWithTimeZone 数组。

    public static void SaveDateWithTimeZone() DateWithTimeZone[] dates = { new DateWithTimeZone(new DateTime(2014, 8, 9, 19, 30, 0), TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), new DateWithTimeZone(new DateTime(2014, 8, 15, 19, 0, 0), TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time")), new DateWithTimeZone(new DateTime(2014, 8, 22, 19, 30, 0), TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), new DateWithTimeZone(new DateTime(2014, 8, 28, 19, 0, 0), TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")) }; var fs = new FileStream(@".\Schedule.bin", FileMode.Create); var formatter = new BinaryFormatter(); formatter.Serialize(fs, dates); // Display dates. foreach (var date in dates) TimeZoneInfo tz = date.TimeZone; Console.WriteLine($"{date.DateTime} {(tz.IsDaylightSavingTime(date.DateTime) ? tz.DaylightName : tz.StandardName)}"); catch (SerializationException e) Console.WriteLine($"Serialization failed. Reason: {e.Message}"); finally if (fs != null) fs.Close(); // The example displays the following output: // 8/9/2014 7:30:00 PM Eastern Daylight Time // 8/15/2014 7:00:00 PM Pacific Daylight Time // 8/22/2014 7:30:00 PM Eastern Daylight Time // 8/28/2014 7:00:00 PM Eastern Daylight Time Public Sub SaveDateWithTimeZone() Dim dates As DateWithTimeZone() = {New DateWithTimeZone(#8/9/2014 7:30PM#, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), New DateWithTimeZone(#8/15/2014 7:00PM#, TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time")), New DateWithTimeZone(#8/22/2014 7:30PM#, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")), New DateWithTimeZone(#8/28/2014 7:00PM#, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"))} Dim fs As New FileStream(".\Schedule.bin", FileMode.Create) Dim formatter As New BinaryFormatter() formatter.Serialize(fs, dates) Catch e As SerializationException Console.WriteLine($"Serialization failed. Reason: {e.Message}") Finally If fs IsNot Nothing Then fs.Close() End Try ' Display dates. For Each dateInfo In dates Dim tz As TimeZoneInfo = dateInfo.TimeZone Console.WriteLine($"{dateInfo.DateTime} {If(tz.IsDaylightSavingTime(dateInfo.DateTime), tz.DaylightName, tz.StandardName)}") End Sub ' The example displays the following output: ' 8/9/2014 7:30:00 PM Eastern Daylight Time ' 8/15/2014 7:00:00 PM Pacific Daylight Time ' 8/22/2014 7:30:00 PM Eastern Daylight Time ' 8/28/2014 7:00:00 PM Eastern Daylight Time

    然后,下面的示例调用 BinaryFormatter.Deserialize 方法来反初始化它。

    public static void RestoreDateWithTimeZone() const string filename = @".\Schedule.bin"; FileStream fs; if (File.Exists(filename)) fs = new FileStream(filename, FileMode.Open); Console.WriteLine("Unable to find file to deserialize."); return; var formatter = new BinaryFormatter(); DateWithTimeZone[] dates; dates = (DateWithTimeZone[])formatter.Deserialize(fs); // Display dates. foreach (var date in dates) TimeZoneInfo tz = date.TimeZone; Console.WriteLine($"{ date.DateTime} {(tz.IsDaylightSavingTime(date.DateTime) ? tz.DaylightName : tz.StandardName)}"); catch (SerializationException e) Console.WriteLine($"Deserialization failed. Reason: {e.Message}"); finally if (fs != null) fs.Close(); // The example displays the following output: // 8/9/2014 7:30:00 PM Eastern Daylight Time // 8/15/2014 7:00:00 PM Pacific Daylight Time // 8/22/2014 7:30:00 PM Eastern Daylight Time // 8/28/2014 7:00:00 PM Eastern Daylight Time Public Sub RestoreDateWithTimeZone() Dim fs As FileStream If File.Exists(filename) Then fs = New FileStream(filename, FileMode.Open) Console.WriteLine("Unable to find file to deserialize.") Exit Sub End If Dim formatter As New BinaryFormatter() Dim dates As DateWithTimeZone ()= Nothing dates = DirectCast(formatter.Deserialize(fs), DateWithTimeZone()) ' Display dates. For Each dateInfo In dates Dim tz As TimeZoneInfo = dateInfo.TimeZone Console.WriteLine($"{dateInfo.DateTime} {If(tz.IsDaylightSavingTime(dateInfo.DateTime), tz.DaylightName, tz.StandardName)}") Catch e As SerializationException Console.WriteLine("Deserialization failed. Reason: {e.Message}") Finally If fs IsNot Nothing Then fs.Close() End Try End Sub ' The example displays the following output: ' 8/9/2014 7:30:00 PM Eastern Daylight Time ' 8/15/2014 7:00:00 PM Pacific Daylight Time ' 8/22/2014 7:30:00 PM Eastern Daylight Time ' 8/28/2014 7:00:00 PM Eastern Daylight Time

    DateTime 与 TimeSpan

    DateTime TimeSpan 值类型不同,因为 表示 DateTime 时刻,而 TimeSpan 表示时间间隔。 可以从另一个实例中减去 一个实例, DateTime 以获取 TimeSpan 表示它们之间的时间间隔的 对象。 或者,可以将正值 TimeSpan 添加到当前 , DateTime 以获取 DateTime 表示未来日期的值。

    可以从对象添加或减去时间间隔 DateTime 。 时间间隔可以是负数也可以是正数,它们可以用时间刻度、秒数或 TimeSpan 对象表示。

    在容错范围内比较是否相等

    值的相等比较 DateTime 是精确的。 这意味着两个值必须表示为相同的刻度数,才能视为相等。 对于许多应用程序而言,这种精度通常是不必要的,甚至不正确。 通常,您需要测试 DateTime 对象是否 大致相等

    下面的示例演示如何比较大致等效 DateTime 值。 当将它们声明为相等时,它将接受较小的差别。

    public static bool RoughlyEquals(DateTime time, DateTime timeWithWindow, int windowInSeconds, int frequencyInSeconds) long delta = (long)((TimeSpan)(timeWithWindow - time)).TotalSeconds % frequencyInSeconds; delta = delta > windowInSeconds ? frequencyInSeconds - delta : delta; return Math.Abs(delta) < windowInSeconds; public static void TestRoughlyEquals() int window = 10; int freq = 60 * 60 * 2; // 2 hours; DateTime d1 = DateTime.Now; DateTime d2 = d1.AddSeconds(2 * window); DateTime d3 = d1.AddSeconds(-2 * window); DateTime d4 = d1.AddSeconds(window / 2); DateTime d5 = d1.AddSeconds(-window / 2); DateTime d6 = (d1.AddHours(2)).AddSeconds(2 * window); DateTime d7 = (d1.AddHours(2)).AddSeconds(-2 * window); DateTime d8 = (d1.AddHours(2)).AddSeconds(window / 2); DateTime d9 = (d1.AddHours(2)).AddSeconds(-window / 2); Console.WriteLine($"d1 ({d1}) ~= d1 ({d1}): {RoughlyEquals(d1, d1, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d2 ({d2}): {RoughlyEquals(d1, d2, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d3 ({d3}): {RoughlyEquals(d1, d3, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d4 ({d4}): {RoughlyEquals(d1, d4, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d5 ({d5}): {RoughlyEquals(d1, d5, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d6 ({d6}): {RoughlyEquals(d1, d6, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d7 ({d7}): {RoughlyEquals(d1, d7, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d8 ({d8}): {RoughlyEquals(d1, d8, window, freq)}"); Console.WriteLine($"d1 ({d1}) ~= d9 ({d9}): {RoughlyEquals(d1, d9, window, freq)}"); // The example displays output similar to the following: // d1 (1/28/2010 9:01:26 PM) ~= d1 (1/28/2010 9:01:26 PM): True // d1 (1/28/2010 9:01:26 PM) ~= d2 (1/28/2010 9:01:46 PM): False // d1 (1/28/2010 9:01:26 PM) ~= d3 (1/28/2010 9:01:06 PM): False // d1 (1/28/2010 9:01:26 PM) ~= d4 (1/28/2010 9:01:31 PM): True // d1 (1/28/2010 9:01:26 PM) ~= d5 (1/28/2010 9:01:21 PM): True // d1 (1/28/2010 9:01:26 PM) ~= d6 (1/28/2010 11:01:46 PM): False // d1 (1/28/2010 9:01:26 PM) ~= d7 (1/28/2010 11:01:06 PM): False // d1 (1/28/2010 9:01:26 PM) ~= d8 (1/28/2010 11:01:31 PM): True // d1 (1/28/2010 9:01:26 PM) ~= d9 (1/28/2010 11:01:21 PM): True Public Shared Function RoughlyEquals(time As DateTime, timeWithWindow As DateTime, windowInSeconds As Integer, frequencyInSeconds As Integer) As Boolean Dim delta As Long = (timeWithWindow.Subtract(time)).TotalSeconds _ Mod frequencyInSeconds If delta > windowInSeconds Then delta = frequencyInSeconds - delta End If Return Math.Abs(delta) < windowInSeconds End Function Public Shared Sub TestRoughlyEquals() Dim window As Integer = 10 Dim freq As Integer = 60 * 60 * 2 ' 2 hours; Dim d1 As DateTime = DateTime.Now Dim d2 As DateTime = d1.AddSeconds(2 * window) Dim d3 As DateTime = d1.AddSeconds(-2 * window) Dim d4 As DateTime = d1.AddSeconds(window / 2) Dim d5 As DateTime = d1.AddSeconds(-window / 2) Dim d6 As DateTime = d1.AddHours(2).AddSeconds(2 * window) Dim d7 As DateTime = d1.AddHours(2).AddSeconds(-2 * window) Dim d8 As DateTime = d1.AddHours(2).AddSeconds(window / 2) Dim d9 As DateTime = d1.AddHours(2).AddSeconds(-window / 2) Console.WriteLine($"d1 ({d1}) ~= d1 ({d1}): {RoughlyEquals(d1, d1, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d2 ({d2}): {RoughlyEquals(d1, d2, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d3 ({d3}): {RoughlyEquals(d1, d3, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d4 ({d4}): {RoughlyEquals(d1, d4, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d5 ({d5}): {RoughlyEquals(d1, d5, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d6 ({d6}): {RoughlyEquals(d1, d6, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d7 ({d7}): {RoughlyEquals(d1, d7, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d8 ({d8}): {RoughlyEquals(d1, d8, window, freq)}") Console.WriteLine($"d1 ({d1}) ~= d9 ({d9}): {RoughlyEquals(d1, d9, window, freq)}") End Sub ' The example displays output similar to the following: ' d1 (1/28/2010 9:01:26 PM) ~= d1 (1/28/2010 9:01:26 PM): True ' d1 (1/28/2010 9:01:26 PM) ~= d2 (1/28/2010 9:01:46 PM): False ' d1 (1/28/2010 9:01:26 PM) ~= d3 (1/28/2010 9:01:06 PM): False ' d1 (1/28/2010 9:01:26 PM) ~= d4 (1/28/2010 9:01:31 PM): True ' d1 (1/28/2010 9:01:26 PM) ~= d5 (1/28/2010 9:01:21 PM): True ' d1 (1/28/2010 9:01:26 PM) ~= d6 (1/28/2010 11:01:46 PM): False ' d1 (1/28/2010 9:01:26 PM) ~= d7 (1/28/2010 11:01:06 PM): False ' d1 (1/28/2010 9:01:26 PM) ~= d8 (1/28/2010 11:01:31 PM): True ' d1 (1/28/2010 9:01:26 PM) ~= d9 (1/28/2010 11:01:21 PM): True

    COM 互操作注意事项

    如果某个 DateTime 值已传输到 COM 应用程序,则会传输回托管应用程序,这被称为往返。 但是, DateTime 仅指定时间的值不会像预期的那样往返。

    如果只往返时间(如下午3点),则最终日期和时间为公元1899年12月30日 在下午3:00,而不是公元0001年1月1日 下午3:00 在仅指定时间时,.NET Framework 和 COM 将假定为默认日期。 但是,COM 系统假定基准日期为公元1899年12月30日,而 .NET Framework 假设基准日期为公元0001年1月1日。

    当只将时间从 .NET Framework 传递到 com 时,会执行特殊处理,将时间转换为 com 使用的格式。 如果仅将时间从 COM 传递到 .NET Framework,则不会执行任何特殊处理,因为这样会损坏1899年12月30日或之前的合法日期和时间。 如果日期从 COM 开始往返,则 .NET Framework 和 com 将保留日期。

    .NET Framework 和 COM 的行为意味着,如果你的应用程序往返 DateTime 仅指定时间,则你的应用程序必须记得修改或忽略最后一个对象中的错误日期 DateTime

    线程安全性

    此类型的所有成员都是线程安全的。 看似修改实例状态的成员实际上返回用新值初始化的新实例。 与任何其他类型一样,读取和写入包含此类型的实例的共享变量时,必须通过锁保护以保证线程安全。

  • DateTimeOffset
  • TimeSpan
  • Calendar
  • GetUtcOffset(DateTime)
  • TimeZoneInfo
  • 在 DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间进行选择
  • 示例:.NET Core WinForms 格式设置实用工具 (C#)
  • 示例:.NET Core WinForms 格式设置实用工具 (Visual Basic)
  •