日付・時間(標準API)

標準API

これまでのプログラムの作成時にListやMap等を使用しました。

ソース上部にこんな文字が記載されていたかと思われます。

import java.util.LinkedMap;
import java.util.LinkedHashMap;
import java.util.ArrayList;
import java.util.List;

これらの処理はJavaが標準で持っている機能で、ListやLinkedMap等の使用時にJavaに対し「この機能を使うのでインポートします」と宣言しています。
Eclipse上ではListやLinkedMapの機能を使用しようとすると、自動的にこの「import」が入力されます。

このような機能をまとめて、他から呼び出すようにしたプログラムをAPIと呼びます。

API(Applicatoin Programming Interface)は、機能をまとめて他のプログラムから呼び出せるようにした部品です。様々なAPI(部品)をまとめたものをライブラリといいます。

JavaでのAPIは主にクラスという単位にまとめられています。そのためのJavaの標準APIをクラスライブラリと呼ぶこともあります。
import内の一番左側「java」が該当します。

次にAPIを使用する際に必要なクラスが格納されている通り道をパッケージと呼び、どのようなAPIがあるかをジャンル分けをしています。
文字列を扱う「String」や出力に使う「System」も実はクラスと呼ばれているものの一つです。

このAPIがあればかなり多様なプログラムを作成する事が可能になりますが、とてもたくさんあるため、全てのAPIを覚える必要はありません。代表的なライブラリは以下の通りです。

パッケージ概要
java.langJavaプログラミング言語の設計にあたり基本的なクラスを提供
java.utilプログラミングを便利にする様々なクラス群
java.math指数関数、対数関数、平方根、および三角関数といった基本的な数値処理を行うための便利なメソッドが用意されている
java.netネットワーク・アプリケーションを実装するためのクラス群
java.ioファイルの読み書きなど、データを逐次処理するためのクラス群

「String」や「Boolean」は「java.lang」に含まれているクラスになります。
ただ、ほかのパッケージと違い、使用頻度がかなり高いパッケージなので、この「java.lang」はimport分を記述しなくても自動的にインポートされるという特徴があります。

なお、「java.lang」内には他に「System」「Integer」「Object」「Math」等が該当します。

日を取り扱うAPI(LocalDate)

システムを開発する上で日付と時間を扱うことはたくさんあります。
例えば登録した日時のデータ登録や現在の日付から1年後までの有効期限の登録など・・・・思いつくだけでも多岐に渡ります。 そして様々なシステムで色々な形やフォーマットで使用されます。

日付を扱う処理でよく使用されているのはLocalDateです。
この処理では日に関する処理を色々行うことができます。
日付の型を宣言する際は、以下のように記述します。

LocalDate 変数名 = LocalDate.処理();

中身を入れるときは、newを使った処理と違い、LocalDateのどの処理をするのかまで記載して実行する必要があります。

「com.cmps.day」パッケージを作成し、その中に「Day」クラスを作成してください。

// パターン1、日付の取得
//日付を取得するAPIはLocalDateを使用する。
LocalDate date = LocalDate.now();
System.out.println(" 年月日" + date);
       
// パターン2、日付の指定
//日付を指定時は「.of(年,月,日)」を使用する。
date = LocalDate.of(2100, 1, 1);
System.out.println(" 年月日" + date);
       
// パターン3、MpnthというAPIで月を取得
date = LocalDate.of(2100, Month.FEBRUARY, 1);
System.out.println(" 年月日" + date);

パターン1では、処理部分に「now()」を入れることで、現在の時間を取得しています。

パターン2では、「LocalDate.of(年, 月, 日);」で各年月日を指定して、2100年1月1日を出力しています。

パターン3では、パターン2で行った月の指定をMonthというAPIを使用して行っています。

「.get○○()」を使うことで、年や月、日にちで個別に出力することも可能です。

// 現在の時間を取得
date = LocalDate.now();
       
// 年・月・日を個別に取得
int year = date.getYear();
int month = date.getMonthValue();
int day = date.getDayOfMonth(); 
       
System.out.println("今年は" + year +"年");
System.out.println("今月は" + month +"月");
System.out.println("今日は" + day +"日");


// String型で月を取得
String monthString = date.getMonth().toString();
System.out.println("今月は" + monthString);
       
// Month型で月を取得
Month month2 = date.getMonth();
System.out.println("今月は" + month2);
       
// 曜日を取得する。(曜日はDayOfWeekで取得可能)
DayOfWeek week = date.getDayOfWeek();
System.out.println("今日は" + week);
      
// String型で曜日を取得
String weekString = date.getDayOfWeek().toString();
System.out.println("今日は" + weekString);

月、曜日を取得する場合のみ、取得した時の型が変わってしまうということに注意してください。

「Month」APIはデータの型としても使用することが可能です。
Stringで取得したい場合はgetMonthの後ろに「toString()」を付けることでString型の取得が可能になります。

DayOfweekも「java,time」パッケージのクラスの一つとして使用することが可能です。
Stringで取得したい場合はgetDayOfweekの後ろに「toString()」を付けることでString型の取得が可能になります。

処理内に存在しない日付を入れるとどうなるでしょうか。

// 存在しない日付を格納
date  = LocalDate.of(2100, 2, 30);
System.out.println(" 年月日:" + date);

LocalDateには指定した日が存在するかを確認する機能があります。

もし存在しない日を指定した場合、「DateTimeException」というエラーが発生します。

時間を取り扱うAPI(LocalTime)

時間を扱うにはLocalTimeというAPIを使用します。


考え方は、先程のLocalDateと同様です。
以下のソースを記述してみましょう。

// パターン1、時間を取得
LocalTime time = LocalTime.now();
System.out.println(" 時間:" + time);

       
// パターン2、時間の中身を取得
//時間を指定時は「.of(時間,分)」を使って指定する。
       
// .of(時間,分)
time = LocalTime.of(12, 30);
System.out.println(" 時間:" + time);
       
// .of(時間,分,秒)
time = LocalTime.of(12, 30, 30);
System.out.println(" 時間:" + time);
        
// .of(時間,分,秒,ナノ秒)
time = LocalTime.of(12, 30, 30, 100);
System.out.println(" 時間:" + time);

使い方はほぼLocalDateと同様ですが、指定方法が異なります。

時間を指定する際は、左から「時間、分、秒、ナノ秒」を指定することが可能で、
出力した時にどこまで指定したかによって出力結果が変わります。

LocalTimeでは、秒、ナノ秒が0秒の場合は表示が不要であると判断され、出力を絞るという特徴があります。
出力上省略されているだけで、個別で取得した場合は「0」を取得することができます。

LocalDate同様、「.get○○()」を使うことで、時間、分、秒、ナノ秒で個別に出力することも可能です。

// 時間・分・秒・ナノ秒を個別に取得する。
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond();
int nano = time.getNano();

System.out.println(" hour  :" + hour);
System.out.println(" minute:" + minute);
System.out.println(" second:" + second);
System.out.println(" nano  :" + nano);
yyyy,MM,dd,hh,mm,ss,nano

時間と日次を取り扱うAPI(LocalDateTime)

LocalDateTimeは、時間と日時の両方を扱えるAPIです。

使用方法は大きく変わりませんが、先ほどの2つと違って、保持するデータ量が多いため、処理時間がほんの少しだけ重たいです。
1回処理をするだけなら誤差レベルで気にする必要もありませんが、
1分間に1万、2万、と処理を重ねてシステムに負担がかかるような処理で使うのであれば、
どの時間APIを使うか現場で少し検討したほうが良いです。

// パターン1、現在の日時を取得
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(" 時間:" + dateTime);

       
// パターン2、日時の中身を指定
       
// 日時の場合は「.of(年,月,日,時間,分)」を使って指定
dateTime = LocalDateTime.of(2025, 12, 11, 22, 33);
System.out.println(" 時間:" + dateTime);
       
// 元の指定に加えて秒を指定
// .of(yyyy,MM,dd,hh,mm,ss)
dateTime = LocalDateTime.of(2025, 12, 11, 22, 33,  44);
System.out.println(" 時間:" + dateTime);
       
//更に追加でnano秒を指定
//  .of(yyyy,MM,dd,hh,mm,ss,nano)
dateTime = LocalDateTime.of(2025, 12, 11, 22, 33,  44, 55);
System.out.println(" 時間:" + dateTime);

日付に加え、時間も指定するので、必然的に値の数も増えます。
これまで必要だったものを両方とも追加する必要があるので、値の位置には気を使う必要があります。

