[[painless-datetime]]
=== Using Datetime in Painless

==== Datetime API

Datetimes in Painless use the standard Java libraries and are available through
the Painless <<painless-api-reference-shared, Shared API>>. Most of the classes
from the following Java packages are available to use in Painless scripts:

* <<painless-api-reference-shared-java-time, java.time>>
* <<painless-api-reference-shared-java-time-chrono, java.time.chrono>>
* <<painless-api-reference-shared-java-time-format, java.time.format>>
* <<painless-api-reference-shared-java-time-temporal, java.time.temporal>>
* <<painless-api-reference-shared-java-time-zone, java.time.zone>>

==== Datetime Representation

Datetimes in Painless are most commonly represented as a
<<primitive-types, long>>, a <<string-type, String>>, or a
<<painless-api-reference-shared-ZonedDateTime, ZonedDateTime>>.

long:: represents a datetime as the number of milliseconds or nanoseconds since
epoch (1970-01-01T00:00:00Z)
String:: represents a datetime as a sequence of characters defined by a
well-known standard such as https://en.wikipedia.org/wiki/ISO_8601[ISO 8601] or
defined by the source of input in a custom way
ZonedDateTime:: a <<reference-types, reference type>> (object) that contains an
internal representation of a datetime and provides numerous
<<painless-api-reference-shared-ZonedDateTime, methods>> for
modification and comparison.

Switching between different representations of datetimes is often necessary to
achieve a script's objective(s). A typical pattern in a script is to switch a
long or String representation of a datetime to a ZonedDateTime representation,
modify or compare the ZonedDateTime representation, and then switch it back to
a long or String representation for storage or as a returned result.

==== Datetime Parsing and Formatting

Datetime parsing is a switch from a String representation to a ZonedDateTime
representation, and datetime formatting is a switch from a ZonedDateTime
representation to a String representation.

A <<painless-api-reference-shared-DateTimeFormatter, DateTimeFormatter>> is a
<<reference-types, reference type>> (object) that defines the allowed sequence
of characters for a String representation of a datetime. Datetime parsing and
formatting often requires a DateTimeFormatter. For more information about how
to use a DateTimeFormatter see the
{java11-javadoc}/java.base/java/time/format/DateTimeFormatter.html[Java documentation].

===== Datetime Parsing Examples

* parse from milliseconds
+
[source,Painless]
----
String milliSinceEpochString = "434931330000";
long milliSinceEpoch = Long.parseLong(milliSinceEpochString);
Instant instant = Instant.ofEpochMilli(milliSinceEpoch);
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.of('Z'));
----
+
* parse from ISO 8601
+
[source,Painless]
----
String datetime = '1983-10-13T22:15:30Z';
ZonedDateTime zdt = ZonedDateTime.parse(datetime);
----
Note the parse method uses ISO 8601 by default.
+
* parse from RFC 1123
+
[source,Painless]
----
String datetime = 'Thu, 13 Oct 1983 22:15:30 GMT';
ZonedDateTime zdt = ZonedDateTime.parse(datetime,
        DateTimeFormatter.RFC_1123_DATE_TIME);
----
Note the use of a built-in DateTimeFormatter.
+
* parse from a custom format
+
[source,Painless]
----
String datetime = 'custom y 1983 m 10 d 13 22:15:30 Z';
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
        "'custom' 'y' yyyy 'm' MM 'd' dd HH:mm:ss VV");
ZonedDateTime zdt = ZonedDateTime.parse(datetime, dtf);
----
Note the use of a custom DateTimeFormatter.

===== Datetime Formatting Examples

* format to a String (ISO 8601)
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
String datetime = zdt.format(DateTimeFormatter.ISO_INSTANT);
----
Note the use of a built-in DateTimeFormatter.
+
* format to a String (custom)
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(
        "'date:' yyyy/MM/dd 'time:' HH:mm:ss");
String datetime = zdt.format(dtf);
----
Note the use of a custom DateTimeFormatter.

==== Datetime Conversion

Datetime conversion is a switch from a long representation to a ZonedDateTime
representation and vice versa.

===== Datetime Conversion Examples

* convert from milliseconds
+
[source,Painless]
----
long milliSinceEpoch = 434931330000L;
Instant instant = Instant.ofEpochMilli(milliSinceEpoch);
ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.of('Z'));
----
+
* convert to milliseconds
+
[source,Painless]
-----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
long milliSinceEpoch = zdt.toInstant().toEpochMilli();
-----

==== Datetime Pieces

Use the ZonedDateTime
<<painless-api-reference-shared-ZonedDateTime, method of>> to create a new
ZonedDateTime from pieces (year, month, day, hour, minute, second, nano,
time zone). Use ZonedDateTime
<<painless-api-reference-shared-ZonedDateTime, methods>> to extract pieces from
a ZonedDateTime.

===== Datetime Pieces Examples

* create a ZonedDateTime from pieces
+
[source,Painless]
----
int year = 1983;
int month = 10;
int day = 13;
int hour = 22;
int minutes = 15;
int seconds = 30;
int nanos = 0;
ZonedDateTime zdt = ZonedDateTime.of(
        year, month, day, hour, minutes, seconds, nanos, ZoneId.of('Z'));
----
+
* extract pieces from a ZonedDateTime
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 100, ZoneId.of(tz));
int year = zdt.getYear();
int month = zdt.getMonthValue();
int day = zdt.getDayOfMonth();
int hour = zdt.getHour();
int minutes = zdt.getMinute();
int seconds = zdt.getSecond();
int nanos = zdt.getNano();
----

==== Datetime Modification

Use either a long or a ZonedDateTime to do datetime modification such as adding
several seconds to a datetime or subtracting several days from a datetime. Use
standard <<painless-operators-numeric, numeric operators>> to modify a long
representation of a datetime. Use ZonedDateTime
<<painless-api-reference-shared-ZonedDateTime, methods>> to modify a
ZonedDateTime representation of a datetime. Note most modification methods for
a ZonedDateTime return a new instance for assignment or immediate use.

===== Datetime Modification Examples

* Subtract three seconds from milliseconds
+
[source,Painless]
----
long milliSinceEpoch = 434931330000L;
milliSinceEpoch = milliSinceEpoch - 1000L*3L;
----
+
* Add three days to a datetime
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
ZonedDateTime updatedZdt = zdt.plusDays(3);
----
+
* Subtract 125 minutes from a datetime
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
ZonedDateTime updatedZdt = zdt.minusMinutes(125);
----
+
* Set the year on a datetime
+
[source,Painless]
----
ZonedDateTime zdt =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
ZonedDateTime updatedZdt = zdt.withYear(1976);
----

==== Elapsed Time

Use either two longs or two ZonedDateTimes to calculate an elapsed
time (difference) between two datetimes. Use
<<subtraction-operator, subtraction>> to calculate an elapsed time
between two longs of the same time unit such as milliseconds. For more complex
datetimes. use <<painless-api-reference-shared-ChronoUnit, ChronoUnit>> to
calculate the difference between two ZonedDateTimes.

===== Elapsed Time Examples

* Elapsed time for two millisecond datetimes
+
[source,Painless]
----
long startTimestamp = 434931327000L;
long endTimestamp = 434931330000L;
long differenceInMillis = endTimestamp - startTimestamp;
----
+
* Elapsed time in milliseconds for two datetimes
+
[source,Painless]
----
ZonedDateTime zdt1 =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 11000000, ZoneId.of('Z'));
ZonedDateTime zdt2 =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 35, 0, ZoneId.of('Z'));
long differenceInMillis = ChronoUnit.MILLIS.between(zdt1, zdt2);
----
+
* Elapsed time in days for two datetimes
+
[source,Painless]
----
ZonedDateTime zdt1 =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 11000000, ZoneId.of('Z'));
ZonedDateTime zdt2 =
        ZonedDateTime.of(1983, 10, 17, 22, 15, 35, 0, ZoneId.of('Z'));
long differenceInDays = ChronoUnit.DAYS.between(zdt1, zdt2);
----

==== Datetime Comparison

Use either two longs or two ZonedDateTimes to do a datetime comparison. Use
standard <<painless-operators-boolean, comparison operators>> to compare two
longs of the same time unit such as milliseconds. For more complex datetimes,
use ZonedDateTime <<painless-api-reference-shared-ZonedDateTime, methods>> to
compare two ZonedDateTimes.

===== Datetime Comparison Examples

* Comparison of two millisecond datetimes
+
[source,Painless]
----
long timestamp1 = 434931327000L;
long timestamp2 = 434931330000L;

if (timestamp1 > timestamp2) {
   // handle condition
}
----
+
* Before comparision of two datetimes
+
[source,Painless]
----
ZonedDateTime zdt1 =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
ZonedDateTime zdt2 =
        ZonedDateTime.of(1983, 10, 17, 22, 15, 35, 0, ZoneId.of('Z'));

if (zdt1.isBefore(zdt2)) {
    // handle condition
}
----
+
* After comparision of two datetimes
+
[source,Painless]
----
ZonedDateTime zdt1 =
        ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));
ZonedDateTime zdt2 =
        ZonedDateTime.of(1983, 10, 17, 22, 15, 35, 0, ZoneId.of('Z'));

if (zdt1.isAfter(zdt2)) {
    // handle condition
}
----
