.NET에서 시간 전용 값을 어떻게 표현합니까?


238

날짜없이 .NET에서 시간 만 값을 나타낼 수있는 방법이 있습니까? 예를 들어 상점의 영업 시간을 나타내는가?

TimeSpan범위를 나타내는 반면 시간 값만 저장하고 싶습니다. DateTime이를 나타 내기 위해 사용하면 새로운 결과 DateTime(1,1,1,8,30,0)가 나오지 않으므로 바람직하지 않습니다.

답변:


144

다른 사람들이 말했듯이 a 사용 DateTime하고 날짜를 무시하거나 a를 사용할 수 TimeSpan있습니다. 개인적으로 나는이 솔루션 중 하나에 관심이 없습니다. 두 유형 모두 실제로 표현하려는 개념을 반영하지 않기 때문에 .NET의 날짜 / 시간 유형은 스파 스 측면에서 다소 시작된 이유 중 하나라고 생각합니다. 노다 시간 . Noda Time에서 LocalTime유형을 사용하여 시간을 나타낼 수 있습니다 .

한 가지 고려해야 할 사항 : 하루 중 시간이 반드시 같은 날 자정 이후의 시간 길이는 아닙니다 ...

(또 다른 점으로, 상점 의 폐점 시간을 나타내려면 24:00, 즉 하루가 끝나는 시간을 나타내는 것이 좋습니다. Noda를 포함한 대부분의 날짜 / 시간 API 시간-시간 값으로 표시하지 마십시오.)


5
"[그] 그날의 시간이 반드시 같은 날 자정 이후의 시간 일 필요는 없습니다 ..."일광 절약 시간이 유일한 이유입니까? 왜 당신이 그것을 무한정 남겨 두 었는지 궁금합니다.
Jason

14
@Jason : 일광 절약은 내가 손으로 생각할 수있는 유일한 이유입니다. 윤초는 대부분의 응용 프로그램과 관련이없는 것으로 무시합니다. 나는 대부분 다른 사람들이 왜 그런지 생각하도록 격려하기 위해 그 길을 떠났습니다. 나는 사람들이 현재보다 날짜 / 시간에 대해 좀 더 깊이 생각하는 것이 좋다고 생각합니다.)
Jon Skeet

LocalTime은 요구 사항을 지원하는 데 필요한 것입니다.
sduplooy

1
@ sduplooy : Joda Time에서 포팅을 도와주는 멋진가요? :)
Jon Skeet

1
@Oakcool : 5 월 18 일에 말한대로 : DurationNoda Time 또는 TimeSpanBCL. 아마도 "video + comment in place"를 유형으로 캡슐화 한 다음 해당 유형의 배열을 가질 것입니다.
Jon Skeet

164

기간 을 사용할 수 있습니다

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

[편집]
다른 답변과 질문에 대한 편집을 고려할 때 여전히 TimeSpan을 사용합니다. 프레임 워크의 기존 구조로 충분하면 새 구조를 작성할 필요가 없습니다.
이 줄에서 많은 기본 데이터 유형이 복제됩니다.


19
바로 그거죠. DateTime은 정확히 그 목적을 위해 TimeSpan을 사용합니다. DateTime.TimeSpan 속성에 대한 Doc : "자정 이후 경과 된 하루의 비율을 나타내는 TimeSpan"
Marcel Jackwerth

4
TimeSpan은 간격을 나타내는 반면 내가 말하는 시간은 간격이 아니라 날짜 범위의 단일 고정 소수점입니다.
sduplooy

3
그것은 고정 소수점으로도 사용될 수 있으며, 질문에서 지정한대로 날짜가 없습니다. 결국 이러한 데이터 유형을 혜택에 사용하는 방법을 결정합니다.
John G

9
@ 존 G : 고정 된 점을 나타내는 데 사용할 수는 있지만 OP에 동의합니다 TimeSpan. 프레임 워크 자체에서 사용할 수있는 것이 가장 좋지만 유쾌하다고 말하는 것과 다릅니다.
Jon Skeet

5
.Net 3.5부터 MSDN은 "TimeSpan 구조를 사용하여 시간을 특정 날짜와 관련이없는 경우에만 시간을 나타내는 데 사용할 수 있습니다"라고 설명합니다. 다시 말해, 이것은 제안 된 질문에 대한 해결책입니다.
Pharap

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)가있는 경우 해당 언어와 함께 사용하십시오. 그러나 나는 당신이 당신의 대답을 어디로 가려고하는지 이해합니다.
Kris Krause

1
이 구조가 TimeSpan과 어떻게 다른가? 이것은 어떤 방식으로 복제 할 것인가이다.
John G

4
TimeSpan이 (가) 이미 존재 하고 상당히 개선 된 방식 으로 인해 귀하를 다운 보팅했습니다 .
Noon Silk

1
@silky, 나는 첫 번째 답변을 읽은 후 이것을 썼다. OP는 질문에 TimeSpan을 사용하고 싶지 않다고 말했다. 나는 개인적으로 평범한 DateTime을 사용하기로 결정했습니다
Rubens Farias

18
+1 잘못 해석 할 가능성이 적기 때문에 TimeSpan보다 낫습니다. TimeSpan은 실제로 간격 (MSDN 참조)으로 사용되므로 TimeSpan을 시간으로 사용할 때 Days와 같은 속성은 의미가 없습니다
Zaid Masud

20

DateTime을 사용한다고 말합니다. 날짜 부분이 필요하지 않으면 무시하십시오. 사용자에게 시간 만 표시해야하는 경우 다음과 같이 사용자에게 형식화 된 시간을 출력하십시오.

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

새 클래스를 만들거나 TimeSpan을 사용하는 모든 추가 작업이 필요하지 않은 것 같습니다.


이 방법으로 몇 초와 몇 초를 어떻게 보여 주겠습니까?
Mona Jalal 2016 년

5
@MonaJalal 밀리 초 : DateTime.Now.ToString("hh:mm:ss.fff");마이크로 초 : DateTime.Now.ToString("hh:mm:ss.ffffff");나노초 (날짜 시간도 훨씬 해상도가있는 경우) DateTime.Now.ToString("hh:mm:ss.fffffffff");으로 당 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 시간을 초과 할 수 있다는 것입니다.
shelbypereira

왜 int가 아닌 int를 사용하여시, 분, 초를 사용합니까? 이유가 없다면 직접 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으로 교체하는 것이 쉽지 않을 것입니다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.