3

.toLocaleDateString() メソッドを使用しない限り、以下の関数は本来よりも 1 日少ない日付を返します。私は何か間違ったことをしていますか、それともこれは欠陥ですか?

    function myDate() {
  var sDate = '05/10/2012';
  var parts = sDate.split('/');
  var d = new Date( parts[2], parts[1]-1, parts[0]);
  Logger.log(d);
  Logger.log(d.toLocaleDateString());
};

ロガーは以下を返します。

2012 年 10 月 4 日木曜日 16:00:00 PDT

2012 年 10 月 5 日 00:00:00 BST

私は英国にいるので、英国の日付形式です。プロジェクト プロパティのタイム ゾーンが「(GMT+00:00) London」に設定されていることを確認したのに、Logger が PDT で最初の日付を表示するのはなぜですか? それが日付を間違えた理由ですか?スタンドアロン プロジェクトで問題を再現しました。ここにリンクがあります。

日付計算を行うために文字列変数を日付オブジェクトに変換したかったので、.toLocaleDateString() メソッドで文字列に戻す必要はありません。

他の日付でテストすることで、おそらくそれを回避できると考えて、一貫性を確認しました。奇妙なことに、sDate の値を 2012 年 1 月 1 日から 2012 年 4 月 3 日の間に変更すると、正確に返されます。2012 年 5 月 3 日以降は 1 日減ります。2013 年の日付では、2013 年 4 月 29 日に再び 1 日減り始めるまで正しく返されます。だから一貫性がない。ここで報告されている問題に似ているようです。

4

5 に答える 5

3

古い DST 規則と新しい DST 規則の間の範囲で getValues() 関数によって読み取られた日付が 1 時間減っていることがわかりました。したがって、日付「2012 年 10 月 30 日」の暗黙の時刻は 00:00:00 です。しかし、読み込むと、前日、つまり 10/29/2012 23:00:00 に着陸して、1 時間ずつ減少していました。

開始日を含まず、終了日を含む、これら 2 つの時間枠の間の日付は、少なくとも getValues() 関数によって読み取られるように、現在この動作を示します。

  • 10月最終日曜日から11月第1日曜日まで
  • 3月の第2日曜日から4月の第1日曜日まで。

最終的に、これらの日付を現在の年の日付として動的に計算するコードを作成し、日付が目標範囲内にある場合は単純に 1 時間ずつ増やします。これはそれを修正するための長い道のりかもしれませんが、うまくいきます。

これが私のコードです:

/*
The old rules for DST stated that the time change occurred on the last Sunday in October, 
which would be 10/28/2012.  So, when you type in 10/29/2012, the timestamp associated with the date (00:00:00) is being decremented by an hour (23:00:00), 
which drops it into the previous day.  The new rules for DST states that DST ends on the 1st Sunday in November, which is 11/04/2012.  Also, the DST rules
for springtime are also an impacted range of dates that exhibit this behavior, between the second sunday in March and the first sunday in April.

Note:  Running this function from the script editor does not produce the strange DST behavior.  It seems to be an issue with the getValues() function on a Range object.
*/
function fixDateDSTProblem(lstrDate){
  var d = new Date(lstrDate);
  //Example ranges affected
  //10/29/2012 - 11/04/2012
  //03/11/2013 - 04/07/2013

  if(isNaN(d.getMonth())){
    //return what was passed in if it's not a date or null.
    return lstrDate;
  }
  else if(isAffectedDate(d)){
    //increment by one hour
    return new Date(d.getTime() + (60 * 60 * 1000));
  }
  else{
    //date is not affected, simply return it as it was passed.
    return lstrDate;
  }
}



//Check to see if a date is within a beginning and end date
function dateWithin(beginDate,endDate,checkDate) {
    var b,e,c;
    b = Date.parse(beginDate);
    e = Date.parse(endDate);
    c = Date.parse(checkDate);
  //slightly modified this for the date checking since beginning date is not inclusive, so needs to be > instead of >=
    if((c <= e && c > b)) {
            return true;
    }
    return false;
}



