How to convert number of week into date? - mysql

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

Related

How to get previous year from day of week and week

I have function get day of week and week from current day in mysql
It look like.
select
DATE_FORMAT(now(), '%U') as w,
DATE_FORMAT(now(), '%w') as day_of_w;
It return w is 37 and day_of_week is 1. How to get correct value w is 37 and day_of_week is 1 of previous year.
I using
select
DATE_FORMAT(now() interval 1 year, '%U') as w,
DATE_FORMAT(now() interval 1 year, '%w') as day_of_w;
But SQL cannot execute.
The function to turn a string into a date is STR_TO_DATE. If you want to get the date for week 37, day 1 in 2019, you could use
select str_to_date('37 1 2019', '%U %w %Y')
If you want the date for today's week and day number in last year:
select str_to_date(concat_ws(' ', date_format(current_date, '%U'),
date_format(current_date, '%w'),
year(current_date) - 1),
'%U %w %Y')
Be aware though, that some years have a week 53 while others don't. If you run this query in a year's 53rd week, you don't get a valid result.
select DAYOFWEEK(now()) day_of_week, week(now()) week from dual
your query has syntactical problem. you forgot +/- symbol
select DATE_FORMAT(now() + interval 1 year, '%U') as w
,DATE_FORMAT(now() + interval 1 year, '%w') as day_of_w;
You just need to use DATE_ADD function in MYSQL to change the date to last year.
select DATE_FORMAT(DATE_ADD(now(), INTERVAL -1 YEAR), '%U') as w
,DATE_FORMAT(DATE_ADD(now(), INTERVAL -1 YEAR), '%w') as day_of_w;
Result:
36 6
Current and last year date:
select now() as curr_yr_date, DATE_ADD(now(), INTERVAL -1 YEAR) as last_yr_date;
Result:
2020-09-14 07:13:24 2019-09-14 07:13:24

select dates on this weekday in the past

I have a list of dates ("start", datetime) and I would like to select all dates where :
today = start + 1 WEEK or
today = start + 2 WEEK or
today = start + 3 WEEK or
today = start + 4 WEEK or
today = start + 5 WEEK or
today = start + 6 WEEK
Maximum is start + 6 weeks.
Any idea ?
setup
create table example
(
id integer primary key not null auto_increment,
start datetime not null
);
insert into example ( start )
values
( date_sub(current_date, interval 1 week) ),
( date_sub(current_date, interval 2 week) ),
( date_sub(current_date, interval 3 week) ),
( date_sub(current_date, interval 4 week) ),
( date_sub(current_date, interval 5 week) ),
( date_sub(current_date, interval 6 week) ),
( date_sub(current_date, interval 6 week) ),
( date_sub(current_date, interval 4 week) ),
( date_sub(current_date, interval 9 week) ),
( date_sub(current_date, interval 12 week) )
;
query
select id, start
from example
where
date(start) in
(
date_sub(current_date, interval 1 week) ,
date_sub(current_date, interval 2 week) ,
date_sub(current_date, interval 3 week) ,
date_sub(current_date, interval 4 week) ,
date_sub(current_date, interval 5 week) ,
date_sub(current_date, interval 6 week)
)
;
output
+----+-----------------------------+
| id | start |
+----+-----------------------------+
| 1 | September, 16 2015 00:00:00 |
| 2 | September, 09 2015 00:00:00 |
| 3 | September, 02 2015 00:00:00 |
| 4 | August, 26 2015 00:00:00 |
| 5 | August, 19 2015 00:00:00 |
| 6 | August, 12 2015 00:00:00 |
| 7 | August, 12 2015 00:00:00 |
| 8 | August, 26 2015 00:00:00 |
+----+-----------------------------+
sqlfiddle
I assume you want a WHERE filter to capture all rows containing start DATETIMEs on this weekday one week ago, and two ... six weeks ago. That's the effect of the logic in your question:
today = start + 1 WEEK or today = start + 2 WEEK or ...
means the same thing as
start = today - 1 WEEK etc.
The thing is, you are using DATETIME values for start. They're not guaranteed to be start = CURDATE() because they may not be at midnight.
So, you need to use the DATE() function to reduce them to midnight values before comparing them. Something like this will work.
WHERE DATE(start) IN (
CURDATE() - INTERVAL 6 WEEK, CURDATE() - INTERVAL 5 WEEK, CURDATE() - INTERVAL 4 WEEK,
CURDATE() - INTERVAL 3 WEEK, CURDATE() - INTERVAL 2 WEEK, CURDATE() - INTERVAL 1 WEEK)
You could also do this -- it picks out all records six weeks old or newer, but not the ones in the most recent week, then picks the ones on today's weekday.
WHERE start >= CURDATE() - INTERVAL 6 WEEK
AND start < CURDATE() - 6 DAY
AND WEEKDAY(CURDATE()) = WEEKDAY(start)
This second formulation will be more efficient if you have a great deal of old data in your table and you have an index on your start column: the first two where clauses are sargeable.
Pro tip: When specifying this kind of date filter, the more effort you spend making your specification exact before you write code, the faster you will finish your work. That's true even if you don't count debugging time.
Could you use between?
So...
today BETWEEN start AND start + INTERVAL 6 WEEK
Something like that?
Try To run this query in mysql,
use dbname;
create table adddate
(
id integer primary key not null auto_increment,
initial datetime not null
)auto_increment=100;
insert into adddate ( initial )
values( DATE_ADD(current_date, interval 1 week) ),
( DATE_ADD(current_date, interval 2 week) ),
( DATE_ADD(current_date, interval 3 week) ),
( DATE_ADD(current_date, interval 4 week) ),
( DATE_ADD(current_date, interval 5 week) ),
( DATE_ADD(current_date, interval 6 week) )
;
date_add is an SQLfunction it is used to do addition operations on date
The syntax is:-
DATE_ADD(date,INTERVAL expr type)
the type can be:-
MICROSECOND,
SECOND,
MINUTE,
HOUR,
DAY,
WEEK,
MONTH,
QUARTER,
YEAR,
SECOND_MICROSECOND,
MINUTE_MICROSECOND,
MINUTE_SECOND,
HOUR_MICROSECOND,
HOUR_SECOND,
HOUR_MINUTE,
DAY_MICROSECOND,
DAY_SECOND,
DAY_MINUTE,
DAY_HOUR,
YEAR_MONTH;

