web-dev-qa-db-ja.com

日付を指定して週番号を計算するにはどうすればよいですか?

日付がある場合、その年のその日の週番号を計算するにはどうすればよいですか?

たとえば、2008年の1月1日から1月6日までは第1週、1月7日から13日までは第2週なので、日付が2008年1月10日の場合、私の週番号は2になります。

アルゴリズムは私を始めるのに最適であり、サンプルコードも役立ちます-私はWindowsのC++で開発しています。

関連:

MS SQL Server 2005の日付から週番号を取得していますか?

25
Big GH

疑似コード:

int julian = getDayOfYear(myDate)  // Jan 1 = 1, Jan 2 = 2, etc...
int dow = getDayOfWeek(myDate)     // Sun = 0, Mon = 1, etc...
int dowJan1 = getDayOfWeek("1/1/" + thisYear)   // find out first of year's day
// int badWeekNum = (julian / 7) + 1  // Get our week# (wrong!  Don't use this)
int weekNum = ((julian + 6) / 7)   // probably better.  CHECK THIS LINE. (See comments.)
if (dow < dowJan1)                 // adjust for being after Saturday of week #1
    ++weekNum;
return (weekNum)

明確にするために、このアルゴリズムでは、次のように週に番号を付けることを前提としています。

S  M  T  W  R  F  S
            1  2  3    <-- week #1
4  5  6  7  8  9 10    <-- week #2
[etc.]

getDayOfWeek()およびgetDayOfYear()は、ほとんどの言語で標準の日付オブジェクト操作です。持っていない場合は、何曜日かを調べてから、既知の日付(1970年1月1日が一般的な日付)からカウントフォワードできます。

