なかなかどうして、この有様よ。

へたれ系PG/SEの技術系メモ+育児日記。

Calendar型からDate形に変換する方法3選

概要

年月日がそれぞれ別の項目(セレクトボックスとか)になっているformの値を、Calendar型経由でDate型に変換する際、設定内容によっては時分秒が想定外の値になる。 なので、その現象と対処方法を記載する。

ソースと注釈

単純にCalendar型からgetTimeをすると、インスタンスを生成したタイミングの時分秒ミリ秒が設定されてしまう。
そのため、時分秒ミリ秒を「00:00:00.000」にする方法を3つ記載する。

  1. Calendar型からgetTime()して取得した日付(時分秒0指定)
     これは、Calendarのインスタンスにsetで年月日を設定する際に時分秒ミリ秒を0で設定する方法となる。
     単純に値を設定する項目が増えるだけなので、一番手軽にできる方法だと思われる。
  2. Calendar型から時分秒ミリ秒をクリアしてgetTime()して取得した日付
     これは、Calendarのインスタンスを一旦clearでクリアし、再度setで年月日を設定する方法となる。
     ただ、clearでは時間のクリアが行えないため、時間はsetで0を設定しなければならない。
     現在日付を利用する際に時分秒ミリ秒のクリアが必要な場合は有効であるが、そうでない場合は手間がかかる方法だと思われる。
  3. Calendar型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付
     これは、SimpleDateFormatでフォーマットを年月日のみにし、その後CalendarのインスタンスからgetTimeした値をSimpleDateFormatで設定したフォーマットに変換し、それを再度SimpleDateFormatでDate形にparseする方法となる。
     parseを行うためTry/Catchかエラーのthrowを記載する必要が発生し、手間がかかるのでどうしてもSimpleDateFormatを利用しないといけない場合以外はあまり利用しない方が無難だと思われる。

以下、サンプルソース
(ブログへのコピーがミスっていなければ、そのままで多分動くはず。)

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;


public class TestCalenderToDate {


    public static void main(String[] args) {


         // 値を設定する
        Integer year = 2020;
        Integer month = 1;
        Integer day = 15;


        System.out.println("設定値");
        System.out.println("年:"+ year.toString());
        System.out.println("月:"+ month.toString());
        System.out.println("日:"+ day.toString());
        System.out.println("-----");
        System.out.println("結果を表示");
         getDate001(year, month, day);
        System.out.println("-----");
         getDate004(year, month, day);
        System.out.println("-----");
         getDate002(year, month, day);
        System.out.println("-----");
         getDate003(year, month, day);
        System.out.println("-----");


    }


    /**
    * Calender型からgetTime()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate001(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day);
        Date date = cal.getTime();


        System.out.println("●Calender型からgetTime()して取得した日付");


        System.out.println(date.toString());
    }


    /**
    * Calender型からgetTime()して取得した日付(時分秒0指定).
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate004(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day, 0, 0, 0);
        Date date = cal.getTime();


        System.out.println("●Calender型からgetTime()して取得した日付(時分秒0指定)");


        System.out.println(date.toString());
    }




    /**
    * Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate002(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();


        // 時分秒ミリ秒をクリアする
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.clear(Calendar.MINUTE);
        cal.clear(Calendar.SECOND);
        cal.clear(Calendar.MILLISECOND);


        cal.set(year, month - 1, day);
        Date date = cal.getTime();


        System.out.println("●Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付");


        System.out.println(date.toString());
    }


    /**
    * Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付.
    *
    * @param year
    * @param month
    * @param day
    */
    private static void getDate003(Integer year, Integer month, Integer day) {


        // 年月日を生成
         Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        cal.set(year, month - 1, day);


        String str = "●Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付";


        try {
            Date date = sdf.parse(sdf.format(cal.getTime()));


            System.out.println(str);
            System.out.println(date.toString());
        } catch (ParseException e) {
            System.out.println(str);
            System.out.println("日付生成エラー");
        }
    }

}

