Prechádzať zdrojové kódy

Add Basic Date Docs to Painless (#42544)

Jack Conradson 6 rokov pred
rodič
commit
7284de4106

+ 2 - 0
docs/painless/painless-guide/index.asciidoc

@@ -1,5 +1,7 @@
 include::painless-walkthrough.asciidoc[]
 
+include::painless-datetime.asciidoc[]
+
 include::painless-method-dispatch.asciidoc[]
 
 include::painless-debugging.asciidoc[]

+ 320 - 0
docs/painless/painless-guide/painless-datetime.asciidoc

@@ -0,0 +1,320 @@
+[[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
+}
+----

+ 180 - 0
modules/lang-painless/src/test/java/org/elasticsearch/painless/DateTests.java

@@ -0,0 +1,180 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.painless;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
+public class DateTests extends ScriptTestCase {
+
+    public void testLongToZonedDateTime() {
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "long milliSinceEpoch = 434931330000L;" +
+                "Instant instant = Instant.ofEpochMilli(milliSinceEpoch);" +
+                "return ZonedDateTime.ofInstant(instant, ZoneId.of('Z'));"
+        ));
+    }
+
+    public void testStringToZonedDateTime() {
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "String milliSinceEpochString = '434931330000';" +
+                "long milliSinceEpoch = Long.parseLong(milliSinceEpochString);" +
+                "Instant instant = Instant.ofEpochMilli(milliSinceEpoch);" +
+                "return ZonedDateTime.ofInstant(instant, ZoneId.of('Z'));"
+        ));
+
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "String datetime = '1983-10-13T22:15:30Z';" +
+                "return ZonedDateTime.parse(datetime);"
+        ));
+
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "String datetime = 'Thu, 13 Oct 1983 22:15:30 GMT';" +
+                "return ZonedDateTime.parse(datetime, DateTimeFormatter.RFC_1123_DATE_TIME);"
+        ));
+
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "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\");" +
+                "return ZonedDateTime.parse(datetime, dtf);"
+        ));
+    }
+
+    public void testPiecesToZonedDateTime() {
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "int year = 1983;" +
+                "int month = 10;" +
+                "int day = 13;" +
+                "int hour = 22;" +
+                "int minutes = 15;" +
+                "int seconds = 30;" +
+                "int nanos = 0;" +
+                "String tz = 'Z';" +
+                "return ZonedDateTime.of(year, month, day, hour, minutes, seconds, nanos, ZoneId.of(tz));"
+        ));
+    }
+
+    public void testZonedDatetimeToLong() {
+        assertEquals(434931330000L, exec(
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));" +
+                "return zdt.toInstant().toEpochMilli();"
+        ));
+    }
+
+    public void testZonedDateTimeToString() {
+        assertEquals("1983-10-13T22:15:30Z", exec(
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));" +
+                "return zdt.format(DateTimeFormatter.ISO_INSTANT);"
+        ));
+
+        assertEquals("date: 1983/10/13 time: 22:15:30", exec(
+                "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\");" +
+                "return zdt.format(dtf);"
+        ));
+    }
+
+    public void testZonedDateTimeToPieces() {
+        assertArrayEquals(new int[] {1983, 10, 13, 22, 15, 30, 100}, (int[])exec(
+                "int[] pieces = new int[7];" +
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 100, ZoneId.of('Z'));" +
+                "pieces[0] = zdt.year;" +
+                "pieces[1] = zdt.monthValue;" +
+                "pieces[2] = zdt.dayOfMonth;" +
+                "pieces[3] = zdt.hour;" +
+                "pieces[4] = zdt.minute;" +
+                "pieces[5] = zdt.second;" +
+                "pieces[6] = zdt.nano;" +
+                "return pieces;"
+        ));
+    }
+
+    public void testLongManipulation() {
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 22, 15, 27, 0, ZoneId.of("Z")), exec(
+                "long milliSinceEpoch = 434931330000L;" +
+                "milliSinceEpoch = milliSinceEpoch - 1000L*3L;" +
+                "Instant instant = Instant.ofEpochMilli(milliSinceEpoch);" +
+                "return ZonedDateTime.ofInstant(instant, ZoneId.of('Z'))"
+        ));
+    }
+
+    public void testZonedDateTimeManipulation() {
+        assertEquals(ZonedDateTime.of(1983, 10, 16, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));" +
+                "return zdt.plusDays(3);"
+        ));
+
+        assertEquals(ZonedDateTime.of(1983, 10, 13, 20, 10, 30, 0, ZoneId.of("Z")), exec(
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));" +
+                "return zdt.minusMinutes(125);"
+        ));
+
+        assertEquals(ZonedDateTime.of(1976, 10, 13, 22, 15, 30, 0, ZoneId.of("Z")), exec(
+                "ZonedDateTime zdt = ZonedDateTime.of(1983, 10, 13, 22, 15, 30, 0, ZoneId.of('Z'));" +
+                "return zdt.withYear(1976);"
+        ));
+    }
+
+    public void testLongTimeDifference() {
+        assertEquals(3000L, exec(
+                "long startTimestamp = 434931327000L;" +
+                "long endTimestamp = 434931330000L;" +
+                "return endTimestamp - startTimestamp;"
+        ));
+    }
+
+    public void testZonedDateTimeDifference() {
+        assertEquals(4989L, exec(
+                "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'));" +
+                "return ChronoUnit.MILLIS.between(zdt1, zdt2);"
+        ));
+
+        assertEquals(4L, exec(
+                "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'));" +
+                "return ChronoUnit.DAYS.between(zdt1, zdt2);"
+        ));
+    }
+
+    public void compareLongs() {
+        assertEquals(false, exec(
+                "long ts1 = 434931327000L;" +
+                "long ts2 = 434931330000L;" +
+                "return ts1 > ts2;"
+        ));
+    }
+
+    public void compareZonedDateTimes() {
+        assertEquals(true, exec(
+                "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'));" +
+                "return zdt1.isBefore(zdt2);"
+        ));
+
+        assertEquals(false, exec(
+                "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'));" +
+                "return zdt1.isAfter(zdt2);"
+        ));
+    }
+}