Converting a four digit month year string to date in Snowflake - sql

So I have some data as follows:
data
1203
0323
0101
1005
1130
0226
So in every case, the first two digits are the month and the last two digits are the year. Is there anyway to easily map these to date for without making it an outright lookup table?
Here's what I am seeking
data date
1203 12/01/2003
0323 03/01/2023
0101 01/01/2001
1005 10/01/2005
1130 11/01/2030
0226 02/01/2026
In every case, I would like the day to be the first of that month.

We can try building a valid date string with a year, month, and day component and then converting to a bona fide date using the TO_DATE function:
WITH cte AS (
SELECT '1203' AS dt
)
SELECT dt, TO_DATE(CONCAT('01', dt), 'DDMMYY') AS dt_out
FROM cte;

Much the same as Tim's answer:
You want to cast from number to text (and if you data is text skip that, BUT if it's variant (from JSON or XML) you still need to cast to TEXT), then parse with TO_DATE as MM month, YY year in 2 column (format tokens). And you will get the 1st of each of those months
SELECT
column1,
to_date(column1::text, 'MMYY')
FROM VALUES
(1203),
(0323),
(0101),
(1005),
(1130),
(0226);
giving:
COLUMN1
TO_DATE(COLUMN1::TEXT, 'MMYY')
1203
2003-12-01
323
2023-03-01
101
2001-10-01
1005
2005-10-01
1130
2030-11-01
226
2026-02-01

Related

How can I handle a Julian Date rollover in SQL?

I have a list of julian dates that I need to keep in order ex. 362, 363, 364, 365, 001, 002, 003. My query starts with getting the last julian date processed and each date after that. Right now it will max my lowest date out at 365 and I can't get the records that follow it. The same set of data also has a date field with the year attached but it doesn't seem to be helpful since those records won't be gathered until the rollover is corrected. Here is my simplified query:
select JulianDate, RecordDate
from table
where JulianField > #LowestJulianDate
and RecordDate between GetDate() and DateAdd(day, 6, GetDate())
Sample date:
JulianDate
RecordDate
362
2020-12-28
363
2020-12-29
364
2020-12-30
365
2020-12-31
001
2021-01-01
002
2021-01-02
003
2021-01-03
Desired output:
JulianDate
362
363
364
365
001
002
003
So if you'll imagine we start on day 362, our #LowestJulianDate is 362, and our record date range is today and the next 6 days, completing that list of julian dates.
How can I get the dates to go in order and resolve in a rollover?
You cannot by just using the "JulianDate" which is actually the DayOfYear value. You would need to also store the year that it refers to either separately or as part of the "JulianDate" value. For example, instead of "362" you need "2021362".
well why not sorting by year column and Julian date column ?
select JulianDate, RecordDate
from table
order by yearcolumn,JulianDate
What we are doing in the case of not having a year and wanting to sort a list on the year rollover for a 7 day rolling window is looking at the left 1 of the Julian day. If it's less than 3 roll it's rolled over. We sort into 2 baskets (old year and new year), order them, then recombine them with the new year's data being the "greatest" in the list.
We look at the left 1 because in our application, the last day of data we get may be 357 and the rollover may be 003 for example.

How to retrieve data within a 6 day time period

I want to retrieve the data between a 6 day time period.
The output I want is:
Date
--------
2019-05-01
2019-05-04
2019-06-01
2019-06-06
2019-07-01
This is my query so far:
select date from data d
where CAST(d.createdate as Date) between CAST('2019-05-01' as Date)
AND DATEADD(CAST(dd,6,'2016-07-01') as Date)
Why is this not retrieving the results I want?
You have several problems with your query.
The first is with your DATEADD statement which is all mixed up. You are not nesting the casted date into the statement properly. This is the corrected version:
DATEADD(dd, 6, CAST('2016-07-01' as Date))
The second is that your select projection refers to the column date which does not exist. Instead, you probably want your createdate column.
The third is that your between clause is back to front. You are saying between 2019-05-01 and 2016-07-01 but the smaller date must come first.
In fact, your given example is incorrect. In your question, you say "want to retrieve the data between two dates only for 6 days." So, why would you start with a date in 2016 and then jump to a date in 2019 and add 6 days to the date in 2019? If you want to use the DATEADD approach, you need to use the same date in both positions.
So here is your corrected query:
select d.createdate from data d
where CAST(d.createdate as Date) between CAST('2019-05-01' as Date)
AND DATEADD(dd, 6, CAST('2019-05-01' as Date))

Convert Year+WeekOfYear+DayOfWeek to a date

I have date values identified by a year, the week number within that year and the weekday and want to convert those into simple dates.
I couldn't find a function or another simple way to combine those, so I came up with a workaround using generate_series to get all dates in a range and JOIN the extracted values of those with my data:
SELECT data.*, days.d result
FROM ( VALUES (2017, 33, 3) ) data(d_year, d_week, d_weekday)
JOIN (
SELECT
-- the potential castdate
d::date d
-- year-week-dayofweek combination for JOINing
, EXTRACT('year' FROM d) d_year, EXTRACT('week' FROM d) d_week, EXTRACT('dow' FROM d) d_weekday
FROM generate_series('2015-01-01', '2019-12-31', INTERVAL '1day') AS days(d)
) days
USING(d_year, d_week, d_weekday)
Result is:
+--------+--------+-----------+------------+
| d_year | d_week | d_weekday | result |
+--------+--------+-----------+------------+
| 2017 | 33 | 3 | 16.08.2017 |
+--------+--------+-----------+------------+
While this works, this seems like overkill for such a simple task. Moreover, if one doesn't have a fixed range, this might not even work.
Is there an easier way to this?
demo:db<>fiddle
you can use the to_date() function, which takes an date string as argument, as well as a format pattern. So if the date string may be '2017-33-3', you could take this pattern to clarify each date part:
'IYYY-IW-ID'
'ID': The tricky part is: Does your week start with Sunday oder with Monday? This question influences the solution because it would shift the week numbers in an unexpected ways if you don't think about it. Thanks to your expected output, I saw you need 'ID' (ISO week day, week starts mondays) instead of 'D' (week day, week start sundays.)
'IW': Because we are taking the ISO week day, we need the ISO week of year as well (instead of 'WW': week of year)
'IYYY': Similar to (2)
More information about date patterns (especially the ISO thing): Postgres documentation
SELECT to_date(d_year || '-' || d_week || '-' || d_weekday, 'IYYY-IW-ID')
If you used the standard week pattern: 'YYYY-WW-D', your result would be 2017-08-13 (see fiddle)
Of course, this works also without the - characters, but it might be less readable:
SELECT to_date(d_year || d_week || d_weekday, 'IYYYIWID')

Oracle - How to convert Date represented by NUMBER(6,0) format

I've got data from our third party partner and every date column is coded in this NUMBER(6,0) format:
118346
118347
118348
118351
119013
119035
119049
119051
118339
118353
119019
119028
119029
119031
None of the last 3 digits are more than 365, so I reckon 118339 must mean 2018 + 339 days, which is December 5, 2018: '2018-12-05'. I've never encountered this kind of format before, so I'm a bit helpless how to handle it. Is this some standardized format? Can I use some built-in convert function or should I just manually cut and convert it using some arithmetics?
I would like to sort my rows grouping by weeks, so maybe I shouldn't even convert it, but for some reason I feel converting to a date type would be more elegant. Which approach is the better?
EDIT: I've just checked my excel version of the data, and this format is in fact working as I've imagined. So the question stands.
This seems to be Excel's 1900-based internal representation of dates. Assuming your interpretation is right, you can convert to a normal date with a bit of manipulation:
-- CTE for sample values
with your_table (num) as (
select *
from table(sys.odcinumberlist(118339, 118346, 118347, 118348, 118351, 119013, 119035,
119049, 119051, 118339, 118353, 119019, 119028, 119029, 119031))
)
-- actual query
select num,
date '1899-12-31'
+ floor(num/1000) * interval '1' year
+ mod(num, 1000) * interval '1' day as converted
from your_table;
NUM CONVERTED
---------- ----------
118339 2018-12-05
118346 2018-12-12
118347 2018-12-13
118348 2018-12-14
118351 2018-12-17
119013 2019-01-13
119035 2019-02-04
119049 2019-02-18
119051 2019-02-20
118339 2018-12-05
118353 2018-12-19
119019 2019-01-19
119028 2019-01-28
119029 2019-01-29
119031 2019-01-31
This treats the first three digits - obtained with floor(num/1000) - as the number of years, offset from 1900. Those are multiplied by a single year interval value, to give 118 or 199 years. Then it treats the last three digits - from mod(num, 1000) - as the number of days into that year, by multiplying by a single day interval. Both are then added to the fixed date 1899-12-31. (You could use 1900-01-01 instead but then you have to subtract a day at the end...)

Change Character to time stamp in IBM informix DB

I am writing a query to convert a character to Date Time
The following query extracts my time stamps in Character format.
select
(to_char(TO_CHAR(MDY(month(current- 1 units month), 1,year(current- 1 units month)),'%Y-%m-%d')||' 13:00:00')),
(to_char(TO_CHAR((DATE(DATE(extend(TODAY, YEAR TO MONTH)) - 1 UNITS DAY)+1),'%d-%m-%Y')||' 13:00:00'))
from dual
Output:
`T 0÷
2015-08-01 13:00:00 01-09-2015 13:00:00
2015-08-01 13:00:00 01-09-2015 13:00:00
Now I am trying to convert the Character to Time stamp using DATETIME(2001-12-31 15:32:55) YEAR TO SECOND function. I am getting syntax error.
select
DATETIME(to_char(TO_CHAR(MDY(month(current- 1 units month), 1,year(current- 1 units month)),'%Y-%m-%d')||' 13:00:00')) YEAR TO SECOND ,
DATETIME(to_char(TO_CHAR((DATE(DATE(extend(TODAY, YEAR TO MONTH)) - 1 UNITS DAY)+1),'%d-%m-%Y')||' 13:00:00') ) YEAR TO SECOND
from dual
How ever the following is working fine:
select DATETIME(2001-12-31 15:32:55) YEAR TO SECOND
from dual
Thanks in Advance. Please do not suggest answers for Oracle. its damn easy in Oracle.
Try using a CAST to convert your output as a DATETIME YEAR TO SECOND:
select
(to_char(TO_CHAR(MDY(month(current- 1 units month), 1,year(current- 1 units month)),'%Y-%m-%d')||' 13:00:00'))::DATETIME YEAR TO SECOND ,
(to_char(TO_CHAR((DATE(DATE(extend(TODAY, YEAR TO MONTH)) - 1 UNITS DAY)+1),'%Y-%m-%d')||' 13:00:00'))::DATETIME YEAR TO SECOND
from dual
That seems to work OK, but I'd suggest you don't do this. Also, note that DATETIME is an ISO standard: YYYY-MM-DD HH:MM:SS.FFF (or part thereof). Your second example with the date in English format is not going to parse to a DATETIME.
Your first algorithm is not leap-day safe, and the second example is horribly over-complicated. You can determine 13:00 on the first day of the current month using the far simpler construction:
(TODAY-DAY(TODAY)+1)::DATETIME YEAR TO SECOND + INTERVAL(13) HOUR TO HOUR
This also has the benefit of avoiding casting back and forth between DATE and CHAR. The calculation of the same time the previous month can be written as:
MDY(MONTH(TODAY-DAY(TODAY)),1,YEAR(TODAY-DAY(TODAY)))::DATETIME YEAR TO SECOND + INTERVAL(13) HOUR TO HOUR
... which will do the right thing on Feb 29/March 1 in a leap year, which your algorithm won't.
The construction TODAY-DAY(TODAY) will always produce the last day of the prior month.