独自の日付カウントルーチンを実装する場合、100で割り切れる年は[〜#〜]ではない[〜#〜]うるう年、ただし400で割り切れない場合。1900年はうるう年ではなく、2000年はうるう年でした。ずっと前に作業する場合は、グレゴリオ暦とユリウス暦などをいじる必要があります。これに関する情報については、 Wikipedia を参照してください。

このリンク は、Windows/C++の日付/時刻関数について詳しく説明しています。

16
Olie

nth年の週の定義は妥当であるが、「標準」ではないことに注意してください。

ISO 8601は、日付、時刻、およびタイムゾーンの表現に関する標準を定義しています。月曜日から始まる週を定義します。また、年の第1週は、指定された年から少なくとも4日を含む週であると述べています。したがって、20xxの12月29日、30日、31日は20xyの第1週(xy = xx + 1)になり、20xyの1月1日、2日、3日はすべて20xxの最後の週になります。さらに、53週になる可能性があります。

[追加:C標準と `strftime()関数は、日曜日から始まる週と月曜日から始まる週を提供することに注意してください。 C標準が日曜日ベースの週の第0週の年数を規定していることは明らかではありません。 Emerick Rogulからの回答も参照してください。]

次に、興味深いテスト段階に入ります。53週目はいつですか? 1つの答えは2010年1月1日金曜日、つまり2009-W53です(実際、2010年1月3日日曜日です)。同様に、2005年1月1日の土曜日は2004-W53ですが、2006年1月1日の日曜日は2005-W52です。

これは、実際にはInformix SPL(ストアドプロシージャ言語)に含まれている次のコードのコメントからの抜粋ですが、詳細な説明がなくても読み取り可能です(おそらく書き込みはできません)。 '||' operatorはSQL文字列連結演算で、日曜日は0日、月曜日は1日、...土曜日は6日です。コメントには、規格の関連テキストを含む広範な注記があります。 1行のコメントは「_--_」で始まります。おそらく複数行のコメントは '_{_'で始まり、次の '_}_'で終わります。

_-- @(#)$Id: iso8601_weekday.spl,v 1.1 2001/04/03 19:34:43 jleffler Exp $
--
-- Calculate ISO 8601 Week Number for given date
-- Defines procedure: iso8601_weekday().
-- Uses procedure: iso8601_weeknum().

{
According to a summary of the ISO 8601:1988 standard "Data Elements and
Interchange Formats -- Information Interchange -- Representation of
dates and times":

    The week notation can also be extended by a number indicating the
    day of the week.  For example the day 1996-12-31 which is the
    Tuesday (day 2) of the first week of 1997 can also be written as

        1997-W01-2 or 1997W012

    for applications like industrial planning where many things like
    shift rotations are organized per week and knowing the week number
    and the day of the week is more handy than knowing the day of the
    month.

This procedure uses iso8601_weeknum() to format the YYYY-Www part of the
date, and appends '-d' to the result, allowing for Informix's coding of
Sunday as day 0 rather than day 7 as required by ISO 8601.
}

CREATE PROCEDURE iso8601_weekday(dateval DATE DEFAULT TODAY) RETURNING CHAR(10);
    DEFINE rv CHAR(10);
    DEFINE dw CHAR(4);
    LET dw = WEEKDAY(dateval);
    IF dw = 0 THEN
            LET dw = 7;
    END IF;
    RETURN iso8601_weeknum(dateval) || '-' || dw;
END PROCEDURE;
-- @(#)$Id: iso8601_weeknum.spl,v 1.1 2001/02/27 20:36:25 jleffler Exp $
--
-- Calculate ISO 8601 Week Number for given date
-- Defines procedures: day_one_week_one() and iso8601_weeknum().

{
According to a summary of the ISO 8601:1988 standard "Data Elements and
Interchange Formats -- Information Interchange -- Representation of
dates and times":

    In commercial and industrial applications (delivery times,
    production plans, etc.), especially in Europe, it is often required
    to refer to a week of a year.  Week 01 of a year is per definition
    the first week which has the Thursday in this year, which is
    equivalent to the week which contains the fourth day of January.  In
    other words, the first week of a new year is the week which has the
    majority of its days in the new year.  Week 01 might also contain
    days from the previous year and the week before week 01 of a year is
    the last week (52 or 53) of the previous year even if it contains
    days from the new year.  A week starts with Monday (day 1) and ends
    with Sunday (day 7).  For example, the first week of the year 1997
    lasts from 1996-12-30 to 1997-01-05 and can be written in standard
    notation as

        1997-W01 or 1997W01

    The week notation can also be extended by a number indicating the
    day of the week.  For example the day 1996-12-31 which is the
    Tuesday (day 2) of the first week of 1997 can also be written as

        1997-W01-2 or 1997W012

    for applications like industrial planning where many things like
    shift rotations are organized per week and knowing the week number
    and the day of the week is more handy than knowing the day of the
    month.

Referring to the standard itself, section 3.17 defines a calendar week:

    week, calendar: A seven day period within a calendar year, starting
    on a Monday and identified by its ordinal number within the year;
    the first calendar week of the year is the one that includes the
    first Thursday of that year.  In the Gregorian calendar, this is
    equivalent to the week which includes 4 January.

Section 5.2.3 "Date identified by Calendar week and day numbers" states:

    Calendar week is represented by two numeric digits.  The first
    calendar week of a year shall be identified as 01 [...]

    Day of the week is represented by one decimal digit.  Monday
    shall be identified as day 1 of any calendar week [...]

Section 5.2.3.1 "Complete representation" states:

    When the application clearly identifies the need for a complete
    representation of a date identified by calendar week and day
    numbers, it shall be one of the alphanumeric representations as
    follows, where CCYY represents a calendar year, W is the week
    designator, ww represents the ordinal number of a calendar week
    within the year, and D represents the ordinal number within the
    calendar week.

    Basic format: CCYYWwwD
        Example: 1985W155
    Extended format: CCYY-Www-D
        Example: 1985-W15-5

Both the summary and the formal definition are intuitively clear, but it
is not obvious how to translate it into an algorithm.  However, we can
deal with the problem by exhaustively enumerating the seven options for
the day of the week on which 1st January falls (with actual year values
for concreteness):

    1st January 2001 is Monday    => Week 1 starts on 2001-01-01
    1st January 2002 is Tuesday   => Week 1 starts on 2001-12-31
    1st January 2003 is Wednesday => Week 1 starts on 2002-12-30
    1st January 2004 is Thursday  => Week 1 starts on 2003-12-29
    1st January 2010 is Friday    => Week 1 starts on 2010-01-04
    1st January 2005 is Saturday  => Week 1 starts on 2005-01-03
    1st January 2006 is Sunday    => Week 1 starts on 2006-01-02

(Cross-check: 1st January 1997 was a Wednesday; the summary notes state
that week 1 of 1997 started on 1996-12-30, which is consistent with the
table derived for dates in the first decade of the third millennium
above).

When working with the Informix DATE types, bear in mind that Informix
uses WEEKDAY values 0 = Sunday, 1 = Monday, 6 = Saturday.  When the
weekday of the first of January has the value in the LH column, you need
to add the value in the RH column to the 1st of January to obtain the
date of the first day of the first week of the year.

    Weekday         Offset to
    1st January     1st day of week 1

    0               +1
    1                0
    2               -1
    3               -2
    4               -3
    5               +3
    6               +2

This can be written as MOD(11-w,7)-3 where w is the (Informix encoding
of the) weekday of 1st January and the value 11 is used to ensure that
no negative values are presented to the MOD operator.  Hence, the
expression for the date corresponding to the 1st day (Monday) of the 1st
week of a given year, yyyy, is:

    d1w1 = MDY(1, 1, yyyy) + MOD(11 - WEEKDAY(MDY(1,1,yyyy)), 7) - 3

This expression is encapsulated in stored procedure day_one_week_one:
}

CREATE PROCEDURE day_one_week_one(yyyy INTEGER) RETURNING DATE;
    DEFINE jan1 DATE;
    LET jan1 = MDY(1, 1, yyyy);
    RETURN jan1 + MOD(11 - WEEKDAY(jan1), 7) - 3;
END PROCEDURE;

{
Given this date d1w1, we can calculate the week number of any other date
in the same year as:

    TRUNC((dateval - d1w1) / 7) + 1

The residual issues are ensuring that the wraparounds are correct.  If
the given date is earlier than the start of the first week of the year
that contains it, then the date belongs to the last week of the previous
year.  If the given date is on or after the start of the first week of
the next year, then the date belongs to the first week of the next year.

Given these observations, we can write iso8601_weeknum as shown below.
(Beware: iso8601_week_number() is too long for servers with the
18-character limit; so is day_one_of_week_one()).

Then comes the interesting testing phase -- when do you get week 53?
One answer is on Friday 1st January 2010, which is in 2009-W53 (as,
indeed, is Sunday 3rd January 2010).  Similarly, Saturday 1st January
2005 is in 2004-W53, but Sunday 1st January 2006 is in 2005-W52.
}

CREATE PROCEDURE iso8601_weeknum(dateval DATE DEFAULT TODAY) RETURNING CHAR(8);
    DEFINE rv CHAR(8);
    DEFINE yyyy CHAR(4);
    DEFINE ww CHAR(2);
    DEFINE d1w1 DATE;
    DEFINE tv DATE;
    DEFINE wn INTEGER;
    DEFINE yn INTEGER;
    -- Calculate year and week number.
    LET yn = YEAR(dateval);
    LET d1w1 = day_one_week_one(yn);
    IF dateval < d1w1 THEN
        -- Date is in early January and is in last week of prior year
        LET yn = yn - 1;
        LET d1w1 = day_one_week_one(yn);
    ELSE
        LET tv = day_one_week_one(yn + 1);
        IF dateval >= tv THEN
            -- Date is in late December and is in the first week of next year
            LET yn = yn + 1;
            LET d1w1 = tv;
        END IF;
    END IF;
    LET wn = TRUNC((dateval - d1w1) / 7) + 1;
    -- Calculation complete: yn is year number and wn is week number.
    -- Format result.
    LET yyyy = yn;
    IF wn < 10 THEN
        LET ww = '0' || wn;
    ELSE
        LET ww = wn;
    END IF
    LET rv = yyyy || '-W' || ww;
    RETURN rv;
END PROCEDURE;
_

完全を期すために、上記のday_one_week_one()関数を使用すると、逆関数も簡単に記述できます。

_-- @(#)$Id: ywd_date.spl,v 1.1 2012/12/29 05:13:27 jleffler Exp $
-- @(#)Create ywd_date() and ywdstr_date() stored procedures

-- Convert a date in format year, week, day (ISO 8601) to DATE.
-- Two variants:
-- ywd_date(yyyy SMALLINT, ww SMALLINT, dd SMALLINT) RETURNING DATE;
-- ywdstr_date(ywd CHAR(10)) RETURNING DATE;

-- NB: If week 53 is supplied, there is no check that the year had week
--     53 (GIGO).
-- NB: If year yyyy is a leap year and yyyy-01-01 falls on Wed (3) or
--     Thu (4), there are 53 weeks in the year.
-- NB: If year yyyy is not a leap year and yyyy-01-01 falls on Thu (4),
--     there are 53 weeks in the year.

CREATE PROCEDURE ywd_date(yyyy SMALLINT, ww SMALLINT, dd SMALLINT) RETURNING DATE AS date;
    DEFINE d DATE;
    -- Check ranges
    IF yyyy < 1 OR yyyy > 9999 OR ww < 1 OR ww > 53 OR dd < 1 OR dd > 7 THEN
        RETURN NULL;
    END IF;
    LET d = day_one_week_one(yyyy);
    LET d = d + (ww - 1) * 7 + (dd - 1);
    RETURN d;
END PROCEDURE;

-- Input: 2012-W52-5
CREATE PROCEDURE ywdstr_date(ywd CHAR(10)) RETURNING DATE AS date;
    DEFINE yyyy SMALLINT;
    DEFINE ww   SMALLINT;
    DEFINE dd   SMALLINT;
    LET yyyy = SUBSTR(ywd,  1, 4);
    LET ww   = SUBSTR(ywd,  7, 2);
    LET dd   = SUBSTR(ywd, 10, 1);
    RETURN ywd_date(yyyy, ww, dd);
END PROCEDURE;

CREATE TEMP TABLE test_dates(d DATE);
INSERT INTO test_dates VALUES('2011-12-28');
INSERT INTO test_dates VALUES('2011-12-29');
INSERT INTO test_dates VALUES('2011-12-30');
INSERT INTO test_dates VALUES('2011-12-31');
INSERT INTO test_dates VALUES('2012-01-01');
INSERT INTO test_dates VALUES('2012-01-02');
INSERT INTO test_dates VALUES('2012-01-03');
INSERT INTO test_dates VALUES('2012-01-04');
INSERT INTO test_dates VALUES('2012-01-05');
INSERT INTO test_dates VALUES('2012-01-06');
INSERT INTO test_dates VALUES('2012-01-07');

SELECT d, iso8601_weeknum(d), iso8601_weekday(d), ywdstr_date(iso8601_weekday(d))
  FROM test_dates
 ORDER BY d;
_

コメントで述べたように、コードは、年が52週しか受け入れない場合でも、週53の日付を受け入れます。

28

C標準ライブラリの時間関数を使用して週数を計算することを強くお勧めします。具体的には、strftime関数には、日付を分解して(struct tm) フォーマット。これを説明する小さなサンプルプログラムを次に示します。

#include <stdio.h>
#include <string.h>
#include <time.h>

int
main(void)
{
  struct tm tm;
  char timebuf[64];

  // Zero out struct tm
  memset(&tm, 0, sizeof tm);

  // November 4, 2008 11:00 pm
  tm.tm_sec = 0;
  tm.tm_min = 0;
  tm.tm_hour = 23;
  tm.tm_mday = 4;
  tm.tm_mon = 10;
  tm.tm_year = 108;
  tm.tm_isdst = -1;

  // Call mktime to recompute tm.tm_wday and tm.tm_yday
  mktime(&tm);

  if (strftime(timebuf, sizeof timebuf, "%W", &tm) != 0) {
    printf("Week number is: %s\n", timebuf);
  }

  return 0;
}

このプログラムからの出力(LinuxではGCC、WindowsではMicrosoft Visual Studio 2005 SP1でコンパイル)は次のとおりです。

Week number is: 44

Strftime here について詳しく知ることができます。

8
Emerick Rogul

申し訳ありませんが、私はここに新しく、回答自体にコメントすることはできませんが、チェックマークの付いた回答からの疑似コードは、正しくありません。

疑似コード:

int julian = getDayOfYear(myDate)  // Jan 1 = 1, Jan 2 = 2, etc...
int dow = getDayOfWeek(myDate)     // Sun = 0, Mon = 1, etc...
int dowJan1 = getDayOfWeek("1/1/" + thisYear)   // find out first of year's day
int weekNum = (julian / 7) + 1     // Get our week#
if (dow < dowJan1)                 // adjust for being after Saturday of week #1
    ++weekNum;
return (weekNum)

「元日」ではなく、昨年の最終日を探す必要があります。

getDayOfWeek("12/31/" + thisYear-1)

の代わりに正しいでしょう

getDayOfWeek("1/1/" + thisYear) 

これを行わない場合、昨年の最後の平日(月曜日など)は常に1週間先になります。

2
Pascalo

両方向に変換するには、こちらを参照してください: ISOの週日のウィキペディアの記事

2
Robert L

struct tmは「故障した時間」を表すために使用され、少なくとも以下のフィールドがあります。

 int tm_sec秒[0,60]。 
 int tm_min Minutes [0,59]。 
 int tm_hour Hour [0,23]。 
 int tm_mday月の日[1,31]。 
 int tm_mon月[0,11]。 
 int tm_year 1900年からの年数。
 int tm_wday曜日[0,6](日曜日= 0)。 
 int tm_yday年間通算日[0,365]。 
 int tm_isdst夏時間フラグ。 

Localtime()関数を使用して、time_tからstruct tmを作成できます。

Mktime()関数を使用して、struct tmからtime_tを作成できます。

Struct tmの最も優れた点は、年のメンバーに24を追加するようなことができ、mktime()を呼び出すと、2年後のtime_tを取得できることです(これは、そのメンバーのいずれでも機能するため、たとえば、時間を1000ずつ増やしてから、41日後のtime_tを取得できます)...

2
dicroce

howardhinnant.github.io/iso_week.htmliso_week.hを使用すると、簡単に実現できます。

#include <iostream>
#include "iso_week.h"

int main() {
    using namespace iso_week;
    using namespace std::chrono;
    // Get the current time_point and floor to convert to the sys_days:
    auto today = floor<days>(system_clock::now());
    // Convert from sys_days to iso_week::year_weeknum_weekday format
    auto yww = year_weeknum_weekday{today};
    // Print current week number of the year
    std::cout << "The current week of " << yww.year() << " is: " 
              << yww.weeknum() << std::endl;

    // Set any day
    auto any_day = 2014_y/9/28;
    // Get week of `any_day`
    std::cout << "The week of " << any_day.year() << " on `any day` was: " 
              << any_day.weeknum() << std::endl;   
}

そして出力は:

The current week of 2019 is: W18
The week in 2014 on `any day` was: W09
1
Remis

非ISO 8601である私の定義(私の目的には十分であり、高速です):

// week number of the year
// (Monday as the first day of the week) as a decimal number [00,53].
// All days in a new year preceding the first Monday are considered to be in week 0.
int GetWeek(const struct tm& ts)
{
    return (ts.tm_yday + 7 - (ts.tm_wday ? (ts.tm_wday - 1) : 6)) / 7;
}
1
Marius

私の想定では、オリエの回答に示されているように、年の最初の週には最大7日が含まれる可能性があります。このコードは、週が日曜日とは別の日に始まる文化を扱いません。それは世界の大部分です。

tm t = ... //the date on which to find week of year

int wy = -1;

struct tm t1;
t1.tm_year = t.tm_year;
t1.tm_mday = t1.tm_mon = 1; //set to 1st of January
time_t tt = mktime(&t1); //compute tm

//remove days for 1st week
int yd = t.tm_yday - (7 - t1.tm_wday);
if(yd <= 0 ) //first week is now negative
  wy = 0;
else
  wy = (int)std::ceil( (double) ( yd/7) ); //second week will be 1 
0
pasx

これは私の解決策ですが、C++ではありません

NoOfDays = (CurrentDate - YearStartDate)+1
IF NoOfDays MOD 7 = 0 Then
    WeekNo = INT(NoOfDays/7)
ELSE
    WeekNo = INT(NoOfDays/7)+1
END 
0
Arun Rao
public int GetWeekOfYear(DateTime todayDate)
{
    int days = todayDate.DayOfYear;
    float result = days / 7;
    result=result+1;
    Response.Write(result.ToString());
    return Convert.ToInt32(result);
}

この関数にパラメーターとして現在の日付を渡すだけです。次に、現在の週番号を取得します。それがあなたの問題を解決することを願っています。どんな提案も大歓迎です。

0
raghu
time_t t = time(NULL);
tm* timePtr = localtime(&t);
double day_of_year=timePtr->tm_yday +1 ; // 1-365
int week_of_year =(int) ceill(day_of_year/7.0);
0
MINH_NV

Boostはgregorian :: date :: week_number()を提供します http://www.boost.org/doc/libs/1_38_0/doc/html/boost/gregorian/date.html および http://www.boost.org/doc/libs/1_38_0/boost/date_time/gregorian/greg_date.hpp

しかし、週番号と一致する年番号を取得する方法がわかりません(その日付の暦年とは異なる場合があります)。

0
danio

Gmtimeまたはlocaltimeを使用して、日曜日(つまり、曜日)からの日数と1月1日からの日数を計算します(後者の場合、1月1日は「0」であることに注意してください)。

任意のビットは、第1週の開始日を決定します。通常、1月1日の曜日にのみ依存します。もちろん、gmtimeからの2つの情報から計算できます。次に、7つの可能性についてテーブルルックアップを使用します。ルールをコーディングするよりもおそらく簡単です。

たとえば、Outlookでは第1週が木曜日を含む最初の週であるという標準を使用していると思います。したがって、1月1日が日曜日の場合、第1週の最初の日は1月1日または0日になります。残りの可能性は月曜日、-1です。火曜日、-2;水曜日、-3;木曜日、-4;金曜日、2;土曜日、1。

負の数に注意してください。「第1週の日曜日」は、7ケースのうち4ケースには実際には存在しませんが、前年にさかのぼって1日だったとしたら、正しい答えが得られます。

それが得られると、それと日付の間の日数から週数がわかります。7で割り、1を加えます。

とは言っても、Outlookが使用しているのと同じ週番号を提供するWindows APIがどこかにあると思います。私はそれが何であるかを知りません、そしてもちろん、あなたの第1週のルールがOutlookのものと異なるなら、それはおそらくあまり役​​に立ちません。

テストされていないコード:

int firstdays[7] = { 0, -1, -2, -3, -4, 2, 1 }; // or some other Week 1 rule
struct tm breakdown;
time_t target = time_you_care_about();
_gmtime_s(&breakdown,&target);
int dayofweek = breakdown.tm_wday;
int dayofyear = breakdown.tm_yday;

int jan1wday = (dayofweek - dayofyear) % 7;
if (jan1wday < 0) jan1wday += 7;

int week1first = firstdays[jan1wday];
if (dayofyear < week1first) return 0;
return ((dayofyear - week1first)/7) + 1;

とにかく、そのようなもの。

0
Steve Jessop