如何在.NET中表示仅时间值?


238

有没有一种方法可以表示.NET中没有日期的仅时间值?例如,指示商店的开业时间?

TimeSpan表示一个范围,而我只想存储一个时间值。使用DateTime表示这将导致产生新的结果,DateTime(1,1,1,8,30,0)这并不是真正希望的。

Answers:


143

正如其他人所说,您可以使用DateTime和忽略日期,或使用TimeSpan。我个人并不热衷于这两种解决方案,因为这两种类型都不能真正反映您要表达的概念-我认为.NET中的日期/时间类型有些稀疏,这就是我开始的原因之一野田时间。在Noda Time中,您可以使用LocalTime类型表示一天中的时间。

要考虑的一件事:一天中的时间不一定是同一天午夜以来的时间长度...

(顺便说一句,如果您还想代表一家商店的关门时间,您可能会发现要代表24:00,即一天结束时的时间。大多数日期/时间API-包括Noda时间-不允许将其表示为时间值。)


5
“一天中的时间不一定是同一天午夜以来的时间长度……”夏时制是唯一的原因吗?只是好奇为什么您不确定它。
杰森2010年

14
@Jason:夏令时是我能想到的唯一原因-忽略leap秒与大多数应用程序无关。我大多以这种方式离开,以鼓励其他人思考为什么会这样。我认为对人们来说,对日期/时间进行比现在更深入的思考是一件好事:)
Jon Skeet 2010年

正是我需要LocalTime来支持我的需求。
sduplooy,2010年

1
@sduplooy:想要帮助我们从Joda Time移植它吗?:)
Jon Skeet 2010年

1
@Oakcool:就像我在5月18日所说的那样:Duration在Noda Time或TimeSpanBCL中。我可能会把“视频+评论中的位置”封装为一种类型,然后有一个该类型的数组。
乔恩·斯基特

164

您可以使用时间跨度

TimeSpan timeSpan = new TimeSpan(2, 14, 18);
Console.WriteLine(timeSpan.ToString());     // Displays "02:14:18".

[编辑]
考虑到其他答案和对问题的编辑,我仍将使用TimeSpan。创建一个足以满足框架中现有条件的新结构毫无意义。
在这些行上,您最终将复制许多本机数据类型。


19
究竟。DateTime正是出于这个目的使用TimeSpan。Doc for DateTime.TimeSpan属性:“ TimeSpan代表自午夜以来经过的一天的比例。”
Marcel Jackwerth 2010年

4
TimeSpan表示一个时间间隔,而我所说的时间不是一个时间间隔,而是一个日期范围内的单个固定点。
sduplooy,2010年

3
它可以很好地用作固定点,并且正如您在问题中指定的那样,它没有日期。毕竟,您要决定如何根据自己的利益使用这些数据类型。
约翰G 2010年

9
@John G:虽然可以用来表示一个固定点,但我同意OP的观点- TimeSpan像这样重载使用有点难看。框架本身可以提供最好的服务,但这与说它令人愉快并不相同。
乔恩·斯基特

5
从.Net 3.5开始,MSDN记录说:“ TimeSpan结构也可以用于表示一天中的时间,但前提是该时间与特定日期无关。” 换句话说,这正是所提出问题的解决方案。
法拉普2015年

34

如果这个空白Date确实困扰您,您还可以创建一个更简单的Time结构:

// more work is required to make this even close to production ready
class Time
{
    // TODO: don't forget to add validation
    public int Hours   { get; set; }
    public int Minutes { get; set; }
    public int Seconds { get; set; }

    public override string ToString()
    {  
        return String.Format(
            "{0:00}:{1:00}:{2:00}",
            this.Hours, this.Minutes, this.Seconds);
    }
}

或者,为什么要打扰呢:如果您不需要对该信息进行任何计算,只需将其存储为String


