Convert decimal year to date - sql

I have dates in a table that are stored as decimal years. An example is 2003.024658 which translates to January 9, 2003.
I would like to convert the decimal years to Oracle's date format.
I've found someone who's done this in Excel: Decimal year to date formula?
=DATE(INT(B1),1,MOD(B1,1)*(DATE(INT(B1)+1,1,1)-DATE(INT(B1),1,1)))
However, I can't quite figure out how to convert the logic to Oracle PL/SQL.

If you start from the assumption that the decimal portion was calculated according to the number of days in the given year (i.e. 365 or 366 depending on whether it was a leap year), you could do something like this:
with
q1 as (select 2003.024658 d from dual)
,q2 as (select d
,mod(d,1) as decimal_portion
,to_date(to_char(d,'0000')||'0101','YYYYMMDD')
as jan01
from q1)
,q3 as (select q2.*
,add_months(jan01,12)-jan01 as days_in_year
from q2)
select d
,decimal_portion * days_in_year as days
,jan01 + (decimal_portion * days_in_year) as result
from q3;
d: 2003.024658
days: 9.00017
result: 10-JAN-2003 12:00am

Related

Convert integer years or months into days in SQL impala

I have two columns; both have integer values. One Representing years, and the other representing months.
My goal is to perform calculations in days (integer), so I have to convert both to calendar days, to achieve that, taking in consideration that we have years with both 365 and 366 days.
Example in pseudo code:
Select Convert(years_int) to days, Convert(months int) to days
from table.
Real Example:
if --> Years = 1 and Months = 12
1) Convert both to days to compare them: Years = 365 days; Months = 365 days
After conversion : (Years = Months) Returns TRUE.
The problem is when we have years = 10 (for example), we must take in account the fact that at least two of them have 366 days. The same with Months - we have 30 and 31 days. So I need to compensate that fact to get the most accurate possible value in days.
Thanks in advance
From integers to timestamp can be done in PostgreSQL. I do not have impala, but hopefully below script will help you getting this done using impala:
with
year as (select 2022 as y union select 2023),
month as (select generate_series(1,12) as m),
day as(select generate_series(1,31) as d )
select y,m,d,dt from (
select
y,m,d,
to_date(ds,'YYYYMMDD')+(((d-1)::char(2))||' day')::interval dt
from ( select
*,
y::char(4)|| right('0'||m::char(2),2) || right('0'||0::char(2),2) as ds
from year,month,day
) x
) y
where extract(year from dt)=y and extract(month from dt)=m
order by dt
;
see: DBFIDDLE
Used functions in this query and, a way, to convert them to imapala (remember I do not use that tool/language/dialect)
function
impala alternative
to_date(a,b)
This will convert the string a to a date using the format b. Using impala you can use CAST(expression AS type FORMAT pattern)
y::char(4)
Cast y to a char(4), Using imala you can use: CAST(expression AS type)
right(a,b)
Use: right()
\\
Use: concat()
generate_series(a,b)
This generates a serie of numbers from a to (an inclusing) b. A SQL altervative is to write SELECT 1 as x union SELECT 2 union SELECT 3, which generates the same series as generate_series(1,3) in PostgreSQL
extract(year from a)
Get the year from the datetime field a, see YEAR()
One special case is this one to_date(ds,'YYYYMMDD')+(((d-1)::char(2))||' day')::interval
This will convert ds (with datatype CHAR(8)) to a date, and then add (using +) a number of days (like: '4 day')
Because I included all days until 31, this will fail in Februari, April, June, September, November because those months do not have 31 days. This is corrected by the WHERE clause in the end (where extract(year from dt)=y and extract(month from dt)=m)

Calculate the years and months it has been from a prescribed date from a table column?

