日付は文字列ではありませんし、日本ではそもそも暦がひとつではありません。省庁・銀行・税務署へ出す書類には 和暦 が求められ、しかも元号の最初の年は「1年」ではなく「元年」と書きます。ここを間違えると書類は差し戻されます。
厄介なのは、これがビューレイヤーで一度片付ければ済む描画の問題ではない、という点です。スプレッドシートでは 値 は数値、表示 は書式コードであり、その書式コードはセルにくっついたまま画面・印刷・PDF を通り抜け、経理から届いた .xlsx からも一緒に入ってきます。この記事はその層の話です ― Excel の表示形式は何を表現できるのか、そして ReoGrid Web はそれをどこまで再現しているのか。
表示形式は無料版の Lite で使えます。この記事の内容にライセンスキーは必要ありません。
Intl.DateTimeFormat ではだめなのか
元号入りの文字列はプラットフォーム標準でも取れます。UI のラベル用途ならそれが正解であることも多いです。
const d = new Date(2019, 6, 1); // 2019-07-01、令和の初日
new Intl.DateTimeFormat('ja-JP-u-ca-japanese', { dateStyle: 'long' }).format(d);
// → '令和元年7月1日' ✅
new Intl.DateTimeFormat('ja-JP-u-ca-japanese', {
era: 'long', year: 'numeric', month: 'numeric', day: 'numeric',
}).format(d);
// → '令和1/7/1' ⚠️ 「元」が消え、「年月日」も消える
2番目の結果が問題の本質をよく表しています。区切り文字も、句読点も、そして どのオプションの組み合わせなら「元」が出るのか も、Intl 側が決めます。こちらはレイアウトを指定しているのではなく、フォーマッターと交渉しているわけです。
書式コードはその逆です。出したい出力をそのまま1つの文字列で書き、それはレンダリング経路ではなく セル に属します。
worksheet.cell('B2').setFormat('[$-ja-JP-x-gannen]ggge"年"m"月"d"日"');
// 2019-07-01 → 令和元年7月1日
// 2024-12-10 → 令和6年12月10日
同じセル、同じ書式で、どの日付でも成立します。しかもこの文字列は Excel 自身が保存しているものと同じなので、経理が作った帳票は書式を保ったまま読み込まれます。
モデル ― 値はひとつ、表示は自由
セルは値を持ち、書式コードはその値の 描かれ方 を決めます。両者が混ざることはありません ― 日付の列をソートすれば、見えているテキストではなく内部のシリアル値で並びます。
import { createReogrid, NumberFormat } from '@reogrid/lite';
const grid = createReogrid({ workspace: '#grid' });
const ws = grid.worksheet;
// セル単位
ws.cell('B2').setValue('4999').setFormat('¥#,##0');
// 範囲単位
ws.range('B2:B20').setFormat(NumberFormat.currency('¥', 0));
// プロパティとしても読み書きできる
ws.cell('C3').format = '0.00%';
ws.cell('C3').format; // → '0.00%'
ws.cell('C3').format = undefined; // 解除
行・列インデックス版もあります。ループの中で使うのはこちらです。
ws.setCellInput(row, col, '45636');
ws.setCellNumberFormat(row, col, 'ggge"年"m"月"d"日"');
ws.getCellNumberFormat(row, col); // → 書式コード
ws.clearCellNumberFormat(row, col); // → General に戻す
ws.getDisplayText(row, col); // → '令和6年12月10日'
getDisplayText は描画・印刷・PDF 出力が内部で使っているのと同じ呼び出しです。ここが返した文字列が、そのまま紙に乗ります。
そもそも日付をどうセルに入れるか
Excel は日付を シリアル値 で保持します(1 が 1900-01-01)。ReoGrid Web も同じです。入れ方は2通り。
日付っぽい文字列を入れる。 自動的に判定して変換されます。
ws.setCellInput(2, 1, '2024/12/10');
// 45636 として格納され、書式に 'yyyy/m/d' が設定される
認識されるパターンは yyyy/m/d、yyyy-m-d、m/d/yyyy、および前2つに h:mm / h:mm:ss を付けた形です。
シリアル値を直接入れる。 インポートや日付ライブラリから数値で来る場合はこちらです。
ws.setCellInput(2, 1, '45636');
ws.cell(2, 1).setFormat('ggge"年"m"月"d"日"');
順序の落とし穴
自動判定は、そのセルに すでに表示形式が設定されている場合はあえてスキップ されます。書式を当てた結果ユーザーの入力が勝手に書き換わるのを防ぐためです。つまり順序に意味があります。
// ✅ 値が先、書式が後
ws.setCellInput(2, 1, '2024/12/10'); // → 45636(本物の日付)
ws.cell(2, 1).setFormat('ggge"年"m"月"d"日"'); // → 令和6年12月10日
// ❌ 書式が先
ws.cell(3, 1).setFormat('ggge"年"m"月"d"日"');
ws.setCellInput(3, 1, '2024/12/10'); // 文字列 '2024/12/10' のまま
後者ではセルが数値にならないため、日付書式が処理する対象がそもそも存在しません。あらかじめ書式を設定したテンプレートにデータを流し込むときは、日付風の文字列ではなく シリアル値を書き込む ようにすれば、この曖昧さは消えます。
元号トークン
トークンは2系統。通常の日付トークンと自由に組み合わせられます。
| トークン | 意味 | 2024-12-10 | 2019-07-01 | 1989-01-07 |
|---|---|---|---|---|
g | 元号の頭文字 | R | R | S |
gg | 元号の略称 | 令 | 令 | 昭 |
ggg | 元号の正式名 | 令和 | 令和 | 昭和 |
e | 元号年 | 6 | 1 | 64 |
ee | 元号年(ゼロ埋め) | 06 | 01 | 64 |
組み合わせるとこうなります。
ws.cell('B2').setFormat('ggge"年"m"月"d"日"'); // 令和6年12月10日
ws.cell('B3').setFormat('ge/m/d'); // R6/12/10
ws.cell('B4').setFormat('gge.m.d'); // 令6.12.10
ws.cell('B5').setFormat('gggee"年"mm"月"dd"日"'); // 令和06年12月10日
ws.cell('B6').setFormat('ggge"年度"'); // 令和6年度
ダブルクォートで囲んだ部分はリテラルです。"年" と書けば「年」がそのまま出ます。ここは重要で、囲まずに書いた d はラベルではなく日付トークンとして解釈されてしまいます。
元号の境界は「年」ではなく「日」
元号は改元日そのもので切り替わります。1989年1月上旬は昭和64年、その1週間後は平成元年です。
| 元号 | 開始日 | 補足 |
|---|---|---|
| 令和 | 2019-05-01 | |
| 平成 | 1989-01-08 | 1989-01-07 はまだ昭和64年 |
| 昭和 | 1926-12-25 | |
| 大正 | 1912-07-30 | |
| 明治 | 1868-01-25 |
明治より前の日付には該当する元号がありません。g と e は何も出力せず、書式のリテラル部分だけが残ります。そこまで遡るデータを扱うなら、条件付きセクションで振り分けるか、西暦書式にフォールバックしてください。
元年 ― この記事の主題
元号の最初の年は「元年」と表記します。Excel はこれをロケールタグで表現し、ReoGrid Web も同じタグを解釈します。
ws.cell('B2').setFormat('[$-ja-JP-x-gannen]ggge"年"m"月"d"日"');
// 2019-07-01 → 令和元年7月1日
// 2020-07-01 → 令和2年7月1日 (変わるのは1年目だけ)
古いファイルが持っている数値形式のタグも認識します。
ws.cell('B3').setFormat('[$-x-gannen411]ggge"年"m"月"d"日"');
タグを付けなければ「令和1年」になります ― これは Excel の挙動と同じで、そして役所の書式では弾かれる表記です。
押さえておきたい点が2つ。ひとつは、このタグは e でも ee でも効くこと。「元」は数ではないのでゼロ埋めのしようがないためです。もうひとつは、タグの効果は それが書かれたセクション に閉じること。複数セクションの書式では、日付を描くセクション側にタグを置きます。
セクション ― 正・負・ゼロ
書式コードは ; で最大4つのセクションに分かれます。先頭3つは値の符号で選ばれます。
正の値 ; 負の値 ; ゼロ
ws.range('B2:E20').setFormat('#,##0;[赤]"▲"#,##0;"−"');
// 1,250,000 → 1,250,000
// -1,250,000 → ▲1,250,000 (赤字)
// 0 → −
ここで効いてくる挙動が 負のセクションには絶対値が渡される ことです。日本の会計表記が成立するのはこのおかげで、"▲"#,##0 と書けば ▲1,250,000 になります(▲-1,250,000 にはなりません)。損益計算書の列にこの書式コードを1つ当てるだけで、日本の財務諸表として読める見た目になり、セルごとの分岐ロジックは要りません。
前年比のようなパーセント列も同じ扱いです。
ws.cell('F5').setFormat('0.0%;[赤]"▲"0.0%');
// 0.443 → 44.3% -0.088 → ▲8.8%(赤字)
カラーブラケット
角括弧に色名を書くと、そのセクションの文字色が変わります。英名・和名のどちらも使えます。
| 英名 | 和名 | 色 |
|---|---|---|
[Black] | [黒] | #000000 |
[Red] | [赤] | #FF0000 |
[Green] | [緑] | #008000 |
[Blue] | [青] | #0000FF |
[White] | [白] | #FFFFFF |
[Yellow] | [黄] | #FFFF00 |
[Cyan] | [水] | #00FFFF |
[Magenta] | [紫] | #FF00FF |
Excel のインデックスカラーも使えます ― [Color 3] または [色3](1〜16)。
解決後の色は取得できます。セルの見た目を UI の別の場所にミラーリングしたいときに便利です。
ws.setCellNumberFormat(5, 2, '#,##0;[赤]"▲"#,##0');
ws.setCellInput(5, 2, '-8000');
ws.getCellFormatColor(5, 2); // → '#FF0000'
書式の色はセルスタイルの color より優先されます。画面でも、PDF 出力でも同じです(v1.5 の PDF はスタイル側の色を使っていましたが、v1.6 から画面に揃いました)。ただしブラウザ印刷は経路が異なり、HTML 経由で描画されるためセルスタイルの色が使われます ― ブラウザから印刷した帳票では [赤] の赤字が黒くなります。そこが重要な帳票では、スタイルの文字色も併せて設定するか、実スタイルを書き込む条件付き書式で色を付けてください(こちらはどの経路でも反映されます)。
条件付きセクション
符号でセクションを選ばせる代わりに、比較条件を前置きできます。条件は最大2つ+フォールスルー1つです。
ws.range('C2:C50').setFormat('[>=100000]"要審査";[>=10000]"確認";"通常"');
最初にマッチしたセクションが使われ、どれもマッチしなければ 条件が付いていない最初のセクション が使われます。符号ベースの選択と違い、条件付きセクションには値がそのまま渡されます(暗黙の絶対値化はありません)ので、負値ならマイナス記号も出ます。
末尾カンマによる千単位スケーリングと組み合わせると実用的です。カンマ1つにつき表示が 1/1000 になります。
ws.range('D2:D50').setFormat('[>=1000000]#,##0,,"百万円";[>=1000]#,##0,"千円";0"円"');
// 24,500,000 → 25百万円
// 1,250,000 → 1百万円
// 8,500 → 9千円
// 320 → 320円
シートは既定で40行です。最終行を超える範囲は警告もなく無視され、表示形式・スタイル・条件付き書式・入力規則のいずれも黙って落ちます。
B2:B100のような範囲を扱う前にsetGridSize()で(あるいはデータ量に合わせて)シートを広げてください。
注意点がひとつ。スケーリング後の数値は書式が要求する桁に丸められるので、#,##0, は 8,500 を「9千円」と表示します ― 正確な金額が必要な列ではなく、サマリー列で使うものです。精度が要るなら小数を足してください(#,##0.0, → 8.5千円)。ただし v1.5 ではスケーリング側を整数パターンにしてください。小数と末尾カンマの組み合わせが効くのは v1.6 以降です。
セルの値そのものは変わりません ― SUM は元の数値で合計します。
通貨とロケールブラケット
通貨記号の付け方は3通りあり、出力は同じです。
ws.cell('B2').setFormat('¥#,##0'); // ¥1,250,000
ws.cell('B3').setFormat('#,##0"円"'); // 1,250,000円
ws.cell('B4').setFormat('[$¥-411]#,##0'); // ¥1,250,000
3番目は、通貨にロケールが紐づいているときに Excel が書き出す形式です。[$記号-ロケールID] のうち記号だけが出力され、ロケールIDは落とされます。日本語版 Excel から来たファイルにはこの形式が大量に入っていますが、そのまま読み込めます。
よく使う書式は NumberFormat ヘルパーで生成できます。文字列を毎回手書きする必要はありません。
NumberFormat.number(2); // '#,##0.00'
NumberFormat.percent(1); // '0.0%'
NumberFormat.currency('¥', 0); // '¥#,##0'
NumberFormat.currency('円', 0, 'suffix'); // '#,##0円'
NumberFormat.date('yyyy-mm-dd'); // 'yyyy-mm-dd'
NumberFormat.time('h:mm AM/PM'); // 'h:mm AM/PM'
実例 ― 申請書のヘッダー部
ここまでを組み合わせると、官公庁向けの申請書ヘッダーは数行で書けます。
import { createReogrid, NumberFormat } from '@reogrid/lite';
const ws = createReogrid({ workspace: '#grid' }).worksheet;
const WAREKI = '[$-ja-JP-x-gannen]ggge"年"m"月"d"日"';
const YEN = NumberFormat.currency('¥', 0);
const ACCT = '#,##0;[赤]"▲"#,##0;"−"';
// 申請日 / 決裁日
ws.cell('B2').setValue('申請日').setStyle({ bold: true });
ws.setCellInput(1, 2, '2019/7/1');
ws.cell(1, 2).setFormat(WAREKI); // 令和元年7月1日
ws.cell('B3').setValue('決裁日').setStyle({ bold: true });
ws.setCellInput(2, 2, '2024/12/10');
ws.cell(2, 2).setFormat(WAREKI); // 令和6年12月10日
// 金額欄
ws.cell('B5').setValue('請求金額').setStyle({ bold: true });
ws.setCellInput(4, 2, '1250000');
ws.cell(4, 2).setFormat(YEN); // ¥1,250,000
ws.cell('B6').setValue('前年差額').setStyle({ bold: true });
ws.setCellInput(5, 2, '-84000');
ws.cell(5, 2).setFormat(ACCT); // ▲84,000(赤字)
// 規模区分 ― 書式コード1つ、分岐なし
ws.cell('B7').setValue('規模区分').setStyle({ bold: true });
ws.setCellInput(6, 2, '1250000');
ws.cell(6, 2).setFormat('[>=1000000]#,##0,,"百万円";[>=1000]#,##0,"千円";0"円"'); // 1百万円
これらのセルはすべて数値のままです。ソートしても、合計しても、数式から参照しても問題ありません。書式はあくまで表示であり、表示だけを担当します。
どこまで一緒に運ばれるか
| 画面表示 | 書式のテキストとカラーブラケットの両方が反映される |
getDisplayText() | 書式適用後のテキストを返す |
| PDF 出力 | テキストとブラケット色の両方(v1.6 以降) |
| ブラウザ印刷 | テキストは反映、色は セルスタイル の色 |
| xlsx インポート | カスタム numFmt を読み取って適用(和暦・元年書式を含む) |
| ReoGrid JSON | ドキュメントと一緒に往復する |
Excel の細かい記法のうちいくつかは、解釈はされますが出力を持ちません。Excel からコピーしてきた書式コードが壊れることはなく、単に余白が付かないだけです ― _c(幅調整プレースホルダー)と *c(塗りつぶし文字)は黙って読み飛ばされます。@(文字列セクション)と [DBNum1] などの漢数字表記には対応していません。
まとめ
日本の帳票には、私たちが使っているどのフレームワークよりも古いルールがあり、そこに交渉の余地はありません ― 1年ではなく元年、マイナス記号ではなく ▲、そして「円」は様式が決めた位置に。Excel は数十年前にそれらを表示形式として符号化しました。ブラウザで同じ要件を満たす最短経路は、同じ言語を話すことです ― セルごとに1つの文字列、値に付随し、グリッドと印刷の両方が尊重する書式コード。
まずは和暦・カラー書式デモでこの記事のコードが実際に描画されるところを確認し、トークンの全一覧は表示形式のドキュメントへ。再現したい様式が今 .xlsx として手元にあるなら、Excel の帳票レイアウトをそのまま Web へから始めてください(書式も一緒に移ってきます)。最終的に紙にする必要があるなら、ブラウザだけで請求書PDFを生成するが続きです。