質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.63%

指定日が第何週目かを返したい

解決済

回答 6

投稿

  • 評価
  • クリップ 1
  • VIEW 3,609

webhajime

score 11

 前提・実現したいこと

53週目の追加とうるう年からずれる週を直したい。
たとえば表示週テストにて「console.log(schedule(new Date(2022,1,21)))」にすると、
"2022年2月21日(月)の週は第2週です。第2週は2022年2月21日(月)~2022年2月27日(日)です。"
となってしまいます。うるう年が来るたびに1週ずつずれてしまうのでどうにかしたいです。
一応1週目であるべきものを記載しておきます。とても困ってます。宜しくお願いします。

1週目にしたい日付
2018/2/19
2019/2/18
2020/2/17
2021/2/15 ---ここまでは問題なし
2022/2/21 ---ここから2週目になってしまう
2023/2/20
2024/2/19
2025/2/17
2026/2/16
2027/2/15
2028/2/21 ---ここから3週目になってしまう


 発生している問題・エラーメッセージ

2021年までは問題なく表示されるが、うるう年が来るたびにずれが生じる。

 該当のソースコード

const referenceDate = new Date(2017, 1, 20) //週起算日
const cycle = 364 //週のサイクル
const dateT = ["日","月","火","水","木","金","土"]


/**
 * 日付から週を取得
 */
function date2Nweek(date) {

  let diff = date.getTime() - referenceDate.getTime()
  let diffDays = Math.floor(diff / (1000*60*60*24))

  if (diffDays < 0) {
    diffDays = (cycle - Math.abs(diffDays) % cycle) % cycle
  }else if (diffDays >= cycle) {
    diffDays = diffDays % cycle
  }
  let nWeek = Math.floor(diffDays / 7) + 1
  let DayOfTheWeek = dateT[date.getDay()]

  return [nWeek, diffDays]
}

/**
 * 指定日の週を取得

 * @param date 対象の日付:dateObject
 * @param nWeek 週数:number
 * @param diffDays 基準日からの差日数:string
 */
function nWeek2Period(date, nWeek, diffDays) {

  let targetWeek = new Date(date)
  targetWeek.setDate(date.getDate() + ((nWeek-1)*7 - diffDays))
  let endTargetWeek = new Date(targetWeek)
  endTargetWeek.setDate(endTargetWeek.getDate() + 6)

  return [targetWeek, endTargetWeek]
}

/**
 * 逆引き
 * @param nWeek 対象の週:number
 * @param flg +-年度:number
 */
function calcSchedule(nWeek, flg = 0) {
  let date = new Date()
  //現在の週を取得
  let retNweek = date2Nweek(date)
  //表示年度の基準日を取得
  date.setDate(date.getDate() + (flg*cycle - retNweek[1]))
  //表示する週の日付を取得
  date.setDate(date.getDate() + 7*(nWeek-1))
  //表示する日付の週を取得
  retNweek = date2Nweek(date)
  let retPeriod = nWeek2Period(date, retNweek[0], retNweek[1])

  let retMessage  = `表示週第${retNweek[0]}週は\n`
                  + `${retPeriod[0].getFullYear()}${retPeriod[0].getMonth()+1}${retPeriod[0].getDate()}日(${dateT[retPeriod[0].getDay()]})`
                  + `~${retPeriod[1].getFullYear()}${retPeriod[1].getMonth()+1}${retPeriod[1].getDate()}日(${dateT[retPeriod[1].getDay()]})です。`
  return retMessage
}

/**
 * 表示週
 * @param date 対象の日付:dateObject
 */
function schedule(date = new Date()) {

  let retNweek = date2Nweek(date)
  let retPeriod = nWeek2Period(date, retNweek[0], retNweek[1])

  let retMessage  = `${date.getFullYear()}${date.getMonth()+1}${date.getDate()}日(${dateT[date.getDay()]})の週は第${retNweek[0]}週です。\n`
                  + `第${retNweek[0]}週は`
                  + `${retPeriod[0].getFullYear()}${retPeriod[0].getMonth()+1}${retPeriod[0].getDate()}日(${dateT[retPeriod[0].getDay()]})`
                  + `~${retPeriod[1].getFullYear()}${retPeriod[1].getMonth()+1}${retPeriod[1].getDate()}日(${dateT[retPeriod[1].getDay()]})です。`

  return retMessage
}


