How to find time differences between two time in c# - c#

I have a form that gives me time_in and time_out hours of all staff members. Time_in and time_out also sql tables. Basically form returns the database table values. What I would like to do is to display the work hour of them. Work hour can be get the differences from time_out to time_in. I have two text boxes that display time in and Time_out. The third one should display work hour.
Here is what I have for time_out value for Wednesday:
//Selected TimeOutWednesday
SqlCommand TimeOutWednesdayMain = cs.CreateCommand();
TimeOutWednesdayMain.CommandText = "SELECT CONVERT(VARCHAR(8), time_out, 108) AS time_out FROM job_punch_card WHERE emp_key='" + listBoxNames.SelectedValue.ToString() + "'and punch_day= DATEADD(week, DATEDIFF(day, 0, getdate())/7, 2)";
Object TimeOutWednesdayMainTemp = TimeOutWednesdayMain.ExecuteScalar();
txtTimeInWed.Text = TimeOutWednesdayMainTemp.ToString();
This code gives me time_out for wednesday for the selected user from my listbox. I have the same code for time_in as well. What I couldn't do is to figure out how find the work hour? How can I display their work hour in a label or text box like I have above?

The DateTime types in OS are simples Integer values, you should make a arithmetical operation and get the Hours or TotalHours:
Hours get the integer portion of difference (dont lose parse your textboxes):
DateTime time_in;
DateTime.TryParse(time_in_TextBox.Text, out time_in);
DateTime time_out;
DateTime.TryParse(time_out_TextBox.Text, out time_out);
int hours = (time_in - time_out).Hours;
TotalHours get a double value with the exact difference in DateTimes:
DateTime time_in;
DateTime.TryParse(time_in_TextBox.Text, out time_in);
DateTime time_out;
DateTime.TryParse(time_out_TextBox.Text, out time_out);
double totalHours = (time_in - time_out).TotalHours;

I don't know if you're set on doing the time difference in SQL, but here's how you can do it in C# - though you would need to convert to a datetime object, not just a time (at least in this example).
See https://dotnetfiddle.net/DSuHmH :
using System;
public class Program
{
public static void Main()
{
DateTime startTime = new DateTime(2014, 1, 1, 1, 5, 1);
DateTime endTime = new DateTime(2014, 1, 1, 5, 10, 55);
TimeSpan ts = endTime - startTime;
Console.WriteLine(ts); // returns "04:05:54"
}
}
to specifically get the hours use:
ts.Hours;
To accomplish in sql you can do something like the following - again you'll need to convert your time to a datetime and then you can utilize the datediff function.
See: http://sqlfiddle.com/#!6/b7a7c/7
select convert(datetime, '2014-01-01 ' + startTime, 101),
convert(datetime, '2014-01-01 ' + endTime, 101),
datediff(
hh,
convert(datetime, '2014-01-01 ' + startTime, 101),
convert(datetime, '2014-01-01 ' + endTime, 101)
)
from test
Note that in the examples above i'm using an arbitrary date to accomplish creating a valid datetime

You should be able to do this in one query. This also shows the best practices for dealing with ADO.Net including putting your sql objects in using statements and passing values into your query as a parameter. Additionally you'll have to use ExecuteReader to get more than one value back and you can add checking for retrieving more or less than 1 row.
using (var cs = new SqlConnection("your connection string"))
{
cs.Open();
using (var command = cs.CreateCommand())
{
command.CommandText =
#"SELECT
time_in,
time_out,
DATEDIFF(minute, time_in, time_out) As minutesWorked
FROM job_punch_card
WHERE emp_key=#EMPKEY
and punch_day= DATEADD(week, DATEDIFF(day, 0, getdate())/7, 2)";
command.Parameters.AddWithValue(
"#EMPKEY",
listBoxNames.SelectedValue.ToString());
using (var reader = command.ExecuteReader())
{
if (!reader.HasRows)
{
// There was no match for your key
}
reader.Read();
DateTime timeIn = reader.GetDateTime(0);
DateTime timeOut = reader.GetDateTime(1);
int minutesWorked = reader.GetInt32(2);
if (reader.Read())
{
// There was more than one match on key
}
}
}
}

You can get the time difference, in minutes, using this:
DATEDIFF(MINUTE, #punch_day, #time_out)
This will return the total minutes between the two dates. If you need hours, divide it by 60. If you need days, then divide it by (60*24). Should you need weeks, divide it by (60*24*7). If you need years, divide it by (60*24*365). You get the idea, I hope!
Check out this example:
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SELECT #StartDate = '12/9/2014 11:04am'
SELECT #EndDate = '12/10/2014 1:38pm'
SELECT DATEDIFF(MINUTE, #StartDate, #EndDate) AS TotalMinutes,
DATEDIFF(MINUTE, #StartDate, #EndDate) / 60 AS TotalHours,
DATEDIFF(MINUTE, #StartDate, #EndDate) / (60*24) AS TotalDays

Related

Using sql to add to dates during a query

I have a table that keeps track of when particular events occur, and how long they last. For reasons I cannot fathom, it was designed to store the start date of the event, start time of the event, then the number of hours and minutes the event lasted. Like this:
EventStartDate | EventStartTime | TimeSpentHours | TimeSpentMinutes
Where EventStartDate is a dateTime with the hours/minutes always set to zero, so that, even though it's a date time, all the values are like "12/22/2016 00:00". The EventStartTime is a char(4) which is military time of the start of the event. TimeSpentHours is and int which is the total hours the event duration, and TimeSpentMinutes is an int for the number of minutes. Obviously the total time spent for the event is the hours plus the minutes.
The problem: I need to be able to, given a particular DateTime, find all the events that were occuring during that time. Put another way, given a particular DateTime I need to get all the events with a starting date and time that's greater than or equal to the given DateTime and less than or equal to an "end" date and time.
So I need to compute the "EndDateTime" based off the values in the database during the query. The database is SqlServer 2008 R2. I am using C# for WinForm application to query the data.
So far I have roughly:
public static List<ImportantEvents> GetEventsDuringDateTime(DateTime timeOfEvent)
{
using (SqlConnection sqlConn = getAndOpenSqlConn())
{
string theEventTime = timeOfEvent.ToString("hhmm");
string sqlStmt = "SELECT EVENT_ID, AGENCY, EVENTSTARTDATE, ACTNOTE, EVENTSTARTTIME, TIMESPENTHOURS, TIMESPENTMINUTES FROM EVENTSMAIN WHERE((EVENTSTARTDATE<= #MYEVENTDATETIME AND EVENTSTART TIME< #ACTTIME) AND ...";"
}
}
(the above SQL obviously won't work and is where I am stuck...)
My question is: how can I, in the query, add the EVENTSTARTTIME to the EVENTSTARTDATE to create a new "temporary" column, then add the TIMESPENTHOURS and TIMESPENTMINUTES to that column into another new "temporary" column, to then query against given a specific DateTime value???
It is possible to achieve this in a single query with a common-table expression like this:
With StartAndEndTimes As (
Select Event_ID,
EventStart = DateAdd(Minute, Convert(int, Right(EventStartTime, 2)), DateAdd(Hour, Convert(int, Left(EventStartTime, 2)), EventStartDate)),
EventEnd = DateAdd(Minute, Convert(int, Right(EventStartTime, 2))+TimeSpentMinutes, DateAdd(Hour, Convert(int, Left(EventStartTime, 2))+TimeSpentHours, EventStartDate))
From EventsMain)
Select Event_Id, EventStart, EventEnd, <<add other fields here>>
From StartAndEndTimes
Where EventStart <= #MyEventDateTime
And EventEnd > #MyEventDateTime;
Basically you can extract the hours and minutes from the start time and add them to the start date to get a true, datetime, start date. Similar with the end date. It is not necessary to use common-table expression here, but it does make the code more readable. Then you just do the ordinary date comparison to your input parameter.
Here I have disected parts of the final query. You will need to put the final part into your query wherever you need it.
SELECT Combined = EVENTSTARTDATE + EVENTSTARTTIMEFROM FROM EventsMain
SELECT CombinedWithHour = DATEADD(hh, TIMESPENTHOURS, Combined) FROM EventsMain
SELECT CombinedWithMinute = DATEADD(mi, TIMESPENTMINUTES, CombinedWithHour) FROM EventsMain
All together:
SELECT DATEADD(mi, TIMESPENTMINUTES, DATEADD(hh, TIMESPENTHOURS, EVENTSTARTDATE + EVENTSTARTTIME)) FROM EventsMain

DbFunctions DiffDays Gives wrong answer

I want to get a list from DataBase, where MyDate is today or tomarrow.
I wrote the following code.
_Log("Now: " + DateTime.Now.ToString());
var v = db_TS.TS_Test.Where(x => DbFunctions.DiffDays(x.MyDate,DateTime.Now) < 2);
foreach (var item in v.ToList())
{
_Log("MyDate: " + item.MyDate.ToString());
}
The following is logged:
Now: 11/08/2016 10:50:00
MyDate: 27/09/2017 09:35:00
Please help me to find what went wrong in the code?
Thank you
You should be doing DbFunctions.DiffDays(DateTime.Now,x.MyDate) since it's supposed to work like subtracting the first parameter from the second one, so in your case, the DiffDays is returning a negative number.
Summarizing it if you have DbFunctions.DiffDays(date1,date2)
and date1 > date2 the result will be < 0
and date1 < date2 the result will be > 0
Simplest approach is to check year, month, and day with the current date. Month and year should be the same. Day has to be the same for today and one day less for tomorrow:
var v = db_TS.TS_Test.Where(x => x.MyDate.Year == DateTime.Now.Year &&
x.MyDate.Month == DateTime.Now.Month &&
x.MyDate.Day >= DateTime.Now.Day - 1);
Edit
As pointed out this simplistic scenario won't work for edge cases.
Better option is to subtract a date from today and check what's the result in days. If it's tomorrow then the difference is 1 day or 0 if it's today.
var v = db_TS.TS_Test.Where(x => DateTime.Now.Subtract(x.MyDate).Days <= 1);
please check here from more info of datediff function.
Syntax
-- DATEDIFF ( datepart , startdate , enddate )
-- Example usage
SELECT DATEDIFF(DAY, GETDATE(), GETDATE() + 1) AS DayDiff
SELECT DATEDIFF(MINUTE, GETDATE(), GETDATE() + 1) AS MinuteDiff
SELECT DATEDIFF(SECOND, GETDATE(), GETDATE() + 1) AS SecondDiff
SELECT DATEDIFF(WEEK, GETDATE(), GETDATE() + 1) AS WeekDiff
SELECT DATEDIFF(HOUR, GETDATE(), GETDATE() + 1) AS HourDiff
You can play with here

Any reason why a sql converted date is converted wrongly?

I'm using the following code:
sqlcom.CommandText = "SELECT * FROM myTable"
+ " WHERE CAST(myTime AS DATE) >= CAST(#mySTime AS DATE)"
+ " AND CAST(myTime AS DATE) <= CAST(#myETime AS DATE)"
+ "order by myTime ";
sqlcom.Parameters.AddWithValue("#mySTime", stime);
sqlcom.Parameters.AddWithValue("#myETime", etime);
stime and etime are both DateTime columns. The following is an abbreviation of the code that sets them:
sTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0).AddDays(-1);
eTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0).AddDays(-1).AddDays(1).AddMilliseconds(-1);
Which for example leads to:
sTime = '2015-10-19 00:00:00';
eTime = '2015-10-19 23:59:59';
when displayed in the debugger (stime and etime have a few other options how they could be set that is why the sql is dynamically taking them in but in this current case the above holds true).
Now when I run the above SQL I get everything even from the current day!
BUT when I change AS DATE to AS DATETIME it works as intended that I just get the LAST day and nothing from today.
Now my question is: Is there any reason why the original sql/date comparison fails? (could it be because of that it is just the millisecond -1 that it rounds it up to the next day? OR is there any other reason there?)
eTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0).AddDays(-1).AddDays(1).AddMilliseconds(-1);
Why are you using .AddDays(-1).AddDays(1). It seems useless.
.AddMilliseconds(-1)
Your data type is datetime. datetime has an accuracy of 3ms with increments of .000, 003 or .007 seconds.
Therefore, any of these 3 values minus 1 (ms) is always rounded back to the original value:
xxx.000 - .001 = .999 => rounded to .000
xxx.003 - .001 = .002 => rounded to .003
xxx.007 - .001 = .006 => rounded to .007
This seems useless as well.
Round up
'2015-10-19 23:59:59' won't be rounded but '2015-10-19 23:59:59.999' will be round up to '2015-10-20 00:00:00.000' because 999 is surrounded by 997 and 000. 000 is the closest value.
<= 18-10-2015 23:59:59
You will miss any time above 23:59:59.000 and below of equal to 23:59:59.997
CAST(myTime AS DATE)
This will most likely prevent the usage of index on myTime. It should not be used.
It is fine to stick to datetime although datetime2 would be a better choice. If you are looking for value on a specific day, you must look for value between DAY at 00:00:00 and the next day at 00:00:00.
You can find a lot of useful information about date comparison on most of the answer here, including my own anwser: Why does my query search datetime not match?

How to convert string to datetime in sql server

I want to count total time in hh:mm:ss format. and I have minutes in int like (465).
I made:
CONVERT(varchar, CONVERT(datetime, cast(cast(TotalMin/60 as int) as nvarchar(50)) + ' : ' + cast(TotalMin%60 as nvarchar(50))),108) AS TotalMin
but it shows below error. Not in SQL Server but when I run code in c#:
Conversion failed when converting date and/or time from character
string.
You can use this code to get the string in SQL Server. It will convert hours and minutes and add 00 for seconds as you don't have them (you're using integer value for minutes):
declare #min int = 465
select right('0' + cast(#min/60 as varchar(2)),2)
+ ':'
+ right('0' + cast((#min - (#min/60)*60) as varchar(2)),2)
+ ':00'
It will work for up to 5999 minutes (99 hours and 59 minutes).
If you need a Unicode version:
declare #min int = 465
select right(N'0' + cast(#min/60 as nvarchar(2)),2)
+ N':'
+ right(N'0' + cast((#min - (#min/60)*60) as nvarchar(2)),2)
+ N':00'
Try this:
TimeSpan t = TimeSpan.FromMinutes( TotalMin);
and see this for more
UPDATE MyTable SET MyDate = CONVERT(datetime, '2009/07/16 08:28:01', 120)
For a full discussion of CAST and CONVERT, including the different date formatting options, see the MSDN Library Link below:
http://msdn.microsoft.com/en-us/library/ms187928.aspx
This will help you
You want to multiply out to milliseconds as the fractional part is discarded.
SELECT DATEADD(ms, 121.25 * 1000, 0)
If you want it without the date portion you can use CONVERT, with style 114
SELECT CONVERT(VARCHAR, DATEADD(ms, 121.25 * 1000, 0), 114)

Sql query with the current date

I've got a simple query where I want to put the current date
var query = #"
SELECT trainid, trainnum
FROM trains
WHERE CONVERT(varchar(10), trainstartdate, 104)=" +
" " +
// so that matches the '104' format
String.Format("{0:dd.MM.YYYY}", DateTime.Now) +
" " +
"ORDER BY trainnum";
But when running I get the error message:
Cannot call methods on numeric. .Net SqlClient Data Provider
How do I specify current date the right way?
Thanks!
Using GETDATE()
Effect:
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value
Using {0:dd.MM.yyyy}
Effect: none
Using CONVERT(varchar(20), GetDate(), 104)
Effect: that works!
Thanks!
Description
I would not convert to a varchar and doing string comparrisson. The performance is much better if you compare trainstartdate using the >= and <.
You can use the T-SQL getDate() method to get the current date.
getDate() returns the current datetime with the time. 2012-02-14 14:51:08.350
DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) return only the current date. `2012-02-14 00:00:00.000
DATEADD(dd, 1, DATEDIFF(dd, 0, GETDATE())) returns only the date of tomorow. 2012-02-15 00:00:00.000
Sample
var query = #"
SELECT trainid, trainnum
FROM trains
WHERE trainstartdate >=
-- today
DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
AND trainstartdate <
-- tommorow
DATEADD(dd, 1, DATEDIFF(dd, 0, GETDATE()))
ORDER BY trainnum"
Note:
If you want to be ANSI compliant, CURRENT_TIMESTAMP does the same.
More Information
MSDN - GETDATE (Transact-SQL)
MSDN - DATEDIFF (Transact-SQL)
MSDN - DATEADD (Transact-SQL)
Stackoverflow - CURRENT_TIMESTAMP vs GetDate()
var query = #"
SELECT trainid, trainnum
FROM trains
WHERE CONVERT(varchar(10), trainstartdate, 104)=
CONVERT(varchar(20), GetDate(), 104)
ORDER BY trainnum";
GETDATE() is all you need...
I think
String.Format("{0:dd.MM.YYYY}", DateTime.Now);
is returning the date with a dot, which makes SQL consider it as a number.
Try using
String.Format("{0:MM/dd/yyyy}", DateTime.Now);
with a / instead.
Change the format pattern of YYYY to small-case letters
{0:dd.MM.yyyy}
You need to be aware that GETDATE() returns the current date and time of day, not only today's date.
If you want to return rows matching today's date, you need to extract the date part. There are a number of ways to do this - e.g. with SQL Server 2008 you can use the DATE data type, but one general way that works with earlier versions of SQL Server is the following:
CONVERT(DATETIME, convert(VARCHAR(10),getdate(),101) )
You can then use the query:
SELECT trainid, trainnum
FROM trains
WHERE trainstartdate = CONVERT(DATETIME, convert(VARCHAR(10),getdate(),101) )
which will work provided you are sure that the date/time in the trains.trainstartdate column is a date only (time of day = 0).
If trainstartdate contains the start date/time, you can get all of today's trains as follows:
SELECT trainid, trainnum
FROM trains
WHERE trainstartdate >= CONVERT(DATETIME, convert(VARCHAR(10),getdate(),101) )
AND trainstartdate < DATEADD(dd,1, CONVERT(DATETIME, convert(VARCHAR(10),getdate(),101) ))
By doing it like this rather than converting to a string, you will take advantage of any index there may be on the trainstartdate column.
Try this .. YYYY should be small letter yyyy
String.Format("{0:dd.MM.yyyy}", DateTime.Now)

Categories

Resources