I am using MySQL and having issues getting the end of the week date from a DATETIME column, where the end of the week is considered Sunday,
My table looks like this:
Unique_ID
Date
123
2020-07-13 17:03:31.035
456
2021-01-01 15:02:19.029
789
2020-08-02 18:07:14.011
I am needing to get the week for each line where the week ends on Sunday. The time isn't needed. So the end result for 2021-01-01 would show 2021-01-03 since that week ends on Sunday. Does anyone know what function to use for this?
Here's an elaboration (I hope) of Akina's suggestion in the comment:
SELECT *,
dt + INTERVAL 6 DAY add6 /*add 6 day ahead*/,
DAYNAME(dt + INTERVAL 6 DAY) dn6 /*6 day ahead dayname*/,
dt + INTERVAL (6 - wkd) DAY nxtsun /*add 6 day ahead then subtract weekday value from date column*/,
DAYNAME(dt + INTERVAL (6 - wkd) DAY) nxtsundn
FROM
(SELECT *,
DATE(date) dt,
DAYNAME(date) dn,
WEEKDAY(date) wkd
FROM mytable) A;
Let's take the second row from your data sample to illustrate what is happening. The base query above:
SELECT *,
DATE(date) dt,
DAYNAME(date) dn,
WEEKDAY(date) wkd
FROM mytable
Will return the following.
Unique_ID
Date
dn
wkd
456
2021-01-01 15:02:19
Friday
4
Note that the WEEKDAY(date) (aliased as wkd in the table) returns 4. Which means it's Friday. According to the docs, WEEKDAY() function returns like the following:
0 = Monday
1 = Tuesday
2 = Wednesday
3 = Thursday
4 = Friday
5 = Saturday
6 = Sunday
Adding 6 day interval to the current WEEKDAY() result goes to the day before next same dayname of the current date value. So WEEKDAY(2021-01-01) which is on Friday, becomes 2021-01-07 which is on Thursday after being added with 6 day ahead. With a subtraction of the pervious obtained WEEKDAY() value, the operation becomes DATE + INTERVAL (6 - 4) DAY, which effectively becomes DATE + INTERVAL 2 DAY.
Here's a fiddle
I found this excellent example from #lserni on calculating the last Friday of a month.
SET #DATE='1962-10-20';
SELECT DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY) AS friday;
+------------+
| friday |
+------------+
| 1962-10-26 |
+------------+
Edit:
Based on the date supplied, give me the next Last Friday of a month (even if the date supplied)
Based on Drew's answer I did some experimenting and found a solution that works for me.
SET #DATE='2015-09-26';
SELECT
CASE WHEN DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY) > #DATE
THEN DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY)
ELSE DATE_SUB(LAST_DAY(DATE_ADD(#DATE, INTERVAL 1 MONTH)), INTERVAL ((WEEKDAY(LAST_DAY(DATE_ADD(#DATE, INTERVAL 1 MONTH)))+7-4))%7 DAY)
END AS last_friday
So, I can do the following to get data from last week.
select * from table where week(date)=week(curdate())-1
Same for 2 weeks ago. But this fails if the data is in the prior year. What query can I use to get data from n weeks ago regardless of what year the data belongs to.
Edit: The week starts on Sunday 12AM and ends Saturday 11:59PM
When does a "week" start? Sunday? Monday? The same day of week as today?
Assuming you are happy with the last option, do this:
SELECT ...
WHERE date >= CURDATE() - INTERVAL $n WEEK
AND date < CURDATE() - INTERVAL $n-1 WEEK
Example
mysql> SELECT CURDATE(), CURDATE() - INTERVAL 9 WEEK, CURDATE() - INTERVAL 9-1 WEEK;
+------------+-----------------------------+-------------------------------+
| CURDATE() | CURDATE() - INTERVAL 9 WEEK | CURDATE() - INTERVAL 9-1 WEEK |
+------------+-----------------------------+-------------------------------+
| 2015-02-22 | 2014-12-21 | 2014-12-28 |
+------------+-----------------------------+-------------------------------+
If you need the week to start on a particular DOW, the query is messier, by further subtracting INTERVAL DAYOFWEEK(CURDATE()) DAY. And that could be off a little.
The start of the current week (assuming it is Sunday) is CURDATE() - INTERVAL (WEEKDAY(CURDATE() + INTERVAL 1 DAY)). So, replace CURDATE() in the above expression (twice) with this long mess.
Given a year and calendar week, how can I get the tuesday of that week as a date?
In MySQL the STR_TO_DATE() function can do the trick in just one line!
Example: We want to get the date of the Tuesday of the 32th week of the year 2013.
SELECT STR_TO_DATE('2013 32 Tuesday', '%X %V %W');
would output:
'2013-08-13'
I think this is the best and shortest solution to your problem.
Given you have year and cw (calender week) as variables (e.g. from a SELECT statement) you can get the DATE as following:
DATE_SUB(
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK),
INTERVAL WEEKDAY(
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK)
) -1 DAY),
The phrase DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK) is duplicated; did not want to store a variable. The SQL-Statement worked nicely for me on MySQL.
UPDATE: Just for clarification: WEEKDAY(DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK)) will yield the first day of the week. Substracting a number from it (-1 for Tuesday; -2 for Wednesday and so forth will select a specific day in the week for you).
See here.
The definitions of calendar week I found all said "a period of seven consecutive days starting on Sunday".
The following is MySQL specific... your mileage may vary...
DATE_ADD(MAKEDATE(year, 1), INTERVAL cw WEEK) adds the weeks from the 1st of the year which is not correct...
mysql> select DATE_ADD(MAKEDATE(2011, 1), INTERVAL 1 WEEK);
+----------------------------------------------+
| DATE_ADD(MAKEDATE(2011, 1), INTERVAL 1 WEEK) |
+----------------------------------------------+
| 2011-01-08 |
+----------------------------------------------+
By this definition, it is only meaningful to have the calendar week range from 1-53, and have this represent the Sunday of that week. As such, we would add 2 days to the nth Sunday of the year to get Tuesday.
The following gets the date of the first sunday of the year...
mysql> select date_add('2012-01-01', interval (8 - dayofweek('2011-01-01')) % 7 DAY);
+------------------------------------------------------------------------+
| date_add('2012-01-01', interval (8 - dayofweek('2011-01-01')) % 7 DAY) |
+------------------------------------------------------------------------+
| 2012-01-02 |
+------------------------------------------------------------------------+
so this will get the date of the 10th sunday (note interval 9 week since we are already at 1)...
mysql> select date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week);
+-----------------------------------------------------------------------------------------------------+
| date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week) |
+-----------------------------------------------------------------------------------------------------+
| 2010-03-07 |
+-----------------------------------------------------------------------------------------------------+
add 2 more days to get to tuesday...
mysql> select date_add( date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week), interval 2 day);
+--------------------------------------------------------------------------------------------------------------------------------+
| date_add( date_add( date_add('2010-01-01', interval (8 - dayofweek('2010-01-01')) % 7 DAY) , interval 9 week), interval 2 day) |
+--------------------------------------------------------------------------------------------------------------------------------+
| 2010-03-09 |
+--------------------------------------------------------------------------------------------------------------------------------+
or more generally:
select
date_add(
date_add(
date_add('<year>-01-01', interval (8 - dayofweek('<year>-01-01')) % 7 DAY)
, interval <week-1> week)
, interval <dayOfWeek> day
);
In looking at indago's answer and then doing a bunch of tests, I was getting the following week as the results.
I've made a minor adjustment, and the dates then matched:
SELECT STR_TO_DATE('2019 1 Monday', '%x %v %W') -- beginning of week
SELECT STR_TO_DATE('2019 1 Sunday', '%x %v %W') -- end of week
You can compare the results with here.
DELIMITER $$
CREATE FUNCTION fn_yearweek_to_date(
var_yearweek INTEGER UNSIGNED,
var_weekday ENUM(
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday'
)
)
RETURNS DATE DETERMINISTIC
BEGIN
RETURN STR_TO_DATE(CONCAT(var_yearweek, var_weekday), '%x%v%W');
END;
DELIMITER ;
SELECT
fn_yearweek_to_date(YEARWEEK(NOW(), 1), 'Sunday'),
fn_yearweek_to_date(YEARWEEK(NOW(), 1), 7)
;
Well theoretically you could use DATEPART with the dw parameter to get to find the first tuesday of the month and then add 7*[CalenderWeek] to get the appropriate date
http://msdn.microsoft.com/en-us/library/ms174420.aspx
I think it'd be easier to write the logic of the function using php.
If you use a php script, you can put all dates in a format similar to "day-month-year" and use a loop to go through every day (from 1980s to 2038 or from your mysql dates column).
http://www.php.net/manual/en/function.date-format.php
Then use date format on the dates in that loop to convert them to the days of the week.
Here is a listing of things that can be used in date formats. http://www.php.net/manual/en/function.date.php
D
N
l
w
all help you with day of the week.
Given solutions doesn't consider, that the first week of a year may start at the end of december. So we must check, if January 1st belongs to calendarweek of old or new year:
SET #week=1;
SET #year=2014;
SET #x_weeks_after_new_year=DATE_ADD(MAKEDATE(#year, 1), INTERVAL (SELECT IF(WEEKOFYEAR(MAKEDATE(#year, 1))>50 , 0 , -1))+#week WEEK);
SELECT
CONCAT(#year, '-', #week) WeekOfYear,
#weekStart:=DATE_SUB(#x_weeks_after_new_year, INTERVAL WEEKDAY(#x_weeks_after_new_year) DAY) Monday,
DATE_ADD(#weekStart, INTERVAL 6 DAY) Sunday
This will result in:
+------------+------------+------------+
| WeekOfYear | Monday | Sunday |
+------------+------------+------------+
| 2014-1 | 2013-12-30 | 2014-01-05 |
+------------+------------+------------+
Here is a sample that might help:
SET DATEFIRST 1
declare #wk int set #wk = 33
declare #yr int set #yr = 2011
select dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 2 -
datepart(dw, dateadd (week, #wk, dateadd (year, #yr-1900, 0)) - 4) as date
and the result is:
2011-08-16 00:00:00.000
which is today (Tuesday).
The upvoted solution worked for me in 2014 and 2015 but did not work for me in 2016 (possibly because the start of the Year is on Monday and not on Sunday.
I used the following function to correct this:
STR_TO_DATE(
CONCAT(mod(day_nr + 1 ,7) , '/', week_nr, '/', year), '%w/%u/%Y')
In my data :
day_nr = 0 -> Monday,
day_nr = 6 -> Sunday
So I had to fix that with a mod function
I have a dateBill field -DATETIME- in my DB.
I would like to retrieve all the info from THE LAST MONTH.
So, today is: 2011-08-02 12:00:00
My query is:
SELECT *
FROM bills
WHERE DATE(dateBill) > DATE_SUB(CURDATE(), INTERVAL 1 MONTH) AND status = 1
ORDER BY id_bill
The status just says if the bill is approved or not.
But I get some weid results:
2011-09-01 21:44:07
2012-08-01 00:00:00
I inserted those values just to test. As you can see, it is not working.
Any help, please?
DATE(dateBill) > DATE_SUB(CURDATE(), INTERVAL 1 MONTH)
captures dates in the future too.
including next month
2011-09-01 21:44:07
and next year
2012-08-01 00:00:00
add AND CURDATE() <= DATE(dateBill) if you dont like that...
SELECT *
FROM bills
WHERE
YEAR(dateBill) = YEAR(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) AND
MONTH(dateBill) = MONTH(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) AND
status = 1
ORDER BY id_bill