/**
 * Example
 *
 */
//表示週テスト
// let d = new Date(2019,1,18)
console.log(schedule(new Date(2028,1,21)))

//逆引き表示週テスト
// console.log(calcSchedule(51, 0))
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • x_x

    2018/03/30 15:55

    現状では364日周期となっていてうるう年でなくてもずれていくと思うのですが、この辺の仕様はどうするのでしょうか?

    キャンセル

  • webhajime

    2018/03/30 16:03 編集

    52週で割り切れる為の記述になってるんですよね。53週が1週目になってしまうことによりずれてしまいます。53週目を毎年表示させるにはどうすればいいか悩んでいます。

    キャンセル

  • think49

    2018/03/30 17:36

    一般的な感覚では何周目といえば、1日を1週目として数えますが、本件はそうではないようです。要求仕様を詳しく書いてみてはどうでしょうか。

    キャンセル

  • think49

    2018/03/30 17:47

    「指定した基準日を1週目とする。基準日と年月が同一なら基準日から何周目か数える。年月が変わっても日付さえ合致するなら1週目とする。」サンプルから推定される仕様です。

    キャンセル

回答 6

checkベストアンサー

+6

jQueryUIに$.datepicker.iso8601Week()というメソッドがあります。
ソースコードを転記しますので、参考にされてはいかがでしょうか。

    iso8601Week: function( date ) {
        var time,
            checkDate = new Date( date.getTime() );

        // Find Thursday of this week starting on Monday
        checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );

        time = checkDate.getTime();
        checkDate.setMonth( 0 ); // Compare with Jan 1
        checkDate.setDate( 1 );
        return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
}


jquery-ui/datepicker.js at 17404ced478a235651513fa7bef3473ef1b039e8 · jquery/jquery-ui · GitHub

 追記

おそらく、「2月21日が含まれる週が第1週」という仕様だと思います。

function schedule(date){

  var getMonday = function(date) {
    return new Date( date.getTime() - ( date.getDay() - 1 ) * 24 * 60 * 60 * 1000 );
  }

  date.setHours(0,0,0);

  var tempDate = new Date( date - 24 * 60 * 60 * 1000 );
  var monday = getMonday( tempDate );
  var sunday = new Date( monday.getTime() + 6 * 24 * 60 * 60 * 1000 );

  tempDate = new Date(date);
  tempDate.setMonth(1,20);
  firstDate = getMonday( tempDate ); 
  if ( firstDate > date ) {
    tempDate.setMonth(-11,20);
    firstDate = getMonday( tempDate );   
  }

  var weekNumber = Math.floor( ( date - firstDate ) / ( 24 * 60 * 60 * 1000 ) / 7 ) + 1;

  return `${
    date.toLocaleDateString('ja',{year:'numeric'})
  }${
    date.toLocaleDateString('ja',{month:'numeric'})
  }${
    date.toLocaleDateString('ja',{day:'numeric'})
  }(${
    date.toLocaleDateString('ja',{weekday:'short'})
  })の週は第${weekNumber}週です。第${weekNumber}週は${
    monday.toLocaleDateString('ja',{year:'numeric'})
  }${
    monday.toLocaleDateString('ja',{month:'numeric'})
  }${
    monday.toLocaleDateString('ja',{day:'numeric'})
  }(月)~${
    sunday.toLocaleDateString('ja',{year:'numeric'})
  }${
    sunday.toLocaleDateString('ja',{month:'numeric'})
  }${
    sunday.toLocaleDateString('ja',{day:'numeric'})
  }(日)です。`;
}
[
  [2018,1,18],
  [2018,1,19],
  [2019,1,18],
  [2020,1,17],
  [2021,1,15],
  [2022,1,21],
  [2027,1,14],
  [2027,1,15],
  [2028,1,20],
  [2028,1,21],
].forEach(function(e){
  console.log( schedule(new Date(...e)) );  
});

