logo To Foot
© J R Stockton, ≥ 2012-11-10

Week Number Calculation.

No-Frame * Framed Index * Frame This
Links within this site :-

Week Number Routines

Requests for "a function to return the week number of a date" are common. It is essential
  •   to check which definition is to be used;
  •   and, for some definitions, to return also the proper year number, because of the overlaps.
Such a function can also return a Day-of-Week number.

Algorithms here are shown mostly in JavaScript; note that JavaScript stores date/time as milliseconds from 1970.0 GMT.. If translating to VBScript (see via VBScript Date and Time Index), DateSerial can be useful. For Pascal/Delphi, see my Borland Pascal Time and Date and unit dateprox.pas.

Here, when using JavaScript, Week Number Dates are commonly held in arrays [y,w,d] and years are assumed to be AD 100 or later. The possibility of a Date Object representing a time other than 00:00h has largely been disregarded. JavaScript's Math.floor(X/Y) and ((X/Y)|0) are equivalent, the latter being faster, and match VBScript/C's X\Y amd Pascal's X div Y.

Each function giving [y, w, d] here should have a corresponding reverse function.

It should generally be easy enough to convert a week-of-year algorithm to a corresponding week-of-month one.

Refer to the corresponding parts of Calendar Weeks for the associated descriptive material.

It is possible that some code may inadvertently assume either that local time is GMT or that local time is less than 12 hours away from GMT. RSVP if you see signs of that here, unless specific warning is given.

Correctness and Efficiency

The conversion routines are believed to give correct results, and the JavaScript ones can be tested on this page.

The underlying algorithms are reasonably efficient, They go directly to the result, without looping; except that some have a single recursion.

The implementation of some of the JavaScript algorithms that run on this page may not be fully optimum. In particular, I have noticed that using UTC date object functions internally is much faster, and have been converting more routines. Also, code converting via DayCount is comparatively fast. See also in JavaScript Date and Time 1 : Date Arithmetic.

Pascal/Delphi code in my dateprox.pas etc. is mostly older, and may be less efficient.

Day-of-Week

Day-of-Week of a given date in early January of a known Gregorian year number is often needed; that can be rapidly determined with expressions such as used by Zeller.

For 1901-2099, to get Day-of-Week of January 1st, using Sun=0 :-
(5*(y-1) div 4) mod 7    or    Trunc(1.25*(y-1)) mod 7

Use Back to return.
An Error

Note that Appendix B of a draft Internet standard, dated in the 1990's, has a C routine (based on Zeller's) for getting day-of-week from Y M D in which the left operand of a mod operator goes negative for some dates early in a century, giving a wrong result. Uncorrected copies are still on the Net.

Imported Functions and Added Methods

See JavaScript Include Files.

ISO Week Number Programming

2007-W50-5 : Another algorithm is given by Jan Stout in Calculating the ISO week number - to be examined.

For a discussion of ISO 8601 week numbers in general, see in Calendar Weeks.

All ISO weeks are Monday = 1 to Sunday = 7, and week 01 of the numbering year is the one containing the first Thursday of the Gregorian year. Thus up to three days of the first and last weeks for a year number can be in the adjacent Gregorian year; December 29 to January 03 can have a numbering year differing from the Gregorian.

A complete ISO Week Number function must also return the Year Number. It is easy enough also to return the Day-of-Week number, DoW.

Some methods give the Year directly. Otherwise, if the Calendar Month Number is large and the ISO Week Number is small then increment the Year, and vice versa, or otherwise.

String forms such as "yyyy-Www" or "yyyy-Www-d" or "yyyy-ww" or "yyyy-ww-d" can be used, depending on context; or arrays [Y, W, D].

Common Functions

This returns a string "yyyy-Www-d" from an array [y, w, d], to represent ISO 8601 week numbers.

General Considerations

Ordinal Date (YYYY-DDD) and Sunday Letter must suffice to determine Y-W-D. For a Leap Year, use the Letter for the start of the year, which is not the one for Easter Sunday. But if the Nearest Thursday is not in the original YYYY, it will be necessary to reconsider.

In practice, the best method of implementation will depend on the nature and speed of the available primitive operations, and on whether the date is represented as a string, as Y-M-D, as Y-D, or as a count of time or whatever.

