kurukuru-papaのブログ

主に、ソフトウェア開発に関連したメモを書き溜めたいと思います。

Chromium版EdgeでIEモードを使う際の注意点

Chromium版EdgeのIEモードを使用して、IE専用のイントラのWebアプリケーションを表示していると、人によって、もしくは端末によって、表示や動きに違いがありました。あるWebアプリケーションのページを表示する際、ある人のEdgeではIE11で描画されるのに対して、私のEdgeではIE7として描画されました。調べてみると、人/端末によって、互換表示するIEバージョンが異なっているようです。この動作の差異について調べてみました。

前提

本記事は、Windows10/11において、Chromium版Edgeを使用していることを前提としています。

ポイント

Chromium版EdgeのIEモードにおいて、ページ描画時に使用するIEのバージョンには、Edgeの「IEモード」とIE11の「互換表示設定」が関連する。

Edgeの「IEモード」

Edgeの「IEモード」を有効にする方法はいくつかあります。

  • 一つは、Edgeのメニューから「設定など」>「Internet Explorerモードで再読み込みする」を実行する方法です。
  • 別の方法としては、Edgeのメニューから「設定など」>「設定」を開き、「既定のブラウザー」で、「Internet Explorer の互換性」に設定を行う方法があります。
  • また、エンタープライズサイトリストマネージャーで、対象サイトを登録する方法もあります。

Edgeの「IEモード」を有効にすると、ページの描画には、IE11のエンジンが使われます。IE11のエンジンでは、基本的にはIE11として動作しますが、IE11の「互換表示設定」が有効な場合は、IEの11~5を自動判定して動作することがあります。

エンタープライズサイトリストマネージャーでは、「互換モード」として、「既定のモード」やIE11~5を選択できます。

ちなみに、IEモードで開発者ツールを使うには、IEChooser.exeを使うことができます。実行パスはC:\Windows\System32\F12\IEChooser.exeです。

IE11の「互換表示設定」

IE11の「互換表示設定」が有効な場合、IEの設定やWebページの内容によって、IE11, IE7, IE5あたりを自動判定して動作することがあります。ただし、詳細な条件は煩雑そうで詳しくは調べていないです。

IE11の「互換表示設定」を参照/変更するためには、IEを起動する必要がありますが、私の環境ではスタートメニューなどにIEが表示されませんでした。その場合は、コマンドプロンプトや「ファイル名を指定して実行」から次のコマンドを実行することで、IEを起動して設定することができます。この起動方法は、下記参考サイトに載っていました。

rem IEを起動するコマンド
rem 「bing」部分は変更可能だが、何かしら記述する必要がある。
iexplore bing -embedding

上記では上手くIE起動できないこともありました。その場合は、PowerShellで次のコマンドを実行してIE起動できました。

$objIE = new-object -com InternetExplorer.Application
$objIE.visible=$true

以上のように、Edgeの「IEモード」とIE11の「互換表示設定」は、ページの描画に大きな影響を与えることが分かりました。正しく設定することで、Webアプリケーションをスムーズに表示することができます。しかし、設定が間違っている場合は、表示や動作に不具合が生じる可能性があります。

参考サイト

電子工作初心者が ラズパイで サーボモータ1~4個を動かした方法

私は、電子工作初心者ですが、Raspberry Pi から、サーボモータ4つを動かすことができるようになったので、その時の手順をまとめてみます。

はじめに

サーボモータ1個であれば、いろいろなブログを参考にして、その通りに動かすことができました。 サーボモータを2個以上動かすときは電源の確保が必要で、何が必要なのか、どんな物を購入する必要があるのか悩んでしまい、なかなか進まなかったのですが、手元にあるUSB充電器や、スマホ用モバイルバッテリーが使えることが分かり解決しました。

使用したプログラミング言語Pythonです。PythonからGPIO制御するライブラリがいくつかあり、どれを使ったらよいのか迷いましたが、いくつか使ってみて、pigpioに落ち着きました。

前提条件/動作環境

要約/ポイント

サーボモータ1個であれば、ラズパイからサーボモータへ電気を供給して、動かすことが可能。