出力結果は以下の様になる。

設定値
年:2020
月:1
日:15
-----
結果を表示
●Calender型からgetTime()して取得した日付
Wed Jan 15 09:14:50 JST 2020
-----
●Calender型からgetTime()して取得した日付(時分秒0指定)
Wed Jan 15 00:00:00 JST 2020
-----
●Calender型から時分秒ミリ秒をクリアしてgetTime()して取得した日付
Wed Jan 15 00:00:00 JST 2020
-----
●Calender型からSimpleDateFormatで「yyyy/MM/dd」にしてparse()して取得した日付
Wed Jan 15 00:00:00 JST 2020
-----

pomeraDM200を買いました。

概要

どうしても欲しくて買ってしまったpomera DM200を、先行モデルのpomera DM100と比較も含め記載していきたいと思います。

pomera DM200とは

pomeraと言えば、最初は折りたたみキーボードを搭載したデジタルメモですが、先行モデルのpomera DM100は「ストレートタイプキーボード(折りたたまないキーボード)」のシリーズとして誕生しました。
そして、その後継モデルとして誕生したのがpomera DM200になります。
ちなみに、折りたたみキーボードタイプのシリーズも新商品が生産されており、大きく2種類のモデルタイプがあります。

pomera DM200でよかった点

ポメラSyncでスマホとの同期が楽に

pomera DM100でも、QRコードEvernote連携、Flash Airを利用してPCやスマホに連携することはできましたが、手順が多かったりするのでなかなか別の端末に移行するのは大変でした。
ですが、今回pomera DM200では連携機能として「ポメラSync」が増えました。
これは、Googleのアカウントを利用したもので、pomera DM200とスマホに同一のGoogleアカウントでログインすれば、pomera DM200とiPhoneなら「メモ」に連携することができます。

しかし、Googleアカウントの設定で少しややこしかったり、iPhoneでメモにGoogleのアカウントを利用する方法が一部わかりにくかったりする場合がありますが、そこさえ乗り越えれば快適に同期ができます。
また、ポメラSyncは自分で同期処理を行わなければ同期されませんので、pomera DM200を起動したらまずポメラSyncでスマホとの同期を取って、その後pomera DM200で変更を加え、pomera DM200をしまう前にポメラSyncでスマホへ同期を行うようにした方が良さそうです。

アウトライン機能が増えた

pomera DM100では、表(CSV)とテキストメモの2種類のファイルを扱えましたが、pomera DM200では表(CSV)機能はなくなりました。
しかし、pomera DM200ではテキストメモでのアウトライン機能が追加されました。
アウトライン機能はMicrosoft Wordなどにありますし、テキストメモでもMarkdown記法を用いて利用することができます。
そして、pomera DM200ではアウトライン表記の際のルールとしてMarkdown記法の見出し(H1)で利用する"#"を利用することができ、はてなブログなどMarkdown記法で記事を投稿できるブログサービスやスマホのメモアプリなどにそのまま利用できるのでとても便利です。
私はMarkdown記法を日常でよく利用しているので、これは便利と即利用しました。

pomera DM200に不満な点

バッテリー残量の表示

まず、pomera DM200は前回のpomera DM100と異なり内蔵のリチウムイオンバッテリー駆動となっています。
なので、バッテリーが充電中なのかどうか、残量が大体どれくらいなのかは下のバーにバッテリーアイコンで表示されていますが、実際に電池残量が何%なのかは、メニューから「設定」>「本体情報」を開いて確認する必要があります。
充電式になった上、下のバーにバッテリーアイコンが表示されているのであれば、そこに「xxx%」とバッテリー残量を表示してもらえたらいいのに、と思いました。

開いたときの起動の速度