If using a [milli-]seconds-based method for DayCount, consider carefully Summer Time, remembering that it can span a New Year. So rounding the time from January 4th is better than truncating the time from January 1st.

It can be useful to know that CJD 0 was a Monday, and that 1970-01-01 was a Thursday.

Date to Week Number and Day-of-Week

When calculating ISO 8601 Week Number, giving the Year Number is essential. The Day Number will normally be easy to find en passant.

Absolute Week Number and Day-of-Week

If a DayCount is known, then
  (DayCount+K) div 7       gives an Absolute Week Number (AWN),
  1 + (DayCount+K) mod 7   gives the Day-of-Week (DoW).
where K allows for the Day-of-Week of DayCount zero, and ensures that the arguments of mod & div are never negative.

Start of Given Week Numbering Year
Start of Current Week Numbering Year
Week Number, A
Week Number, B

Week Number and Day-of-Week to Date

Demonstrations

Using JavaScript,

Interconversion Demonstrator
From yyyy-mm-dd From yyyy-Www-d
days
The answer for each column appears at the top of the other and is reprocessed therein.

Manually tested above :-

Y M D to/from Y W D Arithmetically

Here, JavaScript Date Objects are used only in testing. The methods therefore can be implemented in any language.

First Method

The following determines ISO Week Number by explicit arithmetic from IsLeapYear, Month, Day, and Day-of-Week, and vice versa, without going explicitly via a day-count.

It shows the possibility of calculating ISO Week Number Www-d from IsLeapYear, Month, Day, and day-of-week-of-January-1st, since those suffice to determine the current Day-of-Week, and vice versa.

Algorithm of function NewWN :-

Algorithm of function RevWN :-


Use Back to return.

Second Method

For Day-of-Year, see also in Date Object Information.

Function MathWNo is differently derived and independent of imported functions and Date Objects. It is somewhat optimised in function WNoMath.

   

Optionally, this function body can be tested by "Test WNo" - enter and Define (or UnDefine) your own, remembering to declare all internal variables.
The return value must be [Year, Week, Day] Numbers.

function UserWeekNo(Y, M, D) { // M=1..12 ;  
  }

DayCount to/from Y W D Arithmetically

Some languages handle dates as DayCounts, often with Day 0 being 1899-12-30 and Day 25569 being 1970-01-01. Conversion of DayCount to/from Y W D is easier than conversion to/from Y M D, and does not need system date routines.

In January 2008 I derived, now in unit programs/dateutys.pas, efficient Pascal/Delphi routines for DateTime to/from YWD, covering the (extendable) range AD 0001 to 9999. The following JavaScript routines mimic them.

Date :  
DT :   WN :   DT :   Date :
   

General Testing

Comparison results which can be used by testers, showing Weeks 1, 52 & 53 for over 28 years, are in wknotest.txt.

The following checks should suffice - all weeks have 7 days, Monday is Day 1 of 1..7, January 4th is in Week Number 1 of 1..52/53, Year usually matches, numbers YYYYWWD are in increasing order, testing over 28 years with quadrennial Leap Years (or at least over all 14 year types). That does not fully test the effects of the 100 and 400 year rules introduced by Pope Gregory XIII.

Test cases should include those tabled in Calendar Weeks.

Generation of Sequential Week Numbering Dates

To test a function that generates Week Numbering dates from an ordinary date, or vice versa, one can easily generate an independent sequence of known-good Week Numbering dates.

ISO 8601:2004(E) §3.2.2 "The week calendar" includes The reference point of the time scale assigns Saturday to 1 January 2000; and, since 2000 (being Leap) had 366 = 52×7+2 days, 2001-01-01 was a Monday, and was 2001-W01-1.

Then, starting for example with Y=2001, W=1, D=1, one can increment D in 1..7, carrying into W in 1..52/53, carrying into Y. To choose between 52 and 53, it is sufficient to note that the first day of a Week 53 can only be the 28th or 27th of its month, or that carry into Y can only occur before the 29th to before the 4th.

One can also step Thursdays (D=4) a week at a time, get D=1 to D=7 from them (in the same year and week), and reset W=1 whenever either W=53 or both W=52 and Y has changed.

