C# TimeSpan 详细指南

导航至

一个在容器中的纸板标签 Description 自动生成 哎,C# 的 TimeSpan 结构体。它相当低调,而且似乎没有得到充分利用,如果我的个人经验有任何价值的话。尽管如此,C# 的 TimeSpan 类型仍然具有巨大的潜力,可以使您的代码更加易于阅读和健壮。

您不相信吗?好吧,阅读这篇文章,您将了解到一个类型是如何做到看似最简单的任务——表示时间间隔——却非常出色的。以下是如何进行

  • 我们将从对 TimeSpan 的定义和解释开始。您将了解这个类型是什么,以及它能解决哪些问题。
  • 然后,您将了解使用 TimeSpan 而不是其他替代方案的好处。
  • 接下来,是时候实际操作了:您将了解如何开始使用 TimeSpan,包括其基本语法、初始化方式,以及对其关键方法和属性的说明。
  • 最后,我们将涵盖该类型的一些用例以及您可能遇到的常见错误。

让我们开始吧。

C# TimeSpan:基础知识

让我们从 TimeSpan 101 开始。

C# 中什么是 TimeSpan?

首先,从技术上讲——也许有点咬文嚼字——“C# TimeSpan”并不是一个东西。System.TimeSpan 结构体是 .NET 中的一个类型,其使用并不局限于 C#;您还可以使用任何 .NET 语言,例如 VB.NET 和 F#。(但,是的,这篇文章是为 C# 程序员准备的,所以所有示例都将使用该语言。)

那么,System.TimeSpan 结构体是什么?它来自 .NET BCL(基础类库),表示一个时间间隔。您是否需要表示“五分钟”或“两个小时”的概念?如果是这样,TimeSpan 就是您要找的。

C# TimeSpan:重要性和好处

以下是我认为使用 TimeSpan 的最大好处

  • 它降低了出现错误的可能性
  • 代码变得更易于阅读
  • 它提供了与时间相关的有用功能

但是,你不能简单地使用一个数值类型——比如 int——来表示持续时间吗?

是的,但这种方法有几个缺点。首先,它会导致更易于出现错误的代码。如果在C#中使用原始类型来表示时间间隔,你无法表达你所指的单位。

如果代码的一部分“认为”持续时间是分钟,而另一部分假设它是秒,这样的不匹配将导致错误。是的,你可以使用注释和/或命名约定来减少这种错误的可能性,但这些只能做到一定程度。

另一个例子:假设你有一个方法,它接受两个整数作为参数,一个表示标识符,另一个表示持续时间。一个常见的错误是将两个参数以错误的顺序传递。这种错误在代码审查期间很容易被忽略,并会导致错误。然而,如果你使用了 TimeSpan 来表示时间间隔,这种错误将不可能发生:编译器将阻止你传递错误的数据类型。

另一个后果是代码的可读性降低。如果你看到一个方法签名返回一个 TimeSpan,那么你知道你正在处理一个持续时间。另一方面,一个 int 可能是任何东西,你必须依赖命名约定来理解它的含义。

使用原始类型而不是更具体的“时间”的最后一个缺点是,原始类型无法提供与它所代表的领域相关的有用操作。毕竟,一个数字只是一个数字;它无法了解关于时间的领域。

另一方面,TimeSpan 提供了许多与时间间隔概念相关的有用操作。而且——不出所料——它与其他时间相关类型(如 DateTime)配合得很好。 一个标志的特写说明自动生成

获取C#TimeSpan

在了解了 TimeSpan 类型的 what 之后,现在是时候了解 how 了。我将向您展示的第一个内容是如何获取一个 TimeSpan 值。你可以通过几种方式做到这一点

从两个DateTime值中减去

DateTime now = DateTime.Now;

DateTime twoHoursFromNow = now.AddHours(2);

TimeSpan difference = twoHoursFromNow - now;

Console.WriteLine(difference); // 显示 '02:00:00'

使用其中一个工厂方法

var twoHours = TimeSpan.FromHours(2);

var tenMinutes = TimeSpan.FromMinutes(10);

var eightSeconds = TimeSpan.FromSeconds(8);

将两个或更多TimeSpan相加

var fortyMinutes = TimeSpan.FromMinutes(40);

