質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.40%

  • PHP

    24967questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • CSV

    917questions

    CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

【PHP】fgetcsvで、時刻表の『今から乗れる直近5件』を表示させたいのですが、終電間際、翌日の処理について教えてください。

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 131

arakuman

score 2

前提・実現したいこと

お世話になります。
PHP2ヶ月目の初学者です。
CSV(UTF-8 コンマ区切り)で最寄り駅の時刻表を作りました。

5:17,大,
5:39,大,
5:50,宿,急


23:46,大,
23:56,大,
0:02,大,急
0:08,町,
0:16,大,
0:31,町,
0:48,大,

PHPを使用し、現在時刻以降直近の5件の行だけテーブルに出力したいと考えました。
『5件出力』は半日かかって何とか頑張りました。#もっとスマートな方法があれば合わせてお願いします。
終電間際、日付が変わった後の0:02などが当日のタイムスタンプと認識されてしまうので、これを翌日の0:02としたいと思いますがうまくいきません。

該当のソースコード

<?php

$myTime = strtotime ("now");
//$myTime = strtotime("23:30:00");

$file = "sample.csv";
if ( ( $handle = fopen ( $file, "r" ) ) !== FALSE ) {
    echo "<table>\n";
    $j = 0;
        while ( ( $data = fgetcsv ( $handle, 200, ",", '"' ) ) !== FALSE ) {
             if (strtotime($data[0]) >= $myTime){
                $j++;
                    if ($j > 5 ){        //直近の5件のみ表示
                        break ;
                    }
                echo "\t<tr>\n";
                  $count = count( $data );
                    for ( $i = 0; $i < $count; $i++ ) {
                           echo "\t\t<td>{$data[$i]}</td>\n";
                       }
                echo "\t</tr>\n";
              }
        }
    echo "</table>\n";
    fclose ( $handle );
}
?>

試したこと

if (strtotime ( $data[0] ) < strtotime( "3:00:00" ) ) {        //始発より前の時間なら(仮に午前3時)            
$data[0] = array_replace($data[0] , date("G:i" , strtotime( $data[0] . " +1 day" ) ) );//翌日に変更(??)
}

array_replace とかで配列を書き換え?と色々試しましたが、書き方がよくわかりません。
お忙しいところ恐縮ですが宜しくお願い致します。

#本当は時刻表っぽく
↓こんな感じにしてみたいところです…。急行なら赤とか。
17|39 43 53
18|00 11

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+2

方向

strtotime()に与える引数にどうにかして正しい日付の情報を与えてあげれば解決します。

具体的には、次の日と判定された場合、引数に +1 dayが追加されるようにします。

//こんな感じにすると、実行している日の次の日の0:01のunixタイムスタンプが取得出来る
$time = strtotime("0:01 +1 day");

原理

まず

