How do I represent a time only value in .NET?
Asked Answered
R

11

285

Is there a way one can represent a time only value in .NET without the date? For example, indicating the opening time of a shop?

TimeSpan indicates a range, whereas I only want to store a time value. Using DateTime to indicate this would result in new DateTime(1,1,1,8,30,0) which is not really desirable.

Romans answered 10/1, 2010 at 14:34 Comment(0)
M
182

As others have said, you can use a DateTime and ignore the date, or use a TimeSpan. Personally I'm not keen on either of these solutions, as neither type really reflects the concept you're trying to represent - I regard the date/time types in .NET as somewhat on the sparse side which is one of the reasons I started Noda Time. In Noda Time, you can use the LocalTime type to represent a time of day.

Note that as of .NET 6, there are TimeOnly and DateOnly types which are roughly equivalent to Noda Time's LocalTime and LocalDate types.

One thing to consider: the time of day is not necessarily the length of time since midnight on the same day...

(As another aside, if you're also wanting to represent a closing time of a shop, you may find that you want to represent 24:00, i.e. the time at the end of the day. Most date/time APIs - including Noda Time - don't allow that to be represented as a time-of-day value.)

Moquette answered 10/1, 2010 at 15:0 Comment(21)
"[T]he time of day is not necessarily the length of time since midnight on the same day..." Is daylight savings time the only reason? Just curious why you left it indefinite.Cocytus
@Jason: Daylight saving is the only reason I can think of offhand - ignoring leap seconds as irrelevant to most applications. I mostly left it that way to encourage others to think why that might be. I reckon it's a good thing for people to think a bit more deeply about dates/times than they currently do :)Moquette
LocalTime is exactly what I need to support my requirement.Romans
@sduplooy: Fancy helping us port it from Joda Time then? :)Moquette
Sure, I think I'll start on LocalTime :)Romans
the time of day is not necessarily the length of time since midnight on the same day: also true if I travel and change timezones.Shunt
@AndyM: I don't think "you changing time zone" is relevant here. DST changes are relevant, but a simple time of day doesn't need to take into account one person changing time zone.Moquette
@JonSkeet I want to keep track of time in a song, or a video. Another way of thinking: You are playing a video, and on given point in time I want to show some information, so my time has to be tied to the video time and not the time of the day and obviously with no date, since that video could be played at any day at any time, anywhere. Does Noda takes care of that, or do you have any other ideas?Folklore
@Oakcool: Yes, you want an elapsed time - a Duration in Noda Time, and a TimeSpan in .NET.Moquette
Not sure I understand. Here is what I need: An array of time {00:01:30, 00:06:30, 01:30:30}, so I can while watching a video say, at [0], "oakcool enters the scene and looks very confused,[see his profile here", at [1], Jon Skeet enters the scene and explains it all [see his profile here], at [2], both walk away to find some beer [see solution here]. But with DateTime that is not good enough.Folklore
@Oakcool: Exactly as I said on May 18th: Duration in Noda Time, or TimeSpan in the BCL. I'd probably encapsulate the "place in video + comment" as a type, and then have an array of that type.Moquette
There isi a TimeOfDay property on the .NET DateTime object, so it's supposed to be used for thatNord
@Mafii: I'd argue that TimeOfDay is a TimeSpan simply because there isn't a more suitable type in .NET. That doesn't mean it's a good use for it. What does a TimeSpan of 5 hours mean? It doesn't necessarily mean that 5 hours have passed since midnight - it means it's 5am. It's a broken representation, IMO.Moquette
@JonSkeet fair enough, sound reasoning. Noda time is great btw.Nord
Thanks for updating to include TimeOnly, but I think it should be a bit more prominent than an afterthought (fully grant that you may be intending to do that in a follow-up edit, though)...Prodigy
@TylerH: I've moved it to the second paragraph, but frankly if a reader isn't willing to read 4 paragraphs (hardly War and Peace) then I don't have a huge amount of sympathy.Moquette
@JonSkeet Fair point, though I think it's also fair to consider there's a lot of scrolling to do here to get to through all the answers, and when seeing the first paragraph of your answer start as "use TimeSpan", it should be understandable that a reader might not think "Oh, maybe they talk about TimeOnly later on in this answer" :-)Prodigy
@TylerH: If someone can't even be bothered to read to the second sentence which discourages the use of TimeSpan, I have even less sympathy. (Even the first sentence says you can (with emphasis) rather than saying "use TimeSpan" - I think you've mischaracterized it significantly.) No-one should use Stack Overflow for "I only need to glance at an answer and half-read the first sentence without actually bothering to think."Moquette
@JonSkeet My point is about readers who've already happened upon the first solutions you offer but are looking for something else/better. If I know there's something called TimeOnly and land on this page looking for an answer that talks about how to use it, for example, I'm not going to bother reading past the first sentence or two of an answer before moving onto the next... because TimeOnly is now the best way to do it, so answers containing it should start with that (albeit with a warning that it's in .NET 6.0 only). And I know SO well. Imagine how a new/anonymous user would behave.Prodigy
@JonSkeet I'm not saying that your answer was bad, or that your addition of TimeOnly was not a net positive. I think moving it to the 2nd paragraph is a massive improvement and given that it's only for .NET 6.0 I think that's perfectly fine prominence for now. I was only saying that (in my humble opinion) adding it as an apparent afterthought (below two other options followed by your own custom library, of all things), was not ideal placement.Prodigy
@TylerH: I think we'll have to agree to disagree - in particular I don't believe that TimeOnly is the best way to do it, as I'd still recommend using Noda Time - chances are that if you need "time of day" you need other date/time-related concepts as well, and I still believe that Noda Time provides the best way of doing that. I suggest that this comment thread has really gone on long enough at this point though.Moquette
G
194

