德尔福周数函数基于系统周开始

时间:2015-05-05 23:45:00

标签: windows delphi week-number

DateUtils.WeekOfTheYear功能很棒,但它使用一周的ISO 8601标准定义。也就是说,一周被认为是在星期一开始,在星期日结束。我需要一个类似的功能,根据一周开始的系统设置确定周数。或者至少是周日或星期一的开始,比如MySQL的周功能。任何人都有这样的功能吗?

4 个答案:

答案 0 :(得分:2)

ISO-8601 在这些计算的规格中包含的不仅仅是一周的第一天。例如,还有一些规则决定了一年中的第一周。

目前尚不清楚您所寻找的是否是复制 ISO-8601 计算的功能,其中这些规则原封不动且仅在一周的第一天变化,或直接相当于 MySQL WEEK()功能,或其他类似功能(并未完全定义)。

值得注意的是, MySQL WEEK()函数接受一个参数,该参数不确定标记一周开始的任意日期,而是指示是否为周日或周一将与更改确定计算结果的其他一些规则一起使用。

相比之下,Windows本身第一天的系统设置可以是用户希望的一周中的任何一天 - 周一,周二,周三,周四,周五,周六或周日。

我在下面提供的实现是一个简单的计算(有些可能称之为天真),它只是根据星期之间或部分期间之间经过的值返回值 0..53 。指定的日期和该日期发生的年份的开始。

包含指定日期的年份 1月1日的那一周被视为第0周。

因此,如果 1月1日 星期日和"周开始#34;被定义为 Monday 然后:

Sun, 01-Jan  = Week 0
Mon, 02-Jan  = Week 1
..
Sun, 08-Jan  = Week 1
Mon, 09-Jan  = Week 2
..
etc

实施

我已将实施分为两个不同的部分。

第一个( WeeksSince01Jan )接受一个日期和一个参数,表示在计算中被视为一周中第一天的星期几。

此参数采用 TSYSDayOfWeek 值 - 一个枚举,使得每天的序数值对应于系统区域设置中一周中几天使用的值。 ( RTL DayOfWeek 函数返回的值使用不同的值,在此代码中定义为 TRTLDayOfWeek 。 / p>

实现的第二部分提供 LocaleWe​​eksSince01Jan ,以演示获取当前用户的第一天定义的语言环境。然后,只需通过电话 WeeksSince01Jan

type
  TSYSDayOfWeek = (sdMon, sdTue, sdWed, sdThu, sdFri, sdSat, sdSun);
  TRTLDayOfWeek = 1..7;    // Sun -> Sat


function WeeksSince01Jan(const aDate: TDateTime;
                         const aFirstDayOfWeek: TSYSDayOfWeek): Word;
const
  LOCALE_DOW : array[TRTLDayOfWeek] of TSYSDayOfWeek = (sdSun, sdMon, sdTue, sdWed, sdThu, sdFri, sdSat);
var
  y, m, d: Word;
  dayOfYearStart: TSYSDayOfWeek;
  dtYearStart: TDateTime;
  dtStartOfFirstWeekInYear: TDateTime;
  iAdjust: Integer;
begin
  // Get the date for the first day of the year and determine which
  //  day of the week (Mon-Fri) that was

  DecodeDate(aDate, y, m, d);
  dtYearStart     := EncodeDate(y, 1, 1);
  dayOfYearStart  := LOCALE_DOW[DayOfWeek(dtYearStart)];

  // Week calculation is simply the number of 7 day periods
  //  elapsed since the start of the year to the specified date,
  //  adjusted to reflect any 'offset' to the specified first day of week.

  iAdjust := Ord(dayOfYearStart) - Ord(aFirstDayOfWeek);
  result  := (((Trunc(aDate) + iAdjust) - Trunc(dtYearStart)) div 7);
end;


function LocaleWeeksSince01Jan(const aDate: TDateTime): Word;
var
  localeValue: array[0..1] of Char;
  firstDayOfWeek: TSYSDayOfWeek;
begin
  // Get the system defined first day of the week

  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, localeValue, SizeOf(localeValue));
  firstDayOfWeek := TSYSDayOfWeek(Ord(localeValue[0]) - Ord('0'));

  result := WeeksSince01Jan(aDate, firstDayOfWeek);
