2010年08月16日

POIでセルの値をとるのは大変 日付編(2)

前回はExcelのセルが日付タイプの際に表示されている内容で取得することが困難なことを説明しました。
今回は具体的なコーディングを行います。

方針をおさらいすると下記のとおりになります。
 1.java.text.DateFormatをもつenum CellDateFormatを定義
 2.CellDateFormatに変換を行いたいフォーマットを登録
 3.日付の判定にDateUtil#isCellDateFormatted()を使用し、
   falseだった場合でも、CellDateFormatに存在する場合にtrueとする
 4.CellDateFormatから返されるjava.text.DateFormatで文字列に変換する
それでは順番に説明していきます。

1.java.text.DateFormatをもつenum CellDateFormatを定義
2.CellDateFormatに変換を行いたいフォーマットを登録
フォーマットインデックスでの解決はお勧めできないとしておきながら、インデックスからフォーマットのマッピングを行っています。
この部分が最大の妥協です。
そのため必要最小限のフォーマットのみを登録しておき、それ以外はデフォルトフォーマットになるようにしました。(NONE:ソースの2行め)
対応対象のインデックスは、
 @Excelの内部的に「日付フォーマット」と認識しているもの、
 Aセルの書式設定でデフォルトで選択可能であり、
  かつ一般的に使いそうなものからごく一部のみ
としました。
今回はenumを利用しましたが、Mapにしてもよいかもしれません。
enumを利用した理由は、定数のように扱いたかったからです。
ここまでのところのコーディング例を記載します。
public enum CellDateFormat {
  NONE(-1, new SimpleDateFormat("yyyy/MM/dd")),
  FORMAT_14(14, new SimpleDateFormat("yyyy/M/d")),
  ・・・必要なフォーマットを登録しておきます
  FORMAT_58(58, new SimpleDateFormat("GGGGyy年M月d日", new Locale("ja", "JP", "JP")));

  private int formatId;          // Excelのフォーマットインデックス
  private DateFormat dateFormat; // 文字列への変換フォーマット
  private CellDateFormat(int format, DateFormat dateFormat) {
    this.formatId = format;
    this.dateFormat = dateFormat;
  }
  public DateFormat getDateFormat() {
    return this.dateFormat;
  }
  public int getFormatNo() {
    return this.formatId;
  }
  /**
   * フォーマットインデックスから該当するCellDateFormatを返す
   */
  public static CellDateFormat getFormt(int formatId) {
    for (CellDateFormat elm : CellDateFormat.values()) {
      if (elm.getFormatNo() == formatId) {
        return elm;
      }
    }
    return NONE;
  }
  public static boolean contains(int formatId) {
    for (CellDateFormat elm : CellDateFormat.values()) {
      if (elm.getFormatNo() == formatId) {
        return true;
      }
    }
    return false;
  }
}

3.日付の判定にDateUtil#isCellDateFormatted()を使用し、
  falseだった場合でも、CellDateFormatに存在する場合にtrueとする
4.CellDateFormatから返されるjava.text.DateFormatで文字列に変換する
ここまで準備ができれば、あとは変換ロジックを呼び出すだけです。
日付と認識できれば、最低限デフォルトのフォーマットで返すことができます。
先のCellDateFormatで定義しているフォーマットは正しく表示どおりの文字列を返すことができます。
  ・・・
  case Cell.CELL_TYPE_NUMERIC:
    // 日付・整数・少数の判別を行う
    if (DateUtil.isCellDateFormatted(cell)
      || CellDateFormat.contains(cell.getCellStyle().getDataFormat())
    ) {      //日付
      Date theDate = cell.getDateCellValue();
      DateFormat dateFormat 
        = CellDateFormat.getFormt(cell.getCellStyle().getDataFormat()).getDateFormat();
      return dateFormat.format(theDate);
    } else { // 数値
  ・・・

表示どおりの値で取得できるフォーマットとは?
下記に上記のコーディング例での変換可否を記載します。 >
フォーマット 日付として認識 インデックス フォーマットどおりの文字列として取得可能
*yyyy/M/d14
d-mmm-yy○15
d-mmm16
mmm-yy17
h:mm AM/PM18
h:mm:ss AM/PM19
h:mm20
h:mm:ss21
yyyy/m/d h:mm22
m/d/yy30※
yyyy"年"m"月"d"日"31※
h"時"mm"分"32※
h"時"mm"分"ss"秒"33※
mm:ss45
mm:ss.047
yyyy"年"m"月"55※
m"月"d"日"56※
[$-411]ge.m.d57※
[$-411]ggge"年"m"月"d"日"58※
*yyyy年M月d日187※×
[$-F800]dddd, mmmm dd, yyyy187※×
Gyy.m.d×188※×
GGGGyy年m月d日×189※×
yyyy年M月d日×181※×
yyyy年M月×190※×
M月d日×182※×
yyyy/M/d180※×
yyyy/M/d H:m a191※×
yyyy/M/d HH:m183※×
M/d192※×
M/d/yy193※×
MM/dd/yy194※×
dd-MMM195※×
d-MMM-yy196※×
dd-MMM-yy184※×
MMM-yy197※×
MMMM-yy198※×
[$-411]ge.m.d;@×188※×
yyyyMMdd185※×
yyyy/MM/dd186※×
※Excelの内部的に「日付フォーマット」と認識していないもの
 実行環境によって異なる可能性がある
 (表示どおりのフォーマットにならない可能性がある)
ラベル:java Poi
posted by しん at 22:02| Comment(2) | POI | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
すいません
下のテキストエリアの8行目
DateFormat dateFormat = CellDateFormat.getFormt(cellStyle.getDataFormat()).getDateFormat();
の cellStyle. は何を表しているのでしょうか?
Posted by newint at 2010年10月26日 13:56
コメントありがとうございます。
返事が遅れてしまってすみません。
コメントの存在に気がついていませんでした。

ご質問の「cellStyle」ですが、cell.getCellType()の意味です。
POIでセルの値をとるのは大変日付編(1)で変数化していると勘違いしていました。
直前のif文では変数を利用していないのに、大分混乱してますね。
申し訳ありません。
修正しておきます。
Posted by しん at 2010年11月11日 06:14
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。