pomera DM200ではOSがLinuxベースの独自OSへ新しくなったからか、それとも私の方で設定が悪いのかはきちんと調べていないので判りませんが、pomera DM100の時より画面を開いてからの起動が遅いように感じました。
pomera DM100では、画面を開けば即電源が入り、すぐに入力を開始できましたが、pomera DM200では画面を開いてしばらくしてEnterを押したら画面が表示されるような感じなので、ちょっと待ち時間が発生してしまいます。
私の設定が悪い可能性もあるので、もうしばらく確認してみようかと思いますが、今のところはこんなものなのかな、と思ったりもします。

最後に

pomera DM100に比べると、pomera DM200は格段に使いやすくなっていると思います。
まだ購入して2日目ですが、文章を書く気力がとても湧いてくるのでpomera DM200を使うのが楽しいです。
もし、また何か気になることや便利だったことがあれば追記や別記事で書いていければな、と思いました。

PostgreSQL シーケンス操作の小技

概要

PostgreSQLのシーケンスでよく使う採番方法と、シーケンス操作や同一のSQLで複数回シーケンスを利用する場合の方法などをメモしています。
シーケンスについては以下ページを参考にしました。

9.16. シーケンス操作関数 - PostgreSQL9.5

シーケンス操作

前提として、PostgreSQLのシーケンスは非トランザクション処理のため、トランザクションロールバックされても採番した値はロールバックされません。

シーケンスで次の値を取得する

シーケンスで次の値を取得したい場合、nextval()関数にシーケンス名を指定してselectを行います。

-- シーケンスの次の値の取得
SELECT nextval('seq_id');

シーケンスの現在値を取得する

シーケンスで現在の値を取得したい場合、currval()関数にシーケンス名を指定してselectを行います。

-- シーケンスの現在値の取得
SELECT currval('seq_id');

2020/01/15 追記

currval()関数については以下のPostgreSQLドキュメントにあるように、セッション内でのnextval()関数で取得した現在値を表示するようです。

currval
現在のセッションにおいて、そのシーケンスから nextval によって取得された直近の値を返します。 (セッション内で、シーケンスに対し nextval が呼ばれていない場合には、エラーが報告されます。)

なので、nextval()関数が同一セッション内で実行されていない場合は表示できません。
(私も知らなかったので、currval()関数で今のシーケンスの値は何だろうと確認しようとしてエラーになって初めて知りました。)

シーケンスの初期化

シーケンスを初期化したい場合は、setval()関数にシーケンス名、シーケンス作成時に指定した範囲内の値を指定してselectを行います。

-- シーケンスの初期化
SELECT setval('seq_id', 1);

また、既にあるテーブルのIDの件数を設定したい場合は、setval()関数内にSQL文を記載して取得した結果を設定することが出来ます。

-- シーケンスの初期化(トランテーブルの件数を設定)
SELECT
    setval( 
    'seq_id'
    , ( 
      SELECT
          COUNT(T.ID) 
      FROM
        TRN_DATA T
    )
  );

2020/01/14 追記

count()関数ではなくmax()関数を使ってみたところエラーとなったので、count()関数のみであれば可能のようです。

同一のSQLで複数のシーケンスを利用する場合

また、同一のSQL文の中でシーケンスを取得してその値を別のカラムにも設定したい、という場合、以下のようなSQL文を記載することで同一の値を利用する事が出来ます。

-- シーケンスので採番+採番した値を別カラムにも利用
SELECT
    nextval('seq_id') as id
  , currval('seq_id') as create_no;

ちなみに、この時どちらもnextval()関数を使用すると、それぞれで採番されるので、同一の値になりません。
そのため、最初にnextval()関数で採番し、それ以降はcurrval()関数で採番後の現在値を利用するようにします。

そして、以下のようにシーケンスで採番した値を0埋めして取得するなども可能になります。

-- シーケンスので採番+採番した値のLpadの0埋め
-- ※ to_char時に先頭にスペースが入るので、一旦trimをかけてからlpadして0埋めする
SELECT
    nextval('seq_id') as id
  , lpad( 
    trim( 
      to_char(currval('seq_id'), '99999')
    ) 
    , 5
    , '0'
  ) as create_no;