var twentyMinutes = TimeSpan.FromMinutes(20);

var oneHour = TimeSpan.FromHours(1);

var twoHours = fortyMinutes + twentyMinutes + oneHour;

Console.WriteLine(twoHours); // 显示 '02:00:00'

使用无参数构造函数(空值)

var time = new TimeSpan();

Console.WriteLine(time); // 显示 '00:00:00'

使用其他几个构造函数

var a = new TimeSpan(hours: 2, minutes: 10, seconds: 25);

var b = new TimeSpan(days: 1, hours: 3, minutes: 5, seconds: 48);

var c = new TimeSpan(days: 1, hours: 3, minutes: 5, seconds: 48, milliseconds: 456);

var d = new TimeSpan(days: 1, hours: 3, minutes: 5, seconds: 48, milliseconds: 456, microseconds: 45678);

var e = new TimeSpan(ticks: 10_000_000); // 10百万tick相当于一秒

一旦细化到微秒,事情可能会变得混乱,所以这里有一个关于这些单位的提醒

  • 毫秒是秒的一千分之一。
  • 微秒是秒的一百万分之一。
  • 微秒包含1000纳秒。换句话说,纳秒是秒的一亿分之一。
  • 一个tick相当于100纳秒。换句话说,一秒包含1000万tick。

从字符串表示形式解析

最后,您还可以通过解析字符串表示形式来获取一个 TimeSpan 值,这与您处理日期和数字的方式类似。以下几种方法遵循 ParseTryParse 模式:没有“Try”后缀的方法在解析失败时抛出异常,而带有后缀的方法返回一个布尔值,指示操作的状态。实际解析的值通过 out 参数返回。

以下是一个 TryParse() 版本示例:

var success = TimeSpan.TryParse("1:20:15", out TimeSpan result);

var message = success ? $"Timespan 值: {result}" : "输入不是一个有效的时长";

Console.WriteLine(message);

C# 中与 TimeSpan 相关的关键方法和属性有哪些?

现在您已经知道了获取一个闪亮的、全新的 TimeSpan 值的主要方法。这很棒,但您可以用它做什么呢?这个类型提供了哪些功能?

实际上,TimeSpan 类型有很多方法、字段和属性。我不会全部介绍,只会介绍最重要的。

方法

您刚刚看到,您可以如何添加两个或更多 TimeSpan 值。您还可以使用 SubtractMultiplyDivide 或相应的运算符执行其他操作。

var oneHour = TimeSpan.FromHours(1);

var twoHours = oneHour.Multiply(2);

var fourHours = twoHours * 2; // 这也行

var threeHours = fourHours.Subtract(oneHour);

var oneAndAHalfHours = threeHours.Divide(2);

Console.WriteLine(oneAndAHalfHours); // 显示 '01:30:00'

Console.WriteLine(threeHours); // 显示 '03:00:00'

Console.WriteLine(fourHours); // 显示 '04:00:00'

重要的是要记住,TimeSpan 是一个不可变类型。每次您对其值进行操作时,它都会返回一个新的值,而不是改变现有的值。

您需要反转 TimeSpan 吗?也就是说,使正值变为负值或反之亦然?那么,Negate() 方法就是您的朋友。如果您需要 TimeSpan 的绝对值,则 Duration() 是您要找的方法。

TimeSpan 覆盖了 Equals 方法,这可以通过值进行比较

var oneHour = TimeSpan.FromHours(1);

var sixtyMinutes = TimeSpan.FromMinutes(60);

Console.WriteLine(oneHour.Equals(sixtyMinutes) ? "equal" : "not equal"); // 显示 'equal'

CompareTo 方法可用于确定每个 TimeSpan 值是否比另一个值短、长或相等,为每种情况返回一个整数(-1、0、1)。这在排序值列表时很有用。

属性

TimeSpan 提供了属性,允许您获取其值的各个组成部分,例如小时、分钟、天数等。以下是所有属性

  • Days
  • Hours
  • Milliseconds
  • Minutes
  • Seconds
  • Ticks
  • TotalDays
  • TotalHours
  • TotalMilliseconds
  • TotalMinutes
  • TotalSeconds