ISO Week Numbers in Specific Languages

JavaScript is placed first merely for the convenience of contiguity with the demonstration above.

JavaScript

Where Math.round(X) is used, X|0 may be faster, and is equivalent for 0≤X<231; but beware operator precedences.

Code not using Date Objects is currently above.

As Demo Code Above

These should be evidently the same algorithms as demonstrated. It shows errors in Opera 9; see in JavaScript Date and Time Troubles :-

Test DobToYWDarr and YWDarrToDob
     
Accepts moderately unreasonable dates
Some errors in Opera 9, e.g. 2117-10-29 ->

The next routine is in inc-date.js; YMD2YWD uses a different method and parameters to the above.

NOTE the change of 2007-04-03 in YMD2YWD, to allow for a problem with the browser Firefox 2.0.0.3.

YYYY-MM-DD to YYYY-WW-D, in two ways

ISO 8601 : YYYY-MM-DD to YYYY-WW-D Demos


      returns Yr<0 for an error, else array of Yr, Wk, Dy;







First Validate. Then save Day-of-Week, get nearest Thursday, save Time; go to January 4th, subtract from Time, round the Week difference.

YYYY-WW-D to YYYY-MM-DD, Array or Object

ISO 8601 : YYYY-WW-D to YYYY-MM-DD Demo


      returns Yr<0 for an error, else array of Yr, Mo, Dy;






Basically, put Year Jan 4th into a date object, getDay(), then use setDate() to move back 0..6 days to Monday and to add (WW-1)*7 days for the start of the week.

Week Number without Date Object

Contents moved upwards.

DayCount Routines

A direct routine for CMJD to Year-Week-Day, and a new direct reverse routine, with CMJD to/from Year-Month-Day and Year-Month-Day to/from Year-Week-Day using them :-

Test CMJDToYWD and YWDtoCMJD
       
Accepts moderately unreasonable dates

One day per year for over 400 years.

Note that routines using CMJD are comparatively fast.

Try Above JavaScript ISO Week Number Conversions

Given the straightforward handling of the Week and Day components in conversions from YWD to ordinary date, it seemed sufficient to test just over one day per year for over 400 years.

OPERA 9.21, 9.25 BUGS BROKE TESTS
(in UK, but not in all locations?).
Faulty days are now skipped.
Testing other functions against CMJDtoYWD YWDtoCMJD


Time each for a total of about seconds (P4/3G XP sp2 IE6)

I have some more JavaScript for date conversions, and a little VBScript.

DOS Batch

In unaided DOS..Win98/ME, date calculations cannot reasonably be done.

My NOWMINUS and ENVICALC are batch-enhancing programs for date/time work and for integer arithmetic; get Pascal source and executable via programs.

  NOWMINUS F5  J0 L6 Eisoweekno	; set env. ISOWEEKNO to ISO     YYYYWW
  NOWMINUS F33 J0 L6 Etaxweekno	; set env. TAXWEEKNO to UK HMRC YYYYWW

For an absolute week count, from BC 4713,

  NOWMINUS F9 EDN		  ; set CJD in env. DN, then
  ENVICALC    CDN 3 - 7 / B6 EWN  ; set Local AbsWkNo in env. WN

Adjust the value of 3 by up to ±3 to get the desired first day of the
week (..., Mon, ...); put   N -   after / to alter the zero week by N.

See also in MS-DOS Batch Files, and WSH below this.

NT/XP Batch

In Windows NT, XP, etc., batch, it should be possible to code most or all common date/time work. In particular, CMD.EXE FAQ ZIP #149. But it will often be easier to use WSH with VBS or JS.

Batch file iso-wkno.bat contains and tests CMD.EXE code for obtaining y w d and yyyy-Www-d from yyyy mm dd, for 1900-03-01 to 2100-02-28 inclusive.

VBScript

See my code in VBScript Date and Time 2 - for all valid years.

Note that DatePart("ww", Date, vbMonday, vbFirstFourDays) is not always satisfactory.

Windows Script Host

WSH can execute JavaScript and VBScript; see in MS-DOS Batch Files.

Microsoft .Net

See ISO 8601 Week of Year format in Microsoft .Net.