サーボモータ2個以上の場合、ラズパイから供給できる電流が足りなくなるので、外部電源が必要。 マイクロサーボSG90は、電圧4.8~5Vで動作する。必要な電流は、いくつかのブログを見ると、だいたい、通常130~200mA、最大500mAとか1.2Aの模様。 今回、USB充電器やスマホ用モバイルバッテリーのUSBポートを使用した。USBの規格では電圧5Vに決められている。電流は、今回使ったバッテリーだと、1Aと2.1Aの2つの口があった。 今回試していないが、通常単3電池(1.5V)を3個使用し1.5V×3個=4.5V、充電式単3電池(1.2V)を4個使用し1.2V×4個=4.8Vでも良さそう。

サーボモータを動かすためには、PWMという制御信号を送る必要があり、そのための仕組みがラズパイやPythonライブラリに用意されている。

サーボモータ1個を動かす(ラズパイ電源)

pigpioインストール

PythonからラズパイのGPIOを制御するため、pigpioというライブラリを使いました。 次のコマンドでインストールできました。

$ pip install pigpio

インストールしたpigpioを使うためには、事前に常駐させておく必要があるので、次のコマンドを実行しておきました。

$ sudo pigpiod

動作確認

ラズパイで用意されているハードウェアPWMを使用するため、GPIO13を使用しました。ハードウェアPWMについては、次のブログを参考にしました。

Raspberry PiのハードウェアPWMをpigpioで出力する - Qiita
・ラズパイではハードウェアPWMに対応しているピンが4つあり、独立に制御できるのは2組ずつです。
・PWM Channel 0 : GPIO12、GPIO18
・PWM Channel 1 : GPIO13、GPIO19

プログラムは、こんな感じです。上手く動きました。

import pigpio
import time

SERVO_PIN = 13
pi = pigpio.pi()

try:
    while True:
        pi.hardware_PWM(SERVO_PIN, 50, int(1000000*1.45/20))
        time.sleep(3)
        pi.hardware_PWM(SERVO_PIN, 50, int(1000000*2.4/20))
        time.sleep(3)
        pi.hardware_PWM(SERVO_PIN, 50, int(1000000*0.5/20))
        time.sleep(3)
except KeyboardInterrupt:
    pass

pi.hardware_PWM(SERVO_PIN, 50, 0)
pi.stop()

DSC_0583_240x240.jpg

サーボモータ1個を動かす(外部電源)

サーボモータ2個を動かすためには、外部電源が必要になるので、外部電源を使う練習をしてみました。 外部電源としてUSB充電器を使っています。USBの仕様が次のように決められているので、サーボモータを動かすのにちょうどよいと考えました。

ユニバーサル・シリアル・バス - Wikipedia
USB2.0 電圧5V、電流500mA
・USB3.x 電圧5V、電流900mA

プログラムは、ラズパイ電源で動かした時と同じです。上手く動かすことができました。

DSC_0582_240x240.jpg

上記では、ハードウェアPWMを使いましたが、ソフトウェアPWMでも動かしてみました。 次のプログラムで、動かすことができましたが、高周波が少し不安定になってしまった気もしました。

import pigpio
import time

SERVO_PIN = 13
pi = pigpio.pi()

try:
    while True:
        pi.set_servo_pulsewidth(SERVO_PIN, 1450)
        time.sleep(3)
        pi.set_servo_pulsewidth(SERVO_PIN, 2400)
        time.sleep(3)
        pi.set_servo_pulsewidth(SERVO_PIN, 500)
        time.sleep(3)
except KeyboardInterrupt:
    pass

pi.set_servo_pulsewidth(SERVO_PIN, 0)
pi.stop()

サーボモータ2個を動かす

ハードウェアPWMを使うため、GPIO12,13に接続しました。 プログラムは次です。サーボモータ1個のときと同じ処理を、2つのサーボモータに対して行っています。 少し不安定ですが、動かすことができました。

import pigpio 
import time

SERVO_PIN_1 = 13 
SERVO_PIN_2 = 12 
pi = pigpio.pi()

try: 
    while True: 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
except KeyboardInterrupt: 
    pass 

pi.hardware_PWM(SERVO_PIN_1, 50, 0) 
pi.hardware_PWM(SERVO_PIN_2, 50, 0) 
pi.stop()

DSC_0582 (1)_240x240.JPG

サーボモータ3個を動かす

サーボモータ3個を動かそうとすると、また電源の壁がありました。 今回使ったUSB充電器では、電流が1Aまでで、サーボモータ3個を動かすのは無理でした。 なので、USB充電器でサーボモータ2個を動かし、ラズパイ電源でサーボモータ1個を動かしました。 あと、USB充電器よりもスマホ用モバイルバッテリーの方が扱いが楽だったので、モバイルバッテリーを使っています。

サーボモータ制御用のGPIOピンですが、GPIO 13,12をハードウェアPWMで使用、GPIO 17をソフトウェアPWMで使用しています。

プログラムを次のようにしました。サーボモータが増えたので、処理が長くなりましたが、やっていることは今までとあまり変わりませんでした。

import pigpio 
import time

SERVO_PIN_1 = 13 
SERVO_PIN_2 = 12 
SERVO_PIN_3 = 17 
pi = pigpio.pi() 

try: 
    while True: 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 1450) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 2400) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 500) 
        time.sleep(1) 
except KeyboardInterrupt: 
    pass 

pi.hardware_PWM(SERVO_PIN_1, 50, 0) 
pi.hardware_PWM(SERVO_PIN_2, 50, 0) 
pi.set_servo_pulsewidth(SERVO_PIN_3, 0) 
pi.stop()

DSC_0582(2)_240x240.jpg

サーボモータ4個を動かす

サーボモータ4個を動かそうとすると、またまた電源の壁がありました。 モバイルバッテリーだとサーボモータ2個まで、ラズパイ電源だと1個までなので、最大3個までしか動かせられません。 でも、モバイルバッテリーを見ると、USBポートが2つあったので、各ポートでサーボモータ2個を動かし、計4個動かすことにしました。結果、上手く動きました。

サーボモータ制御用のGPIOピンですが、GPIO 13,12をハードウェアPWMで使用、GPIO 27,23をソフトウェアPWMで使用しています。

プログラムを次のようにしました。これも、サーボモータの増加に伴い処理が増えただけで、やっていることは同じです。

import pigpio 
import time 

SERVO_PIN_1 = 13 
SERVO_PIN_2 = 12 
SERVO_PIN_3 = 27 
SERVO_PIN_4 = 23 
pi = pigpio.pi() 

try: 
    while True: 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*1.45/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 1450) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_4, 1450) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*2.4/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 2400) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_4, 2400) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_1, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
        pi.hardware_PWM(SERVO_PIN_2, 50, int(1000000*0.5/20)) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_3, 500) 
        time.sleep(1) 
        pi.set_servo_pulsewidth(SERVO_PIN_4, 500) 
        time.sleep(1) 
except KeyboardInterrupt: 
    pass 

pi.hardware_PWM(SERVO_PIN_1, 50, 0) 
pi.hardware_PWM(SERVO_PIN_2, 50, 0) 
pi.set_servo_pulsewidth(SERVO_PIN_3, 0) 
pi.set_servo_pulsewidth(SERVO_PIN_4, 0) 
pi.stop()

DSC_0583_5_240x240.JPG DSC_0586_240x240.JPG

トラブル対応

作業中、躓いたところを書いておきます。

pigpioインストールエラー

Pythonライブラリのpigpioをインストールしようとして、試行錯誤、次のようなコマンドを実行していました。

$ pip install pygpio

そして、Pythonの対話形式モードから使おうとすると次のエラーが発生しました。

In [1]: import pygpio
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-1-462f3c478519> in <module>
----> 1 import pygpio
~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/pygpio/__init__.py in <module>
      1 from __future__ import absolute_import
      2
----> 3 from Adafruit_GPIO.GPIO import *
ModuleNotFoundError: No module named 'Adafruit_GPIO'

原因は、ライブラリ名のスペルミスで、誤「pygpio」、正「pigpio」でした。

pigpioインポートエラー

上手くインストールできた後、Pythonからpigpioをインポートしようとすると、次のエラーが発生することがありました。

Can't connect to pigpio at localhost(8888) 
Did you start the pigpio daemon? E.g. sudo pigpiod 
Did you specify the correct Pi host/port in the environment 
variables PIGPIO_ADDR/PIGPIO_PORT? 
E.g. export PIGPIO_ADDR=soft, export PIGPIO_PORT=8888 
Did you specify the correct Pi host/port in the 
pigpio.pi() function? E.g. pigpio.pi('soft', 8888) 

これは、事前にpigpiodを起動していないからでした。次のコマンドを実行してから、インポートすると解決です。

$ sudo pigpiod

おわりに