I need to take the purchasedate from a table and calculate the total years plus the decimal equivalent of months left over from today's date and be able to present this calculated data in yyy.mm.
i.e 4.5 would be deciphered as 4 years and 6 months from purchasedate.
If you work out the difference in months, then divide by 12 to get years, then use the remainder to get months which you can convert into a fraction e.g.
declare #PurchaseDate date = '01 Jan 2015', #Now date = current_timestamp
select convert(varchar(3),datediff(month,#PurchaseDate,#Now)/12) + '.' + substring(convert(varchar(5),convert(decimal(9,2),(datediff(month,#PurchaseDate,#Now)%12)/12.0)),3,2)
Gives
5.33
It might need some tweaking for edge cases.
Note the divide by 12.0 to force a fractional result - otherwise you just get an integer.
Just multiply by 12 and add months:
select dateadd(month, floor(#years * 12), purchasedate)

oracle 11g date function related -

How do we come to know whether the given 2 digit year code(YY) is of 19XX or 20XX? For example if we type 07-apr-10, then is 10 the year 1910 or 2010?
Generally speaking, you should be using the RR datetime format element to convert two-digit years to full years.
If the specified two-digit year is 00 to 49, then
If the last two digits of the current year are 00 to 49, then the returned year has the same first two digits as the current year.
If the last two digits of the current year are 50 to 99, then the first 2 digits of the returned year are 1 greater than the first 2 digits of the current year.
If the specified two-digit year is 50 to 99, then
If the last two digits of the current year are 00 to 49, then the first 2 digits of the returned year are 1 less than the first 2 digits of the current year.
If the last two digits of the current year are 50 to 99, then the returned year has the same first two digits as the current year.
I don't think you want YY, as it always fills the missing digits with ones from the current year. For example:
SELECT TO_CHAR(TO_DATE('98', 'YY'), 'YYYY') FROM dual; -- returns 2098, not 1998
SELECT TO_CHAR(TO_DATE('9', 'Y'), 'YYYY') FROM dual; -- returns 2019
SELECT TO_CHAR(TO_DATE('98', 'RR'), 'YYYY') FROM dual; -- returns 1998 (until we get to 2050)
EDIT: Sorry my client setting only had YY in it. After adding YYYY, I am getting below results. So looks like by default YY takes 20YY
select to_date('7/10/15','DD/MM/YY') from dual;
07-OCT-2015
select to_date('7/10/15','DD/MM/YYYY') from dual;
07-OCT-0015
To check this :
First of all you need to convert a CHAR with YY year format into a date:
TO_DATE('20-01-15','DD-MM-YY');
and after that immediately convert it back to CHAR, and see output, like below:
SELECT TO_CHAR(TO_DATE('20-01-15','DD-MM-YY'),'YYYY') FROM DUAL;

Select Date Between Just Day and Month Excluding Year

The following is the pseudo code for what I want to do:
When Date is Between 04-01 and 03-31 of the following year then output as Q1.
I know how to do this with the year but not excluding the year.
I have no idea what you mean by output "Q1". However, if you want your years to start on April 1st (which seems like a reasonable interpretation of what you are sking), the easiest way is to subtract a number of days. For most years you will deal with, you can do:
select year(dateadd(day, - (31 + 28 + 31), date) as theyear
Of course, this only works three years out of four, because of leap years. One way to fix this is with explicit logic -- but that gets messy. Another way is to add the remaining months and subtract one year:
select year(dateadd(day, (30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31), date) - 1 as theyear
It's unclear exactly what you're trying to do. Q1 usually indicates a quarter, a three-month period. A quarter running from 1 April to 31 March of the following year isn't much of a quarter :)
However, assuming you're trying to select stuff within a certain span of time starting from a particular date, you might try a little date/time arithmetic. First, a few notes:
datetime values have a nominal precision of 1 millisecond (and an actual precision of approximately 3ms). That means that something like '31 March 2014 23:59:59.999' is rounded up to '1 April 2014 00:00:00.000'. The largest time value for a given day is `23:59:59.997'. This can have...deleterious effects on your queries if you're not cognizant of it. Don't ask me how I know this.
datetime literals without a time component, such as '1 April 2013', are interpreted as start-of-day ('1 April 2014 00:00:00.000').
So, something like this:
declare
#dtFrom datetime ,
#dtThru datetime
set #dtFrom = '1 April 2013'
set #dtThru = dateAdd(year,1,dtFrom)
select *
from foo t
where t.someDateTimeValue >= #dtFrom
and t.someDateTimevalue < #dtThru
should probably do you.
You might want to adjust the setting of #dtThru to suit your requirements: if you're actually looking for the end of a quarter, you migh change it to something like
set #dtThru = dateAdd(month,3,dtFrom)
If you have a fiscal year that runs from 1 April through 31 March and want to figure out, say, what fiscal year and quarter your data represents, you might do something like this:
select FiscalYear = datepart(year,t.someDateTimeValue)
- case datepart(month,t.someDateTimeValue) / 4
when 0 then 1 -- jan/feb/mar is quarter 4 of the prev FY
else 0 -- everything else is this FY
end ,
FiscalQuarter = case datepart(month,t.someDateTimevalue) / 4
when 0 then 4 -- jan/feb/mar is Q4 of the prev FY
when 1 then 1 -- apr/may/jun is Q1 of the curr FY
when 2 then 2 -- jul/aug/sep is Q2 of the curr FY
when 3 then 3 -- oct/nov/dec is Q3 of the curr FY
end ,
*
from foo t
I think what you want is the following:
SELECT year(dateadd(q, -1, mydate)) AS yearEndingQ1
FROM mytable
This would give the year as 2014 for all dates between 04/01/2014 and 03/31/2015. Of course it's possible you want a result of 2015 instead in which case you want:
SELECT year(dateadd(q, 3, mydate)) AS yearEndingQ1
FROM mytable
Hope this helps.
UPDATE per OP's comment: "I am tracking data for a year ending Quarter x. Our fiscal year is a bit weird around here. So basically it would be fiscal year ending Q1, fiscal year ending Q2, etc. Perhaps I could have provided more clarity in my question."
This would give results in three separate columns for fiscal year ending Q1, fiscal year ending Q2, and fiscal year ending Q3. (I assume you don't need anything for fiscal year ending Q4!!)
SELECT year(dateadd(q, -1, mydate)) AS yearEndingQ1
, year(dateadd(q, -2, mydate)) AS yearEndingQ2
, year(dateadd(q, -3, mydate)) AS yearEndingQ3
FROM mytable

What is happening in this query?

I am trying to get the last of month, and in order to that i have written the following, to calculate the no. of days between today and the last date.
select datediff(DAY,GETDATE(),dateadd(m,1,getdate()))-GETDATE()
the bold part gives me the no. of days between today and a month from today, say 30 or 31. and then I am subtracting today's date from 30 or 31, which is " -getdate() "
The output for the above query is
1786-06-06 11:44:30.540
Could you please explain what is happening in the query? I am not looking for a solution, I would like to know how is SQL-Server interpreting the query.
Thanks. :)
The bold part of the expressions does not return a date, it returns a number of days:
31
Convert that to a datetime:
SELECT CONVERT(DATETIME, 31);
This is 31 days after day 0 (1900-01-01):
1900-02-01
Now, subtract GETDATE() as an integer (41512 days after day 0):
SELECT 31 - 41512 = -41481
Now add -41481 days to day 0:
SELECT DATEADD(DAY, -41481, 0);
-- or
SELECT DATEADD(DAY, -41481, '19000101');
Or:
SELECT CONVERT(DATETIME, 31 - CONVERT(INT, GETDATE()));
Now, I strongly recommend a couple of things:
Don't use implicit date math. #date_var_or_col - 1 for example fails with new data types like DATE and DATETIME2.
Don't use shorthand like m. If you mean MONTH, just take the massive productivity hit and type out MONTH. To see why, tell me if this provides the results you expect:
SELECT DATEPART(y, GETDATE()), DATEPART(w, GETDATE());
I am subtracting today's date from 30 or 31, which is " -getdate() "
Sounds like you understand exactly what is happening, but maybe don't understand the results.
You are implicitly converting GETDATE() to a number, which represents the number of days (and fractional days) since 1/1/1900 12:00:00 AM
When you "subtract" GETDATE() (41,511 as of 8/27/2013) from 30 or 31 you get an answer of -41,480, or 41,480 days before 1/1/1900, which would be about 6/6/1786 (plus or minus a few hours for the fractional part).