.NET'te yalnızca bir zaman değerini nasıl temsil edebilirim?


238

Bir tarih olmadan .NET yalnızca bir zaman değeri temsil edebileceği bir yolu var mı? Örneğin, bir mağazanın açılış saatini gösterir misiniz?

TimeSpanyalnızca bir zaman değeri saklamak istiyorum. DateTimeBunu belirtmek için kullanılması , DateTime(1,1,1,8,30,0)gerçekten arzu edilmeyen yeni sonuçlara yol açacaktır .

Yanıtlar:


144

Diğerleri söylediler, sen yapabilirsiniz bir kullanmak DateTimeve tarihi göz ardı ettiği ya kullanın TimeSpan. Şahsen bu çözümlerden hiçbirine meraklı değilim, çünkü hiçbiri gerçekten temsil etmeye çalıştığınız konsepti yansıtmıyor - .NET'in tarih / saat türlerini, başlangıçta neden olduğum seyrek taraflardan biri olarak seyrek olarak görüyorum. Noda Zamanı . Noda Time'da,LocalTime günün saatini temsil etmek türü .

Dikkate alınması gereken bir nokta: günün saati, aynı gün gece yarısından bu yana geçen süre değildir ...

(Başka bir kenara, bir mağazanın kapanış saatini temsil etmek istiyorsanız, günün 24 saati, yani günün sonundaki saati temsil etmek istediğinizi görebilirsiniz. Çoğu tarih / saat API'sı - Noda dahil Zaman - bunun günün saati değeri olarak gösterilmesine izin vermeyin.)


5
“[T] günün saati, aynı gün gece yarısından bu yana geçen zamanın uzunluğu kadar değil ..." Yaz saati uygulaması tek sebep midir? Neden belirsiz bıraktığınızı merak ediyorum.
Jason

14
@Jason: Gün ışığından yararlanma, hazırlıksız olarak düşünmemin tek nedenidir - artık saniyeler çoğu uygulama için önemsizdir. Çoğunlukla bu şekilde başkalarını bunun neden olabileceğini düşünmeye teşvik etmek için bıraktım. İnsanların tarihler / saatler hakkında şu anda olduğundan biraz daha derin düşünmeleri iyi bir şeydir :)
Jon Skeet

LocalTime, gereksinimlerimi desteklemek için tam olarak ihtiyacım olan şey.
sduplooy

1
@sduplooy: O zaman Joda Time'dan bağlantı kurmamıza yardımcı olmak ister misin? :)
Jon Skeet

1
@Oakcool: Tam olarak 18 Mayıs'ta söylediğim gibi: DurationNoda Time'da veya BCL'de TimeSpan. Muhtemelen bir tür olarak "video + yorum yer" kapsüllemek ve daha sonra bu tür bir dizi var.
Jon Skeet

164

Sen kullanabilirsiniz zaman aralığı

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

[Düzenle]
Diğer cevaplar ve sorudaki düzenleme dikkate alındığında, yine de TimeSpan kullanacağım. Çerçeveden var olan bir yapının yeterli olduğu yeni bir yapı oluşturmanın anlamı yok.
Bu satırlarda birçok yerel veri türünü çoğaltabilirsiniz.


19
Kesinlikle. DateTime tam da bu amaçla TimeSpan kullanır. Doc for DateTime.TimeSpan Özellik: "Gece yarısından bu yana geçen günün bölümünü temsil eden bir TimeSpan."
Marcel Jackwerth

4
TimeSpan bir aralığı belirtirken, bahsettiğim zaman bir aralık değil, belirli bir tarih aralığındaki tek bir sabit noktadır.
sduplooy

3
Kuyu sabit bir nokta olarak da kullanılabilir ve soruda belirttiğiniz gibi, tarih yoktur. Sonuçta, bu veri tiplerini yararınıza nasıl kullanacağınıza siz karar verirsiniz.
John G

9
@John G: Sabit bir noktayı temsil etmek için kullanılabilse de , OP'ye katılıyorum - bunun TimeSpangibi kullanımın aşırı yüklenmesi biraz çirkin. Çerçevenin içinde mevcut olan en iyisidir, ancak bu hoş olduğunu söylemekle aynı şey değildir.
Jon Skeet

5
Net 3.5'ten itibaren, MSDN, "TimeSpan yapısı günün saatini temsil etmek için de kullanılabilir, ancak yalnızca saatin belirli bir tarihle ilgisiz olması durumunda". Başka bir deyişle, tam olarak önerilen sorunun çözümü budur.
Pharap

34

Bu boş Dategerçekten sizi rahatsız ediyorsa, daha basit bir Timeyapı da oluşturabilirsiniz :

// 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);
    }
}

Veya neden rahatsız edersiniz: bu bilgilerle herhangi bir hesaplama yapmanız gerekmiyorsa, sadece olarak saklayın String.