また、LocalDateTimeでは、指定をする時に、予めLocalDateとLocalTimeを作っていればそのまま代入することが可能です。
なお、LocalDateTimeの値をLoaclDateやLocalTimeに移行する時は「toLocalDate();」「toLocalTime()」が使えます。

// LocalDate,LocalTimeを使用して指定
LocalDate dateLocalDate = LocalDate.of(2025, 12, 11);
LocalTime timeLocalTime = LocalTime.of(22, 33);
dateTime = LocalDateTime.of(dateLocalDate, timeLocalTime);
       
System.out.println(" 時間:" + dateTime);


// LocalDateTimeの値をLoaclDateやLocalTimeに移行
LocalDateTime dateTimeTrans = LocalDateTime.now();
System.out.println(" 時間:" + dateTimeTrans);
       
LocalDate dateTrans = dateTimeTrans.toLocalDate();
System.out.println("LocalDateへ移行:" + dateTrans);
       
LocalTime timeTransTime = dateTimeTrans.toLocalTime();
System.out.println("LocalTimeへ移行:" + timeTransTime);

年や月、日にちや時間などの各値は、LocalDateとLocalTimeで取得した方法と全く同じ方法で取得できます。

文字列から値を取得し、LocalDate型に取り込む

文字列に書き起こした日時を日時型に変換し、値を置き換えることが可能です。

変換は以下の記載方法で可能です。

LocalDateTime.parse(変換したい日時,DateTimeFormatter.ofPattern(日時の形式));

実際に記述してみましょう。

// 文字列からLocalDate型へ
String dateString = "2025/12/31";
// LocalDateTime.parse(変換したい日時,DateTimeFormatter.ofPattern(日時の形式));
LocalDate dateParse = LocalDate.parse(dateString,DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(dateParse);
       
// 文字列からLocalTime型へ
String timeString = "11:22:33";
LocalTime timeParse = LocalTime.parse(timeString,DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(timeParse);
       
// 文字列からLocalDateTime型へ
String dateTimeString = "2025-12-31 11:22:33";
LocalDateTime dateTimeParse = LocalDateTime.parse(dateTimeString,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTimeParse);

例では、まずString変数に取り込みたい値を入れた後に、
LocalDateTime.parse()DateTimeFormatter.ofPatternを使用して変換しています。

この時、String変数内の「/」はofPatternで指定した記号と同じようにする必要があります。
例えば、「2024/01/12」の文字列を変換したい場合は「yyyy(年)/MM(月)/DD(日)」の形式で変換可能です。
「/」の他にも、「-」や「_」等も使えます。

ここで出てくるyやM,D等のアルファベットは予約語、と言われているもので、数字の扱いを日時に置き換えるとどのような形になるのかを見ています。

Formatter内での予約語英語での意味予約語の詳細出現度
yyear of era
Mmonth of year
Eday of week曜日
Hhour-of-day (0-23)0 基準の 24 時間表記
mminute of hour
ssecond-of-minute
dday of month
hclock-hour-of-am-pm (1-12) number
aam-pm-of-dayAMかPMか
Khour-of-am-pm (0-11)午前午後表記前提での時
Sfraction of secondミリ秒
Gera紀元前後
uyear
Dday of year年の中での日
Qquarter of year第◯四半期
Yweek based year週の始まりを年の初めとして扱う年表記
wweek of week based yearその年の第◯週
Wweek of month月第◯週
eloclalized day of week日曜日を基準に曜日を数字表示
kclock-hour-of-am-pm (1-24)24 時間表記での時
Amilli of day1 日の始まりを基準にした時の経過ミリ秒
nnano of secondナノ秒
Nnano of day1 日の始まりを基準にした時の経過ナノ秒
Vtime zone IDタイムゾーンID
ztime zone nameタイムゾーン名
Olocalized zone offsetUTCとの時差 (表示が GMT±◯◯:00 となる)
Xzone offset ‘Z’ for zeroUTCとの時差 (±0:00 の時はZ表示)
xzone offsetUTCとの時差
Zzone offsetUTCとの時差
ppad next直後のパターンを固定幅に変更

予約語以外で文字列に使われている「/」は日付を取り込むときには無視されるように処理されます。

そのため、文字列の「/」の位置があっているかつ、予約語で使用されていなければどんな言葉を入れても構いません。

表示方法の変更方法(DateTimeFormatter)

ここまでは処理の時間について記載しました。
ですが、このままの状態だと少しデータとしては見にくいと感じる部分があります。

そこで、表示を変更して見やすい見た目にしてみましょう。

// 現在日時を取得
LocalDateTime nowLocalDateTime = LocalDateTime.now();
System.out.println(" オリジナル:" + nowLocalDateTime);
       
// 表示形式を指定
// パターン1(通常の変更)
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String getDateTime = format.format(nowLocalDateTime);
System.out.println(" 変換後:" + getDateTime);
       
// パターン2(日本語でわかりやすく)
DateTimeFormatter formatJapanese = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH時mm分ss秒");
String getDateTimeJapanese = formatJapanese.format(nowLocalDateTime);
System.out.println(" 変換後:" + getDateTimeJapanese);
       
// パターン3(既存のフォーマットを使用)
DateTimeFormatter formatExist = DateTimeFormatter.ISO_DATE;
String getDateTimeExist = formatExist.format(nowLocalDateTime);
System.out.println(" 変換後:" + getDateTimeExist);

これらの処理は、出力される日時の出力方法を変更しています。

変更には、DateTimeFormatterクラスを使用します。
以下の記載方法でフォーマットの指定が可能です(記載されているのはパターン1のフォーマット)。

DateTimeFormatter 変数名 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");

パターン2も同じ方法を使い、フォーマットを変換しています。

パターン3ではDateFormatter内にある、時間をフォーマットするための書式を使い変換をしています。
書式については以下の参照サイトをご覧ください。
参照サイト:DateTimeFormatterフィールドの概要

特定の日や時間を設定した新たなインスタンスを生成(TemporalAdjustersインターフェース)

このTemporalAdjusterインターフェース呼び出し元のインスタンスから、例えば月末など、特定の日や時間を設定した新たなインスタンスを生成するために使うインターフェースです(インターフェースについては後の単元にて詳しく説明します)。

例えば、月の初めの日を取得するメソッドが実装されたTemporalAdjusterをwithメソッドの引数に渡すことにより、呼び出し元のインスタンスの日の値が月初の日になったインスタンスを生成します。

// 現在時刻
LocalDateTime timeTemporal = LocalDateTime.now();
System.out.println(" 現在:" + timeTemporal);

// 変数.with(TemporalAdjusters.メソッド)
System.out.println(" 月初:" + timeTemporal.with(TemporalAdjusters.firstDayOfMonth()));

TemporalAdjusterのメソッドには以下のようなものがあります。

メソッド概要
firstDayOfMonth()日の値を月初にした時間的オブジェクトを生成するTemporalAdjusterを返すメソッド
lastDayOfMonth()日の値を月末の日にした時間的オブジェクトを生成するTemporalAdjusterを返すメソッド
firstDayOfNextMonth()月と日の値を翌月の月初にした時間的オブジェクトを生成するTemporalAdjusterを返すメソッド
firstDayOfNextYear()年と月と日の値を翌年の初日にした時間的オブジェクトを生成するTemporalAdjusterを返すメソッド
lastDayOfNextYear()年と月と日の値を翌年の末日にした時間的オブジェクトを生成するTemporalAdjusterを返すメソッド

上記の他にも様々なメソッドがあります。詳しくは以下の参照サイトをご確認ください。
参照サイト:インタフェース java.time.temporal.TemporalAdjusterの使用

日時の加減算(plus○○、minus○○)

日付を扱うにあたって、何日後、のような処理が必要になる場合があります。

以下のソースを記述してみましょう。

// 現在日時を取得
LocalDateTime nowDate = LocalDateTime.now();
//  表示形式を変更
DateTimeFormatter ndtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String getNdtf = ndtf.format(nowDate);
System.out.println(" オリジナル:"+ getNdtf );
       
// 5日後を設定する
nowDate = nowDate.plusDays(5);
getNdtf = ndtf.format(nowDate);
System.out.println(" 今日の5日後:" + getNdtf);
       
// 3日前に戻す(5日後の3日前)
nowDate = nowDate.minusDays(3);
getNdtf = ndtf.format(nowDate);
System.out.println(" 今日の2日後:" + getNdtf);
       
// 2週間先
nowDate = nowDate.plusWeeks(2);
getNdtf = ndtf.format(nowDate);
System.out.println(" 2週間先:" + getNdtf);
       
// 2か月前
nowDate = nowDate.minusMonths(2);
getNdtf = ndtf.format(nowDate);
System.out.println(" 2カ月前:" + getNdtf);
       
// 5年後
nowDate = nowDate.plusYears(5);
getNdtf = ndtf.format(nowDate);
System.out.println(" 5年後:" + getNdtf);

LocalDateの変数に対し加算であれば「.plus○○(DaysやWeeksなど)」、減算であれば「.minus○○」を使用して求めていきます。


日時を取り扱う上での注意点
Javaで使用している時間は、PCに設定されている時間を取得しています。
このPCに設定されている時間はPCの設定でどの国かによって時間を変更することが可能ですが、
日時をJavaで取得した時点でどの国の時間なのか、という情報は失われてしまいます。

システムが使われる対象が世界中の人が使うような処理を想定した時、日本と時差が発生します。
この時、LocaDateやLocalTime、LocalDateTimeをそのまま発行した場合、海外の時刻と一致しない、というトラブルが発生します。


もう1点、「java.time」で使用する処理について解説しましたが、日時を取り扱う処理は他にもあります。「java.util」パッケージ内の「Date」や「Calendar」といわれているAPIです。

かつて、Javaではこの「Date」や「Calendar」といったクラスで日時を扱っていましたが、2038年を過ぎると使えなくなる問題がありました。
そのため、2038年以降も日時を扱えるようにするためにできたのが「java.time」です。

日時の差分・比較(between)

日時の差分を求める場合、ChronoUnitクラスのbetweenメソッドを使います。

        LocalDateTime sampleOldDateTime = LocalDateTime.of(2018, 12, 31, 20, 30);
        LocalDateTime sampleNewDateTime = LocalDateTime.of(2020, 1, 1, 20, 20);

        long compareYears = ChronoUnit.YEARS.between(sampleOldDateTime, sampleNewDateTime);
        System.out.println("差分は" + compareYears + "年です");
        long compareMonth = ChronoUnit.MONTHS.between(sampleOldDateTime, sampleNewDateTime);
        System.out.println("差分は" + compareMonth + "か月です");
        long compareDays = ChronoUnit.DAYS.between(sampleOldDateTime, sampleNewDateTime);
        System.out.println("差分は" + compareDays + "日です");

年で比較する場合は「YEARS」、月で比較する場合は「MONTHS」など該当する列挙型定数を選択します。
他の定数には以下のものがあります。

項目列挙型定数
YEARS
MONTHS
DAYS
WEEKS
HOURS
MINUTS
SECONDS
ミリ秒MILLS
ナノ秒NANOS

また、LocalDateTimeの比較では、isAfterisBeforeisEqualという3つのメソッドを使用することで
シンプルに表現することが可能です。

       LocalDateTime exNewDays = LocalDateTime.of(2025, 1,1,12,00);
       LocalDateTime exOldDays = LocalDateTime.of(2024, 1,1,12,00);
       
       boolean result = exNewDays.isAfter(exOldDays);
       System.out.println(result);
       boolean result2 = exNewDays.isBefore(exOldDays);
       System.out.println(result2);
       boolean result3 = exNewDays.isEqual(exOldDays);
       System.out.println(result3);

出力結果は上から「true・false・false」となったかと思われます。
isAfterは「A.isAfter(B)」で、BがAより前(AがBより後)であればtrue、
isBeforeは「A.isBefore(B)」で、AがBより前であればtrueとなります。

練習問題

「com.cmps.day」パッケージ内に「DayQuestion」クラスを作成して下さい。

問1: 現在時刻の「XXXX年X月X日」、「XXXX/XX/XX」、「XXXX-X-X」を表示しましょう。

問2:現在日時の「5年後」、「5か月後」、「5日後」、「5時間後」、「5分後」、「5秒後」の6つを表示しましょう。

問3: 先月の末日を表示してください。

問4: 「2022/06/02 20:20:20」の「5年前」、「5か月前」、「5日前」、「5時間前」、「5分前」、「5秒前」の6つを「2022/06/02 20:20:20」と同じフォーマットで表示しましょう。

タイトルとURLをコピーしました