How to find week number?

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?
dt->tm_yday / 7? Perhaps +1 if you want the first week to be week 1 instead of week 0.
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.
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
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
Thanks a lot. Very usefull code :)
Topic archived. No new replies allowed.