Calculating days from TimeSpan hours - c#

I have 1 single text box which a user will enter the number of hours. At present, if they enter 26 hours, we get an error because of the TimeSpan's HH limit. This value is going to get stored in a SQL Server 2008 Time(7) field.
How can I get it to recognize more than 23 hours? It is not an option to store it as a decimal because another section of the program requires this field to be a time(7) field.
TimeSpan estiamtedHours;
private void btnSave_Click(object sender, EventArgs e)
{
estimatedHours = TimeSpan.Parse(tbEstHours.Text);
}
The time(7) field also has the limit of 24 hours, what would be the best way round this as Time(7) is required for a Stopwatch on another form.
Thanks

If you know the input value is an hour value as a floating point number, you can use TimeSpan.FromHours():
TimeSpan estiamtedHours;
private void btnSave_Click(object sender, EventArgs e)
{
estimatedHours = TimeSpan.FromHours(Double.Parse(tbEstHours.Text));
}

Parse the text into an int and pass that int as the hours parameter in the TimeSpan constructor.
int hours;
if (Int32.TryParse(tbEstHours.Text, out hours))
{
TimeSpan ts = new TimeSpan(hours, 0, 0);
}
You can also do the same with minutes and seconds. Alternatively, if you just want hours, you can use TimeSpan.FromHours in the same manner instead of the TimeSpan constructor.

Be careful. TimeSpan is meant to measure an elapsed duration of time, while time in SQL Server is specifically a time-of-day. These are two different concepts.
Sometimes these get mixed up. For example, DateTime.TimeOfDay is a TimeSpan type - which goes against its design. It's a reasonable compromise since there is no Time type in .Net and it can fit.
But a TimeSpan that is 24 hours or greater will not fit into a SQL Server time field.
Also, a TimeSpan is based on standard days. You can create one with TimeSpan.FromHours(26) and it will represent "1 day and 2 hours". If you call TimeSpan.FromHours(26).ToString() it will be "1.02:00:00".
If you're storing an elapsed duration of time (not a time of day), then use a TimeSpan in .Net, but use an integer type in SQL Server. Decide what units you want precision for, and that will help you choose a data type.
For example, you can store the full precision of TimeSpan.Ticks using a SQL Server bigint type. But probably you will store TimeSpan.TotalSeconds using an int. When loading, you can use TimeSpan.FromSeconds to get back to a TimeSpan type.
Also be aware that a TimeSpan can be negative, which represents moving backwards in time.
By the way, if you used the Noda Time library - these concepts would be separated for you in types called Duration and LocalTime.
If what you were after is a way to parse a string like "26:00:00" you can't do that with a TimeSpan. But you can use Noda Time:
// starting from this string
string s = "26:00:00";
// Parse as a Duration using the Noda Time Pattern API
DurationPattern pattern = DurationPattern.CreateWithInvariantCulture("H:mm:ss");
Duration d = pattern.Parse(s).Value;
Debug.WriteLine(pattern.Format(d)); // 26:00:00
// if you want a TimeSpan, you can still get one.
TimeSpan ts = d.ToTimeSpan();
Debug.WriteLine(ts); // 1.02:00:00