/*
2018年2月18日(日)の週は第52週です。第52週は2018年2月12日(月)~2018年2月18日(日)です。
2018年2月19日(月)の週は第1週です。第1週は2018年2月19日(月)~2018年2月25日(日)です。
2019年2月18日(月)の週は第1週です。第1週は2019年2月18日(月)~2019年2月24日(日)です。
2020年2月17日(月)の週は第1週です。第1週は2020年2月17日(月)~2020年2月23日(日)です。
2021年2月15日(月)の週は第1週です。第1週は2021年2月15日(月)~2021年2月21日(日)です。
2022年2月21日(月)の週は第1週です。第1週は2022年2月21日(月)~2022年2月27日(日)です。
2027年2月14日(日)の週は第52週です。第52週は2027年2月8日(月)~2027年2月14日(日)です。
2027年2月15日(月)の週は第1週です。第1週は2027年2月15日(月)~2027年2月21日(日)です。
2028年2月20日(日)の週は第53週です。第53週は2028年2月14日(月)~2028年2月20日(日)です。
2028年2月21日(月)の週は第1週です。第1週は2028年2月21日(月)~2028年2月27日(日)です。
*/

思いのほか、しんどかったなあ。
みんな、Moment.js とか使うはずだわ……
勉強させてもらいました。

 コメントを受けて追記

逆引きの方が簡単なはず。コードの重複があるから適当にまとめてください。

