I am trying to store a .Net TimeSpan in SQL server 2008 R2.
EF Code First seems to be suggesting it should be stored as a Time(7) in SQL.
However TimeSpan in .Net can handle longer periods than 24 hours.
What is the best way to handle storing .Net TimeSpan in SQL server?
I'd store it in the database as a BIGINT and I'd store the number of ticks (eg. TimeSpan.Ticks property).
That way, if I wanted to get a TimeSpan object when I retrieve it, I could just do TimeSpan.FromTicks(value) which would be easy.
Thanks for the advice. As there is no equivalent in SQL server. I simply created a 2nd field which converted the TimeSpan to ticks and stored that in the DB. I then prevented storing the TimeSpan
public Int64 ValidityPeriodTicks { get; set; }
[NotMapped]
public TimeSpan ValidityPeriod
{
get { return TimeSpan.FromTicks(ValidityPeriodTicks); }
set { ValidityPeriodTicks = value.Ticks; }
}
If you don't have to store more than 24 hours you can just store time, since SQL Server 2008 and later the mapping is
time (SQL Server) <-> TimeSpan(.NET)
No conversions needed if you only need to store 24 hours or less.
Source: http://msdn.microsoft.com/en-us/library/cc716729(v=vs.110).aspx
But, if you want to store more than 24h, you are going to need to store it in ticks, retrieve the data and then convert to TimeSpan. For example
int timeData = yourContext.yourTable.FirstOrDefault();
TimeSpan ts = TimeSpan.FromMilliseconds(timeData);
There isn't a direct equivalent. Just store it numerically, e.g. number of seconds or something appropriate to your required accuracy.
There are multiple ways how to present a timespan in the database.
time
This datatype is supported since SQL Server 2008 and is the prefered way to store a TimeSpan. There is no mapping needed. It also works well with SQL code.
public TimeSpan ValidityPeriod { get; set; }
However, as stated in the original question, this datatype is limited to 24 hours.
datetimeoffset
The datetimeoffset datatype maps directly to System.DateTimeOffset. It's used to express the offset between a datetime/datetime2 to UTC, but you can also use it for TimeSpan.
However, since the datatype suggests a very specific semantic, so you should also consider other options.
datetime / datetime2
One approach might be to use the datetime or datetime2 types. This is best in scenarios where you need to process the values in the database directly, ie. for views, stored procedures, or reports. The drawback is that you need to substract the value DateTime(1900,01,01,00,00,00) from the date to get back the timespan in your business logic.
public DateTime ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return ValidityPeriod - DateTime(1900,01,01,00,00,00); }
set { ValidityPeriod = DateTime(1900,01,01,00,00,00) + value; }
}
bigint
Another approach might be to convert the TimeSpan into ticks and use the bigint datatype. However, this approach has the drawback that it's cumbersome to use in SQL queries.
public long ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return TimeSpan.FromTicks(ValidityPeriod); }
set { ValidityPeriod = value.Ticks; }
}
varchar(N)
This is best for cases where the value should be readable by humans. You might also use this format in SQL queries by utilizing the CONVERT(datetime, ValidityPeriod) function. Dependent on the required precision, you will need between 8 and 25 characters.
public string ValidityPeriod { get; set; }
[NotMapped]
public TimeSpan ValidityPeriodTimeSpan
{
get { return TimeSpan.Parse(ValidityPeriod); }
set { ValidityPeriod = value.ToString("HH:mm:ss"); }
}
Bonus: Period and Duration
Using a string, you can also store NodaTime datatypes, especially Duration and Period. The first is basically the same as a TimeSpan, while the later respects that some days and months are longer or shorter than others (ie. January has 31 days and February has 28 or 29; some days are longer or shorter because of daylight saving time). In such cases, using a TimeSpan is the wrong choice.
You can use this code to convert Periods:
using NodaTime;
using NodaTime.Serialization.JsonNet;
internal static class PeriodExtensions
{
public static Period ToPeriod(this string input)
{
var js = JsonSerializer.Create(new JsonSerializerSettings());
js.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
var quoted = string.Concat(#"""", input, #"""");
return js.Deserialize<Period>(new JsonTextReader(new StringReader(quoted)));
}
}
And then use it like
public string ValidityPeriod { get; set; }
[NotMapped]
public Period ValidityPeriodPeriod
{
get => ValidityPeriod.ToPeriod();
set => ValidityPeriod = value.ToString();
}
I really like NodaTime and it often saves me from tricky bugs and lots of headache. The drawback here is that you really can't use it in SQL queries and need to do calculations in-memory.
CLR User-Defined Type
You also have the option to use a custom datatype and support a custom TimeSpan class directly. See CLR User-Defined Types for details.
The drawback here is that the datatype might not behave well with SQL Reports. Also, some versions of SQL Server (Azure, Linux, Data Warehouse) are not supported.
Value Conversions
Starting with EntityFramework Core 2.1, you have the option to use Value Conversions.
However, when using this, EF will not be able to convert many queries into SQL, causing queries to run in-memory; potentially transfering lots and lots of data to your application.
So at least for now, it might be better not to use it, and just map the query result with Automapper.
I know this is an old question, but I wanted to make sure a couple of other options are noted.
Since you can't store a TimeSpan greater than 24 hours in a time sql datatype field; a couple of other options might be.
Use a varchar(xx) to store the ToString of the TimeSpan. The benefit of this is the precision doesn't have to be baked into the datatype or the calculation, (seconds vs milliseconds vs days vs fortnights) All you need to to is use TimeSpan.Parse/TryParse. This is what I would do.
Use a second date, datetime or datetimeoffset, that stores the result of first date + timespan. Reading from the db is a matter of TimeSpan x = SecondDate - FirstDate. Using this option will protect you for other non .NET data access libraries access the same data but not understanding TimeSpans; in case you have such an environment.
Now, with EF Core you can convert data type transparently in your AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// i.e. Store TimeSpan as string (custom)
modelBuilder
.Entity<YourClass>()
.Property(x => x.YourTimeSpan)
.HasConversion(
timeSpan => timeSpan.ToString(), // To DB
timeSpanString => TimeSpan.Parse(timeSpanString) // From DB
);
// i.e. Store TimeSpan as string (using TimeSpanToStringConverter)
modelBuilder
.Entity<YourClass>()
.Property(x => x.YourTimeSpan)
.HasConversion(new TimeSpanToStringConverter());
// i.e. Store TimeSpan as number of ticks (custom)
modelBuilder
.Entity<YourClass>()
.Property(x => x.YourTimeSpan)
.HasConversion(
timeSpan => timeSpan.Ticks, // To DB
timeSpanString => TimeSpan.FromTicks(timeSpanString) // From DB
);
// i.e. Store TimeSpan as number of ticks (using TimeSpanToTicksConverter)
modelBuilder
.Entity<YourClass>()
.Property(x => x.YourTimeSpan)
.HasConversion(new TimeSpanToTicksConverter());
}
To be consistent with what is probably the most likely source of generating a time span (computing the difference of 2 times or date-times), you may want to store a .NET TimeSpan as a SQL Server DateTime Type.
This is because in SQL Server, the difference of 2 DateTime's (Cast to Float's and then Cast back to a DateTime) is simply a DateTime relative to Jan. 1, 1900. Ex. A difference of +0.1 second would be January 1, 1900 00:00:00.100 and -0.1 second would be Dec. 31, 1899 23:59:59.900.
To convert a .NET TimeSpan to a SQL Server DateTime Type, you would first convert it to a .NET DateTime Type by adding it to a DateTime of Jan. 1, 1900. Of course, when you read it into .NET from SQL Server, you would first read it into a .NET DateTime and then subtract Jan. 1, 1900 from it to convert it to a .NET TimeSpan.
For use cases where the time spans are being generated from SQL Server DateTime's and within SQL Server (i.e. via T-SQL) and SQL Server is prior to 2016, depending on your range and precision needs, it may not be practical to store them as milliseconds (not to mention Ticks) because the Int Type returned by DateDiff (vs. the BigInt from SS 2016+'s DateDiff_Big) overflows after ~24 days worth of milliseconds and ~67 yrs. of seconds. Whereas, this solution will handle time spans with precision down to 0.1 seconds and from -147 to +8,099 yrs..
WARNINGS:
This would only work if the difference relative to Jan. 1, 1900 would result in a value within the range of a SQL Server DateTime Type (Jan. 1, 1753 to Dec. 31, 9999 aka -147 to +8,099 yrs.). We don't have to worry near as much on the .NET TimeSpan side, since it can hold ~29 k to +29 k yrs. I didn't mention the SQL Server DateTime2 Type (whose range, on the negative side, is much greater than SQL Server DateTime's), because: a) it cannot be converted to a numeric via a simple Cast and b) DateTime's range should suffice for the vast majority of use cases.
SQL Server DateTime differences computed via the Cast - to - Float - and - back method does not appear to be accurate beyond 0.1 seconds.
Typically, I store a TimeSpan as a bigint populated with ticks from the TimeSpan.Ticks property as previously suggested. You can also store a TimeSpan as a varchar(26) populated with the output of TimeSpan.ToString(). The four scalar functions (ConvertFromTimeSpanString, ConvertToTimeSpanString, DateAddTicks, DateDiffTicks) that I wrote are helpful for handling TimeSpan on the SQL side and avoid the hacks that would produce artificially bounded ranges. If you can store the interval in a .NET TimeSpan at all it should work with these functions also. Additionally, the functions allow you to work with TimeSpans and 100-nanosecond ticks even when using technologies that don't include the .NET Framework.
DROP FUNCTION [dbo].[DateDiffTicks]
GO
DROP FUNCTION [dbo].[DateAddTicks]
GO
DROP FUNCTION [dbo].[ConvertToTimeSpanString]
GO
DROP FUNCTION [dbo].[ConvertFromTimeSpanString]
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
-- =============================================
-- Author: James Coe
-- Create date: 2011-05-23
-- Description: Converts from a varchar(26) TimeSpan string to a bigint containing the number of 100 nanosecond ticks.
-- =============================================
/*
[-][d.]hh:mm:ss[.fffffff]
"-"
A minus sign, which indicates a negative time interval. No sign is included for a positive time span.
"d"
The number of days in the time interval. This element is omitted if the time interval is less than one day.
"hh"
The number of hours in the time interval, ranging from 0 to 23.
"mm"
The number of minutes in the time interval, ranging from 0 to 59.
"ss"
The number of seconds in the time interval, ranging from 0 to 59.
"fffffff"
Fractional seconds in the time interval. This element is omitted if the time interval does not include
fractional seconds. If present, fractional seconds are always expressed using seven decimal digits.
*/
CREATE FUNCTION [dbo].[ConvertFromTimeSpanString] (#timeSpan varchar(26))
RETURNS bigint
AS
BEGIN
DECLARE #hourStart int
DECLARE #minuteStart int
DECLARE #secondStart int
DECLARE #ticks bigint
DECLARE #hours bigint
DECLARE #minutes bigint
DECLARE #seconds DECIMAL(9, 7)
SET #hourStart = CHARINDEX('.', #timeSpan) + 1
SET #minuteStart = CHARINDEX(':', #timeSpan) + 1
SET #secondStart = CHARINDEX(':', #timespan, #minuteStart) + 1
SET #ticks = 0
IF (#hourStart > 1 AND #hourStart < #minuteStart)
BEGIN
SET #ticks = CONVERT(bigint, LEFT(#timespan, #hourstart - 2)) * 864000000000
END
ELSE
BEGIN
SET #hourStart = 1
END
SET #hours = CONVERT(bigint, SUBSTRING(#timespan, #hourStart, #minuteStart - #hourStart - 1))
SET #minutes = CONVERT(bigint, SUBSTRING(#timespan, #minuteStart, #secondStart - #minuteStart - 1))
SET #seconds = CONVERT(DECIMAL(9, 7), SUBSTRING(#timespan, #secondStart, LEN(#timeSpan) - #secondStart + 1))
IF (#ticks < 0)
BEGIN
SET #ticks = #ticks - #hours * 36000000000
END
ELSE
BEGIN
SET #ticks = #ticks + #hours * 36000000000
END
IF (#ticks < 0)
BEGIN
SET #ticks = #ticks - #minutes * 600000000
END
ELSE
BEGIN
SET #ticks = #ticks + #minutes * 600000000
END
IF (#ticks < 0)
BEGIN
SET #ticks = #ticks - #seconds * 10000000.0
END
ELSE
BEGIN
SET #ticks = #ticks + #seconds * 10000000.0
END
RETURN #ticks
END
GO
-- =============================================
-- Author: James Coe
-- Create date: 2011-05-23
-- Description: Converts from a bigint containing the number of 100 nanosecond ticks to a varchar(26) TimeSpan string.
-- =============================================
/*
[-][d.]hh:mm:ss[.fffffff]
"-"
A minus sign, which indicates a negative time interval. No sign is included for a positive time span.
"d"
The number of days in the time interval. This element is omitted if the time interval is less than one day.
"hh"
The number of hours in the time interval, ranging from 0 to 23.
"mm"
The number of minutes in the time interval, ranging from 0 to 59.
"ss"
The number of seconds in the time interval, ranging from 0 to 59.
"fffffff"
Fractional seconds in the time interval. This element is omitted if the time interval does not include
fractional seconds. If present, fractional seconds are always expressed using seven decimal digits.
*/
CREATE FUNCTION [dbo].[ConvertToTimeSpanString] (#ticks bigint)
RETURNS varchar(26)
AS
BEGIN
DECLARE #timeSpanString varchar(26)
IF (#ticks < 0)
BEGIN
SET #timeSpanString = '-'
END
ELSE
BEGIN
SET #timeSpanString = ''
END
-- Days
DECLARE #days bigint
SET #days = FLOOR(ABS(#ticks / 864000000000.0))
IF (#days > 0)
BEGIN
SET #timeSpanString = #timeSpanString + CONVERT(varchar(26), #days) + '.'
END
SET #ticks = ABS(#ticks % 864000000000)
-- Hours
SET #timeSpanString = #timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(#ticks / 36000000000.0)), 2) + ':'
SET #ticks = #ticks % 36000000000
-- Minutes
SET #timeSpanString = #timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(#ticks / 600000000.0)), 2) + ':'
SET #ticks = #ticks % 600000000
-- Seconds
SET #timeSpanString = #timeSpanString + RIGHT('0' + CONVERT(varchar(26), FLOOR(#ticks / 10000000.0)), 2)
SET #ticks = #ticks % 10000000
-- Fractional Seconds
IF (#ticks > 0)
BEGIN
SET #timeSpanString = #timeSpanString + '.' + LEFT(CONVERT(varchar(26), #ticks) + '0000000', 7)
END
RETURN #timeSpanString
END
GO
-- =============================================
-- Author: James Coe
-- Create date: 2011-05-23
-- Description: Adds the specified number of 100 nanosecond ticks to a date.
-- =============================================
CREATE FUNCTION [dbo].[DateAddTicks] (
#ticks bigint
, #starting_date datetimeoffset
)
RETURNS datetimeoffset
AS
BEGIN
DECLARE #dateTimeResult datetimeoffset
IF (#ticks < 0)
BEGIN
-- Hours
SET #dateTimeResult = DATEADD(HOUR, CEILING(#ticks / 36000000000.0), #starting_date)
SET #ticks = #ticks % 36000000000
-- Seconds
SET #dateTimeResult = DATEADD(SECOND, CEILING(#ticks / 10000000.0), #dateTimeResult)
SET #ticks = #ticks % 10000000
-- Nanoseconds
SET #dateTimeResult = DATEADD(NANOSECOND, #ticks * 100, #dateTimeResult)
END
ELSE
BEGIN
-- Hours
SET #dateTimeResult = DATEADD(HOUR, FLOOR(#ticks / 36000000000.0), #starting_date)
SET #ticks = #ticks % 36000000000
-- Seconds
SET #dateTimeResult = DATEADD(SECOND, FLOOR(#ticks / 10000000.0), #dateTimeResult)
SET #ticks = #ticks % 10000000
-- Nanoseconds
SET #dateTimeResult = DATEADD(NANOSECOND, #ticks * 100, #dateTimeResult)
END
RETURN #dateTimeResult
END
GO
-- =============================================
-- Author: James Coe
-- Create date: 2011-05-23
-- Description: Gets the difference between two dates in 100 nanosecond ticks.
-- =============================================
CREATE FUNCTION [dbo].[DateDiffTicks] (
#starting_date datetimeoffset
, #ending_date datetimeoffset
)
RETURNS bigint
AS
BEGIN
DECLARE #ticks bigint
DECLARE #days bigint
DECLARE #hours bigint
DECLARE #minutes bigint
DECLARE #seconds bigint
SET #hours = DATEDIFF(HOUR, #starting_date, #ending_date)
SET #starting_date = DATEADD(HOUR, #hours, #starting_date)
SET #ticks = #hours * 36000000000
SET #seconds = DATEDIFF(SECOND, #starting_date, #ending_date)
SET #starting_date = DATEADD(SECOND, #seconds, #starting_date)
SET #ticks = #ticks + #seconds * 10000000
SET #ticks = #ticks + CONVERT(bigint, DATEDIFF(NANOSECOND, #starting_date, #ending_date)) / 100
RETURN #ticks
END
GO
--- BEGIN Test Harness ---
SET NOCOUNT ON
DECLARE #dateTimeOffsetMinValue datetimeoffset
DECLARE #dateTimeOffsetMaxValue datetimeoffset
DECLARE #timeSpanMinValueString varchar(26)
DECLARE #timeSpanZeroString varchar(26)
DECLARE #timeSpanMaxValueString varchar(26)
DECLARE #timeSpanMinValueTicks bigint
DECLARE #timeSpanZeroTicks bigint
DECLARE #timeSpanMaxValueTicks bigint
DECLARE #dateTimeOffsetMinMaxDiffTicks bigint
DECLARE #dateTimeOffsetMaxMinDiffTicks bigint
SET #dateTimeOffsetMinValue = '0001-01-01T00:00:00.0000000+00:00'
SET #dateTimeOffsetMaxValue = '9999-12-31T23:59:59.9999999+00:00'
SET #timeSpanMinValueString = '-10675199.02:48:05.4775808'
SET #timeSpanZeroString = '00:00:00'
SET #timeSpanMaxValueString = '10675199.02:48:05.4775807'
SET #timeSpanMinValueTicks = -9223372036854775808
SET #timeSpanZeroTicks = 0
SET #timeSpanMaxValueTicks = 9223372036854775807
SET #dateTimeOffsetMinMaxDiffTicks = 3155378975999999999
SET #dateTimeOffsetMaxMinDiffTicks = -3155378975999999999
-- TimeSpan Conversion Tests
PRINT 'Testing TimeSpan conversions...'
DECLARE #convertToTimeSpanStringMinTicksResult varchar(26)
DECLARE #convertFromTimeSpanStringMinTimeSpanResult bigint
DECLARE #convertToTimeSpanStringZeroTicksResult varchar(26)
DECLARE #convertFromTimeSpanStringZeroTimeSpanResult bigint
DECLARE #convertToTimeSpanStringMaxTicksResult varchar(26)
DECLARE #convertFromTimeSpanStringMaxTimeSpanResult bigint
SET #convertToTimeSpanStringMinTicksResult = dbo.ConvertToTimeSpanString(#timeSpanMinValueTicks)
SET #convertFromTimeSpanStringMinTimeSpanResult = dbo.ConvertFromTimeSpanString(#timeSpanMinValueString)
SET #convertToTimeSpanStringZeroTicksResult = dbo.ConvertToTimeSpanString(#timeSpanZeroTicks)
SET #convertFromTimeSpanStringZeroTimeSpanResult = dbo.ConvertFromTimeSpanString(#timeSpanZeroString)
SET #convertToTimeSpanStringMaxTicksResult = dbo.ConvertToTimeSpanString(#timeSpanMaxValueTicks)
SET #convertFromTimeSpanStringMaxTimeSpanResult = dbo.ConvertFromTimeSpanString(#timeSpanMaxValueString)
-- Test Results
SELECT 'Convert to TimeSpan String from Ticks (Minimum)' AS Test
, CASE
WHEN #convertToTimeSpanStringMinTicksResult = #timeSpanMinValueString
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #timeSpanMinValueTicks AS [Ticks]
, CONVERT(varchar(26), NULL) AS [TimeSpan String]
, CONVERT(varchar(26), #convertToTimeSpanStringMinTicksResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanMinValueString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Minimum)' AS Test
, CASE
WHEN #convertFromTimeSpanStringMinTimeSpanResult = #timeSpanMinValueTicks
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, NULL AS [Ticks]
, #timeSpanMinValueString AS [TimeSpan String]
, CONVERT(varchar(26), #convertFromTimeSpanStringMinTimeSpanResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanMinValueTicks) AS [Expected Result]
UNION ALL
SELECT 'Convert to TimeSpan String from Ticks (Zero)' AS Test
, CASE
WHEN #convertToTimeSpanStringZeroTicksResult = #timeSpanZeroString
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #timeSpanZeroTicks AS [Ticks]
, CONVERT(varchar(26), NULL) AS [TimeSpan String]
, CONVERT(varchar(26), #convertToTimeSpanStringZeroTicksResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanZeroString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Zero)' AS Test
, CASE
WHEN #convertFromTimeSpanStringZeroTimeSpanResult = #timeSpanZeroTicks
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, NULL AS [Ticks]
, #timeSpanZeroString AS [TimeSpan String]
, CONVERT(varchar(26), #convertFromTimeSpanStringZeroTimeSpanResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanZeroTicks) AS [Expected Result]
UNION ALL
SELECT 'Convert to TimeSpan String from Ticks (Maximum)' AS Test
, CASE
WHEN #convertToTimeSpanStringMaxTicksResult = #timeSpanMaxValueString
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #timeSpanMaxValueTicks AS [Ticks]
, CONVERT(varchar(26), NULL) AS [TimeSpan String]
, CONVERT(varchar(26), #convertToTimeSpanStringMaxTicksResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanMaxValueString) AS [Expected Result]
UNION ALL
SELECT 'Convert from TimeSpan String to Ticks (Maximum)' AS Test
, CASE
WHEN #convertFromTimeSpanStringMaxTimeSpanResult = #timeSpanMaxValueTicks
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, NULL AS [Ticks]
, #timeSpanMaxValueString AS [TimeSpan String]
, CONVERT(varchar(26), #convertFromTimeSpanStringMaxTimeSpanResult) AS [Actual Result]
, CONVERT(varchar(26), #timeSpanMaxValueTicks) AS [Expected Result]
-- Ticks Date Add Test
PRINT 'Testing DateAddTicks...'
DECLARE #DateAddTicksPositiveTicksResult datetimeoffset
DECLARE #DateAddTicksZeroTicksResult datetimeoffset
DECLARE #DateAddTicksNegativeTicksResult datetimeoffset
SET #DateAddTicksPositiveTicksResult = dbo.DateAddTicks(#dateTimeOffsetMinMaxDiffTicks, #dateTimeOffsetMinValue)
SET #DateAddTicksZeroTicksResult = dbo.DateAddTicks(#timeSpanZeroTicks, #dateTimeOffsetMinValue)
SET #DateAddTicksNegativeTicksResult = dbo.DateAddTicks(#dateTimeOffsetMaxMinDiffTicks, #dateTimeOffsetMaxValue)
-- Test Results
SELECT 'Date Add with Ticks Test (Positive)' AS Test
, CASE
WHEN #DateAddTicksPositiveTicksResult = #dateTimeOffsetMaxValue
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #dateTimeOffsetMinMaxDiffTicks AS [Ticks]
, #dateTimeOffsetMinValue AS [Starting Date]
, #DateAddTicksPositiveTicksResult AS [Actual Result]
, #dateTimeOffsetMaxValue AS [Expected Result]
UNION ALL
SELECT 'Date Add with Ticks Test (Zero)' AS Test
, CASE
WHEN #DateAddTicksZeroTicksResult = #dateTimeOffsetMinValue
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #timeSpanZeroTicks AS [Ticks]
, #dateTimeOffsetMinValue AS [Starting Date]
, #DateAddTicksZeroTicksResult AS [Actual Result]
, #dateTimeOffsetMinValue AS [Expected Result]
UNION ALL
SELECT 'Date Add with Ticks Test (Negative)' AS Test
, CASE
WHEN #DateAddTicksNegativeTicksResult = #dateTimeOffsetMinValue
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #dateTimeOffsetMaxMinDiffTicks AS [Ticks]
, #dateTimeOffsetMaxValue AS [Starting Date]
, #DateAddTicksNegativeTicksResult AS [Actual Result]
, #dateTimeOffsetMinValue AS [Expected Result]
-- Ticks Date Diff Test
PRINT 'Testing Date Diff Ticks...'
DECLARE #dateDiffTicksMinMaxResult bigint
DECLARE #dateDiffTicksMaxMinResult bigint
SET #dateDiffTicksMinMaxResult = dbo.DateDiffTicks(#dateTimeOffsetMinValue, #dateTimeOffsetMaxValue)
SET #dateDiffTicksMaxMinResult = dbo.DateDiffTicks(#dateTimeOffsetMaxValue, #dateTimeOffsetMinValue)
-- Test Results
SELECT 'Date Difference in Ticks Test (Min, Max)' AS Test
, CASE
WHEN #dateDiffTicksMinMaxResult = #dateTimeOffsetMinMaxDiffTicks
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #dateTimeOffsetMinValue AS [Starting Date]
, #dateTimeOffsetMaxValue AS [Ending Date]
, #dateDiffTicksMinMaxResult AS [Actual Result]
, #dateTimeOffsetMinMaxDiffTicks AS [Expected Result]
UNION ALL
SELECT 'Date Difference in Ticks Test (Max, Min)' AS Test
, CASE
WHEN #dateDiffTicksMaxMinResult = #dateTimeOffsetMaxMinDiffTicks
THEN 'Pass'
ELSE 'Fail'
END AS [Test Status]
, #dateTimeOffsetMaxValue AS [Starting Date]
, #dateTimeOffsetMinValue AS [Ending Date]
, #dateDiffTicksMaxMinResult AS [Actual Result]
, #dateTimeOffsetMaxMinDiffTicks AS [Expected Result]
PRINT 'Tests Complete.'
GO
--- END Test Harness ---
I would store the timespan.TotalSeconds in a float and then retrieve it using Timespan.FromSeconds(totalSeconds).
Depending on the resolution you need you could use TotalMilliseconds, TotalMinutes, TotalDays.
You could also adjust the precision of your float in the database.
It's not an exact value... but the nice thing about this is that it's easy to read and calculate in simple queries.
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)
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)
The problem is that the dates are stored in the SQL database as nvarchar() and the times are stored in a separate column. I only have read access to this database, so I can't change the format. Besides, it would probably void our support if the manufacturer found out.
As I see I need to first combine the dates and times into one cell as a datetime and then subtract the previous row from the current.
ActualTime, ActualDate
5:30:26, 31-Dec-09
16:01:47, 31-Dec-09
17:35:50, 31-Dec-09
18:31:31, 31-Dec-09
18:51:03, 31-Dec-09
18:55:35, 31-Dec-09
19:26:53, 31-Dec-09
5:25:37, 1-Jan-10
5:38:36, 1-Jan-10
5:46:58, 1-Jan-10
6:27:00, 1-Jan-10
Several people have asked what language I was using. I was hoping to do all of this at the server. On the code side (C#) it's a trivial problem.
Like I said I am looking for an SQL Server server-side solution.
In Microsoft SQL Server, to convert your columns in a date you can
Select Cast( ActualDate + ' ' + ActualTime AS DateTime)
to compare between two dates
Select
Datediff(
second,
Cast('13-dec-2009 ' + '19:39:33' As DateTime),
Cast('13-dec-2009 ' + '19:26:33' As DateTime)
)
More on DATEDIFF (Transact-SQL) parameters.
And to get the difference from the current date/time use the GETDATE(),
Select
*,
oldness = DateDiff(
second,
GETDATE(),
Cast(ActualDate + ' ' + ActualTime AS DateTime)
)
From
your_table
Finally to do it between rows (for the whole table..),
Select *,
Cast(ActualDate + ' ' + ActualTime AS DateTime) as [fulldate],
DiffFromPrevious = Coalesce(
DateDiff(
second,
(
Select Top 1 Cast(ActualDate + ' ' + ActualTime AS DateTime) AS [fulldate]
From yourtable
Where Cast(ActualDate + ' ' + ActualTime AS DateTime) < Cast(t1.ActualDate + ' ' + t1.ActualTime AS DateTime)
Order By [fulldate] Desc
),
Cast(ActualDate + ' ' + ActualTime AS DateTime)
),
0)
From
yourtable t1
Order By
[fulldate] Asc
What language are you using, and what kind of Database is it? I'm not sure if the database has capabilities to do row manipulation within a query (subtracting one row from the other), so you would have to do this programmatically. I'm not sure what language you're using, but if it has a Date or Time API then you can use that to create a Date object. There should a function that returns the total number of seconds since a starting date (January 1, 1970 or something). You create your two Date objects, convert into number of seconds and then subtract them. You can then calculate the number of days between them.
If you're using PHP, I suggest you use the strtotime() function to convert it into a time object. Do this for both dates. The difference will give you the number of seconds between them.