How to get week numbers from dates?

Looking for a function in R to convert dates into week numbers (of year) I went for week from package data.table . However, I observed some strange behaviour:

> week("2014-03-16") # Sun, expecting 11 [1] 11 > week("2014-03-17") # Mon, expecting 12 [1] 11 > week("2014-03-18") # Tue, expecting 12 [1] 12 

Why is the week number switching to 12 on tuesday, instead of monday? What am I missing? (Timezone should be irrelevant as there are just dates?!) Other suggestions for (base) R functions are appreciated as well.

55.4k 12 12 gold badges 123 123 silver badges 221 221 bronze badges asked Mar 16, 2014 at 16:29 Christian Borck Christian Borck 1,852 1 1 gold badge 13 13 silver badges 19 19 bronze badges Try format(as.Date("2014-03-16"), "%U") or format(as.Date("2014-03-16"), "%W") Commented Mar 16, 2014 at 16:54

@GSee thanks, but that returns 11 instead 12 for the following: format(as.Date("2014-03-17"), "%U") and format(as.Date("2014-03-17"), "%W") !?

Commented Mar 16, 2014 at 17:07 so, convert to integer and add 1. See ?strptime Commented Mar 16, 2014 at 19:21

That's what I am doing right now, actually. I was just wondering, why I have to make this workaround? I would expect the week to begin on monday (EU) or sunday (US), but not on tuesday?

Commented Mar 16, 2014 at 19:27 Possible duplicate of as.Date produces unexpected result in a sequence of week-based dates Commented May 9, 2017 at 10:05

8 Answers 8

Base package

Using the function strftime passing the argument %V to obtain the week of the year as decimal number (01–53) as defined in ISO 8601. (More details in the documentarion: ?strftime)

strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%V") 
[1] "11" "12" "12" "01" 
answered May 10, 2017 at 12:32 13.5k 2 2 gold badges 64 64 silver badges 71 71 bronze badges 2014-01-01 and 2014-12-29 will get both 01 . Commented Sep 6, 2019 at 9:56

@giordano That is correct as defined in ISO 8601. If the week (starting on Monday) containing 1 January has four or more days in the new year, then it is considered week 1. You can double-check this in any of the iso 8601 week calculators online.

Commented Sep 6, 2019 at 13:16 It should be 2015-01-01, correct? How to fix this? Commented Oct 19, 2021 at 13:35

if you try with lubridate:

library(lubridate) lubridate::week(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01')) [1] 11 11 12 1 

The pattern is the same. Try isoweek

lubridate::isoweek(ymd("2014-03-16", "2014-03-17","2014-03-18", '2014-01-01')) [1] 11 12 12 1 
59.1k 7 7 gold badges 101 101 silver badges 142 142 bronze badges answered Mar 16, 2014 at 16:37 Paulo E. Cardoso Paulo E. Cardoso 5,856 34 34 silver badges 42 42 bronze badges

?week (lubridate) states: Weeks is the number of complete seven day periods that have occured between the date and January 1st, plus one.

Commented Mar 16, 2014 at 16:44 @ChristianBorck isoweek is what you need? Commented Mar 16, 2014 at 16:51

That looks good, but my lubridate (v 1.3.1) package seems to be missing the isoweek function? Which version do you use?

Commented Mar 16, 2014 at 16:57 @ChristianBorck I'm running lubridate_1.3.3 update it. Commented Mar 16, 2014 at 16:58

I understand the need for packages in certain situations, but the base language is so elegant and so proven (and debugged and optimized).

And then your choice whether the first week of the year is zero (as in indexing in C) or 1 (as in indexing in R).

No packages to learn, update, worry about bugs in.

2,091 1 1 gold badge 23 23 silver badges 35 35 bronze badges answered Mar 16, 2014 at 18:02 user3229754 user3229754 111 3 3 bronze badges

I always try to solve problems with base R first. So, I am with you. But your answer misses to get the (calendar) week number I am looking for!? (dt2$yday-1)%/%7 +1 for example only works right, if January 1st was a monday.

Commented Mar 16, 2014 at 18:42

@ChristianBorck - Not to confuse things even further, but "right" depends on your definition of "week". The ISO-8601 standard defines a week to start on a Monday, although the week numbering depends on what day Jan 1 falls on. The week(. ) function does not claim to use this standard. My point was that week(. ) does not a appear to adhere to it's own definition. If you want ISO-8601 weeks (a good practice, by the way), use isoweek(. ) .

Commented Mar 16, 2014 at 19:28

The above solution by user3229754 returns the day numbers starting with index=0 , I guess you can try : ( dt$yday ) %/%7 +1

Commented May 26, 2017 at 9:52

Actually, I think you may have discovered a bug in the week(. ) function, or at least an error in the documentation. Hopefully someone will jump in and explain why I am wrong.

Looking at the code:

library(lubridate) > week function (x) yday(x)%/%7 + 1

The documentation states:

Weeks is the number of complete seven day periods that have occured between the date and January 1st, plus one.

But since Jan 1 is the first day of the year (not the zeroth), the first "week" will be a six day period. The code should (??) be

(yday(x)-1)%/%7 + 1 

NB: You are using week(. ) in the data.table package, which is the same code as lubridate::week except it coerces everything to integer rather than numeric for efficiency. So this function has the same problem (??).

answered Mar 16, 2014 at 17:18 59.1k 7 7 gold badges 101 101 silver badges 142 142 bronze badges

if you want to get the week number with the year use: "%Y-W%V" :

e.g yearAndweeks  
> strftime(c("2014-03-16", "2014-03-17","2014-03-18", "2014-01-01"), format = "%Y-W%V") 

[1] "2014-W11" "2014-W12" "2014-W12" "2014-W01 "

answered Feb 24, 2018 at 19:31 Grant Shannon Grant Shannon 5,017 2 2 gold badges 50 50 silver badges 38 38 bronze badges

This is dangerous: strftime(c(as.Date("2014-01-01"),as.Date("2014-12-29")), format = "%Y-W%V") gives [1] "2014-W01" "2014-W01" .

Commented Sep 6, 2019 at 9:49 This may be of some help: stackoverflow.com/questions/49904570/… Commented Feb 26, 2021 at 18:23

If you want to get the week number with the year, Grant Shannon's solution using strftime works, but you need to make some corrections for the dates around january 1st. For instance, 2016-01-03 (yyyy-mm-dd) is week 53 of year 2015, not 2016. And 2018-12-31 is week 1 of 2019, not of 2018. This codes provides some examples and a solution. In column "yearweek" the years are sometimes wrong, in "yearweek2" they are corrected (rows 2 and 5).

library(dplyr) library(lubridate) # create a testset test % mutate(date = as.Date(date_txt, format = "%Y-%m-%d")) %>% mutate(yearweek = as.integer(strftime(date, format = "%Y%V"))) %>% mutate(yearweek2 = ifelse(test = day(date) > 7 & substr(yearweek, 5, 6) == '01', yes = yearweek + 100, no = ifelse(test = month(date) == 1 & as.integer(substr(yearweek, 5, 6)) > 51, yes = yearweek - 100, no = yearweek))) # print the result print(test) date_txt date yearweek yearweek2 1 2015-12-31 2015-12-31 201553 201553 2 2016-01-03 2016-01-03 201653 201553 3 2016-01-04 2016-01-04 201601 201601 4 2018-12-30 2018-12-30 201852 201852 5 2018-12-31 2018-12-31 201801 201901 6 2019-01-01 2019-01-01 201901 201901