Compare dates db2/400 vs SQL Server - sql

I want to compare a date/time from SQL Server (type datetime-16) with db2/400 (type Z).
Typically I would like to know that that there are 60 seconds between 18:44 and 18:45
Each one has slightly different display characteristics.
SQL Server 2016-07-26 18:45:00.000
Db2/400 2016-07-26-18.45.00.000000
If I do this on my db2 database:
SELECT
MYDATETIME,
'2016-07-26 18:44:00.000',
MYDATETIME - '2016-07-26 18:44:00.000'
FROM #dates
I get this
MYDATETIME Constant value Numeric Expression
2016-07-26-18.45.00.000000 2016-07-26 18:44:00.000 100.000000
Db2 SQL seems quite generous to accept slightly different formats.
But query tells me that 1 minute difference = 100. Looks like I have a base 10 comparison happening here.
Using db2 timestamp function, I get the same result
SELECT
MYDATETIME,
'2016-07-26 18:44:00.000',
MYDATETIME - timestamp('2016-07-26-18.44.00.000000')
FROM #dates
How can I make comparisons that would give me difference in minutes (or hours or days)?

The results of subtracting two timestamps on DB2 for IBM i is known as a duration
You might find TIMESTAMPDIFF() useful
select
timestampdiff(2
, char(timestamp('2016-07-27 08:35:00.000000')
- timestamp('2016-07-27 08:34:00.000000')
)
)
from sysibm.sysdummy1
note that the first parameter is a small int with the following values
1 Microseconds
2 Seconds
4 Minutes
8 Hours
16 Days
32 Weeks
64 Months
128 Quarters
256 Years
The second parameter must be a character representation of a duration.
EDIT As pointed out by Clockwork-Muse, TIMESTAMPDIFF() is best used for short durations less than a month.
If you need accurate calculation for longer values, use the following form:
(DAYS(t1) - DAYS(t2)) * 86400 +
(MIDNIGHT_SECONDS(t1) - MIDNIGHT_SECONDS(t2))
Sample
select
(DAYS(timestamp('2016-07-27 08:35:00.000000'))
- DAYS(timestamp('2015-07-27 08:35:00.000000'))
) * 86400 +
(MIDNIGHT_SECONDS(timestamp('2016-07-27 08:35:00.000000'))
- MIDNIGHT_SECONDS(timestamp('2015-07-27 08:35:00.000000')))
from sysibm.sysdummy1

I know this is DB2/400, but in my experience the time operators work the same way, so from this article:
SELECT
MYDATETIME,
'2016-07-26 18:44:00.000',
MYDATETIME - timestamp('2016-07-26-18.44.00.000000') SECONDS
FROM #dates
Since I don't have a DB2 instance to test this on, the next thing I would try would be this:
SELECT
MYDATETIME,
'2016-07-26 18:44:00.000',
SECOND(MYDATETIME - timestamp('2016-07-26-18.44.00.000000'))
FROM #dates

Related

Rounding problems with DATETIME