Apparently, the general algorithm above should be easy to implement using only basic DateTime routines.

PowerShell

Code by Joel Bennett is at poshcode; I understand that its results agree with mine, though the algorithm seems to differ.

Also, the thread with this article, by Kiron, dated Sat, 21 Mar 2009 10:38:41 -0600, looks good.

My new (2012-11-05) page weekpshl.ps1, written independently of the above and subsequently reconsidered, contains code to convert between a PowerShell Datetime object and ISO 8601 week-numbering form, etc.

Pascal/Delphi

My Pascal unit dateprox.pas, tested by program mjd_date.pas with included version.pas (BP7; Delphi 3 at least) can calculate week number (ISO, Tax & otherwise) from date, by first principles. Program mjd_date.exe was used for :-

Peter Haas has written Delphi Week Number code, ISO & otherwise (ZIP).

Recent Delphi has WeekOfTheYear to give ISO Week Numbers (NOTE: verified over full 9999 years; wrong within Mondays before 1900). It is longer than necessary, as I can do the job correctly , better and slightly faster, in five commented one-line statements. I can do the reverse in four one-line statements. Routines are now in dateprox.pas and demonstrated in mjd_date.pas; versions in unit programs/dateutys.pas may be even better.

GAWK & C

GAWK code for ISO week number, valid for 1970-2099, independent of strftime(), is tested in programs/iso-wkno.awk. Where different, the result of strftime (which is then expected to be 53) is appended.

Presumably C code would be similar.

See also Week Numbers in UNIX & C Utilities.

SQL Server

I've been sent the following code to determine the start of the ISO Week-Numbering Year; I think it's for SQL Server.

Truncate(date_0, 'IY') =
  Truncate(Truncate(Truncate(date_0, 'IW') + 3, 'YEAR') + 3, 'IW')

"Truncate(date, 'IW') truncates an input date to the nearest Monday (ISO Week start), Truncate(date, 'YEAR') returns calendar year start, and '+ 3' means 'plus 3 days'". That amounts to "go to the nearest Thursday, go to January 4th of that calendar year, go to the start of that week", which is appropriate.

Perl

I don't know, or have, Perl.

Revised Perl code contributed by Robert Urban (2002-08-13), which I have not been able to test, is in programs/what-cw.pl. It uses only basic arithmetic operations. Check it before use.

However, Harry Broomhall wrote (2002-08-18) :-

There are many time/date-based modules/libraries for Perl, but, IMHO, the one known as Date::Calc is the most comprehensive, and the most mature. Quoting this name is generally the standard response to questions about date-handling in newsgroups/lists.