今回、Raspberry Pi から、サーボモータを1個、2個、3個、4個と増やして動かしてみました。電子工作の経験がなく、電源管理に悩まされましたが、動かすことができました。ものが動くと達成感がありますね。

サーボモータ4個を動かした際、4つ足のロボットを作れるかも、と無邪気に素人考えをしてしまいました。 ためしに作成して、何となく歩行した気がするので、動画も作成してみました。良かったらご覧ください。

参考サイト

Raspberry Pi GPIO Pinout

  • ラズパイのGPIOピンの配置を確認しました。

マイクロサーボ9g SG-90: パーツ一般 秋月電子通商-電子部品・ネット通販

  • マイクロサーボSG90の主な仕様が書かれていて助かりました。

Raspberry Piでサーボモーター動作 Raspberry PiのハードウェアPWMをpigpioで出力する - Qiita

ユニバーサル・シリアル・バス - Wikipedia

  • USBの電源供給仕様を確認しました。

見た目もスッキリ! ウインドウの配置・サイズを変更する方法

ブラウザを操作していてキャプチャをとるとき、どうせならウインドウサイズを揃えて、きれいにキャプチャしたくなります。 マウス操作で、いい感じに変更してもよいのですが、少し手間です。

そんなときのため、PowerShellでサクッと解決する方法を調べてみました。

ポイント

  1. ウインドウサイズの変更は、user32.dll の bool MoveWindow() でできる。ただし、呼び出すためにお作法が必要。
  2. ウインドウの一覧は、Get-Process -Name $name | where { $_.MainWindowTitle -ne "" } で取得できる。ただし、ブラウザで複数ウインドウを表示している場合、アクティブなウインドウのみ取得可能な模様。

スクリプト

Edge, IE, Chrome, Firefox を対象に、1024x768サイズに変更するスクリプトです。 対象ブラウザやサイズ、位置は、お好みで変更してみてください。

$name = "msedge","iexplore","chrome","firefox"
$w = 1024
$h = 768
$x = 0
$y = 0
Add-Type @"
  using System;
  using System.Runtime.InteropServices;
  public class Win32Api {
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
  }
"@
Get-Process -Name $name | where { $_.MainWindowTitle -ne "" } | foreach {
  [Win32Api]::MoveWindow($_.MainWindowHandle, $x, $y, $w, $h, $true) | Out-Null
}

動作確認環境

参考にさせていただいたサイト

エクスプローラーが散らかってしまう? PowerShellで整理できました!

私がWindows環境で作業をしていると、いつの間にか色々なアプリケーションのウインドウが表示されています。 目的のウインドウに切り替えようとすると、ウインドウをかきわけるのに、何度もマウス操作しなければなりません。タスクバーの操作でも、何回かクリックが必要です。 特に、10個ぐらい開いてしまっているエクスプローラーの操作が大変です。

そこで、PowerShellを使って、これらのエクスプローラーをきれいに並べることで、扱いやすくしてみました。

ポイント

  1. $shell=New-Object -ComObject Shell.Application; $shell.Windows()エクスプローラーの情報を取得できました。
  2. $shell.Windows() | foreach { $_.Left=0; $_.Top=0; $_.Width=800; $_.Height=600; }エクスプローラーの位置とサイズを変更できました。
  3. [System.Windows.Forms.Screen]::PrimaryScreen[System.Windows.Forms.Screen]::AllScreens で画面サイズを取得できました。

結果イメージ

次のように、エクスプローラーを並べてみました。

コメント 2020-07-26 203203_2.png

スクリプト

画面の左上から右下へ、少しずらしながら重ねて並べる。 なお、重ねる順番は、表示しているパス順にしました。

$w = 800
$h = 600
$x = 0
$y = 0
$dx = 30
$dy = 30
$shell = New-Object -ComObject Shell.Application
$shell.Windows() | where { $_.Name -eq "エクスプローラー" } | sort LocationURL | foreach {
  $_.Left=$x; $_.Top=$y; $_.Width=$w; $_.Height=$h; $x+=$dx; $y+=$dy;
}

同様に、画面右下を基準に並べる。

Add-Type -AssemblyName System.Windows.Forms
$sw = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Width
$sh = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Height
$w = 800
$h = 600
$x = $sw - $w
$y = $sh - $h
$dx = -30
$dy = -30
$shell = New-Object -ComObject Shell.Application
$shell.Windows() | where { $_.Name -eq "エクスプローラー" } | sort LocationURL | foreach {
  $_.Left=$x; $_.Top=$y; $_.Width=$w; $_.Height=$h; $x+=$dx; $y+=$dy;
}

動作確認環境

参考にさせていただいたサイト

ロックしないでファイル読み込み

少し特殊ですが、ファイルをロックしないで読み込みしたくなりました。PowerShellで実装してみましたので、メモしておきます。

実装方法

ファイルをロックせずに、ファイル内容を標準出力します。ポイントは、ファイルオープン時に指定するFileShareのReadWrite、Deleteです。

$inpath = 'D:\tmp\dummy.txt'
$sr = [System.IO.StreamReader]::new(
    [System.IO.FileStream]::new(
        $inpath,
        [System.IO.FileMode]::Open,
        [System.IO.FileAccess]::Read,
        # 他プログラムの読み書き、削除をロックしない
        [System.IO.FileShare]::ReadWrite + [System.IO.FileShare]::Delete))
echo $sr.ReadToEnd()
$sr.Close()

ファイルオープン部分は、次のように書くこともできますね。

$fs = [System.IO.File]::Open(
    $inpath,
    [System.IO.FileMode]::Open,
    [System.IO.FileAccess]::Read,
    [System.IO.FileShare]::ReadWrite + [System.IO.FileShare]::Delete)
$sr = [System.IO.StreamReader]::new($fs)

テキスト読み込み部分は、次のようにして、1行ごとに処理することもできますね。

while (($line = $sr.ReadLine()) -ne $null) {
    echo $line
}

ワンライナー

Windowsコマンドプロンプトから、ワンライナーで実行する場合は、次のように書けました。

powershell -Command "$sr = [System.IO.StreamReader]::new([System.IO.FileStream]::new('D:\tmp\dummy.txt', [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite + [System.IO.FileShare]::Delete)); echo $sr.ReadToEnd(); $sr.Close()"

動作確認方法

上述のファイル読み込み中に、ロックがかかっていないことを動作確認するときは、読み込み処理の途中で、Start-Sleep 10(10秒のスリープ)などを入れると便利です。当プログラムを実行して、スリープ中に、他のプログラムから読み書き、削除ができることを確認できるはずです。なお、他のプログラムがファイル削除した場合、削除処理成功しますが、ファイルはまだ残っていて、当プログラムがファイルクローズ後に、ファイルが削除されました。

動作確認環境

参考

Windows標準機能だけで文字列置換できた!

まったく予期していなかったのですが、Windows標準機能だけで、テキストファイルの文字列置換を行えるのですね。びっくりです。

次の2つのサイトが非常に参考になりました。

knowledge.reontosanta.com
blogs.yahoo.co.jp

私が書いたWindowsバッチファイル(StringConverter.bat)は、次のようになりました。

@echo off
set basedir=%~dp0
set basename=%~n0
set batname=%~n0%~x0
set datestr=%DATE:/=%
set timestrtmp=%TIME: =0%
set timestr=%timestrtmp:~0,2%%timestrtmp:~3,2%%timestrtmp:~6,2%
set timestamp=%datestr%-%timestr%

:INIT
if "%1"=="/?" (
  echo 使い方:%batname% 入力ファイル 出力ファイル 置換前文字列 置換後文字列
  exit /b 0
)
if "%4"=="" (
  echo 引数の数が不正です。
  goto ERROR
)
set inpath=%1
set outpath=%2
set before=%3
set after=%4

:MAIN
call :LOG 処理開始します。


type nul > %outpath%
setlocal enabledelayedexpansion
for /f "tokens=1* delims=: eol=" %%a in ('findstr /n "^" %inpath%') do (
  set line=%%b
  if not "!line!" == "" (
    set line=!line:%before%=%after%!
  )
  echo.!line!>> %outpath%
)
endlocal


:END
call :LOG 正常終了です。
exit /b 0

:ERROR
call :LOG 異常終了です。
exit /b 1

:LOG
echo %DATE% %TIME% %basename% %1
exit /b 0

:EOF

このWindowsバッチファイルは、次のように実行します。

StringConverter.bat 入力ファイル 出力ファイル 置換前文字列 置換後文字列

入力ファイルのサンプルです。

サンプルデータです。
ShiftJIS、CRLFで記述されています。

空白区切り:カラム1 カラム2 カラム3
カンマ区切り:カラム1,カラム2,カラム3
タブ区切り:カラム1 カラム2 カラム3

;セミコロンで始まる行

バッチ実行後の出力ファイルは、次のようになりました。

サンプルデータです。
ShiftJIS、CRLFで記述されています。

空白区切り:column1 column2 column3
カンマ区切り:column1,column2,column3
タブ区切り:column1 column2 column3

;セミコロンで始まる行

Javaで日時を扱う(Java8)

Java8では、日時を扱うクラス群が様変わりしてしまいました。これを機に、日時の扱いをまとめてみたいと思います。前回は、Java7までの日時、今回は、Java8での日時を書きました。

Java8では、Java7までのDate,Calendarの代替として、次のクラスなどが導入されましたね。

現在/指定日時の作成

Instantクラスの現在/指定日時を作成する。

// 現在日時
Instant nowInstant = Instant.now();
// long→Instant
Instant instant2 = Instant.ofEpochMilli(msec1);
// LocalDateTime→Instant
Instant instant3b = localDt1.toInstant(ZoneId.systemDefault().getRules().getOffset(Instant.EPOCH));
// ZonedDateTime→Instant
Instant instant4ca = zonedDt1.toInstant();
Instant instant4cb = Instant.from(zonedDt1);
// 文字列→Instant
Instant instant5 = Instant.parse("2007-12-03T10:15:30.00Z");

LocalDateTimeクラスの現在/指定日時を作成する。

// 現在日時
LocalDateTime nowLocalDt = LocalDateTime.now();
// 指定日時
LocalDateTime localDt2 = LocalDateTime.of(2016, 5, 2, 15, 0, 0);
// long→LocalDateTime
LocalDateTime localDt3 = LocalDateTime.ofInstant(Instant.ofEpochMilli(msec1), ZoneId.systemDefault());
// Instant→LocalDateTime
LocalDateTime localDt4 = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault());
// ZonedDateTime→LocalDateTime
LocalDateTime localDt5a = zonedDt1.toLocalDateTime();
LocalDateTime localDt5b = LocalDateTime.from(zonedDt1);
// 文字列→LocalDateTime
LocalDateTime localDt6a = LocalDateTime.parse("2007-12-03T10:15:30.123");
LocalDateTime localDt6b = LocalDateTime.parse("2007/12/03 10:15:30.123",
		DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"));

ZonedDateTimeの現在/指定日時を作成する。

// 現在日時
ZonedDateTime nowZonedDt = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
// 指定日時
ZonedDateTime zonedDt2 = ZonedDateTime.of(2016, 5, 2, 15, 0, 0, 0, ZoneId.of("Asia/Tokyo"));
// long→ZonedDateTime
ZonedDateTime zonedDt3 = ZonedDateTime.ofInstant(Instant.ofEpochMilli(msec1), ZoneId.systemDefault());
// Instant→ZonedDateTime
ZonedDateTime zonedDt4a = instant1.atZone(ZoneId.systemDefault());
ZonedDateTime zonedDt4b = ZonedDateTime.ofInstant(instant1, ZoneId.systemDefault());
// LocalDateTime→ZonedDateTime
ZonedDateTime zonedDt5a = localDt1.atZone(ZoneId.systemDefault());
ZonedDateTime zonedDt5b = ZonedDateTime.ofLocal(localDt1, ZoneId.systemDefault(), null);
// 文字列→ZonedDateTime型
ZonedDateTime zonedDt6a = ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]");
ZonedDateTime zonedDt6b = ZonedDateTime.parse("2016/05/02 10:15:30 Asia/Tokyo",
		DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss VV"));
ZonedDateTime zonedDt6c = ZonedDateTime.parse("2016/05/02 10:15:30 JST",
		DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss zzz"));

文字列の現在/指定日時を作成する。

// Instant→文字列
String instantStr1 = DateTimeFormatter.ISO_INSTANT.format(instant1);
String instantStr2 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS")
		.format(LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()));
// LocalDateTime→文字列
String localStr1 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS").format(localDt1);
// ZonedDateTime→文字列
// "xxxxx VV" - (例)"+09:00 Asia/Tokyo"
// "xxxx zzz" - (例)"+0900 JST"
String zonedStr1a = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSxxxxx VV").format(zonedDt1);
String zonedStr1b = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSSxxxx zzz").format(zonedDt1);

日時から日付/時刻の取り出し

// Instant 日時→日付
Instant ymdInstant1 = instant1.truncatedTo(ChronoUnit.DAYS);

// LocalDateTime 日時→日付、時刻
LocalDate localDate1 = localDt1.toLocalDate();
LocalTime localTime1 = localDt1.toLocalTime();

// ZonedDateTime 日時→日付、時刻
ZonedDateTime ymdZoned1 = zonedDt1.truncatedTo(ChronoUnit.DAYS);
LocalDate localDate2 = zonedDt1.toLocalDate();
LocalTime localTime2 = zonedDt1.toLocalTime();

曜日の取得

// Instant→曜日
String instantWeek1 = DateTimeFormatter.ofPattern("E, EEEE", Locale.JAPANESE)
		.format(LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()));
String instantWeek2 = LocalDateTime.ofInstant(instant1, ZoneId.systemDefault()).getDayOfWeek()
		.getDisplayName(TextStyle.FULL, Locale.JAPANESE);

// LocalDateTime→曜日
// ※"E","EEEE"は、タイムゾーンによって曜日の表記が変わる。
String localWeek1 = DateTimeFormatter.ofPattern("E, EEEE", Locale.JAPANESE).format(localDt1);
String localWeek2 = localDt1.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.JAPANESE);

// ZonedDateTime→曜日
// ※"E","EEEE"は、タイムゾーンによって曜日の表記が変わる。
String zonedWeek1 = DateTimeFormatter.ofPattern("E, EEEE", Locale.JAPANESE).format(zonedDt1);
String zonedWeek2 = zonedDt1.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.JAPANESE);

日時計

加算/減算

// LocalDateTime 1秒後、1日後、1ヶ月後
LocalDateTime localNextSec = localDt1.plusSeconds(1);
LocalDateTime localNextDay = localDt1.plusDays(1);
LocalDateTime localNextMonth = localDt1.plusMonths(1);
// ZonedDateTime 1秒後、1日後、1ヶ月後
ZonedDateTime zonedNextSec = zonedDt1.plusSeconds(1);
ZonedDateTime zonedNextDay = zonedDt1.plusDays(1);
ZonedDateTime zonedNextMonth = zonedDt1.plusMonths(1);

月初/月末を求める。

// LocalDateTime 月初、月末
LocalDateTime localMonthBeginning = localDt1.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime localMonthEnd = localDt1.with(TemporalAdjusters.lastDayOfMonth());
// ZonedDateTime 月初、月末
ZonedDateTime zonedMonthBeginning = zonedDt1.with(TemporalAdjusters.firstDayOfMonth());
ZonedDateTime zonedMonthEnd = zonedDt1.with(TemporalAdjusters.lastDayOfMonth());

差を求める。

// LocalDateTime 日時の差 ミリ秒単位、日数単位(切り捨て)
long localDiffMsec = ChronoUnit.MILLIS.between(localDt1, localNextDay);
long localDiffDays1 = ChronoUnit.DAYS.between(localDt1, localNextDay);
long localDiffDays2 = ChronoUnit.DAYS.between(localDt1.toLocalDate(), localNextDay.toLocalDate());
// LocalDateTime 日時の差 時分秒単位、年月日単位
Duration localDuration = Duration.between(localDt1, localNextDay);
Period localPeriod = Period.between(localDt1.toLocalDate(), localNextDay.toLocalDate());
// ZonedDateTime 日時の差 ミリ秒単位、日数単位(切り捨て)
long zonedDiffMsec = ChronoUnit.MILLIS.between(zonedDt1, zonedNextDay);
long zonedDiffDays1 = ChronoUnit.DAYS.between(zonedDt1, zonedNextDay);
long zonedDiffDays2 = ChronoUnit.DAYS.between(zonedDt1.toLocalDate(), zonedNextDay.toLocalDate());
// ZonedDateTime 日時の差 時分秒単位、年月日単位
Duration zonedDuration = Duration.between(zonedDt1, zonedNextDay);
Period zonedPeriod = Period.between(zonedDt1.toLocalDate(), zonedNextDay.toLocalDate());

比較する。

// LocalDateTime
boolean localAfterFlag = localNextDay.isAfter(localDt1);
// ZonedDateTime
boolean zonedAfterFlag = zonedNextDay.isAfter(zonedDt1);

参考