The following queries:
DECLARE #__dateRange_StartDate_4 DATETIME ='2021-03-01T00:00:00.000'
DECLARE #__dateRange_EndDate_3 DATETIME ='2021-03-31T23:59:59.999'
SELECT DATEDIFF(DAY, '2021-03-01T00:00:00.000', '2021-03-31T23:59:59.999') + 1
SELECT DATEDIFF(DAY, #__dateRange_StartDate_4, #__dateRange_EndDate_3) + 1
SELECT #__dateRange_EndDate_3
Produces the following results:
31
32
2021-04-01 00:00:00.000
It appears #__dateRange_EndDate_3 is being rounded to the next day, which I don't want.
What is the correct way to have the second SELECT return 31?
Note: My queries are actually being called from Entity Framework so I may be limited to what I can do here, but I at least want to understand the issue as this was unexpected.
DATETIME in SQL Server has an accuracy of 3.33ms (0.003 seconds) - therefore, the "highest" possible value for March 31, 2021 would be 2021-03-31T23:59:59.997 - anything beyond that will be rounded up to the next day.
This is just one of the reasons why as of SQL Server 2008 the general recommendation is to use DATE for when you don't need any time portion, or DATETIME2(n) (when you need the time portion; n is the number of fractional digits after the second - can be 0 through 7) datatypes.
DATETIME2(n) offers accuracy down to 100 ns and thus 2021-03-31T23:59:59.999 will be absolutely no problem in a DATETIME2(3) column.
As an added benefit, DATETIME2(n) also doesn't have this "arbitrary" lower limits of supported dates only through 01.01.1753 - with DATETIME2(n) you can store any date, back to 1 AD
This is silly. Don't bother with trying to get the last increment before a time -- and learning that datetime is only accurate to 0.003 seconds.
Express the logic only using dates:
DECLARE #__dateRange_StartDate_4 DATE ='2021-03-01'
DECLARE #__dateRange_EndDate_3 DATE ='2021-04-01'
SELECT DATEDIFF(DAY, '2021-03-01', '2021-04-01');
SELECT DATEDIFF(DAY, #__dateRange_StartDate_4, #__dateRange_EndDate_3);
SELECT #__dateRange_EndDate_3;
Then use these with inequalities:
WHERE date >= #__dateRange_StartDate_4 AND
date < #__dateRange_EndDate_3
Inequalities -- with >= and < is the recommended way to handle date/time comparisons. Dealing with the "last increment" problem is only one of the problems it solves.
If you really are committed to figuring out the last increment before midnight, you can use DATETIME2 or .997. But I don't recommend either of those approaches. Here is a db<>fiddle.

Pervasive dval stores dates as integer in days from 01/01/0001. I need a function to convert it for sql server

I need help. Pervasive DB stored dates in days. For example 719311 would be the amount of days from 01/01/0001 to May 28 1970. So May 28 1970 is the date represented in pervasive when looking at 719311.
You can use SQL Servers dateadd feature for this. However the values in Pervasive are much too large to add to the base date of 0 (1900-01-01).
To work around that I used the known integer/date provided and calculated the delta between 0001-01-01 and 1900-01-01 - which is 693597. This value is static, and can be subtracted from each Pervasive value and used in the dateadd. The result can then be added to 1900-01-01.
-- GET BASE DATE OF 0
SELECT CONVERT(DATETIME,0)
-- CALCULATE DIFF FROM 0 TO A KNOWN DATE...
SELECT DATEDIFF(DD,0,'1970-05-28')
--SUBTRACT ABOVE DIFF FROM KNOWN INTEGER FOR SAID DATE... THIS IS OUR DELTA
SELECT 719311-25714
-- ADD THE INTEGER OF SAID DATE, MINUS THE ABOVE DELTA TO 0 TO CONFIRM WE GET THE KNOWN DATE.
SELECT DATEADD(DD,719311-693597,0)
-- USING VARIABLES, LEAVE #DELTA AS A STATIC VALUE, JUST UPDATE #PERVASIVE
DECLARE #PERVASIVE INT, #DELTA INT
SET #PERVASIVE=719312
SET #DELTA=693597
SELECT DATEADD(DD,#PERVASIVE-#DELTA,0)
select datediff(day,'0001-01-01','1753-01-01') + 2
select 719311 - 639907 -- sql
select dateadd(day,79404,'1753-01-01')
dateadd() function cannot work with a date older than '1753-01-01', so you need the 719311 days minus the 639907 days. The difference you can than plug into the dateadd() function.
You should get this 1970-05-28 00:00:00.000
Another alternative is add days to a date datatype.
When I add 719311 days to '0001-01-01' I get '1970-05-30'
To get '1970-05-28' I have to remove 2 of those days.
select dateadd(day,719311-2,convert(date,'00010101'))
returns '1970-05-28'

How to filter SQL by time (greater and less than)?

I want to query a subset from a dataset. Each row has a time stamp of the following format:
2014-04-25T17:25:14
2014-04-25T18:40:16
2014-04-25T18:44:57
2014-04-25T19:10:32
2014-04-25T20:22:12
...
Currently, I use the following query to select a time-based subset:
time LIKE '%2014-04-25T18%' OR time LIKE '%2014-04-25T19%'
This becomes quite complicated when you start to filter by mintutes or seconds.
Is there a way to run a query that such as ...
time > '%2014-04-25T18%' AND time < '%2014-04-25T19%'
A regular expression would be okay, too.
The database is a SpatiaLite database. The time column is of type VARCHAR.
If the date is being treated as a string and based on the example above:
time LIKE '%2014-04-25T18%' AND time <> '%2014-04-25T18:00:00:000'
Otherwise, you could convert the date to seconds since midnight and add 60 minutes to that to create the range part of the filter
DECLARE #test DATETIME = '2014-04-25T17:25:14'
SELECT #test
, CONVERT(DATE,#test) AS JustDate
, DATEDIFF(s,CONVERT(DATETIME,(CONVERT(DATE,#test))), #test) AS SecondsSinceMidnight
-- 60 seconds * 60 minutes * 24 hours = 86400
Thanks to your posts and this answer I came up with this solution:
SELECT * FROM data
WHERE DATETIME(
substr(time,1,4)||'-'||
substr(time,6,2)||'-'||
substr(time,9,2)||' '||
substr(time,12,8)
)
BETWEEN DATETIME('2014-04-25 18:00:00') AND DATETIME('2014-04-25 19:00:00');

convert Excel Date Serial Number to Regular Date

I got a column called DateOfBirth in my csv file with Excel Date Serial Number Date
Example:
36464
37104
35412
When i formatted cells in excel these are converted as
36464 => 1/11/1999
37104 => 1/08/2001
35412 => 13/12/1996
I need to do this transformation in SSIS or in SQL. How can this be achieved?
In SQL:
select dateadd(d,36464,'1899-12-30')
-- or thanks to rcdmk
select CAST(36464 - 2 as SmallDateTime)
In SSIS, see here
http://msdn.microsoft.com/en-us/library/ms141719.aspx
The marked answer is not working fine, please change the date to "1899-12-30" instead of "1899-12-31".
select dateadd(d,36464,'1899-12-30')
You can cast it to a SQL SMALLDATETIME:
CAST(36464 - 2 as SMALLDATETIME)
MS SQL Server counts its dates from 01/01/1900 and Excel from 12/30/1899 = 2 days less.
tldr:
select cast(#Input - 2e as datetime)
Explanation:
Excel stores datetimes as a floating point number that represents elapsed time since the beginning of the 20th century, and SQL Server can readily cast between floats and datetimes in the same manner. The difference between Excel and SQL server's conversion of this number to datetimes is 2 days (as of 1900-03-01, that is). Using a literal of 2e for this difference informs SQL Server to implicitly convert other datatypes to floats for very input-friendly and simple queries:
select
cast('43861.875433912' - 2e as datetime) as ExcelToSql, -- even varchar works!
cast(cast('2020-01-31 21:00:37.490' as datetime) + 2e as float) as SqlToExcel
-- Results:
-- ExcelToSql SqlToExcel
-- 2020-01-31 21:00:37.490 43861.875433912
this actually worked for me
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-30')
(minus 1 more day in the date)
referring to the negative commented post
SSIS Solution
The DT_DATE data type is implemented using an 8-byte floating-point number. Days are represented by whole number increments, starting with 30 December 1899, and midnight as time zero. Hour values are expressed as the absolute value of the fractional part of the number. However, a floating point value cannot represent all real values; therefore, there are limits on the range of dates that can be presented in DT_DATE. Read more
From the description above you can see that you can convert these values implicitly when mapping them to a DT_DATE Column after converting it to a 8-byte floating-point number DT_R8.
Use a derived column transformation to convert this column to 8-byte floating-point number:
(DT_R8)[dateColumn]
Then map it to a DT_DATE column
Or cast it twice:
(DT_DATE)(DT_R8)[dateColumn]
You can check my full answer here:
Is there a better way to parse [Integer].[Integer] style dates in SSIS?
Found this topic helpful so much so created a quick SQL UDF for it.
CREATE FUNCTION dbo.ConvertExcelSerialDateToSQL
(
#serial INT
)
RETURNS DATETIME
AS
BEGIN
DECLARE #dt AS DATETIME
SELECT #dt =
CASE
WHEN #serial is not null THEN CAST(#serial - 2 AS DATETIME)
ELSE NULL
END
RETURN #dt
END
GO
I had to take this to the next level because my Excel dates also had times, so I had values like this:
42039.46406 --> 02/04/2015 11:08 AM
42002.37709 --> 12/29/2014 09:03 AM
42032.61869 --> 01/28/2015 02:50 PM
(also, to complicate it a little more, my numeric value with decimal was saved as an NVARCHAR)
The SQL I used to make this conversion is:
SELECT DATEADD(SECOND, (
CONVERT(FLOAT, t.ColumnName) -
FLOOR(CONVERT(FLOAT, t.ColumnName))
) * 86400,
DATEADD(DAY, CONVERT(FLOAT, t.ColumnName), '1899-12-30')
)
In postgresql, you can use the following syntax:
SELECT ((DATE('1899-12-30') + INTERVAL '1 day' * FLOOR(38242.7711805556)) + (INTERVAL '1 sec' * (38242.7711805556 - FLOOR(38242.7711805556)) * 3600 * 24)) as date
In this case, 38242.7711805556 represents 2004-09-12 18:30:30 in excel format
In addition of #Nick.McDermaid answer I would like to post this solution, which convert not only the day but also the hours, minutes and seconds:
SELECT DATEADD(s, (42948.123 - FLOOR(42948.123))*3600*24, dateadd(d, FLOOR(42948.123),'1899-12-30'))
For example
42948.123 to 2017-08-01 02:57:07.000
42818.7166666667 to 2017-03-24 17:12:00.000
You can do this if you just need to display the date in a view:
CAST will be faster than CONVERT if you have a large amount of data, also remember to subtract (2) from the excel date:
CAST(CAST(CAST([Column_With_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you need to update the column to show a date you can either update through a join (self join if necessary) or simply try the following:
You may not need to cast the excel date as INT but since the table I was working with was a varchar I had to do that manipulation first. I also did not want the "time" element so I needed to remove that element with the final cast as "date."
UPDATE [Table_with_Date]
SET [Column_With_Excel_Date] = CAST(CAST(CAST([Column_With_Excel_Date]-2 AS INT)AS smalldatetime) AS DATE)
If you are unsure of what you would like to do with this test and re-test! Make a copy of your table if you need. You can always create a view!
Google BigQuery solution
Standard SQL
Select Date, DATETIME_ADD(DATETIME(xy, xm, xd, 0, 0, 0), INTERVAL xonlyseconds SECOND) xaxsa
from (
Select Date, EXTRACT(YEAR FROM xonlydate) xy, EXTRACT(MONTH FROM xonlydate) xm, EXTRACT(DAY FROM xonlydate) xd, xonlyseconds
From (
Select Date
, DATE_ADD(DATE '1899-12-30', INTERVAL cast(FLOOR(cast(Date as FLOAT64)) as INT64) DAY ) xonlydate
, cast(FLOOR( ( cast(Date as FLOAT64) - cast(FLOOR( cast(Date as FLOAT64)) as INT64) ) * 86400 ) as INT64) xonlyseconds
FROM (Select '43168.682974537034' Date) -- 09.03.2018 16:23:28
) xx1
)
For those looking how to do this in excel (outside of formatting to a date field) you can do this by using the Text function https://exceljet.net/excel-functions/excel-text-function
i.e.
A1 = 132134
=Text(A1,"MM-DD-YYYY") will result in a date
This worked for me because sometimes the field was a numeric to get the time portion.
Command:
dateadd(mi,CONVERT(numeric(17,5),41869.166666666664)*1440,'1899-12-31')

timezone in GetDate query is 12 hours out

I am using MS webmatrix and razor.
I have a query that uses the expression CAST(GetDate() as INT) to get the current date integer value. However, even though my server and PC are both set on GMT + 12 (Wellington, Auckland), the value returned is 12 hours out - and at 12.00 pm on my PC (and the server) it jumps ahead one day.
How do I trim 12 hours off the value, without having to set the time 12 hours wrong on my machines?
Grateful for any help.
Coercing a date directly into an INT looks quite wrong.
To properly get just the INTEGRAL value of the date, use DATEDIFF directly.
select cast(cast('20120301' as datetime) as int) -- 40967
select cast(cast('20120301 12:30' as datetime) as int) -- 40968, oh noes!
select datediff(d,0,'20120301') -- 40967
select datediff(d,0,'20120301 12:30') -- 40967, yes!