2
嗯...也许...但是为什么要重新发明轮子呢?如果该语言已经具有类/结构(C#和VB.NET具备),则请使用它。但是,我确实知道您要在哪里回答。
克里斯·克劳斯

1
这种结构与TimeSpan有什么不同,只是以某种方式进行复制。
约翰G 2010年

4
由于存在TimeSpan,已经可以解决此问题并以明显更好的方式进行投票。
中午

1
@silky,我在阅读第一个答案后写了这个;OP质疑说他不想使用TimeSpan。我个人将选择使用简单的DateTime
Rubens Farias,2010年

18
+1这比TimeSpan更好,因为它具有较少的误解可能性... TimeSpan实际上是要用作间隔(请参见MSDN),因此将TimeSpan用作时间时,像Days这样的属性没有意义
Zaid Masud 2012年

20

我说使用DateTime。如果不需要日期部分,请忽略它。如果您只需要向用户显示时间,则将其格式化后输出给用户,如下所示:

DateTime.Now.ToString("t");  // outputs 10:00 PM

似乎没有必要进行新类甚至使用TimeSpan的所有额外工作。


您如何用这种方法显示秒和毫秒?
莫娜·贾拉勒

5
@MonaJalal毫秒:微秒DateTime.Now.ToString("hh:mm:ss.fff");DateTime.Now.ToString("hh:mm:ss.ffffff");纳秒(如果DateTime甚至具有这么高的分辨率):DateTime.Now.ToString("hh:mm:ss.fffffffff");根据MSDN
Pharap 2015年

2
因此,对于您来说,实现适当的类型所需的5到10分钟的时间似乎比在整个代码库中要考虑的工作要多,对于任何将来的开发,DateTime属性可能只包含一个时间,并且必须进行格式化像是在那种情况下,日期部分可能需要忽略?玩得开心调试出现在那里你会发现在你的数据库“0001-01-01 10:00”,在对外交流等...
MarioDS

10

我认为鲁本斯的课程是一个好主意,因此可以考虑使用基本验证对他的时间类做一个不变的样本。

class Time
{
    public int Hours   { get; private set; }
    public int Minutes { get; private set; }
    public int Seconds { get; private set; }

    public Time(uint h, uint m, uint s)
    {
        if(h > 23 || m > 59 || s > 59)
        {
            throw new ArgumentException("Invalid time specified");
        }
        Hours = (int)h; Minutes = (int)m; Seconds = (int)s;
    }

    public Time(DateTime dt)
    {
        Hours = dt.Hour;
        Minutes = dt.Minute;
        Seconds = dt.Second;
    }

    public override string ToString()
    {  
        return String.Format(
            "{0:00}:{1:00}:{2:00}",
            this.Hours, this.Minutes, this.Seconds);
    }
}

您添加的验证非常重要。TimeSpan类在建模时间方面的主要缺点是一天中的时间可能超过24小时。
shelbypereira

为什么小时,分钟和秒使用int而不使用uint?如果没有理由,我认为他们可以直接使用uint,这样可以避免在构造函数中进行强制转换。
shelbypereira

6

除了Chibueze Opata之外

class Time
{
    public int Hours   { get; private set; }
    public int Minutes { get; private set; }
    public int Seconds { get; private set; }

    public Time(uint h, uint m, uint s)
    {
        if(h > 23 || m > 59 || s > 59)
        {
            throw new ArgumentException("Invalid time specified");
        }
        Hours = (int)h; Minutes = (int)m; Seconds = (int)s;
    }

    public Time(DateTime dt)
    {
        Hours = dt.Hour;
        Minutes = dt.Minute;
        Seconds = dt.Second;
    }

    public override string ToString()
    {  
        return String.Format(
            "{0:00}:{1:00}:{2:00}",
            this.Hours, this.Minutes, this.Seconds);
    }

    public void AddHours(uint h)
    {
        this.Hours += (int)h;
    }

    public void AddMinutes(uint m)
    {
        this.Minutes += (int)m;
        while(this.Minutes > 59)
            this.Minutes -= 60;
            this.AddHours(1);
    }

    public void AddSeconds(uint s)
    {
        this.Seconds += (int)s;
        while(this.Seconds > 59)
            this.Seconds -= 60;
            this.AddMinutes(1);
    }
}

分钟和秒钟你的添加方法是错误的,因为他们没有考虑值高于59
Chibueze Opata

@Chibueze Opate:您完全正确。这只是又快又脏。我应该在这段代码中做更多的工作。稍后再更新...谢谢您的提示!
Jules

5

这是功能齐全的TimeOfDay类。

在简单的情况下,这是多余的,但是如果您需要像我一样的更高级的功能,则可能会有所帮助。

它可以处理极端情况,一些基本数学,比较,与DateTime的交互,解析等。

下面是TimeOfDay类的源代码。您可以在此处查看用法示例并了解更多信息

此类使用DateTime进行大多数内部计算和比较,以便我们可以利用DateTime中已嵌入的所有知识。

// Author: Steve Lautenschlager, CambiaResearch.com
// License: MIT

using System;
using System.Text.RegularExpressions;

namespace Cambia
{
    public class TimeOfDay
    {
        private const int MINUTES_PER_DAY = 60 * 24;
        private const int SECONDS_PER_DAY = SECONDS_PER_HOUR * 24;
        private const int SECONDS_PER_HOUR = 3600;
        private static Regex _TodRegex = new Regex(@"\d?\d:\d\d:\d\d|\d?\d:\d\d");

        public TimeOfDay()
        {
            Init(0, 0, 0);
        }
        public TimeOfDay(int hour, int minute, int second = 0)
        {
            Init(hour, minute, second);
        }
        public TimeOfDay(int hhmmss)
        {
            Init(hhmmss);
        }
        public TimeOfDay(DateTime dt)
        {
            Init(dt);
        }
        public TimeOfDay(TimeOfDay td)
        {
            Init(td.Hour, td.Minute, td.Second);
        }

        public int HHMMSS
        {
            get
            {
                return Hour * 10000 + Minute * 100 + Second;
            }
        }
        public int Hour { get; private set; }
        public int Minute { get; private set; }
        public int Second { get; private set; }
        public double TotalDays
        {
            get
            {
                return TotalSeconds / (24d * SECONDS_PER_HOUR);
            }
        }
        public double TotalHours
        {
            get
            {
                return TotalSeconds / (1d * SECONDS_PER_HOUR);
            }
        }
        public double TotalMinutes
        {
            get
            {
                return TotalSeconds / 60d;
            }
        }
        public int TotalSeconds
        {
            get
            {
                return Hour * 3600 + Minute * 60 + Second;
            }
        }
        public bool Equals(TimeOfDay other)
        {
            if (other == null) { return false; }
            return TotalSeconds == other.TotalSeconds;
        }
        public override bool Equals(object obj)
        {
            if (obj == null) { return false; }
            TimeOfDay td = obj as TimeOfDay;
            if (td == null) { return false; }
            else { return Equals(td); }
        }
        public override int GetHashCode()
        {
            return TotalSeconds;
        }
        public DateTime ToDateTime(DateTime dt)
        {
            return new DateTime(dt.Year, dt.Month, dt.Day, Hour, Minute, Second);
        }
        public override string ToString()
        {
            return ToString("HH:mm:ss");
        }
        public string ToString(string format)
        {
            DateTime now = DateTime.Now;
            DateTime dt = new DateTime(now.Year, now.Month, now.Day, Hour, Minute, Second);
            return dt.ToString(format);
        }
        public TimeSpan ToTimeSpan()
        {
            return new TimeSpan(Hour, Minute, Second);
        }
        public DateTime ToToday()
        {
            var now = DateTime.Now;
            return new DateTime(now.Year, now.Month, now.Day, Hour, Minute, Second);
        }

        #region -- Static --
        public static TimeOfDay Midnight { get { return new TimeOfDay(0, 0, 0); } }
        public static TimeOfDay Noon { get { return new TimeOfDay(12, 0, 0); } }
        public static TimeOfDay operator -(TimeOfDay t1, TimeOfDay t2)
        {
            DateTime now = DateTime.Now;
            DateTime dt1 = new DateTime(now.Year, now.Month, now.Day, t1.Hour, t1.Minute, t1.Second);
            TimeSpan ts = new TimeSpan(t2.Hour, t2.Minute, t2.Second);
            DateTime dt2 = dt1 - ts;
            return new TimeOfDay(dt2);
        }
        public static bool operator !=(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else
            {
                return t1.TotalSeconds != t2.TotalSeconds;
            }
        }
        public static bool operator !=(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 != dt2;
        }
        public static bool operator !=(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 != dt2;
        }
        public static TimeOfDay operator +(TimeOfDay t1, TimeOfDay t2)
        {
            DateTime now = DateTime.Now;
            DateTime dt1 = new DateTime(now.Year, now.Month, now.Day, t1.Hour, t1.Minute, t1.Second);
            TimeSpan ts = new TimeSpan(t2.Hour, t2.Minute, t2.Second);
            DateTime dt2 = dt1 + ts;
            return new TimeOfDay(dt2);
        }
        public static bool operator <(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else
            {
                return t1.TotalSeconds < t2.TotalSeconds;
            }
        }
        public static bool operator <(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 < dt2;
        }
        public static bool operator <(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 < dt2;
        }
        public static bool operator <=(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else
            {
                if (t1 == t2) { return true; }
                return t1.TotalSeconds <= t2.TotalSeconds;
            }
        }
        public static bool operator <=(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 <= dt2;
        }
        public static bool operator <=(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 <= dt2;
        }
        public static bool operator ==(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else { return t1.Equals(t2); }
        }
        public static bool operator ==(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 == dt2;
        }
        public static bool operator ==(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 == dt2;
        }
        public static bool operator >(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else
            {
                return t1.TotalSeconds > t2.TotalSeconds;
            }
        }
        public static bool operator >(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 > dt2;
        }
        public static bool operator >(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 > dt2;
        }
        public static bool operator >=(TimeOfDay t1, TimeOfDay t2)
        {
            if (ReferenceEquals(t1, t2)) { return true; }
            else if (ReferenceEquals(t1, null)) { return true; }
            else
            {
                return t1.TotalSeconds >= t2.TotalSeconds;
            }
        }
        public static bool operator >=(TimeOfDay t1, DateTime dt2)
        {
            if (ReferenceEquals(t1, null)) { return false; }
            DateTime dt1 = new DateTime(dt2.Year, dt2.Month, dt2.Day, t1.Hour, t1.Minute, t1.Second);
            return dt1 >= dt2;
        }
        public static bool operator >=(DateTime dt1, TimeOfDay t2)
        {
            if (ReferenceEquals(t2, null)) { return false; }
            DateTime dt2 = new DateTime(dt1.Year, dt1.Month, dt1.Day, t2.Hour, t2.Minute, t2.Second);
            return dt1 >= dt2;
        }
        /// <summary>
        /// Input examples:
        /// 14:21:17            (2pm 21min 17sec)
        /// 02:15               (2am 15min 0sec)
        /// 2:15                (2am 15min 0sec)
        /// 2/1/2017 14:21      (2pm 21min 0sec)
        /// TimeOfDay=15:13:12  (3pm 13min 12sec)
        /// </summary>
        public static TimeOfDay Parse(string s)
        {
            // We will parse any section of the text that matches this
            // pattern: dd:dd or dd:dd:dd where the first doublet can
            // be one or two digits for the hour.  But minute and second
            // must be two digits.

            Match m = _TodRegex.Match(s);
            string text = m.Value;
            string[] fields = text.Split(':');
            if (fields.Length < 2) { throw new ArgumentException("No valid time of day pattern found in input text"); }
            int hour = Convert.ToInt32(fields[0]);
            int min = Convert.ToInt32(fields[1]);
            int sec = fields.Length > 2 ? Convert.ToInt32(fields[2]) : 0;

            return new TimeOfDay(hour, min, sec);
        }
        #endregion

        private void Init(int hour, int minute, int second)
        {
            if (hour < 0 || hour > 23) { throw new ArgumentException("Invalid hour, must be from 0 to 23."); }
            if (minute < 0 || minute > 59) { throw new ArgumentException("Invalid minute, must be from 0 to 59."); }
            if (second < 0 || second > 59) { throw new ArgumentException("Invalid second, must be from 0 to 59."); }
            Hour = hour;
            Minute = minute;
            Second = second;
        }
        private void Init(int hhmmss)
        {
            int hour = hhmmss / 10000;
            int min = (hhmmss - hour * 10000) / 100;
            int sec = (hhmmss - hour * 10000 - min * 100);
            Init(hour, min, sec);
        }
        private void Init(DateTime dt)
        {
            Init(dt.Hour, dt.Minute, dt.Second);
        }
    }
}

2

如果您不想使用DateTime或TimeSpan,而只想存储一天中的时间,则可以将午夜以来的秒数存储在Int32中,或者(如果您甚至不需要秒数)也可以存储午夜以来的分钟数。将适合Int16。编写一些从这种值访问时,分和秒所需的方法将是微不足道的。

我能想到的避免DateTime / TimeSpan的唯一原因是结构的大小很关键。

(当然,如果您使用像上面这样的简单方案包装在一个类中,那么将来突然用TimeSpan替换存储也是很简单的,如果您突然意识到这样做会给您带来好处)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.