You can use timespan

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

[Edit]
Considering the other answers and the edit to the question, I would still use TimeSpan. No point in creating a new structure where an existing one from the framework suffice.
On these lines you would end up duplicating many native data types.

Gallopade answered 10/1, 2010 at 14:35 Comment(23)
Exactly. DateTime uses TimeSpan for exactly that purpose. Doc for DateTime.TimeSpan Property: "A TimeSpan that represents the fraction of the day that has elapsed since midnight."Hexosan
TimeSpan indicates an interval whereas the time I'm talking about is not an interval, but a single fixed point over a range of dates.Romans
It may be used as a fixed point at well, and as you specified in the question, it is without date. After all you decide how to use these datatypes to your benifit.Gallopade
@John G: While it can be used to represent a fixed point, I agree with the OP - overloading the use of TimeSpan like this is somewhat ugly. It's the best that's available within the framework itself, but that's not the same as saying it's pleasant.Moquette
@MarcelJ: I take issue with that description of DateTime.Time. At 3am on March 28th in London, there will only have been 2 hours since midnight, for example. "Time of day" != "elapsed time since midnight." I realise that's MSDN's description rather than yours, but I'm just pointing out the problems with it.Moquette
You can treat the timepsan in such a scenario to be a GMT representation, again an effecive usage as required.Gallopade
@John G: Yes, you can do that. I just think it's pretty ugly.Moquette
Still IMHO is better than a new set of classes to duplicate natively available data structure.Gallopade
Given new DateTime().TimeSpan and that new TimeSpan(hour, minute, second) both exists I find it pretty compelling that they fully intended TimeSpan to be used to start clock time and not just time deltas.Exist
As of .Net 3.5, MSDN documents that "The TimeSpan structure can also be used to represent the time of day, but only if the time is unrelated to a particular date.". In other words, this is exactly the solution to the proposed question.Porphyritic
@Pharap: Just because it can be used that way doesn't make that a good idea. It means that TimeSpan is used for two very different purposes - just like DateTime is used for values in UTC, local and unspecified time zones. Fundamentally, the date/time types in the framework have significant conceptual issues, IMO.Moquette
@JonSkeet I don't disagree that .Net's time-related features are lacking (particularly in comparison to Java and its time unit conversions) but I suspect they're more concerned with their shiny new features than fixing the things they screwed up. It might seem dirty to be using DateTime and TimeSpan for things that seem to go against their name, but it depends how you view them. Personally I think it makes sense to think that a time of day is not a fixed point on its own. Without a date as a reference point, a time of day is just an offset into some arbitrary day.Porphyritic
@Pharap: I think you've missed my point - I'm basically saying that it's a bad fit, and there are better options available; not within the core framework, but in Noda Time and undoubtedly other libraries. In particular, there are things you can legitimately do with TimeSpan (such as adding two values) which simply don't make sense for time-of-day values. That sort of thing is why I think it's a bad idea to use TimeSpan to represent a time of day.Moquette
@JonSkeet It's not less than what people do with Vectors in most physics and game libraries. Instead of declaring Points and Vectors to be separate, Vectors are used for both. Besides, sometimes a solution outside the core framework isn't ideal. Say for example if you are developing a library for external consumption, the more dependencies you use, the more dependencies you force on the consumers of your library.Porphyritic
@Pharap: Whereas I'd say that it's better to use Point and Vector separately, and using a Vector when you mean a Point leads to confusing code. While I agree that adding dependencies isn't a great idea, I do think it's reasonable to point out that TimeSpan really isn't a brilliant fit for "time of day", and that your suggestion that it "this is exactly the solution to the proposed question" is ignoring that. Just because MS does it doesn't make it a good idea :)Moquette
@JonSkeet To be honest I think we are just going round in circles. I agree that Point and Vector should be treated seperately and equally, my point was that many well known libraries choose to ignore this for simplicity. Feel free to disagree with MSDN but I'm going to stick with them this time. The OP and anyone else is free to choose whatever suits their situation, whether that is the standard library or an external library, but I know which side I'm sticking with. I can't really say much more without more information about the OP's situation.Porphyritic
@Pharap: I think we'll have to agree to disagree. I hate it when my code becomes unclear due to using types to represent values they weren't really designed for.Moquette
Fun fact: Generating an ADO.net data model using MySQL and Schema First will select TimeSpan as the model data type for any columns with type "Time". It still floors me how relatively unsolved the the whole time problem is. I think it should probably be a separate and required class for any CS degree.Labyrinthodont
-1, "No point in creating a new structure where an existing one from the framework suffice" - there is a point, not conflating the concept that similar (even identical) implementations as meaning they are the same thing. I accept your answer in this case is pragmatic, I object to the general advice given.Bloodthirsty
Thinking about "On these lines you would end up duplicating many native data types" you're right. I think it would be nice to be able to "typedef" value types in C# to avoid naked types.Bloodthirsty
You would get conflicts this way. How would you then differentiate the time represented with the same values in different time zones (which if we are talking about when a shop opens can't be avoided). For example the times below are all sequential and you have 1am and 2am repeated. 1am UTC-4 = 5am UTC, 2am UTC-4 = 6am UTC, 1am UTC-5 = 7am UTC, 2am UTC-5 = 8am UTC. You can do this, but you just have to think of time span as a duration not a time on a clock in any sense whatsoever which you would look at to see when you can enter the shop.Stereoscopy
Eh. System.Data.SqlDbType.Time maps to System.TimeSpan. That's good enough for me.Inch
To use Timespan for defining a Time and not a timerange is of course possible, but hour must not be higher than 23, minute not higher than 59 and seonds not higher than 59. Timespan can even accept negative numbers.Peg
M
182

As others have said, you can use a DateTime and ignore the date, or use a TimeSpan. Personally I'm not keen on either of these solutions, as neither type really reflects the concept you're trying to represent - I regard the date/time types in .NET as somewhat on the sparse side which is one of the reasons I started Noda Time. In Noda Time, you can use the LocalTime type to represent a time of day.

Note that as of .NET 6, there are TimeOnly and DateOnly types which are roughly equivalent to Noda Time's LocalTime and LocalDate types.

One thing to consider: the time of day is not necessarily the length of time since midnight on the same day...

(As another aside, if you're also wanting to represent a closing time of a shop, you may find that you want to represent 24:00, i.e. the time at the end of the day. Most date/time APIs - including Noda Time - don't allow that to be represented as a time-of-day value.)

Moquette answered 10/1, 2010 at 15:0 Comment(21)
"[T]he time of day is not necessarily the length of time since midnight on the same day..." Is daylight savings time the only reason? Just curious why you left it indefinite.Cocytus
@Jason: Daylight saving is the only reason I can think of offhand - ignoring leap seconds as irrelevant to most applications. I mostly left it that way to encourage others to think why that might be. I reckon it's a good thing for people to think a bit more deeply about dates/times than they currently do :)Moquette
LocalTime is exactly what I need to support my requirement.Romans
@sduplooy: Fancy helping us port it from Joda Time then? :)Moquette
Sure, I think I'll start on LocalTime :)Romans
the time of day is not necessarily the length of time since midnight on the same day: also true if I travel and change timezones.Shunt
@AndyM: I don't think "you changing time zone" is relevant here. DST changes are relevant, but a simple time of day doesn't need to take into account one person changing time zone.Moquette
@JonSkeet I want to keep track of time in a song, or a video. Another way of thinking: You are playing a video, and on given point in time I want to show some information, so my time has to be tied to the video time and not the time of the day and obviously with no date, since that video could be played at any day at any time, anywhere. Does Noda takes care of that, or do you have any other ideas?Folklore
@Oakcool: Yes, you want an elapsed time - a Duration in Noda Time, and a TimeSpan in .NET.Moquette
Not sure I understand. Here is what I need: An array of time {00:01:30, 00:06:30, 01:30:30}, so I can while watching a video say, at [0], "oakcool enters the scene and looks very confused,[see his profile here", at [1], Jon Skeet enters the scene and explains it all [see his profile here], at [2], both walk away to find some beer [see solution here]. But with DateTime that is not good enough.Folklore
@Oakcool: Exactly as I said on May 18th: Duration in Noda Time, or TimeSpan in the BCL. I'd probably encapsulate the "place in video + comment" as a type, and then have an array of that type.Moquette
There isi a TimeOfDay property on the .NET DateTime object, so it's supposed to be used for thatNord
@Mafii: I'd argue that TimeOfDay is a TimeSpan simply because there isn't a more suitable type in .NET. That doesn't mean it's a good use for it. What does a TimeSpan of 5 hours mean? It doesn't necessarily mean that 5 hours have passed since midnight - it means it's 5am. It's a broken representation, IMO.Moquette
@JonSkeet fair enough, sound reasoning. Noda time is great btw.Nord
Thanks for updating to include TimeOnly, but I think it should be a bit more prominent than an afterthought (fully grant that you may be intending to do that in a follow-up edit, though)...Prodigy
@TylerH: I've moved it to the second paragraph, but frankly if a reader isn't willing to read 4 paragraphs (hardly War and Peace) then I don't have a huge amount of sympathy.Moquette
@JonSkeet Fair point, though I think it's also fair to consider there's a lot of scrolling to do here to get to through all the answers, and when seeing the first paragraph of your answer start as "use TimeSpan", it should be understandable that a reader might not think "Oh, maybe they talk about TimeOnly later on in this answer" :-)Prodigy
@TylerH: If someone can't even be bothered to read to the second sentence which discourages the use of TimeSpan, I have even less sympathy. (Even the first sentence says you can (with emphasis) rather than saying "use TimeSpan" - I think you've mischaracterized it significantly.) No-one should use Stack Overflow for "I only need to glance at an answer and half-read the first sentence without actually bothering to think."Moquette
@JonSkeet My point is about readers who've already happened upon the first solutions you offer but are looking for something else/better. If I know there's something called TimeOnly and land on this page looking for an answer that talks about how to use it, for example, I'm not going to bother reading past the first sentence or two of an answer before moving onto the next... because TimeOnly is now the best way to do it, so answers containing it should start with that (albeit with a warning that it's in .NET 6.0 only). And I know SO well. Imagine how a new/anonymous user would behave.Prodigy
@JonSkeet I'm not saying that your answer was bad, or that your addition of TimeOnly was not a net positive. I think moving it to the 2nd paragraph is a massive improvement and given that it's only for .NET 6.0 I think that's perfectly fine prominence for now. I was only saying that (in my humble opinion) adding it as an apparent afterthought (below two other options followed by your own custom library, of all things), was not ideal placement.Prodigy
@TylerH: I think we'll have to agree to disagree - in particular I don't believe that TimeOnly is the best way to do it, as I'd still recommend using Noda Time - chances are that if you need "time of day" you need other date/time-related concepts as well, and I still believe that Noda Time provides the best way of doing that. I suggest that this comment thread has really gone on long enough at this point though.Moquette
G
38