function isAffectedDate(targetDate){
  var yearNum = targetDate.getFullYear();

  //Get the last Sunday in October
  var lastSunOctDateStr = getLastOccurrenceDate(0, 10, yearNum);

  //Get the first Sunday in November
  var firstSunNovDateStr = getOccurrenceDate(1, 0, 11, yearNum);

  //Get the second Sunday in March
  var secondSunMarDateStr = getOccurrenceDate(2, 0, 3, yearNum);

  //Get the first Sunday in April
  var firstSunAprDateStr = getOccurrenceDate(1, 0, 4, yearNum);  


  //if the date is between the last sunday in october and the first sunday in november
  // or if the date is between the second sunday in march and the first sunday and april, fix it up!
  if(dateWithin(lastSunOctDateStr, firstSunNovDateStr, targetDate) || 
     dateWithin(secondSunMarDateStr, firstSunAprDateStr, targetDate)){
    return true;
  }
  return false;
}



function getOccurrenceDate(numOccurrence, dayIndex, monthCalendar, yearNum){
  //"Get date of first occurrence of Monday in June 2013"
  //"Get date of the second occurrence of Sunday in April 2013"
  //dayIndex:  Sunday = 0, Saturday = 6
  var monthIndex = monthCalendar - 1;
  var numFirstXDay = null;
  var firstDay = new Date(monthCalendar+"/01/"+yearNum);
  var numDayOfWeek = firstDay.getDay();
  if(numDayOfWeek == dayIndex){
    numFirstXDay = 1;
  }
  else if(numDayOfWeek > dayIndex){  
    numFirstXDay = 1+(6-numDayOfWeek)+1+dayIndex+(7*(numOccurrence-1));
  }
  else if(numDayOfWeek < dayIndex){
    numFirstXDay = 1+(dayIndex - numDayOfWeek)+(7*(numOccurrence-1));
  }

  return monthCalendar+"/"+numFirstXDay+"/"+yearNum; 
}



function getLastOccurrenceDate(dayIndex, monthCalendar, yearNum){
  //Example:  "Get date of last occurrence of Monday in June 2013"
  var monthIndex = monthCalendar - 1;
  var numLastXDay = null;
  //TODO:  Handle Leap Year!
  var monthMaxDaysArray = {
    '1':'31',
    '2':'28',
    '3':'31',
    '4':'30',
    '5':'31',
    '6':'30',
    '7':'31',
    '8':'31',
    '9':'30',
    '10':'31',
    '11':'30',
    '12':'31'}    

  var lastDay = new Date(monthCalendar + "/"+monthMaxDaysArray[monthCalendar]+"/" + yearNum);
  var numDayOfWeek = lastDay.getDay();
  if(numDayOfWeek == dayIndex){ 
    numLastXDay = 31;
  }
  else if(numDayOfWeek > dayIndex){    
   numLastXDay = monthMaxDaysArray[monthCalendar] - (numDayOfWeek - dayIndex); 
  }
  else if(numDayOfWeek < dayIndex){
    numLastXDay = (monthMaxDaysArray[monthCalendar] - numDayOfWeek) - (6 - (dayIndex-1));
  }

  return monthCalendar + "/" + numLastXDay + "/" + yearNum;
}
于 2012-10-08T06:34:35.397 に答える
2

ロガーは常に日付を PDT で記録します。それは同じ時間のインスタンスですが、別の方法で表現されています。+ サマータイムの違いが表示されています。

于 2012-10-05T14:36:56.627 に答える
1

コーリーの答えが確認を必要としない場合でも (彼は自分が話していることを知っています ;) いくつかの実用的な詳細を追加させてください...

  • getDay日付オブジェクト ( 、getTime、 ...)に対して行うすべての JavaScript 操作は、getFulYear表示されるロガー値が間違っているように見えても正しい値を返すため、日付を操作する必要がある場合に文字列に変換して数値に戻す必要はありません。
  • これらの日付がスプレッドシートに表示される場合、スプレッドシートのロケール設定を使用して目的の値が表示されます。
  • ロガーに他の形式の値が表示されることがあります...いつ、なぜなのかは聞かないでください。
  • 10 月と 4 月に気付く日付のずれは、夏時間の開始と終了時に実際に発生しており、ある期間のカレンダー イベントで別の期間の日付を定義する必要がある場合は、注意が必要です (たとえば、今日は 12 月にイベントを作成します)。 1 時間の差になります) したがって、ユーザー インターフェイスで日付オブジェクトを読み取るときは、正しい GMT+0X00 値を使用する必要があります。utilities.formatDate
  • 何らかの理由で、夏時間が切り替わる前に開始して終了する日付のリストを表示し、 を使用するutilities.formatDate場合は、変数を使用して「GMT+0X00」を動的に操作することをお勧めします。次のようなものを使用して簡単に取得できます。