SQLで対象のカラムのデータが複数のカラムのデータのいずれかに含まれている場合

ざっくり概要(ざっくり)

最近、SQLを書いていて「とあるマスタテーブルにあるカラムのデータが、とあるトランザクションテーブルにある複数のカラムのうちどこかに含まれている場合は対象にする」というSQLを書く機会があったので、メモとして残しておきます。

「めんどくさい」のは簡潔にしたい

この業界に入って久しいですが、SQLを極めるほどSQL文を書くということはほとんどなかったので、今回のSQLトランザクションテーブルの複数カラムを単純に「Where句にORで全てのカラムを書く」という方法を考えました。
しかし、複数カラムが実は10カラムあったので、それは大変「めんどくさい」ですし、「ORで全てのカラムをバカ正直に条件に書くのはなんかミスりそうでやだなー」という結論に至ったので、いろいろ調べてみたのでした。

そして、こちらの内容を見つけました。
MySQL - sqlで複数のカラムのデータにいずれかを含んでいる状態|teratail
上記の内容を確認すると、SQLでINで条件を設定する際に、対象の複数カラムを設定すればそれを対象として条件検索してくれるということでした。

SQL

以下の構造のテーブルがあったとします。
マスタテーブル(MST_TABLE)にはカラムはID、ITEM_CDとその他カラムがあり、トランザクションテーブル(TRN_TABLE)にはID、ITEM_CD1からITEM_CD10までの連番カラムとその他のカラムがある前提です。

SQL文としては以下になります。

Select 
    T.ID
From 
    MST_TABLE M
  , TRN_TABLE T
Where 
    M.ITEM_CD IN (
           T.ITEM_CD1
          , T.ITEM_CD2
          , T.ITEM_CD3
          , T.ITEM_CD4
          , T.ITEM_CD5
          , T.ITEM_CD6
          , T.ITEM_CD7
          , T.ITEM_CD8
          , T.ITEM_CD9
          , T.ITEM_CD10
        );

これで、SQLで対象のカラムのデータが複数のカラムのデータのいずれかに含まれている場合のデータを取得することができます。

保育園の入園準備は大変。

はじめに

この記事は、入園準備で食事用エプロンを自分で作らないといけないから作ってみたらこうだった、という記事です。
時間がないわ、とか長文読んでも、という方は下の方にまとめてあるのでスクロールしてまとめへどうぞ。
時間がある方で長文読んでもいいよ、って方はそのままお読みください。

入園が決まれば次は入園準備がいる

保育園への入園が決まって、そこで渡されるのが入園時に必要なものリストだったりするわけですが。
うちの園では食事用のエプロンをタオルとゴムで手作りしないといけないというのがありました。
しかも、1日におやつ2回とお昼ご飯1回の3回分、つまり毎日3枚いるわけです。
毎日洗濯するとして洗い替えと考えても最低6枚、そんな余裕なんてないわ!となる可能性を考えると平日だけでも3枚×5日の15枚いるということに。
めちゃ重労働。

で、まぁ保育園の年度初め(4月入園)の申し込みがうちの市だと前年の11月で、内定通知が年明けて2月で面接、そのご決定通知か来るのが3月末というハードスケジュールでした。
一応うちの地域では、内定通知が来た時点で自分が希望した園ではないからと辞退して待機児童にならなければ、よっぽどのことがない限りそのまま決定されるようなので面接時点で入園のしおりを配布されました。 しかし、まぁ初めての時はそれが確定なのかは判らないわけでして。
なので、準備を進めつつも「ホントにいいのかなぁ」と思いながら準備をしていました。
2人目にもなると「内定来た-!よし、入園準備取りかかるか!」みたいになっていましたが。。。w

食事エプロンを作ってみた雑感

で、そのエプロンを作る際に思ったことや2回目の時に変えたことをメモしておこうかと思います。

1人目の時