If that empty Date really bugs you, you can also to create a simpler Time structure:

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

Or, why to bother: if you don't need to do any calculation with that information, just store it as String.

Guff answered 10/1, 2010 at 14:43 Comment(11)
Hmmm... maybe... but why reinvent the wheel? If the language already has a class/structure (which C# and VB.NET do), then go with it. But I do understand where you are trying to go with your answer.Soembawa
How would this structure be any different from TimeSpan, this would just duplicate it in a way.Gallopade
Downvoting you due to the existance of TimeSpan, which already handles this, and in a significantly better way.Yeld
@silky, I wrote this after reading first answer; OP said on question he doesn't wanted to use TimeSpan; I, personally, would opt to use a plain DateTimeGuff
Rubens: Fair enough; I'll remove the downvote via a useless edit.Yeld
@KrisKrause I don't think this is reinventing the wheel, TimeSpan gives you much more than just a Time. If using it as just a Time, it can be misinterpreted and misused e.g. a property like Days has no meaning.Allover
+1 This is better than a TimeSpan because it has less possibilities of misinterpretation... a TimeSpan is really meant to be used as an interval (see MSDN) so a property like Days has no meaning when TimeSpan is used as a TimeAllover
While I think it's reasonable to have a different type, I don't think this is a good solution: 1) it's a mutable value type; 2) it performs no validation of any kind.Moquette
@JonSkeet, this old answer was supposed to describe the idea, not to implement the whole concept.Guff
@Rubens: If you're going to provide any implementation, I think it would be better to avoid obvious bad ideas (mutable value types) and indicate that more work is required to make this even close to production ready.Moquette
I'm upvoting for the concept of the solution, I'm not 100% happy with your implementation of it, but that's just a personal difference.Porphyritic
I
21

