Index: branches/version-2_11-dev/data/Smarty/templates/admin/basis/zip_install.tpl
===================================================================
--- branches/version-2_11-dev/data/Smarty/templates/admin/basis/zip_install.tpl	(revision 20764)
+++ branches/version-2_11-dev/data/Smarty/templates/admin/basis/zip_install.tpl	(revision 20945)
@@ -29,4 +29,5 @@
         <input type="hidden" name="<!--{$smarty.const.TRANSACTION_ID_NAME}-->" value="<!--{$transactionid}-->" />
         <input type="hidden" name="mode" value="">
+        <p>保存されている郵便番号CSVの更新日時: <!--{$tpl_csv_datetime|h}--></p>
         <p>郵便番号CSVには <!--{$tpl_line|h}--> 行のデータがあります。</p>
         <p>郵便番号DBには <!--{$tpl_count_mtb_zip|h}--> 行のデータがあります。</p>
@@ -43,13 +44,19 @@
 
         <div class="basis-zip-item">
-            <h2>自動更新</h2>
-            <p>全ての郵便番号を削除してから、登録しなおします。タイムアウトした場合、元の状態に戻ります。</p>
-            <p><a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('auto', '', ''); return false;"><span class="btn-next">自動更新</span></a></p>
+            <h2>自動登録</h2>
+            <p>下の [削除] [郵便番号CSV更新] [DB手動登録] を順に実行します。ただし、タイムアウトした場合、DBは元の状態に戻ります。</p>
+            <p><a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('auto', '', ''); return false;"><span class="btn-next">自動登録</span></a></p>
         </div>
 
-        <div class="basis-zip-item delete">
-            <h2>手動更新</h2>
+        <div class="basis-zip-item">
+            <h2>DB手動登録</h2>
             <p>指定した行数から郵便番号を登録します。タイムアウトした場合、直前まで登録されます。</p>
-            <p><a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('manual', '', ''); return false;"><span class="btn-next">手動更新</span></a>　開始行: <input type="text" name="startRowNum" value="<!--{$arrForm.startRowNum|default:$tpl_count_mtb_zip+1|h}-->" size="8"><span class="attention"><!--{$arrErr.startRowNum}--></span></p>
+            <p><a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('manual', '', ''); return false;"><span class="btn-next">手動登録</span></a>　開始行: <input type="text" name="startRowNum" value="<!--{$arrForm.startRowNum|default:$tpl_count_mtb_zip+1|h}-->" size="8"><span class="attention"><!--{$arrErr.startRowNum}--></span></p>
+        </div>
+
+        <div class="basis-zip-item">
+            <h2>郵便番号CSV更新</h2>
+            <p>日本郵便の WEB サイトから、郵便番号CSVを取得します。</p>
+            <p><a class="btn-normal" href="javascript:;" onclick="fnModeSubmit('update_csv', '', ''); return false;"><span class="btn-next">更新</span></a><span class="attention"><!--{$arrErr.startRowNum}--></span></p>
         </div>
 
Index: branches/version-2_11-dev/data/class/pages/admin/basis/LC_Page_Admin_Basis_ZipInstall.php
===================================================================
--- branches/version-2_11-dev/data/class/pages/admin/basis/LC_Page_Admin_Basis_ZipInstall.php	(revision 20911)
+++ branches/version-2_11-dev/data/class/pages/admin/basis/LC_Page_Admin_Basis_ZipInstall.php	(revision 20945)
@@ -51,4 +51,13 @@
     var $exec;
     var $tpl_count_mtb_zip;
+
+    /** CSV の更新日時 */
+    var $tpl_csv_datetime;
+
+    /** ZIP アーカイブファイルの取得元 */
+    var $zip_download_url = 'http://www.post.japanpost.jp/zipcode/dl/kogaki/zip/ken_all.zip';
+
+    /** 日本郵便から取得した ZIP アーカイブファイルの保管パス */
+    var $zip_csv_temp_realfile;
 
     // }}}
@@ -70,4 +79,5 @@
         $this->tpl_mode = $this->getMode();
         $this->exec = (boolean)$_GET['exec'];
+        $this->zip_csv_temp_realfile = DATA_REALDIR . 'downloads/tmp/ken_all.zip';
     }
 
@@ -105,5 +115,5 @@
                     $this->lfAutoCommitZip();
                     break;
-                // 手動登録
+                // DB手動登録
                 case 'manual':
                     $this->insertMtbZip($this->arrForm['startRowNum']);
@@ -114,5 +124,5 @@
 
         switch ($this->tpl_mode) {
-            // 手動削除
+            // 削除
             case 'delete':
                 $this->lfDeleteZip();
@@ -120,5 +130,13 @@
                 // 進捗・完了画面を表示しない
                 $this->tpl_mode = null;
-
+                break;
+
+            // 郵便番号CSV更新
+            case 'update_csv';
+                $this->lfDownloadZipFileFromJp();
+                $this->lfExtractZipFile();
+
+                // 進捗・完了画面を表示しない
+                $this->tpl_mode = null;
                 break;
         }
