diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java index d22d402aa15..d513bd1cd77 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java @@ -5,45 +5,48 @@ */ package org.elasticsearch.xpack.watcher.trigger.schedule.tool; -import java.util.Arrays; -import java.util.List; - import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.LoggingAwareCommand; -import org.elasticsearch.cli.UserException; import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; import org.elasticsearch.xpack.core.scheduler.Cron; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.TimeZone; + public class CronEvalTool extends LoggingAwareCommand { public static void main(String[] args) throws Exception { exit(new CronEvalTool().main(args, Terminal.DEFAULT)); } - private static final DateTimeFormatter formatter = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss"); + private static final DateTimeFormatter UTC_FORMATTER = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss") + .withZone(DateTimeZone.UTC).withLocale(Locale.ROOT); + private static final DateTimeFormatter LOCAL_FORMATTER = DateTimeFormat.forPattern("EEE, d MMM yyyy HH:mm:ss Z") + .withZone(DateTimeZone.forTimeZone(TimeZone.getDefault())); - private final OptionSpec countOption; + private final OptionSpec countOption; private final OptionSpec arguments; CronEvalTool() { super("Validates and evaluates a cron expression"); this.countOption = parser.acceptsAll(Arrays.asList("c", "count"), "The number of future times this expression will be triggered") - // TODO: change this to ofType(Integer.class) with jopt-simple 5.0 - // before then it will cause a security exception in tests - .withRequiredArg().defaultsTo("10"); + .withRequiredArg().ofType(Integer.class).defaultsTo(10); this.arguments = parser.nonOptions("expression"); } @Override protected void execute(Terminal terminal, OptionSet options) throws Exception { - int count = Integer.parseInt(countOption.value(options)); + int count = countOption.value(options); List args = arguments.values(options); if (args.size() != 1) { throw new UserException(ExitCodes.USAGE, "expecting a single argument that is the cron expression to evaluate"); @@ -55,8 +58,14 @@ public class CronEvalTool extends LoggingAwareCommand { Cron.validate(expression); terminal.println("Valid!"); - DateTime date = DateTime.now(DateTimeZone.UTC); - terminal.println("Now is [" + formatter.print(date) + "]"); + final DateTime date = DateTime.now(DateTimeZone.UTC); + final boolean isLocalTimeUTC = UTC_FORMATTER.getZone().equals(LOCAL_FORMATTER.getZone()); + if (isLocalTimeUTC) { + terminal.println("Now is [" + UTC_FORMATTER.print(date) + "] in UTC"); + } else { + terminal.println("Now is [" + UTC_FORMATTER.print(date) + "] in UTC, local time is [" + LOCAL_FORMATTER.print(date) + "]"); + + } terminal.println("Here are the next " + count + " times this cron expression will trigger:"); Cron cron = new Cron(expression); @@ -68,11 +77,17 @@ public class CronEvalTool extends LoggingAwareCommand { if (time < 0) { if (i == 0) { throw new UserException(ExitCodes.OK, "Could not compute future times since [" - + formatter.print(prevTime) + "] " + "(perhaps the cron expression only points to times in the past?)"); + + UTC_FORMATTER.print(prevTime) + "] " + "(perhaps the cron expression only points to times in the past?)"); } break; } - terminal.println((i + 1) + ".\t" + formatter.print(time)); + + if (isLocalTimeUTC) { + terminal.println((i + 1) + ".\t" + UTC_FORMATTER.print(time)); + } else { + terminal.println((i + 1) + ".\t" + UTC_FORMATTER.print(time)); + terminal.println("\t" + LOCAL_FORMATTER.print(time)); + } } } } diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolTests.java index f1e864d547c..7a91c3389f1 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolTests.java @@ -7,15 +7,19 @@ package org.elasticsearch.xpack.watcher.trigger.schedule.tool; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.CommandTestCase; +import org.joda.time.DateTimeZone; +import java.util.Arrays; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; public class CronEvalToolTests extends CommandTestCase { + @Override protected Command newCommand() { return new CronEvalTool(); @@ -48,4 +52,22 @@ public class CronEvalToolTests extends CommandTestCase { assertThat(message, containsString("(perhaps the cron expression only points to times in the past?)")); } } + + // randomized testing sets arbitrary locales and timezones, and we do not care + // we always have to output in standard locale and independent from timezone + public void testEnsureDateIsShownInRootLocale() throws Exception { + String output = execute("-c","1", "0 0 11 ? * MON-SAT 2040"); + if (TimeZone.getDefault().equals(DateTimeZone.UTC.toTimeZone())) { + assertThat(output, not(containsString("local time is"))); + long linesStartingWithOne = Arrays.stream(output.split("\n")).filter(s -> s.startsWith("\t")).count(); + assertThat(linesStartingWithOne, is(0L)); + } else { + // check for header line + assertThat(output, containsString("] in UTC, local time is")); + assertThat(output, containsString("Mon, 2 Jan 2040 11:00:00")); + logger.info(output); + long linesStartingWithOne = Arrays.stream(output.split("\n")).filter(s -> s.startsWith("\t")).count(); + assertThat(linesStartingWithOne, is(1L)); + } + } }