I say use a DateTime. If you don't need the date portion, just ignore it. If you need to display just the time to the user, output it formatted to the user like this:

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

It seems like all the extra work of making a new class or even using a TimeSpan is unnecessary.

Intervocalic answered 22/11, 2012 at 1:22 Comment(3)
How would you show seconds and mili-seconds in this method?Expire
@MonaJalal Milliseconds: DateTime.Now.ToString("hh:mm:ss.fff"); Microseconds: DateTime.Now.ToString("hh:mm:ss.ffffff"); Nanoseconds (if DateTime even has that much resolution): DateTime.Now.ToString("hh:mm:ss.fffffffff"); As per MSDNPorphyritic
So, the 5 to 10 minutes it takes to implement a proper type for this seems more work to you than having to consider in the whole codebase, for any future development, that a DateTime property might contain a time only, and has to be formatted like that in those scenarios, and the date portion might need to be ignored? Have fun debugging the occurrences where you will find "0001-01-01 10:00" in your database, in external communications, etc....Microhenry
B
13

I think Rubens' class is a good idea so thought to make an immutable sample of his Time class with basic validation.

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);
    }
}
Blotchy answered 1/7, 2015 at 11:14 Comment(2)
The validation you have added is extremely important. The main drawback of the TimeSpan class in modelling a time is that the time of day can be more than 24 hours.Lewanna
Why are the Hours, minutes and seconds using int and not uint? If there is no reason I think they can directly use uint and this avoids the casting in the constructor.Lewanna
S
8