まず、園では「食事用のエプロンは子供達が自分で脱ぎ着できるようにご家庭で作っていただいています。フェイスタオルを半分に切っていただいて、それにパジャマのズボンに入れるゴムを付けていただければ完成なので。」という感じで説明されました。
で、その通りフェイスタオルを半分に切ってゴムを通して、とやっていたんですが、選んだフェイスタオルが問題でした。
「保育園だし、やっぱり可愛い柄の方が良いよね!」と色気を出してしまって、ミッキーとかの一枚絵になっているタオルを買ってしまったんですよ、全部。
さあ、フェイスタオルを半分に切ってさて、と気付くのが切り口をそのまま折ってしまうと絵柄が上下逆になる問題。
もう衝撃的でしたね。何せせっかく工場で奇麗に縫われている箇所があるのにそれが上側でゴムを縫い付けるために隠れてしまうわ、下側になった切り口を素人の仕事で奇麗に折り返して縫うという作業が発生するわで作業が増える一方でした。
また、厚手の総柄のフェイスタオルを使ったら縫いにくくて縫いにくくて泣きそうでした。
あと、ゴムをあとで通したらめちゃくちゃ時間かかるし手が痛いし、みたいになりました。
結局完成するまでに半日くらいかかっていたと思います。

2人目の時

なので、2人目の時には考えました。
やっぱり可愛い柄でエプロンは作りたい、しかしフェイスタオルはダメだ。
そこで気付いたのです。
ならば、フェイスタオルのほぼ半分のサイズであるハンドタオルを買えばいいのだ、と。
これはホントに当たりでした。
ハンドタオルって結構2枚組とかで売っていたりする事もあるんですよね。
キャラクターものは厚手のものも少ないですし。
なので、2枚組のものとか、1枚だけど可愛いのとかを全部で15枚になるように購入。
そしてエプロンを作る際は上側を折り返してゴムを縫い付けるだけ。
ゴムは折り返す時に一緒に折っておけば、縫うときに既に通っているし、そのあとゴムの端を縫い付けるだけ。
切る手間もなく、下側の処理も要らず、しかも可愛い柄満載。
これを勝利と言わずしてなんというのか!
みたいな感じでエプロンを大量生産しました。
以前に比べると2時間くらいは作業時間が減ったと思います。

まとめ

  • 開き直って1週間に必要な数のエプロンを作る。
    作るのは大変だけど、毎日の事を考えると「洗濯できてないのに!」というストレスを減らす。
  • タオルはなるべく薄手で縫いやすく。
    多少へたっててもせいぜい2年くらいしか使わないので割り切りも必要。
    どうしてもって時は数枚縫い直せばよし。
  • フェイスタオルを使うなら総柄か無地をチョイス。
    切り口を縫うだけにして作業効率アップ。
  • ゴムはタオルと一緒に折り返して通す時間を節約。
  • 可愛さを妥協できないときはハンドタオルで裁断を避ける。
    可愛いは正義。

ということで、本日はこの辺で。

Gitでの各操作メモ。

業務でGitについて調べたので、自分なりにまとめた内容を残しておこうと思います。
実際に使ったわけではないため間違ってるかもしれないので、参考にする際は書いてあることを全部鵜呑みにせずご自分で判断してご利用ください。

Gitリポジトリのセットアップ

  1. Gitリポジトリを新規作成
    $ git init
  2. ローカルリポジトリを作成
    $ git init [リポジトリ名]
  3. リモートリポジトリ(中央リポジトリ)の作成
    $ git init --bare --shared
  4. リモートリポジトリからローカルリポジトリへの取り込み
    $ git clone [リポジトリ名]
  5. ローカルリポジトリからリモートリポジトリへのクローン
    $ git clone --bare [リポジトリ名]
  6. リモートリポジトリのミラーリポジトリの作成
    $ git clone --mirror [リポジトリ名]
    ミラーリポジトリ$ git remote update
  7. 最近実行されたコミットコミット履歴のみをクローン作成
    $ git clone --depth=1 [リポジトリ名]

