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

sdu*_*ooy 219 .net c# time datetime

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

TimeSpan表示范围,而我只想存储时间值.使用DateTime指示,这将导致新的DateTime(1,1,1,8,30,0)并非真正的希望.

Joh*_*n G 154

你可以使用时间跨度

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

[编辑]
考虑到其他答案和问题的编辑,我仍然会使用TimeSpan.没有必要创建一个新结构,其中框架中的现有结构就足够了.
在这些行上,您最终会复制许多本机数据类型.

  • 究竟.DateTime正是为了这个目的而使用TimeSpan.Doc for DateTime.TimeSpan属性:"TimeSpan,表示自午夜以来经过的那一天的一小部分." (18认同)
  • @John G:虽然它*可以*用于表示一个固定点,但我同意OP - 重载使用'TimeSpan`这样有点难看.这是框架内部可用的最好的,但这与说它令人愉快的不一样. (8认同)
  • TimeSpan表示一个时间间隔,而我所说的时间不是一个时间间隔,而是一个日期范围内的一个固定点. (4认同)
  • 它可以用作一个固定点,正如你在问题中指出的那样,它没有日期.毕竟你决定如何将这些数据类型用于你的好处. (2认同)
  • 恕我直言,比复制本机可用数据结构的新类更好. (2认同)
  • 从.Net 3.5开始,MSDN记录说:“ TimeSpan结构也可以用于表示一天中的时间,但前提是该时间与特定日期无关。” 换句话说,这正是所提出问题的解决方案。 (2认同)
  • @Pharap:只是因为它*可以*以这种方式使用并不是一个好主意.这意味着`TimeSpan`用于两个非常不同的目的 - 就像`DateTime`用于UTC,本地和未指定时区的值.从根本上说,框架中的日期/时间类型具有重要的概念问题,即IMO. (2认同)
  • @JonSkeet说实话,我认为我们只是围成一圈.我同意`Point`和`Vector`应该单独和同等对待,我的观点是许多知名的库选择忽略这一点以简化.随意不同意MSDN,但这次我会坚持使用它们.OP和其他任何人都可以自由选择适合他们情况的任何东西,无论是标准库还是外部库,但我知道我坚持哪一方.如果没有关于OP情况的更多信息,我真的不能说更多. (2认同)
  • @Pharap:我想我们必须同意不同意.当我的代码由于使用类型来表示它们并非真正设计的值而导致我的代码变得不清楚时,我*讨厌它. (2认同)
  • 有趣的事实:使用MySQL和Schema First生成ADO.net数据模型将选择TimeSpan作为类型为"Time"的任何列的模型数据类型.它仍然让我了解整个时间问题的相对未解决的问题.我认为它应该是任何CS学位的单独和必需的课程. (2认同)

Jon*_*eet 134

正如其他人所说,你可以使用a DateTime而忽略日期,或者使用a TimeSpan.就个人而言,我并不热衷于这两种解决方案,因为这两种解决方案都没有真正反映出你想要代表的概念 - 我认为.NET中的日期/时间类型在某种程度上是在稀疏方面,这是我开始的原因之一野田时间.在Noda Time中,您可以使用该LocalTime类型来表示一天中的某个时间.

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

(另外,如果您还想表示商店的关闭时间,您可能会发现您希望代表24:00,即一天结束时的时间.大多数日期/时间API - 包括Noda时间 - 不允许将其表示为时间值.)

  • @Jason:夏令时是我可以随意思考的唯一原因 - 忽略闰秒与大多数应用程序无关.我大多离开它是为了鼓励其他人思考为什么会这样.我认为人们比现在更深入地思考日期/时间是件好事:) (12认同)
  • "[T]他一天的时间不一定是同一天午夜以来的时间长度......"夏令时是唯一的原因吗?只是好奇为什么你无限期地离开它. (5认同)

Rub*_*ias 33

如果那个空虚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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

  • +1这比TimeSpan更好,因为它错误解释的可能性更小...... TimeSpan真的意味着用作间隔(参见MSDN)所以当TimeSpan用作Time时,像Days这样的属性没有意义 (17认同)
  • 由于存在已经处理过这种情况的"TimeSpan",并且以明显更好的方式存在,因此向下贬低你. (4认同)
  • 嗯...也许......但为什么要重新发明轮子呢?如果语言已经有一个类/结构(C#和VB.NET这样做),那就去吧.但我确实理解你在哪里试着回答你的问题. (2认同)

bug*_*ixr 20

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

DateTime.Now.ToString("t");  // outputs 10:00 PM
Run Code Online (Sandbox Code Playgroud)

似乎所有创建新类或甚至使用TimeSpan的额外工作都是不必要的.

  • @MonaJalal Milliseconds:`DateTime.Now.ToString("hh:mm:ss.fff");`Microseconds:`DateTime.Now.ToString("hh:mm:ss.ffffff");`纳秒(如果DateTime甚至有那么多分辨率):`DateTime.Now.ToString("hh:mm:ss.fffffffff");`按[MSDN](https://msdn.microsoft.com/en-us/library/8kb3ddd4%28v= vs.110%29.aspx) (5认同)
  • 因此,为此实现一个适当的类型所需的 5 到 10 分钟对您来说似乎比在整个代码库中考虑更多的工作,对于任何未来的开发,DateTime 属性可能只包含一个时间,并且必须被格式化在这些情况下,可能需要忽略日期部分?调试一下您会在数据库、外部通信等中找到“0001-01-01 10:00”的情况,玩得开心...... (2认同)

Chi*_*ata 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mis*_*sky 8

C# 10中,您可以使用TimeOnly

TimeOnly date = TimeOnly.FromDateTime(DateTime.Now);
Run Code Online (Sandbox Code Playgroud)

  • TimeOnly 是一个特定于框架的功能。如果项目面向 net6.0 或更高版本,则可以在 C# 9 中使用该类型 (2认同)

Jul*_*les 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*ger 6

这是一个功能齐全的 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mat*_*int 5

最近批准了一种System.TimeOfDay类型用于即将发布的 .NET 6。

请参阅https://github.com/dotnet/runtime/issues/49036

完成后,这将是表示与任何特定日期或时区无关的时间值的首选方式。

System.TimeSpan仍然是表示经过时间值的推荐方法。