In C# 10 you can use TimeOnly.

TimeOnly date = TimeOnly.FromDateTime(DateTime.Now);
Stope answered 8/11, 2021 at 17:3 Comment(3)
TimeOnly is a framework specific feature. The type could be used in C# 9 if the project targets net6.0 or greaterJunta
@DigitalCoyote as far as I understand C#10 is supported by .Net 6Conversationalist
@GuillaumeRaymond that is correct, but TimeOnly is supported by .Net 6 even if you use C# 9 as your language version. You might use C# 9 or lower to enforce compatability in files linked to projects targeting older frameworks or to allow compiler directives to build an application for an older framework.Junta
F
7

In addition to 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);
    }
}
Faddist answered 2/7, 2015 at 13:41 Comment(2)
Your add methods for minutes and seconds are wrong as they don't account for values above 59.Blotchy
@Chibueze Opate: you're completely right. This was just quick and dirty. I should put some more work in this code. I will update it later... Thanks for your hint!Faddist
A
7

Here's a full featured TimeOfDay class.

This is overkill for simple cases, but if you need more advanced functionality like I did, this may help.

It can handle the corner cases, some basic math, comparisons, interaction with DateTime, parsing, etc.

Below is the source code for the TimeOfDay class. You can see usage examples and learn more here:

This class uses DateTime for most of its internal calculations and comparisons so that we can leverage all of the knowledge already embedded in 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);
        }
    }
}
Angelia answered 31/5, 2019 at 23:9 Comment(0)
R
5

A System.TimeOfDay type was recently approved for an upcoming release of .NET 6.

See https://github.com/dotnet/runtime/issues/49036

When complete, this will be the preferred way of representing time of day values that are not associated with any particular date or time zone.

System.TimeSpan will still be the recommended way to represent elapsed time values.

Rosalynrosalynd answered 25/3, 2021 at 18:59 Comment(0)
S
2

If you don't want to use a DateTime or TimeSpan, and just want to store the time of day, you could just store the seconds since midnight in an Int32, or (if you don't even want seconds) the minutes since midnight would fit into an Int16. It would be trivial to write the few methods required to access the Hour, Minute and Second from such a value.

The only reason I can think of to avoid DateTime/TimeSpan would be if the size of the structure is critical.

(Of course, if you use a simple scheme like the above wrapped in a class, then it would also be trivial to replace the storage with a TimeSpan in future if you suddenly realise that would give you an advantage)

Silvasilvain answered 10/1, 2010 at 21:20 Comment(0)
C
0

You can simply use validation constraint.

[DataType(DataType.Time)]
public DateTime OpeningTime { get; set; }
Cathryncathy answered 6/6, 2022 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.