How to convert Persian date into Gregorian date in SQL Server - c#

I have some Persian dates in my sql server table with the following format:
1394/05/14
I have to use stored procedure to convert it into Gregorian date because I need to compare it with today's date.
Does anyone know the solution? I have found some codes but they have problems in leap year and such things.
BTW I have the following code in C# but I think I have to use sql server proc because this proc should be executed with a fixed schedule.
public static DateTime ConvertToGregorianDate(string persianDate)
{
PersianCalendar pcalendar = new PersianCalendar();
int Year = int.Parse(persianDate.Split('/')[0]);
int Month = int.Parse(persianDate.Split('/')[1]);
int Day = int.Parse(persianDate.Split('/')[2]);
return new DateTime(Year, Month, Day, pcalendar);
}
Thanx in advance.

There is a project at GitHub that exactly does what you want! You just need to install its functions in your database by following the provided instructions. Then you can convert dates like below.
The main benefit of this library is that you are totally free to shape the returned result based on your needs. In fact, there is no fixed returned format.
select dbo.JalaliToGregorian('1395/06/11','/')
--returns 2016-09-01 00:00:00.000
select dbo.GregorianToJalali(GETDATE(),'yyyy MMMM dddd')
-- returns 1395 پنج شنبه مهر
select dbo.GregorianToJalali(GETDATE(),'yyyy/MM/dd HH:mm')
-- returns 1395/07/01 15:04
In the above examples suppose that GETDATE() Method in Sql server returns 2016/09/22 15:04:33!

A few different approaches
1) Use SQL CLR to run C# code from within SQL Server
2) Find or write a correct implementation of Persian<->Gregorian conversion in T-SQL
3) Run your C# code for all the dates you care about and dump the output to a file. Import that file into a table. When you need to convert, just look up the answer.
Option (3) is probably going to be the easiest, most maintainable, and best-performing solution. The nice thing about dates is that there really aren't that many of them. A calendar table for a hundred years is just kilobytes of memory, and databases are pretty good at doing lookups.

You can use bellow functions for your purpose (Iranian Calendar to Georgian Calendar) and for more information you can see here:
-- First, we need to convert Persian calendar date to Julian Calendar date
Create FUNCTION [dbo].[UDF_Persian_To_Julian](#iYear int,#iMonth int,#iDay int)
RETURNS bigint
AS
Begin
Declare #PERSIAN_EPOCH as int
Declare #epbase as bigint
Declare #epyear as bigint
Declare #mdays as bigint
Declare #Jofst as Numeric(18,2)
Declare #jdn bigint
Set #PERSIAN_EPOCH=1948321
Set #Jofst=2415020.5
If #iYear>=0
Begin
Set #epbase=#iyear-474
End
Else
Begin
Set #epbase = #iYear - 473
End
set #epyear=474 + (#epbase%2820)
If #iMonth<=7
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 31)
End
Else
Begin
Set #mdays=(Convert(bigint,(#iMonth) - 1) * 30+6)
End
Set #jdn =Convert(int,#iday) + #mdays+ Cast(((#epyear * 682) - 110) / 2816 as int) + (#epyear - 1) * 365 + Cast(#epbase / 2820 as int) * 1029983 + (#PERSIAN_EPOCH - 1)
RETURN #jdn
End
Go
--Secondly, convert Julian calendar date to Gregorian to achieve the target.
Create FUNCTION [dbo].[UDF_Julian_To_Gregorian] (#jdn bigint)
Returns nvarchar(11)
as
Begin
Declare #Jofst as Numeric(18,2)
Set #Jofst=2415020.5
Return Convert(nvarchar(11),Convert(datetime,(#jdn- #Jofst),113),110)
End
Go
-- Here is the example
Select dbo.[UDF_Julian_To_Gregorian](dbo.[UDF_Persian_To_Julian](1391,1,30))
--Result is 04-18-2012
Another simple conversion is here:
SELECT FORMAT(sysdatetimeoffset() at time zone 'Iran Standard Time' , 'yyyy/MM/dd-HH:mm:ss', 'fa')

I hope you found this useful.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: MohammadSoori
-- Create date: 2021-06-21
-- Description: Convert Persian date to Miladi date.
-- =============================================
-- SELECT [dbo].[PersianToMiladi] ('1400/01/01')
-- =============================================
CREATE FUNCTION [dbo].[PersianToMiladi]
(
#PersianDate VARCHAR(10)
)
RETURNS DATE
AS
BEGIN
SET #PersianDate = RIGHT (#PersianDate, 9)
DECLARE #Year INT = SUBSTRING(#PersianDate, 1, 3)
DECLARE #Month INT = SUBSTRING(#PersianDate, 5, 2)
DECLARE #Day INT = SUBSTRING(#PersianDate, 8, 2)
DECLARE #DiffYear INT = #Year - 350
DECLARE #Days INT = #DiffYear * 365.24 +
CASE WHEN #Month < 7 THEN (#Month - 1) * 31
ELSE 186 + (#Month - 7) * 30 END + #Day
DECLARE #StartDate DATETIME = '03/21/1971'
DECLARE #ResultDate DATE = #StartDate + #Days
RETURN CONVERT(DATE, #ResultDate)
END

Related

Timeformat bigger then 24 hours [duplicate]

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.

T-SQL SQL Server: Compare todays with future date in IF condition

I need to compare todays date with a future date using IF conditional in T-SQL SQL Server. It is contained in a store procedure which is called from C#. Also I want to return no rows if current date is not less or equal than future date.
So I have done:
DECLARE #FutureDate VARCHAR = '2017-05-20 00:00:00'
IF CAST(GETDATE() AS DATE) <= CAST(#FutureDate AS DATE)
BEGIN
....
END
ELSE
BEGIN
SELECT TOP 0 NULL
END
I call this store procedure from C#, But it is generating an error saying cannot convert into datetime. Time is not important for me, only date is important.
UPDATE:
Doing this is working and no need to convert FutureDate as DATE.
DECLARE #FutureDate DATE = '2017-05-20'
IF CAST(GETDATE() AS DATE) <= #FutureDate
BEGIN
....
Doing this is working and no need to convert FutureDate as DATE.
DECLARE #FutureDate DATE = '2017-05-20'
IF CAST(GETDATE() AS DATE) <= #FutureDate
BEGIN
....
END
ELSE
....
END

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)

In SQL I would like to subtract a date from a previous row date

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.

Categories

Resources