How to find week number?

Aug 15, 2017 at 12:42pm
Hello,

What is the fastest way to find the week number (ISO-8601 for weeks starting on Monday) based on a tm struct?

Currently I do:

struct tm *dt = ...
char buffer[8];
strftime(buffer, 8, "%W", dt);
return atoi(buffer);

But I wonder if there is a faster (in cpu time) way of doing it?
Aug 15, 2017 at 1:54pm
dt->tm_yday / 7? Perhaps +1 if you want the first week to be week 1 instead of week 0.
Aug 15, 2017 at 1:58pm
Considering that the struct tm seems to contain integers:
http://www.cplusplus.com/reference/ctime/tm/

You probably could compute an integer from those values directly.
Aug 15, 2017 at 2:23pm
Adapted from FreeBSD libc:
https://svnweb.freebsd.org/base/stable/11/lib/libc/stdtime/strftime.c?revision=302408&view=markup#l448

1
2
3
4
5
6
7
8
9
10
#include <ctime>

int week_number( const std::tm& tm ) {
    
    constexpr int DAYS_PER_WEEK = 7 ;

    const int wday = tm.tm_wday ;
    const int delta = wday ? wday-1 : DAYS_PER_WEEK-1 ;
    return ( tm.tm_yday + DAYS_PER_WEEK - delta ) / DAYS_PER_WEEK ;
}

http://coliru.stacked-crooked.com/a/854e5e7c08e71f17
Aug 15, 2017 at 4:34pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <ctime>
using namespace std;

//======================================================================

bool isLeap( int year )
{
    if ( year % 4 == 0 )
    {
       if ( year % 100 == 0 && year % 400 != 0 ) return false;
       else                                      return true;
    }
    return false;
}

//======================================================================

void getYearAndWeek( tm TM, int &YYYY, int &WW )                       // Reference: https://en.wikipedia.org/wiki/ISO_8601
{
   YYYY = TM.tm_year + 1900;
   int day = TM.tm_yday;

   int Monday = day - ( TM.tm_wday + 6 ) % 7;                          // Monday this week: may be negative down to 1-6 = -5;
   int MondayYear = 1 + ( Monday + 6 ) % 7;                            // First Monday of the year
   int Monday01 = ( MondayYear > 4 ) ? MondayYear - 7 : MondayYear;    // Monday of week 1: should lie between -2 and 4 inclusive
   WW = 1 + ( Monday - Monday01 ) / 7;                                 // Nominal week ... but see below

   // In ISO-8601 there is no week 0 ... it will be week 52 or 53 of the previous year
   if ( WW == 0 )
   {
      YYYY--;
      WW = 52;
      if ( MondayYear == 3 || MondayYear == 4 || ( isLeap( YYYY ) && MondayYear == 2 ) ) WW = 53;
   }

   // Similar issues at the end of the calendar year
   if ( WW == 53)
   {
      int daysInYear = isLeap( YYYY ) ? 366 : 365;
      if ( daysInYear - Monday < 3 )
      {
         YYYY++;
         WW = 1;
      }
   }
}

//====================================================================== 


You would need %V, not %W, in strftime() for true ISO-8601 - http://www.cplusplus.com/reference/ctime/strftime/ . I'm glad to see that the FreeBSD implementation looks just as complicated for case 'V'.

EDITED: corrected the "Week 53" case: depends on the Monday of the current week, not day of the year. Coded from the Wikipedia article, so could still do with some checking ...
Last edited on Aug 16, 2017 at 9:19am
Aug 16, 2017 at 5:45am
Thanks a lot. Very usefull code :)
Topic archived. No new replies allowed.