2
Hmmm ... belki ... ama neden tekerleği yeniden icat ettiniz? Dilin zaten bir sınıfı / yapısı varsa (C # ve VB.NET bunu yapar), onunla git. Ama cevabınızla nereye gitmeye çalıştığınızı anlıyorum.
Kris Krause

1
Bu yapı TimeSpan'dan nasıl farklı olurdu, bu sadece bir şekilde çoğaltacaktır.
John G

4
Bunu TimeSpanzaten ele alan ve çok daha iyi bir şekilde varlığından dolayı sizi aşağıya indiriyor .
Öğlen İpek

1
@silky, bunu ilk cevabı okuduktan sonra yazdım; OP soru üzerine TimeSpan kullanmak istemediğini söyledi; Şahsen, sade bir DateTime kullanmayı tercih ederim
Rubens Farias

18
+1 Bu bir TimeSpan'dan daha iyidir çünkü yanlış yorumlama olasılıkları daha azdır ... TimeSpan gerçekten bir aralık olarak kullanılmalıdır (MSDN'ye bakın), bu nedenle TimeSpan bir Time olarak kullanıldığında Days gibi bir özellik anlamsızdır
Zaid Mesud

20

Bir DateTime kullan diyorum. Tarih bölümüne ihtiyacınız yoksa, yok sayın. Kullanıcıya yalnızca zamanı göstermeniz gerekiyorsa, kullanıcıya şu şekilde biçimlendirin:

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

Yeni bir sınıf yapmak veya hatta bir TimeSpan kullanmak için yapılan tüm ekstra çalışmalar gereksiz gibi görünüyor.


Bu yöntemde saniyeleri ve mili saniyeleri nasıl gösterirdiniz?
Mona Jalal

5
@MonaJalal Milisaniyeler: DateTime.Now.ToString("hh:mm:ss.fff");Mikrosaniye: DateTime.Now.ToString("hh:mm:ss.ffffff");nanosaniye (DateTime bile bu kadar çözünürlüğe sahipse): DateTime.Now.ToString("hh:mm:ss.fffffffff");As per MSDN
Pharap

2
Bu nedenle, bunun için uygun bir tür uygulamak için 5 ila 10 dakika, tüm kod tabanında, gelecekteki herhangi bir geliştirme için, bir DateTime özelliğinin yalnızca bir zaman içerebileceği ve biçimlendirilmesi gerektiğinden daha fazla iş gibi görünüyor. böyle senaryolarda ve tarih bölümünün yoksayılması gerekebilir? "0001-01-01 10:00" veritabanında, dış iletişim, vb nerede bulacaksınız hata ayıklama eğlenin ....
MarioDS

10

Sanırım Rubens'in sınıfı iyi bir fikirdir, bu yüzden Time doğrulamasının değişmez bir örneğini temel doğrulama ile oluşturduğu düşünülür.

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);
    }
}

Eklediğiniz doğrulama son derece önemlidir. TimeSpan sınıfının bir zamanı modellemedeki ana dezavantajı günün saatinin 24 saatten fazla olabilmesidir.
shelbypereira

Neden Saat, Dakika ve Saniye uint değil int kullanıyor? Hiçbir neden yoksa doğrudan uint kullanabilirsiniz düşünüyorum ve bu yapıcı döküm önlemek.
shelbypereira

6

Chibueze Opata'ya ek olarak :

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);
    }
}

Dakika ve saniye için ekleme yöntemleriniz yanlıştır çünkü
59'dan büyük

@Chibueze Opate: Tamamen haklısın. Bu sadece hızlı ve kirli. Bu kodda biraz daha çalışma yapmalıyım. Daha sonra güncelleyeceğim ... İpucu için teşekkürler!
Jules

5

İşte tam özellikli bir TimeOfDay sınıfı.

Bu basit durumlar için aşırıya kaçıyor, ancak yaptığım gibi daha gelişmiş işlevselliğe ihtiyacınız varsa, bu yardımcı olabilir.

Köşe vakaları, bazı temel matematik, karşılaştırmalar, DateTime ile etkileşim, ayrıştırma vb.

TimeOfDay sınıfının kaynak kodu aşağıdadır. Kullanım örneklerini görebilir ve buradan daha fazla bilgi edinebilirsiniz :

Bu sınıf, DateTime'ı dahili hesaplamaları ve karşılaştırmaları için kullanır, böylece DateTime'da zaten gömülü olan tüm bilgilerden yararlanabiliriz.

// 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

Bir DateTime veya TimeSpan kullanmak istemiyorsanız ve sadece günün saatini saklamak istiyorsanız, gece yarısından bu yana geçen saniyeleri bir Int32'de veya gece yarısından bu yana (dakika bile istemiyorsanız) saklayabilirsiniz bir Int16'ya sığar. Saat, Dakika ve Saniye'ye böyle bir değerden erişmek için gereken birkaç yöntemi yazmak önemsiz olacaktır.

DateTime / TimeSpan'dan kaçınmayı düşünebilmemin tek nedeni, yapının boyutu kritikse olabilir.

(Elbette, yukarıdaki gibi bir sınıfa sarılmış basit bir şema kullanırsanız, aniden size bir avantaj sağlayacağını fark ederseniz, depolamayı gelecekte bir TimeSpan ile değiştirmek de önemsiz olacaktır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.