.NETで時間のみの値を表すにはどうすればよいですか?


238

日付なしで.NETで時間のみの値を表す方法はありますか?たとえば、店の開店時間を示していますか?

TimeSpan範囲を示しますが、時間の値のみを保存します。これDateTimeを示すために使用するDateTime(1,1,1,8,30,0)と、本当に望ましくない新しい結果になります。

回答:


143

他の人が言ったように、あなたがすることができます使用DateTimeして日付を無視する、または使用しますTimeSpan。どちらのタイプもあなたが表現しようとしている概念を実際に反映していないので、個人的に私はこれらのソリューションのどちらにも熱心ではありません-。野田時間。野田時間では、LocalTimeタイプを使用して時刻を表すことができます。

考慮すべき1つのこと:時刻は、必ずしも同じ日の真夜中からの時間の長さとは限りません...

(別の言い方をすれば、店の閉店時間も表現したい場合は、24:00、つまり1日の終わりの時間を表現したいと思うかもしれません。ほとんどの日付/時刻API-Nodaを含む時間-それを時刻の値として表すことはできません。)


5
「[時間]は、必ずしも同じ日の真夜中からの時間の長さではありません...」夏時間が唯一の理由ですか?あなたがそれを不明確にした理由に興味があります。
Jason

14
@ジェイソン:夏時間は、私がオフハンドで考えることができる唯一の理由です-うるう秒をほとんどのアプリケーションに無関係であると無視します。私は主にそのようにして、なぜそうなるのかを他の人に考えさせるようにしました。人々が現在よりも日付/時刻について少し深く考えるのは良いことだと思います:)
Jon Skeet

LocalTimeは、まさに私の要件をサポートするために必要なものです。
sduplooy、2010年

1
@sduplooy:Joda Timeからの移植を手伝ってくれませんか?:)
ジョンスキート

1
@Oakcool:5月18日に言ったとおり:Duration野田時間、またはTimeSpanBCL。私はおそらく「動画内の場所+コメント」をタイプとしてカプセル化し、そのタイプの配列を作成します。
Jon Skeet

164

タイムスパンを使用できます

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

[編集]
他の回答と質問の編集を考慮して、TimeSpanを引き続き使用します。フレームワークの既存の構造で十分な新しい構造を作成しても意味がありません。
これらの行では、多くのネイティブデータ型を複製することになります。


19
丁度。DateTimeはまさにそのためにTimeSpanを使用します。DateTime.TimeSpanプロパティのドキュメント:「午前0時から経過した日の割合を表すTimeSpan。」
Marcel Jackwerth、2010年

4
TimeSpanは間隔を示しますが、私が話している時間は間隔ではなく、日付の範囲の1つの固定点です。
sduplooy、2010年

3
固定点としても使用でき、質問で指定したように、日付がありません。結局、あなたはあなたの利益のためにこれらのデータ型を使用する方法を決定します。
John G

9
@ジョンG:それ固定小数点を表すために使用することができますが、私はOPに同意します- TimeSpanこのような使用の過負荷はやや醜いです。それはフレームワーク自体の中で利用できる最高のものですが、それは快適だと言っているのと同じではありません。
Jon Skeet、2010年

5
.Net 3.5の時点で、MSDNは「TimeSpan構造を使用して時刻を表すこともできますが、時刻が特定の日付と無関係の場合のみ」と文書化しています。言い換えれば、これは提案された質問に対する正確な解決策です。
Pharap 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とどのように異なるのでしょうか。これはある方法でそれを複製するだけです。
John G

4
の存在によりTimeSpan、あなたに反対票を投じます。これはすでにこれを処理しており、大幅に改善されています。
正午シルク

1
@silky、私は最初の答えを読んだ後でこれを書いた。OPはTimeSpanを使いたくないと質問しました。私は個人的に、プレーンなDateTimeを使用することを選択します
Rubens Farias

18
1日のようなプロパティがのTimeSpanが時間として使用されては意味がありませんので、誤解の少ない可能性を持っている...のTimeSpanが本当に(MSDNを参照してください)間隔として使用されることを意味しているので、これが良いのTimeSpanより
ザイドマスード

20

私はDateTimeを使用すると言います。日付部分が不要な場合は、無視してください。時間だけをユーザーに表示する必要がある場合は、次のようにフォーマットしてユーザーに出力します。

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

新しいクラスを作成したり、TimeSpanを使用したりするための余分な作業はすべて不要のようです。


この方法で秒とミリ秒をどのように表示しますか?
Mona Jalal

5
@MonaJalalミリ秒:DateTime.Now.ToString("hh:mm:ss.fff");マイクロ秒:DateTime.Now.ToString("hh:mm:ss.ffffff");ナノ秒(日時もその多くの解像度を有する場合):DateTime.Now.ToString("hh:mm:ss.fffffffff");1としてMSDN
Pharap

2
したがって、適切な型を実装するのにかかる5〜10分は、コードベース全体で検討する必要があるよりも多くの作業であるように見えます。将来の開発では、DateTimeプロパティには時刻のみが含まれる可能性があり、フォーマットする必要があるそれらのシナリオではそのようであり、日付部分は無視する必要があるかもしれませんか?データベース、外部通信などで "0001-01-01 10:00"が見つかった場合のデバッグを楽しんでください...
MarioDS

10

Rubensのクラスは良い考えだと思うので、基本的な検証を行ってTimeクラスの不変のサンプルを作成することを考えました。

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時間を超える可能性があることです。
シェルペペレイラ

時、分、秒がuintではなくintを使用しているのはなぜですか?理由がなければuintを直接使用できると思うので、コンストラクターでのキャストを回避できます。
シェルペペレイラ

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に置き換えられて、それがあなたに利点をもたらすと突然わかった場合、それは簡単です。)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.