* 編集*数週間前から構文が変更され、この文字列の書式を変更する必要があるため、それに応じてこのコードを更新しました。(問題 2204 を参照)

`var FUS1=new Date().toString().substr(25,6)+":00";// FUS1 gets the GMT+02:00 or GMT+01:00 string`

それが十分に明確になっていることを願っています

于 2012-10-05T15:49:45.703 に答える
0

これはバグではありません...

この問題に対して、プログラム的ではない別の解決策を提供できますか? 私はこの問題を切り分け、スクリプトが前日と午後 23 時に日付を取得していた理由を突き止めるために 1 日を費やしました。

次に、スプ​​レッドシートはカンマ区切りのデータベースではなく、自動書式設定機能を備えていることに気付きました。したがって、スプレッドシートで日付を含む列を選択し、メニューから[書式]> [数値]> [プレーンテキスト]を選択すると、問題は解決しました。これで、私のスクリプトは完璧に実行されます。これを列全体に適用すると、新しく追加されたセルにも影響します。そのため、その甘い解決策です。

これはstackoverflowであるため、Javaソリューションは次のとおりです(動作することを確認しています):

sheet.getRange(1,n).setNumberFormat('@STRING@');

ここから、christian.simms から取得しました: Format a Google Sheets cell in plaintext via Apps Script

于 2016-03-15T19:36:19.900 に答える
0

数ヶ月遅れてパーティーに参加してすみません...

これらの回答の問題は、これがBUGであることを実際に明らかにしていないことです。または、より正確に言えば、これは Google が「正しい」動作であると判断したものです (Issue 1271を参照してください。たとえば、アクションなしで閉じられました)。

このことを考慮。誰かの生年月日をスプレッドシートに入れたいのですが、それらのリストがたくさんあります。次に、値を読み取り、フォームに入れたいと思います。

function getAge(ss, index) {
  var ages = ss.getRangeByName("birthDates").getValues();
  return ages[index];
}
...
app.createLabel(getAge(ss, index));

フォームには、4 月から 10 月の間に生まれたすべての人の生年月日が間違って含まれるようになりました。Google は、彼らが実際には 1 日早く生まれたと判断しました。または、年齢を読み取って、処理せずに別のスプレッドシートに入力してみてください。両方のスプレッドシートのタイムゾーン設定は同じです。同じ話で、生年月日が間違っているとメールで言われると、本当にバカに見えてしまいます。

これがどのように行われるべきか: 誰かが日付を手動で指定した場合、たとえば "06/06/1997" を使用してその日付を読み取るとgetValue、前日ではなく "06/06/1997" が返されることを期待します。彼らはあなたが彼らを推測しようとすることを本当に期待してgetValueいません: 1時間前に渡しますか?」

それはクレイジーです。もしエクセルがそんなことをしたら、笑いものになるでしょう。直してください。

編集

Serge、それは非常に興味深いですが、あなたのスプレッドシートは TZ を抽出し、その TZ に従って表示する回避策を示しています。しかし、これはそもそも間違っているという問題を回避するだけです。このことを考慮。スプレッドシートには、ほとんどの場合、決まった日付の記念日が含まれています - 誰かの誕生日、戦いの日、予定の日時など。これらはTZ とは独立しています。. 誕生日がベルギーで 1960 年 7 月 6 日である場合、カリフォルニアに住んでいる場合は 7 月 6 日でもあります。午前 0 時 30 分に生まれた場合、CET と PDT で実際には 7 月 5 日だったとしても、パスポートには 7 月 6 日と表示されます。記念日や予定の場合、誰もタイムゾーンを気にしません。スプレッドシートでは、TZ を気にするユーザーはごくわずかです。それが実際に重要な単一のユースケースをすぐに思いつくことはできません。

Lotus 1-2-3 はこれを正しく理解しました。Excelはこれを正しく理解しました。日付は整数のシリアル番号で、タイム ゾーン情報はありません。あるスプレッドシートから別のスプレッドシートに日付をコピーすると、常に正しい結果が得られます。VB などを使用してスプレッドシートから日付を取得し、それを別のスプレッドシートに入れます。それは常に正しいと思います (私はそう思います)。言いたくないことですが、Excel はスプレッドシートを定義します。VBとは別に。