if (strtotime($data[0]) >= $myTime){
が何を意味しているかを理解する必要があります。

strtotimeをPHPマニュアルで確認すると、
strtotime — 英文形式の日付を Unix タイムスタンプに変換する
という関数です。

コード中で渡されている$data[0]は例えば
5:17だったり、0:48だったりして、日付に関する情報が含まれていません。
この場合、省略された日付に関しては本日の日付であると解釈され、
0:48を渡された場合、本日の0:48のUnixタイムスタンプが取得されます。

一方で、$myTime = strtotime ("now");では現在日時のUnixタイムスタンプが取得されます。

結果として23時に実行した場合、
if (本日の0:48のunixタイムスタンプ >= 本日の23:00のUnixタイムスタンプ){ 
という絶対に成立しない条件が作られてしまうため、日付が変わると表示されなくなります。

正しい日付を得るには?

CSVのフォーマットが質問にある通りに厳守されている

  • 先頭行に始発が記録されている
  • 最終行に終電が記録されている

という事であれば、
始発より早い時刻は全て日付変更後の時刻である
という事がいえるはずです。

であれば、こんな感じで条件を加えてあげて、日付変更後の時間に" +1 day"を付与してあげれば動くはずです。(現在のコードと比較して、どう変わっているか確認してみて下さい。)

<?php

$myTime = strtotime ("now");
//$myTime = strtotime("23:30:00");

$file = "sample.csv";
if ( ( $handle = fopen ( $file, "r" ) ) !== FALSE ) {
    echo "<table>\n";

    $firstTime = null;
    $j = 0;
        while ( ( $data = fgetcsv ( $handle, 200, ",", '"' ) ) !== FALSE ) {
             //ここで初回のループの時だけ始発の時間を変数に格納しておく
              if(is_null($firstTime) === true){
                 $firstTime = $data[0];
              }

             //始発より時間が早いかどうか判定
             if(strtotime($data[0]) < strtotime($firstTime)){
                  $time = $data[0]." +1 day";
             }else{
                  $time = $data[0];
             }

             if (strtotime($time) >= $myTime){
                $j++;
                    if ($j > 5 ){        //直近の5件のみ表示
                        break ;
                    }
                echo "\t<tr>\n";
                  $count = count( $data );
                    for ( $i = 0; $i < $count; $i++ ) {
                           echo "\t\t<td>{$data[$i]}</td>\n";
                       }
                echo "\t</tr>\n";
              }
        }
    echo "</table>\n";
    fclose ( $handle );
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/11 10:42

    お忙しい中、ご返信ありがとうございます。

    実はCSVの1行目に関して気持ち悪いけど放っておいた事があります。
    var_dump($firstTime); で見ると"5:17"ときちんと始発の時間を取得しているのですが、
    date("Y/m/d G:i",strtotime($firstTime)) を見ると"1970/01/01 9:00"なのです。

    $data2 = date("Y/m/d G:i",strtotime($time));
    var_dump($date2); では2行目から正しく表示されます。

    $myTime = strtotime("04:30:00");とかにして見ると始発が表示されず、次発の"5:39"からだったので、1行目には”上り-平日”みたいな適当な文字列を入れ、2行目に始発を入力で対応していました。(ここでハマりました…)

    家族が支度中に見るだけなので、切替の時間を"3:00"としてif文を以下にして、無事解決しました。
    if(strtotime($data[0]) < strtotime("3:00:00")){

    }
    『なんとなく時間っぽいただの文字列の$data[0]』に対して、$data[0]." +1 day" なんていう乱暴なことをしても大丈夫なんですね…。懐が広いというか…。

    この度はありがとうございました。また多分お世話になるかと思いますが、その際は宜しくお願い致します。

    キャンセル

  • 2019/07/12 09:42

    やっぱり気持ち悪いので調べたところ、CSVの1行目の問題は原因がわかりました。
    Excelで作成したので先頭にBOMというマークが付いていたんですね。
    このまま2行目から読み込む感じにします。

    キャンセル

+1

毎回ifで判定するからおかしくなるので、このデータなら開始点を見つけたら5回出力する処理に遷移すればいいのでは?

むしろ今が00:03のときに翌日の始発が表示されること、これをクリアしても翌日の始発が5つめに出てこないことのほうがネックになりそうです。

おそらく時刻表をみて作ったデータだと思いますが、質問者様もこう表示したいと仰るようにそれは分かりやすい見た目にした結果であって内部データがそういう形式をとる必然性があるでしょうか?(鉄道会社は一日券の概念があるので必然性があります)

単に0時から始まるデータであれば今のロジックでもよいはずです。
また、土日(祝)があるとは思いますが、金曜日、土曜日、日曜日、他の平日、などを持てば解決します。(これは静的に解決する方法で、動的に作るなら明日の曜日の0時以後と今日の曜日の0時前を連結すれば平易に作れると思います)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/11 10:41

    お忙しい中、ご返信ありがとうございます。

    家族が支度中に見るだけなので、『終電が終わったら空白、深夜3時を過ぎたら始発から表示』でもいいかなという感じでいます。
    今後は、祝日判定をして平日と土日祝で拾ってくるCSVを変えて、金曜日の深夜、日付が変わった後は土日祝ダイヤを拾わないようにして…と課題は山積していますがチマチマやっていきます。

    また多分お世話になるかと思いますが、その際は宜しくお願い致します。

    キャンセル

  • 2019/07/11 22:19

    質問者さまへの直接のお返事ではないです。

    私はこの問題をデータ構造とアルゴリズムの問題だと捉えています。
    データがソートされているとき、毎行の判定は無駄です(処理を遷移するよう提案した趣旨)。それはソートされていないデータに対してのアルゴリズムです。
    逆にソートされていないなら原理的にfilter, sort, sliceの処理が必要であって、所与のコードのようにフィルタ中にsliceするようなことをしては、求まりせん(0時3分の例)
    なので、アルゴリズムをかえるかデータをソートされた状態にするかです。
    アルゴリズムをかえるには、現在時刻との差分をとって、その差でソートして、正の差分の小さいほうから5つをスライスするという処理になります。また日付の概念をもたなければならず修正として複合的でやや複雑です。
    わたしは日付情報がない状態でソートされたデータを作ろうと言っています。他のかたは、日付の概念を持ちこもうと言っています。
    これは構造の問題であって、用途は関係ないです。

    キャンセル

0

始発の前なら時間に24を足すってのではどうでしょう

何かで出力させるときには、24時以上は24引いて出力すればいいし。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/11 10:42

    お忙しい中、ご返信ありがとうございます。無事に解決しました。
    また多分お世話になるかと思いますが、その際は宜しくお願い致します。

    キャンセル

0

いろいろやり方はありそうですが、時間を 300 - 2700 の数字として扱ってしまうのが楽な気がします。

まず CSV の時間表記を上記の数字に書き換えてしまいます。

比較の際は、現在時刻も上記の数字と同じく置き換え、大小を比較します。
条件合致から5件出力すれば完成です。

休日/祝日の切り替えは読み取る CSV そのものを切り替えることで対応するのが良いかなぁ。。。

表示の調整はフローを整理すればコードに落とすことが可能です。
フローの整理には図を書いてみるのが一般的な初学者のやり方です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/11 11:04

    お忙しい中、ご返信ありがとうございます。
    なるほど、そういった考え方もあるんですね。勉強になります。
    表示方法はまだまだずっと先になってしまいそうですが、実現させたいと思っています。
    (その前にまずCSSの勉強なのですが…)

    また多分お世話になるかと思いますが、その際は宜しくお願い致します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.40%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • PHP

    24967questions

    PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

  • CSV

    917questions

    CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

  • トップ
  • PHPに関する質問
  • 【PHP】fgetcsvで、時刻表の『今から乗れる直近5件』を表示させたいのですが、終電間際、翌日の処理について教えてください。