Google Apps Script (GAS) の setValue() で文字列が日付に化ける問題と対策

 

この記事の前提

この記事は、これまで手書きで作成していた仕入伝票を、Googleスプレッドシートからの印字に置き換える作業の中で起こったことを記録したものである。

データシートと伝票レイアウトシートを用意してデータシートの内容を伝票レイアウトシートに吐き出す。紙の仕入伝票の表示位置に合わせてフォントサイズや列幅などを工夫しながらデータが用紙にはまり込むようにレイアウトを整えていく。

図のように金額を記載する場所は3桁ずつ書けるように目盛線で区切られていた。

22B35868-57F1-45D8-96C1-2495308F38CE.PNG

色々とやり様はあるが、今回は数字を通常の数値として右寄せで表示するのではなく、3桁ずつを一定の間隔で配置することにした。

金額を伝票へ吐き出す際には、金額を文字列として扱い、3桁単位に分割する。3桁に満たない部分については、上の桁が存在する場合、必要に応じてゼロ埋めする。
図の例だと、315と000に分割する。

さらに、それぞれの3桁文字列について文字間に半角スペースを挿入し、伝票の桁枠に数字が収まるような表示用文字列を作成する。

例えば、

315

であれば、

3 1 5

という文字列に変換してセルへ書き込む。

ところが、この "3 1 5" を GAS からスプレッドシートに吐き出したところ、意図しない問題が発生した:dizzy_face:

意図しない問題

GASで setValue("3 1 5") のようにスペース区切りの数字文字列をセルに書き込むと、Google スプレッドシートが 日付として自動解釈 してしまう。

// 数量 315 を文字間スペース挿入して書き込みたい
var value = "3 1 5";
ws.getRange("H5").setValue(value);
// → セルには「2003 1 5」が表示される

JavaScript 上では typeof value === 'string' で間違いなく文字列だが、setValue() を経由してスプレッドシートに渡った時点でシート側の自動型変換が働く。

なぜ起きるか

Google スプレッドシートには セルに入力された値を自動的に型推定する機能 がある。これは setValue() による書き込みでも同様に動作する。

スペース区切りの数字(例: "3 1 5")は、シートの日付パーサーが「3月1日 2005年」や「2003年1月5日」のように解釈できるパターンに合致してしまう。

つまり:

  1. GAS側でセル値 315 を文字列 "3 1 5" を生成
  2. setValue("3 1 5") で書き込み
  3. シート側で "3 1 5" → 日付 2003年1月5日 と自動解釈
  4. セル値が Date オブジェクトに変換される

この挙動は GAS の setValue() に限らず、セルに手入力した場合も同じ。セルに 3 1 5 と打つと日付になる。

影響を受けるパターンの例

入力文字列 シートの解釈 左からどう解釈されるか
"3 1 5" 2003年1月5日 年 → 月 → 日
"1 2 3" 2001年2月3日 年 → 月 → 日
"1 3 0" 2000年1月3日 月 → 日 → 年
"2 0 3" 真ん中は月か日のいずれかとなるため日付と解釈されない "2 0 3" のまま

どう解釈されるかはロケール設定にもよると思われるが、今回は日本語環境のみで未検証。

解決方法

方法1: setNumberFormat('@') でテキスト形式を強制する(推奨)

// セルの表示形式をテキストに設定してから値を書き込む
ws.getRange("H5").setNumberFormat('@').setValue("3 1 5");

setNumberFormat('@') は Excel の「セルの書式設定 → 文字列」と同等。セルの書式が「テキスト」になるため、どんな値を setValue() しても自動型変換が行われない。

ヘルパー関数にまとめると使いやすい:

function setTextValue_(range, value) {
  range.setNumberFormat('@').setValue(value);
}

// 使用例
setTextValue_(ws.getRange("H5"), spaceChars(315));

方法2: 先頭にアポストロフィを付ける

ws.getRange("H5").setValue("'" + "3 1 5");

スプレッドシートではセル先頭の '(アポストロフィ)はテキストプレフィックスとして扱われ、表示には現れない。getValue() で読み戻した際には、当然アポストロフィは残らない:sunglasses:

方法3: 範囲全体に事前にテキスト書式を設定する

転記先の範囲が決まっている場合は、書き込み前に一括で設定しておく:

// 明細行 B5:W10 をテキスト書式に
ws.getRange(5, 2, 6, 22).setNumberFormat('@');

まとめ

  • setValue() は JavaScript の型に関係なく、シート側で自動型変換される
  • スペース区切りの数字は日付として解釈されるケースがある
  • setNumberFormat('@') を setValue() の前に呼ぶことで確実にテキストとして書き込める
  • 文字間スペース挿入のような帳票向け加工を行う場合は、この対策が必須:ramen:

0 件のコメント: