I'm looking at some legacy VB6 code (years+years old, before my time) which runs a query against an SQL 2005 db. It supplies a date restriction in the WHERE clause - where the date is given as an integer value as a result of a CLng() on the Date in VB6.
e.g.
...
WHERE SomeDateField >= 40064
40064 is what VB6 converts today's date to (8th Sep) by doing a CLng() on it.
However, in T-SQL this integer actually converts to 10th Sep:
SELECT CAST(40064 AS DATETIME)
And so the results aren't as expected.
Anyone know what may cause this difference in conversion between VB and T-SQL?
I'm assured this always worked without problem, and obviously my suggestion is to pass dates in as dates in standard ISO format. But, need to try to find the reason behind this discrepancy starting to occur.
Seems that VB datetime starts on 30th Dec 1899:
?CDbl(#30/12/1899 03:00:01#)
0.125011574074074
whereas SQL datetime starts on 1st Jun 1900:
SELECT CAST(0 AS DATETIME)
1900-01-01 00:00:00.000
This gives two days difference which fits your results :).
'VB6
CDbl(#2009-09-08#)
40064
-- SQL:
SELECT CAST(40064 AS DATETIME)
2009-09-10 00:00:00.000
There is a similar issue between Excel and SQL server described in this thread - I imagine it is the same thing?
I think it has something to do with whether there was a 29 february 1900. I believe VB6 thinks there is, but for example JavaScript disagrees. I don't know however how SQL Server handles this; i believe that it also doesn't count feb. 29.
Related
I have a query that was working fine before a server migration and now is not working. I'm trying to convert all dates to a specific month and year, but I keep getting this error:
Conversion failed when converting date and/or time from character string.
Looking into the data, there are no null values in InputDate, which is a date data type column. When I run the Concat() function everything is formatted as 'YYYYMMdd', yet both CAST and CONVERT fail with the same error.
Is there an issue with my query that I'm not seeing?
SELECT RandoSTUFF,
DATEADD(day,2,CAST(CONCAT('2023','02',FORMAT(InputDate,'dd')) AS date)) AS MovedDate
FROM a_table_
I expect the issue is you have date values near the end of their months, and you're trying to compose the equivalent values for February, which is shorter.
So if you have an InputDate value of, say, 2022-12-31 and run the code in the question, it will extract the 31 and concat it with the other values, and you'll end up trying to do this:
CAST('20230231' as Date)
Of course, there is no such date.
As it is, it's not clear whether you want such an input to map to February 28 or March 3. To fix this, you'll need to rethink the problem so you only try to map to valid dates, and ensure the final result is more clearly defined. This is one of the many reasons it's almost always better to use Date/time functions instead of composing dates from strings.
I create column in this way:
ALTER TABLE cages
ADD test date;
I add date to this column
for instance
'01/07/21'
and when I use like always select:
select *
from cages
where test = '01/07/21';
I get nothing, it's weird, because in different table it's works... Can it connection with pk or fk or what is the reason of this?
edit:
I use SQL orlace developer.
edit:
thanks everyone for help, problem was that I used calendar to put date to column and it add date with time.
Why is it possible, when I have type date not dateTime?
Not everyone formats date values the same way. When looking at a date like 01/07/21, most of the people on this site will naturally read January 7, 2021*. The group that reads this as July 1, 2021 (today) is significant, but still slightly smaller. A few people come from cultures where July 21, 2001 is the natural interpretation.
To avoid this kind of ambiguity, when writing date literals for SQL you should always format them using the ISO-8601 formats, which always uses four digit years, goes in sequence from most significant term on the left to least significant term on the right, and always uses leading zeroes to fill out the full width of a term:
yyyy-MM-dd HH:mm:ss.fff
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd
yyyyMMdd (unseparated version of the format preferred on Sql Server for date-only values for historical reasons)
Anything else is wrong for SQL.
For completeness, I also want to key in on the word "literals" from the beginning of the second paragraph. We should always use parameterized queries/prepared statements when putting date values into a query from a client code language, rather than using string manipulation to substitute a literal into the SQL command. On strongly typed platforms this usually means using the DateTime type provided by the language to set the value. If you find yourself converting a datetime variable to a string for inclusion in an SQL query, you're making a mistake.
* This isn't just a blind assertion. A few years back I did a basic review of the public portion of the Stack Overflow developer survey, where I first looked up which countries/languages default to which date formats, and then grouped countries together based on their format. I wish I had saved the results :/. I forget how I treated places with mixed heritage like Canada.
Your root problem, and I'm amazed no one seems to have picked up on this, is that your column is a DATE but in your query you are comparing it to a STRING. This may or may not work, depending on your NLS_DATE_FORMAT setting. You need to compare like data types:
select *
from cages
where test = to_date('01/07/21','dd-mm-yy');
I leave it as an exercise for the student to go to the SQL Reference manual and read up on the TO_DATE function.
I also beg and plead with you to not be trying to use 2-digit years. As an industry we were supposed to have solved that problem over 20 years ago. Does the term "Y2k bug" not mean anything to you?
As it is, the date that is represented by the string '01/07/21' could be understood to be any of the following
Jan 7
Jan 21
Jul 1
Jul 21
And who knows the year? 2021? 1921? 1821? 2001? 1901? 2007? 1907?, 1807?
You might want to read this.
Using SQL Developer if you insert a date in a table using user interface it automatically generates hours and minutes like if it's a Timestamp. If the format you are using is correct you should be able to retrieve your rows using like operator instead of equal.
select * from cages where test like '01/07/21%';
The only way to retrieve your rows using equal operator is when the timestamp is set to 00:00:00
1st July 2021 is written date '2021-07-01' in Oracle SQL.
You can read more about literals in the Oracle SQL Reference.
You can also use a to_date expression like
to_date('2021-07-21','YYYY-MM-DD')
or
to_date('1-Jul-21', 'DD-Mon-YY', 'nls_date_language = English')
or indeed
to_date('1-Lug-21', 'DD-Mon-YY', 'nls_date_language = Italian')
but frankly, why would you?
Bear in mind that that the person running the query/report/procedure, or the application server in use, may not have the same territory and language settings as you, so it is dangerous to assume that the century for a 2-digit year will always be what you expect (what year is '50'?) or the language will always be English, or the week always starts on a Monday. I worked on a system once where we deployed some code that used 'DD-MON-YYYY', to offices in London and Paris. We deployed it in September, and we had a production issue in Paris in February, because Sep, Oct, Nov, Dec and Jan still worked, but French has no Feb.
I recently got access to a legacy database where all dates are stored in an unfamiliar format I'm unable to translate, my initial research makes me think this is a Julian date type, but it seems to be a bit off?
Thankfully there's one date column that has a normal counterpart to it, but there are a lot of other dates where only the six digit code exists.
Any ideas what these dates are, or more importantly, how to convert between these two formats?
EX:
LEGACY/NORMAL
152409/2018-04-13
152413/2018-04-17
152427/2018-05-01
It's an ancient MS-SQL database tied to an even more ancient COBOL program if that's relevant information.
It could be the number of Days since 1600-12-31
i.e. 1-Jan-1601 = 1 etc.
Would like to see dates from a different year to confirm
Cobol Date functions
As SaggingRuffus pointed out. Many dialects of Cobol have functions that convert dates To/From Days since 31-Dec-1600
These functions include:
INTEGER-OF-DATE converts YYYYMMDD date to Days-since 31-12-1600
INTEGER-OF-DAY converts YYYYDDD date to Days-since 31-12-1600
DATE-TO-INTEGER converts Days since 31-12-1600 to YYYMMDD
DAY-OF-INTEGER converts Days since 31-12-1600 to YYYDDD
How I came to the Answer
I noticed that:
152413 - 152409 = 4 and 2018-04-17 is 4 days after 2018-04-13
152427 - 152413 = 14 and 2018-05-01 is 14 days after 2018-04-17
It was than a matter of doing the Date calculation which gives 31-dec-1600.
I also knew there where date formats where the date was stored as the number of days from 1600/1601. A date in a different year would confirm the format
Adding to Bruce Martin's answer:
Even ancient SQL Server support DATEADD/DATEDIFF, you just have to modify the start date to 1900 instead of 1600 using the constant 109208 (the number of dates inbetween).
dateadd(day, (legacydatecol- 109208), 0) as legacy2date
datediff(day, '19000101', datecol ) + 109208 as date2legacy
Today I wrote two queries on Datepart() and get different returns below
Query #1:
Select Datepart(day,'2015-07-05')
returns '5', which I expected.
Query #2:
Select Datepart(day, 2015-07-05)
Returns '27', which is a little bit funny, and I don't understand how 27 is being returned.
The difference between these two queries is one with the date inside ' ', and the other without.
Anybody can help me out here?
2015-07-05 is just a mathematical expression which adds up to the integer 2003. (Subtracting 7 from 2015 gives 2008 then subtract 5)
2003 evaluates to '1905-06-27' when implicitly cast to datetime as casting int to datetime works the same as adding that number of days to the base date of 1 Jan 1900 (i.e. equals DATEADD(DAY, 2003,'19000101')).
So this is where the 27 comes from.
The correct way to denote date literals in SQL Server is as a string '2015-07-05' (ISO format - unambiguous for newer datetime datetypes) or '20150705' (unambiguous for legacy datatypes) or using the ODBC format { d '2015-07-05' }.
I've got a query that get's a date of a field in a program. This date has to be modified with 10 years.
The query I made is
SELECT DATEADD(yy, +10, '"+thisfield.value+"')
where '"+thisfield.value+"' is coming from the program and is filled in like 01-08-2012.
The result of the query is 2022-07-31 00:00:00.000. The problem I have is that I just need 2022-08-01 but in the format of 01-08-2022 so that I can automatically fill an other field with this result.
In SQL Server 2005 the date function doesn't work only the datetime function and I just don't need that.
I hope this is clear (first time i post something). Can anyone help me?
You can either truncate the result, or cast it to DATE
CAST(DATEADD(yy, +10, '"+thisfield.value+"') AS DATE)"
CONVERT(VARCHAR, DATEADD(yy, +10, '"+thisfield.value+"'), 101)"
CONVERT using style 101 is in the format mm/dd/yyyy, which happens to be the format you want. Keep in mind that you can format the result however you want in your application for display purposes which is better than returning strings from SQL Server.
Also note that you should look into parameterized queries and/or stored procedures.