Date::Calc can be retrieved from CPAN (the major repository of Perl libraries). The module includes a *lot* of utilities to cover most requirements for 'normal' civil uses (i.e. it doesn't include astro requirements, or odd calendars such as Mayan).

A working example (weekno.pl) to get week-numbers from a date (passed on the command line) is as follows :-

#!/usr/bin/perl -w
use strict;
use Date::Calc qw(Week_of_Year);
my ($wn, $yr);
($wn, $yr) = Week_of_Year($ARGV[0], $ARGV[1], $ARGV[2]);
print "$wn of $yr\n";

The author of Date::Calc clearly recognises ISO 8601 as the standard to use.

DateTime code via Dave Rolsky may have ISO week numbers. Looks promising. May be part of The Perl DateTime Project.

I don't know, or have, Perl.

GNU R

A function in GNU R, derived by Theodore Lytras of Athens from the algorithm which I use.

isoweek <- function(x) {
  x.date<-as.Date(x)
  x.weekday<-as.integer(format(x.date,"%w"))
  x.weekday[x.weekday==0]=7
  x.nearest.thu<-x.date-x.weekday+4
  x.isoyear<-as.integer(substring(x.nearest.thu,1,4))
  x.isoweek<-(as.integer(x.nearest.thu-as.Date(paste(x.isoyear,"-1-1",sep="")))%/%7)+1
  paste(x.isoweek,x.isoyear,sep="/")
  }

HTML

HTML 5 introduced various input control types to handle dates/times. So far, I have only seen these implemented in Opera 9.64+. Generally, they do not work, or work badly.

See also at JavaScript Date and Time 3 : Input and Lengths.

Etc.

Non-ISO Week Number Programming

See in Calendar Weeks.

These examples are in JavaScript, but might be adapted for other languages. Adapting for Week-of-Month should be obvious.

Alas, Financial Years may not approximate to Calendar or ISO Week-Numbering Years; then, the code for Types 1 & 2 will need modification.

For specific uses, the fixed parameter values can be incorporated into the code.

In each case, the Date Object should represent a time near midnight, and is not altered; and Week Number is an array [Y, W, D] of Numbers.

Common Functions

This returns a string to represent non-standard week numbers.

Type 1. Year Number is always Calendar Year

Therefore, weeks 01 and 52 may be, and 53 will, be partial.

Enter a date :      

+ Week 1 has 7 days, entirely within the calendar year.
* As +, and Week 0 of 1-6 days is possible.
# Week 1 contains January 1, and may have 1-7 days.

Week 1 = Sun-Sat row seems to be what Microsoft VBS datepart("ww", D) gives.

Type 2. Every Week has Seven Days

Therefore, the Year Number can differ from the Calendar Year, and must be returned. ISO 8601 Week Numbers are a specific case.

These examples are for numberings in which Week 1 is more-or-less at the start of the calendar year.

Enter a date (blank for today) :    


* Week 1 is entirely within the calendar year.
# Week 1 contains January 1.
? See Programme Weeks -
~ Not best ISO 8601 routine.

2009-01-18 : a somewhat ad hoc fix has been applied, on finding that "BBC" was a week out. See comment in View Source. Check the previous line too.

Type 3. Week 01 begins on a Given Date

Therefore (unless that date is January 1st), the Year Number can differ from the Calendar Year, and Week 53 has one or two days.

Enter a date :  
Enter start of year :      

HM Revenue and Customs Tax Weeks

UK HMRC (HM Revenue and Customs; was HM Inland Revenue) Tax Weeks are a particular case of Type 3. See in Calendar Weeks. For VBScript, Excel, etc. see in VBScript Date and Time 2.

These string functions are in inc-date.js, take different arguments, and return text.

Those can easily be generalised for Month 1 and Week 1 starting on any date of the calendar year; in the code, 134 is just 4×32+6 for Month 4 Day 6.

Type 4. Absolute Week Numbers

These start the week count on Monday, Julian BC 4713-01-01, 00:00:00 GMT or local time (which corresponds with CJD). Modification for other starts is easy. Subtracting two such numbers gives what might be called the difference in ISO Week Number.

  Enter a date/time for new Date() :
 

See in Calendar Weeks.

Week Numbers in UNIX & C Utilities

Check the definitions carefully, and test the implementation well too. Consider the TZ environment variable, and determine whether it is fully used, including the transition dates. Check what day and what number begin each week, and what happens around the change in year number.

Any Week Number program whuch may use strftime .. %V for ISO 8601 Week Number should be tested for 2001-12-30 2001-12-31 2002-01-01 to see whether it shows an erroneous Week 53 for Mon 2001-12-31 only; cf. ISO Week Number Using DatePart.

Function strftime()

A 1999 man page in unixhelp (*) includes ISO formats %G %g for the year of %V the week number, %u for Mon=1..Sun=7. Also, it has other versions of week data.

GAWK

2002-03-30 ff.

An older GAWK man page (FSF, 1994-11-24) implied that the corresponding GAWK strftime() followed the ANSI C standard; and tests on a particular older GAWK show that it includes non-ISO week numbering. Beware.

2002-07-12 ff.

But Markus Kuhn has written :-
- ANSI C is obsolete. The current C standard is ISO C (1999), which has %V in strftime() for ISO 8601 week numbers.

Tests show that my old copy of GAWK (Gnu Awk (gawk) 2.15, patchlevel 6) does have %V but that it is implemented incorrectly for many year rollovers; and does not know %G which should give the proper year. I have code to fix this.

See also Calendar Weeks;
Date Miscellany I & Date Miscellany II;
Zeller; and Time Miscellany.
Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with Firefox and W3's Tidy.
This site, http://www.merlyn.demon.co.uk/, is maintained by me.
Head.