Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/RTL/Delphi/DateUtils    [ Add a report in this area ]  
Report #:  57477   Status: Open
WeekOfTheYear returns wrong week given any non-zero time on any Monday before 1899-12-26.
Project:  Delphi Build #:  11.0.2902.10471
Version:    11.2 Submitted By:   John Herbster
Report Type:  Basic functionality failure Date Reported:  1/26/2008 1:26:39 PM
Severity:    Infrequently encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   257402
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: 2
Description

DateUtils.WeekOfTheYear returns wrong week number if time is non-zero and day is any Monday before 1899-12-26.

WeekOfTheYear is for ISO 8601 weeks.  See Delphi Help and
  http://en.wikipedia.org/wiki/ISO_8601>
and
  http://en.wikipedia.org/wiki/ISO_week_date

Here are the D7, Build 8.1 results, using the program in Steps:
    DT  yyyy-mm-dd hh:nn  DoW  WoY
-6.50  1899-12-24 12:00  Sun  51
-5.00  1899-12-25 00:00  Mon  52
-5.25  1899-12-25 06:00  Mon  51 <== ERROR
-5.50  1899-12-25 12:00  Mon  51 <== ERROR
-5.75  1899-12-25 18:00  Mon  51 <== ERROR
-4.00  1899-12-26 00:00  Tue  52
where WoY is the returned WeekOfTheYear.

The following versions
  BDS2006 10.0.2558.35231  
  CRS2007 Delphi Win32 11.0.2902.10471 (December update)
also give the error.

I am reporting this for Dr. John Stockton:  
John Stockton, Surrey, UK.  ?@merlyn.demon.co.uk   Turnpike v6.05   IE 6
  news:comp.lang.javascript FAQ <URL:http://www.jibbering.com/faq/index.html>.
<URL:http://www.merlyn.demon.co.uk/js-index.htm> jscr maths, dates, sources.
<URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
  .  
My thanks to Paul Scott and Pieter Zijlstra for helping to verify the problem still exists.

JohnH, 2008-01-26
Steps to Reproduce:
On a form with a TButton and a TMemo, add the following OnClick handler and run it.

procedure TForm1.Button1Click(Sender: TObject);
{sub}procedure DoTest(DT: TDateTime);
  var WoY: word; S, DoW: string;
  const DayAbbrs: array [1..7] of string[3] =
        ('Mon','Tue','Wed','Thr','Fri','Sat','Sun');
  begin
  S   := SysUtils.FormatDateTime('yyyy-mm-dd hh:nn',DT);
  DoW := DayAbbrs[DateUtils.DayOfTheWeek(DT)];
  WoY := DateUtils.WeekOfTheYear(DT);
  Memo1.Lines.Add(Format(' %F  %S  %S  %D',[DT,S,DoW,WoY]));
  end;
begin
  Memo1.Lines.Add('    DT  yyyy-mm-dd hh:nn  DoW  WoY');
  DoTest(-6.50);
  DoTest(-5.00);
  DoTest(-5.25);
  DoTest(-5.50);
  DoTest(-5.75);
  DoTest(-4.00);
end;
Workarounds
WeekOfTheYear(DT) is sometimes wrong, but not if the value of DT is integer.  So WeekOfTheYear(Trunc(DT)) should be right.

--JohnH for JRS.
Attachment
None
Comments

John Herbster at 1/30/2008 4:07:29 PM -
Here are the routines recommended by Dr. John Stockton

For latest copy see
<http://www.merlyn.demon.co.uk/programs/del_wkno.pas> *
<http://www.merlyn.demon.co.uk/programs/sub_woty.pas>
* May need to add 'uses' whatever unit WeekOfTheYear lives in ?
--
John Stockton
www.merlyn.demon.co.uk/

////// RECOMMENDED ROUTINES

// 2008-01-17 - I derived the following routines from ISO principles
// The "canonical" are directly derived, whence FASTER are optimised
// All ISO weeks have 7 days, DN being Monday = 1 to Sunday = 7
// The ISO YN and WN of any Date are those of its nearest Thursday
// Weeks are numbered 01 to 52/53, week-dates written like YN-wWN-DN
// Definition: Week 01 of YN has the first Thursday of Calendar Year YN
// Therefore, Year January 4th of Y is in Week 01 of YN = Year
// Up to >= 2008-01-26, the FASTER versions have been getting faster
// Round is faster than Trunc