After parsing the input, use the FromHours method:
double hours
if (double.TryParse(tbEstHours.Text, out hours)
{
TimeSpan time = TimeSpan.FromHours(hours);
}

The TimeSpan.Parse Method expects the input in the format
[ws][-]{ d | [d.]hh:mm[:ss[.ff]] }[ws]
where hh is the hour part, ranging from 0 to 23.
For example,
TimeSpan.Parse("5") returns 5 days,
TimeSpan.Parse("5:14") returns 5 hours and 14 minutes.
If you just want your users to enter a number of hours, you can simply parse the input as an integer and construct a TimeSpan from that:
TimeSpan result = TimeSpan.FromHours(int.Parse("26"));
// result == {1.02:00:00}
(Use int.TryParse for user input.)
If you want your users to enter both hours and minutes (such as 26:14), then you need to implement some parsing method yourself.

Since the other answers don't address this
The concern here is the time column in the database and it expects a valid duration which would be limited to the 24 hr time where as TimeSpan can have them beyond the 24 hr limit.
So you should ideally parse the value as int (use int.Parse or int.TryParse) and then check if it is less than 24 and then create the appropriate TimeSpan

Related

Comparing the binary representation of DateTime in C#

I have a DateTime represented as long (8 bytes), that came from DateTime.ToBinary(), let's call it dateTimeBin. Is there an optimal way of dropping the Time information (I only care for the date) so I can compare it to a start of day? Lets say we have this sample value as a start of day.
DateTime startOfDay = new DateTime(2020,3,4,0,0,0);
long startOfDayBin = startOfDay.ToBinary();
I obviously know I can always convert to a DateTime object then get the date component. However, this operation is going to happen billions of times and every little performance tweak helps.
Is there an efficient way of extracting the Date info of dateTimeBin without converting it to DateTime? Or any arithmetic operation on the long that will return the date only?
Is there a way to match startOfDay (or startOfDayBin) and dateTimeBin if they have the same date components?
Is there a way to see if (dateTimeBin >= startOfDayBin), I don't think the long comparison is valid.
N.B. all the dates are UTC
Since you are working only with UTC dates - makes sense to use DateTime.Ticks instead of DateTime.ToBinary, because former has relatively clear meaning - number of ticks since epoch, just like the unix time, the only difference is unix time interval is second and not tick (where tick is 1/10.000.000 of a second), and epoch is midnight January 1st of 0001 year and not year 1970. While ToBinary only promises that you can restore original DateTime value back and that's it.
With ticks it's easy to extract time and date. To extract time, you need to remainder of division of ticks by number of ticks in a full day, so
long binTicks = myDateTime.Ticks;
long ticksInDay = 24L * 60 * 60 * 10_000_000;
long time = binTicks % ticksInDay;
You can then use convert that to TimeSpan:
var ts = TimeSpan.FromTicks(time);
for convenience, or use as is. The same with extracting only date: just substract time
long date = binTicks - (binTicks % ticksInDay);
Regular comparision (dateTimeBin >= startOfDayBin) in also valid for tick values.

Why is the conversion from ulong to DateTime returning 0?

I'm trying to convert ULONG to DateTime and as DateTime accepts Ticks as param which are LONG, here's how I do it.
ulong time = 12354;
new DateTime((long)time).ToString("HH:mm:ss");
The result of this is 00:00:00.
I don't understand the result, am I doing something wrong?
P.S. i.Time is not 0, I checked multiple times.
Citing the documentation:
Initializes a new instance of the DateTime structure to a specified number of ticks.
ticks
Type: System.Int64
A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
This is 100 nanoseconds which is a super small time unit. So unless your number is larger than 10000000, you don’t even get a single second:
Console.WriteLine(new DateTime((long)10000000).ToString());
// 01.01.0001 00:00:01
So you should really think about what your “time left” (i.Time) value is supposed to mean? Is this really time in the unit of 100 nanoseconds? Very likely not. It’s probably more about seconds or something completely different.
Btw. if the number you have does not actually represent a moment in time, you should not use DateTime. You should use TimeSpan instead. Its long constructor has the same behavior though, but you can use one of the handy static functions to create a time span with the correct unit:
var ts = TimeSpan.FromSeconds(1000);
Console.WriteLine(ts.ToString());
// 00:16:40
Because a tick is 100 nanoseconds, and so 12354 ticks is only 1235400 nanoseconds which is only .0012354 seconds. So your datetime is .0012354 seconds after midnight on 1 Jan in the year one.

Using DateTime for a duration of time e.g. 45:00

I'd just like to know if it is possible to use the DateTime type for durations such as 45:00 (45 minutes) or 120:00 (120 minutes). These values also need to be stored into a Local Sql Server DB. If it is possible, could anyone possibly hint how this could be done using Datetime, or if not just let me know a way it could be done using a different type.
Thank you in advance,
Jamie
You should use the TimeSpan structure
TimeSpan interval = new TimeSpan(0, 45, 0);
Console.WriteLine(interval.ToString());
For the database storing part, you could store the property Ticks because a specific constructor for the TimeSpan structure allows to instantiate a new TimeSpan passing the Ticks value
long ticks = GetTimeSpanValueFromDb();
TimeSpan interval = new TimeSpan(ticks);
I wish to add also that you need a BIGINT T-SQL datatype field to store a long NET datatype
I store durations in seconds in the database and then convert to HH:MM:SS format when comes time to display the data.
Why don't you use TimeSpan instead? You can convert them to Ticks (int), store them in the db and the reverse the process when you need the value.
This is merely a matter of interpretation. SQL Server stores datetime as two four byte integers. One is a signed int count of days from a reference date, the other is an unsigned time of day such that 32bits exactly maps 24 hours. Without the implicit epoch, this isn't a datetime, it's a duration. Nothing prevents you from interpreting it that way.
Of course, it would be more convenient to pick a unit and simply use a float. This is what Windows does, storing datetime as a number of days from a reference date expressed as an 8-byte float (a double).
Personally I don't like "day" as a unit of time. The rotational period of our planet is not constant, and it is necessary to mess about with leap seconds to maintain the illusion that there are 86400 seconds in every day. A better choice is the SI unit, the second, which is defined in terms of repeatable, invariant physical constants.
Better again would be the picosecond, since we could dump the double and use an int64, with all the attendant arithmetical and comparative performance advantages. Depiction in mixed human scale units (yyyy mmm d HH:mm:ss) is already something of a trial. Mapping functions that currently work with fractional days could trivially be scaled to microseconds, although the compensation for leap seconds and leap days would have to be rewritten.
I say picosecond because this is the finest division that fits in 64 bits while encompassing a useful span of time (50,000 years). Femto fits, but fifty years isn't wide enough. I know that eventually there be a year 50K problem but frankly I doubt anyone but archeologists will care about records from 50,000 years ago.

Getting Milliseconds elapsed between arbitrary date and Epoch time

If I write a simple method to return the milliseconds between epoch time and DateTime.UtcNow, I get a proper answer. However, if I write a method to return the milliseconds between some arbitrary date and epoch time, the last three digits are always zero. 'Some arbitrary date' means that I pass in to the method the output of DateTime.Parse("arbitrary date string"). As near as I can make out, the DateTime object returned by .Parse is not returning all the significant digits.
Test method:
static void GetMillis()
{
DateTime dUtc = DateTime.UtcNow;
DateTime epoch = new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc);
double utcmillis = (dUtc - epoch).TotalMilliseconds;
String timestamp = dUtc.ToString();
DateTime arbitrary = (DateTime.Parse(timestamp));
Console.WriteLine("Milliseconds between DateTime.UtcNow {0} \nand epoch time {1} are {2}", dUtc, epoch, utcmillis);
Console.WriteLine("Milliseconds between arbitrary date {0} \nand epoch time {1} are {2}", arbitrary, epoch, (arbitrary - epoch).TotalMilliseconds);
}
Output:
C:\src\vs\epochConverter\epochConverter\bin\Debug
{powem} [54] --> .\epochConverter.exe -debug
Milliseconds between DateTime.UtcNow 8/26/2012 11:12:31 PM
and epoch time 1/1/1970 12:00:00 AM are 1346022751385.8
Milliseconds between arbitrary date 8/26/2012 11:12:31 PM
and epoch time 1/1/1970 12:00:00 AM are 1346022751000
I don't know if I'm doing something grotesquely wrong or not understanding the math here. I've researched in MSDN and can't find anything relating to this difference. I really would like to be able to compute the millis as described -- is it possible?
Thanks.
mp
You want to examine the intermediate values of:
String timestamp = dUtc.ToString();
Just what it returns will depend on your local settings, but it'll be something like 8/26/2012 11:12:31, which is only accurate to the nearest second.
Parsing that of course gives a date-time with 0 milliseconds.
It is therefore correct that your milliseconds-since-epoch method has zeros at that point.
If however you did something like:
arbitrary = new DateTime(2012, 8, 26, 11, 12, 31, 123);
You'd get those 123 milliseconds influencing the result. You can also use a ToString and a ParseExact that includes fractions of a second, or a whole slew of other ways of obtaining a DateTime.
In all, your milliseconds-since-epoch worked perfectly, but your way of getting a date to test it was flawed.
The default DateTime.ToString() format does not include the milliseconds and this is where the data is being lost; it happens before the Parse. To obtain the milliseconds in the string representation, use a custom format:
DateTime.UtcNow.ToString()
// -> 8/26/2012 11:37:24 PM
DateTime.Parse("8/26/2012 11:37:24 PM").Millisecond
// -> 0
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK")
// -> 2012-08-26T23:41:17.3085938Z
DateTime.Parse("2012-08-26T23:41:17.3085938Z").Millisecond
// -> 308
See The Round-trip ("O", "o") Format Specifier
to type less. Or, in this case, consider avoiding the conversion entirely :-)
The math is sound.
The math looks reasonable here. Don't forget there are 1000 milliseconds in a 1 second, so any date computation from an arbitrary time that does not include milliseconds vs an almost identical time that includes milliseconds will have an error of +/- 1000 milliseconds.