以“Total”开头的属性都是 double 类型。它们返回 TimeSpan 实例的总值,以所需的单位表示,并允许有分数部分。没有“Total”的属性是整数(int 和 long 类型),它们返回相应组件的值,没有分数部分。

让我们用一个例子来看看

var ninetyMinutes = new TimeSpan(hours: 1, minutes: 30, seconds: 0);

Console.WriteLine(ninetyMinutes.TotalHours); // 显示 1.5

Console.WriteLine(ninetyMinutes.Hours); // 显示 1

Console.WriteLine(ninetyMinutes.TotalMinutes); // 显示 90

Console.WriteLine(ninetyMinutes.Minutes); // 显示 30

C# 中如何格式化 TimeSpan

C# 中有几种方式来格式化 TimeSpan。让我们来看看其中的一些。

首先,您可以使用 ToString() 方法来返回默认格式

var timeSpan = new TimeSpan(days: 2, hours: 1, minutes: 30, seconds: 0);

Console.WriteLine(timeSpan.ToString()); // 显示 '2.01:30:00'

请注意,上面的 ToString() 不是必需的,因为 WriteLine() 已经在其传入的任何对象上调用了 ToString()。我之所以包含它,只是为了教学目的。

现在,让我们看一个包含更多组件的示例

var timeSpan = new TimeSpan(3, 15, 30, 45, 500);

Console.WriteLine(timeSpan.ToString(@"d'd 'h'h 'm'm 's's'")); // 显示 '3d 15h 30m 45s'

最后,我们来看另一种利用 字符串插值 和直接访问 TimeSpan 值属性的替代方案

var timeSpan = new TimeSpan(days: 3, hours: 15, minutes: 30, seconds: 45, milliseconds: 500);

var directFormat = $"{timeSpan.Days} 天, {timeSpan.Hours} 小时, {timeSpan.Minutes} 分钟, {timeSpan.Seconds} 秒";

Console.WriteLine(directFormat); // 显示 '3 天, 15 小时, 30 分钟, 45 秒'

C# TimeSpan:常见问题和避免方法

在结束之前,让我们来谈谈人们在使用 TimeSpan 值时常见的错误以及如何避免它们。

解析错误

在这个 StackOverflow 问题 中,用户尝试了以下操作

TimeSpan timeTaken = TimeSpan.Parse("51:45:33");

失败了。为什么?在上面的格式中,第一个组件表示小时,其有效范围是 23。因此,为了使其工作,这个人需要使用 Days 组件来表示完整的信息。

误解组件

在文章的前面,你学习了以“Total”开头的 TimeSpan 属性和那些不以“Total”开头的属性之间的区别。一个常见的错误是误解并交换属性类型,从而导致不期望的行为。

错误的算术

人们在处理 TimeSpan 值时可能会犯多种算术错误,但我在这里关注的一个非常具体的错误是:在从 DateTime 值中减去时忽略夏令时。

让我们假设你有这样的代码

var start = DateTime.Now;

// 发生了很多事情

var end = DateTime.Now;

TimeSpan duration = end - start;

这看起来可能没有问题,但如果你生活在有夏令时的地区,可能存在潜在的问题。如果在 startend 变量初始化之间发生夏令时转换,你最终会得到一个错误值,包含额外的 hour 或缺少的 hour。

在这些情况下,你应该使用一个“中性”时区,该时区不会随着夏令时转换而波动。换句话说,使用 UTC

var start = DateTime.UtcNow;

// 发生了很多事情

var end = DateTime.UtcNow;

TimeSpan duration = end - start;

C# TimeSpan:告别原始固执

在这篇文章中,我们向您介绍了 TimeSpan。现在,你知道了它是什么,你可以用它来做什么,它的好处是什么,以及如何开始使用它。

现在,作为下一步,我邀请你继续探索。仔细阅读 TimeSpan 文档。创建玩具项目来探索这个类型及其与 .NET 中其他时间相关类型的关联。当然,一旦有机会,就在你的真实项目中使用 TimeSpan。让原始固执成为过去。

本文由Carlos Schults撰写。 Carlos是一位技术娴熟的软件工程师,也是一位资深的为多个客户撰写技术文档的作家。他热衷于深入探究事物的本质(原始来源),并用易于理解且信息丰富的技术内容吸引读者。