function calcSchedule(weekNumber, year){

  var getMonday = function(date) {
    return new Date( date.getTime() - ( date.getDay() - 1 ) * 24 * 60 * 60 * 1000 );
  }

  var tempDate = new Date(year, 1, 20, 0, 0, 0);
  var firstDate = getMonday( tempDate ); 
  var monday = new Date( firstDate.getTime() + ( weekNumber - 1 ) * 7 * 24 * 60 * 60 * 1000 );
  var sunday = new Date( monday.getTime() + 6 * 24 * 60 * 60 * 1000 );

  return `表示週第第${weekNumber}週は
${
    monday.toLocaleDateString('ja',{year:'numeric'})
  }${
    monday.toLocaleDateString('ja',{month:'numeric'})
  }${
    monday.toLocaleDateString('ja',{day:'numeric'})
  }(月)~${
    sunday.toLocaleDateString('ja',{year:'numeric'})
  }${
    sunday.toLocaleDateString('ja',{month:'numeric'})
  }${
    sunday.toLocaleDateString('ja',{day:'numeric'})
  }(日)です。`;
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/02 19:07

    逆引きは大して難しくないですよね?

    キャンセル

  • 2018/04/03 11:39

    すいません、対応できました。この度はありがとうございます。

    キャンセル

  • 2018/04/06 15:06 編集

    逆引きの導入で0週以下、54週の以上が入力された時、53週がない年はfalseを返すようにしました。

    if( week >= 54 ){
    return ("1~53を入力してください。");
    }
    else if( week <= 0 ){
    return ("1~53を入力してください。");
    }
    else if ( week == 53){
    var weekNumber = Math.floor( ( monday - getFirstDate(monday) ) / ( 24 * 60 * 60 * 1000 ) / 7 ) + 1;
    if (week != weekNumber) {
    return("53週はありません。")
    }
    }

    キャンセル

+2

仕様が特殊なため利用できるかわかりませんが、input要素を使って週情報を取得する方法です。

function getWeekInfo(date) {
  let input = document.createElement('input');
  input.type = 'week';
  input.valueAsDate = date;
  let value = input.value; // "2018-W13"
  return { year: parseInt(value.substring(0, 4), 10), week: parseInt(value.substring(6), 10), start: input.valueAsDate };
}


-- 17:05 追記
1月4日が含まれる日が1週目ということのようですので、次のようにしてみてはどうでしょうか?

// 差分を出しておく。
let a = new Date(referenceDate);
a.setMonth(0);
a.setDate(4);
let dif = referenceDate - a;

// 確認したい日付。
var d = new Date(2022, 1, 21);
console.log(getWeekInfo(new Date(d - dif))); // {year: 2022, week: 1, start: Mon Jan 03 2022 09:00:00 GMT+0900 (東京 (標準時))}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/30 17:10

    含まれる「週」です……

    キャンセル

  • 2018/03/30 17:28

    startを使うときは差分を足さないといけません。念のため

    キャンセル

+1

stackoverflowにも似たような記事がありましたので、情報を共有します.
※とは言え, 本件に応用するには最終週の扱いでいろいろ工夫する必要がありそうです.

参考
https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
※ソース内の情報は既にリンク切れ
https://web.archive.org/web/20150907215801/http://www.merlyn.demon.co.uk/weekcalc.htm

/* For a given date, get the ISO week number
 *
 * Based on information at:
 *
 *    http://www.merlyn.demon.co.uk/weekcalc.htm#WNR
 *
 * Algorithm is to find nearest thursday, it's year
 * is the year of the week number. Then get weeks
 * between that date and the first day of that year.
 *
 * Note that dates in one year can be weeks of previous
 * or next year, overlap is up to 3 days.
 *
 * e.g. 2014/12/29 is Monday in week  1 of 2015
 *      2012/1/1   is Sunday in week 52 of 2011
 */
function getWeekNumber(d) {
    // Copy date so don't modify original
    d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
    // Set to nearest Thursday: current date + 4 - current day number
    // Make Sunday's day number 7
    d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
    // Get first day of year
    var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
    // Calculate full weeks to nearest Thursday
    var weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
    // Return array of year and week number
    return [d.getUTCFullYear(), weekNo];
}

[
    [2018, 2, 19],
    [2019, 2, 18],
    [2020, 2, 17],
    [2021, 2, 15],
    [2022, 2, 21],
    [2023, 2, 20],
    [2024, 2, 19],
    [2025, 2, 17],
    [2026, 2, 16],
    [2027, 2, 15],
    [2028, 2, 21]
].forEach(
    ([y, m, d]) => console.log(
        [y, m, d].join("/"), 
        getWeekNumber(new Date(y, m, d))[1] - getWeekNumber(new Date(y, 2, 21))[1] + 1)
);
2018/2/19 1
2019/2/18 1
2020/2/17 1
2021/2/15 1
2022/2/21 1
2023/2/20 1
2024/2/19 1
2025/2/17 1
2026/2/16 1
2027/2/15 1
2028/2/21 1

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

外部ライブラリ使用してもよいのであれば、moment.js のweek関数を使うのではどうでしょうか? https://momentjs.com/docs/#/get-set/week/

すみません、質問全体読まず知りたい内容と外れてましたので、回答取り下げます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/02 17:20

    直接的な回答となってはいないものの, ヒントとして有益なライブラリの紹介であり, 回答の取り下げまでは必要ないと思います.

    キャンセル

0

普通に考えれば2022-2-21は第4週目ではないかと思いますが、それはさておき。
referenceDateを2017-1-20に固定せず該当月の1日とするようにすれば問題は解決するのではないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/30 12:41

    横からすみません。おそらくISO8601の週番号のことだと思います。
    https://en.wikipedia.org/wiki/Week#Week_numbering

    キャンセル

  • 2018/03/30 12:49

    あ、すみません。誤読していました。失礼致しました。

    キャンセル

  • 2018/03/31 17:00 編集

    referenceDateは締日となっていますので固定になっており、それを基に毎年同じ規則で計算されているようです。(20日〆2017/2/20 月曜 1週目開始)第1週目の日付が毎年変わるのを考えると、毎年2月の中旬の日付からを第1週目として登録しておき、そこから53週目までを毎年表示させたいです。

    キャンセル

0

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.63%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる