[Apex] 日付・日時 と 文字列の変換

## 日付・日時 と 文字列 の変換
### Date → String

```apex:Date→String
Date d = Date.newInstance(2019, 6, 10);
String.valueOf(d); // 2019-06-10
d.format();        // 2019/06/10 (戻り値のStringはコンテキストユーザのロケールの日付形式)
Datetime.newInstance(d.year(), d.month(), d.day()).format('M/d(E)'); // 6/10(Mon)
```
* ロケール別の日付形式はこちらを参照: [サポートされる日付と時刻の形式 (ICU)](https://help.salesforce.com/articleView?id=admin_supported_date_time_format.htm&type=5)
* Date型のままではフォーマットの形式を指定できない。一度Datetime形に変換する必要がある。

### String → Date

```apex:String→Date
Date.parse('2019/06/10'); // 2019-06-10 (引数はコンテキストユーザのロケールの日付形式)
Date.parse('2019/6/10');  // 2019-06-10

Date.valueOf('2019-06-10'); // 2019-06-10
Date.valueOf('2019-6-10');  // 2019-06-10
Date.valueOf('2019-06-10T00:00:00');       // 2019-06-10
Date.valueOf('2019-06-10T00:00:00+09:00'); // 2019-06-10
Date.valueOf('2019-06-10 00:00:00+09:00'); // 2019-06-10
Date.valueOf('2019-06-10 20:00:00-08:00'); // 2019-06-10
```
* Date#valueOf は`yyyy-MM-dd`のみ評価され、時刻・タイムゾーンの指定は無視される。
* ロケールと日付の形式の対応はこちらを参照: [サポートされる日付と時刻の形式 (ICU)](https://help.salesforce.com/articleView?id=admin_supported_date_time_format.htm&type=5)

### Datetime → String

```Apex:Datetime→String
Datetime dt = Datetime.newInstance(2019, 6, 10, 1, 23, 45);
String.valueOf(dt);    // 2019-06-10 01:23:45
String.valueOfGmt(dt); // 2019-06-09 16:23:45

dt.format(); // 2019/06/10 1:23
dt.format('yyyy-MM-dd\'T\'HH:mm:ssXXX'); // 2019-06-10T01:23:45+09:00 (フォーマットについては下記のリンクを参照)
dt.format('yyyy-MM-dd\'T\'HH:mm:ssXXX', 'America/New_York');  // 2019-06-09T12:23:45-04:00
dt.formatGmt('yyyy-MM-dd\'T\'HH:mm:ssXXX'); // 2019-06-09T16:23:45Z
```
* formatの形式はこちらを参照: [SimpleDateFormat (Java Platform SE 7 )](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html)
* タイムゾーンの指定はこちらを参照: [タイムゾーン (TimeZone) - 日時・タイムゾーンの扱い - Java 入門](https://java.keicode.com/lang/datetime-timezone.php)
    * 「Asia/Tokyo」「Japan」「JST」などが使用可能。使用できない形を指定した場合、GMTになる。
* Datetime#format の戻り値はローカルタイムのString。
* Datetime#formatGmt の戻り値はGMTのString。

### String → Datetime
```apex:String→Datetime
Datetime.parse('2019/06/10 01:23'); // 2019-06-10T01:23:00+09:00

Datetime.valueOf('2019-06-10 01:23:45');       // 2019-06-10T01:23:45+09:00
Datetime.valueOf('2019-06-10 01:23:45Z');      // 2019-06-10T01:23:45+09:00
Datetime.valueOf('2019-06-10 01:23:45+0900');  // 2019-06-10T01:23:45+09:00
Datetime.valueOf('2019-06-10 01:23:45+09:00'); // 2019-06-10T01:23:45+09:00

Datetime.valueOfGmt('2019-06-10 01:23:45');       // 2019-06-10T10:23:45+09:00
Datetime.valueOfGmt('2019-06-10 01:23:45Z');      // 2019-06-10T10:23:45+09:00
Datetime.valueOfGmt('2019-06-10 01:23:45+0900');  // 2019-06-10T10:23:45+09:00
Datetime.valueOfGmt('2019-06-10 01:23:45+09:00'); // 2019-06-10T10:23:45+09:00
```
* Datetime#parse は、引数のStringに秒数を含めるとエラーになる
* Datetime#valueOf は、引数のStringに秒数を含めないとエラーになる
* Datetime#valueOf と Datetime#valueOfGmt は、`yyyy-MM-dd HH:mm:ss`のみ評価され、タイムゾーンの指定は無視される。
* Datetime#valueOf の引数はローカルタイムとみなされる。
* Datetime#valueOfGmt の引数はGMTとみなされる。
* `Datetime.newInstance(year, month, day, hour, minute, second)`


## 日付 と 日時 の変換
### Date → Datetime
```apex:Date→Datetime
Date d = Date.newInstance(2019, 6, 10);
Datetime.newInstance(d.year(), d.month(), d.day()); // 2019-06-10T00:00:00+09:00
```

* `Date.newInstance(year, month, day)`: 引数はいずれもInteger型
* `Time.newInstance(hour, minutes, seconds, milliseconds)`: 引数はいずれもInteger型
* `Datetime.newInstance(date, time)`: dateはDate型、timeはTime型
* `Datetime.newInstance(hour, minutes, seconds, milliseconds)`: 引数はいずれもInteger型

### Datetime → Date
```apex:Datetime→Date
Datetime dt = Datetime.newInstance(2019, 6, 10, 1, 23, 45);
dt.date();        // 2019-06-10 (ローカルタイムの日付を返す)
dt.dateGMT();     // 2019-06-09 (GMTの日付を返す)

// 参考
Date.valueOf(dt); // 2019-06-09 04:00:00 (※注意)
```

* DatetimeのDateへの変換はDatetime#dateを使用する。Date#valueOfは、履歴SObjectのOldValue項目またはNewValue項目で使用する。
* Datetimeを Date#valueOf で変換すると時間を含むDate型となってしまう。比較演算子を使用するときは注意が必要(見かけ上は同じ値だが、「==」でfalseになるなど)。

## Appendix
### エポック秒(UNIX秒)の変換
```apex
// エポック秒 → Datatime
Long epochSec = 1560097380L;
Datetime dt = Datetime.newInstance(epochSec * 1000); // 2019-06-10T01:23:00+09:00

// Datetime → エポック秒
Datetime dt2 = Datetime.newInstance(2019, 06, 10, 01, 23, 00);
Long epochSec2 = dt2.getTime() / 1000; // 1560097380L
```

### Date.newInstanceについて
* `Date.newInstance`の月、日に存在しない値を設定した場合、繰下げ・繰上げが行われる。
    * `Date.newInstance(2019, 13, 10)` → 2020-01-10
    * `Date.newInstance(2019, 0, 10)` → 2018-12-10
    * `Date.newInstance(2019, -1, 10)` → 2018-11-10 

## 参考
- [Date クラス | Apex 開発者ガイド | Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_date.htm)
- [Datetime クラス | Apex 開発者ガイド | Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_datetime.htm)
- [String クラス | Apex 開発者ガイド | Salesforce Developers](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_methods_system_string.htm)



<!--
## インスタンスの作成
### Date
```java
Date.newInstance(2019, 11, 25);
```
### Datetime
```java
newInstance(year, month, day)
newInstance(year, month, day, hour, minute, second)
newInstance(milliseconds) // TODO
newInstance(date, time) // TODO

newInstanceGmt(year, month, date)
newInstanceGmt(year, month, date, hour, minute, second)
newInstanceGmt(date, time)
```
-->

<!--
数式
DATETIMEVALUE("2019-01-01 00:00:00+09:00")
は
2019-01-01 00:00:00+00:00:00 
あつかい。
-->

<!--
## 日時 と 時間 の変換
### Datetime → Time
```java:Datetime→Time
```
Account acc = [SELECT Id,LastModifiedDate, format(LastModifiedDate) ft FROM Account LIMIT 1];
System.debug(LoggingLevel.DEBUG, acc.get('Id'));
System.debug(LoggingLevel.DEBUG, (String) acc.get('ft')); // String 2019/11/13 8:51
System.debug(LoggingLevel.DEBUG, acc.LastModifiedDate); // Datetime 2019-11-12 23:51:17
System.debug(LoggingLevel.DEBUG, acc.LastModifiedDate.format()); // String 2019/11/13 8:51
-->