GAS で を使用して日付をコピーするとgetValues、 が間違っています。グーグルはこれを過度に賢くしました。入力した日付が実際には UTC を基準としたエポック日付であると仮定して、スプレッドシートが作成された TZ を見つけ、スプレッドシートの現在の TZ 設定 (少なくとも、私はそれがあなたがしていることだと思います)。しかし、だから何?そして、それは常に機能しますか?スプレッドシートを PDT に住んでいる人に郵送するとどうなりますか? あなたはそれを機能させることができると確信していますが、それは要点を逃しています.これは単なる回避策であり、壊れやすいと思います.スクリプト設定、およびそれらすべてを考慮する必要があります- ahab' を参照してください)。根本的な問題は、Google スプレッドシートが特定の TZ を基準にして日付を保存することです (基になるフォーマット ms は Unix エポックを基準にしていますか??)。これは単に間違っていたものであり、現在では修正できず、日付の処理に関する大量のワームの一部にすぎません。

短期的な修正として、おそらくスプレッドシートのすべての日付に 12 時間を追加できます。長期的な修正として、おそらくすべての日付をテキスト文字列として処理し、JavaScript で自分で処理する必要があります。GAS 日付の失われたミリ秒に関係する問題を処理するために、すべての経過時間を「整数」のミリ秒に置き換える必要がありました。続けて、私が間違っていると納得させてください。

Sergeによる編集: こんにちは、読みやすくするために編集で自由に答えさせていただきました(申し訳ありません)。

私はあなたの意見を理解していますが、私はそれを共有しません.

あなたが参照しているUIの例は、スプレッドシートの日付ではなく、純粋なjavascriptであるため、実際に回避策が必要です...とはいえ、ExcelスプレッドシートとGoogleスプレッドシートは同じように機能します(翌日はday + 1、time = decimals、ref date 0 = 12/31/1899)) スプレッドシートの世界にとどまっている限り、スプレッドシートを終了して Javascript に移動すると問題が発生します (参照日 = 01/01/1970、ミリ秒単位でカウント)...

彼らが TZ を気にせず、どのタイム ゾーンでも日付を「静的」に保つことを選択していた場合、他の多くの問題が発生していたでしょう。たとえば、ベルギーの私とワシントンのオバマ大統領の間でスカイプの予定を設定したい場合はどうなるでしょうか。 ? その会話を月曜日の午前 8 時に行うことにした場合、ベルギーまたはワシントンでは午前 8 時になりますか?

Google ソリューションを使用すると、彼は少し早く起きる必要がありますが、日付と時刻が同じ「瞬間」を示すため、私たちは両方ともそこにいます。あなたが示唆する方法では、お互いに話す機会はありません。 (なんて悲しいことでしょう^^) .

明らかにより良い選択である状況は他にもたくさんあります。Calendar サービスとの通信も良い例です。

このようなドキュメントは同じ方法で共有されず、日付を Javascript 日付オブジェクトに変換しないため、静的な Excel ソリューションはうまく機能します。Excel SS はコンピュータのユーザーに関連付けられています。Google SS は、タイム ゾーン設定を使用してユーザーのコンピュータとの関係を維持します。これは、(本質的に) ユーザーのコンピュータに関連付けられていないためです。私はあなたを納得させる望みはほとんどありません (;-) ですが、これで少なくとも少しは明確になることを願っています... (とにかく私は Google のエンジニアではないので、Google に影響を与える方法はまったくありません ^^)。

もう1つ編集...

これは、TZ 設定が GMT-8 に設定された小さなテスト シートです。このテスト シートは、他のテスト シート (ベルギーの冬時間 GMT+2 に設定)の値を取得し、この単純なスクリプトを使用して正しい方法で表示します。

function timeCorrection() {
  var thisSheet = SpreadsheetApp.getActiveSheet();
  var otherSS = SpreadsheetApp.openById('0AnqSFd3iikE3dENrc2h1M0ktQTlSNWpNUi1TS0lrNlE');
  var otherSheet = otherSS.getSheetByName('Sheet1');
  var otherValue = otherSheet.getRange(1,1).getValue();
  var tz = otherSS.getSpreadsheetTimeZone();
  var thisSheetValue = new Date(Utilities.formatDate(otherValue,tz,'MM/dd/yyyy HH:mm:ss'));
  thisSheet.getRange(1,1).setValue(thisSheetValue);
}

それと同じくらい簡単です;-)、「外国のシート」の設定は、バンクーバーで作成された新しく作成されたシートのデフォルトであるスクリプト設定と同じ値に設定されます。

于 2013-01-26T22:13:28.663 に答える