{$DEFINE FASTER} // or not, for which put a space before the $

{$IFDEF FASTER}

function DTofJan1(Yr : word) : TDateTime ;
// This is faster than EncodeDate, since it does not consider M & D
const Base = 2 - 1899*365 - 1899 div 4 + 1899 div 100 - 1899 div 400 ;
begin Dec(Yr) ;
  Result := Base + Yr*365 +   Yr div 4 -   Yr div 100 +   Yr div 400 ;
  end {DTofJan1} ;

procedure ISODTtoYWD(const DT : TDateTime ; out YN, WN, DN : word) ;
var NThu : TDateTime ; TDT : integer ;
begin // The fast non-canonical version.  1899.5 seems near optimum.
  TDT := Trunc(DT) ;
  DN := (TDT+7777775) mod 7 + 1 { DT : Mon=1 to Sun=7 } ;
  NThu := TDT + 4 - DN { NThu is the Nearest Thursday } ;
  YN := Round(1899.5 + NThu/365.2425) { estimated Year Number } ;
  if NThu < DTofJan1(YN) then Dec(YN) { corrected Year Number } ;
  WN := 1 + Trunc(NThu-DTofJan1(YN)) div 7 { Count of Thursdays } ;
  end {ISODTtoYWD} ; // Ideally, this would be a pure function

function ISOYWDtoDT(const YN, WN, DN : word) : TDateTime ;
var DT : TDateTime ; DW : integer ;
begin // The fast non-canonical version.
  DT := DTofJan1(YN) + 3 { YN Jan 4, which is in YN Week 1 } ;
  DW := (Round(DT)+7777775) mod 7 + 1 { DT : Mon=1 to Sun=7 } ;
  DT := DT - DW { DT to day before Week 1 } ;
  Result := DT + (WN-1)*7 + DN { increment for Weeks and Days } ;
  end {ISOYWDtoDT} ;

{$ELSE}

procedure ISODTtoYWD(const DT : TDateTime ; out YN, WN, DN : word) ;
var X : word ; NThu, Jan1 : TDateTime ;
begin // The canonical version.
  DN := 1 + (DayOfWeek(DT)+5) mod 7 { DT : Mon=1 to Sun=7 } ;
  NThu := Trunc(DT) + 4 - DN { NThu is the Nearest Thursday } ;
  DecodeDate(NThu, YN, X, X) { get Year Number of NThu } ;
  Jan1 := EncodeDate(YN, 1, 1) { January 1 of YN } ;
  WN := 1 + Trunc(NThu-Jan1) div 7 { Count of Thursdays } ;
  end {ISODTtoYWD} ; // Ideally, this would be a pure function

function ISOYWDtoDT(const YN, WN, DN : word) : TDateTime ;
var DT : TDateTime ; DW : integer ;
begin // The canonical version.
  DT := EncodeDate(YN, 1, 4) { YN Jan 4, which is in YN Week 1 } ;
  DW := 1 + (DayOfWeek(DT)+5) mod 7 { DT : Mon=1 to Sun=7 } ;
  DT := DT - DW { DT to day before Week 1 } ;
  Result := DT + (WN-1)*7 + DN { increment for Weeks and Days } ;
  end {ISOYWDtoDT} ;

{$ENDIF}

////// END RECOMMENDED ROUTINES

John Herbster at 11/28/2009 2:01:46 PM -
Why hase this taken so long to implement?  Dr. Stockton gave us his code and the testing can be done over the whole date range in a second or so of execution time.

Tomohiro Takahashi at 11/28/2009 9:18:18 PM -
Thanks for the notification.
I will check the internal status.

John Herbster at 9/24/2010 6:27:41 PM -
Sasan Adami, reported thet he ran the same code in RAD Studio XE (15.0.3890.34076):
     DT  yyyy-mm-dd hh:nn  DoW  WoY
  -6.50  1899-12-24 12:00  Sun  51
  -5.00  1899-12-25 00:00  Mon  52
  -5.25  1899-12-25 06:00  Mon  51
  -5.50  1899-12-25 12:00  Mon  51
  -5.75  1899-12-25 18:00  Mon  51
  -4.00  1899-12-26 00:00  Tue  52
So apparently it has not been fixed (yet).

Tomohiro Takahashi at 9/25/2010 6:12:15 AM -
Unfortunately, this is still Open.

Server Response from: ETNACODE01