Type to store time in C# and corresponding type in T-SQL

I would like to know how to store time in C# and T-SQL. I know that both of them provide a DateTime type but I just need to store a time. For instance:
var startTime = 9PM;
var endTime = 10PM;
And then store/retrieve this values from a database. Thanks in advance.
Francesco
C#
Whether to use a DateTime or TimeSpan type in C# to store 9 PM is up to taste. Personally, I'd use DateTime, leaving the date component empty, since that's semantically closer to what you want. (A TimeSpan is designed to hold time intervals, such as "21 hours".)
The documentation supports both options. This is from the documentation of TimeSpan:
The TimeSpan structure can also be used to represent the time of day, but only if the time is unrelated to a particular date.
On the other hand, the MSDN article Choosing Between DateTime, DateTimeOffset, and TimeZoneInfo mentions the following:
The DateTime structure is suitable for applications that do the following:
* Work with dates only.
* Work with times only.
[...]
T-SQL
SQL Server has a time data type.
In C# there is not a type to hold only a time. There is TimeSpan, but it's intended to keep a period of time and not really a component of a DateTime (i.e. hours and minutes) only.
Starting with SQL Server 2008 there is a time type (Using Date and Time Data) that does only store a time component.
EDIT: Misread your question at first. TimeSpan is exactly what you're looking for and it can be stored in a time type with SQL 2K8.
In C# you'd probably want to use a TimeSpan structure if you just wanted to store a time interval. However, you seem to want to appear to store a start-time and an end-time, which would require storing two values. You could, therefore, use two TimeSpans (based on, say, number of minutes from midnight to represent the time) or you could just use two DateTime values and throw away the date component.
As has been noted, SQL Server 2008 has a Time datatype, but this isn't available in earlier versions which only have DateTime. You could also just store an Int representing number of minutes past midnight which can be easily converted to a TimeSpan (TimeSpan interval = TimeSpan.FromMinutes(60)).
Timespan in c# is how you manipulate time intervals. Contrary to what other posters are saying i don't think the Time data type is correct for storing time intervals in SQL, unless you actually want to store the start time and end time and not the time interval (i.e. 1 hour in you example). It is for storing a time of day, a bit like a DateTime but with no date. When i want to store a time interval in SQL I just use an int and then have it represent a unit of time appropriate to what I am trying to do (e.g.minutes, seconds, milliseconds etc. )

Categories