end;

如果您有更复杂的规则来根据该周的天数等确定一年的第0周或第1周,那么您需要相应地修改此实施。在当前的实施中,没有尝试满足这些需求。

进行测试

以下代码可用作测试输出的基础(使用系统定义的一周中的第一天):

const
  YEAR = 2012;
var
  d: Integer;
  dt: TDateTime;
  wk: Word;
begin
  List.Items.Clear;

  dt := EncodeDate(YEAR, 1, 1) - 7;

  for d := 1 to 380 do
  begin
    dt := dt + 1;
    wk := LocaleWeeksSince01Jan(dt);
    List.Items.Add(Format('%s, %s = week %d', [ShortDayNames[DayOfWeek(dt)],
                                               DateToStr(dt),
                                               wk]));
  end;

列表是对 TListbox 的引用。

更改的值,以产生一系列结果,涵盖指定年份的所有日期+/-另外7/8天,以说明年末结果的变化前几年和后几年。

注意: 2012年是一年,它展示了在该年度返回日期的可能性,涵盖了所有潜在结果, 0..53

答案 1 :(得分:1)

如果您只对星期日而非星期一开始的一周感兴趣,则可以在将DateTime值添加到DateUtils.WeekOfTheYear之前将其减去1天。

编辑: 对David Heffernan的回应评论:

  

想象一下从1月1日开始减去1时会发生什么

这取决于1月1日的哪一天

来自Embarcadero文档:http://docwiki.embarcadero.com/Libraries/XE8/en/System.DateUtils.WeekOfTheYear

  

AYear返回指定周的发生年份。注意   这可能与AValue的年份不同。这是因为   一年中的第一周被定义为具有四个或更多的第一周   那一年的日子。这意味着,如果是第一个日历日   年是周五,周六或周日,然后是前三,二,   或者在日历年的一天,WeekOfTheYear返回上周   去年。同样,如果是一年中的最后一个日历日   是周一,周二或周三,然后是最后一个,两个,或   在日历年的三天,WeekOfTheYear返回1(第一个   下一个日历年的一周)。

因此,如果一周从星期日而不是星期一开始,那么这意味着星期开始和结束日仅向后移动一天。

因此,对于这种情况,最好使用具有附加变量参数的过度版本,本周所属的年份将存储在该变量参数中。

答案 2 :(得分:0)

我结合了Deltics'很好的代码与SilverWarior的简单想法创建一个WeekOfYear函数来处理系统周开始日。

type
  TSYSDayOfWeek = (sdMon, sdTue, sdWed, sdThu, sdFri, sdSat, sdSun);

function LocaleWeekOfTheYear(dte: TDateTime): word;
var
  localeValue: array[0..1] of Char;
  firstDayOfWeek: TSYSDayOfWeek;
  yearOld,yearNew: word;
  dteNew: TDateTime;
begin
  // Get the system defined first day of the week
  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, localeValue, SizeOf(localeValue));
  firstDayOfWeek := TSYSDayOfWeek(Ord(localeValue[0]) - Ord('0'));
  yearOld:= Year(dte);
  if (firstDayOfWeek=sdSun) then
    dteNew:= dte-1
  else 
    dteNew:= dte+Ord(firstDayOfWeek);
  yearNew:= Year(dteNew);
  if (yearOld=yearNew) then
    dte:= dteNew;
  Result:= WeekOfTheYear(dte);
end;

答案 3 :(得分:0)

要使一周的第一天为星期六,我将“ Now date”的值加2。

例如WeekOf(Now + 2):使得第一天为星期六, WeekOf(Now + 2):使第一天为星期日, WeekOf(Now + 0):第一天是星期一,