git initとgit cloneの違い

git clonegit initに依存しており、$ git cloneを行うと内部で$ git initを実行してから既存リポジトリをコピーしている。

Gitリポジトリへの変更内容の登録

  1. ファイルのステージング(インデックスに追加)
    $ git add [ファイル名]
  2. ステージングされたファイルのコミット
    $ git commit [ファイル名]
  3. ステージングと同時にコミット
    $ git commit -a [ファイル名]
  4. 直前のコミットに今回のコミットを含める
    $ git commit --amend [ファイル名]
  5. 直前のコミットメッセージを変更する
    $ git commit --amend
  6. コミットされていない変更を別領域に保存して作業コピーから取り消す
    $ git stash [ファイル名]
  7. コミットメッセージのフォーマット例

    1行目 :変更内容の要約(タイトル、概要)
    2行目 :空行
    3行目以降:変更した理由(内容、詳細)

リモートでの作業

  1. データをリモートリポジトリから取得する
    $ git fetch
  2. フェッチしてリモートブランチの内容を現在のブランチにマージ
    $ git pull
  3. ローカルとリモートをマージし、リモートへ変更を反映させる
    $ git push

ブランチについて

  1. ブランチの作成(のみ)を行う
    $ git branch [ブランチ名]
  2. ブランチの切り替え
    $ git checkout [ブランチ名]
  3. 分岐した履歴を統合して元に戻す
    $ git merge [マージ先ブランチ名]
  4. ブランチを切り替えてマージ
    $ git merge [マージ元ブランチ名] [マージ先ブランチ名]
  5. ブランチを新規作成と同時に切り替える
    $ git checkout -b [ブランチ名]
  6. リポジトリ内のブランチを一覧化する
    $ git branch

masterブランチ/HEADとは

項目 説明
ブランチ 一連のコミットの先端を表すポインタ。
master デフォルトのブランチ名で大方はメインのブランチのこと。
HEAD 今どのブランチで作業しているのかを保持する特別なポインタ。

マージの際の競合(コンフリクト)

$ git statusで状態を確認して手動でマージを行い、$ git addしてステージングする。
すべてのファイルがステージングできたら、$ git commitを行ってマージコミットを完了させる。

マージの取り消し

  1. マージが未着手で、マージを取りやめるとき
    $ git merge --abort
  2. 競合を解消するために作業ディレクトリ内で変更した内容(コミット済み)をすべて取りやめるとき
    (取り消されたコミットがあったことも履歴に残らない)
    $ git reset --hard HEAD
  3. 競合を解消するために変更した内容(コミット済み)を元に戻すとき
    (取り消した履歴が残るが、取り消したマージコミットに含まれていた変更を再度マージすることはできない)
    $ git revert [コミットID]

ブランチの操作

  1. リポジトリ内のブランチを一覧化する
    $ git branch
  2. リポジトリ内の現在作業中のブランチへマージされているブランチを一覧化する
    $ git branch --merged
  3. リポジトリ内現在作業中のブランチへマージされていないのブランチを一覧化する
    $ git branch --no-merged
  4. マージ済みブランチの削除
    $ git branch -d [ブランチ名]
  5. ブランチの強制削除
    $ git branch -D [ブランチ名]

リベース

  1. リベースを行う
$ git checkout [リベース元のブランチ名]  
$ git rebase [リベース先のブランチ名]  

git rebaseとgit mergeの違い

git rebasegit mergeと同様に、変更を1つのブランチから別のブランチに統合するためのコマンド。
マージはマージ元のブランチはけして変更されないが、ブランチに無関係なマージコミットが発生するため、履歴が乱雑になる可能性がある。
リベースはリベース先のブランチの先頭から新しいコミットのすべてをリベース先のブランチに取り込むことができるが、リベース元のブランチの内容が再書き込みされ、いつその変更がリベース先のブランチに取り込まれたのかわからなくなる。