@@ -126,4 +144,7 @@
         $this->tpl_line = $this->countZipCsv();
         $this->tpl_count_mtb_zip = $this->countMtbZip();
+        $this->tpl_csv_datetime = $this->lfGetCsvDatetime();
+        // XXX PHP4 を切捨てたら、ダウンロードの必要性チェックなども行いたい
+        // $arrHeader = get_headers($this->zip_download_url, 1);
     }
 
@@ -140,14 +161,26 @@
         $objQuery =& SC_Query_Ex::getSingletonInstance();
 
+        $this->lfDownloadZipFileFromJp();
+        $this->lfExtractZipFile();
+
         $objQuery->begin();
-        $objQuery->delete('mtb_zip');
+        $this->lfDeleteZip();
         $this->insertMtbZip();
         $objQuery->commit();
     }
 
+    /**
+     * テーブルデータと UTF-8 変換済みの郵便番号 CSV を削除
+     *
+     * @return void
+     */
     function lfDeleteZip() {
         $objQuery =& SC_Query_Ex::getSingletonInstance();
 
+        // DB
         $objQuery->delete('mtb_zip');
+
+        // UTF-8 変換済みの郵便番号 CSV
+        unlink(ZIP_CSV_UTF8_REALFILE);
     }
 
@@ -179,5 +212,5 @@
         </head>
         <body>
-        <p>進捗状況</p>
+        <p>DB 登録進捗状況</p>
         <div style="background-color: #494E5F;">
         <?php
@@ -271,5 +304,4 @@
 
     function openZipCsv() {
-        // http://www.post.japanpost.jp/zipcode/dl/kogaki/lzh/ken_all.lzh
         $this->convertZipCsv();
         $fp = fopen(ZIP_CSV_UTF8_REALFILE, 'r');
@@ -325,4 +357,82 @@
         return $line;
     }
+
+    /**
+     * 日本郵便から郵便番号 CSV の ZIP アーカイブファイルを取得
+     *
+     * @return void
+     */
+    function lfDownloadZipFileFromJp() {
+        $res = copy($this->zip_download_url, $this->zip_csv_temp_realfile);
+        if (!$res) {
+            SC_Utils_Ex::sfDispException($this->zip_download_url . ' の取得または ' . $this->zip_csv_temp_realfile . ' への書き込みに失敗しました。');
+        }
+    }
+
+    /**
+     * ZIP アーカイブファイルを展開して、郵便番号 CSV を上書き
+     *
+     * @return void
+     */
+    function lfExtractZipFile() {
+        if (!function_exists('zip_open')) {
+            SC_Utils_Ex::sfDispException('PHP 拡張モジュール「zip」を有効にしてください。');
+        }
+
+        $zip = zip_open($this->zip_csv_temp_realfile);
+        if (!is_resource($zip)) {
+            SC_Utils_Ex::sfDispException($this->zip_csv_temp_realfile . ' をオープンできません。');
+        }
+
+        do {
+            $entry = zip_read($zip);
+        } while ($entry && zip_entry_name($entry) != 'KEN_ALL.CSV');
+
+        if (!$entry) {
+            SC_Utils_Ex::sfDispException($this->zip_csv_temp_realfile . ' に対象ファイルが見つかりません。');
+        }
+
+        // 展開時の破損を考慮し、別名で一旦展開する。
+        $tmp_csv_realfile = ZIP_CSV_REALFILE . '.tmp';
+
+        $res = zip_entry_open($zip, $entry, 'rb');
+        if (!$res) {
+            SC_Utils_Ex::sfDispException($this->zip_csv_temp_realfile . ' の展開に失敗しました。');
+        }
+
+        $fp = fopen($tmp_csv_realfile, 'w');
+        if (!$fp) {
+            SC_Utils_Ex::sfDispException($tmp_csv_realfile . ' を開けません。');
+        }
+
+        $res = fwrite($fp, zip_entry_read($entry, zip_entry_filesize($entry)));
+        if ($res === FALSE) {
+            SC_Utils_Ex::sfDispException($tmp_csv_realfile . ' の書き込みに失敗しました。');
+        }
+
+        fclose($fp);
+        zip_close($zip);
+
+        // CSV 削除
+        $res = unlink(ZIP_CSV_REALFILE);
+        if (!$res) {
+            SC_Utils_Ex::sfDispException(ZIP_CSV_REALFILE . ' を削除できません。');
+        }
+
+        // CSV ファイル名変更
+        $res = rename($tmp_csv_realfile, ZIP_CSV_REALFILE);
+        if (!$res) {
+            SC_Utils_Ex::sfDispException('ファイル名を変更できません。: ' . $tmp_csv_realfile . ' -> ' . ZIP_CSV_REALFILE);
+        }
+    }
+
+    /**
+     * CSV の更新日時を取得
+     *
+     * @return string CSV の更新日時 (整形済みテキスト)
+     */
+    function lfGetCsvDatetime() {
+        return date('Y/m/d H:i:s', filemtime(ZIP_CSV_REALFILE));
+    }
 }
 ?>