MySQL Retrieve data from any previous week

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.

stuck mysql find last working day in previous month

I would like to ask you that i want to find last working day in previous month in MYSQL.
How to do that ?
this code find last day previous month as the following:
LAST_DAY(Now()- INTERVAL 1 MONTH)
Result that i need as below:
Current Month= 2013-07-01====> Results should be 2013-06-28
Current Month= 2013-06-06====> Results should be 2013-05-31
How to find last working day in previous month ?
Regards
Here’s an example of what I thought of:
LAST_DAY(CURDATE() - INTERVAL 1 MONTH) -
INTERVAL (CASE WEEKDAY(LAST_DAY(CURDATE() - INTERVAL 1 MONTH))
WHEN 5 THEN 1
WHEN 6 THEN 2
ELSE 0 END) DAY
Try something like:
SET #lastworkingday = DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE()) DAY);
// If saturday, take it back one day
IF DAYOFWEEK(#lastworkingday) = 7
THEN #lastworkingday := DATE_SUB(#lastworkingday, INTERVAL 1 DAY);
// If sunday, take it back two days
ELSE IF DAYOFWEEK(#lastworkingday) = 1
THEN #lastworkingday := DATE_SUB(#lastworkingday, INTERVAL 2 DAY);
There may be a more elegant way of writing this (I'd be surprised if there wasn't), but Gumbo's solution might look like this...
Note I've used #dt for testing. You could just substitute CURDATE() in your script.
SET #dt = '2012-07-01'; -- Last day of the previous month (June 2012) was a Saturday
SELECT CASE DAYOFWEEK(LAST_DAY(#dt-INTERVAL 1 MONTH))
WHEN 1 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 2 DAY
WHEN 7 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 1 DAY
ELSE LAST_DAY(#dt-INTERVAL 1 MONTH)
END x;
+------------+
| x |
+------------+
| 2012-06-29 | -- Friday 29th
+------------+
SET #dt = '2012-08-01'; -- Last day of the previous month (July 2012) was a Tuesday
SELECT CASE DAYOFWEEK(LAST_DAY(#dt-INTERVAL 1 MONTH))
WHEN 1 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 2 DAY
WHEN 7 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 1 DAY
ELSE LAST_DAY(#dt-INTERVAL 1 MONTH)
END x;
+------------+
| x |
+------------+
| 2012-07-31 | -- Tuesday 31st
+------------+
SET #dt = '2012-10-01'; -- Last day of the previous month (September 2012) was a Sunday
SELECT CASE DAYOFWEEK(LAST_DAY(#dt-INTERVAL 1 MONTH))
WHEN 1 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 2 DAY
WHEN 7 THEN LAST_DAY(#dt-INTERVAL 1 MONTH)-INTERVAL 1 DAY
ELSE LAST_DAY(#dt-INTERVAL 1 MONTH)
END x;
+------------+
| x |
+------------+
| 2012-09-28 | -- Friday 28th
+------------+
Another way is to build a calendar table of all dates. Ignoring public holidays, the first and last working days of the month then are simply the min and max weekdays of the month.
Although, there is an argument that goes 'so why not just figure this bit out in your application code'!
To get last working date you have to follow following steps :
1. first of all get last date of last month .
2.check whether it is working day or not by weekday function . if it is sunday then subtract 2 from lastdate , if it is saturday then subtract 1 from lastdate otherwise do not change anything .
we can do this thing by case statement . check following query . it will really works :
SELECT
CASE WEEKDAY(LAST_DAY(CONCAT(YEAR(CURDATE()),'-' , MONTH(CURDATE())-1 ,'-' , 1)))
WHEN 6
THEN DATE_ADD(LAST_DAY(CONCAT(YEAR(CURDATE()),'-' , MONTH(CURDATE())-1 ,'-' , 1)),INTERVAL -2 DAY)
WHEN 5
THEN DATE_ADD(LAST_DAY(CONCAT(YEAR(CURDATE()),'-' , MONTH(CURDATE())-1 ,'-' , 1)),INTERVAL - 1 DAY)
ELSE
LAST_DAY(CONCAT(YEAR(CURDATE()),'-' , MONTH(CURDATE())-1 ,'-' , 1)) END;
http://sqlfiddle.com/#!2/d41d8/15940
if you subtract the day of current month then you will get the last day of previous month
(DATE_SUB(CURDATE(),INTERVAL DAYOFMONTH(CURDATE()) DAY))
For example, on the 12th of the month if we subtract 12 days,then well get the the last day of the previous month:

Changing start-date in MySQL for week

I found the following code to help in creating a weekly report based on a start date of Friday. The instructions say to replace ".$startWeekDay." with a 4. When I put '".$startDay."' as '2013-01-30', I get errors.
Also I get a report by day rather than week as I desire.
SELECT SUM(cost) AS total,
CONCAT(IF(date - INTERVAL 6 day < '".$startDay."',
'".$startDay."',
IF(WEEKDAY(date - INTERVAL 6 DAY) = ".$startWeekDay.",
date - INTERVAL 6 DAY,
date - INTERVAL ((WEEKDAY(date) - ".$startWeekDay.")) DAY)),
' - ', date) AS week,
IF((WEEKDAY(date) - ".$startWeekDay.") >= 0,
TO_DAYS(date) - (WEEKDAY(date) - ".$startWeekDay."),
TO_DAYS(date) - (7 - (".$startWeekDay." - WEEKDAY(date)))) AS sortDay
FROM daily_expense
WHERE date BETWEEN '".$startDay."' AND '".$endDay."'
GROUP BY sortDay;
The following code is what I am using
SELECT count(DISTINCT (
UserID)
) AS total, CONCAT(IF(date(LastModified) - INTERVAL 6 day < date(LastModified),
date(LastModified),
IF(WEEKDAY(date(LastModified) - INTERVAL 6 DAY) = 4,
date(LastModified) - INTERVAL 6 DAY,
date(LastModified) - INTERVAL ((WEEKDAY(date(LastModified)) - 4)) DAY)),
' - ', date(LastModified)) AS week
FROM `Purchase`
WHERE `OfferingID` =87
AND `Status`
IN ( 1, 4 )
GROUP BY week
The output I get is
total week
3 2013-01-30 - 2013-01-30
1 2013-01-31 - 2013-01-31
I'm not sure exactly how you want to display your week, the sql above is attempting to display date ranges. If this isn't a requirement, your query could be very simple, you can just offset your time by two days (since friday is two days away from the natural star of the week) and use the week function to get the week number.
The query would look like this:
select count(distinct (UserID)) as total
, year( LastModified + interval 2 day ) as year
, week( LastModified + interval 2 day ) as week_number
FROM `Purchase`
WHERE `OfferingID` =87
AND `Status`
IN ( 1, 4 )
group by year, week_number;